Skip to content

Commit 2913375

Browse files
fix: clarify CLI tunnel startup
1 parent 8c114f7 commit 2913375

6 files changed

Lines changed: 79 additions & 39 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ homepage = "https://github.com/Programmer-Red/LabStreamGate"
99
license = "MIT"
1010
repository = "https://github.com/Programmer-Red/LabStreamGate"
1111
rust-version = "1.95"
12-
version = "0.6.3"
12+
version = "0.6.4"
1313

1414
[profile.release]
1515
codegen-units = 1

‎README.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ LabStreamGate 是 xujclab 靶机的本地连接器。它会把题目页给出的
2020
3. 点击「使用 LabStreamGate 连接」,首次使用时在客户端中允许 xujclab。
2121
4. 按题目页显示的本机地址连接,例如 `127.0.0.1:32123`。
2222

23+
### 使用 CLI
24+
25+
CLI 不需要 `sudo`。将题目页提供的完整 WSS 地址粘贴到命令末尾:
26+
27+
```bash
28+
./labstreamgate connect 'wss://chall.xujclab.com/你的通道ID'
29+
```
30+
31+
启动后终端会显示一个本地连接地址,例如 `127.0.0.1:54813`。保持 CLI 运行,再让浏览器、nc、SSH 或调试器连接这个本地地址。CLI 会一直等待本地连接,这是正常状态;按 `Ctrl+C` 退出。
32+
2333
macOS 如果提示应用无法打开,先将应用拖入「应用程序」,再执行:
2434

2535
```bash

‎crates/wsrx/src/cli/connect.rs‎

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use std::sync::Arc;
1+
use std::{sync::Arc, time::Duration};
22

3-
use tokio::net::{TcpListener, TcpStream};
3+
use tokio::{
4+
net::{TcpListener, TcpStream},
5+
time::timeout,
6+
};
47
use tokio_util::sync::CancellationToken;
5-
use tracing::{debug, error, info, warn};
8+
use tracing::{debug, error, info};
69
use url::Url;
710
use wsrx::proxy;
811

@@ -13,55 +16,75 @@ pub async fn launch(
1316
) {
1417
let log_json = log_json.unwrap_or(false);
1518
init_logger(log_json);
16-
let port = port.unwrap_or(0);
17-
let host = host.unwrap_or(String::from("127.0.0.1"));
18-
let listener = TcpListener::bind(format!("{host}:{port}"))
19-
.await
20-
.expect("failed to bind port");
21-
let Ok(url) = Url::parse(&address) else {
22-
error!("Invalid url, please check your input.");
19+
let Ok(parsed_url) = Url::parse(&address) else {
20+
eprintln!("连接地址无效,请粘贴题目页提供的完整 ws:// 或 wss:// 地址。");
2321
return;
2422
};
25-
if url.scheme() != "ws" && url.scheme() != "wss" {
26-
error!("Invalid url scheme, only `ws` and `wss` are supported.");
23+
if parsed_url.scheme() != "ws" && parsed_url.scheme() != "wss" {
24+
eprintln!("连接地址协议无效,只支持 ws:// 或 wss://。");
2725
return;
2826
}
29-
let url = url.as_ref().to_string();
30-
info!(
31-
"Hi, I am not RX, RX is here -> {}",
32-
listener.local_addr().unwrap()
33-
);
34-
warn!(
35-
"wsrx will not report non-critical errors by default, you can set `RUST_LOG=wsrx=debug` to see more details."
36-
);
27+
28+
let port = port.unwrap_or(0);
29+
let host = host.unwrap_or(String::from("127.0.0.1"));
30+
let listener = match TcpListener::bind(format!("{host}:{port}")).await {
31+
Ok(listener) => listener,
32+
Err(err) => {
33+
eprintln!("无法创建本地监听地址 {host}:{port}:{err}");
34+
return;
35+
}
36+
};
37+
let local_addr = listener
38+
.local_addr()
39+
.expect("failed to read local listener address");
40+
let url = parsed_url.as_ref().to_string();
41+
42+
println!("LabStreamGate CLI 已启动");
43+
println!("本地连接地址:{local_addr}");
44+
println!("远端通道:{url}");
45+
println!("请保持此进程运行,并让浏览器、nc、SSH 或调试器连接上面的本地地址。");
46+
println!("无需使用 sudo;按 Ctrl+C 退出。\n");
47+
info!(local = %local_addr, remote = %url, "local tunnel listener started");
3748

3849
let token = CancellationToken::new();
3950
let url = Arc::new(url);
4051

41-
// This loop will "run forever"
4252
loop {
43-
let Ok((tcp, _)) = listener.accept().await else {
44-
error!("Failed to accept tcp connection, exiting.");
45-
token.cancel();
46-
return;
53+
let accepted = tokio::select! {
54+
result = listener.accept() => result,
55+
result = tokio::signal::ctrl_c() => {
56+
if let Err(err) = result {
57+
error!("failed to listen for Ctrl+C: {err}");
58+
}
59+
println!("\nLabStreamGate CLI 已退出。");
60+
token.cancel();
61+
return;
62+
}
63+
};
64+
let (tcp, peer_addr) = match accepted {
65+
Ok(connection) => connection,
66+
Err(err) => {
67+
eprintln!("本地监听失败,LabStreamGate CLI 已退出:{err}");
68+
token.cancel();
69+
return;
70+
}
4771
};
4872

4973
if token.is_cancelled() {
5074
return;
5175
}
5276

5377
let url = url.clone();
54-
let peer_addr = tcp.peer_addr().unwrap();
55-
56-
info!("CREATE remote <-wsrx-> {}", peer_addr);
78+
println!("收到本地连接 {peer_addr},正在连接远端通道……");
79+
info!(remote = %url, peer = %peer_addr, "opening remote tunnel connection");
5780

5881
let token = token.clone();
5982
tokio::spawn(async move {
6083
match proxy_ws_addr(url.as_ref(), tcp, token).await {
61-
Ok(_) => {}
84+
Ok(_) => println!("本地连接 {peer_addr} 已关闭。"),
6285
Err(e) => {
63-
info!("REMOVE remote <-wsrx-> {} with error", peer_addr);
64-
debug!("TCP connection closed: {}", e);
86+
eprintln!("连接远端通道失败(本地连接 {peer_addr}):{e}");
87+
debug!(peer = %peer_addr, error = %e, "TCP connection closed with error");
6588
}
6689
}
6790
});
@@ -72,8 +95,15 @@ async fn proxy_ws_addr(
7295
addr: impl AsRef<str>, tcp: TcpStream, token: CancellationToken,
7396
) -> Result<(), wsrx::Error> {
7497
let peer_addr = tcp.peer_addr().unwrap();
75-
let (ws, _) = tokio_tungstenite::connect_async(addr.as_ref()).await?;
98+
let (ws, _) = timeout(
99+
Duration::from_secs(15),
100+
tokio_tungstenite::connect_async(addr.as_ref()),
101+
)
102+
.await
103+
.map_err(|_| {
104+
std::io::Error::new(std::io::ErrorKind::TimedOut, "连接远端通道超时(15 秒)")
105+
})??;
76106
proxy(ws.into(), tcp, token).await?;
77-
info!("REMOVE remote <-wsrx-> {}", peer_addr);
107+
info!(peer = %peer_addr, "remote tunnel connection closed");
78108
Ok(())
79109
}

‎crates/wsrx/src/cli/logger.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ pub fn init_logger(json: bool) {
66
tracing_subscriber::registry()
77
.with(
88
tracing_subscriber::EnvFilter::try_from_default_env()
9-
.unwrap_or_else(|_| "wsrx=info,tower_http=info".into()),
9+
.unwrap_or_else(|_| "labstreamgate=info,wsrx=info,tower_http=info".into()),
1010
)
1111
.with(tracing_subscriber::fmt::layer().json())
1212
.init();
1313
} else {
1414
tracing_subscriber::registry()
1515
.with(
1616
tracing_subscriber::EnvFilter::try_from_default_env()
17-
.unwrap_or_else(|_| "wsrx=info,tower_http=info".into()),
17+
.unwrap_or_else(|_| "labstreamgate=info,wsrx=info,tower_http=info".into()),
1818
)
1919
.with(tracing_subscriber::fmt::layer())
2020
.init();

‎windows/setup.nsi‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Unicode True
1111
!define DESCRIPTION "Controlled TCP-over-WebSocket forwarding tunnel."
1212
!define VERSIONMAJOR 0
1313
!define VERSIONMINOR 6
14-
!define VERSIONBUILD 3
14+
!define VERSIONBUILD 4
1515
!define INSTALL_DIR_DEFAULT "$PROGRAMFILES64\${APPNAME}"
1616
!define MUI_ABORTWARNING
1717
!define MUI_FINISHPAGE_RUN

0 commit comments

Comments
 (0)