mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 23:53:32 +01:00
Allow kvlines in control commands.
This commit is contained in:
parent
bb37ad6957
commit
0841a69357
@ -40,6 +40,7 @@
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/encoding/kvline.h"
|
||||
|
||||
#include "core/or/cpath_build_state_st.h"
|
||||
#include "core/or/entry_connection_st.h"
|
||||
@ -74,11 +75,27 @@ control_cmd_args_free_(control_cmd_args_t *args)
|
||||
SMARTLIST_FOREACH(args->args, char *, c, tor_free(c));
|
||||
smartlist_free(args->args);
|
||||
}
|
||||
config_free_lines(args->kwargs);
|
||||
tor_free(args->object);
|
||||
|
||||
tor_free(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff any element of the NULL-terminated <b>array</b> matches
|
||||
* <b>kwd</b>. Case-insensitive.
|
||||
**/
|
||||
static bool
|
||||
string_array_contains_keyword(const char **array, const char *kwd)
|
||||
{
|
||||
for (unsigned i = 0; array[i]; ++i) {
|
||||
if (! strcasecmp(array[i], kwd))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper: parse the arguments to a command according to <b>syntax</b>. On
|
||||
* success, set *<b>error_out</b> to NULL and return a newly allocated
|
||||
@ -96,6 +113,7 @@ control_cmd_parse_args(const char *command,
|
||||
control_cmd_args_t *result = tor_malloc_zero(sizeof(control_cmd_args_t));
|
||||
const char *cmdline;
|
||||
char *cmdline_alloc = NULL;
|
||||
tor_assert(syntax->max_args < INT_MAX || syntax->max_args == UINT_MAX);
|
||||
|
||||
result->command = command;
|
||||
|
||||
@ -120,18 +138,42 @@ control_cmd_parse_args(const char *command,
|
||||
|
||||
result->args = smartlist_new();
|
||||
smartlist_split_string(result->args, cmdline, " ",
|
||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK,
|
||||
(int)(syntax->max_args+1));
|
||||
size_t n_args = smartlist_len(result->args);
|
||||
if (n_args < syntax->min_args) {
|
||||
tor_asprintf(error_out, "Need at least %u argument(s)",
|
||||
syntax->min_args);
|
||||
goto err;
|
||||
} else if (n_args > syntax->max_args) {
|
||||
} else if (n_args > syntax->max_args && ! syntax->accept_keywords) {
|
||||
tor_asprintf(error_out, "Cannot accept more than %u argument(s)",
|
||||
syntax->max_args);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (n_args > syntax->max_args) {
|
||||
tor_assert(n_args == syntax->max_args + 1);
|
||||
tor_assert(syntax->accept_keywords);
|
||||
char *remainder = smartlist_pop_last(result->args);
|
||||
result->kwargs = kvline_parse(remainder, syntax->kvline_flags);
|
||||
tor_free(remainder);
|
||||
if (result->kwargs == NULL) {
|
||||
tor_asprintf(error_out, "Cannot parse keyword argument(s)");
|
||||
goto err;
|
||||
}
|
||||
if (syntax->allowed_keywords) {
|
||||
/* Check for unpermitted arguments */
|
||||
const config_line_t *line;
|
||||
for (line = result->kwargs; line; line = line->next) {
|
||||
if (! string_array_contains_keyword(syntax->allowed_keywords,
|
||||
line->key)) {
|
||||
tor_asprintf(error_out, "Unrecognized keyword argument %s",
|
||||
escaped(line->key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tor_assert_nonfatal(*error_out == NULL);
|
||||
goto done;
|
||||
err:
|
||||
|
@ -43,6 +43,23 @@ typedef struct control_cmd_syntax_t {
|
||||
* UINT_MAX for no limit.
|
||||
**/
|
||||
unsigned int max_args;
|
||||
/**
|
||||
* If true, we should parse options after the positional arguments
|
||||
* as a set of unordered flags and key=value arguments.
|
||||
*
|
||||
* Requires that max_args is not UINT_MAX.
|
||||
**/
|
||||
bool accept_keywords;
|
||||
/**
|
||||
* If accept_keywords is true, then only the keywords listed in this
|
||||
* (NULL-terminated) array are valid keywords for this command.
|
||||
**/
|
||||
const char **allowed_keywords;
|
||||
/**
|
||||
* If accept_keywords is true, this option is passed to kvline_parse() as
|
||||
* its flags.
|
||||
**/
|
||||
unsigned kvline_flags;
|
||||
/**
|
||||
* True iff this command wants to be followed by a multiline object.
|
||||
**/
|
||||
|
@ -31,6 +31,10 @@ struct control_cmd_args_t {
|
||||
* Positional arguments to the command.
|
||||
**/
|
||||
struct smartlist_t *args;
|
||||
/**
|
||||
* Keyword arguments to the command.
|
||||
**/
|
||||
struct config_line_t *kwargs;
|
||||
/**
|
||||
* Number of bytes in <b>object</b>; 0 if <b>object</b> is not set.
|
||||
**/
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "test/test.h"
|
||||
#include "test/test_helpers.h"
|
||||
#include "lib/net/resolve.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/encoding/kvline.h"
|
||||
|
||||
#include "feature/control/control_connection_st.h"
|
||||
#include "feature/control/control_cmd_args_st.h"
|
||||
@ -58,6 +60,16 @@ control_cmd_dump_args(const control_cmd_args_t *result)
|
||||
buf_add_string(buf, ", obj=");
|
||||
buf_add_string(buf, escaped(result->object));
|
||||
}
|
||||
if (result->kwargs) {
|
||||
buf_add_string(buf, ", { ");
|
||||
const config_line_t *line;
|
||||
for (line = result->kwargs; line; line = line->next) {
|
||||
const bool last = (line->next == NULL);
|
||||
buf_add_printf(buf, "%s=%s%s ", line->key, escaped(line->value),
|
||||
last ? "" : ",");
|
||||
}
|
||||
buf_add_string(buf, "}");
|
||||
}
|
||||
buf_add_string(buf, " }");
|
||||
|
||||
char *encoded = buf_extract(buf, NULL);
|
||||
@ -152,6 +164,17 @@ static const control_cmd_syntax_t no_args_one_obj_syntax = {
|
||||
static const parse_test_params_t parse_no_args_one_obj_params =
|
||||
TESTPARAMS( no_args_one_obj_syntax, no_args_one_obj_tests );
|
||||
|
||||
static const parser_testcase_t no_args_kwargs_tests[] = {
|
||||
OK("", "{ args=[] }"),
|
||||
};
|
||||
static const control_cmd_syntax_t no_args_kwargs_syntax = {
|
||||
.min_args=0, .max_args=0,
|
||||
.accept_keywords=true,
|
||||
.kvline_flags=KV_OMIT_VALS
|
||||
};
|
||||
static const parse_test_params_t parse_no_args_kwargs_params =
|
||||
TESTPARAMS( no_args_kwargs_syntax, no_args_kwargs_tests );
|
||||
|
||||
static void
|
||||
test_add_onion_helper_keyarg_v3(void *arg)
|
||||
{
|
||||
@ -1752,6 +1775,7 @@ test_getinfo_md_all(void *arg)
|
||||
struct testcase_t controller_tests[] = {
|
||||
PARSER_TEST(one_to_three),
|
||||
PARSER_TEST(no_args_one_obj),
|
||||
PARSER_TEST(no_args_kwargs),
|
||||
{ "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
|
||||
NULL, NULL },
|
||||
{ "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
|
||||
|
Loading…
Reference in New Issue
Block a user