From d57c212b67e519508e672a8943731013c88cffa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kub=C3=ADk?= Date: Sat, 25 Nov 2023 18:45:55 +0100 Subject: [PATCH] test: add authentication integration test (#31) --- Cargo.lock | 217 ++++++++++++++++++++++------- Cargo.toml | 3 +- src/auth/server.rs | 56 +++++--- src/auth/user.rs | 8 +- src/constants.rs | 3 + src/server/tunnel.rs | 13 +- tests/{common.rs => common/mod.rs} | 101 +++++++------- tests/test_end_to_end.rs | 120 ++++++++-------- tests/test_failed_auth.rs | 83 +++++++++++ 9 files changed, 422 insertions(+), 182 deletions(-) rename tests/{common.rs => common/mod.rs} (51%) create mode 100644 tests/test_failed_auth.rs diff --git a/Cargo.lock b/Cargo.lock index f70dc8c..9815af9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,28 +98,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - [[package]] name = "atomic" version = "0.6.0" @@ -399,18 +377,101 @@ dependencies = [ "version_check", ] +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "futures-sink" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -438,6 +499,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.14.2" @@ -672,6 +739,12 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "powerfmt" version = "0.2.0" @@ -723,6 +796,7 @@ dependencies = [ "once_cell", "quinn", "rpassword", + "rstest", "rustls", "rustls-pemfile", "serde", @@ -730,9 +804,9 @@ dependencies = [ "socket2", "time", "tokio", - "tokio-test", "tracing", "tracing-subscriber", + "tracing-test", "tun", ] @@ -876,6 +950,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" + [[package]] name = "ring" version = "0.16.20" @@ -916,6 +996,35 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.39", + "unicode-ident", +] + [[package]] name = "rtoolbox" version = "0.0.2" @@ -938,6 +1047,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.21.9" @@ -1035,6 +1153,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + [[package]] name = "serde" version = "1.0.193" @@ -1246,30 +1370,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-test" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" -dependencies = [ - "async-stream", - "bytes", - "futures-core", - "tokio", - "tokio-stream", -] - [[package]] name = "tokio-util" version = "0.7.10" @@ -1380,6 +1480,29 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote", + "syn 1.0.109", +] + [[package]] name = "tun" version = "0.6.1" diff --git a/Cargo.toml b/Cargo.toml index e106775..dfcf8b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,4 +68,5 @@ clap = { version = "^4.1", features = ["derive"] } once_cell = "^1.17" [dev-dependencies] -tokio-test = "^0.4.2" +rstest = "^0.18.0" +tracing-test = { version = "^0.2.4", features=["no-env-filter"] } diff --git a/src/auth/server.rs b/src/auth/server.rs index 7171367..6da17ef 100644 --- a/src/auth/server.rs +++ b/src/auth/server.rs @@ -1,6 +1,6 @@ use std::{net::IpAddr, sync::Arc, time::Duration}; -use crate::constants::{AUTH_FAILED_MESSAGE, AUTH_MESSAGE_BUFFER_SIZE}; +use crate::constants::{AUTH_FAILED_MESSAGE, AUTH_MESSAGE_BUFFER_SIZE, AUTH_TIMEOUT_MESSAGE}; use anyhow::{anyhow, Context, Result}; use bytes::BytesMut; use ipnet::IpNet; @@ -45,15 +45,26 @@ impl AuthServer { /// Handles authentication for a client. pub async fn handle_authentication(&mut self) -> Result<()> { - let (send_stream, mut recv_stream) = self.connection.accept_bi().await?; - - let message = timeout(self.auth_timeout, Self::recv_message(&mut recv_stream)).await?; - - if let Ok(AuthClientMessage::Authentication(username, password)) = message { - self.authenticate_user(send_stream, username, password) - .await - } else { - self.handle_failure(send_stream).await + let (send_stream, mut recv_stream) = + match timeout(self.auth_timeout, self.connection.accept_bi()).await { + Ok(Ok(streams)) => streams, + Ok(Err(e)) => return Err(e.into()), + Err(_) => return self.handle_failure(AUTH_TIMEOUT_MESSAGE, None).await, + }; + + match timeout(self.auth_timeout, Self::recv_message(&mut recv_stream)).await { + Ok(Ok(AuthClientMessage::Authentication(username, password))) => { + self.authenticate_user(send_stream, username, password) + .await + } + Ok(Err(_)) => { + self.handle_failure(AUTH_FAILED_MESSAGE, Some(send_stream)) + .await + } + Err(_) => { + self.handle_failure(AUTH_TIMEOUT_MESSAGE, Some(send_stream)) + .await + } } } @@ -67,7 +78,9 @@ impl AuthServer { let auth_result = self.user_database.authenticate(&username, password).await; if auth_result.is_err() { - return self.handle_failure(send_stream).await; + return self + .handle_failure(AUTH_FAILED_MESSAGE, Some(send_stream)) + .await; } let response = AuthServerMessage::Authenticated( @@ -82,18 +95,23 @@ impl AuthServer { } /// Handles a failure during authentication. - async fn handle_failure(&self, send_stream: SendStream) -> Result<()> { - self.close_connection(send_stream, AUTH_FAILED_MESSAGE) - .await?; + async fn handle_failure( + &self, + reason: &'static str, + send_stream: Option, + ) -> Result<()> { + if let Some(mut send_stream) = send_stream { + Self::send_message(&mut send_stream, AuthServerMessage::Failed).await?; + send_stream.finish().await?; + } + + self.close_connection(reason).await?; - Err(anyhow!(AUTH_FAILED_MESSAGE)) + Err(anyhow!(reason)) } /// Closes the connection with the given reason. - async fn close_connection(&self, mut send_stream: SendStream, reason: &str) -> Result<()> { - Self::send_message(&mut send_stream, AuthServerMessage::Failed).await?; - send_stream.finish().await?; - + async fn close_connection(&self, reason: &str) -> Result<()> { self.connection .close(VarInt::from_u32(0x01), reason.as_bytes()); diff --git a/src/auth/user.rs b/src/auth/user.rs index 6708483..2710a74 100644 --- a/src/auth/user.rs +++ b/src/auth/user.rs @@ -138,8 +138,8 @@ mod tests { use argon2::{Argon2, PasswordHasher}; use dashmap::DashMap; - #[test] - fn test_authentication() { + #[tokio::test] + async fn test_authentication() { let users: DashMap = DashMap::new(); let argon = Argon2::default(); @@ -153,7 +153,9 @@ mod tests { users.insert(username.clone(), test_user); let user_db = UserDatabase::new(users); - tokio_test::block_on(user_db.authenticate(&username, password)) + user_db + .authenticate(&username, password) + .await .expect("Credentials are valid"); } } diff --git a/src/constants.rs b/src/constants.rs index f0bde64..896759f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -23,6 +23,9 @@ pub const CLEANUP_INTERVAL: Duration = Duration::from_secs(1); /// Error message when authentication fails. pub const AUTH_FAILED_MESSAGE: &str = "Authentication failed"; +/// Error message when authentication times out. +pub const AUTH_TIMEOUT_MESSAGE: &str = "Authentication timed out"; + /// Buffer size for authentication messages. pub const AUTH_MESSAGE_BUFFER_SIZE: usize = 1024; diff --git a/src/server/tunnel.rs b/src/server/tunnel.rs index aa6cf41..327001e 100644 --- a/src/server/tunnel.rs +++ b/src/server/tunnel.rs @@ -216,7 +216,18 @@ impl QuincyTunnel { client_tun_ip, ); - connection.start().await?; + let connection = match connection.start().await { + Ok(_) => connection, + Err(e) => { + error!( + "Failed to set up connection with client '{client_tun_ip}': {e}", + client_tun_ip = client_tun_ip.addr(), + e = e + ); + continue; + } + }; + info!( "Connection established: {client_tun_ip} ({})", connection.remote_address(), diff --git a/tests/common.rs b/tests/common/mod.rs similarity index 51% rename from tests/common.rs rename to tests/common/mod.rs index fd79c82..3fcc646 100644 --- a/tests/common.rs +++ b/tests/common/mod.rs @@ -1,41 +1,21 @@ use bytes::{BufMut, Bytes, BytesMut}; use etherparse::PacketBuilder; -use ipnet::IpNet; use once_cell::sync::Lazy; -use quincy::interface::{Interface, InterfaceRead, InterfaceWrite}; +use quincy::config::{ClientConfig, FromPath, ServerConfig}; +use quincy::interface::{InterfaceRead, InterfaceWrite}; +use rstest::fixture; use std::io::Error; use std::net::Ipv4Addr; +use std::path::Path; use std::pin::Pin; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio::sync::mpsc; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; +use tokio::sync::{mpsc, Mutex}; -type TestSender = Arc>>; -type TestReceiver = Arc>>; -pub static TEST_QUEUE_CLIENT_SEND: Lazy<(TestSender, TestReceiver)> = Lazy::new(|| { - let (tx, rx) = mpsc::unbounded_channel(); - (Arc::new(Mutex::new(tx)), Arc::new(Mutex::new(rx))) -}); - -pub static TEST_QUEUE_CLIENT_RECV: Lazy<(TestSender, TestReceiver)> = Lazy::new(|| { - let (tx, rx) = mpsc::unbounded_channel(); - (Arc::new(Mutex::new(tx)), Arc::new(Mutex::new(rx))) -}); - -pub static TEST_QUEUE_SERVER_SEND: Lazy<(TestSender, TestReceiver)> = Lazy::new(|| { - let (tx, rx) = mpsc::unbounded_channel(); - (Arc::new(Mutex::new(tx)), Arc::new(Mutex::new(rx))) -}); - -pub static TEST_QUEUE_SERVER_RECV: Lazy<(TestSender, TestReceiver)> = Lazy::new(|| { - let (tx, rx) = mpsc::unbounded_channel(); - (Arc::new(Mutex::new(tx)), Arc::new(Mutex::new(rx))) -}); - -pub struct Client; -pub struct Server; +pub type TestSender = Arc>>; +pub type TestReceiver = Arc>>; pub struct TestInterface { _p: std::marker::PhantomData, @@ -43,8 +23,15 @@ pub struct TestInterface { rx: TestReceiver, } -pub type TestInterfaceClient = TestInterface; -pub type TestInterfaceServer = TestInterface; +impl TestInterface { + pub fn new(tx: TestSender, rx: TestReceiver) -> Self { + Self { + _p: std::marker::PhantomData, + tx, + rx, + } + } +} impl AsyncRead for TestInterface { fn poll_read( @@ -52,7 +39,7 @@ impl AsyncRead for TestInterface { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - let data = self.get_mut().rx.lock().unwrap().poll_recv(cx); + let data = self.get_mut().rx.try_lock().unwrap().poll_recv(cx); match data { Poll::Ready(Some(data)) => { buf.put_slice(&data); @@ -71,7 +58,7 @@ impl AsyncWrite for TestInterface { buf: &[u8], ) -> Poll> { let data = Bytes::copy_from_slice(buf); - self.get_mut().tx.lock().unwrap().send(data).unwrap(); + self.get_mut().tx.try_lock().unwrap().send(data).unwrap(); Poll::Ready(Ok(buf.len())) } @@ -86,25 +73,6 @@ impl AsyncWrite for TestInterface { impl InterfaceRead for TestInterface {} impl InterfaceWrite for TestInterface {} -impl Interface for TestInterface { - fn create(_interface_address: IpNet, _mtu: i32) -> anyhow::Result { - Ok(Self { - _p: std::marker::PhantomData, - tx: TEST_QUEUE_CLIENT_SEND.0.clone(), - rx: TEST_QUEUE_CLIENT_RECV.1.clone(), - }) - } -} - -impl Interface for TestInterface { - fn create(_interface_address: IpNet, _mtu: i32) -> anyhow::Result { - Ok(Self { - _p: std::marker::PhantomData, - tx: TEST_QUEUE_SERVER_SEND.0.clone(), - rx: TEST_QUEUE_SERVER_RECV.1.clone(), - }) - } -} pub fn dummy_packet(src: Ipv4Addr, dest: Ipv4Addr) -> Bytes { let mut writer = BytesMut::new().writer(); @@ -115,3 +83,34 @@ pub fn dummy_packet(src: Ipv4Addr, dest: Ipv4Addr) -> Bytes { writer.into_inner().into() } + +#[fixture] +pub fn client_config() -> ClientConfig { + ClientConfig::from_path(Path::new("tests/static/client.toml"), "QUINCY").unwrap() +} + +#[fixture] +pub fn server_config() -> ServerConfig { + ServerConfig::from_path(Path::new("tests/static/server.toml"), "QUINCY").unwrap() +} + +pub const fn make_queue_pair() -> Lazy<(TestSender, TestReceiver)> { + Lazy::new(|| { + let (tx, rx) = mpsc::unbounded_channel(); + (Arc::new(Mutex::new(tx)), Arc::new(Mutex::new(rx))) + }) +} + +#[macro_export] +macro_rules! interface_impl { + ($name:ident, $test_queue_send:ident, $test_queue_recv:ident) => { + impl Interface for $name { + fn create(_interface_address: IpNet, _mtu: i32) -> Result { + Ok(Self::new( + $test_queue_send.0.clone(), + $test_queue_recv.1.clone(), + )) + } + } + }; +} diff --git a/tests/test_end_to_end.rs b/tests/test_end_to_end.rs index 19f5e31..33ca4c9 100644 --- a/tests/test_end_to_end.rs +++ b/tests/test_end_to_end.rs @@ -1,84 +1,84 @@ use crate::common::{ - dummy_packet, TestInterfaceClient, TestInterfaceServer, TEST_QUEUE_CLIENT_RECV, - TEST_QUEUE_CLIENT_SEND, TEST_QUEUE_SERVER_RECV, TEST_QUEUE_SERVER_SEND, + client_config, dummy_packet, make_queue_pair, server_config, TestInterface, TestReceiver, + TestSender, }; +use anyhow::Result; +use ipnet::IpNet; +use once_cell::sync::Lazy; use quincy::client::QuincyClient; -use quincy::config::{ClientConfig, FromPath, ServerConfig}; +use quincy::config::{ClientConfig, ServerConfig}; +use quincy::interface::Interface; use quincy::server::QuincyServer; +use rstest::rstest; use std::net::Ipv4Addr; -use std::path::Path; -use tokio::task::spawn_blocking; mod common; -#[test] -fn test_end_to_end_communication() { +struct Client; +type ClientInterface = TestInterface; +struct Server; +type ServerInterface = TestInterface; + +pub static TEST_QUEUE_CLIENT_SEND: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_CLIENT_RECV: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_SERVER_SEND: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_SERVER_RECV: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); + +interface_impl!( + ClientInterface, + TEST_QUEUE_CLIENT_SEND, + TEST_QUEUE_CLIENT_RECV +); +interface_impl!( + ServerInterface, + TEST_QUEUE_SERVER_SEND, + TEST_QUEUE_SERVER_RECV +); + +#[rstest] +#[tokio::test] +async fn test_end_to_end_communication(client_config: ClientConfig, server_config: ServerConfig) { #[cfg(target_os = "macos")] use quincy::interface::prepend_packet_info_header; - let client_config = - ClientConfig::from_path(Path::new("tests/static/client.toml"), "QUINCY").unwrap(); let client = QuincyClient::new(client_config); - - let server_config = - ServerConfig::from_path(Path::new("tests/static/server.toml"), "QUINCY").unwrap(); let server = QuincyServer::new(server_config).unwrap(); let ip_server = Ipv4Addr::new(10, 0, 0, 1); let ip_client = Ipv4Addr::new(10, 0, 0, 2); - tokio_test::block_on(async move { - tokio::spawn(async move { server.run::().await.unwrap() }); - tokio::spawn(async move { client.run::().await.unwrap() }); - - // Test client -> server - let test_packet = dummy_packet(ip_client, ip_server); - #[cfg(target_os = "macos")] - let test_packet = prepend_packet_info_header(test_packet).unwrap(); - - TEST_QUEUE_CLIENT_RECV - .0 - .lock() - .unwrap() - .send(test_packet.clone()) - .unwrap(); - - let recv_packet = spawn_blocking(|| { - TEST_QUEUE_SERVER_SEND - .1 - .lock() - .unwrap() - .blocking_recv() - .unwrap() - }) + tokio::spawn(async move { server.run::().await.unwrap() }); + tokio::spawn(async move { client.run::().await.unwrap() }); + + // Test client -> server + let test_packet = dummy_packet(ip_client, ip_server); + #[cfg(target_os = "macos")] + let test_packet = prepend_packet_info_header(test_packet).unwrap(); + + TEST_QUEUE_CLIENT_RECV + .0 + .lock() .await + .send(test_packet.clone()) .unwrap(); - assert_eq!(test_packet, recv_packet); - - // Test server -> client - let test_packet = dummy_packet(ip_server, ip_client); - #[cfg(target_os = "macos")] - let test_packet = prepend_packet_info_header(test_packet).unwrap(); - - TEST_QUEUE_SERVER_RECV - .0 - .lock() - .unwrap() - .send(test_packet.clone()) - .unwrap(); - - let recv_packet = spawn_blocking(|| { - TEST_QUEUE_CLIENT_SEND - .1 - .lock() - .unwrap() - .blocking_recv() - .unwrap() - }) + let recv_packet = TEST_QUEUE_SERVER_SEND.1.lock().await.recv().await.unwrap(); + + assert_eq!(test_packet, recv_packet); + + // Test server -> client + let test_packet = dummy_packet(ip_server, ip_client); + #[cfg(target_os = "macos")] + let test_packet = prepend_packet_info_header(test_packet).unwrap(); + + TEST_QUEUE_SERVER_RECV + .0 + .lock() .await + .send(test_packet.clone()) .unwrap(); - assert_eq!(test_packet, recv_packet); - }); + let recv_packet = TEST_QUEUE_CLIENT_SEND.1.lock().await.recv().await.unwrap(); + + assert_eq!(test_packet, recv_packet); } diff --git a/tests/test_failed_auth.rs b/tests/test_failed_auth.rs new file mode 100644 index 0000000..3d66106 --- /dev/null +++ b/tests/test_failed_auth.rs @@ -0,0 +1,83 @@ +use crate::common::{ + client_config, dummy_packet, make_queue_pair, server_config, TestInterface, TestReceiver, + TestSender, +}; +use anyhow::Result; +use ipnet::IpNet; +use once_cell::sync::Lazy; +use quincy::client::QuincyClient; +use quincy::config::{ClientConfig, ServerConfig}; +use quincy::interface::Interface; +use quincy::server::QuincyServer; +use rstest::rstest; +use std::net::Ipv4Addr; +use std::time::Duration; +use tokio::time::sleep; +use tracing_test::traced_test; + +mod common; + +struct Client; +type ClientInterface = TestInterface; +struct Server; +type ServerInterface = TestInterface; + +pub static TEST_QUEUE_CLIENT_SEND: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_CLIENT_RECV: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_SERVER_SEND: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); +pub static TEST_QUEUE_SERVER_RECV: Lazy<(TestSender, TestReceiver)> = make_queue_pair(); + +interface_impl!( + ClientInterface, + TEST_QUEUE_CLIENT_SEND, + TEST_QUEUE_CLIENT_RECV +); +interface_impl!( + ServerInterface, + TEST_QUEUE_SERVER_SEND, + TEST_QUEUE_SERVER_RECV +); + +#[rstest] +#[tokio::test] +#[traced_test] +async fn test_failed_auth(mut client_config: ClientConfig, server_config: ServerConfig) { + client_config.authentication.password = "wrong_password".to_string(); + let client = QuincyClient::new(client_config); + let server = QuincyServer::new(server_config).unwrap(); + + let ip_server = Ipv4Addr::new(10, 0, 0, 1); + let ip_client = Ipv4Addr::new(10, 0, 0, 2); + let test_packet_client = dummy_packet(ip_client, ip_server); + let test_packet_server = dummy_packet(ip_server, ip_client); + + tokio::spawn(async move { server.run::().await }); + let client_task = tokio::spawn(async move { client.run::().await }); + + TEST_QUEUE_CLIENT_RECV + .0 + .lock() + .await + .send(test_packet_client) + .unwrap(); + + TEST_QUEUE_SERVER_RECV + .0 + .lock() + .await + .send(test_packet_server) + .unwrap(); + + assert!(client_task.await.unwrap().is_err()); + + // Wait for everything to propagate and be logged + sleep(Duration::from_secs(1)).await; + + let recv_packet_server = TEST_QUEUE_SERVER_SEND.1.lock().await.try_recv(); + assert!(recv_packet_server.is_err()); + + let recv_packet_client = TEST_QUEUE_CLIENT_SEND.1.lock().await.try_recv(); + assert!(recv_packet_client.is_err()); + + assert!(logs_contain("Failed to set up connection with client")); +}