diff --git a/gatuna-win/SessionManager.cs b/gatuna-win/SessionManager.cs index bebc73b..560a646 100644 --- a/gatuna-win/SessionManager.cs +++ b/gatuna-win/SessionManager.cs @@ -85,7 +85,7 @@ sealed class SessionManager : IDisposable lock (_openLock) { if (_pending != null) - _pending.Client.Dispose(); + NetUtil.RstClose(_pending.Client); } ProcessQueue(); break; @@ -546,7 +546,21 @@ sealed class Session( _cts.Cancel(); _deliverChannel.Writer.TryComplete(); SendClose(); - try { client.Dispose(); } catch { } + NetUtil.RstClose(client); _cts.Dispose(); } } + +/// Close a TcpClient with a TCP RST instead of a FIN. +static partial class NetUtil +{ + public static void RstClose(TcpClient c) + { + try + { + c.LingerState = new LingerOption(true, 0); + c.Close(); + } + catch { } + } +} diff --git a/gatunad/src/main.rs b/gatunad/src/main.rs index a661f04..5e39e2c 100644 --- a/gatunad/src/main.rs +++ b/gatunad/src/main.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use tokio::net::TcpStream; use tokio::sync::mpsc; use tokio::sync::Mutex as AsyncMutex; -use tracing::{error, Level}; +use tracing::{error, info, Level}; use crate::frame::{ Frame, REASON_CONNECT_FAILED, REASON_UNKNOWN_SESSION, REASON_UNKNOWN_UPSTREAM, @@ -33,12 +33,16 @@ struct Args { /// One or more TCP upstreams as PORT[:label], relayed to 127.0.0.1:PORT. #[arg(num_args = 1..)] ports: Vec, + /// Verbose logging (lifecycle events to stdout). + #[arg(short, long)] + verbose: bool, } #[tokio::main] async fn main() -> ExitCode { + let level = if args.verbose { Level::INFO } else { Level::ERROR }; tracing_subscriber::fmt() - .with_max_level(Level::ERROR) + .with_max_level(level) .with_writer(|| std::io::stdout()) .init(); @@ -141,13 +145,16 @@ async fn handle_frame( ) { match frame { Frame::Discover => { + info!("DISCOVER from {src:?}"); let manifest = Frame::Manifest { hostname: (**hostname).clone(), entries: table.entries(), }; let _ = tx.send((src, manifest.encode())).await; + info!("MANIFEST sent to {src:?} ({} upstreams)", table.0.len()); } Frame::Open { upstream_id, proto: _ } => { + info!("OPEN upstream {upstream_id} from {src:?}"); let upstream = table.get(upstream_id); match upstream { Some(upstream) => { @@ -176,10 +183,12 @@ async fn handle_frame( ); let ack = Frame::OpenAck { session_id: sid, upstream_id, proto }; let _ = tx.send((src, ack.encode())).await; + info!("session {sid} established (upstream {upstream_id})"); spawn_pump(r, sid, proto, src, tx, store); } Err(e) => { error!("connect 127.0.0.1:{port} failed: {e}"); + info!("OPEN_NAK upstream {upstream_id} (connect_failed) to {src:?}"); let nak = Frame::OpenNak { upstream_id, reason: REASON_CONNECT_FAILED }; let _ = tx.send((src, nak.encode())).await; @@ -215,6 +224,7 @@ async fn handle_frame( } } Frame::Close { session_id, reason: _ } => { + info!("CLOSE session {session_id} from {src:?}"); store.lock().expect("store poisoned").remove(&session_id); } Frame::Ping { nonce } => {