diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/or/config.c | 14 | ||||
-rw-r--r-- | src/or/directory.c | 218 | ||||
-rw-r--r-- | src/or/dirserv.c | 24 | ||||
-rw-r--r-- | src/or/networkstatus.c | 1 | ||||
-rw-r--r-- | src/or/or.h | 51 | ||||
-rw-r--r-- | src/or/rendcommon.c | 368 | ||||
-rw-r--r-- | src/or/rendservice.c | 3 | ||||
-rw-r--r-- | src/or/router.c | 6 | ||||
-rw-r--r-- | src/or/routerlist.c | 171 | ||||
-rw-r--r-- | src/or/routerparse.c | 26 |
10 files changed, 835 insertions, 47 deletions
diff --git a/src/or/config.c b/src/or/config.c index 6f66ce258..acb5dc71e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -188,6 +188,7 @@ static config_var_t _option_vars[] = { V(Group, STRING, NULL), V(HardwareAccel, BOOL, "0"), V(HashedControlPassword, STRING, NULL), + V(HidServDirectoryV2, BOOL, "0"), VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceExcludeNodes", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceNodes", LINELIST_S, RendConfigLines, NULL), @@ -286,8 +287,12 @@ static config_var_t _option_vars[] = { VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), V(VirtualAddrNetwork, STRING, "127.192.0.0/10"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), + VAR("__ConsiderAllRoutersAsHidServDirectories", BOOL, + __ConsiderAllRoutersAsHidServDirectories, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), + VAR("__MinUptimeHidServDirectoryV2", INTERVAL, + __MinUptimeHidServDirectoryV2, "24 hours"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -2688,6 +2693,9 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("HSAuthorityRecordStats is set but we're not running as " "a hidden service authority."); + if (options->HidServDirectoryV2 && !options->DirPort) + REJECT("Running as hidden service directory, but no DirPort set."); + if (options->ConnLimit <= 0) { r = tor_snprintf(buf, sizeof(buf), "ConnLimit must be greater than 0, but was set to %d", @@ -2820,6 +2828,12 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + if (options->__MinUptimeHidServDirectoryV2 < 0) { + log_warn(LD_CONFIG, "__MinUptimeHidServDirectoryV2 option must be at " + "least 0 seconds. Changing to 0."); + options->__MinUptimeHidServDirectoryV2 = 0; + } + if (options->RendPostPeriod < MIN_REND_POST_PERIOD) { log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds." " Clipping.", MIN_REND_POST_PERIOD); diff --git a/src/or/directory.c b/src/or/directory.c index e286c392f..6e2265ffe 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -157,6 +157,10 @@ dir_conn_purpose_to_string(int purpose) return "status vote fetch"; case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: return "consensus signature fetch"; + case DIR_PURPOSE_FETCH_RENDDESC_V2: + return "hidden-service v2 descriptor fetch"; + case DIR_PURPOSE_UPLOAD_RENDDESC_V2: + return "hidden-service v2 descriptor upload"; } log_warn(LD_BUG, "Called with unknown purpose %d", purpose); @@ -422,7 +426,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose, * upload or download a server or rendezvous * descriptor. <b>dir_purpose</b> determines what * kind of directory connection we're launching, and must be one of - * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}. <b>router_purpose</b> + * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}. <b>router_purpose</b> * specifies the descriptor purposes we have in mind (currently only * used for FETCH_DIR). * @@ -867,12 +871,27 @@ directory_send_command(dir_connection_t *conn, url = tor_malloc(len); tor_snprintf(url, len, "/tor/rendezvous/%s", resource); break; + case DIR_PURPOSE_FETCH_RENDDESC_V2: + tor_assert(resource); + tor_assert(!payload); + tor_assert(strlen(resource) <= REND_DESC_ID_V2_BASE32); + httpcommand = "GET"; + len = strlen(resource) + 32; + url = tor_malloc(len); + tor_snprintf(url, len, "/tor/rendezvous2/%s", resource); + break; case DIR_PURPOSE_UPLOAD_RENDDESC: tor_assert(!resource); tor_assert(payload); httpcommand = "POST"; url = tor_strdup("/tor/rendezvous/publish"); break; + case DIR_PURPOSE_UPLOAD_RENDDESC_V2: + tor_assert(!resource); + tor_assert(payload); + httpcommand = "POST"; + url = tor_strdup("/tor/rendezvous2/publish"); + break; default: tor_assert(0); return; @@ -1721,8 +1740,46 @@ connection_dir_client_reached_eof(dir_connection_t *conn) } } - if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC) { - log_info(LD_REND,"Uploaded rendezvous descriptor (status %d (%s))", + if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) { + log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " + "(%s))", + (int)body_len, status_code, escaped(reason)); + switch (status_code) { + case 200: + if (rend_cache_store_v2_client(body, NULL) < 0) { + log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed."); + /* alice's ap_stream will notice when connection_mark_for_close + * cleans it up */ + } else { + /* success. notify pending connections about this. */ + log_info(LD_REND, "Successfully fetched rendezvous descriptor."); + conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC; + rend_client_desc_here(conn->rend_query); + } + break; + case 404: + /* not there. pending connections will be notified when + * connection_mark_for_close cleans it up. */ + break; + case 400: + log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " + "http status 400 (%s). Dirserver didn't like our " + "v2 rendezvous query?", escaped(reason)); + break; + default: + log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " + "http status %d (%s) response unexpected while " + "fetching v2 hidden service descriptor (server '%s:%d').", + status_code, escaped(reason), conn->_base.address, + conn->_base.port); + break; + } + } + + if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC || + conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) { + log_info(LD_REND,"Uploaded rendezvous descriptor (status %d " + "(%s))", status_code, escaped(reason)); switch (status_code) { case 200: @@ -2428,6 +2485,32 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, goto done; } + if (options->HidServDirectoryV2 && + !strcmpstart(url,"/tor/rendezvous2/")) { + /* Handle v2 rendezvous descriptor fetch request. */ + char *descp; + const char *query = url + strlen("/tor/rendezvous2/"); + if (strlen(query) == REND_DESC_ID_V2_BASE32) { + log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'", + query); + switch (rend_cache_lookup_v2_dir(query, &descp)) { + case 1: /* valid */ + write_http_response_header(conn, strlen(descp), 0, 0); + connection_write_to_buf(descp, strlen(descp), TO_CONN(conn)); + break; + case 0: /* well-formed but not present */ + write_http_status_line(conn, 404, "Not found"); + break; + case -1: /* not well-formed */ + write_http_status_line(conn, 400, "Bad request"); + break; + } + } else { /* not well-formed */ + write_http_status_line(conn, 400, "Bad request"); + } + goto done; + } + if (options->HSAuthoritativeDir && !strcmpstart(url,"/tor/rendezvous/")) { /* rendezvous descriptor fetch */ const char *descp; @@ -2546,6 +2629,27 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, conn->_base.state = DIR_CONN_STATE_SERVER_WRITING; + if (parse_http_url(headers, &url) < 0) { + write_http_status_line(conn, 400, "Bad request"); + return 0; + } + log_debug(LD_DIRSERV,"rewritten url as '%s'.", url); + + /* Handle v2 rendezvous service publish request. */ + if (options->HidServDirectoryV2 && + !strcmpstart(url,"/tor/rendezvous2/publish")) { + if (rend_cache_store_v2_dir(body) < 0) { + log_warn(LD_REND, "Rejected rend descriptor (length %d) from %s.", + (int)body_len, conn->_base.address); + write_http_status_line(conn, 400, "Invalid service descriptor rejected"); + log_info(LD_REND, "Handled v2 rendezvous descriptor post: rejected"); + } else { + write_http_status_line(conn, 200, "Service descriptor stored"); + log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted"); + } + goto done; + } + if (!authdir_mode(options)) { /* we just provide cached directories; we don't want to * receive anything. */ @@ -2554,12 +2658,6 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, return 0; } - if (parse_http_url(headers, &url) < 0) { - write_http_status_line(conn, 400, "Bad request"); - return 0; - } - log_debug(LD_DIRSERV,"rewritten url as '%s'.", url); - if (authdir_mode_handles_descs(options) && !strcmp(url,"/tor/")) { /* server descriptor post */ const char *msg = NULL; @@ -2948,3 +3046,105 @@ dir_split_resource_into_fingerprints(const char *resource, return 0; } +/** Determine the responsible hidden service directories for + * <b>desc_ids</b> and upload the appropriate descriptor from + * <b>desc_strs</b> to them; each smartlist must contain + * REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS entries; <b>service_id</b> and + * <b>seconds_valid</b> are only passed for logging purposes.*/ +/* XXXX020 enable tunneling when available!! */ +void +directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs, + const char *service_id, int seconds_valid, + smartlist_t *hs_dirs_) +{ + int i, j; + smartlist_t *responsible_dirs; + routerinfo_t *hs_dir; + if (smartlist_len(desc_ids) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS || + smartlist_len(desc_strs) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) { + log_warn(LD_REND, "Could not post descriptors to hidden service " + "directories: Illegal number of descriptor " + "IDs/strings"); + return; + } + responsible_dirs = smartlist_create(); + for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) { + const char *desc_id = smartlist_get(desc_ids, i); + const char *desc_str = smartlist_get(desc_strs, i); + /* Determine responsible dirs. */ + if (hid_serv_get_responsible_directories(responsible_dirs, desc_id, + hs_dirs_) < 0) { + log_warn(LD_REND, "Could not determine the responsible hidden service " + "directories to post descriptors to."); + smartlist_free(responsible_dirs); + return; + } + for (j = 0; j < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; j++) { + char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1]; + hs_dir = smartlist_get(responsible_dirs, j); + /* Send publish request. */ + directory_initiate_command(hs_dir->address, hs_dir->addr, + hs_dir->or_port, hs_dir->dir_port, 0, + hs_dir->cache_info.identity_digest, + DIR_PURPOSE_UPLOAD_RENDDESC_V2, + ROUTER_PURPOSE_GENERAL, + 1, NULL, desc_str, strlen(desc_str), 0); + base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1, + desc_id, DIGEST_LEN); + log_info(LD_REND, "Sending publish request for v2 descriptor for " + "service '%s' with descriptor ID '%s' with validity " + "of %d seconds to hidden service directory '%s' on " + "port %d.", + service_id, + desc_id_base32, + seconds_valid, + hs_dir->nickname, + hs_dir->dir_port); + } + smartlist_clear(responsible_dirs); + } + smartlist_free(responsible_dirs); +} + +/** Determine the responsible hidden service directories for <b>desc_id</b> + * and fetch the descriptor belonging to this ID from one of them; + * <b>query</b> is only passed for pretty log statements. + * XXXX020 enable tunneling when available!! */ +void +directory_get_from_hs_dir(const char *desc_id, const char *query, + smartlist_t *hs_dirs_) +{ + smartlist_t *responsible_dirs = smartlist_create(); + routerinfo_t *hs_dir; + char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1]; + int replica; + tor_assert(desc_id); + tor_assert(query); + tor_assert(strlen(query) == REND_SERVICE_ID_LEN); + /* Determine responsible dirs. */ + if (hid_serv_get_responsible_directories(responsible_dirs, desc_id, + hs_dirs_) < 0) { + log_warn(LD_REND, "Could not determine the responsible hidden service " + "directories to fetch descriptors."); + smartlist_free(responsible_dirs); + return; + } + replica = crypto_rand_int(REND_NUMBER_OF_CONSECUTIVE_REPLICAS); + hs_dir = smartlist_get(responsible_dirs, replica); + /* XXXX020 if hsdir fails, use another one... */ + base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1, + desc_id, DIGEST_LEN); + /* Send fetch request. */ + directory_initiate_command(hs_dir->address, hs_dir->addr, + hs_dir->or_port, hs_dir->dir_port, 0, + hs_dir->cache_info.identity_digest, + DIR_PURPOSE_FETCH_RENDDESC_V2, + ROUTER_PURPOSE_GENERAL, + 1, desc_id_base32, NULL, 0, 0); + log_info(LD_REND, "Sending fetch request for v2 descriptor for " + "service '%s' with descriptor ID '%s' to hidden " + "service directory '%s' on port %d.", + query, desc_id_base32, hs_dir->nickname, hs_dir->dir_port); + smartlist_free(responsible_dirs); +} + diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 6cc0634fc..a1ecdb884 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1526,6 +1526,21 @@ dirserv_thinks_router_is_unreliable(time_t now, return 0; } +/** Return true if <b>router</b> has an uptime of at least + * <b>__MinUptimeHidServDirectoryV2</b> and is reachable in the last + * REND_HS_DIR_REACHABLE_TIMEOUT seconds, else false. + */ +static int +dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now) +{ + int uptime = real_uptime(router, now); + + return (router->wants_to_be_hs_dir && + uptime > get_options()->__MinUptimeHidServDirectoryV2 && + ((router_is_me(router) && !we_are_hibernating()) || + (now < router->last_reachable + REND_HS_DIR_REACHABLE_TIMEOUT))); +} + /** Look through the routerlist, and assign the median uptime of running valid * servers to stable_uptime, and the relative bandwidth capacities to * fast_bandwidth and guard_bandwidth. Set total_bandwidth to the total @@ -1674,13 +1689,14 @@ routerstatus_format_entry(char *buf, size_t buf_len, return 0; cp = buf + strlen(buf); r = tor_snprintf(cp, buf_len - (cp-buf), - "s%s%s%s%s%s%s%s%s%s%s%s\n", + "s%s%s%s%s%s%s%s%s%s%s%s%s\n", /* These must stay in alphabetical order. */ rs->is_authority?" Authority":"", rs->is_bad_exit?" BadExit":"", rs->is_exit?" Exit":"", rs->is_fast?" Fast":"", rs->is_possible_guard?" Guard":"", + rs->is_hs_dir?" HSDir":"", rs->is_named?" Named":"", rs->is_running?" Running":"", rs->is_stable?" Stable":"", @@ -1843,6 +1859,10 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->is_possible_guard = 0; } rs->is_bad_exit = listbadexits && ri->is_bad_exit; + ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now); + if (get_options()->__ConsiderAllRoutersAsHidServDirectories) + ri->is_hs_dir = 1; /* Override real value. */ + rs->is_hs_dir = ri->is_hs_dir; /* 0.1.1.9-alpha is the first version to support fetch by descriptor * hash. */ rs->is_v2_dir = ri->dir_port && @@ -1992,7 +2012,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, v3_out->server_versions = server_versions; v3_out->known_flags = smartlist_create(); smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Running Stable V2Dir Valid", + "Authority Exit Fast Guard HSDir Running Stable V2Dir Valid", 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (listbadexits) smartlist_add(v3_out->known_flags, tor_strdup("BadExit")); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 055da051f..355fac390 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1576,6 +1576,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers, router->is_possible_guard = rs->is_possible_guard; router->is_exit = rs->is_exit; router->is_bad_exit = rs->is_bad_exit; + router->is_hs_dir = rs->is_hs_dir; } if (router->is_running && ds) { download_status_reset(&ds->v2_ns_dl_status); diff --git a/src/or/or.h b/src/or/or.h index 46fffeced..c035c437e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -351,7 +351,13 @@ typedef enum { /** Purpose for connection at a directory server. */ #define DIR_PURPOSE_SERVER 16 -#define _DIR_PURPOSE_MAX 16 +/** A connection to a hidden service directory server: upload a v2 rendezvous + * descriptor. */ +#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 17 +/** A connection to a hidden service directory server: download a v2 rendezvous + * descriptor. */ +#define DIR_PURPOSE_FETCH_RENDDESC_V2 18 +#define _DIR_PURPOSE_MAX 18 #define _EXIT_PURPOSE_MIN 1 /** This exit stream wants to do an ordinary connect. */ @@ -608,6 +614,14 @@ typedef enum { /** Length of v2 descriptor ID (32 base32 chars = 160 bits). */ #define REND_DESC_ID_V2_BASE32 32 +/** Length of the base32-encoded secret ID part of versioned hidden service + * descriptors. */ +#define REND_SECRET_ID_PART_LEN_BASE32 32 + +/** Length of the base32-encoded hash of an introduction point's + * identity key. */ +#define REND_INTRO_POINT_ID_LEN_BASE32 32 + #define CELL_DIRECTION_IN 1 #define CELL_DIRECTION_OUT 2 @@ -1197,6 +1211,11 @@ typedef struct { unsigned int is_exit:1; /**< Do we think this is an OK exit? */ unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked, * or otherwise nasty? */ + unsigned int wants_to_be_hs_dir:1; /**< True iff this router has set a flag + to possibly act as hidden service + directory. */ + unsigned int is_hs_dir:1; /**< True iff this router is a hidden service + * directory. */ /** Tor can use this router for general positions in circuits. */ #define ROUTER_PURPOSE_GENERAL 0 @@ -1269,6 +1288,8 @@ typedef struct routerstatus_t { * an exit node. */ unsigned int is_bad_directory:1; /**< Do we think this directory is junky, * underpowered, or otherwise useless? */ + unsigned int is_hs_dir:1; /** True iff this router is a hidden service + * directory. */ /** True iff we know version info for this router. (i.e., a "v" entry was * included.) We'll replace all these with a big tor_version_t or a char[] * if the number of traits we care about ever becomes incredibly big. */ @@ -1998,6 +2019,10 @@ typedef struct { int PublishHidServDescriptors; int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */ int FetchHidServDescriptors; /** and hidden service descriptors? */ + int HidServDirectoryV2; /**< Do we act as hs dir? */ + int __MinUptimeHidServDirectoryV2; /**< Accept hs dirs after what time? */ + int __ConsiderAllRoutersAsHidServDirectories; /**< Consider all routers as + * hidden service dirs? */ int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */ int AllDirActionsPrivate; /**< Should every directory action be sent * through a Tor circuit? */ @@ -2849,6 +2874,12 @@ int dir_split_resource_into_fingerprints(const char *resource, char *directory_dump_request_log(void); int router_supports_extrainfo(const char *identity_digest, int is_authority); +void directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *descs, + const char *service_id, int seconds_valid, + smartlist_t *hs_dirs); +void directory_get_from_hs_dir(const char *desc_id, const char *query, + smartlist_t *hs_dirs); + time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now); @@ -3422,13 +3453,19 @@ typedef struct rend_cache_entry_t { void rend_cache_init(void); void rend_cache_clean(void); +void rend_cache_clean_up(void); +void rend_cache_clean_v2_dir(void); void rend_cache_free_all(void); int rend_valid_service_id(const char *query); int rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len); int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **entry_out); +int rend_cache_lookup_v2_dir(const char *query, char **desc); int rend_cache_store(const char *desc, size_t desc_len, int published); +int rend_cache_store_v2_client(const char *desc, + const char *descriptor_cookie); +int rend_cache_store_v2_dir(const char *desc); int rend_cache_size(void); int rend_encode_v2_descriptors(smartlist_t *desc_strs_out, smartlist_t *desc_ids_out, @@ -3702,6 +3739,18 @@ void routerlist_assert_ok(routerlist_t *rl); const char *esc_router_info(routerinfo_t *router); void routers_sort_by_identity(smartlist_t *routers); +smartlist_t *hid_serv_create_routing_table(void); +int hid_serv_have_enough_directories(smartlist_t *hs_dirs); +int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, + const char *id, + smartlist_t *hs_dirs); +routerinfo_t *hid_serv_next_directory(const char *id, + smartlist_t *hs_dirs); +routerinfo_t *hid_serv_previous_directory(const char *id, + smartlist_t *hs_dirs); +int hid_serv_acting_as_directory(smartlist_t *hs_dirs); +int hid_serv_responsible_for_desc_id(const char *id, smartlist_t *hs_dirs); + /********************************* routerparse.c ************************/ #define MAX_STATUS_TAG_LEN 32 diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 60f295e4a..8f1db3571 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -60,25 +60,13 @@ rend_service_descriptor_free(rend_service_descriptor_t *desc) /*XXXX020 Rename to include "len" and maybe not "binary" */ #define REND_SERVICE_ID_BINARY 10 -/** Length of the time period that is used to encode the secret ID part of - * versioned hidden service descriptors. */ -/*XXXX020 Rename to include "len" and maybe not "binary" */ -#define REND_TIME_PERIOD_BINARY 4 - /** Length of the descriptor cookie that is used for versioned hidden * service descriptors. */ -/* XXXX020 rename to REND_DESC_COOKIE_(BINARY_)LEN */ -#define REND_DESC_COOKIE_BINARY 16 +#define REND_DESC_COOKIE_LEN 16 /** Length of the replica number that is used to determine the secret ID * part of versioned hidden service descriptors. */ -/* XXXX020 rename to REND_REPLICA_(BINARY_)LEN */ -#define REND_REPLICA_BINARY 1 - -/** Length of the base32-encoded secret ID part of versioned hidden service - * descriptors. */ -/*XXXX020 Rename to include "len" */ -#define REND_SECRET_ID_PART_BASE32 32 +#define REND_REPLICA_LEN 1 /** Compute the descriptor ID for <b>service_id</b> of length * <b>REND_SERVICE_ID_BINARY</b> and <b>secret_id_part</b> of length @@ -98,7 +86,7 @@ rend_get_descriptor_id_bytes(char *descriptor_id_out, /** Compute the secret ID part for time_period, * a <b>descriptor_cookie</b> of length - * <b>REND_DESC_COOKIE_BINARY</b> which may also be <b>NULL</b> if no + * <b>REND_DESC_COOKIE_LEN</b> which may also be <b>NULL</b> if no * descriptor_cookie shall be used, and <b>replica</b>, and write it to * <b>secret_id_part</b> of length DIGEST_LEN. */ static void @@ -110,9 +98,9 @@ get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period, crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t)); if (descriptor_cookie) { crypto_digest_add_bytes(digest, descriptor_cookie, - REND_DESC_COOKIE_BINARY); + REND_DESC_COOKIE_LEN); } - crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_BINARY); + crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN); crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN); crypto_free_digest_env(digest); } @@ -146,7 +134,7 @@ get_seconds_valid(time_t now, const char *service_id) /** Compute the binary <b>desc_id_out</b> (DIGEST_LEN bytes long) for a given * base32-encoded <b>service_id</b> and optional unencoded - * <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY, + * <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN, * at time <b>now</b> for replica number * <b>replica</b>. <b>desc_id</b> needs to have <b>DIGEST_LEN</b> bytes * free. Return 0 for success, -1 otherwise. */ @@ -188,7 +176,7 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, } /* Encode the introduction points in <b>desc</b>, optionally encrypt them with - * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY, + * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN, * encode it in base64, and write it to a newly allocated string, and write a * pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1 * otherwise. */ @@ -207,7 +195,7 @@ rend_encode_v2_intro_points(char **ipos_base64, unenc_len = desc->n_intro_points * 1000; /* too long, but ok. */ unenc = tor_malloc_zero(unenc_len); for (i = 0; i < desc->n_intro_points; i++) { - char id_base32[32 + 1]; /*XXXX020 should be a macro */ + char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1]; char *onion_key = NULL; size_t onion_key_len; crypto_pk_env_t *intro_key; @@ -370,7 +358,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out, /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */ for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) { char secret_id_part[DIGEST_LEN]; - char secret_id_part_base32[REND_SECRET_ID_PART_BASE32 + 1]; + char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1]; char *desc_id; char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1]; char *permanent_key = NULL; @@ -387,7 +375,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out, /* Calculate secret-id-part = h(time-period + cookie + replica). */ get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie, k); - base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_BASE32 + 1, + base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_LEN_BASE32 + 1, secret_id_part, DIGEST_LEN); /* Calculate descriptor ID. */ desc_id = tor_malloc_zero(DIGEST_LEN); @@ -628,12 +616,17 @@ rend_get_service_id(crypto_pk_env_t *pk, char *out) * rend_cache_entry_t. */ static strmap_t *rend_cache = NULL; +/** Map from descriptor id to rend_cache_entry_t; only for hidden service + * directories. */ +static digestmap_t *rend_cache_v2_dir = NULL; + /** Initializes the service descriptor cache. */ void rend_cache_init(void) { rend_cache = strmap_new(); + rend_cache_v2_dir = digestmap_new(); } /** Helper: free storage held by a single service descriptor cache entry. */ @@ -651,7 +644,9 @@ void rend_cache_free_all(void) { strmap_free(rend_cache, _rend_cache_entry_free); + digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free); rend_cache = NULL; + rend_cache_v2_dir = NULL; } /** Removes all old entries from the service descriptor cache. @@ -677,6 +672,88 @@ rend_cache_clean(void) } } +/** Remove all old entries on v2 hidden service directories. */ +void +rend_cache_clean_v2_dir(void) +{ + digestmap_iter_t *iter; + const char *key; + void *val; + rend_cache_entry_t *ent; + time_t cutoff; + cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; + for (iter = digestmap_iter_init(rend_cache_v2_dir); + !digestmap_iter_done(iter); ) { + digestmap_iter_get(iter, &key, &val); + ent = (rend_cache_entry_t*)val; + if (ent->parsed->timestamp < cutoff) { + char key_base32[REND_DESC_ID_V2_BASE32 + 1]; + base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN); + log_info(LD_REND, "Removing descriptor with ID '%s' from cache, " + "because it is too old!", + key_base32); + iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); + _rend_cache_entry_free(ent); + } else { + iter = digestmap_iter_next(rend_cache_v2_dir, iter); + } + } +} + +/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and + * <b>c</b> (included) in a circular digest ring; returns 1 if this is the + * case, and 0 otherwise. + */ +int +rend_id_is_in_interval(const char *a, const char *b, const char *c) +{ + tor_assert(a); + tor_assert(b); + tor_assert(c); + /* There are five cases in which a is outside the interval ]b,c]: */ + if ((memcmp(a, b, DIGEST_LEN) == 0) || /* 1. a == b (b is excluded) */ + /* 2. b == c (interval is empty) */ + (memcmp(b, c, DIGEST_LEN) == 0) || + /* 3. a b c */ + (memcmp(a, b, DIGEST_LEN) <= 0 && memcmp(b, c, DIGEST_LEN) < 0) || + /* 4. c a b */ + (memcmp(c, a, DIGEST_LEN) < 0 && memcmp(a, b, DIGEST_LEN) <= 0) || + /* 5. b c a */ + (memcmp(b, c, DIGEST_LEN) < 0 && memcmp(c, a, DIGEST_LEN) < 0)) + return 0; + /* In the other cases, a is inside the interval. */ + else + return 1; +} + +/** Clean up all values for which this node as hidden service directory is + * not responsible */ +void +rend_cache_clean_up(void) +{ + digestmap_iter_t *iter; + const char *key; + void *val; + rend_cache_entry_t *ent; + smartlist_t *hs_dirs = hid_serv_create_routing_table(); + for (iter = digestmap_iter_init(rend_cache_v2_dir); + !digestmap_iter_done(iter); ) { + digestmap_iter_get(iter, &key, &val); + ent = (rend_cache_entry_t*)val; + if (!hid_serv_responsible_for_desc_id(key, hs_dirs)) { + char key_base32[REND_DESC_ID_V2_BASE32 + 1]; + base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN); + log_info(LD_REND, "Removing descriptor with ID '%s' from cache, " + "because we are not reponsible for it!", key_base32); + iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); + _rend_cache_entry_free(ent); + } else { + iter = digestmap_iter_next(rend_cache_v2_dir, iter); + } + } + smartlist_free(hs_dirs); +} + /** Return true iff <b>query</b> is a syntactically valid service ID (as * generated by rend_get_service_id). */ int @@ -731,6 +808,41 @@ rend_cache_lookup_desc(const char *query, int version, const char **desc, return 1; } +/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and + * copy the pointer to it to <b>desc</b>. + */ +int +rend_cache_lookup_v2_dir(const char *desc_id, char **desc) +{ + rend_cache_entry_t *e; + char desc_id_digest[DIGEST_LEN]; + smartlist_t *hs_dirs; + tor_assert(rend_cache_v2_dir); + if (base32_decode(desc_id_digest, DIGEST_LEN, + desc_id, REND_DESC_ID_V2_BASE32) < 0) { + log_warn(LD_REND, "Descriptor ID contains illegal characters: %s", + desc_id); + return -1; + } + /* Determine if we are responsible. */ + hs_dirs = hid_serv_create_routing_table(); + if (hid_serv_responsible_for_desc_id(desc_id_digest, hs_dirs) < 0) { + log_info(LD_REND, "Could not answer fetch request for v2 descriptor; " + "either we are no hidden service directory, or we are " + "not responsible for the requested ID."); + smartlist_free(hs_dirs); + return -1; + } + smartlist_free(hs_dirs); + /* Lookup descriptor and return. */ + e = (rend_cache_entry_t*) digestmap_get(rend_cache_v2_dir, desc_id_digest); + if (e) { + *desc = e->desc; + return 1; + } + return 0; +} + /** Parse *desc, calculate its service id, and store it in the cache. * If we have a newer descriptor with the same ID, ignore this one. * If we have an older descriptor with the same ID, replace it. @@ -813,6 +925,218 @@ rend_cache_store(const char *desc, size_t desc_len, int published) return 1; } +/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the + * local rend cache. Don't attempt to decrypt the included list of introduction + * points (as we don't have a descriptor cookie for it). + * + * If we have a newer descriptor with the same ID, ignore this one. + * If we have an older descriptor with the same ID, replace it. + * Return -1 if it's malformed or otherwise rejected; return 0 if + * it's the same or older than one we've already got; return 1 if + * it's novel. + */ +int +rend_cache_store_v2_dir(const char *desc) +{ + rend_service_descriptor_t *parsed; + char desc_id[DIGEST_LEN]; + char *intro_content; + size_t intro_size; + size_t encoded_size; + char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1]; + int number_stored = 0; + const char *current_desc = desc; + const char *next_desc; + rend_cache_entry_t *e; + time_t now = time(NULL); + smartlist_t *hs_dirs = hid_serv_create_routing_table(); + tor_assert(rend_cache_v2_dir); + tor_assert(desc); + if (!hid_serv_acting_as_directory(hs_dirs)) { + /* Cannot store descs, because we are (currently) not acting as + * hidden service directory. */ + log_info(LD_REND, "Cannot store descs: Not acting as hs dir"); + smartlist_free(hs_dirs); + return -1; + } + while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, current_desc) >= 0) { + /* We don't care about the introduction points. */ + tor_free(intro_content); + /* For pretty log statements. */ + base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1, + desc_id, DIGEST_LEN); + /* Is desc ID in the range that we are (directly or indirectly) responsible + * for? */ + if (!hid_serv_responsible_for_desc_id(desc_id, hs_dirs)) { + log_info(LD_REND, "Service descriptor with desc ID %s is not in " + "interval that we are responsible for.", + desc_id_base32); + rend_service_descriptor_free(parsed); + goto skip; + } + /* Is descriptor too old? */ + if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { + log_info(LD_REND, "Service descriptor with desc ID %s is too old.", + desc_id_base32); + rend_service_descriptor_free(parsed); + goto skip; + } + /* Is descriptor too far in the future? */ + if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { + log_info(LD_REND, "Service descriptor with desc ID %s is too far in the " + "future.", + desc_id_base32); + rend_service_descriptor_free(parsed); + goto skip; + } + /* Do we already have a newer descriptor? */ + e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a newer service descriptor with the " + "same desc ID %s and version.", desc_id_base32); + rend_service_descriptor_free(parsed); + goto skip; + } + /* Do we already have this descriptor? */ + if (e && !strcmp(desc, e->desc)) { + log_info(LD_REND, "We already have this service descriptor with desc " + "ID %s.", desc_id_base32); + e->received = time(NULL); + rend_service_descriptor_free(parsed); + goto skip; + } + /* Store received descriptor. */ + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + digestmap_set(rend_cache_v2_dir, desc_id, e); + } else { + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->received = time(NULL); + e->parsed = parsed; + e->desc = tor_malloc(encoded_size + 1); + strlcpy(e->desc, current_desc, encoded_size + 1); + e->len = encoded_size; + log_info(LD_REND, "Successfully stored service descriptor with desc ID " + "'%s' and len %d.", desc_id_base32, encoded_size); + number_stored++; + skip: + /* advance to next descriptor, if available. */ + current_desc = next_desc; + /* check if there is a next descriptor. */ + if (strncmp(current_desc, "rendezvous-service-descriptor ", + strlen("rendezvous-service-descriptor "))) + break; + } + log_info(LD_REND, "Parsed and added %d descriptor%s.", + number_stored, number_stored != 1 ? "s" : ""); + return number_stored; +} + +/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list + * of introduction points with <b>descriptor_cookie</b> (which may also be + * <b>NULL</b> if decryption is not necessary), and store the descriptor to + * the local cache under its version and service id. + * + * If we have a newer descriptor with the same ID, ignore this one. + * If we have an older descriptor with the same ID, replace it. + * Return -1 if it's malformed or otherwise rejected; return 0 if + * it's the same or older than one we've already got; return 1 if + * it's novel. + */ +int +rend_cache_store_v2_client(const char *desc, const char *descriptor_cookie) +{ + rend_service_descriptor_t *parsed = NULL; + char desc_id[DIGEST_LEN]; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + time_t now = time(NULL); + char key[REND_SERVICE_ID_LEN+2]; + char service_id[REND_SERVICE_ID_LEN+1]; + rend_cache_entry_t *e; + tor_assert(rend_cache); + tor_assert(desc); + /* Parse the descriptor. */ + if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc) < 0) { + if (parsed) rend_service_descriptor_free(parsed); + if (intro_content) tor_free(intro_content); + log_warn(LD_REND, "Could not parse descriptor."); + return -1; + } + /* Compute service ID from public key. */ + if (rend_get_service_id(parsed->pk, service_id)<0) { + log_warn(LD_REND, "Couldn't compute service ID."); + rend_service_descriptor_free(parsed); + tor_free(intro_content); + return -1; + } + /* Decode/decrypt introduction points. */ + if (rend_decrypt_introduction_points(parsed, descriptor_cookie, + intro_content, intro_size) < 0) { + log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points."); + rend_service_descriptor_free(parsed); + tor_free(intro_content); + return -1; + } + /* We don't need the encoded/encrypted introduction points any longer. */ + tor_free(intro_content); + /* Is descriptor too old? */ + if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { + log_warn(LD_REND, "Service descriptor with service ID %s is too old.", + service_id); + rend_service_descriptor_free(parsed); + return -1; + } + /* Is descriptor too far in the future? */ + if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { + log_warn(LD_REND, "Service descriptor with service ID %s is too far in " + "the future.", service_id); + rend_service_descriptor_free(parsed); + return -1; + } + /* Do we already have a newer descriptor? */ + tor_snprintf(key, sizeof(key), "2%s", service_id); + e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a newer service descriptor for " + "service ID %s with the same desc ID and version.", + service_id); + rend_service_descriptor_free(parsed); + return 0; + } + /* Do we already have this descriptor? */ + if (e && !strcmp(desc, e->desc)) { + log_info(LD_REND,"We already have this service descriptor %s.", + service_id); + e->received = time(NULL); + rend_service_descriptor_free(parsed); + return 0; + } + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + strmap_set_lc(rend_cache, key, e); + } else { + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->received = time(NULL); + e->parsed = parsed; + e->desc = tor_malloc_zero(encoded_size + 1); + strlcpy(e->desc, desc, encoded_size + 1); + e->len = encoded_size; + log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", + service_id, encoded_size); + return 1; +} + /** Called when we get a rendezvous-related relay cell on circuit * <b>circ</b>. Dispatch on rendezvous relay command. */ void diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 2eb615a47..04a46fe4f 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -309,7 +309,8 @@ rend_service_update_descriptor(rend_service_t *service) d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t)); d->pk = crypto_pk_dup_key(service->private_key); d->timestamp = time(NULL); - d->version = 1; + d->version = 1; /*< XXXX020 this value is ignored by the + * encode functions; do we need to set it at all? */ n = smartlist_len(service->intro_nodes); d->n_intro_points = 0; d->intro_points = tor_malloc_zero(sizeof(char*)*n); diff --git a/src/or/router.c b/src/or/router.c index 99ca23aba..c7bf5efa1 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1577,7 +1577,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, "opt extra-info-digest %s\n%s" "onion-key\n%s" "signing-key\n%s" - "%s%s", + "%s%s%s", router->nickname, router->address, router->or_port, @@ -1593,7 +1593,9 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, options->DownloadExtraInfo ? "opt caches-extra-info\n" : "", onion_pkey, identity_pkey, family_line, - we_are_hibernating() ? "opt hibernating 1\n" : ""); + we_are_hibernating() ? "opt hibernating 1\n" : "", + options->HidServDirectoryV2 ? "opt hidden-service-dir\n" : ""); + tor_free(family_line); tor_free(onion_pkey); tor_free(identity_pkey); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index de6e1c03a..32c859710 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -4243,3 +4243,174 @@ routers_sort_by_identity(smartlist_t *routers) smartlist_sort(routers, _compare_routerinfo_by_id_digest); } +/** Return the first router that is acting as hidden service directory and that + * has a greater ID than <b>id</b>; if all routers have smaller IDs than + * <b>id</b>, return the router with the smallest ID; if the router list is + * NULL, or has no elements, return NULL. + */ +routerinfo_t * +hid_serv_next_directory(const char *id, smartlist_t *hs_dirs) +{ + int i; + if (!hs_dirs) return NULL; + if (smartlist_len(hs_dirs) == 0) return NULL; + for (i = 0; i < smartlist_len(hs_dirs); i++) { + routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i); + if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) > 0) { + return router; + } + } + return (routerinfo_t *) smartlist_get(hs_dirs, 0); +} + +/** Return the first router that is acting as hidden service directory and that + * has a smaller ID than <b>id</b>; if all routers have greater IDs than + * <b>id</b>, return the router with the highest ID; if the router list is + * NULL, or has no elements, return NULL. + */ +routerinfo_t * +hid_serv_previous_directory(const char *id, smartlist_t *hs_dirs) +{ + int i; + if (!hs_dirs) return NULL; + if (smartlist_len(hs_dirs) == 0) return NULL; + for (i = smartlist_len(hs_dirs) - 1; i >= 0; i--) { + routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i); + if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) < 0) { + return router; + } + } + return (routerinfo_t *) + smartlist_get(hs_dirs, smartlist_len(hs_dirs) - 1); +} + +/** Returns true, if we are aware of enough hidden service directory to + * usefully perform v2 rend operations on them (publish, fetch, replicate), + * or false otherwise. */ +int +hid_serv_have_enough_directories(smartlist_t *hs_dirs) +{ + return (smartlist_len(hs_dirs) > REND_NUMBER_OF_CONSECUTIVE_REPLICAS); +} + +/** Determine the REND_NUMBER_OF_CONSECUTIVE_REPLICAS routers that are + * responsible for <b>id</b> (binary) and add pointers to those routers' + * routerstatus_t to <b>responsible_dirs</b>. If we don't have enough + * hidden service directories, return -1, else 0. */ +int +hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, + const char *id, + smartlist_t *hs_dirs) +{ + const char *digest; + int i; + routerinfo_t *router; + char id_base32[32+1]; + base32_encode(id_base32, REND_DESC_ID_V2_BASE32 + 1, id, DIGEST_LEN); + tor_assert(id); + if (!hid_serv_have_enough_directories(hs_dirs)) { + log_warn(LD_REND, "We don't have enough hidden service directories to " + "perform v2 rendezvous operations!"); + return -1; + } + digest = id; + for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) { + router = hid_serv_next_directory(digest, hs_dirs); + digest = router->cache_info.identity_digest; + if (!router) { + log_warn(LD_REND, "Could not determine next router in " + "hidden service routing table."); + return -1; + } + smartlist_add(responsible_dirs, router); + } + return 0; +} + +/** Create a list of routerinfo_t in ascending order of identity digests + * containing all routers that have been assigned as hidden service + * directories by the directory authorities; this list can be used as + * hidden service routing table. */ +smartlist_t * +hid_serv_create_routing_table(void) +{ + smartlist_t *hs_dirs = smartlist_create(); + tor_assert(routerlist); + /* Copy the routerinfo_t's of all hidden service directories to a new + * smartlist. */ + SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r, + { + if (r->is_hs_dir) + smartlist_add(hs_dirs, r); + }); + routers_sort_by_identity(hs_dirs); + return hs_dirs; +} + +/** Return true if this node is currently acting as hidden service + * directory, false otherwise. */ +int +hid_serv_acting_as_directory(smartlist_t *hs_dirs) +{ + routerinfo_t *me = routerlist_find_my_routerinfo(); + int found_me = 0; + if (!me) { + return 0; + } + if (!get_options()->HidServDirectoryV2) { + log_info(LD_REND, "We are not acting as hidden service directory, " + "because we have not been configured as such."); + return 0; + } + if (!hs_dirs) { + /* routing table is NULL */ + log_info(LD_REND, "We are not acting as hidden service directory, " + "because our own routing table is NULL."); + return 0; + } + SMARTLIST_FOREACH(hs_dirs, routerinfo_t *, router, + { + if (router_is_me(router)) + found_me = 1; + }); + if (!found_me) { + /* not acting as HS Dir */ + char me_base32[REND_DESC_ID_V2_BASE32 + 1]; + base32_encode(me_base32, REND_DESC_ID_V2_BASE32 + 1, + me->cache_info.identity_digest, DIGEST_LEN); + log_info(LD_REND, "We are not acting as hidden service directory, " + "because we are not listed as such in our own " + "routing table. me=%s, num entries in RT=%d", + me_base32, smartlist_len(hs_dirs)); + return 0; + } + if (smartlist_len(hs_dirs) <= REND_NUMBER_OF_CONSECUTIVE_REPLICAS) { + /* too few HS Dirs -- that won't work */ + log_info(LD_REND, "We are not acting as hidden service directory, " + "because there are too few hidden service " + "directories in the routing table."); + return 0; + } + return 1; +} + +/** Return true if this node is responsible for storing the descriptor ID + * in <b>query</b> and false otherwise. */ +int +hid_serv_responsible_for_desc_id(const char *query, smartlist_t *hs_dirs) +{ + const char *me; + const char *predecessor; + routerinfo_t *router; + int i; + if (!hid_serv_acting_as_directory(hs_dirs)) + return 0; + me = router_get_my_routerinfo()->cache_info.identity_digest; + predecessor = me; + for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) { + router = hid_serv_previous_directory(predecessor, hs_dirs); + predecessor = router->cache_info.identity_digest; + } + return rend_id_is_in_interval(query, predecessor, me); +} + diff --git a/src/or/routerparse.c b/src/or/routerparse.c index ab520c94e..95de8b58d 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -57,6 +57,7 @@ typedef enum { K_EXTRA_INFO, K_EXTRA_INFO_DIGEST, K_CACHES_EXTRA_INFO, + K_HIDDEN_SERVICE_DIR, K_DIR_KEY_CERTIFICATE_VERSION, K_DIR_IDENTITY_KEY, @@ -218,6 +219,7 @@ static token_rule_t routerdesc_token_table[] = { T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ), + T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ), @@ -1255,6 +1257,10 @@ router_parse_entry_from_string(const char *s, const char *end, } } + if ((tok = find_first_by_keyword(tokens, K_HIDDEN_SERVICE_DIR))) { + router->wants_to_be_hs_dir = 1; + } + tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE); tor_assert(tok); note_crypto_pk_op(VERIFY_RTR); @@ -1698,6 +1704,8 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens, consensus_method >= 2) { /* Unnamed is computed right by consensus method 2 and later. */ rs->is_unnamed = 1; + } else if (!strcmp(tok->args[i], "HSDir")) { + rs->is_hs_dir = 1; } } } @@ -3172,7 +3180,7 @@ sort_version_list(smartlist_t *versions, int remove_duplicates) * *<b>intro_points_encrypted_out</b>, their encrypted size to * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor * to *<b>encoded_size_out</b>, and a pointer to the possibly next - * descriptor to *<b>next_now</b>; return 0 for success (including validation) + * descriptor to *<b>next_out</b>; return 0 for success (including validation) * and -1 for failure. */ int @@ -3228,15 +3236,13 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, log_warn(LD_REND, "Impossibly short descriptor."); goto err; } - /* Check whether descriptor starts correctly. */ /* Parse base32-encoded descriptor ID. */ tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR); tor_assert(tok); tor_assert(tok == smartlist_get(tokens, 0)); tor_assert(tok->n_args == 1); - /*XXXX020 magic 32. */ - if (strlen(tok->args[0]) != 32 || - strspn(tok->args[0], BASE32_CHARS) != 32) { + if (strlen(tok->args[0]) != REND_DESC_ID_V2_BASE32 || + strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_BASE32) { log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]); goto err; } @@ -3252,6 +3258,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, tor_assert(tok->n_args == 1); result->version = atoi(tok->args[0]); if (result->version < 2) { /*XXXX020 what if > 2? */ + /* Good question: should higher versions + * be rejected by directories? -KL */ log_warn(LD_REND, "Wrong descriptor version: %d", result->version); goto err; } @@ -3264,9 +3272,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, tok = find_first_by_keyword(tokens, R_SECRET_ID_PART); tor_assert(tok); tor_assert(tok->n_args == 1); - /* XXXX020 magic 32. */ - if (strlen(tok->args[0]) != 32 || - strspn(tok->args[0], BASE32_CHARS) != 32) { + if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 || + strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) { log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]); goto err; } @@ -3418,9 +3425,8 @@ rend_decrypt_introduction_points(rend_service_descriptor_t *parsed, /* Parse identifier. */ tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER); tor_assert(tok); - /* XXXX020 magic 32. */ if (base32_decode(info->identity_digest, DIGEST_LEN, - tok->args[0], 32) < 0) { + tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) { log_warn(LD_REND, "Identity digest contains illegal characters: %s", tok->args[0]); tor_free(info); |