mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Start on a command-parsing tool for controller commands.
There _is_ an underlying logic to these commands, but it isn't wholly uniform, given years of tweaks and changes. Fortunately I think there is a superset that will work. This commit adds a parser for some of the most basic cases -- the ones currently handled by getargs_helper() and some of the object-taking ones. Soon will come initial tests; then I'll start using the parser. After that, I'll expand the parser to handle the other cases that come up in the controller protocol.
This commit is contained in:
parent
e9ca904dbf
commit
de70eebc65
@ -298,6 +298,7 @@ noinst_HEADERS += \
|
||||
src/feature/control/control.h \
|
||||
src/feature/control/control_auth.h \
|
||||
src/feature/control/control_cmd.h \
|
||||
src/feature/control/control_cmd_args_st.h \
|
||||
src/feature/control/control_connection_st.h \
|
||||
src/feature/control/control_events.h \
|
||||
src/feature/control/control_fmt.h \
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "core/or/entry_connection_st.h"
|
||||
#include "core/or/origin_circuit_st.h"
|
||||
#include "core/or/socks_request_st.h"
|
||||
#include "feature/control/control_cmd_args_st.h"
|
||||
#include "feature/control/control_connection_st.h"
|
||||
#include "feature/nodelist/node_st.h"
|
||||
#include "feature/nodelist/routerinfo_st.h"
|
||||
@ -60,6 +61,87 @@ static int control_setconf_helper(control_connection_t *conn, uint32_t len,
|
||||
* finished authentication and is accepting commands. */
|
||||
#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
|
||||
|
||||
/**
|
||||
* Release all storage held in <b>args</b>
|
||||
**/
|
||||
void
|
||||
control_cmd_args_free_(control_cmd_args_t *args)
|
||||
{
|
||||
if (! args)
|
||||
return;
|
||||
|
||||
if (args->args) {
|
||||
SMARTLIST_FOREACH(args->args, char *, c, tor_free(c));
|
||||
smartlist_free(args->args);
|
||||
}
|
||||
tor_free(args->object);
|
||||
|
||||
tor_free(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* control_cmd_args_t. On failure, set *<b>error_out</b> to newly allocated
|
||||
* error string, and return NULL.
|
||||
**/
|
||||
STATIC control_cmd_args_t *
|
||||
control_cmd_parse_args(const char *command,
|
||||
const control_cmd_syntax_t *syntax,
|
||||
size_t body_len,
|
||||
const char *body,
|
||||
char **error_out)
|
||||
{
|
||||
*error_out = NULL;
|
||||
control_cmd_args_t *result = tor_malloc_zero(sizeof(control_cmd_args_t));
|
||||
const char *cmdline;
|
||||
char *cmdline_alloc = NULL;
|
||||
|
||||
result->command = command;
|
||||
|
||||
const char *eol = memchr(body, '\n', body_len);
|
||||
if (syntax->want_object) {
|
||||
if (! eol || (eol+1) == body+body_len) {
|
||||
*error_out = tor_strdup("Empty body");
|
||||
goto err;
|
||||
}
|
||||
cmdline_alloc = tor_memdup_nulterm(body, eol-body);
|
||||
cmdline = cmdline_alloc;
|
||||
++eol;
|
||||
result->object_len = read_escaped_data(eol, (body+body_len)-eol,
|
||||
&result->object);
|
||||
} else {
|
||||
if (eol && (eol+1) != body+body_len) {
|
||||
*error_out = tor_strdup("Unexpected body");
|
||||
goto err;
|
||||
}
|
||||
cmdline = body;
|
||||
}
|
||||
|
||||
result->args = smartlist_new();
|
||||
smartlist_split_string(result->args, cmdline, " ",
|
||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||
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) {
|
||||
tor_asprintf(error_out, "Cannot accept more than %u argument(s)",
|
||||
syntax->max_args);
|
||||
goto err;
|
||||
}
|
||||
|
||||
tor_assert_nonfatal(*error_out == NULL);
|
||||
goto done;
|
||||
err:
|
||||
tor_assert_nonfatal(*error_out != NULL);
|
||||
control_cmd_args_free(result);
|
||||
done:
|
||||
tor_free(cmdline_alloc);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Called when we receive a SETCONF message: parse the body and try
|
||||
* to update our configuration. Reply with a DONE or ERROR message.
|
||||
* Modifies the contents of body.*/
|
||||
@ -2230,7 +2312,8 @@ handle_control_obsolete(control_connection_t *conn,
|
||||
**/
|
||||
typedef enum handler_type_t {
|
||||
hnd_legacy,
|
||||
hnd_legacy_mut
|
||||
hnd_legacy_mut,
|
||||
hnd_parsed,
|
||||
} handler_type_t;
|
||||
|
||||
/**
|
||||
@ -2257,6 +2340,13 @@ typedef union handler_fn_t {
|
||||
int (*legacy_mut)(control_connection_t *conn,
|
||||
uint32_t arg_len,
|
||||
char *args);
|
||||
|
||||
/**
|
||||
* A "parsed" handler expects its arguments in a pre-parsed format, in
|
||||
* an immutable control_cmd_args_t *object.
|
||||
**/
|
||||
int (*parsed)(control_connection_t *conn,
|
||||
const control_cmd_args_t *args);
|
||||
} handler_fn_t;
|
||||
|
||||
/**
|
||||
@ -2279,6 +2369,10 @@ typedef struct control_cmd_def_t {
|
||||
* Zero or more CMD_FL_* flags, or'd together.
|
||||
*/
|
||||
unsigned flags;
|
||||
/**
|
||||
* For parsed command: a syntax description.
|
||||
*/
|
||||
const control_cmd_syntax_t *syntax;
|
||||
} control_cmd_def_t;
|
||||
|
||||
/**
|
||||
@ -2287,16 +2381,27 @@ typedef struct control_cmd_def_t {
|
||||
*/
|
||||
#define CMD_FL_WIPE (1u<<0)
|
||||
|
||||
/**
|
||||
* Macro: declare a command with a one-line argument and a given set of
|
||||
* flags.
|
||||
#define SYNTAX_IGNORE { 0, UINT_MAX, false }
|
||||
|
||||
/** Macro: declare a command with a one-line argument, a given set of flags,
|
||||
* and a syntax definition.
|
||||
**/
|
||||
#define ONE_LINE(name, htype, flags) \
|
||||
#define ONE_LINE_(name, htype, flags, syntax) \
|
||||
{ #name, \
|
||||
hnd_ ##htype, \
|
||||
{ .htype = handle_control_ ##name }, \
|
||||
flags \
|
||||
flags, \
|
||||
syntax, \
|
||||
}
|
||||
|
||||
/** Macro: declare a parsed command with a one-line argument, a given set of
|
||||
* flags, and a syntax definition.
|
||||
**/
|
||||
#define ONE_LINE(name, htype, flags) \
|
||||
ONE_LINE_(name, htype, flags, NULL)
|
||||
#define ONE_LINE_PARSED(name, flags, syntax) \
|
||||
ONE_LINE_(name, parsed, flags, syntax)
|
||||
|
||||
/**
|
||||
* Macro: declare a command with a multi-line argument and a given set of
|
||||
* flags.
|
||||
@ -2305,7 +2410,8 @@ typedef struct control_cmd_def_t {
|
||||
{ "+"#name, \
|
||||
hnd_ ##htype, \
|
||||
{ .htype = handle_control_ ##name }, \
|
||||
flags \
|
||||
flags, \
|
||||
NULL \
|
||||
}
|
||||
/**
|
||||
* Macro: declare an obsolete command. (Obsolete commands give a different
|
||||
@ -2315,7 +2421,8 @@ typedef struct control_cmd_def_t {
|
||||
{ #name, \
|
||||
hnd_legacy, \
|
||||
{ .legacy = handle_control_obsolete }, \
|
||||
0 \
|
||||
0, \
|
||||
NULL, \
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2379,6 +2486,28 @@ handle_single_control_command(const control_cmd_def_t *def,
|
||||
if (def->handler.legacy_mut(conn, cmd_data_len, args))
|
||||
rv = -1;
|
||||
break;
|
||||
case hnd_parsed: {
|
||||
control_cmd_args_t *parsed_args;
|
||||
char *err=NULL;
|
||||
tor_assert(def->syntax);
|
||||
parsed_args = control_cmd_parse_args(conn->incoming_cmd,
|
||||
def->syntax,
|
||||
cmd_data_len, args,
|
||||
&err);
|
||||
if (!parsed_args) {
|
||||
connection_printf_to_buf(conn,
|
||||
"512 Bad arguments to %s: %s\r\n",
|
||||
conn->incoming_cmd, err?err:"");
|
||||
tor_free(err);
|
||||
} else {
|
||||
if (BUG(err))
|
||||
tor_free(err);
|
||||
if (def->handler.parsed(conn, parsed_args))
|
||||
rv = 0;
|
||||
control_cmd_args_free(parsed_args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
tor_assert_unreached();
|
||||
}
|
||||
|
@ -12,11 +12,19 @@
|
||||
#ifndef TOR_CONTROL_CMD_H
|
||||
#define TOR_CONTROL_CMD_H
|
||||
|
||||
#include "lib/malloc/malloc.h"
|
||||
|
||||
int handle_control_command(control_connection_t *conn,
|
||||
uint32_t cmd_data_len,
|
||||
char *args);
|
||||
void control_cmd_free_all(void);
|
||||
|
||||
typedef struct control_cmd_args_t control_cmd_args_t;
|
||||
void control_cmd_args_free_(control_cmd_args_t *args);
|
||||
|
||||
#define control_cmd_args_free(v) \
|
||||
FREE_AND_NULL(control_cmd_args_t, control_cmd_args_free_, (v))
|
||||
|
||||
#ifdef CONTROL_CMD_PRIVATE
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
|
||||
@ -39,6 +47,37 @@ STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
|
||||
int *created, char **err_msg_out);
|
||||
|
||||
/**
|
||||
* Definition for the syntax of a controller command, as parsed by
|
||||
* control_cmd_parse_args.
|
||||
*
|
||||
* WORK IN PROGRESS: This structure is going to get more complex as this
|
||||
* branch goes on.
|
||||
**/
|
||||
typedef struct control_cmd_syntax_t {
|
||||
/**
|
||||
* Lowest number of positional arguments that this command accepts.
|
||||
* 0 for "it's okay not to have positional arguments."
|
||||
**/
|
||||
unsigned int min_args;
|
||||
/**
|
||||
* Highest number of positional arguments that this command accepts.
|
||||
* UINT_MAX for no limit.
|
||||
**/
|
||||
unsigned int max_args;
|
||||
/**
|
||||
* True iff this command wants to be followed by a multiline object.
|
||||
**/
|
||||
bool want_object;
|
||||
} control_cmd_syntax_t;
|
||||
|
||||
STATIC control_cmd_args_t *control_cmd_parse_args(
|
||||
const char *command,
|
||||
const control_cmd_syntax_t *syntax,
|
||||
size_t body_len,
|
||||
const char *body,
|
||||
char **error_out);
|
||||
|
||||
#endif /* defined(CONTROL_CMD_PRIVATE) */
|
||||
|
||||
#ifdef CONTROL_MODULE_PRIVATE
|
||||
|
44
src/feature/control/control_cmd_args_st.h
Normal file
44
src/feature/control/control_cmd_args_st.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* Copyright (c) 2001 Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file control_cmd_args_st.h
|
||||
* \brief Definition for control_cmd_args_t
|
||||
**/
|
||||
|
||||
#ifndef TOR_CONTROL_CMD_ST_H
|
||||
#define TOR_CONTROL_CMD_ST_H
|
||||
|
||||
struct smartlist_t;
|
||||
struct config_line_t;
|
||||
|
||||
/**
|
||||
* Parsed arguments for a control command.
|
||||
*
|
||||
* WORK IN PROGRESS: This structure is going to get more complex as this
|
||||
* branch goes on.
|
||||
**/
|
||||
struct control_cmd_args_t {
|
||||
/**
|
||||
* The command itself, as provided by the controller. Not owned by this
|
||||
* structure.
|
||||
**/
|
||||
const char *command;
|
||||
/**
|
||||
* Positional arguments to the command.
|
||||
**/
|
||||
struct smartlist_t *args;
|
||||
/**
|
||||
* Number of bytes in <b>object</b>; 0 if <b>object</b> is not set.
|
||||
**/
|
||||
size_t object_len;
|
||||
/**
|
||||
* A multiline object passed with this command.
|
||||
**/
|
||||
char *object;
|
||||
};
|
||||
|
||||
#endif /* !defined(TOR_CONTROL_CMD_ST_H) */
|
Loading…
Reference in New Issue
Block a user