|
@@ -7,12 +7,14 @@ use std::{
|
|
|
fmt,
|
|
|
};
|
|
|
|
|
|
+pub mod analyzer;
|
|
|
pub mod bridge_verification_info;
|
|
|
pub mod extra_info;
|
|
|
pub mod negative_report;
|
|
|
pub mod positive_report;
|
|
|
pub mod request_handler;
|
|
|
|
|
|
+use analyzer::Analyzer;
|
|
|
use extra_info::*;
|
|
|
use negative_report::*;
|
|
|
use positive_report::*;
|
|
@@ -170,11 +172,18 @@ impl fmt::Display for BridgeCountryInfo {
|
|
|
/// but this extra-info contains different data for some reason, use the
|
|
|
/// greater count of connections from each country.
|
|
|
pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) {
|
|
|
- let fingerprint = extra_info.fingerprint;
|
|
|
- let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
|
|
+ let mut bridges = match db.get("bridges").unwrap() {
|
|
|
Some(v) => bincode::deserialize(&v).unwrap(),
|
|
|
- None => BridgeInfo::new(fingerprint, &extra_info.nickname),
|
|
|
+ None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
|
|
};
|
|
|
+ let fingerprint = extra_info.fingerprint;
|
|
|
+ if !bridges.contains_key(&fingerprint) {
|
|
|
+ bridges.insert(
|
|
|
+ fingerprint,
|
|
|
+ BridgeInfo::new(fingerprint, &extra_info.nickname),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
|
|
for country in extra_info.bridge_ips.keys() {
|
|
|
if bridge_info.info_by_country.contains_key::<String>(country) {
|
|
|
bridge_info
|
|
@@ -200,7 +209,7 @@ pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) {
|
|
|
}
|
|
|
}
|
|
|
// Commit changes to database
|
|
|
- db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
|
|
+ db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
|
|
.unwrap();
|
|
|
}
|
|
|
|
|
@@ -322,19 +331,23 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|
|
let country = first_report.country;
|
|
|
let count_valid = verify_negative_reports(&distributors, reports).await;
|
|
|
|
|
|
- let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
|
|
+ let mut bridges = match db.get("bridges").unwrap() {
|
|
|
Some(v) => bincode::deserialize(&v).unwrap(),
|
|
|
- // It should already exist, unless the bridge hasn't published
|
|
|
- // any bridge stats.
|
|
|
- None => BridgeInfo::new(fingerprint, &"".to_string()),
|
|
|
+ None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
|
|
};
|
|
|
+
|
|
|
+ // Get bridge info or make new one
|
|
|
+ if !bridges.contains_key(&fingerprint) {
|
|
|
+ // This case shouldn't happen unless the bridge hasn't published
|
|
|
+ // any bridge stats.
|
|
|
+ bridges.insert(fingerprint, BridgeInfo::new(fingerprint, &"".to_string()));
|
|
|
+ }
|
|
|
+ let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
|
|
+
|
|
|
// Add the new report count to it
|
|
|
if bridge_info.info_by_country.contains_key(&country) {
|
|
|
let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
|
|
bridge_country_info.add_info(BridgeInfoType::NegativeReports, date, count_valid);
|
|
|
- // Commit changes to database
|
|
|
- db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
|
|
- .unwrap();
|
|
|
} else {
|
|
|
// No existing entry; make a new one.
|
|
|
let mut bridge_country_info = BridgeCountryInfo::new();
|
|
@@ -342,10 +355,11 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|
|
bridge_info
|
|
|
.info_by_country
|
|
|
.insert(country, bridge_country_info);
|
|
|
- // Commit changes to database
|
|
|
- db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
|
|
- .unwrap();
|
|
|
}
|
|
|
+
|
|
|
+ // Commit changes to database
|
|
|
+ db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
|
|
+ .unwrap();
|
|
|
}
|
|
|
}
|
|
|
// TODO: Would it be cheaper to just recreate it?
|
|
@@ -434,19 +448,25 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|
|
let date = first_report.date;
|
|
|
let country = first_report.country.clone();
|
|
|
let count_valid = verify_positive_reports(&distributors, reports).await;
|
|
|
- let mut bridge_info = match db.get(&fingerprint).unwrap() {
|
|
|
+
|
|
|
+ // Get bridge data from database
|
|
|
+ let mut bridges = match db.get("bridges").unwrap() {
|
|
|
Some(v) => bincode::deserialize(&v).unwrap(),
|
|
|
- // It should already exist, unless the bridge hasn't published
|
|
|
- // any bridge stats.
|
|
|
- None => BridgeInfo::new(fingerprint, &"".to_string()),
|
|
|
+ None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
|
|
};
|
|
|
+
|
|
|
+ // Get bridge info or make new one
|
|
|
+ if !bridges.contains_key(&fingerprint) {
|
|
|
+ // This case shouldn't happen unless the bridge hasn't published
|
|
|
+ // any bridge stats.
|
|
|
+ bridges.insert(fingerprint, BridgeInfo::new(fingerprint, &"".to_string()));
|
|
|
+ }
|
|
|
+ let bridge_info = bridges.get_mut(&fingerprint).unwrap();
|
|
|
+
|
|
|
// Add the new report count to it
|
|
|
if bridge_info.info_by_country.contains_key(&country) {
|
|
|
let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
|
|
bridge_country_info.add_info(BridgeInfoType::PositiveReports, date, count_valid);
|
|
|
- // Commit changes to database
|
|
|
- db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
|
|
- .unwrap();
|
|
|
} else {
|
|
|
// No existing entry; make a new one.
|
|
|
let mut bridge_country_info = BridgeCountryInfo::new();
|
|
@@ -454,10 +474,10 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|
|
bridge_info
|
|
|
.info_by_country
|
|
|
.insert(country, bridge_country_info);
|
|
|
- // Commit changes to database
|
|
|
- db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
|
|
|
- .unwrap();
|
|
|
}
|
|
|
+ // Commit changes to database
|
|
|
+ db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
|
|
+ .unwrap();
|
|
|
}
|
|
|
}
|
|
|
// TODO: Would it be cheaper to just recreate it?
|
|
@@ -470,4 +490,74 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap<BridgeDist
|
|
|
.unwrap();
|
|
|
}
|
|
|
|
|
|
-// TODO: function to mark a bridge as blocked
|
|
|
+// Verdict on bridge reachability
|
|
|
+
|
|
|
+/// Guess which countries block a bridge. This function returns a map of new
|
|
|
+/// blockages (fingerprint : set of countries which block the bridge)
|
|
|
+pub fn guess_blockages(db: &Db, analyzer: &dyn Analyzer) -> HashMap<[u8; 20], HashSet<String>> {
|
|
|
+ // Map of bridge fingerprint to set of countries which newly block it
|
|
|
+ let mut blockages = HashMap::<[u8; 20], HashSet<String>>::new();
|
|
|
+
|
|
|
+ // Get bridge data from database
|
|
|
+ let mut bridges = match db.get("bridges").unwrap() {
|
|
|
+ Some(v) => bincode::deserialize(&v).unwrap(),
|
|
|
+ None => BTreeMap::<[u8; 20], BridgeInfo>::new(),
|
|
|
+ };
|
|
|
+
|
|
|
+ // Guess for each bridge
|
|
|
+ for (fingerprint, bridge_info) in &mut bridges {
|
|
|
+ let mut new_blockages = HashSet::<String>::new();
|
|
|
+ let blocked_in = analyzer.blocked_in(&bridge_info);
|
|
|
+ for country in blocked_in {
|
|
|
+ let bridge_country_info = bridge_info.info_by_country.get_mut(&country).unwrap();
|
|
|
+ if !bridge_country_info.blocked {
|
|
|
+ new_blockages.insert(country.to_string());
|
|
|
+ // Mark bridge as blocked when db gets updated
|
|
|
+ bridge_country_info.blocked = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ blockages.insert(*fingerprint, new_blockages);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Commit changes to database
|
|
|
+ db.insert("bridges", bincode::serialize(&bridges).unwrap())
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ // Return map of new blockages
|
|
|
+ blockages
|
|
|
+}
|
|
|
+
|
|
|
+/// Report blocked bridges to bridge distributor
|
|
|
+pub async fn report_blockages(
|
|
|
+ distributors: &BTreeMap<BridgeDistributor, String>,
|
|
|
+ blockages: HashMap<[u8; 20], HashSet<String>>,
|
|
|
+) {
|
|
|
+ // For now, only report to Lox
|
|
|
+ // TODO: Support more distributors
|
|
|
+ let uri: String = (distributors
|
|
|
+ .get(&BridgeDistributor::Lox)
|
|
|
+ .unwrap()
|
|
|
+ .to_owned()
|
|
|
+ + "/reportblocked")
|
|
|
+ .parse()
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ // Convert map keys from [u8; 20] to 40-character hex strings
|
|
|
+ let mut blockages_str = HashMap::<String, HashSet<String>>::new();
|
|
|
+ for (fingerprint, countries) in blockages {
|
|
|
+ let fpr_string = array_bytes::bytes2hex("", fingerprint);
|
|
|
+ blockages_str.insert(fpr_string, countries);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Report blocked bridges to bridge distributor
|
|
|
+ let client = Client::new();
|
|
|
+ let req = Request::builder()
|
|
|
+ .method(Method::POST)
|
|
|
+ .uri(uri)
|
|
|
+ .body(Body::from(serde_json::to_string(&blockages_str).unwrap()))
|
|
|
+ .unwrap();
|
|
|
+ let resp = client.request(req).await.unwrap();
|
|
|
+ let buf = hyper::body::to_bytes(resp).await.unwrap();
|
|
|
+ let resp_str: String = serde_json::from_slice(&buf).unwrap();
|
|
|
+ assert_eq!("OK", resp_str);
|
|
|
+}
|