From 461d7d4ce526c338038f2f17dad8d73a722dc4cb Mon Sep 17 00:00:00 2001 From: Vecna Date: Wed, 29 May 2024 13:20:56 -0400 Subject: [PATCH] Assorted improvements, mostly suggested by clippy --- src/crypto.rs | 6 +-- src/extra_info.rs | 47 ++++++++-------- src/lib.rs | 118 +++++++++++++++++++---------------------- src/main.rs | 18 +++---- src/negative_report.rs | 10 ++-- src/positive_report.rs | 16 +++--- src/request_handler.rs | 4 +- 7 files changed, 102 insertions(+), 117 deletions(-) diff --git a/src/crypto.rs b/src/crypto.rs index 8bea524..e01d2f6 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -23,7 +23,7 @@ impl EciesCiphertext { let mut rng = rand::thread_rng(); let secret = EphemeralSecret::random_from_rng(&mut rng); let client_pubkey = PublicKey::from(&secret); - let shared_secret = secret.diffie_hellman(&receiver_pubkey); + let shared_secret = secret.diffie_hellman(receiver_pubkey); // Compute key let hk = Hkdf::::new(None, shared_secret.as_bytes()); @@ -39,11 +39,11 @@ impl EciesCiphertext { let key: Key = symmetric_key.into(); let cipher = Aes256Gcm::new(&key); let nonce = Aes256Gcm::generate_nonce(&mut rng); - match cipher.encrypt(&nonce, &*message) { + match cipher.encrypt(&nonce, message) { Ok(ct) => Ok(EciesCiphertext { pubkey: client_pubkey, nonce: nonce.to_vec(), - ct: ct, + ct, }), Err(_) => Err("Failed to encrypt".to_string()), } diff --git a/src/extra_info.rs b/src/extra_info.rs index 4cfdee9..44ae12d 100644 --- a/src/extra_info.rs +++ b/src/extra_info.rs @@ -60,7 +60,7 @@ impl ExtraInfo { let date_str = if entry.contains_key("bridge-stats-end") { let line = entry.get("bridge-stats-end").unwrap(); // Parse out (86400 s) from end of line - &line[..line.find("(").unwrap() - 1] + &line[..line.find('(').unwrap() - 1] } else { entry.get("published").unwrap().as_str() }; @@ -77,7 +77,7 @@ impl ExtraInfo { let mut bridge_ips: BTreeMap = BTreeMap::new(); let countries: Vec<&str> = bridge_ips_str.split(',').collect(); for country in countries { - if country != "" { + if !country.is_empty() { // bridge-ips may be empty let (cc, count) = country.split_once('=').unwrap(); bridge_ips.insert(cc.to_string(), count.parse::().unwrap()); @@ -94,46 +94,43 @@ impl ExtraInfo { /// Accepts a downloaded extra-infos file as a big string, returns a set of /// the ExtraInfos represented by the file. - pub fn parse_file<'a>(extra_info_str: &str) -> HashSet { + pub fn parse_file(extra_info_str: &str) -> HashSet { let mut set = HashSet::::new(); let mut entry = HashMap::::new(); for line in extra_info_str.lines() { - let line = line; if line.starts_with("@type bridge-extra-info ") { if !entry.is_empty() { let extra_info = Self::from_map(&entry); - if extra_info.is_ok() { - set.insert(extra_info.unwrap()); + if let Ok(ei) = extra_info { + set.insert(ei); } else { // Just print the error and continue. println!("{}", extra_info.err().unwrap()); } entry = HashMap::::new(); } - } else { - if line.starts_with("extra-info ") { - // extra-info line has format: - // extra-info - let line_split: Vec<&str> = line.split(' ').collect(); - if line_split.len() != 3 { - println!("Misformed extra-info line"); - } else { - entry.insert("nickname".to_string(), line_split[1].to_string()); - entry.insert("fingerprint".to_string(), line_split[2].to_string()); - } + } else if line.starts_with("extra-info ") { + // extra-info line has format: + // extra-info + let line_split: Vec<&str> = line.split(' ').collect(); + if line_split.len() != 3 { + println!("Misformed extra-info line"); } else { - let (key, value) = match line.split_once(' ') { - Some((k, v)) => (k, v), - None => (line, ""), - }; - entry.insert(key.to_string(), value.to_string()); + entry.insert("nickname".to_string(), line_split[1].to_string()); + entry.insert("fingerprint".to_string(), line_split[2].to_string()); } + } else { + let (key, value) = match line.split_once(' ') { + Some((k, v)) => (k, v), + None => (line, ""), + }; + entry.insert(key.to_string(), value.to_string()); } } // Do for the last one let extra_info = Self::from_map(&entry); - if extra_info.is_ok() { - set.insert(extra_info.unwrap()); + if let Ok(ei) = extra_info { + set.insert(ei); } else { println!("{}", extra_info.err().unwrap()); } @@ -168,7 +165,7 @@ impl fmt::Display for ExtraInfo { str.push_str(format!("{}={}", cc, count,).as_str()); first_cc = false; } - str.push_str("\n"); + str.push('\n'); write!(f, "{}", str) } diff --git a/src/lib.rs b/src/lib.rs index f9aef21..9854a34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use sled::Db; use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet}, fmt, }; use x25519_dalek::{PublicKey, StaticSecret}; @@ -130,7 +130,7 @@ pub struct BridgeInfo { impl BridgeInfo { pub fn new(fingerprint: [u8; 20], nickname: &String) -> Self { Self { - fingerprint: fingerprint, + fingerprint, nickname: nickname.to_string(), info_by_country: HashMap::::new(), } @@ -182,13 +182,17 @@ impl BridgeCountryInfo { Self { info_by_day: BTreeMap::>::new(), blocked: false, - first_seen: first_seen, + first_seen, first_pr: None, } } pub fn add_info(&mut self, info_type: BridgeInfoType, date: u32, count: u32) { - if self.info_by_day.contains_key(&date) { + if let btree_map::Entry::Vacant(e) = self.info_by_day.entry(date) { + let mut info = BTreeMap::::new(); + info.insert(info_type, count); + e.insert(info); + } else { let info = self.info_by_day.get_mut(&date).unwrap(); if !info.contains_key(&info_type) { info.insert(info_type, count); @@ -202,10 +206,6 @@ impl BridgeCountryInfo { let new_count = info.get(&info_type).unwrap() + count; info.insert(info_type, new_count); } - } else { - let mut info = BTreeMap::::new(); - info.insert(info_type, count); - self.info_by_day.insert(date, info); } // If this is the first instance of positive reports, save the date @@ -229,18 +229,18 @@ impl fmt::Display for BridgeCountryInfo { for date in self.info_by_day.keys() { let info = self.info_by_day.get(date).unwrap(); let ip_count = match info.get(&BridgeInfoType::BridgeIps) { - Some(v) => v, - None => &0, + Some(&v) => v, + None => 0, }; let nr_count = match info.get(&BridgeInfoType::NegativeReports) { - Some(v) => v, - None => &0, + Some(&v) => v, + None => 0, }; let pr_count = match info.get(&BridgeInfoType::PositiveReports) { - Some(v) => v, - None => &0, + Some(&v) => v, + None => 0, }; - if ip_count > &0 || nr_count > &0 || pr_count > &0 { + if ip_count > 0 || nr_count > 0 || pr_count > 0 { str.push_str( format!( "\n date: {}\n connections: {}\n negative reports: {}\n positive reports: {}", @@ -319,7 +319,7 @@ pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) { let mut bridge_info = match db.get(fingerprint).unwrap() { Some(v) => bincode::deserialize(&v).unwrap(), None => { - add_bridge_to_db(&db, fingerprint); + add_bridge_to_db(db, fingerprint); BridgeInfo::new(fingerprint, &extra_info.nickname) } }; @@ -402,7 +402,7 @@ pub async fn update_extra_infos( // Add new extra-infos data to database for extra_info in new_extra_infos { - add_extra_info_to_db(&db, extra_info); + add_extra_info_to_db(db, extra_info); } // Store which files we've already downloaded and processed @@ -429,16 +429,16 @@ pub fn new_negative_report_key(db: &Db, date: u32) -> Option { Err(_) => BTreeMap::::new(), } }; - if nr_keys.contains_key(&date) { - None - } else { - let mut rng = rand::thread_rng(); - let secret = StaticSecret::random_from_rng(&mut rng); + if let btree_map::Entry::Vacant(_e) = nr_keys.entry(date) { + let rng = rand::thread_rng(); + let secret = StaticSecret::random_from_rng(rng); let public = PublicKey::from(&secret); nr_keys.insert(date, secret); db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap()) .unwrap(); Some(public) + } else { + None } } @@ -465,23 +465,16 @@ pub fn get_negative_report_secret_key(db: &Db, date: u32) -> Option Option { - match get_negative_report_secret_key(&db, date) { - Some(secret) => Some(PublicKey::from(&secret)), - None => None, - } + get_negative_report_secret_key(db, date).map(|secret| PublicKey::from(&secret)) } /// Receive an encrypted negative report. Attempt to decrypt it and if /// successful, add it to the database to be processed later. pub fn handle_encrypted_negative_report(db: &Db, enc_report: EncryptedNegativeReport) { - match get_negative_report_secret_key(&db, enc_report.date) { - Some(secret) => match enc_report.decrypt(&secret) { - Ok(nr) => { - save_negative_report_to_process(&db, nr); - } - Err(_) => {} - }, - None => {} + if let Some(secret) = get_negative_report_secret_key(db, enc_report.date) { + if let Ok(nr) = enc_report.decrypt(&secret) { + save_negative_report_to_process(db, nr); + } } } @@ -508,19 +501,18 @@ pub fn save_negative_report_to_process(db: &Db, nr: NegativeReport) { // Store to-be-processed reports with key [fingerprint]_[country]_[date] let map_key = format!( "{}_{}_{}", - array_bytes::bytes2hex("", &nr.fingerprint), + array_bytes::bytes2hex("", nr.fingerprint), &nr.country, &nr.date, ); - if reports.contains_key(&map_key) { + if let btree_map::Entry::Vacant(e) = reports.entry(map_key.clone()) { + let nrs = vec![nr.to_serializable_report()]; + e.insert(nrs); + } else { reports .get_mut(&map_key) .unwrap() .push(nr.to_serializable_report()); - } else { - let mut nrs = Vec::::new(); - nrs.push(nr.to_serializable_report()); - reports.insert(map_key, nrs); } // Commit changes to database db.insert("nrs-to-process", bincode::serialize(&reports).unwrap()) @@ -580,7 +572,7 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap 0 && date < today { @@ -600,22 +592,23 @@ pub async fn update_negative_reports(db: &Db, distributors: &BTreeMap { // This case shouldn't happen unless the bridge hasn't // published any bridge stats. - add_bridge_to_db(&db, fingerprint); + add_bridge_to_db(db, fingerprint); BridgeInfo::new(fingerprint, &String::default()) } }; // 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); - } else { + if let hash_map::Entry::Vacant(_e) = bridge_info.info_by_country.entry(country.clone()) + { // No existing entry; make a new one. let mut bridge_country_info = BridgeCountryInfo::new(date); bridge_country_info.add_info(BridgeInfoType::NegativeReports, date, count_valid); bridge_info .info_by_country .insert(country, bridge_country_info); + } else { + 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()) @@ -648,19 +641,18 @@ pub fn save_positive_report_to_process(db: &Db, pr: PositiveReport) { // Store to-be-processed reports with key [fingerprint]_[country]_[date] let map_key = format!( "{}_{}_{}", - array_bytes::bytes2hex("", &pr.fingerprint), + array_bytes::bytes2hex("", pr.fingerprint), &pr.country, &pr.date, ); - if reports.contains_key(&map_key) { + if let btree_map::Entry::Vacant(e) = reports.entry(map_key.clone()) { + let prs = vec![pr.to_serializable_report()]; + e.insert(prs); + } else { reports .get_mut(&map_key) .unwrap() .push(pr.to_serializable_report()); - } else { - let mut prs = Vec::::new(); - prs.push(pr.to_serializable_report()); - reports.insert(map_key, prs); } // Commit changes to database db.insert("prs-to-process", bincode::serialize(&reports).unwrap()) @@ -719,7 +711,7 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap 0 && date < today { @@ -739,22 +731,20 @@ pub async fn update_positive_reports(db: &Db, distributors: &BTreeMap { // This case shouldn't happen unless the bridge hasn't // published any bridge stats. - add_bridge_to_db(&db, fingerprint); + add_bridge_to_db(db, fingerprint); BridgeInfo::new(fingerprint, &String::default()) } }; // 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); - } else { + if let hash_map::Entry::Vacant(e) = bridge_info.info_by_country.entry(country.clone()) { // No existing entry; make a new one. let mut bridge_country_info = BridgeCountryInfo::new(date); bridge_country_info.add_info(BridgeInfoType::PositiveReports, date, count_valid); - bridge_info - .info_by_country - .insert(country, bridge_country_info); + e.insert(bridge_country_info); + } else { + 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()) @@ -806,7 +796,7 @@ pub fn guess_blockages( let mut bridge_info: BridgeInfo = bincode::deserialize(&db.get(fingerprint).unwrap().unwrap()).unwrap(); let mut new_blockages = HashSet::::new(); - let fpr_str = array_bytes::bytes2hex("", &fingerprint); + let fpr_str = array_bytes::bytes2hex("", fingerprint); let first_date = if bridges_to_re_evaluate.contains_key(&fpr_str) { *bridges_to_re_evaluate.get(&fpr_str).unwrap() } else { @@ -871,12 +861,12 @@ pub async fn report_blockages( let mut blockages_str = HashMap::>::new(); for (fingerprint, countries) in blockages { let fpr_string = array_bytes::bytes2hex("", fingerprint); - if countries.len() > 0 { + if !countries.is_empty() { blockages_str.insert(fpr_string, countries); } } - if blockages_str.len() > 0 { + if !blockages_str.is_empty() { // Report blocked bridges to bridge distributor let client = Client::new(); let req = Request::builder() diff --git a/src/main.rs b/src/main.rs index 937ce20..5868a57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ use tokio::{ sync::{broadcast, mpsc, oneshot}, time::sleep, }; -#[cfg(not(features = "simulation"))] +#[cfg(not(feature = "simulation"))] use tokio_cron::{Job, Scheduler}; async fn shutdown_signal() { @@ -95,22 +95,20 @@ async fn update_daily_info( min_historical_days: u32, max_historical_days: u32, ) -> HashMap<[u8; 20], HashSet> { - update_extra_infos(&db, &extra_infos_base_url) - .await - .unwrap(); - update_negative_reports(&db, &distributors).await; - update_positive_reports(&db, &distributors).await; + update_extra_infos(db, extra_infos_base_url).await.unwrap(); + update_negative_reports(db, distributors).await; + update_positive_reports(db, distributors).await; let new_blockages = guess_blockages( - &db, + db, &analysis::NormalAnalyzer::new(max_threshold, scaling_factor), confidence, min_historical_days, max_historical_days, ); - report_blockages(&distributors, new_blockages.clone()).await; + report_blockages(distributors, new_blockages.clone()).await; // Generate tomorrow's key if we don't already have it - new_negative_report_key(&db, get_date() + 1); + new_negative_report_key(db, get_date() + 1); // Return new detected blockages new_blockages @@ -177,7 +175,7 @@ async fn context_manager( let blockages = update_daily_info( &db, &distributors, - &extra_infos_base_url, + extra_infos_base_url, confidence, max_threshold, scaling_factor, diff --git a/src/negative_report.rs b/src/negative_report.rs index 3057a2d..849005b 100644 --- a/src/negative_report.rs +++ b/src/negative_report.rs @@ -148,7 +148,7 @@ impl NegativeReport { /// Deserializes the report from slice, eliding the underlying process pub fn from_slice(slice: &[u8]) -> Result { - match serde_json::from_slice::(&slice) { + match serde_json::from_slice::(slice) { Ok(v) => v.to_report(), Err(_) => Err(NegativeReportError::FailedToDeserialize), } @@ -163,7 +163,7 @@ impl NegativeReport { } ProofOfBridgeKnowledge::HashOfBucket(pok) => { for b in &bridge_info.buckets { - let hash = HashOfBucket::new(&b, self.date, self.nonce); + let hash = HashOfBucket::new(b, self.date, self.nonce); if hash == pok { return true; } @@ -188,7 +188,7 @@ pub struct SerializableNegativeReport { impl SerializableNegativeReport { pub fn to_report(self) -> Result { - if self.country == "" { + if self.country.is_empty() { return Err(NegativeReportError::MissingCountryCode); } if !COUNTRY_CODES.contains(self.country.as_str()) { @@ -205,7 +205,7 @@ impl SerializableNegativeReport { fingerprint: self.fingerprint, bridge_pok: self.bridge_pok, country: self.country.to_string(), - date: self.date.try_into().unwrap(), + date: self.date, nonce: self.nonce, distributor: self.distributor, }) @@ -224,7 +224,7 @@ pub struct EncryptedNegativeReport { impl EncryptedNegativeReport { pub fn decrypt(self, secret: &StaticSecret) -> Result { - match self.ciphertext.decrypt(&secret) { + match self.ciphertext.decrypt(secret) { Ok(m) => match bincode::deserialize::(&m) { Ok(ser_report) => ser_report.to_report(), Err(_) => Err(NegativeReportError::FailedToDeserialize), diff --git a/src/positive_report.rs b/src/positive_report.rs index dbacb37..c6840a3 100644 --- a/src/positive_report.rs +++ b/src/positive_report.rs @@ -93,7 +93,7 @@ impl PositiveReport { }; SerializablePositiveReport { fingerprint: self.fingerprint, - bridge_token: bridge_token, + bridge_token, lox_proof: self.lox_proof, country: self.country, date: self.date, @@ -115,7 +115,7 @@ impl PositiveReport { /// Deserializes the report from slice, eliding the underlying process pub fn from_slice(slice: &[u8]) -> Result { - match serde_json::from_slice::(&slice) { + match serde_json::from_slice::(slice) { Ok(v) => v.to_report(), Err(_) => Err(PositiveReportError::FailedToDeserialize), } @@ -152,7 +152,7 @@ impl PositiveReport { let BP = self.lox_proof.BP; for bucket in buckets { if bucket * Htable == BP { - return la.handle_positive_report(self.lox_proof, &Htable).is_ok(); + return la.handle_positive_report(self.lox_proof, Htable).is_ok(); } } false @@ -177,7 +177,7 @@ impl SerializablePositiveReport { if REQUIRE_BRIDGE_TOKEN && self.bridge_token.is_none() { return Err(PositiveReportError::MissingBridgeToken); } - if self.country == "" { + if self.country.is_empty() { return Err(PositiveReportError::MissingCountryCode); } if !COUNTRY_CODES.contains(self.country.as_str()) { @@ -209,7 +209,7 @@ impl SerializablePositiveReport { }; Ok(PositiveReport { fingerprint: self.fingerprint, - bridge_token: bridge_token, + bridge_token, lox_proof: self.lox_proof, country: self.country, date: self.date, @@ -275,9 +275,9 @@ pub struct SerializableUnsignedBridgeToken { impl SerializableUnsignedBridgeToken { pub fn to_unsigned_bridge_token(self) -> Result { - if self.country == "" + if self.country.is_empty() || !COUNTRY_CODES.contains(self.country.as_str()) - || self.date > get_date().into() + || self.date > get_date() { return Err(PositiveReportError::InvalidBridgeToken); } @@ -345,7 +345,7 @@ impl SerializableBridgeToken { pub fn to_bridge_token(self) -> Result { let unsigned_bridge_token = self.unsigned_bridge_token.to_unsigned_bridge_token()?; Ok(BridgeToken { - unsigned_bridge_token: unsigned_bridge_token, + unsigned_bridge_token, sig: self.sig, }) } diff --git a/src/request_handler.rs b/src/request_handler.rs index 3d7ea7c..04489ef 100644 --- a/src/request_handler.rs +++ b/src/request_handler.rs @@ -48,7 +48,7 @@ pub async fn handle(db: &Db, req: Request) -> Result, Infal return Ok(prepare_header(val)); } }; - handle_encrypted_negative_report(&db, enr); + handle_encrypted_negative_report(db, enr); prepare_header("OK".to_string()) }), (&Method::POST, "/positivereport") => Ok::<_, Infallible>({ @@ -61,7 +61,7 @@ pub async fn handle(db: &Db, req: Request) -> Result, Infal return Ok(prepare_header(val)); } }; - save_positive_report_to_process(&db, pr); + save_positive_report_to_process(db, pr); prepare_header("OK".to_string()) }), _ => {