diff --git a/src/user.rs b/src/user.rs index d1e9ff0..f39ca10 100644 --- a/src/user.rs +++ b/src/user.rs @@ -40,6 +40,13 @@ pub struct User { // How likely is this user to use bridges on a given day? prob_use_bridges: f64, + + // If the censor implements partial blocking, is the user blocked? + in_censorship_range: bool, + + // Track date the user joined and whether they're able to connect + pub join_date: u32, + pub able_to_connect: bool, } impl User { @@ -51,29 +58,51 @@ impl User { ) -> Result { let cred = Self::get_new_credential(&config).await?.0; - // Probabilistically decide whether this user submits reports - let submits_reports = if is_censor { - false + // Decide how likely this user is to use bridges on a given day + // and whether they submit reports + let (prob_use_bridges, submits_reports) = if is_censor { + (0.0, false) } else { - event_happens(config.prob_user_submits_reports) + let mut rng = rand::thread_rng(); + let prob_use_bridges = rng.gen_range(0.0..=1.0); + let submits_reports = event_happens(config.prob_user_submits_reports); + (prob_use_bridges, submits_reports) }; - // Randomly determine how likely this user is to use bridges on - // a given day - let mut rng = rand::thread_rng(); - let prob_use_bridges = rng.gen_range(0.0..=1.0); + let in_censorship_range = if config.censor_totality == Partial { + event_happens(config.censor_partial_blocking_percent) + } else { + true + }; - // If the user cooperates with the censor, immediately tell the - // censor about all the bridges - if is_censor { - let (bucket, _reachcred) = get_bucket(&config.la_net, &cred).await?; - for bridgeline in bucket { - if bridgeline != BridgeLine::default() { - if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { - let bridge = Bridge::from_bridge_line(&bridgeline); - bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); - } + let mut able_to_connect = false; + + // Immediately download and try to use our bridges, or report them to the censor. + let (bucket, _reachcred) = get_bucket(&config.la_net, &cred).await?; + for bridgeline in bucket { + if bridgeline != BridgeLine::default() { + if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + let bridge = Bridge::from_bridge_line(&bridgeline); + bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); + } + let bridge = bridges + .get_mut(&bridgeline.get_hashed_fingerprint()) + .unwrap(); + if is_censor { censor.learn_bridge(&bridgeline.get_hashed_fingerprint()); + } else if Self::connect(in_censorship_range, config, bridge, censor) { + able_to_connect = true; + } else if submits_reports { + // New user only has one bridge, so no need + // to collect the negative reports before + // sending. Just send one now. + let mut negative_reports = Vec::::new(); + negative_reports.push(NegativeReport::from_bridgeline( + bridgeline, + config.country.to_string(), + BridgeDistributor::Lox, + )); + Self::send_negative_reports(&config, negative_reports).await?; } } } @@ -84,6 +113,9 @@ impl User { secondary_cred: None, submits_reports: submits_reports, prob_use_bridges: prob_use_bridges, + in_censorship_range, + join_date: get_date(), + able_to_connect, }) } @@ -139,20 +171,50 @@ impl User { (prob_use_bridges, submits_reports) }; - // If the user cooperates with the censor, immediately tell the - // censor about all the bridges - if is_censor { - let (bucket, _reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?; - for bridgeline in bucket { - if bridgeline != BridgeLine::default() { - if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { - let bridge = Bridge::from_bridge_line(&bridgeline); - bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); - } + let in_censorship_range = if config.censor_totality == Partial { + event_happens(config.censor_partial_blocking_percent) + } else { + true + }; + + let mut able_to_connect = false; + + // Immediately download bucket and test bridges or give them to + // the censor + let mut negative_reports = Vec::::new(); + let (bucket, _reachcred) = get_bucket(&config.la_net, &friend_cred).await?; + for bridgeline in bucket { + if bridgeline != BridgeLine::default() { + if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + let bridge = Bridge::from_bridge_line(&bridgeline); + bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); + } + let bridge = bridges + .get_mut(&bridgeline.get_hashed_fingerprint()) + .unwrap(); + if is_censor { censor.learn_bridge(&bridgeline.get_hashed_fingerprint()); + } else if Self::connect(in_censorship_range, config, bridge, censor) { + able_to_connect = true; + } else if submits_reports { + negative_reports.push(NegativeReport::from_bridgeline( + bridgeline, + config.country.to_string(), + BridgeDistributor::Lox, + )); } } } + // Submit reports if we have them + if negative_reports.len() > 0 { + Self::send_negative_reports(&config, negative_reports).await?; + } + + let in_censorship_range = if config.censor_totality == Partial { + event_happens(config.censor_partial_blocking_percent) + } else { + true + }; Ok(Self { is_censor, @@ -160,17 +222,24 @@ impl User { secondary_cred: None, submits_reports: submits_reports, prob_use_bridges: prob_use_bridges, + in_censorship_range, + join_date: get_date(), + able_to_connect, }) } // Attempt to "connect" to the bridge, returns true if successful. // Note that this does not involve making a real connection to a // real bridge. - pub fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool { + pub fn connect( + in_censorship_range: bool, + config: &Config, + bridge: &mut Bridge, + censor: &Censor, + ) -> bool { if censor.blocks_bridge(config, &bridge.fingerprint) { if config.censor_totality == Full - || config.censor_totality == Partial - && event_happens(censor.partial_blocking_percent) + || config.censor_totality == Partial && in_censorship_range { // If censor tries to hide its censorship, record a // false connection @@ -327,7 +396,8 @@ impl User { for i in 0..bucket.len() { // At level 0, we only have 1 bridge if bucket[i] != BridgeLine::default() { - if self.connect( + if Self::connect( + self.in_censorship_range, &config, bridges .get_mut(&bucket[i].get_hashed_fingerprint()) @@ -374,7 +444,8 @@ impl User { ); } // Attempt to connect to second cred's bridge - if self.connect( + if Self::connect( + self.in_censorship_range, &config, bridges .get_mut(&bridgeline.get_hashed_fingerprint())