Skip to content

Commit

Permalink
Add: GETINFO support for dumping microdesc consensus
Browse files Browse the repository at this point in the history
test: case to check GETINFO consensus commands are functional.
fix: code structure
update: changes with human readable description
fix: command naming convention
add: test case checks if consensus body is returned
fix: potential memory leak
test: memory leak trace
  • Loading branch information
ltbringer committed Oct 3, 2019
1 parent d41c4f1 commit 5b34a4d
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 58 deletions.
2 changes: 1 addition & 1 deletion changes/ticket31684
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
o Minor features (onion services):
- GETINFO dir/status-vote/current/consensus-microdesc dumps microdesc consensus. Closes ticket 31684.
- Implement a new GETINFO command to fetch consensus. Closes ticket 31684.
56 changes: 20 additions & 36 deletions src/feature/control/control_getinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,46 +326,30 @@ getinfo_helper_current_time(control_connection_t *control_conn,
}

/**
* Switch between microdesc vs networkstatus descriptor dumps
* Assumes that question would be either:
* - dir/status-vote/current/consensus-microdesc
* or
* - dir/status-vote/current/consensus
* Helps to access directory information for a given
* type of consensus.
*/
STATIC int
getinfo_helper_current_consensus(const char* question,
char** answer,
const char** errmsg)
getinfo_helper_current_consensus(const char* flavor_name,
char** answer,
const char** errmsg)
{
// Ensures question contains a request for
// ns or microdesc consensus
if (
strcmp(question, "dir/status-vote/current/consensus") &&
strcmp(question, "dir/status-vote/current/consensus-microdesc")) {
return 0;
}

const char* consensus_type = !strcmp(
question, "dir/status-vote/current/consensus-microdesc"
) ? "microdesc" : "ns";

if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) {
const cached_dir_t *consensus = dirserv_get_consensus(consensus_type);
if (consensus) {
const cached_dir_t *consensus = dirserv_get_consensus(flavor_name);
if (consensus)
*answer = tor_strdup(consensus->dir);
}
}
if (!*answer) { /* try loading it from disk */
tor_mmap_t *mapped = networkstatus_map_cached_consensus(consensus_type);
tor_mmap_t *mapped = networkstatus_map_cached_consensus(flavor_name);
if (mapped) {
*answer = tor_memdup_nulterm(mapped->data, mapped->size);
tor_munmap_file(mapped);
}
if (!*answer) { /* generate an error */
*errmsg = "Could not open cached consensus. "
"Make sure FetchUselessDescriptors is set to 1.";
return -1;
}
return -1;
}
}
return 0;
}
Expand Down Expand Up @@ -622,16 +606,16 @@ getinfo_helper_dir(control_connection_t *control_conn,
smartlist_free(descs);
} else if (!strcmpstart(question, "dir/status/")) {
*answer = tor_strdup("");
} else if (-1 == getinfo_helper_current_consensus(
question,
answer,
errmsg))
{
/**
* answer is set by getinfo_helper_current_consensus
* if the question matches
*/
return -1;
} else if (!strcmp(question, "dir/status-vote/current/consensus")) {
if (getinfo_helper_current_consensus("ns", answer, errmsg) == -1) {
return -1;
}
} else if (!strcmp(question,
"dir/status-vote/current/consensus-microdesc")) {
if (getinfo_helper_current_consensus("microdesc",
answer, errmsg) == -1) {
return -1;
}
} else if (!strcmp(question, "network-status")) { /* v1 */
static int network_status_warned = 0;
if (!network_status_warned) {
Expand Down
3 changes: 2 additions & 1 deletion src/feature/control/control_getinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ STATIC int getinfo_helper_downloads(
const char *question, char **answer,
const char **errmsg);
STATIC int getinfo_helper_current_consensus(
const char *question, char **answer,
const char* flavor_name,
char **answer,
const char **errmsg);
STATIC int getinfo_helper_dir(
control_connection_t *control_conn,
Expand Down
5 changes: 3 additions & 2 deletions src/feature/dircache/dirserv.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,9 @@ dirserv_set_cached_consensus_networkstatus(const char *networkstatus,

/** Return the latest downloaded consensus networkstatus in encoded, signed,
* optionally compressed format, suitable for sending to clients. */
cached_dir_t *
dirserv_get_consensus(const char *flavor_name)
MOCK_IMPL(cached_dir_t *,
dirserv_get_consensus,
(const char *flavor_name))
{
if (!cached_consensuses)
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/feature/dircache/dirserv.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ int directory_permits_begindir_requests(const or_options_t *options);
int directory_too_idle_to_fetch_descriptors(const or_options_t *options,
time_t now);

cached_dir_t *dirserv_get_consensus(const char *flavor_name);
MOCK_DECL(cached_dir_t *, dirserv_get_consensus, (const char *flavor_name));
void dirserv_set_cached_consensus_networkstatus(const char *consensus,
size_t consensus_len,
const char *flavor_name,
Expand Down
9 changes: 5 additions & 4 deletions src/feature/nodelist/networkstatus.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,11 @@ networkstatus_reset_download_failures(void)
}

/** Return the filename used to cache the consensus of a given flavor */
static char *
networkstatus_get_cache_fname(int flav,
const char *flavorname,
int unverified_consensus)
MOCK_IMPL(char *,
networkstatus_get_cache_fname,
(int flav,
const char *flavorname,
int unverified_consensus))
{
char buf[128];
const char *prefix;
Expand Down
7 changes: 6 additions & 1 deletion src/feature/nodelist/networkstatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@

void networkstatus_reset_warnings(void);
void networkstatus_reset_download_failures(void);
tor_mmap_t *networkstatus_map_cached_consensus(const char *flavorname);
MOCK_DECL(char *,
networkstatus_get_cache_fname,
(int flav,
const char *flavorname,
int unverified_consensus));
tor_mmap_t * networkstatus_map_cached_consensus(const char *flavorname);
int router_reload_consensus_networkstatus(void);
void routerstatus_free_(routerstatus_t *rs);
#define routerstatus_free(rs) \
Expand Down
13 changes: 5 additions & 8 deletions src/lib/fs/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@
* failure, return NULL. Sets errno properly, using ERANGE to mean
* "empty file". Must only be called on trusted Tor-owned files, as changing
* the underlying file's size causes unspecified behavior. */
tor_mmap_t *
tor_mmap_file(const char *filename)
MOCK_IMPL(tor_mmap_t *, tor_mmap_file, (const char *filename))
{
int fd; /* router file */
char *string;
Expand Down Expand Up @@ -111,8 +110,7 @@ tor_mmap_file(const char *filename)
}
/** Release storage held for a memory mapping; returns 0 on success,
* or -1 on failure (and logs a warning). */
int
tor_munmap_file(tor_mmap_t *handle)
MOCK_IMPL(int, tor_munmap_file, (tor_mmap_t *handle))
{
int res;

Expand All @@ -132,8 +130,8 @@ tor_munmap_file(tor_mmap_t *handle)
return res;
}
#elif defined(_WIN32)
tor_mmap_t *
tor_mmap_file(const char *filename)

MOCK_IMPL(tor_mmap_t *, tor_mmap_file, (const char *filename))
{
TCHAR tfilename[MAX_PATH]= {0};
tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
Expand Down Expand Up @@ -213,8 +211,7 @@ tor_mmap_file(const char *filename)
}

/* Unmap the file, and return 0 for success or -1 for failure */
int
tor_munmap_file(tor_mmap_t *handle)
MOCK_IMPL(int, tor_munmap_file, (tor_mmap_t *handle))
{
if (handle == NULL)
return 0;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/fs/mmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define TOR_MMAP_H

#include "lib/cc/compat_compiler.h"
#include "lib/testsupport/testsupport.h"
#include <stddef.h>

#ifdef _WIN32
Expand All @@ -35,7 +36,7 @@ typedef struct tor_mmap_t {

} tor_mmap_t;

tor_mmap_t *tor_mmap_file(const char *filename);
int tor_munmap_file(tor_mmap_t *handle);
MOCK_DECL(tor_mmap_t *, tor_mmap_file, (const char *filename));
MOCK_DECL(int, tor_munmap_file, (tor_mmap_t *handle));

#endif /* !defined(TOR_MMAP_H) */
115 changes: 113 additions & 2 deletions src/test/test_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "feature/dirclient/download_status_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/dircache/dirserv.c"

typedef struct {
const char *input;
Expand Down Expand Up @@ -756,6 +757,7 @@ static download_status_t descbr_digest_2_dl;
static const char *descbr_expected_list =
"616408544C7345822696074A1A3DFA16AB381CBD\n"
"06E8067246967265DBCB6641631B530EFEC12DC3\n";

/*
* Flag to make all descbr queries fail, to simulate not being
* configured such that such queries make sense.
Expand Down Expand Up @@ -1689,6 +1691,114 @@ test_download_status_bridge(void *arg)
return;
}

cached_dir_t *
mock_dirserv_get_consensus(const char *flavor_name);

cached_dir_t *
mock_dirserv_get_consensus(const char *flavor_name)
{
const char* string = NULL;
if (!strcmp(flavor_name, "microdesc")) {
string = "mock_microdesc_consensus";
} else if (!strcmp(flavor_name, "ns")) {
string = "mock_ns_consensus";
}
return new_cached_dir(tor_memdup_nulterm(string,
strlen(string)),
time(NULL));
}

char *
mock_networkstatus_get_cache_fname(int flav,
const char *flavor_name,
int unverified_consensus);

char *
mock_networkstatus_get_cache_fname(int flav,
const char *flavor_name,
int unverified_consensus)
{
flav = flav & 1;
unverified_consensus = unverified_consensus & 1;
return tor_strdup(flavor_name);
}

tor_mmap_t *
mock_tor_mmap_file(const char* filename);

tor_mmap_t *
mock_tor_mmap_file(const char* filename)
{
tor_mmap_t *res;
res = tor_malloc_zero(sizeof(tor_mmap_t));
if (!strcmp(filename, "ns")) {
res->data = "mock_ns_consensus";
}
if (!strcmp(filename, "microdesc")) {
res->data = "mock_microdesc_consensus";
}
res->size = strlen(res->data);
return res;
}

int
mock_tor_munmap_file(tor_mmap_t *handle);

int
mock_tor_munmap_file(tor_mmap_t *handle)
{
tor_free(handle);
return 0;
}

static void
test_getinfo_helper_dir(void *arg)
{
/* We just need one of these to pass, it doesn't matter what's in it */
control_connection_t dummy;
/* Get results out */
char *answer = NULL;
const char *errmsg = NULL;

(void)arg;

setup_bridge_mocks();
MOCK(tor_mmap_file, mock_tor_mmap_file);
MOCK(tor_munmap_file, mock_tor_munmap_file);
MOCK(networkstatus_get_cache_fname,
mock_networkstatus_get_cache_fname);
MOCK(networkstatus_get_cache_fname,
mock_networkstatus_get_cache_fname);
MOCK(dirserv_get_consensus,
mock_dirserv_get_consensus);

getinfo_helper_dir(&dummy,
"dir/status-vote/current/consensus",
&answer,
&errmsg);
tt_str_op(answer, OP_EQ, "mock_ns_consensus");
tt_ptr_op(errmsg, OP_EQ, NULL);
tor_free(answer);
errmsg = NULL;

getinfo_helper_dir(&dummy,
"dir/status-vote/current/consensus-microdesc",
&answer,
&errmsg);
tt_str_op(answer, OP_EQ, "mock_microdesc_consensus");
tt_ptr_op(errmsg, OP_EQ, NULL);
errmsg = NULL;

done:
clear_bridge_mocks();
tor_free(answer);
UNMOCK(tor_mmap_file);
UNMOCK(tor_munmap_file);
UNMOCK(networkstatus_get_cache_fname);
UNMOCK(dirserv_get_consensus);
return;
}

/** Set timeval to a mock date and time. This is necessary
* to make tor_gettimeofday() mockable. */
static void
Expand Down Expand Up @@ -1734,13 +1844,12 @@ test_current_time(void *arg)
tt_ptr_op(answer, OP_NE, NULL);
tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, timebuf);
tor_free(answer);
errmsg = NULL;
answer = NULL;

done:
UNMOCK(tor_gettimeofday);
tor_free(answer);

return;
}

Expand Down Expand Up @@ -1838,6 +1947,8 @@ struct testcase_t controller_tests[] = {
NULL },
{ "download_status_consensus", test_download_status_consensus, 0, NULL,
NULL },
{"getinfo_helper_dir", test_getinfo_helper_dir, 0, NULL,
NULL },
{ "download_status_cert", test_download_status_cert, 0, NULL,
NULL },
{ "download_status_desc", test_download_status_desc, 0, NULL, NULL },
Expand Down

0 comments on commit 5b34a4d

Please sign in to comment.