| 
					
				 | 
			
			
				@@ -51,6 +51,7 @@ dirserv_get_status_impl(const char *fp, const char *nickname, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         const char **msg, int should_log); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int dirserv_thinks_router_is_reachable(routerinfo_t *router, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                               time_t now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void clear_cached_dir(cached_dir_t *d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /************** Fingerprint handling code ************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -865,12 +866,12 @@ dirserv_dump_directory_to_string(char **dir_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Most recently generated encoded signed directory. (auth dirservers only.)*/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static cached_dir_t the_directory = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t *the_directory = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* Used only by non-auth dirservers: The directory and runningrouters we'll 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * serve when requested. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t *cached_directory = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0, -1 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* Used for other dirservers' v2 network statuses.  Map from hexdigest to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * cached_dir_t. */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -907,6 +908,32 @@ set_cached_dir(cached_dir_t *d, char *directory, time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+cached_dir_decref(cached_dir_t *d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!d || --d->refcnt > 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  clear_cached_dir(d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(d); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+new_cached_dir(char *s, time_t published) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  d->refcnt = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  d->dir = s; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  d->dir_len = strlen(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  d->published = published; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ZLIB_METHOD)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_warn(LD_BUG, "Error compressing directory"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 clear_cached_dir(cached_dir_t *d) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -932,9 +959,12 @@ 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, tor_strdup(directory), published); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (is_running_routers) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    set_cached_dir(&cached_runningrouters, tor_strdup(directory), published); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cached_dir_decref(cached_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cached_directory = new_cached_dir(tor_strdup(directory), published); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** We've just received a v2 network-status for an authoritative directory 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1043,7 +1073,8 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * this kind of object. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  **/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-dirserv_get_obj(const char **out, int compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_obj(const char **out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                int compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 cached_dir_t *cache_src, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 cached_dir_t *auth_src, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 time_t dirty, int (*regenerate)(void), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1065,17 +1096,16 @@ dirserv_get_obj(const char **out, int compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-/** Set *<b>directory</b> to the most recently generated encoded signed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * directory, generating a new one as necessary.  If not an authoritative 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * directory may return 0 if no directory is yet cached.*/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-size_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-dirserv_get_directory(const char **directory, int compress) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Return the most recently generated encoded signed directory, generating a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * new one as necessary.  If not an authoritative directory may return NULL if 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * no directory is yet cached.*/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+cached_dir_t * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_directory(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return dirserv_get_obj(directory, compress, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                         &cached_directory, &the_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                         the_directory_is_dirty, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                         dirserv_regenerate_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                         "server directory", 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return dirserv_pick_cached_dir_obj(cached_directory, the_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     the_directory_is_dirty, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     dirserv_regenerate_directory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     "server directory", 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1092,23 +1122,24 @@ dirserv_regenerate_directory(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(new_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  set_cached_dir(&the_directory, new_directory, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_decref(the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  the_directory = new_cached_dir(new_directory, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   log_info(LD_DIRSERV,"New directory (size %d) has been built.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-           (int)the_directory.dir_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           (int)the_directory->dir_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   log_debug(LD_DIRSERV,"New directory (size %d):\n%s", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            (int)the_directory.dir_len, the_directory.dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (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.dir, time(NULL), 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  dirserv_set_cached_directory(the_directory->dir, time(NULL), 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** For authoritative directories: the current (v1) network status */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Replace the current running-routers list with a newly generated one. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1177,7 +1208,7 @@ dirserv_get_runningrouters(const char **rr, int compress) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** For authoritative directories: the current (v2) network status */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static cached_dir_t the_v2_networkstatus = { NULL, NULL, 0, 0, 0 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static cached_dir_t the_v2_networkstatus = { NULL, NULL, 0, 0, 0, -1 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 should_generate_v2_networkstatus(void) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1535,6 +1566,44 @@ dirserv_get_networkstatus_v2(smartlist_t *result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * pointers, adds copies of digests to fps_out.  For a /tor/server/d/ request, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * adds descriptor digests; for other requests, adds identity digests. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    const char **msg) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *msg = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!strcmp(key, "/tor/server/all")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    routerlist_t *rl = router_get_routerlist(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      smartlist_add(fps_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      tor_memdup(r->cache_info.identity_digest, DIGEST_LEN))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmp(key, "/tor/server/authority")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    routerinfo_t *ri = router_get_my_routerinfo(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (ri) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      smartlist_add(fps_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmpstart(key, "/tor/server/d/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    key += strlen("/tor/server/d/"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dir_split_resource_into_fingerprints(key, fps_out, NULL, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (!strcmpstart(key, "/tor/server/fp/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    key += strlen("/tor/server/fp/"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dir_split_resource_into_fingerprints(key, fps_out, NULL, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *msg = "Key not recognized"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!smartlist_len(fps_out)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *msg = "Servers unavailable"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Add a signed_descriptor_t to <b>descs_out</b> for each router matching 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * <b>key</b>.  The key should be either 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  *   - "/tor/server/authority" for our own routerinfo; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1673,6 +1742,119 @@ dirserv_orconn_tls_done(const char *address, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** When we're spooling data onto our outbuf, add more whenever we dip 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * below this threshold. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define DIRSERV_BUFFER_MIN 16384 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+connection_dirserv_add_servers_to_outbuf(connection_t *conn) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int fp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!strcmpstart(conn->requested_resource, "/tor/server/d/")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fp = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fp = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (smartlist_len(conn->fingerprint_stack) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         buf_datalen(conn->outbuf) < DIRSERV_BUFFER_MIN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char *fp = smartlist_pop_last(conn->fingerprint_stack); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    signed_descriptor_t *sd = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (fp) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (router_digest_is_me(fp)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sd = &(router_get_my_routerinfo()->cache_info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        routerinfo_t *ri = router_get_by_digest(fp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (ri && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ri->cache_info.published_on > time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          sd = &ri->cache_info; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      sd = router_get_by_descriptor_digest(fp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tor_free(fp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!sd) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (conn->zlib_state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_to_buf_zlib(conn->outbuf, conn->zlib_state, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        sd->signed_descriptor_body, sd->signed_descriptor_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_to_buf(sd->signed_descriptor_body, sd->signed_descriptor_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   conn->outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!smartlist_len(conn->fingerprint_stack)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* We just wrote the last one; finish up. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (conn->zlib_state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_to_buf_zlib(conn->outbuf, conn->zlib_state, "", 0, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tor_zlib_free(conn->zlib_state); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      conn->zlib_state = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_free(conn->fingerprint_stack); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    conn->fingerprint_stack = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+connection_dirserv_add_dir_bytes_to_outbuf(connection_t *conn) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bytes, remaining; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(bytes > 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bytes < 8192) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bytes = 8192; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  remaining = conn->cached_dir->dir_z_len - conn->cached_dir_offset; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bytes > remaining) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bytes = remaining; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (conn->zlib_state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_to_buf_zlib(conn->outbuf, conn->zlib_state, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      conn->cached_dir->dir_z + conn->cached_dir_offset, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      bytes, bytes == remaining); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_to_buf(conn->cached_dir->dir_z + conn->cached_dir_offset, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 bytes, conn->outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  conn->cached_dir_offset += bytes; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bytes == (int)conn->cached_dir->dir_z_len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* We just wrote the last one; finish up. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (conn->zlib_state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_to_buf_zlib(conn->outbuf, conn->zlib_state, "", 0, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tor_zlib_free(conn->zlib_state); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      conn->zlib_state = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cached_dir_decref(conn->cached_dir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    conn->cached_dir = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Called whenever we have flushed some directory data in state 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * SERVER_WRITING. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+connection_dirserv_flushed_some(connection_t *conn) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(conn->type == CONN_TYPE_DIR); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(conn->state == DIR_CONN_STATE_SERVER_WRITING); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (! (conn->fingerprint_stack || conn->cached_dir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      || buf_datalen(conn->outbuf) > DIRSERV_BUFFER_MIN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!strcmpstart(conn->requested_resource, "/tor/server/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return connection_dirserv_add_servers_to_outbuf(conn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else if (conn->cached_dir) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return connection_dirserv_add_dir_bytes_to_outbuf(conn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Release all storage used by the directory server. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 dirserv_free_all(void) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1685,10 +1867,10 @@ dirserv_free_all(void) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     smartlist_free(fingerprint_list); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     fingerprint_list = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  clear_cached_dir(&the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_decref(the_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   clear_cached_dir(&the_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   clear_cached_dir(&the_v2_networkstatus); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  clear_cached_dir(&cached_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cached_dir_decref(cached_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   clear_cached_dir(&cached_runningrouters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (cached_v2_networkstatus) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     digestmap_free(cached_v2_networkstatus, free_cached_dir); 
			 |