diff --git a/src/onion/tests.rs b/src/onion/tests.rs index 3f5d670..7d3447e 100644 --- a/src/onion/tests.rs +++ b/src/onion/tests.rs @@ -8,6 +8,8 @@ use tokio::stream; const TEST_IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); static PORT_COUNTER: AtomicU16 = AtomicU16::new(42000); +const TIME_ERROR_TIMEOUT: u64 = 4; +const ERROR_TIMEOUT: Duration = Duration::from_secs(TIME_ERROR_TIMEOUT); async fn listen(mut listener: TcpListener, host_key: &RsaPrivateKey) -> Result<()> { println!( @@ -208,3 +210,55 @@ async fn test_data_bidirectional() -> Result<()> { ); Ok(()) } + +#[tokio::test] +async fn test_keep_alive() -> Result<()> { + let rng = rand::SystemRandom::new(); + let peers = spawn_n_peers(3).await; + let mut tunnel = Tunnel::init(0, &peers[0], &rng).await?; + for i in 1..3 { + tunnel.extend(&peers[i], &rng).await?; + } + assert_eq!(tunnel.len(), 3); + tunnel.keep_alive(&rng).await?; + assert_eq!(tunnel.len(), 3); + Ok(()) +} + +#[tokio::test] +async fn test_timeout() -> Result<()> { + let rng = rand::SystemRandom::new(); + let peers = spawn_n_peers(3).await; + let mut tunnel = Tunnel::init(0, &peers[0], &rng).await?; + for i in 1..2 { + tunnel.extend(&peers[i], &rng).await?; + } + assert_eq!(tunnel.len(), 2); + let (tx, rx) = mpsc::unbounded_channel(); + let (events_tx, mut events_rx) = mpsc::channel(1); + let tunnel_id = tunnel.id; + let peer_provider = PeerProvider::from_stream(stream::iter(vec![peers[2].clone()])); + let builder = TunnelBuilder::new(tunnel.id, TunnelDestination::Fixed(peers[1].clone()), 1, peer_provider, rng); + let mut handler = TunnelHandler::new(tunnel, builder, rx, events_tx); + + let handler_task = tokio::spawn({ + async move { + handler.handle().await; + } + }); + + tx.send(tunnel::Request::Switchover)?; + match time::timeout(ERROR_TIMEOUT, events_rx.next()).await { + Ok(Some(Event::Ready { tunnel_id: ev_tunnel_id })) => assert_eq!(ev_tunnel_id, tunnel_id), + Ok(e) => panic!("Expected ready event, got {:?}", e), + Err(_) => panic!("Expected ready event, got timeout"), + } + + let mut delay = time::delay_for(Duration::from_secs(circuit::TIMEOUT_IDLE + TIME_ERROR_TIMEOUT)); + tokio::select! { + _ = handler_task => Ok(()), + _ = &mut delay => { + panic!("No circuit destroyed the tunnel before timeout") + }, + } +} diff --git a/tests/onion.rs b/tests/onion.rs index e68d42a..7e2d051 100644 --- a/tests/onion.rs +++ b/tests/onion.rs @@ -9,8 +9,9 @@ use tokio::stream::StreamExt; use tokio::time; static PORT_COUNTER: AtomicU16 = AtomicU16::new(42000); -const ERROR_TIMEOUT: Duration = Duration::from_secs(2); +const ERROR_TIMEOUT: Duration = Duration::from_secs(4); const ROUND_TIMEOUT: Duration = Duration::from_secs(45); +const DELAY_TIMEOUT: Duration = Duration::from_secs(2); const TUNNEL_ID: TunnelId = 3; // chosen by fair dice roll const TEST_DATA: &[u8] = b"test"; const LONG_DATA: [u8; 4098] = [13; 4098]; @@ -28,6 +29,15 @@ fn new_unique_peer() -> (Peer, RsaPrivateKey) { (Peer::new(addr, hostkey.public_key()), hostkey) } +fn new_unique_peers(n: usize) -> Vec { + let mut peers = vec![]; + for _ in 0..n { + let (peer, _) = new_unique_peer(); + peers.push(peer); + } + peers +} + async fn spawn_peer( peers: Vec, cover: bool, @@ -61,7 +71,7 @@ async fn test_cover() { pretty_env_logger::init(); let peer1 = spawn_simple_peer().await; let mut peer2 = spawn_peer(vec![peer1.peer], true, 0).await; - time::delay_for(ERROR_TIMEOUT).await; + time::delay_for(DELAY_TIMEOUT).await; peer2.onion.send_cover(1); match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { Ok(e) => panic!("Got error event: {:?}", e), @@ -116,6 +126,46 @@ async fn test_build_error() { } } +#[tokio::test] +async fn test_build_unstable_success() { + // For some reason 8 (MAX_PEER_FAILURES - 2) or higher fails + let mut peers = new_unique_peers(7); + let peer2 = spawn_peer(vec![]).await; + peers.push(peer2.peer); + let mut peer1 = spawn_peer(peers).await; + let mut peer3 = spawn_peer(vec![]).await; + peer1.onion.build_tunnel(TUNNEL_ID, peer3.peer, 1); + match time::timeout(ROUND_TIMEOUT, peer1.events.next()).await { + Ok(Some(Event::Ready { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected ready event, got {:?}", e), + Err(_) => panic!("Expected ready event, got timeout"), + } + + match time::timeout(ERROR_TIMEOUT, peer3.events.next()).await { + Ok(Some(Event::Incoming { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected incoming event, got {:?}", e), + Err(_) => panic!("Expected incoming event, got timeout"), + } +} + +#[tokio::test] +async fn test_build_unstable_error() { + let mut peers = new_unique_peers(12); + let peer2 = spawn_peer(vec![]).await; + peers.push(peer2.peer); + let mut peer1 = spawn_peer(peers).await; + let peer3 = spawn_peer(vec![]).await; + peer1.onion.build_tunnel(TUNNEL_ID, peer3.peer, 1); + match time::timeout(ERROR_TIMEOUT, peer1.events.next()).await { + Ok(Some(Event::Error { + reason: ErrorReason::Build, + tunnel_id, + })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected error event, got {:?}", e), + Err(_) => panic!("Expected error event, got timeout"), + } +} + #[tokio::test] async fn test_data() { let mut peer1 = spawn_simple_peer().await; @@ -220,3 +270,87 @@ async fn test_long_data_two_hops() { } } } + +#[tokio::test] +async fn test_data_error_tunnel_id() { + let mut peer1 = spawn_peer(vec![]).await; + let mut peer2 = spawn_peer(vec![]).await; + + peer1.onion.build_tunnel(TUNNEL_ID, peer2.peer, 0); + match time::timeout(ROUND_TIMEOUT, peer1.events.next()).await { + Ok(Some(Event::Ready { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected ready event, got {:?}", e), + Err(_) => panic!("Expected ready event, got timeout"), + } + + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(Some(Event::Incoming { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected incoming event, got {:?}", e), + Err(_) => panic!("Expected incoming event, got timeout"), + } + + let wrong_tunnel_id = TUNNEL_ID + 1; + peer1.onion.send_data(wrong_tunnel_id, TEST_DATA); + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(Some(Event::Error { + reason: ErrorReason::Data, + tunnel_id, + })) => assert_eq!(tunnel_id, wrong_tunnel_id), + Ok(e) => panic!("Expected error event, got {:?}", e), + Err(_) => panic!("Expected error event, got timeout"), + } +} + +#[tokio::test] +async fn test_data_error_disconnected_destination() { + let mut peer1 = spawn_peer(vec![]).await; + let mut peer2 = spawn_peer(vec![]).await; + + peer1.onion.build_tunnel(TUNNEL_ID, peer2.peer, 0); + match time::timeout(ROUND_TIMEOUT, peer1.events.next()).await { + Ok(Some(Event::Ready { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected ready event, got {:?}", e), + Err(_) => panic!("Expected ready event, got timeout"), + } + + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(Some(Event::Incoming { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected incoming event, got {:?}", e), + Err(_) => panic!("Expected incoming event, got timeout"), + } + + peer2.onion.destroy_tunnel(TUNNEL_ID); + time::delay_for(DELAY_TIMEOUT).await; + peer1.onion.send_data(TUNNEL_ID, TEST_DATA); + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(e) => panic!("Got unexpected event: {:?}", e), + Err(_) => {} + } +} + +#[tokio::test] +async fn test_data_error_disconnected_source() { + let mut peer1 = spawn_peer(vec![]).await; + let mut peer2 = spawn_peer(vec![]).await; + + peer1.onion.build_tunnel(TUNNEL_ID, peer2.peer, 0); + match time::timeout(ROUND_TIMEOUT, peer1.events.next()).await { + Ok(Some(Event::Ready { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected ready event, got {:?}", e), + Err(_) => panic!("Expected ready event, got timeout"), + } + + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(Some(Event::Incoming { tunnel_id })) => assert_eq!(tunnel_id, TUNNEL_ID), + Ok(e) => panic!("Expected incoming event, got {:?}", e), + Err(_) => panic!("Expected incoming event, got timeout"), + } + + peer1.onion.destroy_tunnel(TUNNEL_ID); + time::delay_for(DELAY_TIMEOUT).await; + peer1.onion.send_data(TUNNEL_ID, TEST_DATA); + match time::timeout(ERROR_TIMEOUT, peer2.events.next()).await { + Ok(e) => panic!("Got unexpected event: {:?}", e), + Err(_) => {} + } +}