From f99c4e84886a978b3d606206061a9686a0eff203 Mon Sep 17 00:00:00 2001 From: _ <> Date: Sun, 10 Oct 2021 21:55:51 +0000 Subject: [PATCH] reproducing a crash in FL_Flex --- Cargo.lock | 13 - prototypes/ptth_quic_client_gui/Cargo.toml | 16 +- prototypes/ptth_quic_client_gui/src/main.rs | 396 ++------------------ 3 files changed, 22 insertions(+), 403 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9287c76..0df7791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,20 +1215,7 @@ dependencies = [ name = "ptth_quic_client_gui" version = "0.1.0" dependencies = [ - "anyhow", - "blake3", "fltk", - "quic_demo", - "quinn", - "rand", - "rand_chacha", - "reqwest", - "rmp-serde", - "serde", - "structopt", - "tokio", - "tracing", - "tracing-subscriber", ] [[package]] diff --git a/prototypes/ptth_quic_client_gui/Cargo.toml b/prototypes/ptth_quic_client_gui/Cargo.toml index 23b6df2..a73b674 100644 --- a/prototypes/ptth_quic_client_gui/Cargo.toml +++ b/prototypes/ptth_quic_client_gui/Cargo.toml @@ -1,24 +1,10 @@ [package] name = "ptth_quic_client_gui" version = "0.1.0" -authors = ["Trish"] +authors = [] edition = "2018" -license = "AGPL-3.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.38" -blake3 = "1.0.0" fltk = "1.2.7" -quic_demo = { path = "../quic_demo" } -quinn = "0.7.2" -rand = "0.8.4" -rand_chacha = "0.3.1" -reqwest = "0.11.4" -rmp-serde = "0.15.5" -serde = "1.0.130" -structopt = "0.3.20" -tokio = { version = "1.8.1", features = ["full"] } -tracing-subscriber = "0.2.16" -tracing = "0.1.25" diff --git a/prototypes/ptth_quic_client_gui/src/main.rs b/prototypes/ptth_quic_client_gui/src/main.rs index 6ee835b..6fd8286 100644 --- a/prototypes/ptth_quic_client_gui/src/main.rs +++ b/prototypes/ptth_quic_client_gui/src/main.rs @@ -1,393 +1,39 @@ -use std::{ - str::FromStr, -}; +use fltk::prelude::*; +use fltk::group::Flex; -use fltk::{ - app, - button::Button, - enums::CallbackTrigger, - frame::Frame, - group::Flex, - input::*, - prelude::*, - window::Window -}; -use rand::{ - Rng, - SeedableRng, -}; -use structopt::StructOpt; -use tokio::runtime::Runtime; - -use quic_demo::{ - client_proxy::*, - prelude::*, - protocol::PeerId, -}; - -#[derive (Debug, StructOpt)] -struct Opt { - #[structopt (long)] - window_title: Option , - #[structopt (long)] - relay_addr: Option , - #[structopt (long)] - client_id: Option , - #[structopt (long)] - cert_url: Option , -} - -#[derive (Clone, Copy)] -enum Message { - OpenPort (usize), - ClosePort (usize), - AddPort, -} - -struct GuiClient <'a> { - rt: &'a Runtime, - frame_status: Frame, - ports: Vec , -} - -struct Port { - gui: GuiPort, - forwarding_instance: Option , -} - -impl Port { - pub fn open_port (&mut self, rt: &Runtime, connection_p2_p3: quinn::Connection) - -> anyhow::Result <()> - { - let params = self.gui.get_params ()?; - - let _guard = rt.enter (); - let forwarding_instance = rt.block_on (ForwardingInstance::new ( - connection_p2_p3, - params, - ))?; - - self.gui.input_client_port.set_value (&forwarding_instance.local_port ().to_string ()); - - self.forwarding_instance.replace (forwarding_instance); - self.gui.set_forwarding (true); - - Ok (()) - } -} - -struct GuiPort { - row: fltk::group::Flex, - input_client_port: Input, - input_server_id: Input, - input_server_port: Input, - but_open: Button, - but_close: Button, -} - -impl GuiClient <'_> { - pub fn open_port ( - &mut self, - connection_p2_p3: quinn::Connection, - port_idx: usize, - ) -> anyhow::Result <()> - { - self.ports [port_idx].open_port (&self.rt, connection_p2_p3)?; - self.sync_status (); - - Ok (()) - } - - pub fn close_port (&mut self, port_idx: usize) -> anyhow::Result <()> { - if let Some (old_instance) = self.ports [port_idx].forwarding_instance.take () { - self.rt.block_on (async { - old_instance.close () - .await - .context ("closing ForwardingInstance")?; - - Ok::<_, anyhow::Error> (()) - })?; - } - - self.ports [port_idx].gui.set_forwarding (false); - self.sync_status (); - - Ok (()) - } - - fn open_ports (&self) -> usize { - self.ports.iter () - .map (|x| if x.forwarding_instance.is_some () { 1 } else { 0 }) - .sum () - } - - pub fn sync_status (&mut self) { - let open_ports = self.open_ports (); - - self.frame_status.set_label (&format! ("Forwarding {} ports", open_ports)); - } - - pub fn add_port ( - &mut self, - ports_col: &mut Flex, - fltk_tx: fltk::app::Sender - ) { - let mut gui = GuiPort::new (fltk_tx, self.ports.len ()); - ports_col.add (&gui.row); - ports_col.set_size (&mut gui.row, 30); - - let port = Port { - gui, - forwarding_instance: None, - }; - - self.ports.push (port); - } -} - -fn main () -> anyhow::Result <()> { - tracing_subscriber::fmt::init (); - let rt = Runtime::new ()?; - - let opt = Opt::from_args (); - - let (fltk_tx, fltk_rx) = app::channel:: (); - - let app = app::App::default (); - let window_title = opt.window_title.clone ().unwrap_or_else (|| "PTTH client proxy".to_string ()); - let mut wind = Window::new (100, 100, 800, 600, None) - .with_label (&window_title); - wind.make_resizable (true); +fn main () { + let _wind = fltk::window::Window::default ().with_size (800, 600); let mut col = Flex::default ().column ().size_of_parent (); - let mut frame_status = Frame::default (); - col.set_size (&mut frame_status, 30); + // If I set `workaround` to true so that the column contains a single + // non-sized element, then I can add 100 rows with no problem. + // Otherwise it crashes at 11. - { - let mut row = Flex::default ().row (); - - let mut l = Frame::default ().with_label ("Server ID"); - row.set_size (&mut l, 120); - let mut l = Frame::default ().with_label ("Server port"); - row.set_size (&mut l, 80); - let mut l = Frame::default ().with_label ("Local port"); - row.set_size (&mut l, 80); + let workaround = true; + if workaround { + let row = Flex::default ().row (); row.end (); - col.set_size (&mut row, 30); + col.add (&row); } - let mut ports_col = Flex::default ().column (); - ports_col.end (); + // 11 will trigger the floating-point exception, 10 will not - let mut but_add_port = Button::default ().with_label ("+"); - but_add_port.set_trigger (CallbackTrigger::Release); - but_add_port.emit (fltk_tx, Message::AddPort); - col.set_size (&mut but_add_port, 30); - - col.end (); - - let mut gui_client = GuiClient { - rt: &rt, - frame_status, - ports: Default::default (), - }; - - gui_client.add_port (&mut ports_col, fltk_tx); - ports_col.recalc (); - - gui_client.sync_status (); - - wind.end (); - wind.show (); - - let connection_p2_p3 = rt.block_on (async move { - let server_cert = match opt.cert_url.as_ref () { - Some (url) => reqwest::get (url).await?.bytes ().await?, - None => tokio::fs::read ("ptth_quic_output/quic_server.crt").await?.into (), - }; - - let relay_addr = opt.relay_addr - .unwrap_or_else (|| String::from ("127.0.0.1:30380")) - .parse () - .context ("relay_addr should be like 127.0.0.1:30380")?; - let endpoint = make_client_endpoint ("0.0.0.0:0".parse ()?, &[&server_cert])?; - - trace! ("Connecting to relay server"); - - let client_id = opt.client_id.unwrap_or_else (|| "bogus_client".to_string ()); - - let quinn::NewConnection { - connection, - .. - } = protocol::p2_connect_to_p3 (&endpoint, &relay_addr, &client_id).await - .context ("P2 can't connect to P3")?; - - Ok::<_, anyhow::Error> (connection) - })?; - - while app.wait () { - match fltk_rx.recv () { - Some (Message::OpenPort (port_idx)) => { - if let Err (e) = gui_client.open_port (connection_p2_p3.clone (), port_idx) - { - error! ("{:?}", e); - } - }, - Some (Message::ClosePort (port_idx)) => { - gui_client.close_port (port_idx)?; - }, - Some (Message::AddPort) => { - gui_client.add_port (&mut ports_col, fltk_tx); - ports_col.recalc (); - ports_col.redraw (); - }, - None => (), - } - } - - Ok (()) -} - -fn set_active (w: &mut W, b: bool) { - if b { - w.activate (); + let count = if workaround { + 100 } else { - w.deactivate (); - } -} - -impl GuiPort { - fn new (fltk_tx: fltk::app::Sender , port_idx: usize) -> Self { - + 11 + }; + + for _ in 0..count { let mut row = Flex::default ().row (); - - let mut input_server_id = Input::default (); - let mut input_server_port = Input::default (); - let mut input_client_port = Input::default (); - let mut but_open = Button::default ().with_label ("Open"); - let mut but_close = Button::default ().with_label ("Close"); - - row.set_size (&mut input_server_id, 120); - row.set_size (&mut input_server_port, 80); - row.set_size (&mut input_client_port, 80); - row.set_size (&mut but_open, 80); - row.set_size (&mut but_close, 80); - - input_client_port.set_value (""); - input_client_port.set_readonly (true); - input_server_id.set_value ("bogus_server"); - input_server_port.set_value ("5900"); - - but_open.set_trigger (CallbackTrigger::Release); - but_open.emit (fltk_tx, Message::OpenPort (port_idx)); - but_close.set_trigger (CallbackTrigger::Release); - but_close.emit (fltk_tx, Message::ClosePort (port_idx)); - row.end (); - let mut output = Self { - row, - input_client_port, - input_server_id, - input_server_port, - but_open, - but_close, - }; - - output.set_forwarding (false); - - output - } - - fn get_params (&self) -> anyhow::Result - { - let server_tcp_port = u16::from_str (&self.input_server_port.value ())?; - let server_id = self.input_server_id.value (); - - let client_tcp_port = PortInfo { - relay_addr: "bogus_relay", - server_id: &server_id, - server_tcp_port, - }.random_eph_port (); - - Ok (ForwardingParams { - client_tcp_port, - server_id, - server_tcp_port, - }) - } - - fn set_forwarding (&mut self, x: bool) { - set_active (&mut self.input_client_port, x); - set_active (&mut self.input_server_id, !x); - set_active (&mut self.input_server_port, !x); - set_active (&mut self.but_open, !x); - set_active (&mut self.but_close, x); - - self.but_open.set (x); - self.but_close.set (!x); + col.add (&row); + col.set_size (&mut row, 30); } + col.recalc (); } -// This can collide, but who cares -// It's not secure or anything - It's just supposed to pick a port somewhat -// deterministically based on the server and relay info. - -#[derive (serde::Serialize)] -struct PortInfo <'a> { - relay_addr: &'a str, - server_id: &'a str, - server_tcp_port: u16 -} - -impl PortInfo <'_> { - // https://en.wikipedia.org/wiki/TCP_ports#Dynamic,_private_or_ephemeral_ports - - fn random_eph_port (&self) -> u16 - { - let seed = blake3::hash (&rmp_serde::to_vec (self).expect ("Can't hash PortInfo - impossible error")); - - let mut rng = rand_chacha::ChaCha20Rng::from_seed (*seed.as_bytes ()); - - let tcp_eph_range = 49152..=65535; - rng.gen_range (tcp_eph_range) - } -} - -#[cfg (test)] -mod test { - use blake3::Hasher; - - use super::*; - - #[test] - fn prng () { - let hasher = Hasher::default (); - let seed = hasher.finalize (); - - let mut rng = rand_chacha::ChaCha20Rng::from_seed (*seed.as_bytes ()); - - let tcp_eph_range = 49152..=65535; - let port = rng.gen_range (tcp_eph_range); - assert_eq! (port, 49408); - - for (input, expected) in vec! [ - (("bogus_relay", "bogus_server", 22), 62350), - (("real_relay", "bogus_server", 22), 61081), - (("bogus_relay", "real_server", 22), 50513), - (("bogus_relay", "bogus_server", 5900), 60730), - ] { - let (relay_addr, server_id, server_tcp_port) = input; - let input = PortInfo { - relay_addr, - server_id, - server_tcp_port, - }; - let actual = input.random_eph_port (); - assert_eq! (expected, actual); - } - } -}