reproducing a crash in FL_Flex

main
_ 2021-10-10 21:55:51 +00:00
parent e6cf9e2b72
commit f99c4e8488
3 changed files with 22 additions and 403 deletions

13
Cargo.lock generated
View File

@ -1215,20 +1215,7 @@ dependencies = [
name = "ptth_quic_client_gui" name = "ptth_quic_client_gui"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"blake3",
"fltk", "fltk",
"quic_demo",
"quinn",
"rand",
"rand_chacha",
"reqwest",
"rmp-serde",
"serde",
"structopt",
"tokio",
"tracing",
"tracing-subscriber",
] ]
[[package]] [[package]]

View File

@ -1,24 +1,10 @@
[package] [package]
name = "ptth_quic_client_gui" name = "ptth_quic_client_gui"
version = "0.1.0" version = "0.1.0"
authors = ["Trish"] authors = []
edition = "2018" edition = "2018"
license = "AGPL-3.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.38"
blake3 = "1.0.0"
fltk = "1.2.7" 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"

View File

@ -1,393 +1,39 @@
use std::{ use fltk::prelude::*;
str::FromStr, use fltk::group::Flex;
};
use fltk::{ fn main () {
app, let _wind = fltk::window::Window::default ().with_size (800, 600);
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 <String>,
#[structopt (long)]
relay_addr: Option <String>,
#[structopt (long)]
client_id: Option <PeerId>,
#[structopt (long)]
cert_url: Option <String>,
}
#[derive (Clone, Copy)]
enum Message {
OpenPort (usize),
ClosePort (usize),
AddPort,
}
struct GuiClient <'a> {
rt: &'a Runtime,
frame_status: Frame,
ports: Vec <Port>,
}
struct Port {
gui: GuiPort,
forwarding_instance: Option <ForwardingInstance>,
}
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 <Message>
) {
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::<Message> ();
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);
let mut col = Flex::default ().column ().size_of_parent (); let mut col = Flex::default ().column ().size_of_parent ();
let mut frame_status = Frame::default (); // If I set `workaround` to true so that the column contains a single
col.set_size (&mut frame_status, 30); // non-sized element, then I can add 100 rows with no problem.
// Otherwise it crashes at 11.
{ let workaround = true;
let mut row = Flex::default ().row (); if workaround {
let 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);
row.end (); row.end ();
col.set_size (&mut row, 30); col.add (&row);
} }
let mut ports_col = Flex::default ().column (); // 11 will trigger the floating-point exception, 10 will not
ports_col.end ();
let mut but_add_port = Button::default ().with_label ("+"); let count = if workaround {
but_add_port.set_trigger (CallbackTrigger::Release); 100
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: WidgetExt> (w: &mut W, b: bool) {
if b {
w.activate ();
} }
else { else {
w.deactivate (); 11
} };
}
for _ in 0..count {
impl GuiPort {
fn new (fltk_tx: fltk::app::Sender <Message>, port_idx: usize) -> Self {
let mut row = Flex::default ().row (); 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 (); row.end ();
let mut output = Self { col.add (&row);
row, col.set_size (&mut row, 30);
input_client_port,
input_server_id,
input_server_port,
but_open,
but_close,
};
output.set_forwarding (false);
output
}
fn get_params (&self) -> anyhow::Result <ForwardingParams>
{
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.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);
}
}
}