♻️ 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".
## 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
itself with a 4-byte request. The bytes are:
Requests look like this:
1. Client type
2. Client ID
1. Command type
2. Extra data, or unused, depending on command type
3. Unused
4. Unused
The type is "2" or "4" to identify the client as a P2 or a P4. The IDs are
hard-coded as "42" and "43" for now.
Responses look like this:
P3 responds with these 4 bytes:
1. Status code
2. Unused
1. Status code (Always "20" for now, meaning "OK")
2. Command type of the request that we're responding to
3. Unused
4. Unused
The status code is always "20" to mean "OK". If authorization or authentication
had failed, it would be something else, and P3 would close the connection.
The command types are:
## PTTH connection
When P1 connects to P2's TCP listener, P2 opens a bi stream to P3 and sends
this request:
1. "1" to mean "Open a PTTH connection"
2. Server ID, hard-coded as "43" for the only P4 server instance we have
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.
| Type | Sender | Receiver | Meaning |
| ---- | ------ | ----------- | ------------------------------------------------- |
| 2 | P2 | P3 | Client proxy wants to connect to the relay |
| 4 | P4 | P3 | Server proxy wants to connect to the relay |
| 10 | P2 | P3 | Client wants relay to connect it to a server |
| 11 | P3 | P4 | Relay tells server that a client wants to connect |
| 12 | P2 | P4 (via P3) | Client wants a port forwarded from the server |
# Plan

View File

@ -21,11 +21,24 @@ async fn main () -> anyhow::Result <()> {
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?;
let mut resp_buf = [0u8, 0, 0, 0];
let mut resp_buf = [0, 0, 0, 0];
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?;
@ -44,23 +57,42 @@ async fn main () -> anyhow::Result <()> {
// 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?;
let mut resp_buf = [0; 4];
relay_recv.read_exact (&mut resp_buf).await?;
debug! ("Status code from P3: {}", resp_buf [0]);
assert_eq! (resp_buf, [
Command::OKAY.0,
Command::CONNECT_P2_TO_P4.0,
0,
0,
]);
// 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?;
let mut resp_buf = [0; 4];
relay_recv.read_exact (&mut resp_buf).await?;
debug! ("Status code from P4: {}", resp_buf [0]);
assert_eq! (resp_buf, [
Command::OKAY.0,
Command::CONNECT_P2_TO_P5.0,
0,
0,
]);
trace! ("Relaying bytes...");
@ -116,7 +148,7 @@ impl PtthNewConnection {
relay_send.write_all (buf_slice).await?;
}
debug! ("Uplink closed");
trace! ("Uplink closed");
Ok::<_, anyhow::Error> (())
});
@ -131,7 +163,7 @@ impl PtthNewConnection {
local_send.write_all (buf_slice).await?;
}
debug! ("Downlink closed");
trace! ("Downlink closed");
Ok::<_, anyhow::Error> (())
});

View File

@ -20,11 +20,23 @@ async fn main () -> anyhow::Result <()> {
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?;
let mut resp_buf = [0u8, 0, 0, 0];
let mut resp_buf = [0, 0, 0, 0];
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");
@ -34,18 +46,37 @@ async fn main () -> anyhow::Result <()> {
tokio::spawn (async move {
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_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");
let stream = TcpStream::connect ("127.0.0.1:30382").await?;
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...");
let ptth_conn = PtthNewConnection {
@ -94,7 +125,7 @@ impl PtthNewConnection {
local_send.write_all (buf_slice).await?;
}
debug! ("Downlink closed");
trace! ("Downlink closed");
Ok::<_, anyhow::Error> (())
});
@ -113,7 +144,7 @@ impl PtthNewConnection {
relay_send.write_all (buf_slice).await?;
}
debug! ("Uplink closed");
trace! ("Uplink closed");
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 req_buf = [0u8; 4];
let mut req_buf = [0, 0, 0, 0];
recv.read_exact (&mut req_buf).await?;
let peer_type = req_buf [0];
@ -170,7 +170,12 @@ async fn handle_quic_connection (
_ => 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?;
match peer_type {
@ -205,12 +210,12 @@ async fn handle_p2_connection (
tokio::spawn (async move {
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?;
let cmd_type = req_buf [0];
match cmd_type {
1 => {
match Command (cmd_type) {
Command::CONNECT_P2_TO_P4 => {
let server_id = req_buf [1];
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
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?;
{
@ -294,16 +304,22 @@ async fn handle_p4_connection (
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?;
let mut resp_buf = [0u8, 0, 0, 0];
let mut resp_buf = [0, 0, 0, 0];
server_recv.read_exact (&mut resp_buf).await?;
let status_code = resp_buf [0];
if status_code != 20 {
bail! ("P4 rejected request from {}", client_id);
}
assert_eq! (resp_buf, [
Command::OKAY.0,
Command::CONNECT_P2_TO_P4_STEP_2.0,
0,
0,
]);
trace! ("Relaying bytes...");

View File

@ -32,3 +32,15 @@ pub use tracing::{
};
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);
}