Browse Source

Verify Troll Patrol's inferences but still count wrong inferences

Vecna 4 months ago
parent
commit
0c74c62e3e
5 changed files with 215 additions and 3 deletions
  1. 1 1
      Dockerfile
  2. 1 0
      configs/troll_patrol_config.json.template
  3. 177 0
      src/direct_scan_server.rs
  4. 1 0
      src/lib.rs
  5. 35 2
      src/main.rs

+ 1 - 1
Dockerfile

@@ -53,7 +53,7 @@ RUN cp /home/user/build/config.toml .cargo/
 WORKDIR /home/user/build
 RUN git clone https://git-crysp.uwaterloo.ca/vvecna/troll-patrol.git
 WORKDIR /home/user/build/troll-patrol
-RUN git checkout 7acba0a6f00c6ffdb429b4993ee109a8e125b466
+RUN git checkout ff3aa2d71812fe5fb974856c021c98d812d2f880
 RUN mkdir -p .cargo
 RUN cp /home/user/build/config.toml .cargo/
 

+ 1 - 0
configs/troll_patrol_config.json.template

@@ -6,6 +6,7 @@
         "Lox": "http://127.0.0.1:8002"
     },
     "extra_infos_base_url": "http://127.0.0.1:8004/",
+    "verify_blockages_url": "http://127.0.0.1:8006/verify_blocked_bridges",
     "confidence": 0.95,
     "max_threshold": HARSHNESS,
     "scaling_factor": 0.25,

+ 177 - 0
src/direct_scan_server.rs

@@ -0,0 +1,177 @@
+// Simulates direct scans by keeping a list of bridges blocked by the censor
+
+use hyper::{
+    body::{self, Bytes},
+    header::HeaderValue,
+    server::conn::AddrStream,
+    service::{make_service_fn, service_fn},
+    Body, Method, Request, Response, Server, StatusCode,
+};
+use serde_json::json;
+use std::{
+    collections::{HashMap, HashSet},
+    convert::Infallible,
+    net::SocketAddr,
+    time::Duration,
+};
+use tokio::{
+    spawn,
+    sync::{mpsc, oneshot},
+    time::sleep,
+};
+
+async fn serve_direct_scan_api(
+    // We have to serialize our HashMap keys as Strings instead of [u8; 20]s,
+    // so it's easier to just use Strings throughout.
+    blocked_bridges: &mut HashSet<String>,
+    guessed_bridges: &mut HashMap<String, HashSet<String>>,
+    req: Request<Body>,
+) -> Result<Response<Body>, Infallible> {
+    match req.method() {
+        &Method::OPTIONS => Ok(Response::builder()
+            .header("Access-Control-Allow-Origin", HeaderValue::from_static("*"))
+            .header("Access-Control-Allow-Headers", "accept, content-type")
+            .header("Access-Control-Allow-Methods", "POST")
+            .status(200)
+            .body(Body::from("Allow POST"))
+            .unwrap()),
+        _ => match req.uri().path() {
+            "/add_blocked_bridges" => Ok::<_, Infallible>({
+                let bytes = body::to_bytes(req.into_body()).await.unwrap();
+                add_blocked_bridges(blocked_bridges, bytes)
+            }),
+            "/verify_blocked_bridges" => Ok::<_, Infallible>({
+                let bytes = body::to_bytes(req.into_body()).await.unwrap();
+                verify_blocked_bridges(blocked_bridges, guessed_bridges, bytes)
+            }),
+            // Get the bridges Troll Patrol guessed were blocked today
+            "/get_guessed_bridges" => Ok::<_, Infallible>({
+                prepare_header(serde_json::to_string(&guessed_bridges).unwrap())
+            }),
+            // Reset guessed bridges at the beginning of the day
+            "/reset_guessed_bridges" => Ok::<_, Infallible>({
+                *guessed_bridges = HashMap::<String, HashSet<String>>::new();
+                prepare_header("OK".to_string())
+            }),
+            _ => Ok::<_, Infallible>({
+                Response::builder()
+                    .status(StatusCode::NOT_FOUND)
+                    .body(Body::from("Not found"))
+                    .unwrap()
+            }),
+        },
+    }
+}
+
+pub async fn server() {
+    let (context_tx, context_rx) = mpsc::channel(32);
+    let request_tx = context_tx.clone();
+
+    spawn(async move { create_context_manager(context_rx).await });
+
+    let addr = SocketAddr::from(([127, 0, 0, 1], 8006));
+    let make_svc = make_service_fn(move |_conn: &AddrStream| {
+        let request_tx = request_tx.clone();
+        let service = service_fn(move |req| {
+            let request_tx = request_tx.clone();
+            let (response_tx, response_rx) = oneshot::channel();
+            let cmd = Command::Request {
+                req,
+                sender: response_tx,
+            };
+            async move {
+                request_tx.send(cmd).await.unwrap();
+                response_rx.await.unwrap()
+            }
+        });
+        async move { Ok::<_, Infallible>(service) }
+    });
+    let server = Server::bind(&addr).serve(make_svc);
+    println!("Listening on localhost:8006");
+    if let Err(e) = server.await {
+        eprintln!("server error: {}", e);
+    }
+}
+
+async fn create_context_manager(context_rx: mpsc::Receiver<Command>) {
+    tokio::select! {
+        create_context = context_manager(context_rx) => create_context,
+    }
+}
+
+async fn context_manager(mut context_rx: mpsc::Receiver<Command>) {
+    let mut blocked_bridges = HashSet::<String>::new();
+    let mut guessed_bridges = HashMap::<String, HashSet<String>>::new();
+
+    while let Some(cmd) = context_rx.recv().await {
+        use Command::*;
+        match cmd {
+            Request { req, sender } => {
+                let response =
+                    serve_direct_scan_api(&mut blocked_bridges, &mut guessed_bridges, req).await;
+                if let Err(e) = sender.send(response) {
+                    eprintln!("Server Response Error: {:?}", e);
+                }
+                sleep(Duration::from_millis(1)).await;
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+enum Command {
+    Request {
+        req: Request<Body>,
+        sender: oneshot::Sender<Result<Response<Body>, Infallible>>,
+    },
+}
+
+fn add_blocked_bridges(blocked_bridges: &mut HashSet<String>, request: Bytes) -> Response<Body> {
+    let new_blocked_bridges: HashSet<String> = match serde_json::from_slice(&request) {
+        Ok(req) => req,
+        Err(e) => {
+            let response = json!({"error": e.to_string()});
+            let val = serde_json::to_string(&response).unwrap();
+            return prepare_header(val);
+        }
+    };
+
+    blocked_bridges.extend(new_blocked_bridges);
+
+    prepare_header("OK".to_string())
+}
+
+fn verify_blocked_bridges(
+    blocked_bridges: &mut HashSet<String>,
+    guessed_bridges: &mut HashMap<String, HashSet<String>>,
+    request: Bytes,
+) -> Response<Body> {
+    let mut verified_blocked_bridges = HashMap::<String, HashSet<String>>::new();
+    let possibly_blocked_bridges: HashMap<String, HashSet<String>> =
+        match serde_json::from_slice(&request) {
+            Ok(req) => req,
+            Err(e) => {
+                let response = json!({"error": e.to_string()});
+                let val = serde_json::to_string(&response).unwrap();
+                return prepare_header(val);
+            }
+        };
+
+    *guessed_bridges = possibly_blocked_bridges.clone();
+
+    for (fpr, countries) in possibly_blocked_bridges {
+        if blocked_bridges.contains(&fpr) {
+            verified_blocked_bridges.insert(fpr, countries);
+        }
+    }
+
+    prepare_header(serde_json::to_string(&verified_blocked_bridges).unwrap())
+}
+
+// Prepare HTTP Response for successful Server Request
+fn prepare_header(response: String) -> Response<Body> {
+    let mut resp = Response::new(Body::from(response));
+    resp.headers_mut()
+        .insert("Access-Control-Allow-Origin", HeaderValue::from_static("*"));
+    resp
+}

+ 1 - 0
src/lib.rs

@@ -1,5 +1,6 @@
 pub mod bridge;
 pub mod censor;
 pub mod config;
+pub mod direct_scan_server;
 pub mod extra_infos_server;
 pub mod user;

+ 35 - 2
src/main.rs

@@ -7,7 +7,7 @@ use lox_simulation::{
     bridge::Bridge,
     censor::{self, Censor},
     config::Config as SConfig,
-    extra_infos_server,
+    direct_scan_server, extra_infos_server,
     user::User,
 };
 
@@ -88,6 +88,9 @@ pub async fn main() {
     let extra_infos_net = HyperNet {
         hostname: "http://localhost:8004".to_string(),
     };
+    let scan_net = HyperNet {
+        hostname: "http://localhost:8006".to_string(),
+    };
 
     let la_pubkeys = get_lox_auth_keys(&la_net).await.unwrap();
 
@@ -134,6 +137,12 @@ pub async fn main() {
     });
     sleep(Duration::from_millis(1)).await;
 
+    // Set up blockage verification server
+    spawn(async move {
+        direct_scan_server::server().await;
+    });
+    sleep(Duration::from_millis(1)).await;
+
     // Only consider bridges that have been distributed to users
     let mut false_neg = 0;
     let mut false_pos = 0;
@@ -164,6 +173,11 @@ pub async fn main() {
         // Save some function calls by storing this
         let date = get_date();
 
+        // Reset Troll Patrol's guesses
+        let _ = scan_net
+            .request("/reset_guessed_bridges".to_string(), vec![])
+            .await;
+
         // Count of users who could use at least one bridge today
         let mut count_users_can_connect = 0;
         let mut count_users_cannot_connect = 0;
@@ -335,6 +349,18 @@ pub async fn main() {
         }
 
         if censor.is_active() {
+            // Update blockage verification server with list of known bridges
+            let mut known_bridges = HashSet::<String>::new();
+            for bridge in &censor.known_bridges {
+                known_bridges.insert(array_bytes::bytes2hex("", bridge));
+            }
+            let _ = scan_net
+                .request(
+                    "/add_blocked_bridges".to_string(),
+                    serde_json::to_string(&known_bridges).unwrap().into(),
+                )
+                .await;
+
             censor.end_of_day_tasks(&sconfig, &mut bridges).await;
         }
 
@@ -362,7 +388,14 @@ pub async fn main() {
         }
 
         // TROLL PATROL TASKS
-        let new_blockages_resp = tp_net_test.request("/update".to_string(), vec![]).await;
+
+        // This gets verified guesses, which we ignore here
+        let _new_blockages_resp = tp_net_test.request("/update".to_string(), vec![]).await;
+
+        // Get Troll Patrol's unverified guesses
+        let new_blockages_resp = scan_net
+            .request("/get_guessed_bridges".to_string(), vec![])
+            .await;
         let new_blockages = match new_blockages_resp {
             Ok(resp) => match serde_json::from_slice(&resp) {
                 Ok(new_blockages) => new_blockages,