diff --git a/ChangeLog b/ChangeLog index 9a7ff5af77..5680fee45d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 958894d724..cf50d71aad 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -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(); diff --git a/src/or/config.c b/src/or/config.c index 5da79db38e..d7f7209994 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -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; diff --git a/src/or/control.c b/src/or/control.c index e07b81b1f6..c04135d8d1 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -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 question, and return - * the answer in *answer (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 question, and return + * the answer in *answer (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 */ } diff --git a/src/or/dirserv.c b/src/or/dirserv.c index cccc494f4d..ff1da4125d 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -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 dirty -- when we're next asked for a diff --git a/src/or/hibernate.c b/src/or/hibernate.c index f4249d2efa..2063ccdbd2 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -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")) { diff --git a/src/or/or.h b/src/or/or.h index 053c5a499d..d108ec41e4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -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 ************************/ diff --git a/src/or/policies.c b/src/or/policies.c index 3c3c17d1a6..d318b5c9b8 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -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; } diff --git a/src/or/routerlist.c b/src/or/routerlist.c index b87c3fa2b9..e4ec1420d3 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -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("");