2021-10-10 19:37:58 +00:00
|
|
|
use std::{
|
|
|
|
str::FromStr,
|
|
|
|
};
|
2021-07-19 01:56:42 +00:00
|
|
|
|
2021-07-19 00:01:46 +00:00
|
|
|
use fltk::{
|
|
|
|
app,
|
|
|
|
button::Button,
|
|
|
|
enums::CallbackTrigger,
|
|
|
|
frame::Frame,
|
2021-10-10 19:37:58 +00:00
|
|
|
group::Flex,
|
2021-07-19 00:01:46 +00:00
|
|
|
input::*,
|
|
|
|
prelude::*,
|
|
|
|
window::Window
|
|
|
|
};
|
2021-10-10 21:20:35 +00:00
|
|
|
use rand::{
|
|
|
|
Rng,
|
|
|
|
SeedableRng,
|
|
|
|
};
|
2021-07-19 00:25:25 +00:00
|
|
|
use structopt::StructOpt;
|
2021-10-10 17:56:13 +00:00
|
|
|
use tokio::runtime::Runtime;
|
2021-07-19 00:25:25 +00:00
|
|
|
|
2021-07-21 23:28:42 +00:00
|
|
|
use quic_demo::{
|
|
|
|
client_proxy::*,
|
|
|
|
prelude::*,
|
|
|
|
protocol::PeerId,
|
|
|
|
};
|
2021-07-19 00:25:25 +00:00
|
|
|
|
|
|
|
#[derive (Debug, StructOpt)]
|
|
|
|
struct Opt {
|
2021-07-19 02:28:18 +00:00
|
|
|
#[structopt (long)]
|
|
|
|
window_title: Option <String>,
|
2021-07-19 00:25:25 +00:00
|
|
|
#[structopt (long)]
|
|
|
|
relay_addr: Option <String>,
|
|
|
|
#[structopt (long)]
|
|
|
|
client_id: Option <PeerId>,
|
2021-10-01 17:19:23 +00:00
|
|
|
#[structopt (long)]
|
|
|
|
cert_url: Option <String>,
|
2021-07-19 00:25:25 +00:00
|
|
|
}
|
2021-07-19 00:01:46 +00:00
|
|
|
|
|
|
|
#[derive (Clone, Copy)]
|
|
|
|
enum Message {
|
2021-07-19 02:37:09 +00:00
|
|
|
OpenPort (usize),
|
|
|
|
ClosePort (usize),
|
2021-10-10 19:51:25 +00:00
|
|
|
AddPort,
|
2021-07-19 00:01:46 +00:00
|
|
|
}
|
|
|
|
|
2021-10-10 17:56:13 +00:00
|
|
|
struct GuiClient <'a> {
|
|
|
|
rt: &'a Runtime,
|
|
|
|
frame_status: Frame,
|
2021-10-10 18:31:52 +00:00
|
|
|
ports: Vec <Port>,
|
2021-10-10 23:34:50 +00:00
|
|
|
but_add_port: Button,
|
2021-10-10 18:31:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Port {
|
|
|
|
gui: GuiPort,
|
|
|
|
forwarding_instance: Option <ForwardingInstance>,
|
|
|
|
}
|
|
|
|
|
2021-10-10 20:42:17 +00:00
|
|
|
impl Port {
|
2021-10-10 23:34:50 +00:00
|
|
|
pub fn open_port (
|
|
|
|
&mut self,
|
|
|
|
rt: &Runtime,
|
|
|
|
connection_p2_p3: quinn::Connection,
|
|
|
|
) -> anyhow::Result <()>
|
2021-10-10 20:42:17 +00:00
|
|
|
{
|
|
|
|
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 (())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-10 18:31:52 +00:00
|
|
|
struct GuiPort {
|
2021-10-10 19:37:58 +00:00
|
|
|
row: fltk::group::Flex,
|
2021-10-10 18:31:52 +00:00
|
|
|
input_client_port: Input,
|
|
|
|
input_server_id: Input,
|
|
|
|
input_server_port: Input,
|
|
|
|
but_open: Button,
|
|
|
|
but_close: Button,
|
2021-10-10 17:56:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiClient <'_> {
|
|
|
|
pub fn open_port (
|
|
|
|
&mut self,
|
|
|
|
connection_p2_p3: quinn::Connection,
|
|
|
|
port_idx: usize,
|
|
|
|
) -> anyhow::Result <()>
|
|
|
|
{
|
2022-05-26 01:19:54 +00:00
|
|
|
self.ports [port_idx].open_port (self.rt, connection_p2_p3)?;
|
2021-10-10 18:00:20 +00:00
|
|
|
self.sync_status ();
|
2021-10-10 17:56:13 +00:00
|
|
|
|
|
|
|
Ok (())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn close_port (&mut self, port_idx: usize) -> anyhow::Result <()> {
|
2021-10-10 18:31:52 +00:00
|
|
|
if let Some (old_instance) = self.ports [port_idx].forwarding_instance.take () {
|
2021-10-10 18:08:25 +00:00
|
|
|
self.rt.block_on (async {
|
|
|
|
old_instance.close ()
|
|
|
|
.await
|
|
|
|
.context ("closing ForwardingInstance")?;
|
|
|
|
|
|
|
|
Ok::<_, anyhow::Error> (())
|
|
|
|
})?;
|
2021-10-10 17:56:13 +00:00
|
|
|
}
|
|
|
|
|
2021-10-10 18:31:52 +00:00
|
|
|
self.ports [port_idx].gui.set_forwarding (false);
|
2021-10-10 18:00:20 +00:00
|
|
|
self.sync_status ();
|
2021-10-10 17:56:13 +00:00
|
|
|
|
|
|
|
Ok (())
|
|
|
|
}
|
2021-10-10 18:00:20 +00:00
|
|
|
|
|
|
|
fn open_ports (&self) -> usize {
|
2021-10-10 18:31:52 +00:00
|
|
|
self.ports.iter ()
|
|
|
|
.map (|x| if x.forwarding_instance.is_some () { 1 } else { 0 })
|
2021-10-10 18:00:20 +00:00
|
|
|
.sum ()
|
|
|
|
}
|
|
|
|
|
2021-10-10 19:44:45 +00:00
|
|
|
pub fn sync_status (&mut self) {
|
2021-10-10 18:00:20 +00:00
|
|
|
let open_ports = self.open_ports ();
|
|
|
|
|
|
|
|
self.frame_status.set_label (&format! ("Forwarding {} ports", open_ports));
|
|
|
|
}
|
2021-10-10 19:55:52 +00:00
|
|
|
|
|
|
|
pub fn add_port (
|
|
|
|
&mut self,
|
|
|
|
ports_col: &mut Flex,
|
|
|
|
fltk_tx: fltk::app::Sender <Message>
|
|
|
|
) {
|
2021-10-19 16:19:50 +00:00
|
|
|
const MAX_PORTS: usize = 15;
|
2021-10-19 16:19:01 +00:00
|
|
|
|
|
|
|
if self.ports.len () >= MAX_PORTS {
|
2021-10-10 23:34:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
let gui = GuiPort::new (fltk_tx, self.ports.len ());
|
2021-10-10 19:55:52 +00:00
|
|
|
ports_col.add (&gui.row);
|
2022-05-26 01:19:54 +00:00
|
|
|
ports_col.set_size (&gui.row, 30);
|
2021-10-10 19:55:52 +00:00
|
|
|
|
|
|
|
let port = Port {
|
|
|
|
gui,
|
|
|
|
forwarding_instance: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.ports.push (port);
|
2021-10-10 23:34:50 +00:00
|
|
|
|
2021-10-19 16:19:01 +00:00
|
|
|
if self.ports.len () >= MAX_PORTS {
|
2021-10-10 23:34:50 +00:00
|
|
|
self.but_add_port.deactivate ();
|
|
|
|
}
|
2021-10-10 19:55:52 +00:00
|
|
|
}
|
2021-10-10 17:56:13 +00:00
|
|
|
}
|
|
|
|
|
2021-07-19 00:01:46 +00:00
|
|
|
fn main () -> anyhow::Result <()> {
|
|
|
|
tracing_subscriber::fmt::init ();
|
2021-10-10 17:56:13 +00:00
|
|
|
let rt = Runtime::new ()?;
|
2021-07-19 00:01:46 +00:00
|
|
|
|
2021-07-19 00:25:25 +00:00
|
|
|
let opt = Opt::from_args ();
|
|
|
|
|
2021-07-19 00:01:46 +00:00
|
|
|
let (fltk_tx, fltk_rx) = app::channel::<Message> ();
|
|
|
|
|
2021-07-19 02:28:18 +00:00
|
|
|
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);
|
2021-10-10 19:44:45 +00:00
|
|
|
wind.make_resizable (true);
|
2021-07-19 00:01:46 +00:00
|
|
|
|
2021-10-10 19:37:58 +00:00
|
|
|
let mut col = Flex::default ().column ().size_of_parent ();
|
2021-07-19 00:01:46 +00:00
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
let frame_status = Frame::default ();
|
|
|
|
col.set_size (&frame_status, 30);
|
2021-07-19 00:01:46 +00:00
|
|
|
|
|
|
|
{
|
2021-10-10 19:37:58 +00:00
|
|
|
let mut row = Flex::default ().row ();
|
2021-07-19 00:01:46 +00:00
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
let l = Frame::default ().with_label ("Server ID");
|
|
|
|
row.set_size (&l, 120);
|
|
|
|
let l = Frame::default ().with_label ("Server port");
|
|
|
|
row.set_size (&l, 80);
|
|
|
|
let l = Frame::default ().with_label ("Local port");
|
|
|
|
row.set_size (&l, 80);
|
2021-10-10 19:37:58 +00:00
|
|
|
row.end ();
|
2021-07-19 00:01:46 +00:00
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
col.set_size (&row, 30);
|
2021-07-19 00:01:46 +00:00
|
|
|
}
|
|
|
|
|
2021-10-10 19:51:25 +00:00
|
|
|
let mut ports_col = Flex::default ().column ();
|
|
|
|
ports_col.end ();
|
|
|
|
|
2021-10-10 19:44:45 +00:00
|
|
|
let mut but_add_port = Button::default ().with_label ("+");
|
2021-10-10 19:51:25 +00:00
|
|
|
but_add_port.set_trigger (CallbackTrigger::Release);
|
|
|
|
but_add_port.emit (fltk_tx, Message::AddPort);
|
2022-05-26 01:19:54 +00:00
|
|
|
col.set_size (&but_add_port, 30);
|
2021-10-10 19:44:45 +00:00
|
|
|
|
2021-10-10 19:37:58 +00:00
|
|
|
col.end ();
|
2021-07-19 02:11:13 +00:00
|
|
|
|
2021-10-10 23:34:50 +00:00
|
|
|
let relay_addr = opt.relay_addr.as_ref ()
|
|
|
|
.map (|s| &s[..])
|
|
|
|
.unwrap_or ("127.0.0.1:30380")
|
|
|
|
.parse ()
|
|
|
|
.context ("relay_addr should be like 127.0.0.1:30380")?;
|
|
|
|
|
2021-10-10 17:56:13 +00:00
|
|
|
let mut gui_client = GuiClient {
|
|
|
|
rt: &rt,
|
|
|
|
frame_status,
|
2021-10-10 19:55:52 +00:00
|
|
|
ports: Default::default (),
|
2021-10-10 23:34:50 +00:00
|
|
|
but_add_port,
|
2021-10-10 17:56:13 +00:00
|
|
|
};
|
|
|
|
|
2021-10-10 19:55:52 +00:00
|
|
|
gui_client.add_port (&mut ports_col, fltk_tx);
|
|
|
|
ports_col.recalc ();
|
|
|
|
|
2021-10-10 19:44:45 +00:00
|
|
|
gui_client.sync_status ();
|
|
|
|
|
2021-07-19 00:01:46 +00:00
|
|
|
wind.end ();
|
|
|
|
wind.show ();
|
|
|
|
|
2021-07-19 00:25:25 +00:00
|
|
|
let connection_p2_p3 = rt.block_on (async move {
|
2021-10-01 17:19:23 +00:00
|
|
|
let server_cert = match opt.cert_url.as_ref () {
|
|
|
|
Some (url) => reqwest::get (url).await?.bytes ().await?,
|
2021-10-19 16:11:12 +00:00
|
|
|
None => tokio::fs::read ("ptth_quic_output/quic_server.crt").await.context ("can't read quic_server.crt from disk")?.into (),
|
2021-10-01 17:19:23 +00:00
|
|
|
};
|
|
|
|
|
2021-07-19 00:25:25 +00:00
|
|
|
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,
|
|
|
|
..
|
2021-10-01 17:19:23 +00:00
|
|
|
} = protocol::p2_connect_to_p3 (&endpoint, &relay_addr, &client_id).await
|
|
|
|
.context ("P2 can't connect to P3")?;
|
2021-07-19 00:25:25 +00:00
|
|
|
|
|
|
|
Ok::<_, anyhow::Error> (connection)
|
|
|
|
})?;
|
2021-07-19 00:01:46 +00:00
|
|
|
|
|
|
|
while app.wait () {
|
|
|
|
match fltk_rx.recv () {
|
2021-07-19 02:37:09 +00:00
|
|
|
Some (Message::OpenPort (port_idx)) => {
|
2021-10-10 19:39:50 +00:00
|
|
|
if let Err (e) = gui_client.open_port (connection_p2_p3.clone (), port_idx)
|
|
|
|
{
|
|
|
|
error! ("{:?}", e);
|
|
|
|
}
|
2021-07-19 00:01:46 +00:00
|
|
|
},
|
2021-07-19 02:37:09 +00:00
|
|
|
Some (Message::ClosePort (port_idx)) => {
|
2021-10-10 17:56:13 +00:00
|
|
|
gui_client.close_port (port_idx)?;
|
2021-07-19 00:01:46 +00:00
|
|
|
},
|
2021-10-10 19:51:25 +00:00
|
|
|
Some (Message::AddPort) => {
|
2021-10-10 19:55:52 +00:00
|
|
|
gui_client.add_port (&mut ports_col, fltk_tx);
|
2021-10-10 19:51:25 +00:00
|
|
|
ports_col.recalc ();
|
|
|
|
ports_col.redraw ();
|
|
|
|
},
|
2021-07-19 00:01:46 +00:00
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok (())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_active <W: WidgetExt> (w: &mut W, b: bool) {
|
|
|
|
if b {
|
|
|
|
w.activate ();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
w.deactivate ();
|
|
|
|
}
|
|
|
|
}
|
2021-07-19 00:25:25 +00:00
|
|
|
|
2021-07-19 02:11:13 +00:00
|
|
|
impl GuiPort {
|
2021-10-10 19:37:58 +00:00
|
|
|
fn new (fltk_tx: fltk::app::Sender <Message>, port_idx: usize) -> Self {
|
|
|
|
let mut row = Flex::default ().row ();
|
2021-07-19 02:28:18 +00:00
|
|
|
|
2021-10-10 19:37:58 +00:00
|
|
|
let mut input_server_id = Input::default ();
|
|
|
|
let mut input_server_port = Input::default ();
|
2021-10-10 20:42:17 +00:00
|
|
|
let mut input_client_port = Input::default ();
|
2021-10-10 19:37:58 +00:00
|
|
|
let mut but_open = Button::default ().with_label ("Open");
|
|
|
|
let mut but_close = Button::default ().with_label ("Close");
|
2021-07-19 02:28:18 +00:00
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
row.set_size (&input_server_id, 120);
|
|
|
|
row.set_size (&input_server_port, 80);
|
|
|
|
row.set_size (&input_client_port, 80);
|
|
|
|
row.set_size (&but_open, 80);
|
|
|
|
row.set_size (&but_close, 80);
|
2021-07-19 02:28:18 +00:00
|
|
|
|
2021-10-10 21:20:35 +00:00
|
|
|
input_client_port.set_value ("");
|
|
|
|
input_client_port.set_readonly (true);
|
2021-07-19 02:28:18 +00:00
|
|
|
input_server_id.set_value ("bogus_server");
|
|
|
|
input_server_port.set_value ("5900");
|
|
|
|
|
2021-10-10 18:17:50 +00:00
|
|
|
but_open.set_trigger (CallbackTrigger::Release);
|
2021-07-19 02:37:09 +00:00
|
|
|
but_open.emit (fltk_tx, Message::OpenPort (port_idx));
|
2021-10-10 18:17:50 +00:00
|
|
|
but_close.set_trigger (CallbackTrigger::Release);
|
2021-07-19 02:37:09 +00:00
|
|
|
but_close.emit (fltk_tx, Message::ClosePort (port_idx));
|
2021-07-19 02:28:18 +00:00
|
|
|
|
2021-10-10 19:37:58 +00:00
|
|
|
row.end ();
|
|
|
|
|
2021-10-10 21:20:35 +00:00
|
|
|
let mut output = Self {
|
2021-10-10 19:37:58 +00:00
|
|
|
row,
|
2021-07-19 02:28:18 +00:00
|
|
|
input_client_port,
|
|
|
|
input_server_id,
|
|
|
|
input_server_port,
|
|
|
|
but_open,
|
|
|
|
but_close,
|
2021-10-10 21:20:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
output.set_forwarding (false);
|
|
|
|
|
|
|
|
output
|
2021-07-19 02:28:18 +00:00
|
|
|
}
|
|
|
|
|
2021-07-19 02:11:13 +00:00
|
|
|
fn get_params (&self) -> anyhow::Result <ForwardingParams>
|
|
|
|
{
|
|
|
|
let server_tcp_port = u16::from_str (&self.input_server_port.value ())?;
|
2021-10-10 21:20:35 +00:00
|
|
|
let server_id = self.input_server_id.value ();
|
|
|
|
|
|
|
|
let client_tcp_port = PortInfo {
|
|
|
|
server_id: &server_id,
|
|
|
|
server_tcp_port,
|
|
|
|
}.random_eph_port ();
|
2021-07-19 02:11:13 +00:00
|
|
|
|
|
|
|
Ok (ForwardingParams {
|
|
|
|
client_tcp_port,
|
|
|
|
server_id,
|
|
|
|
server_tcp_port,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_forwarding (&mut self, x: bool) {
|
2021-10-10 21:20:35 +00:00
|
|
|
set_active (&mut self.input_client_port, x);
|
2021-07-19 02:11:13 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2021-10-10 21:20:35 +00:00
|
|
|
|
|
|
|
// 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> {
|
2021-10-10 23:34:50 +00:00
|
|
|
// relay_addr: SocketAddr,
|
2021-10-10 21:20:35 +00:00
|
|
|
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);
|
|
|
|
|
2022-05-26 01:19:54 +00:00
|
|
|
for (input, expected) in [
|
2021-10-10 23:34:50 +00:00
|
|
|
(("127.0.0.1:4000", "bogus_server", 22), 51168),
|
2021-10-10 23:53:21 +00:00
|
|
|
// The relay address is explicitly excluded from the eph port
|
|
|
|
// computation in case I want to support connecting to a server
|
|
|
|
// across multiple relays
|
2021-10-10 23:34:50 +00:00
|
|
|
(("127.0.0.1:30380", "bogus_server", 22), 51168),
|
|
|
|
(("127.0.0.1:4000", "real_server", 22), 53873),
|
|
|
|
(("127.0.0.1:4000", "bogus_server", 5900), 53844),
|
2021-10-10 21:20:35 +00:00
|
|
|
] {
|
2021-10-10 23:34:50 +00:00
|
|
|
let (_relay_addr, server_id, server_tcp_port) = input;
|
2021-10-10 21:20:35 +00:00
|
|
|
let input = PortInfo {
|
|
|
|
server_id,
|
|
|
|
server_tcp_port,
|
|
|
|
};
|
|
|
|
let actual = input.random_eph_port ();
|
|
|
|
assert_eq! (expected, actual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|