All checks were successful
CI / test (push) Successful in 1m24s
Server now binds on both IPv4 (0.0.0.0) and IPv6 (::) by default. Uses tokio::select! to accept from whichever listener has a connection. New flags: --listen <addr> IPv4 listen address (default: 0.0.0.0, "none" to disable) --listen6 <addr> IPv6 listen address (default: ::, "none" to disable) Examples: btest -s # listen on both v4 and v6 btest -s --listen6 none # IPv4 only btest -s --listen none # IPv6 only btest -s --listen 192.168.1.1 # specific IPv4 address Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
189 lines
4.6 KiB
Rust
189 lines
4.6 KiB
Rust
use std::time::Duration;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::TcpStream;
|
|
|
|
const SERVER_PORT: u16 = 13000;
|
|
|
|
async fn start_ecsrp5_server(port: u16) {
|
|
tokio::spawn(async move {
|
|
let _ = btest_rs::server::run_server(
|
|
port,
|
|
Some("testuser".into()),
|
|
Some("testpass".into()),
|
|
true,
|
|
Some("127.0.0.1".into()),
|
|
None,
|
|
)
|
|
.await;
|
|
});
|
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
}
|
|
|
|
async fn start_md5_server(port: u16) {
|
|
tokio::spawn(async move {
|
|
let _ = btest_rs::server::run_server(
|
|
port,
|
|
Some("testuser".into()),
|
|
Some("testpass".into()),
|
|
false,
|
|
Some("127.0.0.1".into()),
|
|
None,
|
|
)
|
|
.await;
|
|
});
|
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
}
|
|
|
|
async fn start_noauth_server(port: u16) {
|
|
tokio::spawn(async move {
|
|
let _ = btest_rs::server::run_server(port, None, None, false, Some("127.0.0.1".into()), None).await;
|
|
});
|
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ecsrp5_server_sends_03_response() {
|
|
let port = SERVER_PORT;
|
|
start_ecsrp5_server(port).await;
|
|
|
|
let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))
|
|
.await
|
|
.unwrap();
|
|
|
|
// Read HELLO
|
|
let mut buf = [0u8; 4];
|
|
stream.read_exact(&mut buf).await.unwrap();
|
|
assert_eq!(buf, [0x01, 0x00, 0x00, 0x00]);
|
|
|
|
// Send command (TCP, server TX)
|
|
let cmd = btest_rs::protocol::Command::new(
|
|
btest_rs::protocol::CMD_PROTO_TCP,
|
|
btest_rs::protocol::CMD_DIR_TX,
|
|
);
|
|
stream.write_all(&cmd.serialize()).await.unwrap();
|
|
stream.flush().await.unwrap();
|
|
|
|
// Should receive EC-SRP5 auth required
|
|
stream.read_exact(&mut buf).await.unwrap();
|
|
assert_eq!(buf, [0x03, 0x00, 0x00, 0x00], "Expected EC-SRP5 auth response");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ecsrp5_full_client_auth() {
|
|
let port = SERVER_PORT + 1;
|
|
start_ecsrp5_server(port).await;
|
|
|
|
// Use our client with EC-SRP5
|
|
let handle = tokio::spawn(async move {
|
|
btest_rs::client::run_client(
|
|
"127.0.0.1",
|
|
port,
|
|
btest_rs::protocol::CMD_DIR_TX, // server TX = client RX
|
|
false,
|
|
0,
|
|
0,
|
|
Some("testuser".into()),
|
|
Some("testpass".into()),
|
|
false,
|
|
)
|
|
.await
|
|
});
|
|
|
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
|
handle.abort();
|
|
// If we got here without panic, EC-SRP5 auth + data transfer worked
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ecsrp5_wrong_password_fails() {
|
|
let port = SERVER_PORT + 2;
|
|
start_ecsrp5_server(port).await;
|
|
|
|
let result = btest_rs::client::run_client(
|
|
"127.0.0.1",
|
|
port,
|
|
btest_rs::protocol::CMD_DIR_TX,
|
|
false,
|
|
0,
|
|
0,
|
|
Some("testuser".into()),
|
|
Some("wrongpass".into()),
|
|
false,
|
|
)
|
|
.await;
|
|
|
|
assert!(result.is_err(), "Wrong password should fail");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_md5_auth_still_works() {
|
|
let port = SERVER_PORT + 3;
|
|
start_md5_server(port).await;
|
|
|
|
let handle = tokio::spawn(async move {
|
|
btest_rs::client::run_client(
|
|
"127.0.0.1",
|
|
port,
|
|
btest_rs::protocol::CMD_DIR_TX,
|
|
false,
|
|
0,
|
|
0,
|
|
Some("testuser".into()),
|
|
Some("testpass".into()),
|
|
false,
|
|
)
|
|
.await
|
|
});
|
|
|
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
handle.abort();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_noauth_still_works() {
|
|
let port = SERVER_PORT + 4;
|
|
start_noauth_server(port).await;
|
|
|
|
let handle = tokio::spawn(async move {
|
|
btest_rs::client::run_client(
|
|
"127.0.0.1",
|
|
port,
|
|
btest_rs::protocol::CMD_DIR_TX,
|
|
false,
|
|
0,
|
|
0,
|
|
None,
|
|
None,
|
|
false,
|
|
)
|
|
.await
|
|
});
|
|
|
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
handle.abort();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ecsrp5_udp_bidirectional() {
|
|
let port = SERVER_PORT + 5;
|
|
start_ecsrp5_server(port).await;
|
|
|
|
let handle = tokio::spawn(async move {
|
|
btest_rs::client::run_client(
|
|
"127.0.0.1",
|
|
port,
|
|
btest_rs::protocol::CMD_DIR_BOTH,
|
|
true, // UDP
|
|
0,
|
|
0,
|
|
Some("testuser".into()),
|
|
Some("testpass".into()),
|
|
false,
|
|
)
|
|
.await
|
|
});
|
|
|
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
|
handle.abort();
|
|
}
|