LCOV - code coverage report
Current view: top level - feature/control - control_cmd.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 266 966 27.5 %
Date: 2021-11-24 03:28:48 Functions: 13 47 27.7 %

          Line data    Source code
       1             : /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       2             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       3             : /* See LICENSE for licensing information */
       4             : 
       5             : /**
       6             :  * \file control_cmd.c
       7             :  * \brief Implement various commands for Tor's control-socket interface.
       8             :  **/
       9             : 
      10             : #define CONTROL_MODULE_PRIVATE
      11             : #define CONTROL_CMD_PRIVATE
      12             : #define CONTROL_EVENTS_PRIVATE
      13             : 
      14             : #include "core/or/or.h"
      15             : #include "app/config/config.h"
      16             : #include "lib/confmgt/confmgt.h"
      17             : #include "app/main/main.h"
      18             : #include "core/mainloop/connection.h"
      19             : #include "core/or/circuitbuild.h"
      20             : #include "core/or/circuitlist.h"
      21             : #include "core/or/circuituse.h"
      22             : #include "core/or/connection_edge.h"
      23             : #include "core/or/circuitstats.h"
      24             : #include "core/or/extendinfo.h"
      25             : #include "feature/client/addressmap.h"
      26             : #include "feature/client/dnsserv.h"
      27             : #include "feature/client/entrynodes.h"
      28             : #include "feature/control/control.h"
      29             : #include "feature/control/control_auth.h"
      30             : #include "feature/control/control_cmd.h"
      31             : #include "feature/control/control_hs.h"
      32             : #include "feature/control/control_events.h"
      33             : #include "feature/control/control_getinfo.h"
      34             : #include "feature/control/control_proto.h"
      35             : #include "feature/hs/hs_control.h"
      36             : #include "feature/hs/hs_service.h"
      37             : #include "feature/nodelist/nodelist.h"
      38             : #include "feature/nodelist/routerinfo.h"
      39             : #include "feature/nodelist/routerlist.h"
      40             : #include "feature/rend/rendcommon.h"
      41             : #include "lib/crypt_ops/crypto_rand.h"
      42             : #include "lib/crypt_ops/crypto_util.h"
      43             : #include "lib/encoding/confline.h"
      44             : #include "lib/encoding/kvline.h"
      45             : 
      46             : #include "core/or/cpath_build_state_st.h"
      47             : #include "core/or/entry_connection_st.h"
      48             : #include "core/or/origin_circuit_st.h"
      49             : #include "core/or/socks_request_st.h"
      50             : #include "feature/control/control_cmd_args_st.h"
      51             : #include "feature/control/control_connection_st.h"
      52             : #include "feature/nodelist/node_st.h"
      53             : #include "feature/nodelist/routerinfo_st.h"
      54             : 
      55             : #include "app/config/statefile.h"
      56             : 
      57             : static int control_setconf_helper(control_connection_t *conn,
      58             :                                   const control_cmd_args_t *args,
      59             :                                   int use_defaults);
      60             : 
      61             : /** Yield true iff <b>s</b> is the state of a control_connection_t that has
      62             :  * finished authentication and is accepting commands. */
      63             : #define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
      64             : 
      65             : /**
      66             :  * Release all storage held in <b>args</b>
      67             :  **/
      68             : void
      69          68 : control_cmd_args_free_(control_cmd_args_t *args)
      70             : {
      71          68 :   if (! args)
      72             :     return;
      73             : 
      74          55 :   if (args->args) {
      75         113 :     SMARTLIST_FOREACH(args->args, char *, c, tor_free(c));
      76          53 :     smartlist_free(args->args);
      77             :   }
      78          55 :   config_free_lines(args->kwargs);
      79          55 :   tor_free(args->cmddata);
      80             : 
      81          55 :   tor_free(args);
      82             : }
      83             : 
      84             : /** Erase all memory held in <b>args</b>. */
      85             : void
      86          13 : control_cmd_args_wipe(control_cmd_args_t *args)
      87             : {
      88          13 :   if (!args)
      89             :     return;
      90             : 
      91          13 :   if (args->args) {
      92          36 :     SMARTLIST_FOREACH(args->args, char *, c, memwipe(c, 0, strlen(c)));
      93             :   }
      94          26 :   for (config_line_t *line = args->kwargs; line; line = line->next) {
      95          13 :     memwipe(line->key, 0, strlen(line->key));
      96          13 :     memwipe(line->value, 0, strlen(line->value));
      97             :   }
      98          13 :   if (args->cmddata)
      99           0 :     memwipe(args->cmddata, 0, args->cmddata_len);
     100             : }
     101             : 
     102             : /**
     103             :  * Return true iff any element of the NULL-terminated <b>array</b> matches
     104             :  * <b>kwd</b>. Case-insensitive.
     105             :  **/
     106             : static bool
     107          12 : string_array_contains_keyword(const char **array, const char *kwd)
     108             : {
     109          26 :   for (unsigned i = 0; array[i]; ++i) {
     110          25 :     if (! strcasecmp(array[i], kwd))
     111             :       return true;
     112             :   }
     113             :   return false;
     114             : }
     115             : 
     116             : /** Helper for argument parsing: check whether the keyword arguments just
     117             :  * parsed in <b>result</b> were well-formed according to <b>syntax</b>.
     118             :  *
     119             :  * On success, return 0.  On failure, return -1 and set *<b>error_out</b>
     120             :  * to a newly allocated error string.
     121             :  **/
     122             : static int
     123          14 : kvline_check_keyword_args(const control_cmd_args_t *result,
     124             :                           const control_cmd_syntax_t *syntax,
     125             :                           char **error_out)
     126             : {
     127          14 :   if (result->kwargs == NULL) {
     128           1 :     tor_asprintf(error_out, "Cannot parse keyword argument(s)");
     129           1 :     return -1;
     130             :   }
     131             : 
     132          13 :   if (! syntax->allowed_keywords) {
     133             :     /* All keywords are permitted. */
     134             :     return 0;
     135             :   }
     136             : 
     137             :   /* Check for unpermitted arguments */
     138             :   const config_line_t *line;
     139          17 :   for (line = result->kwargs; line; line = line->next) {
     140          12 :     if (! string_array_contains_keyword(syntax->allowed_keywords,
     141          12 :                                         line->key)) {
     142           1 :       tor_asprintf(error_out, "Unrecognized keyword argument %s",
     143             :                    escaped(line->key));
     144           1 :       return -1;
     145             :     }
     146             :   }
     147             : 
     148             :   return 0;
     149             : }
     150             : 
     151             : /**
     152             :  * Helper: parse the arguments to a command according to <b>syntax</b>.  On
     153             :  * success, set *<b>error_out</b> to NULL and return a newly allocated
     154             :  * control_cmd_args_t.  On failure, set *<b>error_out</b> to newly allocated
     155             :  * error string, and return NULL.
     156             :  **/
     157             : STATIC control_cmd_args_t *
     158          55 : control_cmd_parse_args(const char *command,
     159             :                        const control_cmd_syntax_t *syntax,
     160             :                        size_t body_len,
     161             :                        const char *body,
     162             :                        char **error_out)
     163             : {
     164          55 :   *error_out = NULL;
     165          55 :   control_cmd_args_t *result = tor_malloc_zero(sizeof(control_cmd_args_t));
     166          55 :   const char *cmdline;
     167          55 :   char *cmdline_alloc = NULL;
     168          55 :   tor_assert(syntax->max_args < INT_MAX || syntax->max_args == UINT_MAX);
     169             : 
     170          55 :   result->command = command;
     171             : 
     172          55 :   if (syntax->store_raw_body) {
     173           0 :     tor_assert(body[body_len] == 0);
     174           0 :     result->raw_body = body;
     175             :   }
     176             : 
     177          55 :   const char *eol = memchr(body, '\n', body_len);
     178          55 :   if (syntax->want_cmddata) {
     179           6 :     if (! eol || (eol+1) == body+body_len) {
     180           1 :       *error_out = tor_strdup("Empty body");
     181           1 :       goto err;
     182             :     }
     183           5 :     cmdline_alloc = tor_memdup_nulterm(body, eol-body);
     184           5 :     cmdline = cmdline_alloc;
     185           5 :     ++eol;
     186           5 :     result->cmddata_len = read_escaped_data(eol, (body+body_len)-eol,
     187             :                                            &result->cmddata);
     188             :   } else {
     189          49 :     if (eol && (eol+1) != body+body_len) {
     190           1 :       *error_out = tor_strdup("Unexpected body");
     191           1 :       goto err;
     192             :     }
     193             :     cmdline = body;
     194             :   }
     195             : 
     196          53 :   result->args = smartlist_new();
     197          53 :   smartlist_split_string(result->args, cmdline, " ",
     198             :                          SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK,
     199          53 :                          (int)(syntax->max_args+1));
     200          53 :   size_t n_args = smartlist_len(result->args);
     201          53 :   if (n_args < syntax->min_args) {
     202           3 :     tor_asprintf(error_out, "Need at least %u argument(s)",
     203             :                  syntax->min_args);
     204           3 :     goto err;
     205          50 :   } else if (n_args > syntax->max_args && ! syntax->accept_keywords) {
     206           2 :     tor_asprintf(error_out, "Cannot accept more than %u argument(s)",
     207             :                  syntax->max_args);
     208           2 :     goto err;
     209             :   }
     210             : 
     211          48 :   if (n_args > syntax->max_args) {
     212             :     /* We have extra arguments after the positional arguments, and we didn't
     213             :        treat them as an error, so they must count as keyword arguments: Either
     214             :        K=V pairs, or flags, or both. */
     215          14 :     tor_assert(n_args == syntax->max_args + 1);
     216          14 :     tor_assert(syntax->accept_keywords);
     217          14 :     char *remainder = smartlist_pop_last(result->args);
     218          14 :     result->kwargs = kvline_parse(remainder, syntax->kvline_flags);
     219          14 :     tor_free(remainder);
     220          14 :     if (kvline_check_keyword_args(result, syntax, error_out) < 0) {
     221           2 :       goto err;
     222             :     }
     223             :   }
     224             : 
     225          46 :   tor_assert_nonfatal(*error_out == NULL);
     226          46 :   goto done;
     227           9 :  err:
     228           9 :   tor_assert_nonfatal(*error_out != NULL);
     229           9 :   control_cmd_args_free(result);
     230          55 :  done:
     231          55 :   tor_free(cmdline_alloc);
     232          55 :   return result;
     233             : }
     234             : 
     235             : /**
     236             :  * Return true iff <b>lines</b> contains <b>flags</b> as a no-value
     237             :  * (keyword-only) entry.
     238             :  **/
     239             : static bool
     240           0 : config_lines_contain_flag(const config_line_t *lines, const char *flag)
     241             : {
     242           0 :   const config_line_t *line = config_line_find_case(lines, flag);
     243           0 :   return line && !strcmp(line->value, "");
     244             : }
     245             : 
     246             : static const control_cmd_syntax_t setconf_syntax = {
     247             :   .max_args=0,
     248             :   .accept_keywords=true,
     249             :   .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
     250             : };
     251             : 
     252             : /** Called when we receive a SETCONF message: parse the body and try
     253             :  * to update our configuration.  Reply with a DONE or ERROR message.
     254             :  * Modifies the contents of body.*/
     255             : static int
     256           0 : handle_control_setconf(control_connection_t *conn,
     257             :                        const control_cmd_args_t *args)
     258             : {
     259           0 :   return control_setconf_helper(conn, args, 0);
     260             : }
     261             : 
     262             : static const control_cmd_syntax_t resetconf_syntax = {
     263             :   .max_args=0,
     264             :   .accept_keywords=true,
     265             :   .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
     266             : };
     267             : 
     268             : /** Called when we receive a RESETCONF message: parse the body and try
     269             :  * to update our configuration.  Reply with a DONE or ERROR message.
     270             :  * Modifies the contents of body. */
     271             : static int
     272           0 : handle_control_resetconf(control_connection_t *conn,
     273             :                          const control_cmd_args_t *args)
     274             : {
     275           0 :   return control_setconf_helper(conn, args, 1);
     276             : }
     277             : 
     278             : static const control_cmd_syntax_t getconf_syntax = {
     279             :   .max_args=UINT_MAX
     280             : };
     281             : 
     282             : /** Called when we receive a GETCONF message.  Parse the request, and
     283             :  * reply with a CONFVALUE or an ERROR message */
     284             : static int
     285           6 : handle_control_getconf(control_connection_t *conn,
     286             :                        const control_cmd_args_t *args)
     287             : {
     288           6 :   const smartlist_t *questions = args->args;
     289           6 :   smartlist_t *answers = smartlist_new();
     290           6 :   smartlist_t *unrecognized = smartlist_new();
     291           6 :   const or_options_t *options = get_options();
     292             : 
     293          14 :   SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
     294           8 :     if (!option_is_recognized(q)) {
     295           4 :       control_reply_add_printf(unrecognized, 552,
     296             :                                "Unrecognized configuration key \"%s\"", q);
     297             :     } else {
     298           4 :       config_line_t *answer = option_get_assignment(options,q);
     299           4 :       if (!answer) {
     300           1 :         const char *name = option_get_canonical_name(q);
     301           1 :         control_reply_add_one_kv(answers, 250, KV_OMIT_VALS, name, "");
     302             :       }
     303             : 
     304           7 :       while (answer) {
     305           3 :         config_line_t *next;
     306           3 :         control_reply_add_one_kv(answers, 250, KV_RAW, answer->key,
     307           3 :                                  answer->value);
     308           3 :         next = answer->next;
     309           3 :         tor_free(answer->key);
     310           3 :         tor_free(answer->value);
     311           3 :         tor_free(answer);
     312           3 :         answer = next;
     313             :       }
     314             :     }
     315           8 :   } SMARTLIST_FOREACH_END(q);
     316             : 
     317           6 :   if (smartlist_len(unrecognized)) {
     318           3 :     control_write_reply_lines(conn, unrecognized);
     319           3 :   } else if (smartlist_len(answers)) {
     320           2 :     control_write_reply_lines(conn, answers);
     321             :   } else {
     322           1 :     send_control_done(conn);
     323             :   }
     324             : 
     325           6 :   control_reply_free(answers);
     326           6 :   control_reply_free(unrecognized);
     327           6 :   return 0;
     328             : }
     329             : 
     330             : static const control_cmd_syntax_t loadconf_syntax = {
     331             :   .want_cmddata = true
     332             : };
     333             : 
     334             : /** Called when we get a +LOADCONF message. */
     335             : static int
     336           0 : handle_control_loadconf(control_connection_t *conn,
     337             :                         const control_cmd_args_t *args)
     338             : {
     339           0 :   setopt_err_t retval;
     340           0 :   char *errstring = NULL;
     341             : 
     342           0 :   retval = options_init_from_string(NULL, args->cmddata,
     343             :                                     CMD_RUN_TOR, NULL, &errstring);
     344             : 
     345           0 :   if (retval != SETOPT_OK)
     346           0 :     log_warn(LD_CONTROL,
     347             :              "Controller gave us config file that didn't validate: %s",
     348             :              errstring);
     349             : 
     350             : #define SEND_ERRMSG(code, msg)                          \
     351             :   control_printf_endreply(conn, code, msg "%s%s",       \
     352             :                           errstring ? ": " : "",        \
     353             :                           errstring ? errstring : "")
     354           0 :   switch (retval) {
     355           0 :   case SETOPT_ERR_PARSE:
     356           0 :     SEND_ERRMSG(552, "Invalid config file");
     357           0 :     break;
     358           0 :   case SETOPT_ERR_TRANSITION:
     359           0 :     SEND_ERRMSG(553, "Transition not allowed");
     360           0 :     break;
     361           0 :   case SETOPT_ERR_SETTING:
     362           0 :     SEND_ERRMSG(553, "Unable to set option");
     363           0 :     break;
     364           0 :   case SETOPT_ERR_MISC:
     365             :   default:
     366           0 :     SEND_ERRMSG(550, "Unable to load config");
     367           0 :     break;
     368           0 :   case SETOPT_OK:
     369           0 :     send_control_done(conn);
     370           0 :     break;
     371             :   }
     372             : #undef SEND_ERRMSG
     373           0 :   tor_free(errstring);
     374           0 :   return 0;
     375             : }
     376             : 
     377             : static const control_cmd_syntax_t setevents_syntax = {
     378             :   .max_args = UINT_MAX
     379             : };
     380             : 
     381             : /** Called when we get a SETEVENTS message: update conn->event_mask,
     382             :  * and reply with DONE or ERROR. */
     383             : static int
     384           0 : handle_control_setevents(control_connection_t *conn,
     385             :                          const control_cmd_args_t *args)
     386             : {
     387           0 :   int event_code;
     388           0 :   event_mask_t event_mask = 0;
     389           0 :   const smartlist_t *events = args->args;
     390             : 
     391           0 :   SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
     392             :     {
     393           0 :       if (!strcasecmp(ev, "EXTENDED") ||
     394           0 :           !strcasecmp(ev, "AUTHDIR_NEWDESCS")) {
     395           0 :         log_warn(LD_CONTROL, "The \"%s\" SETEVENTS argument is no longer "
     396             :                  "supported.", ev);
     397           0 :         continue;
     398             :       } else {
     399             :         int i;
     400           0 :         event_code = -1;
     401             : 
     402           0 :         for (i = 0; control_event_table[i].event_name != NULL; ++i) {
     403           0 :           if (!strcasecmp(ev, control_event_table[i].event_name)) {
     404           0 :             event_code = control_event_table[i].event_code;
     405           0 :             break;
     406             :           }
     407             :         }
     408             : 
     409           0 :         if (event_code == -1) {
     410           0 :           control_printf_endreply(conn, 552, "Unrecognized event \"%s\"", ev);
     411           0 :           return 0;
     412             :         }
     413             :       }
     414           0 :       event_mask |= (((event_mask_t)1) << event_code);
     415             :     }
     416           0 :   SMARTLIST_FOREACH_END(ev);
     417             : 
     418           0 :   conn->event_mask = event_mask;
     419             : 
     420           0 :   control_update_global_event_mask();
     421           0 :   send_control_done(conn);
     422           0 :   return 0;
     423             : }
     424             : 
     425             : static const control_cmd_syntax_t saveconf_syntax = {
     426             :   .max_args = 0,
     427             :   .accept_keywords = true,
     428             :   .kvline_flags=KV_OMIT_VALS,
     429             : };
     430             : 
     431             : /** Called when we get a SAVECONF command. Try to flush the current options to
     432             :  * disk, and report success or failure. */
     433             : static int
     434           0 : handle_control_saveconf(control_connection_t *conn,
     435             :                         const control_cmd_args_t *args)
     436             : {
     437           0 :   bool force = config_lines_contain_flag(args->kwargs, "FORCE");
     438           0 :   const or_options_t *options = get_options();
     439           0 :   if ((!force && options->IncludeUsed) || options_save_current() < 0) {
     440           0 :     control_write_endreply(conn, 551,
     441             :                            "Unable to write configuration to disk.");
     442             :   } else {
     443           0 :     send_control_done(conn);
     444             :   }
     445           0 :   return 0;
     446             : }
     447             : 
     448             : static const control_cmd_syntax_t signal_syntax = {
     449             :   .min_args = 1,
     450             :   .max_args = 1,
     451             : };
     452             : 
     453             : /** Called when we get a SIGNAL command. React to the provided signal, and
     454             :  * report success or failure. (If the signal results in a shutdown, success
     455             :  * may not be reported.) */
     456             : static int
     457           0 : handle_control_signal(control_connection_t *conn,
     458             :                       const control_cmd_args_t *args)
     459             : {
     460           0 :   int sig = -1;
     461           0 :   int i;
     462             : 
     463           0 :   tor_assert(smartlist_len(args->args) == 1);
     464           0 :   const char *s = smartlist_get(args->args, 0);
     465             : 
     466           0 :   for (i = 0; signal_table[i].signal_name != NULL; ++i) {
     467           0 :     if (!strcasecmp(s, signal_table[i].signal_name)) {
     468           0 :       sig = signal_table[i].sig;
     469           0 :       break;
     470             :     }
     471             :   }
     472             : 
     473           0 :   if (sig < 0)
     474           0 :     control_printf_endreply(conn, 552, "Unrecognized signal code \"%s\"", s);
     475           0 :   if (sig < 0)
     476             :     return 0;
     477             : 
     478           0 :   send_control_done(conn);
     479             :   /* Flush the "done" first if the signal might make us shut down. */
     480           0 :   if (sig == SIGTERM || sig == SIGINT)
     481           0 :     connection_flush(TO_CONN(conn));
     482             : 
     483           0 :   activate_signal(sig);
     484             : 
     485           0 :   return 0;
     486             : }
     487             : 
     488             : static const control_cmd_syntax_t takeownership_syntax = {
     489             :   .max_args = UINT_MAX, // This should probably become zero. XXXXX
     490             : };
     491             : 
     492             : /** Called when we get a TAKEOWNERSHIP command.  Mark this connection
     493             :  * as an owning connection, so that we will exit if the connection
     494             :  * closes. */
     495             : static int
     496           0 : handle_control_takeownership(control_connection_t *conn,
     497             :                              const control_cmd_args_t *args)
     498             : {
     499           0 :   (void)args;
     500             : 
     501           0 :   conn->is_owning_control_connection = 1;
     502             : 
     503           0 :   log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
     504             :            "Tor instance.",
     505             :            (int)(conn->base_.s));
     506             : 
     507           0 :   send_control_done(conn);
     508           0 :   return 0;
     509             : }
     510             : 
     511             : static const control_cmd_syntax_t dropownership_syntax = {
     512             :   .max_args = UINT_MAX, // This should probably become zero. XXXXX
     513             : };
     514             : 
     515             : /** Called when we get a DROPOWNERSHIP command.  Mark this connection
     516             :  * as a non-owning connection, so that we will not exit if the connection
     517             :  * closes. */
     518             : static int
     519           0 : handle_control_dropownership(control_connection_t *conn,
     520             :                              const control_cmd_args_t *args)
     521             : {
     522           0 :   (void)args;
     523             : 
     524           0 :   conn->is_owning_control_connection = 0;
     525             : 
     526           0 :   log_info(LD_CONTROL, "Control connection %d has dropped ownership of this "
     527             :            "Tor instance.",
     528             :            (int)(conn->base_.s));
     529             : 
     530           0 :   send_control_done(conn);
     531           0 :   return 0;
     532             : }
     533             : 
     534             : /** Given a text circuit <b>id</b>, return the corresponding circuit. */
     535             : static origin_circuit_t *
     536           0 : get_circ(const char *id)
     537             : {
     538           0 :   uint32_t n_id;
     539           0 :   int ok;
     540           0 :   n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL);
     541           0 :   if (!ok)
     542             :     return NULL;
     543           0 :   return circuit_get_by_global_id(n_id);
     544             : }
     545             : 
     546             : /** Given a text stream <b>id</b>, return the corresponding AP connection. */
     547             : static entry_connection_t *
     548           0 : get_stream(const char *id)
     549             : {
     550           0 :   uint64_t n_id;
     551           0 :   int ok;
     552           0 :   connection_t *conn;
     553           0 :   n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL);
     554           0 :   if (!ok)
     555             :     return NULL;
     556           0 :   conn = connection_get_by_global_id(n_id);
     557           0 :   if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
     558             :     return NULL;
     559           0 :   return TO_ENTRY_CONN(conn);
     560             : }
     561             : 
     562             : /** Helper for setconf and resetconf. Acts like setconf, except
     563             :  * it passes <b>use_defaults</b> on to options_trial_assign().  Modifies the
     564             :  * contents of body.
     565             :  */
     566             : static int
     567           0 : control_setconf_helper(control_connection_t *conn,
     568             :                        const control_cmd_args_t *args,
     569             :                        int use_defaults)
     570             : {
     571           0 :   setopt_err_t opt_err;
     572           0 :   char *errstring = NULL;
     573           0 :   const unsigned flags =
     574           0 :     CAL_CLEAR_FIRST | (use_defaults ? CAL_USE_DEFAULTS : 0);
     575             : 
     576             :   // We need a copy here, since confmgt.c wants to canonicalize cases.
     577           0 :   config_line_t *lines = config_lines_dup(args->kwargs);
     578             : 
     579           0 :   opt_err = options_trial_assign(lines, flags, &errstring);
     580             :   {
     581             : #define SEND_ERRMSG(code, msg)                                  \
     582             :     control_printf_endreply(conn, code, msg ": %s", errstring);
     583             : 
     584           0 :     switch (opt_err) {
     585           0 :       case SETOPT_ERR_MISC:
     586           0 :         SEND_ERRMSG(552, "Unrecognized option");
     587           0 :         break;
     588           0 :       case SETOPT_ERR_PARSE:
     589           0 :         SEND_ERRMSG(513, "Unacceptable option value");
     590           0 :         break;
     591           0 :       case SETOPT_ERR_TRANSITION:
     592           0 :         SEND_ERRMSG(553, "Transition not allowed");
     593           0 :         break;
     594           0 :       case SETOPT_ERR_SETTING:
     595             :       default:
     596           0 :         SEND_ERRMSG(553, "Unable to set option");
     597           0 :         break;
     598           0 :       case SETOPT_OK:
     599           0 :         config_free_lines(lines);
     600           0 :         send_control_done(conn);
     601           0 :         return 0;
     602             :     }
     603             : #undef SEND_ERRMSG
     604           0 :     log_warn(LD_CONTROL,
     605             :              "Controller gave us config lines that didn't validate: %s",
     606             :              errstring);
     607           0 :     config_free_lines(lines);
     608           0 :     tor_free(errstring);
     609           0 :     return 0;
     610             :   }
     611             : }
     612             : 
     613             : /** Return true iff <b>addr</b> is unusable as a mapaddress target because of
     614             :  * containing funny characters. */
     615             : static int
     616           0 : address_is_invalid_mapaddress_target(const char *addr)
     617             : {
     618           0 :   if (!strcmpstart(addr, "*."))
     619           0 :     return address_is_invalid_destination(addr+2, 1);
     620             :   else
     621           0 :     return address_is_invalid_destination(addr, 1);
     622             : }
     623             : 
     624             : static const control_cmd_syntax_t mapaddress_syntax = {
     625             :   // no positional arguments are expected
     626             :   .max_args=0,
     627             :   // an arbitrary number of K=V entries are supported.
     628             :   .accept_keywords=true,
     629             : };
     630             : 
     631             : /** Called when we get a MAPADDRESS command; try to bind all listed addresses,
     632             :  * and report success or failure. */
     633             : static int
     634           0 : handle_control_mapaddress(control_connection_t *conn,
     635             :                           const control_cmd_args_t *args)
     636             : {
     637           0 :   smartlist_t *reply;
     638           0 :   char *r;
     639           0 :   size_t sz;
     640             : 
     641           0 :   reply = smartlist_new();
     642           0 :   const config_line_t *line;
     643           0 :   for (line = args->kwargs; line; line = line->next) {
     644           0 :     const char *from = line->key;
     645           0 :     const char *to = line->value;
     646             :     {
     647           0 :       if (address_is_invalid_mapaddress_target(to)) {
     648           0 :         smartlist_add_asprintf(reply,
     649             :                      "512-syntax error: invalid address '%s'", to);
     650           0 :         log_warn(LD_CONTROL,
     651             :                  "Skipping invalid argument '%s' in MapAddress msg", to);
     652           0 :       } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0") ||
     653           0 :                  !strcmp(from, "::")) {
     654           0 :         const char type =
     655             :           !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME :
     656           0 :           (!strcmp(from, "0.0.0.0") ? RESOLVED_TYPE_IPV4 : RESOLVED_TYPE_IPV6);
     657           0 :         const char *address = addressmap_register_virtual_address(
     658             :                                                      type, tor_strdup(to));
     659           0 :         if (!address) {
     660           0 :           smartlist_add_asprintf(reply,
     661             :                    "451-resource exhausted: skipping '%s=%s'", from,to);
     662           0 :           log_warn(LD_CONTROL,
     663             :                    "Unable to allocate address for '%s' in MapAddress msg",
     664             :                    safe_str_client(to));
     665             :         } else {
     666           0 :           smartlist_add_asprintf(reply, "250-%s=%s", address, to);
     667             :         }
     668             :       } else {
     669           0 :         const char *msg;
     670           0 :         if (addressmap_register_auto(from, to, 1,
     671             :                                      ADDRMAPSRC_CONTROLLER, &msg) < 0) {
     672           0 :           smartlist_add_asprintf(reply,
     673             :                                  "512-syntax error: invalid address mapping "
     674             :                                  " '%s=%s': %s", from, to, msg);
     675           0 :           log_warn(LD_CONTROL,
     676             :                    "Skipping invalid argument '%s=%s' in MapAddress msg: %s",
     677             :                    from, to, msg);
     678             :         } else {
     679           0 :           smartlist_add_asprintf(reply, "250-%s=%s", from, to);
     680             :         }
     681             :       }
     682             :     }
     683             :   }
     684             : 
     685           0 :   if (smartlist_len(reply)) {
     686           0 :     ((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' ';
     687           0 :     r = smartlist_join_strings(reply, "\r\n", 1, &sz);
     688           0 :     connection_buf_add(r, sz, TO_CONN(conn));
     689           0 :     tor_free(r);
     690             :   } else {
     691           0 :     control_write_endreply(conn, 512, "syntax error: "
     692             :                            "not enough arguments to mapaddress.");
     693             :   }
     694             : 
     695           0 :   SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp));
     696           0 :   smartlist_free(reply);
     697           0 :   return 0;
     698             : }
     699             : 
     700             : /** Given a string, convert it to a circuit purpose. */
     701             : static uint8_t
     702           0 : circuit_purpose_from_string(const char *string)
     703             : {
     704           0 :   if (!strcasecmpstart(string, "purpose="))
     705           0 :     string += strlen("purpose=");
     706             : 
     707           0 :   if (!strcasecmp(string, "general"))
     708             :     return CIRCUIT_PURPOSE_C_GENERAL;
     709           0 :   else if (!strcasecmp(string, "controller"))
     710             :     return CIRCUIT_PURPOSE_CONTROLLER;
     711             :   else
     712           0 :     return CIRCUIT_PURPOSE_UNKNOWN;
     713             : }
     714             : 
     715             : static const control_cmd_syntax_t extendcircuit_syntax = {
     716             :   .min_args=1,
     717             :   .max_args=1, // see note in function
     718             :   .accept_keywords=true,
     719             :   .kvline_flags=KV_OMIT_VALS
     720             : };
     721             : 
     722             : /** Called when we get an EXTENDCIRCUIT message.  Try to extend the listed
     723             :  * circuit, and report success or failure. */
     724             : static int
     725           0 : handle_control_extendcircuit(control_connection_t *conn,
     726             :                              const control_cmd_args_t *args)
     727             : {
     728           0 :   smartlist_t *router_nicknames=smartlist_new(), *nodes=NULL;
     729           0 :   origin_circuit_t *circ = NULL;
     730           0 :   uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
     731           0 :   const config_line_t *kwargs = args->kwargs;
     732           0 :   const char *circ_id = smartlist_get(args->args, 0);
     733           0 :   const char *path_str = NULL;
     734           0 :   char *path_str_alloc = NULL;
     735             : 
     736             :   /* The syntax for this command is unfortunate. The second argument is
     737             :      optional, and is a comma-separated list long-format fingerprints, which
     738             :      can (historically!) contain an equals sign.
     739             : 
     740             :      Here we check the second argument to see if it's a path, and if so we
     741             :      remove it from the kwargs list and put it in path_str.
     742             :   */
     743           0 :   if (kwargs) {
     744           0 :     const config_line_t *arg1 = kwargs;
     745           0 :     if (!strcmp(arg1->value, "")) {
     746           0 :       path_str = arg1->key;
     747           0 :       kwargs = kwargs->next;
     748           0 :     } else if (arg1->key[0] == '$') {
     749           0 :       tor_asprintf(&path_str_alloc, "%s=%s", arg1->key, arg1->value);
     750           0 :       path_str = path_str_alloc;
     751           0 :       kwargs = kwargs->next;
     752             :     }
     753             :   }
     754             : 
     755           0 :   const config_line_t *purpose_line = config_line_find_case(kwargs, "PURPOSE");
     756           0 :   bool zero_circ = !strcmp("0", circ_id);
     757             : 
     758           0 :   if (purpose_line) {
     759           0 :     intended_purpose = circuit_purpose_from_string(purpose_line->value);
     760           0 :     if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
     761           0 :       control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
     762           0 :                               purpose_line->value);
     763           0 :       goto done;
     764             :     }
     765             :   }
     766             : 
     767           0 :   if (zero_circ) {
     768           0 :     if (!path_str) {
     769             :       // "EXTENDCIRCUIT 0" with no path.
     770           0 :       circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
     771           0 :       if (!circ) {
     772           0 :         control_write_endreply(conn, 551, "Couldn't start circuit");
     773             :       } else {
     774           0 :         control_printf_endreply(conn, 250, "EXTENDED %lu",
     775           0 :                                 (unsigned long)circ->global_identifier);
     776             :       }
     777           0 :       goto done;
     778             :     }
     779             :   }
     780             : 
     781           0 :   if (!zero_circ && !(circ = get_circ(circ_id))) {
     782           0 :     control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
     783           0 :     goto done;
     784             :   }
     785             : 
     786           0 :   if (!path_str) {
     787           0 :     control_write_endreply(conn, 512, "syntax error: path required.");
     788           0 :     goto done;
     789             :   }
     790             : 
     791           0 :   smartlist_split_string(router_nicknames, path_str, ",", 0, 0);
     792             : 
     793           0 :   nodes = smartlist_new();
     794           0 :   bool first_node = zero_circ;
     795           0 :   SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
     796           0 :     const node_t *node = node_get_by_nickname(n, 0);
     797           0 :     if (!node) {
     798           0 :       control_printf_endreply(conn, 552, "No such router \"%s\"", n);
     799           0 :       goto done;
     800             :     }
     801           0 :     if (!node_has_preferred_descriptor(node, first_node)) {
     802           0 :       control_printf_endreply(conn, 552, "No descriptor for \"%s\"", n);
     803           0 :       goto done;
     804             :     }
     805           0 :     smartlist_add(nodes, (void*)node);
     806           0 :     first_node = false;
     807           0 :   } SMARTLIST_FOREACH_END(n);
     808             : 
     809           0 :   if (!smartlist_len(nodes)) {
     810           0 :     control_write_endreply(conn, 512, "No router names provided");
     811           0 :     goto done;
     812             :   }
     813             : 
     814           0 :   if (zero_circ) {
     815             :     /* start a new circuit */
     816           0 :     circ = origin_circuit_init(intended_purpose, 0);
     817           0 :     circ->first_hop_from_controller = 1;
     818             :   }
     819             : 
     820             :   /* now circ refers to something that is ready to be extended */
     821           0 :   first_node = zero_circ;
     822           0 :   SMARTLIST_FOREACH(nodes, const node_t *, node,
     823             :   {
     824             :     extend_info_t *info = extend_info_from_node(node, first_node);
     825             :     if (!info) {
     826             :       tor_assert_nonfatal(first_node);
     827             :       log_warn(LD_CONTROL,
     828             :                "controller tried to connect to a node that lacks a suitable "
     829             :                "descriptor, or which doesn't have any "
     830             :                "addresses that are allowed by the firewall configuration; "
     831             :                "circuit marked for closing.");
     832             :       circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
     833             :       control_write_endreply(conn, 551, "Couldn't start circuit");
     834             :       goto done;
     835             :     }
     836             :     circuit_append_new_exit(circ, info);
     837             :     if (circ->build_state->desired_path_len > 1) {
     838             :       circ->build_state->onehop_tunnel = 0;
     839             :     }
     840             :     extend_info_free(info);
     841             :     first_node = 0;
     842             :   });
     843             : 
     844             :   /* now that we've populated the cpath, start extending */
     845           0 :   if (zero_circ) {
     846           0 :     int err_reason = 0;
     847           0 :     if ((err_reason = circuit_handle_first_hop(circ)) < 0) {
     848           0 :       circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
     849           0 :       control_write_endreply(conn, 551, "Couldn't start circuit");
     850           0 :       goto done;
     851             :     }
     852             :   } else {
     853           0 :     if (circ->base_.state == CIRCUIT_STATE_OPEN ||
     854             :         circ->base_.state == CIRCUIT_STATE_GUARD_WAIT) {
     855           0 :       int err_reason = 0;
     856           0 :       circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
     857           0 :       if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) {
     858           0 :         log_info(LD_CONTROL,
     859             :                  "send_next_onion_skin failed; circuit marked for closing.");
     860           0 :         circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
     861           0 :         control_write_endreply(conn, 551, "Couldn't send onion skin");
     862           0 :         goto done;
     863             :       }
     864             :     }
     865             :   }
     866             : 
     867           0 :   control_printf_endreply(conn, 250, "EXTENDED %lu",
     868           0 :                           (unsigned long)circ->global_identifier);
     869           0 :   if (zero_circ) /* send a 'launched' event, for completeness */
     870           0 :     circuit_event_status(circ, CIRC_EVENT_LAUNCHED, 0);
     871           0 :  done:
     872           0 :   SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
     873           0 :   smartlist_free(router_nicknames);
     874           0 :   smartlist_free(nodes);
     875           0 :   tor_free(path_str_alloc);
     876           0 :   return 0;
     877             : }
     878             : 
     879             : static const control_cmd_syntax_t setcircuitpurpose_syntax = {
     880             :   .max_args=1,
     881             :   .accept_keywords=true,
     882             : };
     883             : 
     884             : /** Called when we get a SETCIRCUITPURPOSE message. If we can find the
     885             :  * circuit and it's a valid purpose, change it. */
     886             : static int
     887           0 : handle_control_setcircuitpurpose(control_connection_t *conn,
     888             :                                  const control_cmd_args_t *args)
     889             : {
     890           0 :   origin_circuit_t *circ = NULL;
     891           0 :   uint8_t new_purpose;
     892           0 :   const char *circ_id = smartlist_get(args->args,0);
     893             : 
     894           0 :   if (!(circ = get_circ(circ_id))) {
     895           0 :     control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
     896           0 :     goto done;
     897             :   }
     898             : 
     899             :   {
     900           0 :     const config_line_t *purp = config_line_find_case(args->kwargs, "PURPOSE");
     901           0 :     if (!purp) {
     902           0 :       control_write_endreply(conn, 552, "No purpose given");
     903           0 :       goto done;
     904             :     }
     905           0 :     new_purpose = circuit_purpose_from_string(purp->value);
     906           0 :     if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
     907           0 :       control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
     908           0 :                               purp->value);
     909           0 :       goto done;
     910             :     }
     911             :   }
     912             : 
     913           0 :   circuit_change_purpose(TO_CIRCUIT(circ), new_purpose);
     914           0 :   send_control_done(conn);
     915             : 
     916           0 :  done:
     917           0 :   return 0;
     918             : }
     919             : 
     920             : static const char *attachstream_keywords[] = {
     921             :   "HOP", NULL
     922             : };
     923             : static const control_cmd_syntax_t attachstream_syntax = {
     924             :   .min_args=2, .max_args=2,
     925             :   .accept_keywords=true,
     926             :   .allowed_keywords=attachstream_keywords
     927             : };
     928             : 
     929             : /** Called when we get an ATTACHSTREAM message.  Try to attach the requested
     930             :  * stream, and report success or failure. */
     931             : static int
     932           0 : handle_control_attachstream(control_connection_t *conn,
     933             :                             const control_cmd_args_t *args)
     934             : {
     935           0 :   entry_connection_t *ap_conn = NULL;
     936           0 :   origin_circuit_t *circ = NULL;
     937           0 :   crypt_path_t *cpath=NULL;
     938           0 :   int hop=0, hop_line_ok=1;
     939           0 :   const char *stream_id = smartlist_get(args->args, 0);
     940           0 :   const char *circ_id = smartlist_get(args->args, 1);
     941           0 :   int zero_circ = !strcmp(circ_id, "0");
     942           0 :   const config_line_t *hoparg = config_line_find_case(args->kwargs, "HOP");
     943             : 
     944           0 :   if (!(ap_conn = get_stream(stream_id))) {
     945           0 :     control_printf_endreply(conn, 552, "Unknown stream \"%s\"", stream_id);
     946           0 :     return 0;
     947           0 :   } else if (!zero_circ && !(circ = get_circ(circ_id))) {
     948           0 :     control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
     949           0 :     return 0;
     950           0 :   } else if (circ) {
     951           0 :     if (hoparg) {
     952           0 :       hop = (int) tor_parse_ulong(hoparg->value, 10, 0, INT_MAX,
     953             :                                   &hop_line_ok, NULL);
     954           0 :       if (!hop_line_ok) { /* broken hop line */
     955           0 :         control_printf_endreply(conn, 552, "Bad value hop=%s",
     956           0 :                                 hoparg->value);
     957           0 :         return 0;
     958             :       }
     959             :     }
     960             :   }
     961             : 
     962           0 :   if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT &&
     963           0 :       ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT &&
     964             :       ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) {
     965           0 :     control_write_endreply(conn, 555,
     966             :                            "Connection is not managed by controller.");
     967           0 :     return 0;
     968             :   }
     969             : 
     970             :   /* Do we need to detach it first? */
     971           0 :   if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) {
     972           0 :     edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
     973           0 :     circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn);
     974           0 :     connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT);
     975             :     /* Un-mark it as ending, since we're going to reuse it. */
     976           0 :     edge_conn->edge_has_sent_end = 0;
     977           0 :     edge_conn->end_reason = 0;
     978           0 :     if (tmpcirc)
     979           0 :       circuit_detach_stream(tmpcirc, edge_conn);
     980           0 :     connection_entry_set_controller_wait(ap_conn);
     981             :   }
     982             : 
     983           0 :   if (circ && (circ->base_.state != CIRCUIT_STATE_OPEN)) {
     984           0 :     control_write_endreply(conn, 551,
     985             :                            "Can't attach stream to non-open origin circuit");
     986           0 :     return 0;
     987             :   }
     988             :   /* Is this a single hop circuit? */
     989           0 :   if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
     990           0 :     control_write_endreply(conn, 551,
     991             :                            "Can't attach stream to this one-hop circuit.");
     992           0 :     return 0;
     993             :   }
     994             : 
     995           0 :   if (circ && hop>0) {
     996             :     /* find this hop in the circuit, and set cpath */
     997           0 :     cpath = circuit_get_cpath_hop(circ, hop);
     998           0 :     if (!cpath) {
     999           0 :       control_printf_endreply(conn, 551, "Circuit doesn't have %d hops.", hop);
    1000           0 :       return 0;
    1001             :     }
    1002             :   }
    1003           0 :   if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) {
    1004           0 :     control_write_endreply(conn, 551, "Unable to attach stream");
    1005           0 :     return 0;
    1006             :   }
    1007           0 :   send_control_done(conn);
    1008           0 :   return 0;
    1009             : }
    1010             : 
    1011             : static const char *postdescriptor_keywords[] = {
    1012             :   "cache", "purpose", NULL,
    1013             : };
    1014             : 
    1015             : static const control_cmd_syntax_t postdescriptor_syntax = {
    1016             :   .max_args = 0,
    1017             :   .accept_keywords = true,
    1018             :   .allowed_keywords = postdescriptor_keywords,
    1019             :   .want_cmddata = true,
    1020             : };
    1021             : 
    1022             : /** Called when we get a POSTDESCRIPTOR message.  Try to learn the provided
    1023             :  * descriptor, and report success or failure. */
    1024             : static int
    1025           0 : handle_control_postdescriptor(control_connection_t *conn,
    1026             :                               const control_cmd_args_t *args)
    1027             : {
    1028           0 :   const char *msg=NULL;
    1029           0 :   uint8_t purpose = ROUTER_PURPOSE_GENERAL;
    1030           0 :   int cache = 0; /* eventually, we may switch this to 1 */
    1031           0 :   const config_line_t *line;
    1032             : 
    1033           0 :   line = config_line_find_case(args->kwargs, "purpose");
    1034           0 :   if (line) {
    1035           0 :     purpose = router_purpose_from_string(line->value);
    1036           0 :     if (purpose == ROUTER_PURPOSE_UNKNOWN) {
    1037           0 :       control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
    1038           0 :                               line->value);
    1039           0 :       goto done;
    1040             :     }
    1041             :   }
    1042           0 :   line = config_line_find_case(args->kwargs, "cache");
    1043           0 :   if (line) {
    1044           0 :     if (!strcasecmp(line->value, "no"))
    1045             :       cache = 0;
    1046           0 :     else if (!strcasecmp(line->value, "yes"))
    1047             :       cache = 1;
    1048             :     else {
    1049           0 :       control_printf_endreply(conn, 552, "Unknown cache request \"%s\"",
    1050             :                               line->value);
    1051           0 :       goto done;
    1052             :     }
    1053             :   }
    1054             : 
    1055           0 :   switch (router_load_single_router(args->cmddata, purpose, cache, &msg)) {
    1056           0 :   case -1:
    1057           0 :     if (!msg) msg = "Could not parse descriptor";
    1058           0 :     control_write_endreply(conn, 554, msg);
    1059           0 :     break;
    1060           0 :   case 0:
    1061           0 :     if (!msg) msg = "Descriptor not added";
    1062           0 :     control_write_endreply(conn, 251, msg);
    1063           0 :     break;
    1064           0 :   case 1:
    1065           0 :     send_control_done(conn);
    1066           0 :     break;
    1067             :   }
    1068             : 
    1069           0 :  done:
    1070           0 :   return 0;
    1071             : }
    1072             : 
    1073             : static const control_cmd_syntax_t redirectstream_syntax = {
    1074             :   .min_args = 2,
    1075             :   .max_args = UINT_MAX, // XXX should be 3.
    1076             : };
    1077             : 
    1078             : /** Called when we receive a REDIRECTSTERAM command.  Try to change the target
    1079             :  * address of the named AP stream, and report success or failure. */
    1080             : static int
    1081           0 : handle_control_redirectstream(control_connection_t *conn,
    1082             :                               const control_cmd_args_t *cmd_args)
    1083             : {
    1084           0 :   entry_connection_t *ap_conn = NULL;
    1085           0 :   char *new_addr = NULL;
    1086           0 :   uint16_t new_port = 0;
    1087           0 :   const smartlist_t *args = cmd_args->args;
    1088             : 
    1089           0 :   if (!(ap_conn = get_stream(smartlist_get(args, 0)))
    1090           0 :            || !ap_conn->socks_request) {
    1091           0 :     control_printf_endreply(conn, 552, "Unknown stream \"%s\"",
    1092           0 :                             (char*)smartlist_get(args, 0));
    1093             :   } else {
    1094           0 :     int ok = 1;
    1095           0 :     if (smartlist_len(args) > 2) { /* they included a port too */
    1096           0 :       new_port = (uint16_t) tor_parse_ulong(smartlist_get(args, 2),
    1097             :                                             10, 1, 65535, &ok, NULL);
    1098             :     }
    1099           0 :     if (!ok) {
    1100           0 :       control_printf_endreply(conn, 512, "Cannot parse port \"%s\"",
    1101           0 :                               (char*)smartlist_get(args, 2));
    1102             :     } else {
    1103           0 :       new_addr = tor_strdup(smartlist_get(args, 1));
    1104             :     }
    1105             :   }
    1106             : 
    1107           0 :   if (!new_addr)
    1108           0 :     return 0;
    1109             : 
    1110           0 :   strlcpy(ap_conn->socks_request->address, new_addr,
    1111             :           sizeof(ap_conn->socks_request->address));
    1112           0 :   if (new_port)
    1113           0 :     ap_conn->socks_request->port = new_port;
    1114           0 :   tor_free(new_addr);
    1115           0 :   send_control_done(conn);
    1116           0 :   return 0;
    1117             : }
    1118             : 
    1119             : static const control_cmd_syntax_t closestream_syntax = {
    1120             :   .min_args = 2,
    1121             :   .max_args = UINT_MAX, /* XXXX This is the original behavior, but
    1122             :                          * maybe we should change the spec. */
    1123             : };
    1124             : 
    1125             : /** Called when we get a CLOSESTREAM command; try to close the named stream
    1126             :  * and report success or failure. */
    1127             : static int
    1128           0 : handle_control_closestream(control_connection_t *conn,
    1129             :                            const control_cmd_args_t *cmd_args)
    1130             : {
    1131           0 :   entry_connection_t *ap_conn=NULL;
    1132           0 :   uint8_t reason=0;
    1133           0 :   int ok;
    1134           0 :   const smartlist_t *args = cmd_args->args;
    1135             : 
    1136           0 :   tor_assert(smartlist_len(args) >= 2);
    1137             : 
    1138           0 :   if (!(ap_conn = get_stream(smartlist_get(args, 0))))
    1139           0 :     control_printf_endreply(conn, 552, "Unknown stream \"%s\"",
    1140           0 :                             (char*)smartlist_get(args, 0));
    1141             :   else {
    1142           0 :     reason = (uint8_t) tor_parse_ulong(smartlist_get(args,1), 10, 0, 255,
    1143             :                                        &ok, NULL);
    1144           0 :     if (!ok) {
    1145           0 :       control_printf_endreply(conn, 552, "Unrecognized reason \"%s\"",
    1146           0 :                               (char*)smartlist_get(args, 1));
    1147           0 :       ap_conn = NULL;
    1148             :     }
    1149             :   }
    1150           0 :   if (!ap_conn)
    1151           0 :     return 0;
    1152             : 
    1153           0 :   connection_mark_unattached_ap(ap_conn, reason);
    1154           0 :   send_control_done(conn);
    1155           0 :   return 0;
    1156             : }
    1157             : 
    1158             : static const control_cmd_syntax_t closecircuit_syntax = {
    1159             :   .min_args=1, .max_args=1,
    1160             :   .accept_keywords=true,
    1161             :   .kvline_flags=KV_OMIT_VALS,
    1162             :   // XXXX we might want to exclude unrecognized flags, but for now we
    1163             :   // XXXX just ignore them for backward compatibility.
    1164             : };
    1165             : 
    1166             : /** Called when we get a CLOSECIRCUIT command; try to close the named circuit
    1167             :  * and report success or failure. */
    1168             : static int
    1169           0 : handle_control_closecircuit(control_connection_t *conn,
    1170             :                             const control_cmd_args_t *args)
    1171             : {
    1172           0 :   const char *circ_id = smartlist_get(args->args, 0);
    1173           0 :   origin_circuit_t *circ = NULL;
    1174             : 
    1175           0 :   if (!(circ=get_circ(circ_id))) {
    1176           0 :     control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
    1177           0 :     return 0;
    1178             :   }
    1179             : 
    1180           0 :   bool safe =  config_lines_contain_flag(args->kwargs, "IfUnused");
    1181             : 
    1182           0 :   if (!safe || !circ->p_streams) {
    1183           0 :     circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_REQUESTED);
    1184             :   }
    1185             : 
    1186           0 :   send_control_done(conn);
    1187           0 :   return 0;
    1188             : }
    1189             : 
    1190             : static const control_cmd_syntax_t resolve_syntax = {
    1191             :   .max_args=0,
    1192             :   .accept_keywords=true,
    1193             :   .kvline_flags=KV_OMIT_VALS,
    1194             : };
    1195             : 
    1196             : /** Called when we get a RESOLVE command: start trying to resolve
    1197             :  * the listed addresses. */
    1198             : static int
    1199           0 : handle_control_resolve(control_connection_t *conn,
    1200             :                        const control_cmd_args_t *args)
    1201             : {
    1202           0 :   smartlist_t *failed;
    1203           0 :   int is_reverse = 0;
    1204             : 
    1205           0 :   if (!(conn->event_mask & (((event_mask_t)1)<<EVENT_ADDRMAP))) {
    1206           0 :     log_warn(LD_CONTROL, "Controller asked us to resolve an address, but "
    1207             :              "isn't listening for ADDRMAP events.  It probably won't see "
    1208             :              "the answer.");
    1209             :   }
    1210             : 
    1211             :   {
    1212           0 :     const config_line_t *modearg = config_line_find_case(args->kwargs, "mode");
    1213           0 :     if (modearg && !strcasecmp(modearg->value, "reverse"))
    1214           0 :       is_reverse = 1;
    1215             :   }
    1216           0 :   failed = smartlist_new();
    1217           0 :   for (const config_line_t *line = args->kwargs; line; line = line->next) {
    1218           0 :     if (!strlen(line->value)) {
    1219           0 :       const char *addr = line->key;
    1220           0 :       if (dnsserv_launch_request(addr, is_reverse, conn)<0)
    1221           0 :         smartlist_add(failed, (char*)addr);
    1222             :     } else {
    1223             :       // XXXX arguably we should reject unrecognized keyword arguments,
    1224             :       // XXXX but the old implementation didn't do that.
    1225           0 :     }
    1226             :   }
    1227             : 
    1228           0 :   send_control_done(conn);
    1229           0 :   SMARTLIST_FOREACH(failed, const char *, arg, {
    1230             :       control_event_address_mapped(arg, arg, time(NULL),
    1231             :                                    "internal", 0, 0);
    1232             :   });
    1233             : 
    1234           0 :   smartlist_free(failed);
    1235           0 :   return 0;
    1236             : }
    1237             : 
    1238             : static const control_cmd_syntax_t protocolinfo_syntax = {
    1239             :   .max_args = UINT_MAX
    1240             : };
    1241             : 
    1242             : /** Return a comma-separated list of authentication methods for
    1243             :     handle_control_protocolinfo().  Caller must free this string. */
    1244             : static char *
    1245           0 : get_authmethods(const or_options_t *options)
    1246             : {
    1247           0 :   int cookies = options->CookieAuthentication;
    1248           0 :   char *methods;
    1249           0 :   int passwd = (options->HashedControlPassword != NULL ||
    1250           0 :                 options->HashedControlSessionPassword != NULL);
    1251           0 :   smartlist_t *mlist = smartlist_new();
    1252             : 
    1253           0 :   if (cookies) {
    1254           0 :     smartlist_add(mlist, (char*)"COOKIE");
    1255           0 :     smartlist_add(mlist, (char*)"SAFECOOKIE");
    1256             :   }
    1257           0 :   if (passwd)
    1258           0 :     smartlist_add(mlist, (char*)"HASHEDPASSWORD");
    1259           0 :   if (!cookies && !passwd)
    1260           0 :     smartlist_add(mlist, (char*)"NULL");
    1261           0 :   methods = smartlist_join_strings(mlist, ",", 0, NULL);
    1262           0 :   smartlist_free(mlist);
    1263             : 
    1264           0 :   return methods;
    1265             : }
    1266             : 
    1267             : /** Return escaped cookie filename.  Caller must free this string.
    1268             :     Return NULL if cookie authentication is disabled. */
    1269             : static char *
    1270           0 : get_esc_cfile(const or_options_t *options)
    1271             : {
    1272           0 :   char *cfile = NULL, *abs_cfile = NULL, *esc_cfile = NULL;
    1273             : 
    1274           0 :   if (!options->CookieAuthentication)
    1275             :     return NULL;
    1276             : 
    1277           0 :   cfile = get_controller_cookie_file_name();
    1278           0 :   abs_cfile = make_path_absolute(cfile);
    1279           0 :   esc_cfile = esc_for_log(abs_cfile);
    1280           0 :   tor_free(cfile);
    1281           0 :   tor_free(abs_cfile);
    1282           0 :   return esc_cfile;
    1283             : }
    1284             : 
    1285             : /** Compose the auth methods line of a PROTOCOLINFO reply. */
    1286             : static void
    1287           0 : add_authmethods(smartlist_t *reply)
    1288             : {
    1289           0 :   const or_options_t *options = get_options();
    1290           0 :   char *methods = get_authmethods(options);
    1291           0 :   char *esc_cfile = get_esc_cfile(options);
    1292             : 
    1293           0 :   control_reply_add_str(reply, 250, "AUTH");
    1294           0 :   control_reply_append_kv(reply, "METHODS", methods);
    1295           0 :   if (esc_cfile)
    1296           0 :     control_reply_append_kv(reply, "COOKIEFILE", esc_cfile);
    1297             : 
    1298           0 :   tor_free(methods);
    1299           0 :   tor_free(esc_cfile);
    1300           0 : }
    1301             : 
    1302             : /** Called when we get a PROTOCOLINFO command: send back a reply. */
    1303             : static int
    1304           0 : handle_control_protocolinfo(control_connection_t *conn,
    1305             :                             const control_cmd_args_t *cmd_args)
    1306             : {
    1307           0 :   const char *bad_arg = NULL;
    1308           0 :   const smartlist_t *args = cmd_args->args;
    1309           0 :   smartlist_t *reply = NULL;
    1310             : 
    1311           0 :   conn->have_sent_protocolinfo = 1;
    1312             : 
    1313           0 :   SMARTLIST_FOREACH(args, const char *, arg, {
    1314             :       int ok;
    1315             :       tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
    1316             :       if (!ok) {
    1317             :         bad_arg = arg;
    1318             :         break;
    1319             :       }
    1320             :     });
    1321           0 :   if (bad_arg) {
    1322           0 :     control_printf_endreply(conn, 513, "No such version %s",
    1323             :                             escaped(bad_arg));
    1324             :     /* Don't tolerate bad arguments when not authenticated. */
    1325           0 :     if (!STATE_IS_OPEN(TO_CONN(conn)->state))
    1326           0 :       connection_mark_for_close(TO_CONN(conn));
    1327           0 :     return 0;
    1328             :   }
    1329           0 :   reply = smartlist_new();
    1330           0 :   control_reply_add_str(reply, 250, "PROTOCOLINFO 1");
    1331           0 :   add_authmethods(reply);
    1332           0 :   control_reply_add_str(reply, 250, "VERSION");
    1333           0 :   control_reply_append_kv(reply, "Tor", escaped(VERSION));
    1334           0 :   control_reply_add_done(reply);
    1335             : 
    1336           0 :   control_write_reply_lines(conn, reply);
    1337           0 :   control_reply_free(reply);
    1338           0 :   return 0;
    1339             : }
    1340             : 
    1341             : static const control_cmd_syntax_t usefeature_syntax = {
    1342             :   .max_args = UINT_MAX
    1343             : };
    1344             : 
    1345             : /** Called when we get a USEFEATURE command: parse the feature list, and
    1346             :  * set up the control_connection's options properly. */
    1347             : static int
    1348           0 : handle_control_usefeature(control_connection_t *conn,
    1349             :                           const control_cmd_args_t *cmd_args)
    1350             : {
    1351           0 :   const smartlist_t *args = cmd_args->args;
    1352           0 :   int bad = 0;
    1353           0 :   SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
    1354           0 :       if (!strcasecmp(arg, "VERBOSE_NAMES"))
    1355             :         ;
    1356           0 :       else if (!strcasecmp(arg, "EXTENDED_EVENTS"))
    1357             :         ;
    1358             :       else {
    1359           0 :         control_printf_endreply(conn, 552, "Unrecognized feature \"%s\"",
    1360             :                                 arg);
    1361           0 :         bad = 1;
    1362           0 :         break;
    1363             :       }
    1364           0 :   } SMARTLIST_FOREACH_END(arg);
    1365             : 
    1366           0 :   if (!bad) {
    1367           0 :     send_control_done(conn);
    1368             :   }
    1369             : 
    1370           0 :   return 0;
    1371             : }
    1372             : 
    1373             : static const control_cmd_syntax_t dropguards_syntax = {
    1374             :   .max_args = 0,
    1375             : };
    1376             : 
    1377             : /** Implementation for the DROPGUARDS command. */
    1378             : static int
    1379           0 : handle_control_dropguards(control_connection_t *conn,
    1380             :                           const control_cmd_args_t *args)
    1381             : {
    1382           0 :   (void) args; /* We don't take arguments. */
    1383             : 
    1384           0 :   static int have_warned = 0;
    1385           0 :   if (! have_warned) {
    1386           0 :     log_warn(LD_CONTROL, "DROPGUARDS is dangerous; make sure you understand "
    1387             :              "the risks before using it. It may be removed in a future "
    1388             :              "version of Tor.");
    1389           0 :     have_warned = 1;
    1390             :   }
    1391             : 
    1392           0 :   remove_all_entry_guards();
    1393           0 :   send_control_done(conn);
    1394             : 
    1395           0 :   return 0;
    1396             : }
    1397             : 
    1398             : static const control_cmd_syntax_t droptimeouts_syntax = {
    1399             :   .max_args = 0,
    1400             : };
    1401             : 
    1402             : /** Implementation for the DROPTIMEOUTS command. */
    1403             : static int
    1404           0 : handle_control_droptimeouts(control_connection_t *conn,
    1405             :                           const control_cmd_args_t *args)
    1406             : {
    1407           0 :   (void) args; /* We don't take arguments. */
    1408             : 
    1409           0 :   static int have_warned = 0;
    1410           0 :   if (! have_warned) {
    1411           0 :     log_warn(LD_CONTROL, "DROPTIMEOUTS is dangerous; make sure you understand "
    1412             :              "the risks before using it. It may be removed in a future "
    1413             :              "version of Tor.");
    1414           0 :     have_warned = 1;
    1415             :   }
    1416             : 
    1417           0 :   circuit_build_times_reset(get_circuit_build_times_mutable());
    1418           0 :   send_control_done(conn);
    1419           0 :   or_state_mark_dirty(get_or_state(), 0);
    1420           0 :   cbt_control_event_buildtimeout_set(get_circuit_build_times(),
    1421             :                                      BUILDTIMEOUT_SET_EVENT_RESET);
    1422             : 
    1423           0 :   return 0;
    1424             : }
    1425             : 
    1426             : static const char *hsfetch_keywords[] = {
    1427             :   "SERVER", NULL,
    1428             : };
    1429             : static const control_cmd_syntax_t hsfetch_syntax = {
    1430             :   .min_args = 1, .max_args = 1,
    1431             :   .accept_keywords = true,
    1432             :   .allowed_keywords = hsfetch_keywords,
    1433             : };
    1434             : 
    1435             : /** Implementation for the HSFETCH command. */
    1436             : static int
    1437           0 : handle_control_hsfetch(control_connection_t *conn,
    1438             :                        const control_cmd_args_t *args)
    1439             : 
    1440             : {
    1441           0 :   smartlist_t *hsdirs = NULL;
    1442           0 :   ed25519_public_key_t v3_pk;
    1443           0 :   uint32_t version;
    1444           0 :   const char *hsaddress = NULL;
    1445             : 
    1446             :   /* Extract the first argument (either HSAddress or DescID). */
    1447           0 :   const char *arg1 = smartlist_get(args->args, 0);
    1448           0 :   if (hs_address_is_valid(arg1)) {
    1449           0 :     hsaddress = arg1;
    1450           0 :     version = HS_VERSION_THREE;
    1451           0 :     hs_parse_address(hsaddress, &v3_pk, NULL, NULL);
    1452             :   } else {
    1453           0 :     control_printf_endreply(conn, 513, "Invalid argument \"%s\"", arg1);
    1454           0 :     goto done;
    1455             :   }
    1456             : 
    1457           0 :   for (const config_line_t *line = args->kwargs; line; line = line->next) {
    1458           0 :     if (!strcasecmp(line->key, "SERVER")) {
    1459           0 :       const char *server = line->value;
    1460             : 
    1461           0 :       const node_t *node = node_get_by_hex_id(server, 0);
    1462           0 :       if (!node) {
    1463           0 :         control_printf_endreply(conn, 552, "Server \"%s\" not found", server);
    1464           0 :         goto done;
    1465             :       }
    1466           0 :       if (!hsdirs) {
    1467             :         /* Stores routerstatus_t cmddata for each specified server. */
    1468           0 :         hsdirs = smartlist_new();
    1469             :       }
    1470             :       /* Valid server, add it to our local list. */
    1471           0 :       smartlist_add(hsdirs, node->rs);
    1472             :     } else {
    1473           0 :       tor_assert_nonfatal_unreached();
    1474             :     }
    1475             :   }
    1476             : 
    1477             :   /* We are about to trigger HSDir fetch so send the OK now because after
    1478             :    * that 650 event(s) are possible so better to have the 250 OK before them
    1479             :    * to avoid out of order replies. */
    1480           0 :   send_control_done(conn);
    1481             : 
    1482             :   /* Trigger the fetch using the built rend query and possibly a list of HS
    1483             :    * directory to use. This function ignores the client cache thus this will
    1484             :    * always send a fetch command. */
    1485           0 :   if (version == HS_VERSION_THREE) {
    1486           0 :     hs_control_hsfetch_command(&v3_pk, hsdirs);
    1487             :   }
    1488             : 
    1489           0 :  done:
    1490             :   /* Contains data pointer that we don't own thus no cleanup. */
    1491           0 :   smartlist_free(hsdirs);
    1492           0 :   return 0;
    1493             : }
    1494             : 
    1495             : static const char *hspost_keywords[] = {
    1496             :   "SERVER", "HSADDRESS", NULL
    1497             : };
    1498             : static const control_cmd_syntax_t hspost_syntax = {
    1499             :   .min_args = 0, .max_args = 0,
    1500             :   .accept_keywords = true,
    1501             :   .want_cmddata = true,
    1502             :   .allowed_keywords = hspost_keywords
    1503             : };
    1504             : 
    1505             : /** Implementation for the HSPOST command. */
    1506             : static int
    1507           0 : handle_control_hspost(control_connection_t *conn,
    1508             :                       const control_cmd_args_t *args)
    1509             : {
    1510           0 :   smartlist_t *hs_dirs = NULL;
    1511           0 :   const char *encoded_desc = args->cmddata;
    1512           0 :   const char *onion_address = NULL;
    1513           0 :   const config_line_t *line;
    1514             : 
    1515           0 :   for (line = args->kwargs; line; line = line->next) {
    1516           0 :     if (!strcasecmpstart(line->key, "SERVER")) {
    1517           0 :       const char *server = line->value;
    1518           0 :       const node_t *node = node_get_by_hex_id(server, 0);
    1519             : 
    1520           0 :       if (!node || !node->rs) {
    1521           0 :         control_printf_endreply(conn, 552, "Server \"%s\" not found",
    1522             :                                 server);
    1523           0 :         goto done;
    1524             :       }
    1525             :       /* Valid server, add it to our local list. */
    1526           0 :       if (!hs_dirs)
    1527           0 :         hs_dirs = smartlist_new();
    1528           0 :       smartlist_add(hs_dirs, node->rs);
    1529           0 :     } else if (!strcasecmpstart(line->key, "HSADDRESS")) {
    1530           0 :       const char *address = line->value;
    1531           0 :       if (!hs_address_is_valid(address)) {
    1532           0 :         control_write_endreply(conn, 512, "Malformed onion address");
    1533           0 :         goto done;
    1534             :       }
    1535             :       onion_address = address;
    1536             :     } else {
    1537           0 :       tor_assert_nonfatal_unreached();
    1538             :     }
    1539             :   }
    1540             : 
    1541             :   /* Handle the v3 case. */
    1542           0 :   if (onion_address) {
    1543           0 :     if (hs_control_hspost_command(encoded_desc, onion_address, hs_dirs) < 0) {
    1544           0 :       control_write_endreply(conn, 554, "Invalid descriptor");
    1545             :     } else {
    1546           0 :       send_control_done(conn);
    1547             :     }
    1548           0 :     goto done;
    1549             :   }
    1550             : 
    1551           0 :  done:
    1552           0 :   smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
    1553           0 :   return 0;
    1554             : }
    1555             : 
    1556             : /* Helper function for ADD_ONION that adds an ephemeral service depending on
    1557             :  * the given hs_version.
    1558             :  *
    1559             :  * The secret key in pk depends on the hs_version. The ownership of the key
    1560             :  * used in pk is given to the HS subsystem so the caller must stop accessing
    1561             :  * it after.
    1562             :  *
    1563             :  * The port_cfgs is a list of service port. Ownership transferred to service.
    1564             :  * The max_streams refers to the MaxStreams= key.
    1565             :  * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
    1566             :  * The ownership of that list is transferred to the service.
    1567             :  *
    1568             :  * On success (RSAE_OKAY), the address_out points to a newly allocated string
    1569             :  * containing the onion address without the .onion part. On error, address_out
    1570             :  * is untouched. */
    1571             : STATIC hs_service_add_ephemeral_status_t
    1572           4 : add_onion_helper_add_service(int hs_version,
    1573             :                              add_onion_secret_key_t *pk,
    1574             :                              smartlist_t *port_cfgs, int max_streams,
    1575             :                              int max_streams_close_circuit,
    1576             :                              smartlist_t *auth_clients_v3, char **address_out)
    1577             : {
    1578           4 :   hs_service_add_ephemeral_status_t ret;
    1579             : 
    1580           4 :   tor_assert(pk);
    1581           4 :   tor_assert(port_cfgs);
    1582           4 :   tor_assert(address_out);
    1583             : 
    1584           4 :   switch (hs_version) {
    1585           4 :   case HS_VERSION_THREE:
    1586           4 :     ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
    1587             :                                    max_streams_close_circuit,
    1588             :                                    auth_clients_v3, address_out);
    1589           4 :     break;
    1590           0 :   default:
    1591           0 :     tor_assert_unreached();
    1592             :   }
    1593             : 
    1594           4 :   return ret;
    1595             : }
    1596             : 
    1597             : /** The list of onion services that have been added via ADD_ONION that do not
    1598             :  * belong to any particular control connection.
    1599             :  */
    1600             : static smartlist_t *detached_onion_services = NULL;
    1601             : 
    1602             : /**
    1603             :  * Return a list of detached onion services, or NULL if none exist.
    1604             :  **/
    1605             : smartlist_t *
    1606           1 : get_detached_onion_services(void)
    1607             : {
    1608           1 :   return detached_onion_services;
    1609             : }
    1610             : 
    1611             : static const char *add_onion_keywords[] = {
    1612             :    "Port", "Flags", "MaxStreams", "ClientAuth", "ClientAuthV3", NULL
    1613             : };
    1614             : static const control_cmd_syntax_t add_onion_syntax = {
    1615             :   .min_args = 1, .max_args = 1,
    1616             :   .accept_keywords = true,
    1617             :   .allowed_keywords = add_onion_keywords
    1618             : };
    1619             : 
    1620             : /** Called when we get a ADD_ONION command; parse the body, and set up
    1621             :  * the new ephemeral Onion Service. */
    1622             : static int
    1623           3 : handle_control_add_onion(control_connection_t *conn,
    1624             :                          const control_cmd_args_t *args)
    1625             : {
    1626             :   /* Parse all of the arguments that do not involve handling cryptographic
    1627             :    * material first, since there's no reason to touch that at all if any of
    1628             :    * the other arguments are malformed.
    1629             :    */
    1630           3 :   rend_auth_type_t auth_type = REND_NO_AUTH;
    1631           3 :   smartlist_t *port_cfgs = smartlist_new();
    1632           3 :   smartlist_t *auth_clients_v3 = NULL;
    1633           3 :   smartlist_t *auth_clients_v3_str = NULL;
    1634           3 :   int discard_pk = 0;
    1635           3 :   int detach = 0;
    1636           3 :   int max_streams = 0;
    1637           3 :   int max_streams_close_circuit = 0;
    1638           3 :   int non_anonymous = 0;
    1639           3 :   const config_line_t *arg;
    1640             : 
    1641           8 :   for (arg = args->kwargs; arg; arg = arg->next) {
    1642           6 :     if (!strcasecmp(arg->key, "Port")) {
    1643             :       /* "Port=VIRTPORT[,TARGET]". */
    1644           2 :       hs_port_config_t *cfg = hs_parse_port_config(arg->value, ",", NULL);
    1645           2 :       if (!cfg) {
    1646           0 :         control_write_endreply(conn, 512, "Invalid VIRTPORT/TARGET");
    1647           0 :         goto out;
    1648             :       }
    1649           2 :       smartlist_add(port_cfgs, cfg);
    1650           4 :     } else if (!strcasecmp(arg->key, "MaxStreams")) {
    1651             :       /* "MaxStreams=[0..65535]". */
    1652           0 :       int ok = 0;
    1653           0 :       max_streams = (int)tor_parse_long(arg->value, 10, 0, 65535, &ok, NULL);
    1654           0 :       if (!ok) {
    1655           0 :         control_write_endreply(conn, 512, "Invalid MaxStreams");
    1656           0 :         goto out;
    1657             :       }
    1658           4 :     } else if (!strcasecmp(arg->key, "Flags")) {
    1659             :       /* "Flags=Flag[,Flag]", where Flag can be:
    1660             :        *   * 'DiscardPK' - If tor generates the keypair, do not include it in
    1661             :        *                   the response.
    1662             :        *   * 'Detach' - Do not tie this onion service to any particular control
    1663             :        *                connection.
    1664             :        *   * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
    1665             :        *                                exceeded.
    1666             :        *   * 'BasicAuth' - Client authorization using the 'basic' method.
    1667             :        *   * 'NonAnonymous' - Add a non-anonymous Single Onion Service. If this
    1668             :        *                      flag is present, tor must be in non-anonymous
    1669             :        *                      hidden service mode. If this flag is absent,
    1670             :        *                      tor must be in anonymous hidden service mode.
    1671             :        */
    1672           2 :       static const char *discard_flag = "DiscardPK";
    1673           2 :       static const char *detach_flag = "Detach";
    1674           2 :       static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
    1675           2 :       static const char *v3auth_flag = "V3Auth";
    1676           2 :       static const char *non_anonymous_flag = "NonAnonymous";
    1677             : 
    1678           2 :       smartlist_t *flags = smartlist_new();
    1679           2 :       int bad = 0;
    1680             : 
    1681           2 :       smartlist_split_string(flags, arg->value, ",", SPLIT_IGNORE_BLANK, 0);
    1682           2 :       if (smartlist_len(flags) < 1) {
    1683           0 :         control_write_endreply(conn, 512, "Invalid 'Flags' argument");
    1684           0 :         bad = 1;
    1685             :       }
    1686           4 :       SMARTLIST_FOREACH_BEGIN(flags, const char *, flag)
    1687             :       {
    1688           2 :         if (!strcasecmp(flag, discard_flag)) {
    1689             :           discard_pk = 1;
    1690           1 :         } else if (!strcasecmp(flag, detach_flag)) {
    1691             :           detach = 1;
    1692           1 :         } else if (!strcasecmp(flag, max_s_close_flag)) {
    1693             :           max_streams_close_circuit = 1;
    1694           1 :         } else if (!strcasecmp(flag, v3auth_flag)) {
    1695             :           auth_type = REND_V3_AUTH;
    1696           0 :         } else if (!strcasecmp(flag, non_anonymous_flag)) {
    1697             :           non_anonymous = 1;
    1698             :         } else {
    1699           0 :           control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
    1700             :                                   escaped(flag));
    1701           0 :           bad = 1;
    1702           0 :           break;
    1703             :         }
    1704           2 :       } SMARTLIST_FOREACH_END(flag);
    1705           4 :       SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
    1706           2 :       smartlist_free(flags);
    1707           2 :       if (bad)
    1708           0 :         goto out;
    1709           2 :     } else if (!strcasecmp(arg->key, "ClientAuthV3")) {
    1710           2 :       hs_service_authorized_client_t *client_v3 =
    1711           2 :                              parse_authorized_client_key(arg->value, LOG_INFO);
    1712           2 :       if (!client_v3) {
    1713           1 :         control_write_endreply(conn, 512, "Cannot decode v3 client auth key");
    1714           1 :         goto out;
    1715             :       }
    1716             : 
    1717           1 :       if (auth_clients_v3 == NULL) {
    1718           1 :         auth_clients_v3 = smartlist_new();
    1719           1 :         auth_clients_v3_str = smartlist_new();
    1720             :       }
    1721             : 
    1722           1 :       smartlist_add(auth_clients_v3, client_v3);
    1723           1 :       smartlist_add(auth_clients_v3_str, tor_strdup(arg->value));
    1724             :     } else {
    1725           0 :       tor_assert_nonfatal_unreached();
    1726           0 :       goto out;
    1727             :     }
    1728             :   }
    1729           2 :   if (smartlist_len(port_cfgs) == 0) {
    1730           0 :     control_write_endreply(conn, 512, "Missing 'Port' argument");
    1731           0 :     goto out;
    1732           2 :   } else if (auth_type == REND_NO_AUTH && auth_clients_v3 != NULL) {
    1733           0 :     control_write_endreply(conn, 512, "No auth type specified");
    1734           0 :     goto out;
    1735           2 :   } else if (auth_type != REND_NO_AUTH && auth_clients_v3 == NULL) {
    1736           0 :     control_write_endreply(conn, 512, "No auth clients specified");
    1737           0 :     goto out;
    1738           2 :   } else if (non_anonymous != hs_service_non_anonymous_mode_enabled(
    1739             :                                                             get_options())) {
    1740             :     /* If we failed, and the non-anonymous flag is set, Tor must be in
    1741             :      * anonymous hidden service mode.
    1742             :      * The error message changes based on the current Tor config:
    1743             :      * 512 Tor is in anonymous hidden service mode
    1744             :      * 512 Tor is in non-anonymous hidden service mode
    1745             :      * (I've deliberately written them out in full here to aid searchability.)
    1746             :      */
    1747           0 :     control_printf_endreply(conn, 512,
    1748             :                             "Tor is in %sanonymous hidden service " "mode",
    1749             :                             non_anonymous ? "" : "non-");
    1750           0 :     goto out;
    1751             :   }
    1752             : 
    1753             :   /* Parse the "keytype:keyblob" argument. */
    1754           2 :   int hs_version = 0;
    1755           2 :   add_onion_secret_key_t pk = { NULL };
    1756           2 :   const char *key_new_alg = NULL;
    1757           2 :   char *key_new_blob = NULL;
    1758             : 
    1759           2 :   const char *onionkey = smartlist_get(args->args, 0);
    1760           2 :   if (add_onion_helper_keyarg(onionkey, discard_pk,
    1761             :                               &key_new_alg, &key_new_blob, &pk, &hs_version,
    1762             :                               conn) < 0) {
    1763           0 :     goto out;
    1764             :   }
    1765             : 
    1766             :   /* Create the HS, using private key pk and port config port_cfg.
    1767             :    * hs_service_add_ephemeral() will take ownership of pk and port_cfg,
    1768             :    * regardless of success/failure. */
    1769           2 :   char *service_id = NULL;
    1770           2 :   int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
    1771             :                                          max_streams,
    1772             :                                          max_streams_close_circuit,
    1773             :                                          auth_clients_v3, &service_id);
    1774           2 :   port_cfgs = NULL; /* port_cfgs is now owned by the hs_service code. */
    1775           2 :   auth_clients_v3 = NULL; /* so is auth_clients_v3 */
    1776           2 :   switch (ret) {
    1777           1 :   case RSAE_OKAY:
    1778             :   {
    1779           1 :     if (detach) {
    1780           0 :       if (!detached_onion_services)
    1781           0 :         detached_onion_services = smartlist_new();
    1782           0 :       smartlist_add(detached_onion_services, service_id);
    1783             :     } else {
    1784           1 :       if (!conn->ephemeral_onion_services)
    1785           1 :         conn->ephemeral_onion_services = smartlist_new();
    1786           1 :       smartlist_add(conn->ephemeral_onion_services, service_id);
    1787             :     }
    1788             : 
    1789           1 :     tor_assert(service_id);
    1790           1 :     control_printf_midreply(conn, 250, "ServiceID=%s", service_id);
    1791           1 :     if (key_new_alg) {
    1792           0 :       tor_assert(key_new_blob);
    1793           0 :       control_printf_midreply(conn, 250, "PrivateKey=%s:%s",
    1794             :                               key_new_alg, key_new_blob);
    1795             :     }
    1796           1 :     if (auth_clients_v3_str) {
    1797           2 :       SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str, {
    1798             :         control_printf_midreply(conn, 250, "ClientAuthV3=%s", client_str);
    1799             :       });
    1800             :     }
    1801             : 
    1802           1 :     send_control_done(conn);
    1803           1 :     break;
    1804             :   }
    1805           1 :   case RSAE_BADPRIVKEY:
    1806           1 :     control_write_endreply(conn, 551, "Failed to generate onion address");
    1807           1 :     break;
    1808           0 :   case RSAE_ADDREXISTS:
    1809           0 :     control_write_endreply(conn, 550, "Onion address collision");
    1810           0 :     break;
    1811           0 :   case RSAE_BADVIRTPORT:
    1812           0 :     control_write_endreply(conn, 512, "Invalid VIRTPORT/TARGET");
    1813           0 :     break;
    1814           0 :   case RSAE_BADAUTH:
    1815           0 :     control_write_endreply(conn, 512, "Invalid client authorization");
    1816           0 :     break;
    1817           0 :   case RSAE_INTERNAL: FALLTHROUGH;
    1818             :   default:
    1819           0 :     control_write_endreply(conn, 551, "Failed to add Onion Service");
    1820             :   }
    1821           2 :   if (key_new_blob) {
    1822           0 :     memwipe(key_new_blob, 0, strlen(key_new_blob));
    1823           0 :     tor_free(key_new_blob);
    1824             :   }
    1825             : 
    1826           2 :  out:
    1827           1 :   if (port_cfgs) {
    1828           1 :     SMARTLIST_FOREACH(port_cfgs, hs_port_config_t*, p,
    1829             :                       hs_port_config_free(p));
    1830           1 :     smartlist_free(port_cfgs);
    1831             :   }
    1832           3 :   if (auth_clients_v3) {
    1833           0 :     SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, ac,
    1834             :                       service_authorized_client_free(ac));
    1835           0 :     smartlist_free(auth_clients_v3);
    1836             :   }
    1837           3 :   if (auth_clients_v3_str) {
    1838           2 :     SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str,
    1839             :                       tor_free(client_str));
    1840           1 :     smartlist_free(auth_clients_v3_str);
    1841             :   }
    1842             : 
    1843           3 :   return 0;
    1844             : }
    1845             : 
    1846             : /** Helper function to handle parsing the KeyType:KeyBlob argument to the
    1847             :  * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated
    1848             :  * and the private key not discarded, the algorithm and serialized private key,
    1849             :  * or NULL and an optional control protocol error message on failure.  The
    1850             :  * caller is responsible for freeing the returned key_new_blob.
    1851             :  *
    1852             :  * Note: The error messages returned are deliberately vague to avoid echoing
    1853             :  * key material.
    1854             :  *
    1855             :  * Note: conn is only used for writing control replies. For testing
    1856             :  * purposes, it can be NULL if control_write_reply() is appropriately
    1857             :  * mocked.
    1858             :  */
    1859             : STATIC int
    1860           8 : add_onion_helper_keyarg(const char *arg, int discard_pk,
    1861             :                         const char **key_new_alg_out, char **key_new_blob_out,
    1862             :                         add_onion_secret_key_t *decoded_key, int *hs_version,
    1863             :                         control_connection_t *conn)
    1864             : {
    1865           8 :   smartlist_t *key_args = smartlist_new();
    1866           8 :   const char *key_new_alg = NULL;
    1867           8 :   char *key_new_blob = NULL;
    1868           8 :   int ret = -1;
    1869             : 
    1870           8 :   smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
    1871           8 :   if (smartlist_len(key_args) != 2) {
    1872           0 :     control_write_endreply(conn, 512, "Invalid key type/blob");
    1873           0 :     goto err;
    1874             :   }
    1875             : 
    1876             :   /* The format is "KeyType:KeyBlob". */
    1877           8 :   static const char *key_type_new = "NEW";
    1878           8 :   static const char *key_type_best = "BEST";
    1879           8 :   static const char *key_type_ed25519_v3 = "ED25519-V3";
    1880             : 
    1881           8 :   const char *key_type = smartlist_get(key_args, 0);
    1882           8 :   const char *key_blob = smartlist_get(key_args, 1);
    1883             : 
    1884           8 :   if (!strcasecmp(key_type_ed25519_v3, key_type)) {
    1885             :     /* parsing of private ed25519 key */
    1886             :     /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
    1887           3 :     ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
    1888           3 :     if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
    1889             :                       strlen(key_blob)) != sizeof(sk->seckey)) {
    1890           0 :       tor_free(sk);
    1891           0 :       control_write_endreply(conn, 512, "Failed to decode ED25519-V3 key");
    1892           0 :       goto err;
    1893             :     }
    1894           3 :     decoded_key->v3 = sk;
    1895           3 :     *hs_version = HS_VERSION_THREE;
    1896           5 :   } else if (!strcasecmp(key_type_new, key_type)) {
    1897             :     /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
    1898           5 :     if (!strcasecmp(key_type_ed25519_v3, key_blob) ||
    1899           1 :         !strcasecmp(key_type_best, key_blob)) {
    1900             :       /* "ED25519-V3", ed25519 key, also currently "BEST" by default. */
    1901           5 :       ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
    1902           5 :       if (ed25519_secret_key_generate(sk, 1) < 0) {
    1903           0 :         tor_free(sk);
    1904           0 :         control_printf_endreply(conn, 551, "Failed to generate %s key",
    1905             :                                 key_type_ed25519_v3);
    1906           0 :         goto err;
    1907             :       }
    1908           5 :       if (!discard_pk) {
    1909           4 :         ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
    1910           4 :         key_new_blob = tor_malloc_zero(len);
    1911           4 :         if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
    1912           4 :                           sizeof(sk->seckey), 0) != (len - 1)) {
    1913           0 :           tor_free(sk);
    1914           0 :           tor_free(key_new_blob);
    1915           0 :           control_printf_endreply(conn, 551, "Failed to encode %s key",
    1916             :                                   key_type_ed25519_v3);
    1917           0 :           goto err;
    1918             :         }
    1919           4 :         key_new_alg = key_type_ed25519_v3;
    1920             :       }
    1921           5 :       decoded_key->v3 = sk;
    1922           5 :       *hs_version = HS_VERSION_THREE;
    1923             :     } else {
    1924           0 :       control_write_endreply(conn, 513, "Invalid key type");
    1925           0 :       goto err;
    1926             :     }
    1927             :   } else {
    1928           0 :     control_write_endreply(conn, 513, "Invalid key type");
    1929           0 :     goto err;
    1930             :   }
    1931             : 
    1932             :   /* Succeeded in loading or generating a private key. */
    1933             :   ret = 0;
    1934             : 
    1935           8 :  err:
    1936          24 :   SMARTLIST_FOREACH(key_args, char *, cp, {
    1937             :     memwipe(cp, 0, strlen(cp));
    1938             :     tor_free(cp);
    1939             :   });
    1940           8 :   smartlist_free(key_args);
    1941             : 
    1942           8 :   *key_new_alg_out = key_new_alg;
    1943           8 :   *key_new_blob_out = key_new_blob;
    1944             : 
    1945           8 :   return ret;
    1946             : }
    1947             : 
    1948             : static const control_cmd_syntax_t del_onion_syntax = {
    1949             :   .min_args = 1, .max_args = 1,
    1950             : };
    1951             : 
    1952             : /** Called when we get a DEL_ONION command; parse the body, and remove
    1953             :  * the existing ephemeral Onion Service. */
    1954             : static int
    1955           0 : handle_control_del_onion(control_connection_t *conn,
    1956             :                          const control_cmd_args_t *cmd_args)
    1957             : {
    1958           0 :   int hs_version = 0;
    1959           0 :   smartlist_t *args = cmd_args->args;
    1960           0 :   tor_assert(smartlist_len(args) == 1);
    1961             : 
    1962           0 :   const char *service_id = smartlist_get(args, 0);
    1963           0 :   if (hs_address_is_valid(service_id)) {
    1964           0 :     hs_version = HS_VERSION_THREE;
    1965             :   } else {
    1966           0 :     control_write_endreply(conn, 512, "Malformed Onion Service id");
    1967           0 :     goto out;
    1968             :   }
    1969             : 
    1970             :   /* Determine if the onion service belongs to this particular control
    1971             :    * connection, or if it is in the global list of detached services.  If it
    1972             :    * is in neither, either the service ID is invalid in some way, or it
    1973             :    * explicitly belongs to a different control connection, and an error
    1974             :    * should be returned.
    1975             :    */
    1976           0 :   smartlist_t *services[2] = {
    1977           0 :     conn->ephemeral_onion_services,
    1978             :     detached_onion_services
    1979             :   };
    1980           0 :   smartlist_t *onion_services = NULL;
    1981           0 :   int idx = -1;
    1982           0 :   for (size_t i = 0; i < ARRAY_LENGTH(services); i++) {
    1983           0 :     idx = smartlist_string_pos(services[i], service_id);
    1984           0 :     if (idx != -1) {
    1985             :       onion_services = services[i];
    1986             :       break;
    1987             :     }
    1988             :   }
    1989           0 :   if (onion_services == NULL) {
    1990           0 :     control_write_endreply(conn, 552, "Unknown Onion Service id");
    1991             :   } else {
    1992           0 :     int ret = -1;
    1993           0 :     switch (hs_version) {
    1994           0 :     case HS_VERSION_THREE:
    1995           0 :       ret = hs_service_del_ephemeral(service_id);
    1996           0 :       break;
    1997             :     default:
    1998             :       /* The ret value will be -1 thus hitting the warning below. This should
    1999             :        * never happen because of the check at the start of the function. */
    2000             :       break;
    2001             :     }
    2002           0 :     if (ret < 0) {
    2003             :       /* This should *NEVER* fail, since the service is on either the
    2004             :        * per-control connection list, or the global one.
    2005             :        */
    2006           0 :       log_warn(LD_BUG, "Failed to remove Onion Service %s.",
    2007             :                escaped(service_id));
    2008           0 :       tor_fragile_assert();
    2009             :     }
    2010             : 
    2011             :     /* Remove/scrub the service_id from the appropriate list. */
    2012           0 :     char *cp = smartlist_get(onion_services, idx);
    2013           0 :     smartlist_del(onion_services, idx);
    2014           0 :     memwipe(cp, 0, strlen(cp));
    2015           0 :     tor_free(cp);
    2016             : 
    2017           0 :     send_control_done(conn);
    2018             :   }
    2019             : 
    2020           0 :  out:
    2021           0 :   return 0;
    2022             : }
    2023             : 
    2024             : static const control_cmd_syntax_t obsolete_syntax = {
    2025             :   .max_args = UINT_MAX
    2026             : };
    2027             : 
    2028             : /**
    2029             :  * Called when we get an obsolete command: tell the controller that it is
    2030             :  * obsolete.
    2031             :  */
    2032             : static int
    2033           0 : handle_control_obsolete(control_connection_t *conn,
    2034             :                         const control_cmd_args_t *args)
    2035             : {
    2036           0 :   (void)args;
    2037           0 :   char *command = tor_strdup(conn->current_cmd);
    2038           0 :   tor_strupper(command);
    2039           0 :   control_printf_endreply(conn, 511, "%s is obsolete.", command);
    2040           0 :   tor_free(command);
    2041           0 :   return 0;
    2042             : }
    2043             : 
    2044             : /**
    2045             :  * Function pointer to a handler function for a controller command.
    2046             :  **/
    2047             : typedef int (*handler_fn_t) (control_connection_t *conn,
    2048             :                              const control_cmd_args_t *args);
    2049             : 
    2050             : /**
    2051             :  * Definition for a controller command.
    2052             :  */
    2053             : typedef struct control_cmd_def_t {
    2054             :   /**
    2055             :    * The name of the command. If the command is multiline, the name must
    2056             :    * begin with "+".  This is not case-sensitive. */
    2057             :   const char *name;
    2058             :   /**
    2059             :    * A function to execute the command.
    2060             :    */
    2061             :   handler_fn_t handler;
    2062             :   /**
    2063             :    * Zero or more CMD_FL_* flags, or'd together.
    2064             :    */
    2065             :   unsigned flags;
    2066             :   /**
    2067             :    * For parsed command: a syntax description.
    2068             :    */
    2069             :   const control_cmd_syntax_t *syntax;
    2070             : } control_cmd_def_t;
    2071             : 
    2072             : /**
    2073             :  * Indicates that the command's arguments are sensitive, and should be
    2074             :  * memwiped after use.
    2075             :  */
    2076             : #define CMD_FL_WIPE (1u<<0)
    2077             : 
    2078             : #ifndef COCCI
    2079             : /** Macro: declare a command with a one-line argument, a given set of flags,
    2080             :  * and a syntax definition.
    2081             :  **/
    2082             : #define ONE_LINE(name, flags)                                   \
    2083             :   {                                                             \
    2084             :     (#name),                                                    \
    2085             :       handle_control_ ##name,                                   \
    2086             :       flags,                                                    \
    2087             :       &name##_syntax,                                           \
    2088             :    }
    2089             : 
    2090             : /**
    2091             :  * Macro: declare a command with a multi-line argument and a given set of
    2092             :  * flags.
    2093             :  **/
    2094             : #define MULTLINE(name, flags)                                   \
    2095             :   { ("+"#name),                                                 \
    2096             :       handle_control_ ##name,                                   \
    2097             :       flags,                                                    \
    2098             :       &name##_syntax                                            \
    2099             :   }
    2100             : 
    2101             : /**
    2102             :  * Macro: declare an obsolete command. (Obsolete commands give a different
    2103             :  * error than non-existent ones.)
    2104             :  **/
    2105             : #define OBSOLETE(name)                          \
    2106             :   { #name,                                      \
    2107             :       handle_control_obsolete,                  \
    2108             :       0,                                        \
    2109             :       &obsolete_syntax,                         \
    2110             :   }
    2111             : #endif /* !defined(COCCI) */
    2112             : 
    2113             : /**
    2114             :  * An array defining all the recognized controller commands.
    2115             :  **/
    2116             : static const control_cmd_def_t CONTROL_COMMANDS[] =
    2117             : {
    2118             :   ONE_LINE(setconf, 0),
    2119             :   ONE_LINE(resetconf, 0),
    2120             :   ONE_LINE(getconf, 0),
    2121             :   MULTLINE(loadconf, 0),
    2122             :   ONE_LINE(setevents, 0),
    2123             :   ONE_LINE(authenticate, CMD_FL_WIPE),
    2124             :   ONE_LINE(saveconf, 0),
    2125             :   ONE_LINE(signal, 0),
    2126             :   ONE_LINE(takeownership, 0),
    2127             :   ONE_LINE(dropownership, 0),
    2128             :   ONE_LINE(mapaddress, 0),
    2129             :   ONE_LINE(getinfo, 0),
    2130             :   ONE_LINE(extendcircuit, 0),
    2131             :   ONE_LINE(setcircuitpurpose, 0),
    2132             :   OBSOLETE(setrouterpurpose),
    2133             :   ONE_LINE(attachstream, 0),
    2134             :   MULTLINE(postdescriptor, 0),
    2135             :   ONE_LINE(redirectstream, 0),
    2136             :   ONE_LINE(closestream, 0),
    2137             :   ONE_LINE(closecircuit, 0),
    2138             :   ONE_LINE(usefeature, 0),
    2139             :   ONE_LINE(resolve, 0),
    2140             :   ONE_LINE(protocolinfo, 0),
    2141             :   ONE_LINE(authchallenge, CMD_FL_WIPE),
    2142             :   ONE_LINE(dropguards, 0),
    2143             :   ONE_LINE(droptimeouts, 0),
    2144             :   ONE_LINE(hsfetch, 0),
    2145             :   MULTLINE(hspost, 0),
    2146             :   ONE_LINE(add_onion, CMD_FL_WIPE),
    2147             :   ONE_LINE(del_onion, CMD_FL_WIPE),
    2148             :   ONE_LINE(onion_client_auth_add, CMD_FL_WIPE),
    2149             :   ONE_LINE(onion_client_auth_remove, 0),
    2150             :   ONE_LINE(onion_client_auth_view, 0),
    2151             : };
    2152             : 
    2153             : /**
    2154             :  * The number of entries in CONTROL_COMMANDS.
    2155             :  **/
    2156             : static const size_t N_CONTROL_COMMANDS = ARRAY_LENGTH(CONTROL_COMMANDS);
    2157             : 
    2158             : /**
    2159             :  * Run a single control command, as defined by a control_cmd_def_t,
    2160             :  * with a given set of arguments.
    2161             :  */
    2162             : static int
    2163          29 : handle_single_control_command(const control_cmd_def_t *def,
    2164             :                               control_connection_t *conn,
    2165             :                               uint32_t cmd_data_len,
    2166             :                               char *args)
    2167             : {
    2168          29 :   int rv = 0;
    2169             : 
    2170          29 :   control_cmd_args_t *parsed_args;
    2171          29 :   char *err=NULL;
    2172          29 :   tor_assert(def->syntax);
    2173          29 :   parsed_args = control_cmd_parse_args(conn->current_cmd,
    2174             :                                        def->syntax,
    2175             :                                        cmd_data_len, args,
    2176             :                                        &err);
    2177          29 :   if (!parsed_args) {
    2178           0 :     control_printf_endreply(conn, 512, "Bad arguments to %s: %s",
    2179           0 :                             conn->current_cmd, err?err:"");
    2180           0 :     tor_free(err);
    2181             :   } else {
    2182          29 :     if (BUG(err))
    2183           0 :       tor_free(err);
    2184          29 :     if (def->handler(conn, parsed_args))
    2185             :       rv = 0;
    2186             : 
    2187          29 :     if (def->flags & CMD_FL_WIPE)
    2188          13 :       control_cmd_args_wipe(parsed_args);
    2189             : 
    2190          29 :     control_cmd_args_free(parsed_args);
    2191             :   }
    2192             : 
    2193          29 :   if (def->flags & CMD_FL_WIPE)
    2194          13 :     memwipe(args, 0, cmd_data_len);
    2195             : 
    2196          29 :   return rv;
    2197             : }
    2198             : 
    2199             : /**
    2200             :  * Run a given controller command, as selected by the current_cmd field of
    2201             :  * <b>conn</b>.
    2202             :  */
    2203             : int
    2204          29 : handle_control_command(control_connection_t *conn,
    2205             :                        uint32_t cmd_data_len,
    2206             :                        char *args)
    2207             : {
    2208          29 :   tor_assert(conn);
    2209          29 :   tor_assert(args);
    2210          29 :   tor_assert(args[cmd_data_len] == '\0');
    2211             : 
    2212         739 :   for (unsigned i = 0; i < N_CONTROL_COMMANDS; ++i) {
    2213         739 :     const control_cmd_def_t *def = &CONTROL_COMMANDS[i];
    2214         739 :     if (!strcasecmp(conn->current_cmd, def->name)) {
    2215          29 :       return handle_single_control_command(def, conn, cmd_data_len, args);
    2216             :     }
    2217             :   }
    2218             : 
    2219           0 :   control_printf_endreply(conn, 510, "Unrecognized command \"%s\"",
    2220             :                           conn->current_cmd);
    2221             : 
    2222           0 :   return 0;
    2223             : }
    2224             : 
    2225             : void
    2226         235 : control_cmd_free_all(void)
    2227             : {
    2228         235 :   if (detached_onion_services) { /* Free the detached onion services */
    2229           0 :     SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
    2230           0 :     smartlist_free(detached_onion_services);
    2231             :   }
    2232         235 : }

Generated by: LCOV version 1.14