[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r10640: added encoding and parsing of new ASCII-based (v2) rendezvou (in tor/branches/114-dist-storage: . src/or)
Author: kloesing
Date: 2007-06-17 11:40:27 -0400 (Sun, 17 Jun 2007)
New Revision: 10640
Modified:
tor/branches/114-dist-storage/ChangeLog
tor/branches/114-dist-storage/src/or/config.c
tor/branches/114-dist-storage/src/or/connection_edge.c
tor/branches/114-dist-storage/src/or/directory.c
tor/branches/114-dist-storage/src/or/or.h
tor/branches/114-dist-storage/src/or/rendclient.c
tor/branches/114-dist-storage/src/or/rendcommon.c
tor/branches/114-dist-storage/src/or/rendservice.c
tor/branches/114-dist-storage/src/or/routerparse.c
Log:
added encoding and parsing of new ASCII-based (v2) rendezvous service descriptors
Modified: tor/branches/114-dist-storage/ChangeLog
===================================================================
--- tor/branches/114-dist-storage/ChangeLog 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/ChangeLog 2007-06-17 15:40:27 UTC (rev 10640)
@@ -1,3 +1,39 @@
+Changes in branch 114-dist-storage - 2007-??-??
+
+ o Completed features:
+ - Hidden service providers can set the config option
+ StoreV2HidServDescriptorsToDisk to store v2 descriptors to their local
+ data directory whenever they consider publishing a new service descriptor
+ (for testing purposes only).
+ - On startup, hidden service providers check whether they publish v2
+ service descriptors and if they have already created a secret_cookie and
+ a hostname2 file; if not, they create them.
+ - Hidden service providers can set the config option
+ PublishV2HidServDescriptors to publish v2 descriptors to the directory
+ (publishing is not implemented, yet).
+ - Hidden service clients can set the config option
+ LoadV2HidServDescriptorsFromDisk to load v2 descriptors from their local
+ data directory whenever they get a request for a v2 onion address.
+ - Hidden service clients handle SOCKS requests for v2 onion addresses by
+ trying to load v2 descriptors from the data directory, parse them, and
+ store them to the renddesc cache.
+ - Hidden service clients can set the config option
+ FetchV2HidServDescriptors to fetch v2 descriptors from the directory
+ (fetching is not implemented, yet).
+
+ o Planned features:
+ - Hidden service providers can set the config option HiddenServiceCookie
+ that overrides the random secret_cookie with one or more user defined
+ passphrases, or rather hashes of those.
+ - Hidden service directory nodes parse and store v2 service descriptors
+ from hidden service providers.
+ - Hidden service directory nodes serve v2 service descriptors to hidden
+ service clients.
+ - Hidden service clients try to fetch v2 service descriptors from directory
+ nodes (instead of loading them from the local data directory as it is
+ currently done).
+
+
Changes in version 0.2.0.3-alpha - 2007-??-??
o Minor features:
- Create listener connections before we setuid to the configured User and
Modified: tor/branches/114-dist-storage/src/or/config.c
===================================================================
--- tor/branches/114-dist-storage/src/or/config.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/config.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -174,6 +174,7 @@
VAR("FastFirstHopPK", BOOL, FastFirstHopPK, "1"),
VAR("FetchServerDescriptors",BOOL, FetchServerDescriptors,"1"),
VAR("FetchHidServDescriptors",BOOL, FetchHidServDescriptors, "1"),
+ VAR("FetchV2HidServDescriptors",BOOL,FetchV2HidServDescriptors,"0"),
VAR("FetchUselessDescriptors",BOOL, FetchUselessDescriptors, "0"),
VAR("Group", STRING, Group, NULL),
VAR("HardwareAccel", BOOL, HardwareAccel, "0"),
@@ -191,6 +192,8 @@
VAR("HttpsProxyAuthenticator",STRING,HttpsProxyAuthenticator,NULL),
OBSOLETE("IgnoreVersion"),
VAR("KeepalivePeriod", INTERVAL, KeepalivePeriod, "5 minutes"),
+ VAR("LoadV2HidServDescriptorsFromDisk",BOOL,
+ LoadV2HidServDescriptorsFromDisk,"0"),
VAR("Log", LINELIST, Logs, NULL),
OBSOLETE("LinkPadding"),
OBSOLETE("LogLevel"),
@@ -221,6 +224,7 @@
VAR("ProtocolWarnings", BOOL, ProtocolWarnings, "0"),
VAR("PublishServerDescriptor", CSV, PublishServerDescriptor,"v1,v2"),
VAR("PublishHidServDescriptors",BOOL,PublishHidServDescriptors, "1"),
+ VAR("PublishV2HidServDescriptors",BOOL,PublishV2HidServDescriptors,"0"),
VAR("ReachableAddresses", LINELIST, ReachableAddresses, NULL),
VAR("ReachableDirAddresses",LINELIST,ReachableDirAddresses,NULL),
VAR("ReachableORAddresses",LINELIST, ReachableORAddresses, NULL),
@@ -252,6 +256,8 @@
VAR("SocksPort", UINT, SocksPort, "9050"),
VAR("SocksTimeout", INTERVAL, SocksTimeout, "2 minutes"),
OBSOLETE("StatusFetchPeriod"),
+ VAR("StoreV2HidServDescriptorsToDisk",BOOL,
+ StoreV2HidServDescriptorsToDisk,"0"),
VAR("StrictEntryNodes", BOOL, StrictEntryNodes, "0"),
VAR("StrictExitNodes", BOOL, StrictExitNodes, "0"),
OBSOLETE("SysLog"),
Modified: tor/branches/114-dist-storage/src/or/connection_edge.c
===================================================================
--- tor/branches/114-dist-storage/src/or/connection_edge.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/connection_edge.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -1375,6 +1375,7 @@
/* it's a hidden-service request */
rend_cache_entry_t *entry;
int r;
+ time_t now = time(NULL);
tor_assert(!automap);
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
/* if it's a resolve request, fail it right now, rather than
@@ -1407,16 +1408,30 @@
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
+/** How long after we receive a hidden service descriptor do we consider
+ * it valid? */
+#define NUM_SECONDS_BEFORE_HS_REFETCH (60*15)
+ /* if necessary and configured, try to read descriptor from disk */
+ if (get_options()->LoadV2HidServDescriptorsFromDisk &&
+ (r==0 || (r>0 && now - entry->received >=
+ NUM_SECONDS_BEFORE_HS_REFETCH))) {
+ if (rend_load_service_descriptor_from_disk(socks->address,
+ time(NULL)) < 0) {
+ log_warn(LD_REND, "Loading of renddesc from disk failed for id %s",
+ escaped_safe_str(socks->address));
+ } else {
+ log_debug(LD_REND,"Loading of renddesc from disk successful for id %s",
+ escaped_safe_str(socks->address));
+ r = rend_cache_lookup_entry(socks->address, -1, &entry);
+ }
+ }
if (r==0) {
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str(conn->rend_query));
rend_client_refetch_renddesc(conn->rend_query);
} else { /* r > 0 */
-/** How long after we receive a hidden service descriptor do we consider
- * it valid? */
-#define NUM_SECONDS_BEFORE_HS_REFETCH (60*15)
- if (time(NULL) - entry->received < NUM_SECONDS_BEFORE_HS_REFETCH) {
+ if (now - entry->received < NUM_SECONDS_BEFORE_HS_REFETCH) {
conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here and fresh enough. Great.");
if (connection_ap_handshake_attach_circuit(conn) < 0) {
@@ -2582,31 +2597,27 @@
hostname_type_t
parse_extended_hostname(char *address)
{
- char *s;
- char query[REND_SERVICE_ID_LEN+1];
+ char *s;
- s = strrchr(address,'.');
- if (!s)
- return NORMAL_HOSTNAME; /* no dot, thus normal */
- if (!strcmp(s+1,"exit")) {
- *s = 0; /* nul-terminate it */
- return EXIT_HOSTNAME; /* .exit */
- }
- if (strcmp(s+1,"onion"))
- return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
+ s = strrchr(address,'.');
+ if (!s)
+ return NORMAL_HOSTNAME; /* no dot, thus normal */
+ if (!strcmp(s+1,"exit")) {
+ *s = 0; /* nul-terminate it */
+ return EXIT_HOSTNAME; /* .exit */
+ }
+ if (strcmp(s+1,"onion"))
+ return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
- /* so it is .onion */
- *s = 0; /* nul-terminate it */
- if (strlcpy(query, address, REND_SERVICE_ID_LEN+1) >=
- REND_SERVICE_ID_LEN+1)
- goto failed;
- if (rend_valid_service_id(query)) {
- return ONION_HOSTNAME; /* success */
- }
-failed:
- /* otherwise, return to previous state and return 0 */
- *s = '.';
- return BAD_HOSTNAME;
+ /* so it is .onion */
+ *s = 0; /* nul-terminate it */
+ if (rend_valid_service_id(address) || rend_valid_v2_service_id(address)) {
+ return ONION_HOSTNAME; /* success */
+ }
+
+ /* otherwise, return to previous state and return 0 */
+ *s = '.';
+ return BAD_HOSTNAME;
}
/** Check if the address is of the form "y.noconnect"
Modified: tor/branches/114-dist-storage/src/or/directory.c
===================================================================
--- tor/branches/114-dist-storage/src/or/directory.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/directory.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -1367,7 +1367,11 @@
(int)body_len, status_code, escaped(reason));
switch (status_code) {
case 200:
- if (rend_cache_store(body, body_len, 0) < 0) {
+ /* TODO when implementing storing/fetching v2 descriptors, replace NULL
+ * by secret_cookie that was given to us in the onion address when the
+ * client requested the hidden service (need to memorize that
+ * somewhere). This way it won't work!! */
+ if (rend_cache_store(body, body_len, 0, 1, NULL) < 0) {
log_warn(LD_REND,"Failed to store rendezvous descriptor.");
/* alice's ap_stream will notice when connection_mark_for_close
* cleans it up */
@@ -2055,7 +2059,7 @@
if (options->HSAuthoritativeDir &&
!strcmpstart(url,"/tor/rendezvous/publish")) {
/* rendezvous descriptor post */
- if (rend_cache_store(body, body_len, 1) < 0) {
+ if (rend_cache_store(body, body_len, 1, 1, NULL) < 0) {
// char tmp[1024*2+1];
log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV,
"Rejected rend descriptor (length %d) from %s.",
Modified: tor/branches/114-dist-storage/src/or/or.h
===================================================================
--- tor/branches/114-dist-storage/src/or/or.h 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/or.h 2007-06-17 15:40:27 UTC (rev 10640)
@@ -1870,8 +1870,14 @@
authority_type_t _PublishServerDescriptor;
/** Boolean: do we publish hidden service descriptors to the HS auths? */
int PublishHidServDescriptors;
+ int PublishV2HidServDescriptors; /**< and v2 hidden service descriptors? */
+ /** do we store v2 hidden service descriptors to disk? */
+ int StoreV2HidServDescriptorsToDisk;
+ /** do we first try to read v2 hidden service descriptors from disk? */
+ int LoadV2HidServDescriptorsFromDisk;
int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */
int FetchHidServDescriptors; /** and hidden service descriptors? */
+ int FetchV2HidServDescriptors; /** and v2 hidden service descriptors? */
int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */
int AllDirActionsPrivate; /**< Should every directory action be sent
* through a Tor circuit? */
@@ -3043,13 +3049,14 @@
int rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc);
+int rend_load_service_descriptor_from_disk(const char *service_id, time_t now);
/********************************* rendcommon.c ***************************/
/** Information used to connect to a hidden service. */
typedef struct rend_service_descriptor_t {
crypto_pk_env_t *pk; /**< This service's public key. */
- int version; /**< 0 or 1. */
+ int version; /**< 0 to 2. */
time_t timestamp; /**< Time when the descriptor was generated. */
uint16_t protocols; /**< Bitmask: which rendezvous protocols are supported?
* (We allow bits '0', '1', and '2' to be set.) */
@@ -3063,6 +3070,27 @@
* from this array if introduction attempts fail. If this array is present,
* its elements correspond to the elements of intro_points. */
extend_info_t **intro_point_extend_info;
+ /** Descriptor identifiers (current and next) for distributed storage. */
+ char desc_id[2][20];
+ /** Number of seconds that the current desc_id will be valid. */
+ int seconds_valid;
+ /** Permanent part of the computation of desc_id that is derived from pk
+ * or passed by a client as part of the onion address. */
+ char permanent_id[10];
+ /** Changing part (current and next) of the computation of desc_id that is
+ * computed from time_period and secret_cookie. */
+ char secret_id_part[2][20];
+ /** Derived value of current and next time period. */
+ char time_period[2][4];
+ /** AES-encrypted list of introduction points. */
+ char *intro_points_encrypted;
+ /** Size of encrypted string. */
+ int intro_points_encrypted_size;
+ /** Secret cookie for generating secret_id_part and {en|de}crypting the
+ * introduction points. Is randomly generated by the hidden service provider
+ * on first configuration of the service and passed by a client as part of
+ * the onion address; storing nodes do not know this and set it to NULL. */
+ char secret_cookie[16];
} rend_service_descriptor_t;
int rend_cmp_service_ids(const char *one, const char *two);
@@ -3092,12 +3120,21 @@
void rend_cache_clean(void);
void rend_cache_free_all(void);
int rend_valid_service_id(const char *query);
+int rend_valid_v2_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_store(const char *desc, size_t desc_len, int published);
+int rend_cache_store(const char *desc, size_t desc_len, int published,
+ int version, const char *secret_cookie);
int rend_cache_size(void);
+int rend_encode_v2_descriptor(char *current_desc, char *next_desc,
+ rend_service_descriptor_t *desc,
+ time_t now, //crypto_pk_env_t *key,
+ const char *secret_cookie);//,
+ //char *descriptor_id_base32);
+int rend_compute_desc_id(char *desc_id, const char *permanent_id,
+ const char *secret_cookie, time_t now);
/********************************* rendservice.c ***************************/
@@ -3449,6 +3486,10 @@
authority_cert_t *authority_cert_parse_from_string(const char *s,
const char **end_of_string);
+int rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
+ const char *str);
+int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+ const char *secret_cookie);
#endif
Modified: tor/branches/114-dist-storage/src/or/rendclient.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendclient.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendclient.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -247,6 +247,57 @@
return 0;
}
+/* Loads the rendezvous service descriptor for <b>address</b> from a file with
+ * file name "<corresponding descriptor id>.onion" from the data directory,
+ * parses it and stores it to the descritor cache, if it is valid. Returns 0 if
+ * successful, -1 otherwise. */
+int
+rend_load_service_descriptor_from_disk(const char *address, time_t now)
+{
+
+ char *s = NULL; /* string read from file */
+ char *fname; /* file name */
+ int flen; /* length of file name */
+ char service_id[10]; /* service id part of address in binary format */
+ char secret_cookie[15]; /* cookie part of address in binary format */
+ char descriptor_id_digest[20]; /* computed descriptor id in binary format */
+ char descriptor_id_base32[32+1]; /* base32-formatted descriptor id */
+
+ /* address is formatted like "oo4qinerb64ybxkd.xvqwspfbixjhz2i2qwixb2ej" */
+
+ /* extract service id */
+ base32_decode(service_id, 10, address, 16);
+
+ /* extract cookie */
+ base32_decode(secret_cookie, 15, address+17, 24);
+
+ /* compute current descriptor id */
+ rend_compute_desc_id(descriptor_id_digest, service_id, secret_cookie, now);
+
+ /* encode base 32 representation of descriptor id */
+ base32_encode(descriptor_id_base32, 32+1, descriptor_id_digest, 20);
+
+ /* construct file name */
+ flen = strlen(get_options()->DataDirectory) + 40;
+ fname = tor_malloc(flen);
+ tor_snprintf(fname, flen, "%s"PATH_SEPARATOR"%s.rsd",
+ get_options()->DataDirectory, descriptor_id_base32);
+
+ /* read string from file */
+ if (!(s = read_file_to_str(fname, 0, NULL))) {
+ log_warn(LD_FS, "Unable to read descriptor \"%s\"", fname);
+ return -1;
+ }
+
+ /* parse and store descriptor */
+ if (rend_cache_store(s, strlen(s), 0, 2, secret_cookie) < 0) {
+ log_warn(LD_FS, "Could not add descriptor to the cache");
+ return -1;
+ }
+
+ return 0;
+}
+
/** If we are not currently fetching a rendezvous service descriptor
* for the service ID <b>query</b>, start a directory connection to fetch a
* new one.
@@ -254,8 +305,12 @@
void
rend_client_refetch_renddesc(const char *query)
{
- if (!get_options()->FetchHidServDescriptors)
+ if (!get_options()->FetchHidServDescriptors &&
+ !get_options()->FetchV2HidServDescriptors)
return;
+
+ /* TODO fetching v2 hidden service descriptors not implemented yet! */
+
if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR, 0, query)) {
log_info(LD_REND,"Would fetch a new renddesc here (for %s), but one is "
"already in progress.", escaped_safe_str(query));
Modified: tor/branches/114-dist-storage/src/or/rendcommon.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendcommon.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendcommon.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -43,6 +43,292 @@
tor_free(desc);
}
+#define TIME_PERIOD_LENGTH 600
+
+/* Fill the fields of <b>desc</b> so that it a valid v2 descriptor can be
+ * encoded from it. Use the current time <b>now</b> and the
+ * <b>secret_cookie</b>. Returns 0 on success, -1 otherwise. */
+static int
+rend_compute_v2_descriptor_fields(rend_service_descriptor_t *desc,
+ time_t now, const char *secret_cookie)
+{
+
+ char permanent_id_temp[DIGEST_LEN];
+ double time_period_part1;
+ double time_period_part2;
+ uint64_t permanent_id_value = 0;
+ uint32_t time_period;
+ int i;
+ crypto_digest_env_t *digest;
+
+ /* obtain permanent-id from public key */
+ crypto_pk_get_digest(desc->pk, permanent_id_temp);
+ memcpy(desc->permanent_id, permanent_id_temp, 10);
+
+ /* copy secret_cookie */
+ memcpy(desc->secret_cookie, secret_cookie, 15);
+ desc->secret_cookie[15] = 0;
+
+ /* calculate current time-period */
+ time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
+ permanent_id_value=((((uint64_t) desc->permanent_id[0]) + 256) % 256) << 32;
+ permanent_id_value+=((((uint64_t) desc->permanent_id[1]) + 256) % 256) << 24;
+ permanent_id_value+=((((uint64_t) desc->permanent_id[2]) + 256) % 256) << 16;
+ permanent_id_value+=((((uint64_t) desc->permanent_id[3]) + 256) % 256) << 8;
+ permanent_id_value+=(((uint64_t) desc->permanent_id[4]) + 256) % 256;
+ time_period_part2 = ((double) permanent_id_value) /
+ ((double) (((uint64_t) 1) << 40));
+ time_period = (uint32_t) (time_period_part1 + time_period_part2);
+ desc->seconds_valid = TIME_PERIOD_LENGTH - (uint32_t) (((time_period_part1
+ + time_period_part2) - (double) time_period) * TIME_PERIOD_LENGTH);
+
+ /* Compute descriptor ids for the current and the next time period */
+ for (i = 0; i < 2; i++) {
+
+ /* time-period */
+ desc->time_period[i][0] = (char) ((time_period+i) >> 24);
+ desc->time_period[i][1] = (char) ((time_period+i) >> 16);
+ desc->time_period[i][2] = (char) ((time_period+i) >> 8);
+ desc->time_period[i][3] = (char) (time_period+i);
+
+ /* secret-id-part = h(time-period + cookie) */
+ digest = crypto_new_digest_env();
+ crypto_digest_add_bytes(digest, desc->time_period[i], 4);
+ crypto_digest_add_bytes(digest, desc->secret_cookie, 16);
+ crypto_digest_get_digest(digest, desc->secret_id_part[i], 20);
+ crypto_free_digest_env(digest);
+
+ /* descriptor id */
+ digest = crypto_new_digest_env();
+ crypto_digest_add_bytes(digest, desc->permanent_id, 10);
+ crypto_digest_add_bytes(digest, desc->secret_id_part[i], 20);
+ crypto_digest_get_digest(digest, desc->desc_id[i], 20);
+ crypto_free_digest_env(digest);
+ }
+
+ return 0;
+}
+
+/* Compute the <b>desc_id</b> for a given <b>service_id</b> and
+ * <b>secret_cookie</b> at time <b>now</b>. */
+int
+rend_compute_desc_id(char *desc_id, const char *service_id,
+ const char *secret_cookie, time_t now)
+{
+ uint64_t permanent_id_value = 0;
+ double time_period_part1;
+ double time_period_part2;
+ char time_period_bytes[4];
+ uint32_t time_period;
+ crypto_digest_env_t *digest;
+ char secret_id_part_digest[20];
+ char secret_cookie_16[16];
+
+ /* time-period */
+ time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
+ permanent_id_value=((((uint64_t) service_id[0]) + 256) % 256) << 32;
+ permanent_id_value+=((((uint64_t) service_id[1]) + 256) % 256) << 24;
+ permanent_id_value+=((((uint64_t) service_id[2]) + 256) % 256) << 16;
+ permanent_id_value+=((((uint64_t) service_id[3]) + 256) % 256) << 8;
+ permanent_id_value+=(((uint64_t) service_id[4]) + 256) % 256;
+ time_period_part2 = ((double) permanent_id_value) /
+ ((double) (((uint64_t) 1) << 40));
+ time_period = (uint32_t) (time_period_part1 + time_period_part2);
+ time_period_bytes[0] = (char) (time_period >> 24);
+ time_period_bytes[1] = (char) (time_period >> 16);
+ time_period_bytes[2] = (char) (time_period >> 8);
+ time_period_bytes[3] = (char) time_period;
+
+ /* secret-id-part = h(time-period + cookie) */
+ digest = crypto_new_digest_env();
+ crypto_digest_add_bytes(digest, time_period_bytes, 4);
+ memcpy(secret_cookie_16, secret_cookie, 15);
+ secret_cookie_16[15] = 0;
+ crypto_digest_add_bytes(digest, secret_cookie_16, 16);
+ crypto_digest_get_digest(digest, secret_id_part_digest, 20);
+ crypto_free_digest_env(digest);
+
+ /* descriptor id */
+ digest = crypto_new_digest_env();
+ crypto_digest_add_bytes(digest, service_id, 10);
+ crypto_digest_add_bytes(digest, secret_id_part_digest, 20);
+ crypto_digest_get_digest(digest, desc_id, 20);
+ crypto_free_digest_env(digest);
+
+ return 0;
+}
+
+/* Encodes the current (<b>current_desc</b>) and next descriptor (<b>next_desc</b>) from
+ * <b>desc</b>. Returns 0 on success, -1 otherwise. */
+int
+rend_encode_v2_descriptor(char *current_desc, char *next_desc,
+ rend_service_descriptor_t *desc, time_t now,
+ const char *secret_cookie)
+{
+
+ char *pkey; /* public key, PEM-encoded. */
+ size_t pkeylen; /* public key length */
+ char published[ISO_TIME_LEN+1]; /* timestamp */
+ size_t iposlen; /* max length of unencrypted introduction points */
+ char *ipos; /* unencrypted introduction points */
+ int ipowritten; /* actual length of unencrypted introduction points */
+ char secret_id_part_base32[32+1];
+ char descriptor_id_base32[32+1];
+ int i;
+ char *ipos_enc;
+ int enclen;
+ char *ipos_encrypted_base64;
+ int result = 0;
+ size_t written = 0;
+ char desc_digest[DIGEST_LEN];
+ char *buf;
+
+ /* prepare descriptor */
+ rend_compute_v2_descriptor_fields(desc, now, secret_cookie);
+
+ /* PEM-encode the public key */
+ if (crypto_pk_write_public_key_to_string(desc->pk, &pkey, &pkeylen) < 0) {
+ log_warn(LD_DIR, "write public key failed!");
+ return -1;
+ }
+
+ /* encode timestamp */
+ format_iso_time(published, desc->timestamp);
+
+ /* assemble unencrypted list of introduction points */
+ iposlen = desc->n_intro_points * 700;
+ ipos = tor_malloc_zero(iposlen);
+ ipowritten = 0;
+ for (i=0; i < desc->n_intro_points; ++i) {
+ char id_base32[32+1];
+ char *okey; /* onion key, PEM-encoded. */
+ size_t okeylen;
+ char *skey; /* service key, PEM_encoded. */
+ size_t skeylen;
+ int res;
+
+ /* obtain extend info with introduction point details */
+ extend_info_t *info = desc->intro_point_extend_info[i];
+
+ /* encode introduction point ID */
+ base32_encode(id_base32, 32+1, info->identity_digest, DIGEST_LEN);
+
+ /* encode onion key */
+ if (crypto_pk_write_public_key_to_string(info->onion_key, &okey,
+ &okeylen) < 0) {
+ log_warn(LD_DIR, "write onion key failed");
+ continue;
+ }
+
+ /* encode service key; TODO replace Bob's public key by newly generated
+ * service key */
+ if (crypto_pk_write_public_key_to_string(desc->pk, &skey, &skeylen) < 0) {
+ log_warn(LD_DIR, "write service key failed");
+ continue;
+ }
+
+ /* assemble everything for this introduction point */
+ res = tor_snprintf(ipos+ipowritten, iposlen-ipowritten,
+ "introduction-point %s\n"
+ "ip-address %u.%u.%u.%u\n"
+ "onion-port %d\n"
+ "onion-key\n%s"
+ "service-key\n%s",
+ id_base32,
+ (info->addr >> 24) % 256,
+ (info->addr >> 16) % 256,
+ (info->addr >> 8) % 256,
+ info->addr % 256,
+ info->port,
+ okey,
+ skey);
+ if (res < 0) {
+ log_warn(LD_BUG, "not enough space for writing ipo");
+ return -1;
+ }
+
+ /* update total number of written bytes for unencrypted intro points */
+ ipowritten += res;
+ }
+
+ /* finalize unencrypted introduction points */
+ ipos[ipowritten++] = '\n';
+ ipos[ipowritten++] = 0;
+
+ /* encrypt introduction points */
+ ipos_enc = tor_malloc_zero(ipowritten + 32);
+ enclen = crypto_cipher_encrypt_cbc(desc->secret_cookie, ipos_enc,
+ ipos, ipowritten);
+
+ /* free memory for unencrypted introduction points */
+ tor_free(ipos);
+
+ /* base64-encode introduction points */
+ ipos_encrypted_base64 = tor_malloc_zero(ipowritten * 2);
+ if (base64_encode(ipos_encrypted_base64, ipowritten * 2, ipos_enc,
+ enclen) < 0) {
+ log_warn(LD_DIR, "could not encode ipos to base64");
+ return -1;
+ }
+
+ /* encode both, the current and the next descriptor */
+ for (i = 0; i < 2; i++) {
+ buf = (i == 0 ? current_desc : next_desc);
+ base32_encode(secret_id_part_base32, 32+1, desc->secret_id_part[i], 20);
+ base32_encode(descriptor_id_base32, 32+1, desc->desc_id[i], 20);
+
+ /* complete descriptor; TODO make 4000 a little more dynamic */
+ result = tor_snprintf(buf, 4000,
+ "rendezvous-service-descriptor %s\n"
+ "version 2\n"
+ "permanent-key\n%s"
+ "secret-id-part %s\n"
+ "publication-time %s\n"
+ "protocol-versions %d\n"
+ "introduction-points\n-----BEGIN AES ENCRYPTED MESSAGE-----\n%s"
+ "-----END AES ENCRYPTED MESSAGE-----\n",
+ descriptor_id_base32,
+ pkey,
+ secret_id_part_base32,
+ published,
+ desc->protocols,
+ ipos_encrypted_base64);
+ if (result < 0) {
+ log_warn(LD_BUG, "descriptor ran out of room!");
+ return -1;
+ }
+ written = result;
+
+ /* add signature */
+ strlcpy(buf + written, "signature\n", 4000 - written);
+ written += strlen(buf + written);
+ buf[written] = '\0';
+ if (crypto_digest(desc_digest, buf, written) < 0) {
+ log_warn(LD_BUG, "could not create digest");
+ return -1;
+ }
+ if (router_append_dirobj_signature(buf+written,4000-written,
+ desc_digest,desc->pk) < 0) {
+ log_warn(LD_BUG, "Couldn't sign desc");
+ return -1;
+ }
+ written += strlen(buf+written);
+ if (written+2 > 4000) {
+ log_warn(LD_BUG, "could not finish desc");
+ return -1;
+ }
+ buf[written++] = '\n';
+ buf[written++] = 0;
+
+ }
+
+ /* free memory */
+ tor_free(pkey);
+
+ /* return descriptor size (both descriptors have the same size) */
+ return written;
+}
+
/** Encode a service descriptor for <b>desc</b>, and sign it with
* <b>key</b>. Store the descriptor in *<b>str_out</b>, and set
* *<b>len_out</b> to its length.
@@ -307,6 +593,20 @@
return 1;
}
+/** Return true iff <b>query</b> is a syntactically valid v2 service ID */
+int
+rend_valid_v2_service_id(const char *query)
+{
+ if (strlen(query) != 16 + 1 + 24)
+ return 0;
+ if (strspn(query, BASE32_CHARS) != 40
+ && strspn(query+16, ".") != 1
+ && strspn(query+17, BASE32_CHARS) != 24)
+ return 0;
+
+ return 1;
+}
+
/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>,
* set *<b>e</b> to that entry and return 1. Else return 0. If
* <b>version</b> is nonnegative, only return an entry in that descriptor
@@ -318,6 +618,19 @@
{
char key[REND_SERVICE_ID_LEN+2]; /* 1<query>\0 or 0<query>\0 */
tor_assert(rend_cache);
+ /* if request is v2 formatted, remove secret_cookie part */
+ if (rend_valid_v2_service_id(query)) {
+ char query_without_cookie[REND_SERVICE_ID_LEN+1];
+ *e = NULL;
+ strncpy(query_without_cookie, query, REND_SERVICE_ID_LEN);
+ tor_snprintf(key, sizeof(key), "1%s", query_without_cookie);
+ *e = strmap_get_lc(rend_cache, key);
+ if (!*e)
+ return 0;
+ return 1;
+ }
+
+ /* no v2 request? look for v0 or v1 renddesc */
if (!rend_valid_service_id(query))
return -1;
*e = NULL;
@@ -362,9 +675,13 @@
* it's the same or older than one we've already got; return 1 if
* it's novel. The published flag tells us if we store the descriptor
* in our role as directory (1) or if we cache it as client (0).
+ * The version can be either >= 2 for v2 descriptors or <= 1 for v0/v1
+ * descriptors. If published is 0, secret_cookie needs to contain a key,
+ * otherwise it is NULL.
*/
int
-rend_cache_store(const char *desc, size_t desc_len, int published)
+rend_cache_store(const char *desc, size_t desc_len, int published, int version,
+ const char *secret_cookie)
{
rend_cache_entry_t *e;
rend_service_descriptor_t *parsed;
@@ -373,10 +690,23 @@
time_t now;
or_options_t *options = get_options();
tor_assert(rend_cache);
- parsed = rend_parse_service_descriptor(desc,desc_len);
- if (!parsed) {
- log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
- return -1;
+ if (version >= 2) {
+ parsed = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ if (rend_parse_v2_service_descriptor(parsed, desc) < 0) {
+ log_warn(LD_FS, "Could not parse descriptor");
+ return -1;
+ }
+ if (!published &&
+ rend_decrypt_introduction_points(parsed, secret_cookie) < 0) {
+ log_warn(LD_PROTOCOL,"Couldn't decrypt introduction points.");
+ return -1;
+ }
+ } else {
+ parsed = rend_parse_service_descriptor(desc,desc_len);
+ if (!parsed) {
+ log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
+ return -1;
+ }
}
if (rend_get_service_id(parsed->pk, query)<0) {
log_warn(LD_BUG,"Couldn't compute service ID.");
Modified: tor/branches/114-dist-storage/src/or/rendservice.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendservice.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendservice.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -57,6 +57,7 @@
rend_service_descriptor_t *desc;
time_t desc_is_dirty;
time_t next_upload_time;
+ char *secret_cookie;
} rend_service_t;
/** A list of rend_service_t's for services run on this OP.
@@ -320,6 +321,30 @@
}
}
+/* Write the v2 onion address of <b>service</b> to <b>hostname2</b>. */
+static int
+rend_get_hostname2(rend_service_t *service, char *hostname2)
+{
+
+ char permanent_id_base32[16+1];
+ char cookie_base32[24+1];
+
+ /* base32-encode service id */
+ base32_encode(permanent_id_base32, 16+1, service->pk_digest, 10);
+
+ /* base32-encode cookie */
+ base32_encode(cookie_base32, 24+1, service->secret_cookie, 15);
+
+ /* assemlbe onion address */
+ if (tor_snprintf(hostname2, 50, "%s.%s.onion\n", permanent_id_base32,
+ cookie_base32) < 0) {
+ log_warn(LD_BUG, "could not encode hostname");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Load and/or generate private keys for all hidden services. Return 0 on
* success, -1 on failure.
*/
@@ -332,6 +357,10 @@
char buf[128];
for (i=0; i < smartlist_len(rend_service_list); ++i) {
+
+ char hostname2[50];
+ char fname2[512];
+
s = smartlist_get(rend_service_list,i);
if (s->private_key)
continue;
@@ -353,7 +382,23 @@
s->private_key = init_key_from_file(fname, 1, LOG_ERR);
if (!s->private_key)
return -1;
-
+ /* Try to read secret_cookie file; if that fails, create new file */
+ if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
+ strlcat(fname,PATH_SEPARATOR"secret_cookie",sizeof(fname))
+ >= sizeof(fname)) {
+ log_warn(LD_CONFIG,"Directory name too long to store secret_cookie file:"
+ " \"%s\".", s->directory);
+ return -1;
+ }
+ if (!(s->secret_cookie = read_file_to_str(fname, 0, NULL))) {
+ s->secret_cookie = tor_malloc_zero(16);
+ crypto_rand(s->secret_cookie, 16);
+ if (write_str_to_file(fname, s->secret_cookie, 0) < 0) {
+ log_warn(LD_CONFIG, "Could not write secret_cookie file:"
+ " \"%s\".", s->directory);
+ return -1;
+ }
+ }
/* Create service file */
if (rend_get_service_id(s->private_key, s->service_id)<0) {
log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
@@ -373,6 +418,18 @@
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
if (write_str_to_file(fname,buf,0)<0)
return -1;
+
+ /* write hostname2 file */
+ rend_get_hostname2(s, hostname2);
+ if (strlcpy(fname2,s->directory,sizeof(fname2)) >= sizeof(fname2) ||
+ strlcat(fname2,PATH_SEPARATOR"hostname2",sizeof(fname2))
+ >= sizeof(fname2)) {
+ log_warn(LD_CONFIG, "Directory name too long to store hostname2 file:"
+ " \"%s\".", s->directory);
+ return -1;
+ }
+ if (write_str_to_file(fname2, hostname2, 0) < 0)
+ return -1;
}
return 0;
}
@@ -1068,6 +1125,62 @@
smartlist_free(exclude_routers);
}
+/* Stores the current and possibly next rendezvous service descriptor for
+ * <b>service</b> at time <b>now</b> to one or two files with file name
+ * "<corresponding descriptor id>.onion" to the data directory. Returns 0 if
+ * successful, -1 otherwise. */
+static int
+rend_store_service_descriptor_to_disk(rend_service_t *service, time_t now)
+{
+
+ char *current_desc;
+ char *next_desc;
+ size_t flen; // len of file name
+ char *fname; // file name
+ char descriptor_id_base32[32+1];
+ int i, n = 1;
+
+ /* TODO make 4000 configurable */
+ current_desc = tor_malloc_zero(4000);
+ next_desc = tor_malloc_zero(4000);
+
+ /* TODO might be better to prevent this from being executed multiple times */
+ rend_service_update_descriptor(service);
+
+ /* encode descriptor */
+ if (rend_encode_v2_descriptor(current_desc, next_desc, service->desc, now,
+ service->secret_cookie) < 0) {
+ log_warn(LD_DIR, "could not encode descriptor");
+ return -1;
+ }
+
+ /* write to file */
+ flen = strlen(get_options()->DataDirectory) + 40;
+ fname = tor_malloc(flen);
+
+ /* write one or two descriptors to disk? TODO make 6 configurable */
+ if (service->desc->seconds_valid < 6) n = 2;
+
+ for (i = 0; i < 2; i++) {
+
+ /* base32-encode descriptor id */
+ base32_encode(descriptor_id_base32, 32+1, service->desc->desc_id[i], 20);
+
+ /* determine file name */
+ tor_snprintf(fname, flen, "%s"PATH_SEPARATOR"%s.rsd",
+ get_options()->DataDirectory, descriptor_id_base32);
+
+ /* write string to file */
+ write_str_to_file(fname,(i == 0 ? current_desc : next_desc),0);
+ }
+
+ tor_free(current_desc);
+ tor_free(next_desc);
+ tor_free(fname);
+
+ return 0;
+}
+
/** Regenerate and upload rendezvous service descriptors for all
* services, if necessary. If the descriptor has been dirty enough
* for long enough, definitely upload; else only upload when the
@@ -1083,7 +1196,9 @@
rend_service_t *service;
int rendpostperiod = get_options()->RendPostPeriod;
- if (!get_options()->PublishHidServDescriptors)
+ if (!get_options()->PublishHidServDescriptors &&
+ !get_options()->PublishV2HidServDescriptors &&
+ !get_options()->StoreV2HidServDescriptorsToDisk)
return;
for (i=0; i < smartlist_len(rend_service_list); ++i) {
@@ -1098,7 +1213,18 @@
/* if it's time, or if the directory servers have a wrong service
* descriptor and ours has been stable for 30 seconds, upload a
* new one of each format. */
- upload_service_descriptor(service, 0);
+ if (get_options()->StoreV2HidServDescriptorsToDisk) {
+ if (rend_store_service_descriptor_to_disk(service, now) < 0) {
+ log_warn(LD_DIR, "could not store descriptor to disk");
+ }
+ log_warn(LD_DIR, "stored descriptor to disk");
+ }
+ if (get_options()->PublishHidServDescriptors) {
+ upload_service_descriptor(service, 0);
+ }
+
+ /* TODO publishing v2 hidden service descriptors not implemented yet! */
+
service->next_upload_time = now + rendpostperiod;
}
}
Modified: tor/branches/114-dist-storage/src/or/routerparse.c
===================================================================
--- tor/branches/114-dist-storage/src/or/routerparse.c 2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/routerparse.c 2007-06-17 15:40:27 UTC (rev 10640)
@@ -76,6 +76,21 @@
K_VOTE_DIGEST,
K_CONSENSUS_DIGEST,
+ R_RENDEZVOUS_SERVICE_DESCRIPTOR,
+ R_VERSION,
+ R_PERMANENT_KEY,
+ R_SECRET_ID_PART,
+ R_PUBLICATION_TIME,
+ R_PROTOCOL_VERSIONS,
+ R_INTRODUCTION_POINTS,
+ R_SIGNATURE,
+
+ R_IPO_IDENTIFIER,
+ R_IPO_IP_ADDRESS,
+ R_IPO_ONION_PORT,
+ R_IPO_ONION_KEY,
+ R_IPO_SERVICE_KEY,
+
_UNRECOGNIZED,
_ERR,
_EOF,
@@ -287,6 +302,31 @@
END_OF_TABLE
};
+/** List of tokens allowable in rendezvous service derscriptors */
+static token_rule_t desc_token_table[] = {
+ T1("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR, EQ(1), \
+ NO_OBJ),
+ T1("version", R_VERSION, EQ(1), NO_OBJ),
+ T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
+ T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
+ T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
+ T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
+ T1("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
+ T1("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
+ END_OF_TABLE
+};
+
+/** List of tokens allowed in the (encrypted) list of introduction points of
+ * rendezvous service descriptors */
+static token_rule_t ipo_token_table[] = {
+ T1("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
+ T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
+ T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
+ T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
+ T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
+ END_OF_TABLE
+};
+
static token_rule_t networkstatus_vote_token_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
@@ -2802,3 +2842,246 @@
smartlist_uniq(versions, _compare_tor_version_str_ptr, NULL);
}
+
+/* Parse encoded descriptor; TODO add possibility to parse multiple descs from
+ * one string for replication purposes! */
+int
+rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
+ const char *s)
+{
+
+ const char *end;
+ char digesthash[128];
+ int protocols;
+ smartlist_t *tokens = NULL;
+ directory_token_t *tok;
+ uint8_t version;
+
+ /* point 'end' to a point immediately after the final newline. */
+ end = s + strlen(s);
+ while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
+ --end;
+
+ /* compute descriptor hash */
+ if (router_get_hash_impl(s,digesthash, "rendezvous-service-descriptor ",
+ "\nsignature", '\n') < 0) {
+ log_warn(LD_DIR, "Couldn't compute descriptor hash.");
+ return -1;
+ }
+
+ /* tokenize descriptor */
+ tokens = smartlist_create();
+ if (tokenize_string(s,end,tokens,desc_token_table)) {
+ log_warn(LD_DIR, "Error tokenizing descriptor.");
+ goto err;
+ }
+
+ /* check min allowed length of token list */
+ if (smartlist_len(tokens) < 8) {
+ log_warn(LD_DIR, "Impossibly short descriptor.");
+ goto err;
+ }
+
+ /* check whether descriptor starts correctly */
+ tok = smartlist_get(tokens,0);
+ if (tok->tp != R_RENDEZVOUS_SERVICE_DESCRIPTOR) {
+ log_warn(LD_DIR,"Entry does not start with "
+ "\"rendezvous-service-descriptor\"");
+ goto err;
+ }
+
+ /* parse publication time; TODO check if up-to-date */
+ tok = find_first_by_keyword(tokens, R_PUBLICATION_TIME);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ if (parse_iso_time(tok->args[0], &result->timestamp) < 0)
+ goto err;
+
+ /* parse version */
+ tok = find_first_by_keyword(tokens, R_VERSION);
+ version = atoi(tok->args[0]);
+ if (version != 2) {
+ log_warn(LD_DIR, "wrong descriptor version: %d", version);
+ goto err;
+ }
+
+ /* parse public key */
+ tok = find_first_by_keyword(tokens, R_PERMANENT_KEY);
+ tor_assert(tok);
+ result->pk = tok->key;
+ tok->key = NULL; /* Prevent free */
+
+ /* parse secret ID part */
+ tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
+ tor_assert(tok->n_args == 1);
+ if (base32_decode(result->secret_id_part[0], 20, tok->args[0],
+ strlen(tok->args[0]))) {
+ log_warn(LD_DIR, "Couldn't decode secret id part %s",
+ escaped(tok->args[0]));
+ goto err;
+ }
+
+ /* parse descriptor ID */
+ tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
+ tor_assert(tok->n_args == 1);
+ if (base32_decode(result->desc_id[0], 20, tok->args[0],
+ strlen(tok->args[0]))) {
+ log_warn(LD_DIR, "Couldn't decode desc id %s",
+ escaped(tok->args[0]));
+ goto err;
+ }
+
+ /* parse encrypted introduction points */
+ tok = find_first_by_keyword(tokens, R_INTRODUCTION_POINTS);
+ result->intro_points_encrypted = tok->object_body;
+ tok->object_body = NULL; /* Prevent free */
+ result->intro_points_encrypted_size = tok->object_size;
+
+ /* parse descriptor version */
+ tok = find_first_by_keyword(tokens, R_PROTOCOL_VERSIONS);
+ log_warn(LD_DIR, "parsed protocol versions: %s", tok->args[0]);
+ /* for the moment, use version 1 to keep things working TODO change */
+ result->version = 1;
+
+ /* parse protocol versions */
+ protocols = atoi(tok->args[0]);
+ result->protocols = protocols;
+
+ /* parse and verify signature */
+ tok = find_first_by_keyword(tokens, R_SIGNATURE);
+ tor_assert(tok);
+ note_crypto_pk_op(VERIFY_RTR);
+ if (check_signature_token(digesthash, tok, result->pk, 0,
+ "rendezvous service descriptor") < 0)
+ goto err;
+
+ goto done;
+
+ err:
+ /* TODO free some memory */
+
+ done:
+ /* TODO free smartlists and stuff */
+
+ return 0;
+}
+
+int
+rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+ const char *secret_cookie)
+{
+ smartlist_t *intropoints; /* smartlist for parsed intro points */
+ char key[16]; /* decryption key */
+ char *ipos_decrypted; /* decrypted intro points */
+ int unenclen; /* length of unencrypted intro points */
+ smartlist_t *tokens; /* smartlist for intro point tokens */
+ const char **s2; /* currently investigated intro point string */
+ int i;
+
+ /* create smartlists */
+ tokens = smartlist_create();
+ intropoints = smartlist_create();
+
+ /* decrypt introduction points and store info in parsed */
+ ipos_decrypted = tor_malloc_zero(parsed->intro_points_encrypted_size);
+ memcpy(key, secret_cookie, 15);
+ key[15] = 0;
+ unenclen = crypto_cipher_decrypt_cbc(key, ipos_decrypted,
+ parsed->intro_points_encrypted,
+ parsed->intro_points_encrypted_size);
+
+ /* consider one intro point after the other */
+ s2 = (const char **)&ipos_decrypted;
+ while (!strcmpstart(*s2, "introduction-point ")) {
+
+ const char *eos; /* end position of string */
+ directory_token_t *tok; /* single token */
+ extend_info_t *info; /* extend info to write parsed result to */
+ struct in_addr in; /* IP address */
+
+ /* determine end of string */
+ eos = strstr(*s2, "\nintroduction-point ");
+ if (!eos)
+ eos = *s2+strlen(*s2);
+ else
+ eos = eos+1;
+
+ /* free old tokens, if available, and clear token list */
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_clear(tokens);
+
+ /* tokenize string */
+ if (tokenize_string(*s2, eos, tokens, ipo_token_table)) {
+ log_warn(LD_DIR, "Error tokenizing introduction point");
+ goto err;
+ }
+
+ /* check minimum allowed length of introduction point */
+ if (smartlist_len(tokens) < 5) {
+ log_warn(LD_DIR, "Impossibly short introduction point");
+ goto err;
+ }
+
+ /* allocate new extend info */
+ info = tor_malloc_zero(sizeof(extend_info_t));
+
+ /* parse identifier */
+ tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
+ tor_assert(tok);
+
+ /* base32-decode identifier and store it */
+ base32_decode(info->identity_digest, DIGEST_LEN, tok->args[0], 32);
+
+ /* write identifier to nickname */
+ info->nickname[0] = '$';
+ base16_encode(info->nickname+1, sizeof(info->nickname)-1,
+ info->identity_digest, DIGEST_LEN);
+
+ /* parse IP address */
+ tok = find_first_by_keyword(tokens, R_IPO_IP_ADDRESS);
+ if (tor_inet_aton(tok->args[0], &in) != 0) {
+ info->addr = ntohl(in.s_addr);
+ } else {
+ log_warn(LD_DIR, "could not parse IP address");
+ }
+
+ /* parse onion port */
+ tok = find_first_by_keyword(tokens, R_IPO_ONION_PORT);
+ info->port = (uint16_t) atoi(tok->args[0]);
+
+ /* parse onion key */
+ tok = find_first_by_keyword(tokens, R_IPO_ONION_KEY);
+ info->onion_key = tok->key;
+ tok->key = NULL; /* Prevent free */
+
+ /* add extend info to list of introduction points */
+ smartlist_add(intropoints, info);
+
+ /* advance to next introduction point, if available */
+ *s2 = eos;
+ }
+
+ /* write extend infos to descriptor */
+ parsed->n_intro_points = smartlist_len(intropoints);
+ parsed->intro_point_extend_info =
+ tor_malloc_zero(sizeof(extend_info_t*) * parsed->n_intro_points);
+ parsed->intro_points =
+ tor_malloc_zero(sizeof(char*) * parsed->n_intro_points);
+
+ i = 0;
+ SMARTLIST_FOREACH(intropoints, extend_info_t *, ipo, {
+ parsed->intro_points[i] = tor_strdup(ipo->nickname);
+ parsed->intro_point_extend_info[i++] = ipo;
+ });
+
+ goto done;
+
+ err:
+ /* TODO free some memory */
+
+ done:
+ /* TODO free smartlists and stuff */
+
+ return 0;
+}
+