mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
parent
1e8bb79bbe
commit
1a68a18093
@ -22,6 +22,8 @@
|
||||
#include "core/or/origin_circuit_st.h"
|
||||
#include "core/or/socks_request_st.h"
|
||||
#include "feature/control/control_connection_st.h"
|
||||
#include "lib/container/smartlist.h"
|
||||
#include "lib/encoding/kvline.h"
|
||||
|
||||
/** Append a NUL-terminated string <b>s</b> to the end of
|
||||
* <b>conn</b>-\>outbuf.
|
||||
@ -275,3 +277,158 @@ control_write_data(control_connection_t *conn, const char *data)
|
||||
connection_buf_add(esc, esc_len, TO_CONN(conn));
|
||||
tor_free(esc);
|
||||
}
|
||||
|
||||
/** Write a single reply line to @a conn.
|
||||
*
|
||||
* @param conn control connection
|
||||
* @param line control reply line to write
|
||||
* @param lastone true if this is the last reply line of a multi-line reply
|
||||
*/
|
||||
void
|
||||
control_write_reply_line(control_connection_t *conn,
|
||||
const control_reply_line_t *line, bool lastone)
|
||||
{
|
||||
const config_line_t *kvline = line->kvline;
|
||||
char *s = NULL;
|
||||
|
||||
if (strpbrk(kvline->value, "\r\n") != NULL) {
|
||||
/* If a key-value pair needs to be encoded as CmdData, it can be
|
||||
the only key-value pair in that reply line */
|
||||
tor_assert(kvline->next == NULL);
|
||||
control_printf_datareply(conn, line->code, "%s=", kvline->key);
|
||||
control_write_data(conn, kvline->value);
|
||||
return;
|
||||
}
|
||||
s = kvline_encode(kvline, line->flags);
|
||||
if (lastone) {
|
||||
control_write_endreply(conn, line->code, s);
|
||||
} else {
|
||||
control_write_midreply(conn, line->code, s);
|
||||
}
|
||||
tor_free(s);
|
||||
}
|
||||
|
||||
/** Write a set of reply lines to @a conn.
|
||||
*
|
||||
* @param conn control connection
|
||||
* @param lines smartlist of pointers to control_reply_line_t to write
|
||||
*/
|
||||
void
|
||||
control_write_reply_lines(control_connection_t *conn, smartlist_t *lines)
|
||||
{
|
||||
bool lastone = false;
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(lines, control_reply_line_t *, line) {
|
||||
if (line_sl_idx >= line_sl_len - 1)
|
||||
lastone = true;
|
||||
control_write_reply_line(conn, line, lastone);
|
||||
} SMARTLIST_FOREACH_END(line);
|
||||
}
|
||||
|
||||
/** Add a single key-value pair as a new reply line to a control reply
|
||||
* line list.
|
||||
*
|
||||
* @param reply smartlist of pointers to control_reply_line_t
|
||||
* @param code numeric control reply code
|
||||
* @param flags kvline encoding flags
|
||||
* @param key key
|
||||
* @param val value
|
||||
*/
|
||||
void
|
||||
control_reply_add_1kv(smartlist_t *reply, int code, int flags,
|
||||
const char *key, const char *val)
|
||||
{
|
||||
control_reply_line_t *line = tor_malloc_zero(sizeof(*line));
|
||||
|
||||
line->code = code;
|
||||
line->flags = flags;
|
||||
config_line_append(&line->kvline, key, val);
|
||||
smartlist_add(reply, line);
|
||||
}
|
||||
|
||||
/** Append a single key-value pair to last reply line in a control
|
||||
* reply line list.
|
||||
*
|
||||
* @param reply smartlist of pointers to control_reply_line_t
|
||||
* @param key key
|
||||
* @param val value
|
||||
*/
|
||||
void
|
||||
control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
|
||||
{
|
||||
int len = smartlist_len(reply);
|
||||
control_reply_line_t *line;
|
||||
|
||||
tor_assert(len > 0);
|
||||
|
||||
line = smartlist_get(reply, len - 1);
|
||||
config_line_append(&line->kvline, key, val);
|
||||
}
|
||||
|
||||
/** Add new reply line consisting of the string @a s
|
||||
*
|
||||
* @param reply smartlist of pointers to control_reply_line_t
|
||||
* @param code numeric control reply code
|
||||
* @param s string containing the rest of the reply line
|
||||
*/
|
||||
void
|
||||
control_reply_add_str(smartlist_t *reply, int code, const char *s)
|
||||
{
|
||||
control_reply_add_1kv(reply, code, KV_OMIT_KEYS|KV_RAW, "", s);
|
||||
}
|
||||
|
||||
/** Format a new reply line
|
||||
*
|
||||
* @param reply smartlist of pointers to control_reply_line_t
|
||||
* @param code numeric control reply code
|
||||
* @param fmt format string
|
||||
*/
|
||||
void
|
||||
control_reply_add_printf(smartlist_t *reply, int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *buf = NULL;
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void)tor_vasprintf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
control_reply_add_str(reply, code, buf);
|
||||
tor_free(buf);
|
||||
}
|
||||
|
||||
/** Add a "250 OK" line to a set of control reply lines */
|
||||
void
|
||||
control_reply_add_done(smartlist_t *reply)
|
||||
{
|
||||
control_reply_add_str(reply, 250, "OK");
|
||||
}
|
||||
|
||||
/** Free a control_reply_line_t. Don't call this directly; use the
|
||||
* control_reply_line_free() macro instead. */
|
||||
void
|
||||
control_reply_line_free_(control_reply_line_t *line)
|
||||
{
|
||||
if (!line)
|
||||
return;
|
||||
config_free_lines(line->kvline);
|
||||
tor_free_(line);
|
||||
}
|
||||
|
||||
/** Clear a smartlist of control_reply_line_t. Doesn't free the
|
||||
* smartlist, but does free each individual line. */
|
||||
void
|
||||
control_reply_clear(smartlist_t *reply)
|
||||
{
|
||||
SMARTLIST_FOREACH(reply, control_reply_line_t *, line,
|
||||
control_reply_line_free(line));
|
||||
smartlist_clear(reply);
|
||||
}
|
||||
|
||||
/** Free a smartlist of control_reply_line_t. Don't call this
|
||||
* directly; use the control_reply_free() macro instead. */
|
||||
void
|
||||
control_reply_free_(smartlist_t *reply)
|
||||
{
|
||||
control_reply_clear(reply);
|
||||
smartlist_free_(reply);
|
||||
}
|
||||
|
@ -7,11 +7,56 @@
|
||||
/**
|
||||
* \file control_proto.h
|
||||
* \brief Header file for control_proto.c.
|
||||
*
|
||||
* See @ref replylines for details about the key-value abstraction for
|
||||
* generating reply lines.
|
||||
**/
|
||||
|
||||
#ifndef TOR_CONTROL_PROTO_H
|
||||
#define TOR_CONTROL_PROTO_H
|
||||
|
||||
#include "lib/encoding/confline.h"
|
||||
|
||||
/**
|
||||
* @defgroup replylines Control reply lines
|
||||
* @brief Key-value structures for control reply lines
|
||||
*
|
||||
* Control reply lines are config_line_t key-value structures with
|
||||
* some additional information to help formatting, such as the numeric
|
||||
* result code specified in the control protocol and flags affecting
|
||||
* the way kvline_encode() formats the @a kvline.
|
||||
*
|
||||
* Generally, modules implementing control commands will work with
|
||||
* smartlists of these structures, using functions like
|
||||
* control_reply_add_str() for adding a reply line consisting of a
|
||||
* single string, or control_reply_add_1kv() and
|
||||
* control_reply_append_kv() for composing a line containing one or
|
||||
* more key-value pairs.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
/** @brief A reply line for the control protocol.
|
||||
*
|
||||
* This wraps config_line_t with some additional information that's
|
||||
* useful when generating control reply lines.
|
||||
*/
|
||||
typedef struct control_reply_line_t {
|
||||
int code; /**< numeric code */
|
||||
int flags; /**< kvline encoding flags */
|
||||
config_line_t *kvline; /**< kvline */
|
||||
} control_reply_line_t;
|
||||
|
||||
void control_reply_line_free_(control_reply_line_t *line);
|
||||
/**
|
||||
* @brief Free and null a control_reply_line_t
|
||||
*
|
||||
* @param line pointer to control_reply_line_t to free
|
||||
*/
|
||||
#define control_reply_line_free(line) \
|
||||
FREE_AND_NULL(control_reply_line_t, \
|
||||
control_reply_line_free_, (line))
|
||||
/** @} */
|
||||
|
||||
void connection_write_str_to_buf(const char *s, control_connection_t *conn);
|
||||
void connection_printf_to_buf(control_connection_t *conn,
|
||||
const char *format, ...)
|
||||
@ -45,4 +90,31 @@ void control_printf_datareply(control_connection_t *conn, int code,
|
||||
CHECK_PRINTF(3, 4);
|
||||
void control_write_data(control_connection_t *conn, const char *data);
|
||||
|
||||
/** @addtogroup replylines
|
||||
* @{
|
||||
*/
|
||||
void control_write_reply_line(control_connection_t *conn,
|
||||
const control_reply_line_t *line, bool lastone);
|
||||
void control_write_reply_lines(control_connection_t *conn, smartlist_t *lines);
|
||||
|
||||
void control_reply_add_1kv(smartlist_t *reply, int code, int flags,
|
||||
const char *key, const char *val);
|
||||
void control_reply_append_kv(smartlist_t *reply, const char *key,
|
||||
const char *val);
|
||||
void control_reply_add_str(smartlist_t *reply, int code, const char *s);
|
||||
void control_reply_add_printf(smartlist_t *reply, int code,
|
||||
const char *fmt, ...)
|
||||
CHECK_PRINTF(3, 4);
|
||||
void control_reply_add_done(smartlist_t *reply);
|
||||
|
||||
void control_reply_clear(smartlist_t *reply);
|
||||
void control_reply_free_(smartlist_t *reply);
|
||||
|
||||
/** @brief Free and null a smartlist of control_reply_line_t.
|
||||
*
|
||||
* @param r pointer to smartlist_t of control_reply_line_t to free */
|
||||
#define control_reply_free(r) \
|
||||
FREE_AND_NULL(smartlist_t, control_reply_free_, (r))
|
||||
/** @} */
|
||||
|
||||
#endif /* !defined(TOR_CONTROL_PROTO_H) */
|
||||
|
@ -1957,6 +1957,76 @@ test_getinfo_md_all(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
static smartlist_t *reply_strs;
|
||||
|
||||
static void
|
||||
mock_control_write_reply_list(control_connection_t *conn, int code, int c,
|
||||
const char *s)
|
||||
{
|
||||
(void)conn;
|
||||
/* To make matching easier, don't append "\r\n" */
|
||||
smartlist_add_asprintf(reply_strs, "%03d%c%s", code, c, s);
|
||||
}
|
||||
|
||||
static void
|
||||
test_control_reply(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
smartlist_t *lines = smartlist_new();
|
||||
|
||||
MOCK(control_write_reply, mock_control_write_reply);
|
||||
|
||||
tor_free(reply_str);
|
||||
control_reply_clear(lines);
|
||||
control_reply_add_str(lines, 250, "FOO");
|
||||
control_write_reply_lines(NULL, lines);
|
||||
tt_str_op(reply_str, OP_EQ, "FOO");
|
||||
|
||||
tor_free(reply_str);
|
||||
control_reply_clear(lines);
|
||||
control_reply_add_done(lines);
|
||||
control_write_reply_lines(NULL, lines);
|
||||
tt_str_op(reply_str, OP_EQ, "OK");
|
||||
|
||||
tor_free(reply_str);
|
||||
control_reply_clear(lines);
|
||||
UNMOCK(control_write_reply);
|
||||
MOCK(control_write_reply, mock_control_write_reply_list);
|
||||
reply_strs = smartlist_new();
|
||||
control_reply_add_1kv(lines, 250, 0, "A", "B");
|
||||
control_reply_add_1kv(lines, 250, 0, "C", "D");
|
||||
control_write_reply_lines(NULL, lines);
|
||||
tt_int_op(smartlist_len(reply_strs), OP_EQ, 2);
|
||||
tt_str_op((char *)smartlist_get(reply_strs, 0), OP_EQ, "250-A=B");
|
||||
tt_str_op((char *)smartlist_get(reply_strs, 1), OP_EQ, "250 C=D");
|
||||
|
||||
control_reply_clear(lines);
|
||||
SMARTLIST_FOREACH(reply_strs, char *, p, tor_free(p));
|
||||
smartlist_clear(reply_strs);
|
||||
control_reply_add_printf(lines, 250, "PROTOCOLINFO %d", 1);
|
||||
control_reply_add_1kv(lines, 250, KV_OMIT_VALS|KV_RAW, "AUTH", "");
|
||||
control_reply_append_kv(lines, "METHODS", "COOKIE");
|
||||
control_reply_append_kv(lines, "COOKIEFILE", escaped("/tmp/cookie"));
|
||||
control_reply_add_done(lines);
|
||||
control_write_reply_lines(NULL, lines);
|
||||
tt_int_op(smartlist_len(reply_strs), OP_EQ, 3);
|
||||
tt_str_op((char *)smartlist_get(reply_strs, 0),
|
||||
OP_EQ, "250-PROTOCOLINFO 1");
|
||||
tt_str_op((char *)smartlist_get(reply_strs, 1),
|
||||
OP_EQ, "250-AUTH METHODS=COOKIE COOKIEFILE=\"/tmp/cookie\"");
|
||||
tt_str_op((char *)smartlist_get(reply_strs, 2),
|
||||
OP_EQ, "250 OK");
|
||||
|
||||
done:
|
||||
UNMOCK(control_write_reply);
|
||||
tor_free(reply_str);
|
||||
control_reply_free(lines);
|
||||
if (reply_strs)
|
||||
SMARTLIST_FOREACH(reply_strs, char *, p, tor_free(p));
|
||||
smartlist_free(reply_strs);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef COCCI
|
||||
#define PARSER_TEST(type) \
|
||||
{ "parse/" #type, test_controller_parse_cmd, 0, &passthrough_setup, \
|
||||
@ -1989,5 +2059,6 @@ struct testcase_t controller_tests[] = {
|
||||
{ "download_status_bridge", test_download_status_bridge, 0, NULL, NULL },
|
||||
{ "current_time", test_current_time, 0, NULL, NULL },
|
||||
{ "getinfo_md_all", test_getinfo_md_all, 0, NULL, NULL },
|
||||
{ "control_reply", test_control_reply, 0, NULL, NULL },
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user