lox/crates/lox-distributor/src/resource_parser.rs

185 lines
6.5 KiB
Rust
Raw Normal View History

2023-08-28 19:33:19 -04:00
use chrono::{Duration, Utc};
use lox_library::bridge_table::{BridgeLine, BRIDGE_BYTES, MAX_BRIDGES_PER_BUCKET};
use rdsys_backend::proto::Resource;
2023-08-28 19:33:19 -04:00
pub const ACCEPTED_HOURS_OF_FAILURE: i64 = 3;
// Parse each resource from rdsys into a Bridgeline as expected by the Lox Bridgetable
pub fn parse_into_bridgelines(resources: Vec<Resource>) -> Vec<BridgeLine> {
2023-09-13 12:08:48 -04:00
let mut bridgelines: Vec<BridgeLine> = Vec::new();
for resource in resources {
let mut ip_bytes: [u8; 16] = [0; 16];
ip_bytes[..resource.address.len()].copy_from_slice(resource.address.as_bytes());
let resource_uid = resource
.get_uid()
.expect("Unable to get Fingerprint UID of resource");
let infostr: String = format!(
"type={} blocked_in={:?} protocol={} fingerprint={:?} or_addresses={:?} distribution={} flags={:?} params={:?}",
resource.r#type,
resource.blocked_in,
resource.protocol,
resource.fingerprint,
resource.or_addresses,
resource.distribution,
resource.flags,
resource.params,
);
2023-09-13 12:08:48 -04:00
let mut info_bytes: [u8; BRIDGE_BYTES - 26] = [0; BRIDGE_BYTES - 26];
2023-09-13 12:08:48 -04:00
info_bytes[..infostr.len()].copy_from_slice(infostr.as_bytes());
bridgelines.push(BridgeLine {
addr: ip_bytes,
port: resource.port,
uid_fingerprint: resource_uid,
info: info_bytes,
})
}
2023-09-13 12:08:48 -04:00
bridgelines
}
// Allocate each Bridgeline into a bucket that will later be allocated into spare buckets or open invitation buckets
// Any leftover buckets from total_bridgelines % MAX_BRIDGES_PER_BUCKET are returned in a separate Vec<Bridgeline>
// TODO: Improve this function to sort bridgelines into buckets in a more intentional manner. This could include
// sorting bridgelines with high bandwidth into buckets that are only distributed to more trusted users or sorting
// bridgelines by location
pub fn parse_into_buckets(
mut bridgelines: Vec<BridgeLine>,
) -> (Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]>, Vec<BridgeLine>) {
let mut buckets: Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]> = Vec::new();
let mut count = 0;
let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
let mut leftovers: Vec<BridgeLine> = Vec::new();
for bridgeline in bridgelines.clone() {
println!(
"Added bridge with fingerprint: {:?}",
bridgeline.uid_fingerprint
);
if count < MAX_BRIDGES_PER_BUCKET {
bucket[count] = bridgeline;
count += 1;
} else {
buckets.push(bucket);
count = 0;
bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
}
}
// Handle the extra buckets that were not allocated already
if count != 0 {
for _ in 0..count {
// Assumes that the unallocated bridgelines will be the last x of the passed bridgelines
leftovers.push(bridgelines.pop().unwrap());
}
}
(buckets, leftovers)
}
2023-08-28 19:33:19 -04:00
// Sort Resources into those that are functional and those that are failing based on the last time
// they were passing tests. Before passing them back to the calling function, they are parsed into
// BridgeLines
pub fn sort_for_parsing(resources: Vec<Resource>) -> (Vec<BridgeLine>, Vec<BridgeLine>) {
let mut functional: Vec<Resource> = Vec::new();
let mut failing: Vec<Resource> = Vec::new();
for resource in resources {
if resource.last_passed + Duration::hours(ACCEPTED_HOURS_OF_FAILURE) >= Utc::now() {
functional.push(resource);
} else {
failing.push(resource);
}
}
let functional_bridgelines = parse_into_bridgelines(functional);
let failing_bridgelines = parse_into_bridgelines(failing);
(functional_bridgelines, failing_bridgelines)
}
#[cfg(test)]
mod tests {
use rdsys_backend::proto::Resource;
use std::collections::HashMap;
use chrono::{Duration, Utc};
use super::sort_for_parsing;
pub fn make_resource(
rtype: String,
address: String,
port: u16,
fingerprint: String,
last_passed: i64,
) -> Resource {
let mut flags = HashMap::new();
flags.insert(String::from("fast"), true);
flags.insert(String::from("stable"), true);
let mut params = HashMap::new();
params.insert(
String::from("password"),
String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"),
);
Resource {
r#type: String::from(rtype),
blocked_in: HashMap::new(),
last_passed: Utc::now() - Duration::hours(last_passed),
protocol: String::from("tcp"),
address: String::from(address),
port: port,
fingerprint: String::from(fingerprint),
or_addresses: None,
distribution: String::from("https"),
flags: Some(flags),
params: Some(params),
}
}
#[test]
fn test_sort_for_parsing() {
let resource_one = make_resource(
"scramblesuit".to_owned(),
"123.456.789.100".to_owned(),
3002,
"BE84A97D02130470A1C77839954392BA979F7EE1".to_owned(),
2,
);
let resource_two = make_resource(
"https".to_owned(),
"123.222.333.444".to_owned(),
6002,
"C56B9EF202130470A1C77839954392BA979F7FF9".to_owned(),
5,
);
let resource_three = make_resource(
"scramblesuit".to_owned(),
"444.888.222.100".to_owned(),
3042,
"1A4C8BD902130470A1C77839954392BA979F7B46".to_owned(),
4,
);
let resource_four = make_resource(
"https".to_owned(),
"555.444.212.100".to_owned(),
8022,
"FF024DC302130470A1C77839954392BA979F7AE2".to_owned(),
3,
);
let resource_five = make_resource(
"https".to_owned(),
"234.111.212.100".to_owned(),
10432,
"7B4DE14CB2130470A1C77839954392BA979F7AE2".to_owned(),
1,
);
let mut test_vec: Vec<Resource> = Vec::new();
test_vec.push(resource_one);
test_vec.push(resource_two);
test_vec.push(resource_three);
test_vec.push(resource_four);
test_vec.push(resource_five);
let (functional, failing) = sort_for_parsing(test_vec);
assert!(
functional.len() == 2,
"There should be 2 functional bridges"
);
assert!(failing.len() == 3, "There should be 3 failing bridges");
}
}