#!/usr/bin/env python3 import csv import os # Algorithm used in https://research.torproject.org/techreports/blocking-2011-09-15.pdf def evaluate_loesing (fingerprint): fingerprint = fingerprint.upper() bridge_ips_max = None # First get the max value in whole range with open (f"data/bridge_data_cleaned/{fingerprint}", 'r') as file: bridge_data = csv.reader(file, delimiter=',') for row in bridge_data: bridge_ips_today = int(row[1]) if bridge_ips_max is None: if bridge_ips_today > 0: bridge_ips_max = bridge_ips_today #print (f"{fingerprint} bridge_ips_max set to {bridge_ips_today} on {row[0]}") elif bridge_ips_today > bridge_ips_max: bridge_ips_max = bridge_ips_today #print (f"{fingerprint} bridge_ips_max set to {bridge_ips_today} on {row[0]}") # Now evaluate with open (f"data/bridge_data_cleaned/{fingerprint}", 'r') as file: bridge_data = csv.reader(file, delimiter=',') for row in bridge_data: bridge_ips_today = int(row[1]) if not bridge_ips_max is None and bridge_ips_max >= 100 and bridge_ips_today <= 32: return row[0] return None # Consider a bridge blocked if it receives <= t connections in one day, # when it has previously received >= m connections. Note that t=32, # m=100 is NOT the same as Loesing's algorithm, as we consider m only # for previous days, while Loesing's algorithm considers all days within # the period. t must be less than m. def is_blocked_abs (t, m, bridge_ips_max, bridge_ips_today): if bridge_ips_max is None: return False return bridge_ips_max >= m and bridge_ips_today <= t def evaluate_abs (t, m, fingerprint): fingerprint = fingerprint.upper() bridge_ips_max = None with open (f"data/bridge_data_cleaned/{fingerprint}", 'r') as file: bridge_data = csv.reader(file, delimiter=',') for row in bridge_data: bridge_ips_today = int(row[1]) if not bridge_ips_max is None: if is_blocked_abs (t, m, bridge_ips_max, bridge_ips_today): return row[0] if bridge_ips_today > bridge_ips_max: bridge_ips_max = bridge_ips_today # Start bridge_ips_max only when we have a non-zero # connection count elif bridge_ips_today > 0: bridge_ips_max = bridge_ips_today # If we got here, the bridge is not blocked return None # Consider a bridge blocked if we observe a drop of at least d from the # previous day's count. d must be at least 8. def is_blocked_rel (d, bridge_ips_yesterday, bridge_ips_today): if bridge_ips_yesterday is None: return False return bridge_ips_today <= bridge_ips_yesterday - d def evaluate_rel (d, fingerprint): fingerprint = fingerprint.upper() bridge_ips_yesterday = None bridge_ips_today = None with open (f"data/bridge_data_cleaned/{fingerprint}", 'r') as file: bridge_data = csv.reader(file, delimiter=',') for row in bridge_data: bridge_ips_today = int(row[1]) if is_blocked_rel (d, bridge_ips_yesterday, bridge_ips_today): return row[0] bridge_ips_yesterday = bridge_ips_today # Remove any previous blocked_* files, start over if os.path.exists (f"data/blocked_loesing"): os.remove (f"data/blocked_loesing") for t in range (8, 40, 8): for m in range (t+8, 112, 8): if os.path.exists (f"data/blocked_abs_{t}_{m}"): os.remove (f"data/blocked_abs_{t}_{m}") for d in range (8, 112, 8): if os.path.exists (f"data/blocked_rel_{d}"): os.remove (f"data/blocked_rel_{d}") with open ("data/all-bridges", 'r') as all_bridges: for fingerprint in all_bridges: fingerprint = fingerprint.strip() if fingerprint: # Evaluate with Loesing's algorithm blocked = evaluate_loesing (fingerprint) if not blocked is None: with open (f"data/blocked_loesing", 'a') as f: f.write(f"{fingerprint},{blocked}\n") # Absolute threshold with t and m for t in range (8, 40, 8): for m in range (t+8, 112, 8): blocked = evaluate_abs (t, m, fingerprint) if not blocked is None: with open (f"data/blocked_abs_{t}_{m}", 'a') as f: f.write (f"{fingerprint},{blocked}\n") # Relative threshold with d for d in range (8, 112, 8): blocked = evaluate_rel (d, fingerprint) if not blocked is None: with open (f"data/blocked_rel_{d}", 'a') as f: f.write (f"{fingerprint},{blocked}\n")