checkpoint: integrate authenticated transports
This commit is contained in:
1 parent
313ab00765
commit
586ff8f223
42 files changed
+6277
-771
No files matched your search
@@ -0,0 +1,146 @@
|
||||
use std::{os::unix::fs::PermissionsExt, path::Path};
|
||||
use tempfile::tempdir;
|
||||
use tokio::time::Duration;
|
||||
use tokio::{net::UdpSocket, time::timeout};
|
||||
use wgcloak::{
|
||||
config::Config,
|
||||
control, server,
|
||||
transport::{direct_udp::DirectUdpSession, TransportSession},
|
||||
};
|
||||
|
||||
const DIRECT_SECRET: &str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
|
||||
fn private_runtime_dir(root: &Path) -> std::path::PathBuf {
|
||||
let path = root.join("runtime");
|
||||
std::fs::create_dir(&path).unwrap();
|
||||
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn control_commands_drive_only_supported_runtime_actions() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path = private_runtime_dir(dir.path()).join("runtime.sock");
|
||||
let state = control::ControlState::default();
|
||||
state.enable_reconnect();
|
||||
let server = control::ControlServer::bind(&path, state.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
let task = tokio::spawn(server.run());
|
||||
|
||||
state.set_status("connected; transport=direct_udp").await;
|
||||
state
|
||||
.set_stats_json("{\"datagrams_sent\":7,\"datagrams_dropped\":1}")
|
||||
.await;
|
||||
let status = control::send_command(&path, "status").await.unwrap();
|
||||
assert!(status.contains("connected; transport=direct_udp"));
|
||||
let stats = control::send_command(&path, "stats").await.unwrap();
|
||||
assert!(stats.contains("\"datagrams_sent\":7"));
|
||||
|
||||
let reconnect = state.reconnect.notified();
|
||||
tokio::pin!(reconnect);
|
||||
let response = control::send_command(&path, "reconnect").await.unwrap();
|
||||
assert!(response.contains("\"ok\":true"));
|
||||
timeout(Duration::from_secs(1), &mut reconnect)
|
||||
.await
|
||||
.expect("reconnect should reach the running client loop");
|
||||
|
||||
let response = control::send_command(&path, "reload").await.unwrap();
|
||||
assert!(response.contains("\"ok\":false"));
|
||||
assert!(response.contains("not supported"));
|
||||
|
||||
let response = control::send_command(&path, "shutdown").await.unwrap();
|
||||
assert!(response.contains("\"ok\":true"));
|
||||
timeout(Duration::from_secs(1), task)
|
||||
.await
|
||||
.expect("control server should stop on shutdown")
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_control_rejects_client_only_reconnect() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path = private_runtime_dir(dir.path()).join("server.sock");
|
||||
let state = control::ControlState::default();
|
||||
let server = control::ControlServer::bind(&path, state.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
let task = tokio::spawn(server.run());
|
||||
|
||||
let response = control::send_command(&path, "reconnect").await.unwrap();
|
||||
assert!(response.contains("\"ok\":false"));
|
||||
assert!(response.contains("not supported"));
|
||||
|
||||
state.shutdown.cancel();
|
||||
timeout(Duration::from_secs(1), task)
|
||||
.await
|
||||
.expect("control server should stop")
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_server_control_stats_track_authenticated_session_lifecycle() {
|
||||
let probe = UdpSocket::bind("127.0.0.1:0").await.unwrap();
|
||||
let port = probe.local_addr().unwrap().port();
|
||||
drop(probe);
|
||||
|
||||
let config = Config::from_str(&format!(
|
||||
"[server]\nlisten='127.0.0.1'\n[backend]\ntarget='127.0.0.1:9'\n[transports.direct_udp]\nport={port}\n[[auth.clients]]\nid='runtime-test'\nsecret='{DIRECT_SECRET}'\n"
|
||||
))
|
||||
.unwrap()
|
||||
.into_server()
|
||||
.unwrap();
|
||||
let client_config = Config::from_str(&format!(
|
||||
"[client]\nserver='127.0.0.1'\nid='runtime-test'\nsecret='{DIRECT_SECRET}'\n[transport.direct_udp]\nport={port}\n"
|
||||
))
|
||||
.unwrap()
|
||||
.into_client()
|
||||
.unwrap();
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let control_path = private_runtime_dir(dir.path()).join("direct-runtime.sock");
|
||||
let server_task = tokio::spawn(server::run_with_control_path(config, control_path.clone()));
|
||||
|
||||
let session = timeout(
|
||||
Duration::from_secs(3),
|
||||
DirectUdpSession::connect(&client_config),
|
||||
)
|
||||
.await
|
||||
.expect("direct handshake should complete")
|
||||
.unwrap();
|
||||
|
||||
wait_for_active_count(&control_path, 1).await;
|
||||
let status = control::send_command(&control_path, "status")
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(status.contains("active_sessions=1"));
|
||||
session.close().await.unwrap();
|
||||
wait_for_active_count(&control_path, 0).await;
|
||||
|
||||
let response = control::send_command(&control_path, "shutdown")
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(response.contains("\"ok\":true"));
|
||||
timeout(Duration::from_secs(3), server_task)
|
||||
.await
|
||||
.expect("server runtime should stop")
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn wait_for_active_count(path: &std::path::Path, expected: usize) {
|
||||
timeout(Duration::from_secs(4), async {
|
||||
loop {
|
||||
if let Ok(response) = control::send_command(path, "stats").await {
|
||||
if response.contains(&format!("\"active_sessions\":{expected}")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("server control stats did not report {expected} active sessions"));
|
||||
}
|
||||
Reference in new issue
Block a user