use std::str::FromStr; use fltk::{ app, button::Button, enums::CallbackTrigger, frame::Frame, 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), } struct GuiClient <'a> { rt: &'a Runtime, frame_status: Frame, ports: Vec , } struct Port { gui: GuiPort, forwarding_instance: Option , } struct GuiPort { 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 () } fn sync_status (&mut self) { let open_ports = self.open_ports (); self.frame_status.set_label (&format! ("Forwarding {} ports", open_ports)); } } 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); let margin = 10; let h = 30; let mut x = margin; let mut y = margin; let frame_status = Frame::new (x, y, 800 - 20, h, "Forwarding 0 ports"); y += h + margin; x = margin; { let w = 80; Frame::new (x, y, w, h, "Local port"); x += w + margin; let w = 120; Frame::new (x, y, w, h, "Server ID"); x += w + margin; let w = 80; Frame::new (x, y, w, h, "Server port"); // x += w + margin; } y += h + margin; x = margin; let gui_port_0 = GuiPort::new (fltk_tx, &mut x, y, 0); y += h + margin; x = margin; let gui_port_1 = GuiPort::new (fltk_tx, &mut x, y, 1); y += h + margin; x = margin; let gui_port_2 = GuiPort::new (fltk_tx, &mut x, y, 2); // y += h + margin; // x = margin; let ports = vec! [ Port { gui: gui_port_0, forwarding_instance: None, }, Port { gui: gui_port_1, forwarding_instance: None, }, Port { gui: gui_port_2, forwarding_instance: None, }, ]; let mut gui_client = GuiClient { rt: &rt, frame_status, ports, }; // y += h + margin; 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)) => { match gui_client.open_port (connection_p2_p3.clone (), port_idx) { Err (e) => error! ("{:?}", e), _ => (), }; }, Some (Message::ClosePort (port_idx)) => { gui_client.close_port (port_idx)?; }, 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 , x: &mut i32, y: i32, port_idx: usize) -> Self { let margin = 10; let h = 30; let w = 80; let mut input_client_port = Input::new (*x, y, w, h, ""); *x += w + margin; let w = 120; let mut input_server_id = Input::new (*x, y, w, h, ""); *x += w + margin; let w = 80; let mut input_server_port = Input::new (*x, y, w, h, ""); *x += w + margin; let w = 80; let mut but_open = Button::new (*x, y, w, h, "Open"); *x += w + margin; let w = 80; let mut but_close = Button::new (*x, y, w, h, "Close"); // *x += w + margin; 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); Self { 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); } }