From 2fe2e8cdbc3bb520994ff0944188f2fa657c546a Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 17:49:08 +0100 Subject: v4.0.0: require HELLO MAJOR.MINOR lockstep before auth --- src/bin/client/connection.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'src/bin/client') diff --git a/src/bin/client/connection.rs b/src/bin/client/connection.rs index 7516836..6bc9b09 100644 --- a/src/bin/client/connection.rs +++ b/src/bin/client/connection.rs @@ -8,6 +8,7 @@ use std::thread; use std::time::{Duration, Instant}; use zagan_bet::client::database; use zagan_bet::client::ui::first_contact_lines; +use zagan_bet::version::{is_newer, parse_hello, VERSION}; static UNSAFE_MODE: Mutex = Mutex::new(false); @@ -69,16 +70,35 @@ pub fn connect( let mut tls = tls; tls.set_read_timeout(Some(Duration::from_secs(10)))?; + // Version lockstep: the first line after TLS is the handshake, and no + // credentials are sent until the server accepts it. let start = Instant::now(); - tls.write_line("PING")?; + tls.write_line(&format!("HELLO {VERSION}"))?; let mut response = String::new(); - tls.read_line(&mut response)?; + if tls.read_line(&mut response)? == 0 { + return Err("server closed the connection during HELLO".into()); + } let elapsed = start.elapsed(); + let response = response.trim(); + + if let Some(message) = response.strip_prefix("ERROR") { + return Err(message.trim().to_string().into()); + } + let Some(hello) = parse_hello(response) else { + return Err(format!("unexpected HELLO reply: {response}").into()); + }; + println!( "\n\rConnected to {} with ping delay: {:.1}ms (TLS)", addr, elapsed.as_secs_f64() * 1000.0 ); + if is_newer(&hello.latest, VERSION) { + println!( + "A newer version is available: {} ({})", + hello.latest, hello.url + ); + } // Credentials are stored per server identity, so any address presenting // this certificate key can log in with the same stored account. -- cgit v1.2.3