From eae7f316d4efaabebefd62d9e0f0b5453a93dd99 Mon Sep 17 00:00:00 2001 From: Tipragot Date: Tue, 13 Feb 2024 00:48:06 +0100 Subject: [PATCH] Remove last bevnet system and prepare for the relay system --- Cargo.lock | 211 +--------------------- crates/bevnet/Cargo.toml | 5 +- crates/bevnet/src/lib.rs | 368 --------------------------------------- 3 files changed, 10 insertions(+), 574 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81a3594..8b7bace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,41 +86,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "ahash" version = "0.8.7" @@ -368,18 +333,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" -dependencies = [ - "http 0.2.11", - "log", - "url", - "wildmatch", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -397,7 +350,7 @@ dependencies = [ "base64 0.21.7", "bytes", "futures-util", - "http 1.0.0", + "http", "http-body", "http-body-util", "hyper", @@ -432,7 +385,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.0.0", + "http", "http-body", "http-body-util", "mime", @@ -475,10 +428,7 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" name = "bevnet" version = "0.2.0" dependencies = [ - "aes-gcm", - "base64 0.21.7", - "igd", - "local-ip-address", + "relay-client", ] [[package]] @@ -1429,16 +1379,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.7.0" @@ -1673,19 +1613,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - [[package]] name = "d3d12" version = "0.7.0" @@ -2174,16 +2104,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gilrs" version = "0.10.4" @@ -2378,7 +2298,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", + "http", "indexmap 2.2.2", "slab", "tokio", @@ -2449,17 +2369,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.0.0" @@ -2478,7 +2387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http", ] [[package]] @@ -2489,7 +2398,7 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http 1.0.0", + "http", "http-body", "pin-project-lite", ] @@ -2516,7 +2425,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http 1.0.0", + "http", "http-body", "httparse", "httpdate", @@ -2533,7 +2442,7 @@ checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-util", - "http 1.0.0", + "http", "http-body", "hyper", "pin-project-lite", @@ -2551,19 +2460,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "igd" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556b5a75cd4adb7c4ea21c64af1c48cefb2ce7d43dc4352c720a1fe47c21f355" -dependencies = [ - "attohttpc", - "log", - "rand", - "url", - "xmltree", -] - [[package]] name = "image" version = "0.24.8" @@ -2624,15 +2520,6 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - [[package]] name = "instant" version = "0.1.12" @@ -2834,18 +2721,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "local-ip-address" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612ed4ea9ce5acfb5d26339302528a5e1e59dfed95e9e11af3c083236ff1d15d" -dependencies = [ - "libc", - "neli", - "thiserror", - "windows-sys 0.48.0", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -3029,31 +2904,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "neli" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43" -dependencies = [ - "byteorder", - "libc", - "log", - "neli-proc-macros", -] - -[[package]] -name = "neli-proc-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" -dependencies = [ - "either", - "proc-macro2", - "quote", - "serde", - "syn 1.0.109", -] - [[package]] name = "nix" version = "0.24.3" @@ -3313,12 +3163,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl-probe" version = "0.1.5" @@ -3487,18 +3331,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "pp-rs" version = "0.2.1" @@ -4418,7 +4250,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.0.0", + "http", "httparse", "log", "rand", @@ -4480,16 +4312,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "untrusted" version = "0.9.0" @@ -4778,12 +4600,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" -[[package]] -name = "wildmatch" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" - [[package]] name = "winapi" version = "0.3.9" @@ -5176,15 +4992,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" -[[package]] -name = "xmltree" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" -dependencies = [ - "xml-rs", -] - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/crates/bevnet/Cargo.toml b/crates/bevnet/Cargo.toml index f64774c..e2e0037 100644 --- a/crates/bevnet/Cargo.toml +++ b/crates/bevnet/Cargo.toml @@ -12,7 +12,4 @@ categories = ["network-programming", "game-development"] workspace = true [dependencies] -local-ip-address = "0.5.7" -aes-gcm = "0.10.3" -base64 = "0.21.7" -igd = "0.12.1" +relay-client = { path = "../relay-client" } diff --git a/crates/bevnet/src/lib.rs b/crates/bevnet/src/lib.rs index 42aaa9a..a25e0a9 100644 --- a/crates/bevnet/src/lib.rs +++ b/crates/bevnet/src/lib.rs @@ -1,369 +1 @@ //! A networking library for Bevy. -//! -//! This contains an implementation of a non-blocking tcp connection and -//! listener with encryption and auto port forwarding. -//! -//! # Example -//! ```no_run -//! use std::io; -//! -//! use bevnet::{Connection, Listener}; -//! -//! # fn main() -> io::Result<()> { -//! let listener = Listener::new()?; -//! let mut connection = Connection::connect(&listener.connection_string())?; -//! -//! // The accept operation is not blocking. So we need to loop here. -//! let mut server_connection; -//! loop { -//! if let Some(new_connection) = listener.accept()? { -//! server_connection = new_connection; -//! break; -//! } -//! } -//! -//! // We don't need to loop here because the send operation just appends to the send buffer. -//! connection.send(b"Hello, world!")?; -//! -//! // To be sure the message has been sent, we need to update the connection. -//! while !connection.update()? { -//! // Wait until the connection is updated. -//! std::thread::yield_now(); -//! } -//! -//! // The receive operation is not blocking. So we need to loop here. -//! loop { -//! if let Some(message) = server_connection.receive()? { -//! assert_eq!(message, b"Hello, world!"); -//! break; -//! } -//! } -//! # Ok(()) -//! # } -//! ``` - -use std::collections::LinkedList; -use std::io::{self, Read, Write}; -use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}; - -use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng}; -use aes_gcm::{Aes128Gcm, Key, Nonce}; -use base64::prelude::*; -use igd::{Gateway, PortMappingProtocol}; -use local_ip_address::local_ip; - -/// A non-blocking tcp connection. -pub struct Connection { - /// The underlying [TcpStream] used for the connection. - stream: TcpStream, - - /// Contains the buffers that are not yet being sent. - send_buffers: LinkedList<(usize, Vec)>, - - /// The length of the next message to be received. - /// - /// `None` if the message length is not yet received. - receive_message_len: Option, - - /// The nonce used for encryption. - receive_message_nonce: Option>, - - /// The length of the received byte block. - /// - /// Used by [Connection::receive_partial] to determine if the block is - /// complete. - receive_filled_len: usize, - - /// The buffer used to receive a byte block. - receive_buffer: Vec, - - /// The cipher used for encryption and decryption. - cipher: Aes128Gcm, -} - -impl Connection { - /// Creates a new [Connection] from a [TcpStream]. - fn new(stream: TcpStream, secret_key: &Key) -> io::Result { - stream.set_nonblocking(true)?; - Ok(Self { - stream, - send_buffers: LinkedList::new(), - receive_message_len: None, - receive_message_nonce: None, - receive_filled_len: 0, - receive_buffer: Vec::new(), - cipher: Aes128Gcm::new(secret_key), - }) - } - - /// Creates a new [Connection] that connects to the given connection string. - /// - /// This function is blocking. - pub fn connect(connection_string: &str) -> io::Result { - let data = BASE64_URL_SAFE_NO_PAD - .decode(connection_string) - .map_err(io::Error::other)?; - if data.len() != 22 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!("invalid connection string: {}", connection_string), - )); - } - let address = SocketAddrV4::new( - Ipv4Addr::new(data[0], data[1], data[2], data[3]), - u16::from_ne_bytes(data[4..=5].try_into().map_err(|_| { - io::Error::new( - io::ErrorKind::InvalidInput, - format!("invalid connection string: {}", connection_string), - ) - })?), - ); - Self::new( - TcpStream::connect(address)?, - Key::::from_slice(&data[6..]), - ) - } - - /// Sends a message over the connection. - /// - /// Returns `true` if the message has been sent directly and `false` - /// if the message is still in the send queue. - /// - /// This function is not blocking. - pub fn send(&mut self, message: &[u8]) -> io::Result { - // Encrypt the message. - let nonce = Aes128Gcm::generate_nonce(OsRng); - let message = self.cipher.encrypt(&nonce, message).map_err(|e| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("failed to encrypt message: {}", e), - ) - })?; - - // Get the length of the message as a u16. - let message_len: u16 = match message.len().try_into() { - Ok(len) => len, - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!("message length is too large: {}", message.len()), - )); - } - }; - - // Add a new buffer to the send queue. - self.send_buffers - .push_back((0, message_len.to_ne_bytes().to_vec())); - self.send_buffers.push_back((0, nonce.to_vec())); - self.send_buffers.push_back((0, message)); - - // Update the connection. - self.update() - } - - /// Updates the connection. - /// - /// This function sends any pending messages that have not been sent yet. - /// It returns `true` if there is no remaining data to send after updating - /// the connection and `false` otherwise. - /// - /// This function is not blocking. - pub fn update(&mut self) -> io::Result { - // Looping over the send buffers. - while let Some((offset, buffer)) = self.send_buffers.front_mut() { - // Writing the buffer to the stream. - match self.stream.write(&buffer[*offset..]) { - Ok(n) => *offset += n, - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break, - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => break, - Err(e) => return Err(e), - } - - // Removing the buffer if it is fully sent. - if *offset >= buffer.len() { - self.send_buffers.pop_front(); - } - } - - // Returning success. - Ok(self.send_buffers.is_empty()) - } - - /// Receives a byte block from the connection. - /// - /// This function fills the receive buffer and returns `true` if the - /// buffer is successfully filled with `len` bytes and `false` if the - /// buffer is not filled yet. - /// - /// This function mustn't be called for different byte block sequentially - /// because the function can only process one byte block at a time. - /// - /// This function is not blocking. - fn receive_partial(&mut self, len: u16) -> io::Result { - let len = len as usize; - - // Resizing the buffer if it is not large enough. - if self.receive_buffer.len() < len { - self.receive_buffer.resize(len, 0); - } - - // Checking if the buffer is already filled. - if self.receive_filled_len >= len { - self.receive_filled_len = 0; - return Ok(true); - } - - // Reading from the stream. - let start_index = self.receive_filled_len; - let receive_buffer = &mut self.receive_buffer[start_index..start_index + len]; - let received_len = self.stream.read(receive_buffer); - self.receive_filled_len += match received_len { - Ok(0) => { - return Err(io::Error::new( - io::ErrorKind::ConnectionAborted, - "connection closed by remote peer", - )); - } - Ok(n) => n, - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(false), - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => return Ok(false), - Err(e) => return Err(e), - }; - - // Checking if the buffer is filled. - if self.receive_filled_len >= len { - self.receive_filled_len = 0; - return Ok(true); - } - Ok(false) - } - - /// Receives a message from the connection. - /// - /// If no message is available, returns `None`. - /// - /// This function is not blocking. - pub fn receive(&mut self) -> io::Result>> { - // Receiving the message length. - let message_len = match self.receive_message_len { - Some(message_len) => message_len, - None => { - // If the message length is not received yet, return `None`. - if !self.receive_partial(2)? { - return Ok(None); - } - - // Setting the message length. - let message_len = - u16::from_ne_bytes(self.receive_buffer[..2].try_into().map_err(|_| { - io::Error::new(io::ErrorKind::InvalidData, "invalid message length") - })?); - self.receive_message_len = Some(message_len); - - // Returning the message length. - message_len - } - }; - - if self.receive_message_nonce.is_none() { - // If the nonce is not received yet, return `None`. - if !self.receive_partial(12)? { - return Ok(None); - } - - // Setting the nonce. - self.receive_message_nonce = Some(self.receive_buffer[..12].to_vec()); - } - - // Receiving the message. - if !self.receive_partial(message_len)? { - return Ok(None); - } - let message = &self.receive_buffer[..message_len as usize]; - - // Getting the nonce. - let nonce = self - .receive_message_nonce - .as_ref() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "missing nonce"))?; - - // Decrypting the message. - let message = self - .cipher - .decrypt(Nonce::from_slice(nonce), message) - .map_err(|e| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("failed to decrypt message: {}", e), - ) - })?; - - // Resetting the message length and nonce. - self.receive_message_len = None; - self.receive_message_nonce = None; - - // Returning the message. - Ok(Some(message)) - } -} - -/// A non-blocking tcp listener. -pub struct Listener(TcpListener, Gateway, SocketAddrV4, Key); - -impl Listener { - /// Creates a new listener. - pub fn new() -> io::Result { - let local_address = match local_ip().map_err(io::Error::other)? { - IpAddr::V4(address) => address, - IpAddr::V6(_) => unreachable!(), - }; - let listener = TcpListener::bind(SocketAddrV4::new(local_address, 0))?; - let gateway = igd::search_gateway(Default::default()).map_err(io::Error::other)?; - let opened_port = gateway - .add_any_port( - PortMappingProtocol::TCP, - SocketAddrV4::new(local_address, listener.local_addr()?.port()), - 3600 * 24, - "bevnet", - ) - .map_err(io::Error::other)?; - let external_address = gateway.get_external_ip().map_err(io::Error::other)?; - listener.set_nonblocking(true)?; - Ok(Self( - listener, - gateway, - SocketAddrV4::new(external_address, opened_port), - Aes128Gcm::generate_key(OsRng), - )) - } - - /// Accepts a new [Connection]. - /// - /// This function is not blocking. - pub fn accept(&self) -> io::Result> { - match self.0.accept() { - Ok((stream, _)) => Connection::new(stream, &self.3).map(Some), - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None), - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => Ok(None), - Err(e) => Err(e), - } - } - - /// Returns the connection string that can be used to connect to th - /// listener. - pub fn connection_string(&self) -> String { - let mut data = Vec::with_capacity(22); - data.extend(self.2.ip().octets()); - data.extend(self.2.port().to_ne_bytes()); - data.extend(self.3); - BASE64_URL_SAFE_NO_PAD.encode(&data) - } -} - -impl Drop for Listener { - fn drop(&mut self) { - self.1 - .remove_port(PortMappingProtocol::TCP, self.2.port()) - .ok(); - } -}