use std::{ str::FromStr, }; use fltk::{ app, button::Button, enums::CallbackTrigger, frame::Frame, group::Flex, input::*, prelude::*, window::Window }; 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 , } 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 <()> { let params = self.ports [port_idx].gui.get_params ()?; let _guard = self.rt.enter (); let forwarding_instance = self.rt.block_on (ForwardingInstance::new ( connection_p2_p3, params, ))?; self.ports [port_idx].forwarding_instance.replace (forwarding_instance); self.ports [port_idx].gui.set_forwarding (true); 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); let mut col = Flex::default ().column ().size_of_parent (); let mut frame_status = Frame::default (); col.set_size (&mut frame_status, 30); { let mut row = Flex::default ().row (); let mut l = Frame::default ().with_label ("Local port"); row.set_size (&mut l, 80); 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); row.end (); col.set_size (&mut row, 30); } let mut ports_col = Flex::default ().column (); ports_col.end (); 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 (); } else { w.deactivate (); } } impl GuiPort { fn new (fltk_tx: fltk::app::Sender , port_idx: usize) -> Self { let mut row = Flex::default ().row (); let mut input_client_port = Input::default (); let mut input_server_id = Input::default (); let mut input_server_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_client_port, 80); row.set_size (&mut input_server_id, 120); row.set_size (&mut input_server_port, 80); row.set_size (&mut but_open, 80); row.set_size (&mut but_close, 80); input_client_port.set_value ("5901"); 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)); set_active (&mut but_open, true); set_active (&mut but_close, false); row.end (); Self { row, input_client_port, input_server_id, input_server_port, but_open, but_close, } } fn get_params (&self) -> anyhow::Result { let client_tcp_port = u16::from_str (&self.input_client_port.value ())?; let server_id = self.input_server_id.value (); let server_tcp_port = u16::from_str (&self.input_server_port.value ())?; 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); } }