| 
					
				 | 
			
			
				@@ -1081,6 +1081,173 @@ geoip_dirreq_stats_write(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(data_v3); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Start time of bridge stats. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static time_t start_of_bridge_stats_interval; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Initialize bridge stats. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+geoip_bridge_stats_init(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  start_of_bridge_stats_interval = now; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Parse the bridge statistics as they are written to extra-info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * descriptors for being returned to controller clients. Return the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * controller string if successful, or NULL otherwise. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+parse_bridge_stats_controller(const char *stats_str, time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char stats_end_str[ISO_TIME_LEN+1], stats_start_str[ISO_TIME_LEN+1], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       *controller_str, *eos; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const char *stats_end_line = "bridge-stats-end", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             *ips_line = "bridge-ips", *tmp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  time_t stats_end_time; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t controller_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int seconds; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(stats_str); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Parse timestamp and number of seconds from 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tmp = strstr(stats_str, stats_end_line); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!tmp || strlen(tmp) < strlen(stats_end_line) + 1 + ISO_TIME_LEN + 6) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  strlcpy(stats_end_str, tmp + strlen(stats_end_line) + 1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          sizeof(stats_end_str)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (parse_iso_time(stats_end_str, &stats_end_time) < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (stats_end_time < now - (25*60*60) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      stats_end_time > now + (1*60*60)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  seconds = (int)strtol(tmp + strlen(stats_end_line) + 1 + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ISO_TIME_LEN + 2, &eos, 10); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!eos || seconds < 23*60*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  format_iso_time(stats_start_str, stats_end_time - seconds); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Parse: "bridge-ips CC=N,CC=N,..." */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tmp = strstr(tmp, ips_line); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!tmp || strlen(tmp) < strlen(ips_line)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (strlen(tmp) > strlen(ips_line) + 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tmp += strlen(ips_line) + 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tmp = ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  controller_len = strlen("TimeStarted=\"\" CountrySummary=") + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          ISO_TIME_LEN + strlen(tmp) + 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  controller_str = tor_malloc(controller_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (tor_snprintf(controller_str, controller_len, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   "TimeStarted=\"%s\" CountrySummary=%s", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   stats_start_str, tmp) < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tor_free(controller_str); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return controller_str; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Most recent bridge statistics formatted to be written to extra-info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * descriptors. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char *bridge_stats_extrainfo = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Most recent bridge statistics formatted to be returned to controller 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * clients. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static char *bridge_stats_controller = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Write bridge statistics to $DATADIR/stats/bridge-stats and return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * when we should next try to write statistics. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+time_t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+geoip_bridge_stats_write(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *statsdir = NULL, *filename = NULL, *data = NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       written[ISO_TIME_LEN+1], *out = NULL, *controller_str; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* If we changed from relay to bridge recently, adapt starting time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   * of current measurements. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (start_of_bridge_stats_interval < client_history_starts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_of_bridge_stats_interval = client_history_starts; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Check if 24 hours have passed since starting measurements. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (now < start_of_bridge_stats_interval + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            DIR_ENTRY_RECORD_USAGE_RETAIN_IPS) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return start_of_bridge_stats_interval + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           DIR_ENTRY_RECORD_USAGE_RETAIN_IPS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Discard all items in the client history that are too old. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  geoip_remove_old_clients(start_of_bridge_stats_interval); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  statsdir = get_datadir_fname("stats"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (check_private_dir(statsdir, CPD_CREATE) < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  filename = get_datadir_fname("stats"PATH_SEPARATOR"bridge-stats"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  data = geoip_get_client_history_bridge(now, GEOIP_CLIENT_CONNECT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  format_iso_time(written, now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  len = strlen("bridge-stats-end  (999999 s)\nbridge-ips \n") + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ISO_TIME_LEN + (data ? strlen(data) : 0) + 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  out = tor_malloc(len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (tor_snprintf(out, len, "bridge-stats-end %s (%u s)\nbridge-ips %s\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              written, (unsigned) (now - start_of_bridge_stats_interval), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              data ? data : "") < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  write_str_to_file(filename, out, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  controller_str = parse_bridge_stats_controller(out, now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!controller_str) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  start_of_bridge_stats_interval = now; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(bridge_stats_extrainfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(bridge_stats_controller); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bridge_stats_extrainfo = out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  out = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bridge_stats_controller = controller_str; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  control_event_clients_seen(controller_str); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ done: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(filename); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(statsdir); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return start_of_bridge_stats_interval + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         DIR_ENTRY_RECORD_USAGE_RETAIN_IPS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Try to load the most recent bridge statistics from disk, unless we 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * have finished a measurement interval lately. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+load_bridge_stats(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *fname, *contents, *controller_str; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bridge_stats_extrainfo) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  fname = get_datadir_fname("stats"PATH_SEPARATOR"bridge-stats"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  contents = read_file_to_str(fname, 0, NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (contents) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    controller_str = parse_bridge_stats_controller(contents, now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (controller_str) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      bridge_stats_extrainfo = contents; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      bridge_stats_controller = controller_str; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tor_free(contents); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(fname); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Return most recent bridge statistics for inclusion in extra-info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * descriptors, or NULL if we don't have recent bridge statistics. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+char * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+geoip_get_bridge_stats_extrainfo(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  load_bridge_stats(now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bridge_stats_extrainfo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Return most recent bridge statistics to be returned to controller 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * clients, or NULL if we don't have recent bridge statistics. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+char * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+geoip_get_bridge_stats_controller(time_t now) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  load_bridge_stats(now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bridge_stats_controller; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Start time of entry stats. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static time_t start_of_entry_stats_interval; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |