♻️ refactor: change up the protocol so that everything has a distinct 4-byte message

main
_ 2021-07-17 23:14:50 +00:00
parent d3675473ed
commit 57091ddaab
5 changed files with 138 additions and 84 deletions

View File

@ -29,68 +29,31 @@ start of bidirectional streams.
Unused bytes are always "0". Unused bytes are always "0".
## QUIC client connection Messages are sent in request-response pairs, like HTTP.
When P2 or P4 connects to P3 over QUIC, it opens a bi stream and identifies Requests look like this:
itself with a 4-byte request. The bytes are:
1. Client type 1. Command type
2. Client ID 2. Extra data, or unused, depending on command type
3. Unused 3. Unused
4. Unused 4. Unused
The type is "2" or "4" to identify the client as a P2 or a P4. The IDs are Responses look like this:
hard-coded as "42" and "43" for now.
P3 responds with these 4 bytes: 1. Status code (Always "20" for now, meaning "OK")
2. Command type of the request that we're responding to
1. Status code
2. Unused
3. Unused 3. Unused
4. Unused 4. Unused
The status code is always "20" to mean "OK". If authorization or authentication The command types are:
had failed, it would be something else, and P3 would close the connection.
## PTTH connection | Type | Sender | Receiver | Meaning |
| ---- | ------ | ----------- | ------------------------------------------------- |
When P1 connects to P2's TCP listener, P2 opens a bi stream to P3 and sends | 2 | P2 | P3 | Client proxy wants to connect to the relay |
this request: | 4 | P4 | P3 | Server proxy wants to connect to the relay |
| 10 | P2 | P3 | Client wants relay to connect it to a server |
1. "1" to mean "Open a PTTH connection" | 11 | P3 | P4 | Relay tells server that a client wants to connect |
2. Server ID, hard-coded as "43" for the only P4 server instance we have | 12 | P2 | P4 (via P3) | Client wants a port forwarded from the server |
3. Unused
4. Unused
The first 4 bytes are interpreted by P3. If everything is okay, P3 responds
with:
1. "20" to mean "OK".
2. Unused
3. Unused
4. Unused
Then P3 opens a bi stream to P4 and begins relaying from P2 to P4.
P2 reads that response and sends another request, which will be forwarded
to P4:
1. "1" to mean "Open a PTTH connection"
2. Unused (Reserved for choosing port)
3. Unused
4. Unused
P4 parses the last 4 bytes. If everything is okay, P4 (via P3) responds to P2 with:
1. "20" to mean "OK".
2. Unused
3. Unused
4. Unused
And then P4 begins relaying between P2 and P5.
P2 reads the 4 byte response from P3 and the 4 byte response from P4.
If everything is okay, it completes the relaying from P1 to P5.
# Plan # Plan

View File

@ -21,11 +21,24 @@ async fn main () -> anyhow::Result <()> {
let (mut send, mut recv) = connection.open_bi ().await?; let (mut send, mut recv) = connection.open_bi ().await?;
let req_buf = [2u8, 42, 0, 0]; let our_id = 42;
let req_buf = [
Command::CONNECT_P2_TO_P3.0,
our_id,
0,
0,
];
send.write_all (&req_buf).await?; send.write_all (&req_buf).await?;
let mut resp_buf = [0u8, 0, 0, 0]; let mut resp_buf = [0, 0, 0, 0];
recv.read_exact (&mut resp_buf).await?; recv.read_exact (&mut resp_buf).await?;
assert_eq! (resp_buf, [
Command::OKAY.0,
Command::CONNECT_P2_TO_P3.0,
0,
0,
]);
let listener = TcpListener::bind ("127.0.0.1:30381").await?; let listener = TcpListener::bind ("127.0.0.1:30381").await?;
@ -44,23 +57,42 @@ async fn main () -> anyhow::Result <()> {
// Ask P3 if we can connect to P4 // Ask P3 if we can connect to P4
let req_buf = [1, 43, 0, 0]; let server_id = 43;
let req_buf = [
Command::CONNECT_P2_TO_P4.0,
server_id,
0,
0,
];
relay_send.write_all (&req_buf).await?; relay_send.write_all (&req_buf).await?;
let mut resp_buf = [0; 4]; let mut resp_buf = [0; 4];
relay_recv.read_exact (&mut resp_buf).await?; relay_recv.read_exact (&mut resp_buf).await?;
assert_eq! (resp_buf, [
debug! ("Status code from P3: {}", resp_buf [0]); Command::OKAY.0,
Command::CONNECT_P2_TO_P4.0,
0,
0,
]);
// Ask P4 if we can connect to P5 // Ask P4 if we can connect to P5
let req_buf = [1, 0, 0, 0]; let req_buf = [
Command::CONNECT_P2_TO_P5.0,
0,
0,
0,
];
relay_send.write_all (&req_buf).await?; relay_send.write_all (&req_buf).await?;
let mut resp_buf = [0; 4]; let mut resp_buf = [0; 4];
relay_recv.read_exact (&mut resp_buf).await?; relay_recv.read_exact (&mut resp_buf).await?;
assert_eq! (resp_buf, [
debug! ("Status code from P4: {}", resp_buf [0]); Command::OKAY.0,
Command::CONNECT_P2_TO_P5.0,
0,
0,
]);
trace! ("Relaying bytes..."); trace! ("Relaying bytes...");
@ -116,7 +148,7 @@ impl PtthNewConnection {
relay_send.write_all (buf_slice).await?; relay_send.write_all (buf_slice).await?;
} }
debug! ("Uplink closed"); trace! ("Uplink closed");
Ok::<_, anyhow::Error> (()) Ok::<_, anyhow::Error> (())
}); });
@ -131,7 +163,7 @@ impl PtthNewConnection {
local_send.write_all (buf_slice).await?; local_send.write_all (buf_slice).await?;
} }
debug! ("Downlink closed"); trace! ("Downlink closed");
Ok::<_, anyhow::Error> (()) Ok::<_, anyhow::Error> (())
}); });

View File

@ -20,11 +20,23 @@ async fn main () -> anyhow::Result <()> {
let (mut send, mut recv) = connection.open_bi ().await?; let (mut send, mut recv) = connection.open_bi ().await?;
let req_buf = [4u8, 43, 0, 0]; let our_id = 43;
let req_buf = [
Command::CONNECT_P4_TO_P3.0,
our_id,
0,
0,
];
send.write_all (&req_buf).await?; send.write_all (&req_buf).await?;
let mut resp_buf = [0u8, 0, 0, 0]; let mut resp_buf = [0, 0, 0, 0];
recv.read_exact (&mut resp_buf).await?; recv.read_exact (&mut resp_buf).await?;
assert_eq! (resp_buf, [
Command::OKAY.0,
Command::CONNECT_P4_TO_P3.0,
0,
0,
]);
trace! ("Accepting bi streams from P3"); trace! ("Accepting bi streams from P3");
@ -34,18 +46,37 @@ async fn main () -> anyhow::Result <()> {
tokio::spawn (async move { tokio::spawn (async move {
let mut req_buf = [0, 0, 0, 0]; let mut req_buf = [0, 0, 0, 0];
relay_recv.read_exact (&mut req_buf).await?; relay_recv.read_exact (&mut req_buf).await?;
assert_eq! (req_buf [0], Command::CONNECT_P2_TO_P4_STEP_2.0);
// TODO: Auth stuff // TODO: Authorize P2 to connect to us
let resp_buf = [
Command::OKAY.0,
Command::CONNECT_P2_TO_P4_STEP_2.0,
0,
0,
];
relay_send.write_all (&resp_buf).await?;
let mut req_buf = [0, 0, 0, 0];
relay_recv.read_exact (&mut req_buf).await?;
assert_eq! (req_buf [0], Command::CONNECT_P2_TO_P5.0);
// TODO: Authorize P2 to connect to P5
let resp_buf = [
Command::OKAY.0,
Command::CONNECT_P2_TO_P5.0,
0,
0,
];
relay_send.write_all (&resp_buf).await?;
debug! ("Started PTTH connection"); debug! ("Started PTTH connection");
let stream = TcpStream::connect ("127.0.0.1:30382").await?; let stream = TcpStream::connect ("127.0.0.1:30382").await?;
let (local_recv, local_send) = stream.into_split (); let (local_recv, local_send) = stream.into_split ();
let resp_buf = [20, 0, 0, 0];
relay_send.write_all (&resp_buf).await?;
relay_send.write_all (&resp_buf).await?;
trace! ("Relaying bytes..."); trace! ("Relaying bytes...");
let ptth_conn = PtthNewConnection { let ptth_conn = PtthNewConnection {
@ -94,7 +125,7 @@ impl PtthNewConnection {
local_send.write_all (buf_slice).await?; local_send.write_all (buf_slice).await?;
} }
debug! ("Downlink closed"); trace! ("Downlink closed");
Ok::<_, anyhow::Error> (()) Ok::<_, anyhow::Error> (())
}); });
@ -113,7 +144,7 @@ impl PtthNewConnection {
relay_send.write_all (buf_slice).await?; relay_send.write_all (buf_slice).await?;
} }
debug! ("Uplink closed"); trace! ("Uplink closed");
Ok::<_, anyhow::Error> (()) Ok::<_, anyhow::Error> (())
}); });

View File

@ -158,7 +158,7 @@ async fn handle_quic_connection (
let (mut send, mut recv) = conn.bi_streams.next ().await.ok_or_else (|| anyhow::anyhow! ("QUIC client didn't identify itself"))??; let (mut send, mut recv) = conn.bi_streams.next ().await.ok_or_else (|| anyhow::anyhow! ("QUIC client didn't identify itself"))??;
let mut req_buf = [0u8; 4]; let mut req_buf = [0, 0, 0, 0];
recv.read_exact (&mut req_buf).await?; recv.read_exact (&mut req_buf).await?;
let peer_type = req_buf [0]; let peer_type = req_buf [0];
@ -170,7 +170,12 @@ async fn handle_quic_connection (
_ => bail! ("Unknown QUIC client type"), _ => bail! ("Unknown QUIC client type"),
} }
let resp_buf = [20u8, 0, 0, 0]; let resp_buf = [
Command::OKAY.0,
peer_type,
0,
0,
];
send.write_all (&resp_buf).await?; send.write_all (&resp_buf).await?;
match peer_type { match peer_type {
@ -205,12 +210,12 @@ async fn handle_p2_connection (
tokio::spawn (async move { tokio::spawn (async move {
debug! ("Request started for P2"); debug! ("Request started for P2");
let mut req_buf = [0u8; 4]; let mut req_buf = [0, 0, 0, 0];
client_recv.read_exact (&mut req_buf).await?; client_recv.read_exact (&mut req_buf).await?;
let cmd_type = req_buf [0]; let cmd_type = req_buf [0];
match cmd_type { match Command (cmd_type) {
1 => { Command::CONNECT_P2_TO_P4 => {
let server_id = req_buf [1]; let server_id = req_buf [1];
handle_request_p2_to_p4 (relay_state, client_id, server_id, client_send, client_recv).await?; handle_request_p2_to_p4 (relay_state, client_id, server_id, client_send, client_recv).await?;
}, },
@ -239,7 +244,12 @@ async fn handle_request_p2_to_p4 (
// TODO: Auth checks // TODO: Auth checks
let resp_buf = [20, 0, 0, 0]; let resp_buf = [
Command::OKAY.0,
Command::CONNECT_P2_TO_P4.0,
0,
0,
];
client_send.write_all (&resp_buf).await?; client_send.write_all (&resp_buf).await?;
{ {
@ -294,16 +304,22 @@ async fn handle_p4_connection (
let (mut server_send, mut server_recv) = connection.open_bi ().await?; let (mut server_send, mut server_recv) = connection.open_bi ().await?;
let req_buf = [2u8, client_id, 0, 0]; let req_buf = [
Command::CONNECT_P2_TO_P4_STEP_2.0,
client_id,
0,
0,
];
server_send.write_all (&req_buf).await?; server_send.write_all (&req_buf).await?;
let mut resp_buf = [0u8, 0, 0, 0]; let mut resp_buf = [0, 0, 0, 0];
server_recv.read_exact (&mut resp_buf).await?; server_recv.read_exact (&mut resp_buf).await?;
assert_eq! (resp_buf, [
let status_code = resp_buf [0]; Command::OKAY.0,
if status_code != 20 { Command::CONNECT_P2_TO_P4_STEP_2.0,
bail! ("P4 rejected request from {}", client_id); 0,
} 0,
]);
trace! ("Relaying bytes..."); trace! ("Relaying bytes...");

View File

@ -32,3 +32,15 @@ pub use tracing::{
}; };
pub use crate::quinn_utils::*; pub use crate::quinn_utils::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Command (pub u8);
impl Command {
pub const CONNECT_P2_TO_P3: Command = Command (2);
pub const CONNECT_P4_TO_P3: Command = Command (4);
pub const CONNECT_P2_TO_P4: Command = Command (10);
pub const CONNECT_P2_TO_P4_STEP_2: Command = Command (11);
pub const CONNECT_P2_TO_P5: Command = Command (12);
pub const OKAY: Command = Command (20);
}