| 
					
				 | 
			
			
				@@ -22,11 +22,14 @@ extern long stats_n_seconds_working; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Do we need to regenerate the directory when someone asks for it? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int the_directory_is_dirty = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int runningrouters_is_dirty = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int networkstatus_v2_is_dirty = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static void directory_remove_invalid(void); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int dirserv_regenerate_directory(void); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char *format_versions_list(config_line_t *ln); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* Should be static; exposed for testing */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 int add_fingerprint_to_dir(const char *nickname, const char *fp, smartlist_t *list); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int router_is_general_exit(routerinfo_t *ri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /************** Fingerprint handling code ************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -660,6 +663,55 @@ dirserv_remove_old_servers(int age) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+format_versions_list(config_line_t *ln) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  smartlist_t *versions; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  versions = smartlist_create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for ( ; ln; ln = ln->next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_split_string(versions, ln->value, ",", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = smartlist_join_strings(versions,",",0,NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  smartlist_free(versions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+append_signature(char *buf, size_t buf_len, const char *digest, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 crypto_pk_env_t *private_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char signature[PK_BYTES]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (crypto_pk_private_sign(private_key, signature, digest, DIGEST_LEN) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN,"Couldn't sign digest."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (strlcat(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto truncated; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  i = strlen(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (base64_encode(buf+i, buf_len-i, signature, 128) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN,"couldn't base64-encode signature"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tor_free(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (strlcat(buf, "-----END SIGNATURE-----\n", buf_len) >= buf_len) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto truncated; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ truncated: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_fn(LOG_WARN,"tried to exceed string length."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Generate a new directory and write it into a newly allocated string. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * Point *<b>dir_out</b> to the allocated string.  Sign the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * directory with <b>private_key</b>.  Return 0 on success, -1 on 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -672,13 +724,11 @@ dirserv_dump_directory_to_string(char **dir_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *router_status; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *identity_pkey; /* Identity key, DER64-encoded. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *recommended_versions; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char digest[20]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char signature[128]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char published[33]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char digest[DIGEST_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char published[ISO_TIME_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   time_t published_on; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *buf = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   size_t buf_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  int i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   size_t identity_pkey_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_assert(dir_out); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -696,18 +746,7 @@ dirserv_dump_directory_to_string(char **dir_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    smartlist_t *versions; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    config_line_t *ln; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    versions = smartlist_create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    for (ln = get_options()->RecommendedVersions; ln; ln = ln->next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      smartlist_split_string(versions, ln->value, ",", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                             SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    recommended_versions = smartlist_join_strings(versions,",",0,NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    smartlist_free(versions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  recommended_versions = format_versions_list(get_options()->RecommendedVersions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   dirserv_remove_old_servers(ROUTER_MAX_AGE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   published_on = time(NULL); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -755,26 +794,11 @@ dirserv_dump_directory_to_string(char **dir_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (crypto_pk_private_sign(private_key, signature, digest, 20) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN,"couldn't sign digest"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    tor_free(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  log(LOG_DEBUG,"generated directory digest begins with %s",hex_str(digest,4)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (strlcat(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    goto truncated; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  i = strlen(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (base64_encode(buf+i, buf_len-i, signature, 128) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN,"couldn't base64-encode signature"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (append_signature(buf,buf_len,digest,private_key)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(buf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (strlcat(buf, "-----END SIGNATURE-----\n", buf_len) >= buf_len) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    goto truncated; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   *dir_out = buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  truncated: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -783,12 +807,6 @@ dirserv_dump_directory_to_string(char **dir_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-/** Most recently generated encoded signed directory. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static char *the_directory = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static size_t the_directory_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static char *the_directory_z = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static size_t the_directory_z_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 typedef struct cached_dir_t { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *dir; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -798,21 +816,22 @@ typedef struct cached_dir_t { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   time_t published; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } cached_dir_t; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Most recently generated encoded signed directory. (auth dirservers only.)*/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t the_directory = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* used only by non-auth dirservers */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-/** If we have no cached directory, or it is older than <b>when</b>, then 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * replace it with <b>directory</b>, published at <b>when</b>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-dirserv_set_cached_directory(const char *directory, time_t when, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                             int is_running_routers) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Used for other dirservers' network statuses.  Map from hexdigest to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * cached_dir_t. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static strmap_t *cached_v2_networkstatus = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+set_cached_dir(cached_dir_t *d, const char *directory, time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  time_t now; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  cached_dir_t *d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  now = time(NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  d = is_running_routers ? &cached_runningrouters : &cached_directory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  time_t now = time(NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (when<=d->published) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     log_fn(LOG_INFO, "Ignoring old directory; not caching."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } else if (when>=now+ROUTER_MAX_AGE) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -829,13 +848,105 @@ dirserv_set_cached_directory(const char *directory, time_t when, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       log_fn(LOG_WARN,"Error compressing cached directory"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     d->published = when; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!is_running_routers) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      char filename[512]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      tor_snprintf(filename,sizeof(filename),"%s/cached-directory", get_options()->DataDirectory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      if (write_str_to_file(filename,cached_directory.dir,0) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        log_fn(LOG_NOTICE, "Couldn't write cached directory to disk. Ignoring."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+clear_cached_dir(cached_dir_t *d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(d->dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(d->dir_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memset(d, 0, sizeof(cached_dir_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+free_cached_dir(void *_d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_t *d = (cached_dir_t *)_d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** If we have no cached directory, or it is older than <b>when</b>, then 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * replace it with <b>directory</b>, published at <b>when</b>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_set_cached_directory(const char *directory, time_t published, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             int is_running_routers) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_t *d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  d = is_running_routers ? &cached_runningrouters : &cached_directory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  set_cached_dir(d, directory, published); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!is_running_routers) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char filename[512]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tor_snprintf(filename,sizeof(filename),"%s/cached-directory", get_options()->DataDirectory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (write_str_to_file(filename,cached_directory.dir,0) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      log_fn(LOG_NOTICE, "Couldn't write cached directory to disk. Ignoring."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_set_cached_networkstatus_v2(const char *directory, const char *fp, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    time_t published) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_t *d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char fname[512]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!cached_v2_networkstatus) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cached_v2_networkstatus = strmap_new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(strlen(fp) == HEX_DIGEST_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!(d = strmap_get(cached_v2_networkstatus, fp))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    d = tor_malloc_zero(sizeof(cached_dir_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    strmap_set(cached_v2_networkstatus, fp, d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  set_cached_dir(d, directory, published); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!d->dir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_snprintf(fname,sizeof(fname), "%s/cached-status/%s", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               get_options()->DataDirectory, fp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (write_str_to_file(fname, d->dir, 0)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_NOTICE, "Couldn't write cached network status to disk. Ignoring."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_obj(const char **out, int compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                cached_dir_t *cache_src, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                cached_dir_t *auth_src, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                time_t dirty, int (*regenerate)(void), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const char *name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_t *d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!get_options()->AuthoritativeDir || !auth_src) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    d = cache_src; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (regenerate != NULL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (dirty && dirty + DIR_REGEN_SLACK_TIME < time(NULL)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (regenerate()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          log_fn(LOG_ERR, "Couldn't generate %s?", name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          exit(1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log_fn(LOG_INFO, "The %s is still clean; reusing.", name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    d = auth_src; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *out = compress ? d->dir_z : d->dir; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (*out) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return compress ? d->dir_z_len : d->dir_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* not yet available. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -845,25 +956,11 @@ dirserv_set_cached_directory(const char *directory, time_t when, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 dirserv_get_directory(const char **directory, int compress) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (!get_options()->AuthoritativeDir) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    cached_dir_t *d = &cached_directory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    *directory = compress ? d->dir_z : d->dir; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (*directory) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return compress ? d->dir_z_len : d->dir_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      /* no directory yet retrieved */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (the_directory_is_dirty && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      the_directory_is_dirty + DIR_REGEN_SLACK_TIME < time(NULL)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (dirserv_regenerate_directory()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log(LOG_INFO,"Directory still clean, reusing."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  *directory = compress ? the_directory_z : the_directory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return compress ? the_directory_z_len : the_directory_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return dirserv_get_obj(directory, compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         &cached_directory, &the_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         the_directory_is_dirty, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         dirserv_regenerate_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         "server directory"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -880,45 +977,31 @@ dirserv_regenerate_directory(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(new_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_directory = new_directory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_directory_len = strlen(the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  log_fn(LOG_INFO,"New directory (size %d):\n%s",(int)the_directory_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_directory_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (tor_gzip_compress(&the_directory_z, &the_directory_z_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        the_directory, the_directory_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        ZLIB_METHOD)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN, "Error gzipping directory."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  set_cached_dir(&the_directory, new_directory, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_fn(LOG_INFO,"New directory (size %d):\n%s",(int)the_directory.dir_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         the_directory.dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   the_directory_is_dirty = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   /* Save the directory to disk so we re-load it quickly on startup. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				    */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  dirserv_set_cached_directory(the_directory, time(NULL), 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  dirserv_set_cached_directory(the_directory.dir, time(NULL), 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static char *the_runningrouters=NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static size_t the_runningrouters_len=0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static char *the_runningrouters_z=NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static size_t the_runningrouters_z_len=0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Replace the current running-routers list with a newly generated one. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-generate_runningrouters(crypto_pk_env_t *private_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+generate_runningrouters(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char *s=NULL, *cp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *s=NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *router_status=NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char digest[DIGEST_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char signature[PK_BYTES]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  int i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  char published[33]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char published[ISO_TIME_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   size_t len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  time_t published_on; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_pk_env_t *private_key = get_identity_key(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *identity_pkey; /* Identity key, DER64-encoded. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   size_t identity_pkey_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -933,49 +1016,27 @@ generate_runningrouters(crypto_pk_env_t *private_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     log_fn(LOG_WARN,"write identity_pkey to string failed!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     goto err; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  published_on = time(NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  format_iso_time(published, published_on); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  format_iso_time(published, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   len = 2048+strlen(router_status); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   s = tor_malloc_zero(len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_snprintf(s, len, "network-status\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             "published %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             "router-status %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             "dir-signing-key\n%s" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             "directory-signature %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             "-----BEGIN SIGNATURE-----\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          published, router_status, identity_pkey, get_options()->Nickname); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_snprintf(s, len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "network-status\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "published %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "router-status %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "dir-signing-key\n%s" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "directory-signature %s\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               published, router_status, identity_pkey, get_options()->Nickname); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(router_status); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(identity_pkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (router_get_runningrouters_hash(s,digest)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     log_fn(LOG_WARN,"couldn't compute digest"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     goto err; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (crypto_pk_private_sign(private_key, signature, digest, 20) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN,"couldn't sign digest"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    goto err; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  i = strlen(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  cp = s+i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (base64_encode(cp, len-i, signature, 128) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN,"couldn't base64-encode signature"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    goto err; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (strlcat(s, "-----END SIGNATURE-----\n", len) >= len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (append_signature(s, len, digest, private_key)<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     goto err; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_runningrouters = s; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_runningrouters_len = strlen(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_runningrouters_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (tor_gzip_compress(&the_runningrouters_z, &the_runningrouters_z_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        the_runningrouters, the_runningrouters_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        ZLIB_METHOD)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    log_fn(LOG_WARN, "Error gzipping runningrouters"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  set_cached_dir(&the_runningrouters, s, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   runningrouters_is_dirty = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   /* We don't cache running-routers to disk, so there's no point in 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -995,25 +1056,255 @@ generate_runningrouters(crypto_pk_env_t *private_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 dirserv_get_runningrouters(const char **rr, int compress) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (!get_options()->AuthoritativeDir) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    cached_dir_t *d = &cached_runningrouters; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    *rr = compress ? d->dir_z : d->dir; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (*rr) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return compress ? d->dir_z_len : d->dir_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      /* no directory yet retrieved */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return dirserv_get_obj(rr, compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         &cached_runningrouters, &the_runningrouters, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         runningrouters_is_dirty, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         generate_runningrouters, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         "v1 network status list"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+router_is_general_exit(routerinfo_t *ri) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static const int ports[] = { 80, 443, 194 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int n_allowed = 3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (i = 0; i < 3; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    struct addr_policy_t *policy = ri->exit_policy; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for ( ; policy; policy = policy->next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (policy->prt_min > ports[i] || policy->prt_max < ports[i]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        continue; /* Doesn't cover our port. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if ((policy->msk & 0x00fffffful) != 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        continue; /* Wider than /8. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if ((policy->addr & 0xff000000ul) == 0x7f000000ul) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        continue; /* 127.x */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* We have a match that is wider than /24. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (policy->policy_type != ADDR_POLICY_ACCEPT) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        --n_allowed; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (runningrouters_is_dirty && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      runningrouters_is_dirty + DIR_REGEN_SLACK_TIME < time(NULL)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (generate_runningrouters(get_identity_key())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      log_fn(LOG_ERR, "Couldn't generate running-routers list?"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return n_allowed > 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t the_v2_networkstatus = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+generate_v2_networkstatus(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define BASE64_DIGEST_LEN 29 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define LONGEST_STATUS_FLAG_NAME_LEN 7 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define N_STATUS_FLAGS 6 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RS_ENTRY_LEN                                                    \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ( /* first line */                                                    \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   MAX_NICKNAME_LEN+BASE64_DIGEST_LEN*2+ISO_TIME_LEN+INET_NTOA_BUF_LEN+ \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   5*2 /* ports */ + 10 /* punctuation */ +                             \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   /* second line */                                                    \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   (LONGEST_STATUS_FLAG_NAME_LEN+1)*N_STATUS_FLAGS + 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int r = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t len, identity_pkey_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *status = NULL, *client_versions = NULL, *server_versions = NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *identity_pkey = NULL, *hostname = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *outp, *endp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  or_options_t *options = get_options(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char fingerprint[FINGERPRINT_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char ipaddr[INET_NTOA_BUF_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char published[ISO_TIME_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char digest[DIGEST_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct in_addr in; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint32_t addr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_pk_env_t *private_key = get_identity_key(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (resolve_my_address(options, &addr, &hostname)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN, "Couldn't resolve my hostname"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  in.s_addr = htonl(addr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  format_iso_time(published, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  client_versions = format_versions_list(options->RecommendedClientVersions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  server_versions = format_versions_list(options->RecommendedServerVersions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (crypto_pk_write_public_key_to_string(private_key, &identity_pkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                           &identity_pkey_len)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN,"Writing public key to string failed."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (crypto_pk_get_fingerprint(private_key, fingerprint, 0)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_ERR, "Error computing fingerprint"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  len = 2048+strlen(client_versions)+strlen(server_versions)+identity_pkey_len*2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  len += (RS_ENTRY_LEN)*smartlist_len(descriptor_list) ; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  status = tor_malloc(len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_snprintf(status, len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "network-status-version 2\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "dir-source %s %s %d\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "dir-fingerprint %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "published %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "dir-options %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "client-versions %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "server-versions %s\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "dir-signing-key\n%s\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               hostname, ipaddr, (int)options->DirPort, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               fingerprint, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               published, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "Names", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               client_versions, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               server_versions, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               identity_pkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  outp = status + strlen(status); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  endp = status + len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SMARTLIST_FOREACH(descriptor_list, routerinfo_t *, ri, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_exit = router_is_general_exit(ri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_stable = !router_is_unreliable(ri, 1, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_fast = !router_is_unreliable(ri, 0, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_running; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_named = ri->is_verified; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      int f_valid = f_named; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      char identity64[128]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      char digest64[128]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (options->AuthoritativeDir) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        connection_t *conn = connection_get_by_identity_digest( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                         ri->identity_digest, CONN_TYPE_OR); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        f_running = (router_is_me(ri) && !we_are_hibernating()) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          (conn && conn->state == OR_CONN_STATE_OPEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        f_running = ri->is_running; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      format_iso_time(published, ri->published_on); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (base64_encode(identity64, sizeof(identity64), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ri->identity_digest, DIGEST_LEN)<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (base64_encode(digest64, sizeof(digest64), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ri->signed_descriptor_digest, DIGEST_LEN)<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      identity64[BASE64_DIGEST_LEN] = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      digest64[BASE64_DIGEST_LEN] = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      in.s_addr = htonl(ri->addr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (tor_snprintf(outp, endp-outp, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       "r %s %s %s %s %s %d %d\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       "s%s%s%s%s%s%s\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       ri->nickname, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       identity64, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       digest64, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       published, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       ipaddr, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       ri->or_port, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       ri->dir_port, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_exit?" Exit":"", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_stable?" Stable":"", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_fast?" Fast":"", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_running?" Running":"", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_named?" Named":"", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       f_valid?" Valid":"")<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log_fn(LOG_WARN, "Unable to print router status."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      outp += strlen(outp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (tor_snprintf(outp, endp-outp, "directory-signature %s\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   get_options()->Nickname)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN, "Unable to write signature line."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (router_get_networkstatus_v2_hash(status, digest)<0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_fn(LOG_WARN, "Unable to hash network status"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (append_signature(outp,endp-outp,digest,private_key)<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  set_cached_dir(&the_v2_networkstatus, status, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  dirserv_set_cached_networkstatus_v2(status, fingerprint, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  r = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ done: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(client_versions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(server_versions); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(status); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(hostname); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(identity_pkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return r; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_networkstatus_v2(const char **directory, const char *key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             int compress) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *directory = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!(strcmp(key,"authority"))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (get_options()->AuthoritativeDir) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return dirserv_get_obj(directory, compress, NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             &the_v2_networkstatus, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             networkstatus_v2_is_dirty, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             generate_v2_networkstatus, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             "network status list"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmp(key, "all")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // XXXX NM 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return dirserv_get_networkstatus_v2(directory, "authority", compress); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (strlen(key)==HEX_DIGEST_LEN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cached_dir_t *cached = strmap_get(cached_v2_networkstatus, key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (cached) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return dirserv_get_obj(directory, compress, cached, NULL, 0, NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             "cached network status"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_routerdescs(smartlist_t *descs_out, const char *key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!strcmp(key, "/tor/server/all")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_add_all(descs_out, descriptor_list); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmp(key, "/tor/server/authority")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    routerinfo_t *ri = router_get_my_routerinfo(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (ri) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      smartlist_add(descs_out, ri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmpstart(key, "/tor/server/fp/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_t *hexdigests = smartlist_create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_t *digests = smartlist_create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    key += strlen("/tor/server/fp/"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_split_string(hexdigests, key, "+", 0, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH(hexdigests, char *, cp, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        char *d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (strlen(cp) != HEX_DIGEST_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        d = tor_malloc_zero(DIGEST_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        base16_decode(d, DIGEST_LEN, cp, HEX_DIGEST_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        tor_free(cp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        smartlist_add(digests, d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_free(hexdigests); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH(descriptor_list, routerinfo_t *, ri, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      SMARTLIST_FOREACH(digests, const char *, d, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (!memcmp(d,ri->identity_digest,DIGEST_LEN)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          smartlist_add(descs_out,ri); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        })); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH(digests, char *, d, tor_free(d)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_free(digests); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  *rr = compress ? the_runningrouters_z : the_runningrouters; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return compress ? the_runningrouters_z_len : the_runningrouters_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Called when a TLS handshake has completed successfully with a 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1087,19 +1378,11 @@ dirserv_free_all(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     smartlist_free(descriptor_list); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     descriptor_list = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_directory_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_directory_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_directory_z_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(the_runningrouters_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_runningrouters_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  the_runningrouters_z_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(cached_directory.dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(cached_directory.dir_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(cached_runningrouters.dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  tor_free(cached_runningrouters.dir_z); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  memset(&cached_directory, 0, sizeof(cached_directory)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  memset(&cached_runningrouters, 0, sizeof(cached_runningrouters)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(&the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(&the_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(&cached_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(&cached_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  strmap_free(cached_v2_networkstatus, free_cached_dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_v2_networkstatus = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |