r11479@Kushana: nickm | 2006-12-07 23:38:54 -0500

Refactor GETINFO into a table-driven dispatch, as suggested by arma.  My brain hurts.


svn:r9052
This commit is contained in:
Nick Mathewson 2006-12-08 04:39:13 +00:00
parent 1567e13dc8
commit cf04e1e6e7
9 changed files with 258 additions and 157 deletions

View File

@ -28,6 +28,8 @@ Changes in version 0.1.2.5-xxxx - 200?-??-??
o Controller features
- Have GETINFO dir/status/* work on hosts with DirPort disabled.
- Reimplement GETINFO so that info/names stays in sync with the
actual keys.
o Controller bugfixes:
- Report the circuit number correctly in STREAM CLOSED events. (Bug

View File

@ -2447,8 +2447,11 @@ entry_guards_update_state(or_state_t *state)
* For backward compatibility, we also handle the string "helper-nodes".
* */
int
entry_guards_getinfo(int use_long_names, const char *question, char **answer)
getinfo_helper_entry_guards(control_connection_t *conn,
const char *question, char **answer)
{
int use_long_names = conn->use_long_names;
if (!strcmp(question,"entry-guards") ||
!strcmp(question,"helper-nodes")) {
smartlist_t *sl = smartlist_create();

View File

@ -4000,8 +4000,10 @@ or_state_save(time_t now)
* new string describing the supported configuration variables and their
* types. */
int
config_getinfo_helper(const char *question, char **answer)
getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer)
{
(void) conn;
if (!strcmp(question, "config/names")) {
smartlist_t *sl = smartlist_create();
int i;

View File

@ -196,6 +196,7 @@ static int handle_control_signal(control_connection_t *conn, uint32_t len,
const char *body);
static int handle_control_mapaddress(control_connection_t *conn, uint32_t len,
const char *body);
static char *list_getinfo_options(void);
static int handle_control_getinfo(control_connection_t *conn, uint32_t len,
const char *body);
static int handle_control_extendcircuit(control_connection_t *conn,
@ -1424,71 +1425,15 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
return 0;
}
/** Return a newly allocated string listing all valid GETINFO fields as
* required by GETINFO info/names. */
static char *
list_getinfo_options(void)
{
return tor_strdup(
"accounting/bytes Number of bytes read/written so far in interval.\n"
"accounting/bytes-left Number of bytes left to read/write in interval.\n"
"accounting/enabled Is accounting currently enabled?\n"
"accounting/hibernating Are we hibernating or awake?\n"
"accounting/interval-end Time when interval ends.\n"
"accounting/interval-start Time when interval starts.\n"
"accounting/interval-wake Time to wake up in this interval.\n"
"addr-mappings/all All current remapped addresses.\n"
"addr-mappings/cache Addresses remapped by DNS cache.\n"
"addr-mappings/configl Addresses remapped from configuration options.\n"
"addr-mappings/control Addresses remapped by a controller.\n"
"address The best guess at our external IP address.\n"
"circuit-status Status of each current circuit.\n"
"config-file Current location of the \"torrc\" file.\n"
"config/names List of configuration options, types, and documentation.\n"
"desc/id/* Server descriptor by hex ID.\n"
"desc/name/* Server descriptor by nickname.\n"
"desc/all-recent Latest server descriptor for every router.\n"
"dir/server/* Fetch server descriptors -- see dir-spec.txt.\n"
"dir/status/* Fetch networkstatus documents -- see dir-spec.txt.\n"
"entry-guards Which nodes will we use as entry guards?\n"
"events/names What events the controller can ask for.\n"
"exit-policy/default Default lines appended to config->ExitPolicy.\n"
"features/names What arguments can USEFEATURE take?\n"
"info/names List of GETINFO options, types, and documentation.\n"
"network-status List of hex IDs, nicknames, server statuses.\n"
"orconn-status Status of each current OR connection.\n"
"stream-status Status of each current application stream.\n"
"version The current version of Tor.\n");
// XXXX Uptodate!
/* This has been hard to keep up to date. Is it worth making
* a table with names, descriptions, whether to match with
* strsmpstart, and functions to call, so there's only one
* place to maintain? -RD */
}
/** Lookup the 'getinfo' entry <b>question</b>, and return
* the answer in <b>*answer</b> (or NULL if key not recognized).
* Return 0 if success or unrecognized, or -1 if recognized but
* internal error. */
static int
handle_getinfo_helper(control_connection_t *control_conn,
const char *question, char **answer)
getinfo_helper_misc(control_connection_t *conn, const char *question,
char **answer)
{
*answer = NULL; /* unrecognized key by default */
(void) conn;
if (!strcmp(question, "version")) {
*answer = tor_strdup(VERSION);
} else if (!strcmp(question, "config-file")) {
*answer = tor_strdup(get_torrc_fname());
} else if (!strcmpstart(question, "accounting/")) {
return accounting_getinfo_helper(question, answer);
} else if (!strcmpstart(question, "helper-nodes")) { /* deprecated */
return entry_guards_getinfo(control_conn->use_long_names,
question, answer);
} else if (!strcmpstart(question, "entry-guards")) {
return entry_guards_getinfo(control_conn->use_long_names,
question, answer);
} else if (!strcmpstart(question, "config/")) {
return config_getinfo_helper(question, answer);
} else if (!strcmp(question, "info/names")) {
*answer = list_getinfo_options();
} else if (!strcmp(question, "events/names")) {
@ -1497,7 +1442,22 @@ handle_getinfo_helper(control_connection_t *control_conn,
"NS STATUS_GENERAL STATUS_CLIENT STATUS_SERVER");
} else if (!strcmp(question, "features/names")) {
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmpstart(question, "desc/id/")) {
} else if (!strcmp(question, "address")) {
uint32_t addr;
if (router_pick_published_address(get_options(), &addr) < 0)
return -1;
*answer = tor_dup_addr(addr);
} else if (!strcmp(question, "dir-usage")) {
*answer = directory_dump_request_log();
}
return 0;
}
static int
getinfo_helper_dir(control_connection_t *control_conn,
const char *question, char **answer)
{
if (!strcmpstart(question, "desc/id/")) {
routerinfo_t *ri = router_get_by_hexdigest(question+strlen("desc/id/"));
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
@ -1526,11 +1486,70 @@ handle_getinfo_helper(control_connection_t *control_conn,
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
} else if (!strcmpstart(question, "ns/")) {
return networkstatus_getinfo_helper(question, answer);
} else if (!strcmpstart(question, "unregistered-servers-")) {
*answer = dirserver_getinfo_unregistered(question +
strlen("unregistered-servers-"));
} else if (!strcmpstart(question, "dir/server/")) {
size_t answer_len = 0, url_len = strlen(question)+2;
char *url = tor_malloc(url_len);
smartlist_t *descs = smartlist_create();
const char *msg;
int res;
char *cp;
tor_snprintf(url, url_len, "/tor/%s", question+4);
res = dirserv_get_routerdescs(descs, url, &msg);
if (res) {
log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
return -1;
}
SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
answer_len += sd->signed_descriptor_len);
cp = *answer = tor_malloc(answer_len+1);
SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
{
memcpy(cp, signed_descriptor_get_body(sd),
sd->signed_descriptor_len);
cp += sd->signed_descriptor_len;
});
*cp = '\0';
tor_free(url);
smartlist_free(descs);
} else if (!strcmpstart(question, "dir/status/")) {
size_t len;
char *cp;
if (get_options()->DirPort) {
smartlist_t *status_list = smartlist_create();
dirserv_get_networkstatus_v2(status_list,
question+strlen("dir/status/"));
SMARTLIST_FOREACH(status_list, cached_dir_t *, d, len += d->dir_len);
len = 0;
cp = *answer = tor_malloc(len+1);
SMARTLIST_FOREACH(status_list, cached_dir_t *, d, {
memcpy(cp, d->dir, d->dir_len);
cp += d->dir_len;
});
*cp = '\0';
smartlist_free(status_list);
} else {
smartlist_t *fp_list = smartlist_create();
smartlist_t *status_list = smartlist_create();
size_t fn_len = strlen(get_options()->DataDirectory)+HEX_DIGEST_LEN+32;
char *fn = tor_malloc(fn_len+1);
char hex_id[HEX_DIGEST_LEN+1];
dirserv_get_networkstatus_v2_fingerprints(
fp_list, question+strlen("dir/status/"));
SMARTLIST_FOREACH(fp_list, const char *, fp, {
char *s;
base16_encode(hex_id, sizeof(hex_id), fp, DIGEST_LEN);
tor_snprintf(fn, fn_len, "%s/cached-status/%s",
get_options()->DataDirectory, hex_id);
s = read_file_to_str(fn, 0, NULL);
if (s)
smartlist_add(status_list, s);
});
SMARTLIST_FOREACH(fp_list, char *, fp, tor_free(fp));
smartlist_free(fp_list);
*answer = smartlist_join_strings(status_list, "", 0, NULL);
SMARTLIST_FOREACH(status_list, char *, s, tor_free(s));
smartlist_free(status_list);
}
} else if (!strcmp(question, "network-status")) {
routerlist_t *routerlist = router_get_routerlist();
int verbose = control_conn->use_long_names;
@ -1538,7 +1557,15 @@ handle_getinfo_helper(control_connection_t *control_conn,
list_server_status(routerlist->routers, answer, verbose ? 2 : 1) < 0) {
return -1;
}
} else if (!strcmp(question, "circuit-status")) {
}
return 0;
}
static int
getinfo_helper_events(control_connection_t *control_conn,
const char *question, char **answer)
{
if (!strcmp(question, "circuit-status")) {
circuit_t *circ;
smartlist_t *status = smartlist_create();
for (circ = _circuit_get_global_list(); circ; circ = circ->next) {
@ -1676,80 +1703,131 @@ handle_getinfo_helper(control_connection_t *control_conn,
*answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
smartlist_free(mappings);
} else if (!strcmp(question, "address")) {
uint32_t addr;
if (router_pick_published_address(get_options(), &addr) < 0)
return -1;
*answer = tor_dup_addr(addr);
} else if (!strcmp(question, "dir-usage")) {
*answer = directory_dump_request_log();
} else if (!strcmpstart(question, "dir/server/")) {
size_t answer_len = 0, url_len = strlen(question)+2;
char *url = tor_malloc(url_len);
smartlist_t *descs = smartlist_create();
const char *msg;
int res;
char *cp;
tor_snprintf(url, url_len, "/tor/%s", question+4);
res = dirserv_get_routerdescs(descs, url, &msg);
if (res) {
log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
return -1;
}
SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
answer_len += sd->signed_descriptor_len);
cp = *answer = tor_malloc(answer_len+1);
SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
{
memcpy(cp, signed_descriptor_get_body(sd),
sd->signed_descriptor_len);
cp += sd->signed_descriptor_len;
});
*cp = '\0';
tor_free(url);
smartlist_free(descs);
} else if (!strcmpstart(question, "dir/status/")) {
size_t len;
char *cp;
if (get_options()->DirPort) {
smartlist_t *status_list = smartlist_create();
dirserv_get_networkstatus_v2(status_list,
question+strlen("dir/status/"));
SMARTLIST_FOREACH(status_list, cached_dir_t *, d, len += d->dir_len);
len = 0;
cp = *answer = tor_malloc(len+1);
SMARTLIST_FOREACH(status_list, cached_dir_t *, d, {
memcpy(cp, d->dir, d->dir_len);
cp += d->dir_len;
});
*cp = '\0';
smartlist_free(status_list);
} else {
smartlist_t *fp_list = smartlist_create();
smartlist_t *status_list = smartlist_create();
size_t fn_len = strlen(get_options()->DataDirectory)+HEX_DIGEST_LEN+32;
char *fn = tor_malloc(fn_len+1);
char hex_id[HEX_DIGEST_LEN+1];
dirserv_get_networkstatus_v2_fingerprints(
fp_list, question+strlen("dir/status/"));
SMARTLIST_FOREACH(fp_list, const char *, fp, {
char *s;
base16_encode(hex_id, sizeof(hex_id), fp, DIGEST_LEN);
tor_snprintf(fn, fn_len, "%s/cached-status/%s",
get_options()->DataDirectory, hex_id);
s = read_file_to_str(fn, 0, NULL);
if (s)
smartlist_add(status_list, s);
});
SMARTLIST_FOREACH(fp_list, char *, fp, tor_free(fp));
smartlist_free(fp_list);
*answer = smartlist_join_strings(status_list, "", 0, NULL);
SMARTLIST_FOREACH(status_list, char *, s, tor_free(s));
smartlist_free(status_list);
}
} else if (!strcmpstart(question, "exit-policy/")) {
return policies_getinfo_helper(question, answer);
}
return 0;
}
typedef int (*getinfo_helper_t)(control_connection_t *,
const char *q, char **a);
typedef struct getinfo_item_t {
const char *varname;
getinfo_helper_t fn;
const char *desc;
int is_prefix;
} getinfo_item_t;
#define ITEM(name, fn, desc) { name, getinfo_helper_##fn, desc, 0 }
#define PREFIX(name, fn, desc) { name, getinfo_helper_##fn, desc, 1 }
#define DOC(name, desc) { name, NULL, desc, 0 }
static const getinfo_item_t getinfo_items[] = {
ITEM("version", misc, "The current version of Tor."),
ITEM("config-file", misc, "Current location of the \"torrc\" file."),
ITEM("accounting/bytes", accounting,
"Number of bytes read/written so far in the accounting interval."),
ITEM("accounting/bytes-left", accounting,
"Number of bytes left to write/read so far in the accounting interval."),
ITEM("accounting/enabled", accounting, "Is accounting currently enabled?"),
ITEM("accounting/hibernating", accounting, "Are we hibernating or awake?"),
ITEM("accounting/interval-start", accounting,
"Time when the accounting period starts."),
ITEM("accounting/interval-end", accounting,
"Time when the accounting period ends."),
ITEM("accounting/interval-warke", accounting,
"Time to wake up in this accounting period."),
/* deprecated */
ITEM("helper-nodes", entry_guards, NULL),
ITEM("entry-nodes", entry_guards,
"Which nodes are we using as entry guards?"),
PREFIX("config/", config, "Current configuration values."),
DOC("config/names",
"List of configuration options, types, and documentation."),
ITEM("info/names", misc,
"List of GETINFO options, types, and documentation."),
ITEM("events/names", misc,
"Events that the controller can ask for with SETEVENTS."),
ITEM("features/names", misc, "What arguments can USEFEATURE take?"),
PREFIX("desc/id/", dir, "Router descriptors by ID."),
PREFIX("desc/name/", dir, "Router descriptors by nickname."),
ITEM("desc/all-recent", dir,
"All non-expired, non-superseded router descriptors."),
PREFIX("ns/id/", networkstatus,
"Brief summary of router status by ID (v2 directory format)."),
PREFIX("ns/name/", networkstatus,
"Brief summary of router status by nickname (v2 directory format)."),
PREFIX("unregisterd-servers-", dirserv_unregistered, NULL),
ITEM("network-status", dir,
"Brief summary of router status (v1 directory format)"),
ITEM("circuit-status", events, "List of current circuits originating here."),
ITEM("stream-status", events,"List of current streams."),
ITEM("orconn-status", events, "A list of current OR connections."),
PREFIX("addr-mappings/", events, NULL),
DOC("addr-mappings/all", "Current address mappings."),
DOC("addr-mappings/cache", "Current cached DNS replies."),
DOC("addr-mappings/config", "Current address mappings from configuration."),
DOC("addr-mappings/control", "Current address mappings from controller."),
ITEM("address", misc, "IP address of this Tor host, if we can guess it."),
ITEM("dir-usage", misc, "Breakdown of bytes transferred over DirPort."),
PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."),
PREFIX("dir/status/", dir,"Networkstatus docs as retrieved from a DirPort."),
PREFIX("exit-policy/default", policies,
"The default value appended to the configured exit policy."),
{ NULL, NULL, NULL, 0 }
};
static char *
list_getinfo_options(void)
{
int i;
char buf[300];
smartlist_t *lines = smartlist_create();
char *ans;
for (i = 0; getinfo_items[i].varname; ++i) {
if (!getinfo_items[i].desc)
continue;
tor_snprintf(buf, sizeof(buf), "%s%s -- %s\n",
getinfo_items[i].varname,
getinfo_items[i].is_prefix ? "*" : "",
getinfo_items[i].desc);
smartlist_add(lines, tor_strdup(buf));
}
smartlist_sort_strings(lines);
ans = smartlist_join_strings(lines, "", 0, NULL);
SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
smartlist_free(lines);
return ans;
}
/** Lookup the 'getinfo' entry <b>question</b>, and return
* the answer in <b>*answer</b> (or NULL if key not recognized).
* Return 0 if success or unrecognized, or -1 if recognized but
* internal error. */
static int
handle_getinfo_helper(control_connection_t *control_conn,
const char *question, char **answer)
{
int i;
*answer = NULL; /* unrecognized key by default */
for (i = 0; getinfo_items[i].varname; ++i) {
int match;
if (getinfo_items[i].is_prefix)
match = !strcmpstart(question, getinfo_items[i].varname);
else
match = !strcmp(question, getinfo_items[i].varname);
if (match) {
tor_assert(getinfo_items[i].fn);
return getinfo_items[i].fn(control_conn, question, answer);
}
}
return 0; /* unrecognized */
}

View File

@ -629,8 +629,9 @@ directory_remove_invalid(void)
* string and return it. Used by dirserv operators to keep track of
* fast nodes that haven't registered.
*/
char *
dirserver_getinfo_unregistered(const char *question)
int
getinfo_helper_dirserv_unregistered(control_connection_t *control_conn,
const char *question, char **answer_out)
{
smartlist_t *answerlist;
char buf[1024];
@ -638,6 +639,12 @@ dirserver_getinfo_unregistered(const char *question)
int min_bw = atoi(question);
routerlist_t *rl = router_get_routerlist();
(void) control_conn;
if (strcmpstart(question, "unregistered-servers-"))
return 0;
question += strlen("unregistered-servers-");
answerlist = smartlist_create();
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ent, {
uint32_t r = dirserv_router_get_status(ent, NULL);
@ -654,7 +661,8 @@ dirserver_getinfo_unregistered(const char *question)
answer = smartlist_join_strings(answerlist, "\r\n", 0, NULL);
SMARTLIST_FOREACH(answerlist, char *, cp, tor_free(cp));
smartlist_free(answerlist);
return answer;
*answer_out = answer;
return 0;
}
/** Mark the directory as <b>dirty</b> -- when we're next asked for a

View File

@ -928,8 +928,10 @@ consider_hibernation(time_t now)
/** DOCDOC */
int
accounting_getinfo_helper(const char *question, char **answer)
getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer)
{
(void) conn;
if (!strcmp(question, "accounting/enabled")) {
*answer = tor_strdup(get_options()->AccountingMax ? "1" : "0");
} else if (!strcmp(question, "accounting/hibernating")) {

View File

@ -1808,8 +1808,8 @@ void entry_nodes_should_be_added(void);
void entry_guards_prepend_from_config(void);
void entry_guards_update_state(or_state_t *state);
int entry_guards_parse_state(or_state_t *state, int set, char **msg);
int entry_guards_getinfo(int use_long_names,
const char *question, char **answer);
int getinfo_helper_entry_guards(control_connection_t *conn,
const char *question, char **answer);
void entry_guards_free_all(void);
/********************************* circuitlist.c ***********************/
@ -1923,7 +1923,8 @@ or_state_t *get_or_state(void);
int or_state_load(void);
int or_state_save(time_t now);
int config_getinfo_helper(const char *question, char **answer);
int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer);
/********************************* connection.c ***************************/
@ -2227,7 +2228,8 @@ int dirserv_load_fingerprint_file(void);
void dirserv_free_fingerprint_list(void);
const char *dirserv_get_nickname_by_digest(const char *digest);
int dirserv_add_descriptor(const char *desc, const char **msg);
char *dirserver_getinfo_unregistered(const char *question);
int getinfo_helper_dirserv_unregistered(control_connection_t *conn,
const char *question, char **answer);
void dirserv_free_descriptors(void);
int dirserv_thinks_router_is_blatantly_unreachable(routerinfo_t *router,
time_t now);
@ -2290,7 +2292,8 @@ int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void);
int we_are_hibernating(void);
void consider_hibernation(time_t now);
int accounting_getinfo_helper(const char *question, char **answer);
int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer);
void accounting_set_bandwidth_usage_from_state(or_state_t *state);
/********************************* main.c ***************************/
@ -2387,7 +2390,8 @@ int policies_parse_exit_policy(config_line_t *cfg,
int rejectprivate);
int exit_policy_is_general_exit(addr_policy_t *policy);
int policy_is_reject_star(addr_policy_t *policy);
int policies_getinfo_helper(const char *question, char **answer);
int getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer);
void addr_policy_free(addr_policy_t *p);
void policies_free_all(void);
@ -2749,7 +2753,8 @@ int router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2);
const char *esc_router_info(routerinfo_t *router);
char *networkstatus_getinfo_helper_single(routerstatus_t *rs);
int networkstatus_getinfo_helper(const char *question, char **answer);
int getinfo_helper_networkstatus(control_connection_t *conn,
const char *question, char **answer);
/********************************* routerparse.c ************************/

View File

@ -658,13 +658,12 @@ policy_is_reject_star(addr_policy_t *p)
}
int
policies_getinfo_helper(const char *question, char **answer)
getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer)
{
(void) conn;
if (!strcmp(question, "exit-policy/default")) {
*answer = tor_strdup(DEFAULT_EXIT_POLICY);
// } else if (!strcmp(question, "exit-policy/prepend")) {
} else {
*answer = NULL;
}
return 0;
}

View File

@ -4187,9 +4187,11 @@ networkstatus_getinfo_helper_single(routerstatus_t *rs)
* newly-allocated string containing networkstatus lines for the appropriate
* ORs. Return 0 on success, -1 on unrecognized question format. */
int
networkstatus_getinfo_helper(const char *question, char **answer)
getinfo_helper_networkstatus(control_connection_t *conn,
const char *question, char **answer)
{
local_routerstatus_t *status;
(void) conn;
if (!routerstatus_list) {
*answer = tor_strdup("");