feat: remote version query — wzp-client --version-check <relay>
Some checks failed
Mirror to GitHub / mirror (push) Failing after 1m32s
Build Release Binaries / build-amd64 (push) Failing after 2m16s

Connects to a relay over QUIC with SNI "version", reads build hash
from a unidirectional stream, prints "<relay> <git-hash>" and exits.

Usage: wzp-client --version-check 172.16.81.175:4434
Output: 172.16.81.175:4434 8dbda3e

Relay side: detects "version" SNI, opens uni stream, writes
BUILD_GIT_HASH, waits 100ms for client to read, closes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-08 12:47:37 +04:00
parent 8dbda3e052
commit 5c24adf1c1
2 changed files with 35 additions and 1 deletions

View File

@@ -47,6 +47,7 @@ struct CliArgs {
room: Option<String>,
token: Option<String>,
_metrics_file: Option<String>,
version_check: bool,
}
impl CliArgs {
@@ -88,6 +89,7 @@ fn parse_args() -> CliArgs {
let mut room = None;
let mut token = None;
let mut metrics_file = None;
let mut version_check = false;
let mut relay_str = None;
let mut i = 1;
@@ -169,6 +171,7 @@ fn parse_args() -> CliArgs {
);
}
"--sweep" => sweep = true,
"--version-check" => { version_check = true; }
"--help" | "-h" => {
eprintln!("Usage: wzp-client [options] [relay-addr]");
eprintln!();
@@ -221,6 +224,7 @@ fn parse_args() -> CliArgs {
room,
token,
_metrics_file: metrics_file,
version_check,
}
}
@@ -239,6 +243,26 @@ async fn main() -> anyhow::Result<()> {
return Ok(());
}
// --version-check: query relay version over QUIC and exit
if cli.version_check {
let client_config = wzp_transport::client_config();
let bind_addr: SocketAddr = "0.0.0.0:0".parse()?;
let endpoint = wzp_transport::create_endpoint(bind_addr, None)?;
let conn = wzp_transport::connect(&endpoint, cli.relay_addr, "version", client_config).await?;
match conn.accept_uni().await {
Ok(mut recv) => {
let data = recv.read_to_end(256).await.unwrap_or_default();
let version = String::from_utf8_lossy(&data);
println!("{} {}", cli.relay_addr, version.trim());
}
Err(e) => {
eprintln!("relay {} does not support version query: {e}", cli.relay_addr);
}
}
endpoint.close(0u32.into(), b"done");
return Ok(());
}
let seed = cli.resolve_seed();
info!(

View File

@@ -490,12 +490,22 @@ async fn main() -> anyhow::Result<()> {
let transport = Arc::new(wzp_transport::QuinnTransport::new(connection));
// Ping connections: client just measures QUIC connect RTT.
// No handshake, no streams — client closes immediately after connecting.
if room_name == "ping" {
info!(%addr, "ping connection (RTT probe)");
return;
}
// Version query: respond with build hash over a uni stream.
if room_name == "version" {
if let Ok(mut send) = transport.connection().open_uni().await {
let _ = send.write_all(BUILD_GIT_HASH.as_bytes()).await;
let _ = send.finish();
// Wait for client to read before closing
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
return;
}
// Probe connections use SNI "_probe" to identify themselves.
// They skip auth + handshake and just do Ping->Pong + presence gossip.
if room_name == "_probe" {