diff --git a/libsrt/src/tunnel.rs b/libsrt/src/tunnel.rs index 4c5f472..9ba9843 100644 --- a/libsrt/src/tunnel.rs +++ b/libsrt/src/tunnel.rs @@ -1,12 +1,92 @@ +use std::error::Error; use std::net::SocketAddr; use rand::rngs::OsRng; use x25519_dalek::{EphemeralSecret, PublicKey}; use crate::cryptography::HASH; +use crate::packets::clientbound::{ChannelInUsePacket, ChannelNotReadyPacket, ChannelReadyPacket}; +use crate::packets::initbound::HandshakeStartPacket; +use crate::packets::recvbound::{EncryptedDataPacket, HandshakeResponsePacket}; +use crate::packets::relaybound::RelayDataPacket; +use crate::packets::SRTPacket; + +// so we should have a method to handle each of the different types of packets + +// lets plan out the state machine? + +// State = NotConnected +// ... => Ignore // we should not be receiving packets if we are not connected to the relay +// => State = Connected + +// State = Connected +// ... => Ignore // even if we are connected to the relay, we havent sent init yet so shouldnt be recv anything +// => State = SentChannelWait + +// State = SentChannelWait +// ChannelReady(init) => State = TunnelSendInit +// ChannelReady(recv) => State = TunnelWaitForInit +// ChannelNotReady => Wait for ChannelReady +// ... => Ignore + +// State = TunnelSentInit +// ChannelNotReady => State = Connected // receiving a ChannelNotReady while a tunnel is active indicates the other side disconnected +// ... => Ignore +// => State = TunnelWaitForResp + +// State = TunnelWaitForInit +// HandshakeStart => State = TunnelSendHandshakeResponse +// ChannelNotReady => State = Connected +// ... => Ignore + +// State = TunnelWaitForResp +// HandshakeResponse => State = Active +// ChannelNotReady => State = Connected +// ... => Ignore + +// State => TunnelSendHandshakeResponse +// ChannelNotReady => State = Connected +// ... => Ignore +// => State = Active + +// State => Active +// EncryptedData => +// ChannelNotReady => State = Connected +// ... => Ignore + +// something like this? +// i think this covers every possible situation that the system could be in +// i cannot think of others so good enough +pub enum SPTunnelState { + TunnelNeedToSendChannelWait, + TunnelInUse, + TunnelSentChannelWaitWaitingForPartner, + + TunnelNeedToSendInit, + TunnelSentInit, + TunnelWaitForResp, + + TunnelWaitForInit, + TunnelNeedToSendHandshakeResponse, + TunnelSentHandshakeResponse, + + Active +} + +// keeping this for reference + +// so i guess we should go through each of these and make a function that implements that logic +// we should make a function to handle every packet type we could receive, and then match the current state to figure out what to do +// (arbitrary decision) match type then handle based on state, or check state then analyse packet? + +// like, say the recv loop parses a RelayData packet +// it would then call .recv_relay_data() on the SPTunnel, which will decide what to do with that packet based on the current internal state of the tunnel +// ok, so check pkt type first +// then we need one big switch-case +// each packet type gets its own function; parsing it from bytes into the struct form is done in the receive loop +// so we should make .recv_relay_data(), .recv_encrypted_data(), etc +// right /// The main tunnel state machine container pub struct SPTunnel { - relay: SocketAddr, - channel_name: String, pmk: [u8; 32], cid: [u8; 32], @@ -14,10 +94,13 @@ pub struct SPTunnel { ee: PublicKey, eer: PublicKey, - state: SPTunnelState + state: SPTunnelState, + + we_are_initializer: bool } impl SPTunnel { - fn new(relay: SocketAddr, channel_name: String) -> Self { + fn new(channel_name: String) -> Self { + let state = SPTunnelState::TunnelNeedToSendChannelWait; // calculate the pmk and cid (various hashes of the channel name) let pmk = HASH(&channel_name); let cid = HASH(&pmk); @@ -27,18 +110,66 @@ impl SPTunnel { let ee = PublicKey::from(&e); SPTunnel { - relay, - channel_name, pmk, cid, e, ee, + we_are_initializer: false, eer: PublicKey::from([0u8; 32]), state: SPTunnelState::NotConnected, } } + + /** FROM-RELAY Packets (us <- relay) **/ + fn recv_channel_not_ready(&mut self, pkt: &ChannelNotReadyPacket) { + if pkt.channel != self.cid { + return; // ignore this packet, it isnt for us + } + + // regenerate eph keys + self.e = EphemeralSecret::new(OsRng); + self.ee = PublicKey::from(&self.e); + + self.state = SPTunnelState::TunnelSentChannelWaitWaitingForPartner; + } + fn recv_channel_ready(&mut self, pkt: &ChannelReadyPacket) { + if pkt.channel != self.cid { + return; // ignore this packet, it isnt for us + } + if pkt.you_are_initializer { + self.we_are_initializer = true; + self.state = SPTunnelState::TunnelNeedToSendInit; + } else { + self.we_are_initializer = false; + self.state = SPTunnelState::TunnelWaitForInit; + } + } + fn recv_channel_in_use(&mut self, pkt: &ChannelInUsePacket) { + if pkt.channel != self.cid { + return; // ignore this packet, it isnt for us + } + // we cant use this channel right now; set our state to reflect that + self.state = SPTunnelState::TunnelInUse; + } + fn recv_channel_relay_data(&mut self, pkt: &RelayDataPacket) { + if pkt.channel != self.cid { + return; + } + // we need to deencapsulate the packet and hand it BACK to the packet parse loop to be sent back to us and parse + + } + + /** VIA-RELAY Packets (us <--relay--< them) **/ + fn recv_handshake_start(&mut self, pkt: &HandshakeStartPacket) { + } + fn recv_handshake_response(&mut self, pkt: &HandshakeResponsePacket) { + } + fn recv_encrypted_data(&mut self, pkt: &EncryptedDataPacket) { + } + + // State = Connected +// ... => Ignore // even if we are connected to the relay, we havent sent init yet so shouldnt be recv anything +// => State = SentChannelWait + } -pub enum SPTunnelState { - NotConnected -} \ No newline at end of file