add --verbose flag and TCP RST on connection failure
Server (gatunad): - New --verbose/-v flag: logs lifecycle events at info level (DISCOVER, MANIFEST sent, OPEN, session established, CLOSE, OPEN_NAK). Without the flag, errors only as before. Client (gatuna): - OPEN_NAK and session CLOSE now send TCP RST to the local app instead of a graceful FIN. LingerOption(true, 0) causes Winsock to emit RST on close. The local app (e.g. ssh) sees a broken connection instead of a clean close, which is more honest about what happened (upstream was unreachable).
This commit is contained in:
+12
-2
@@ -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<String>,
|
||||
/// 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 } => {
|
||||
|
||||
Reference in New Issue
Block a user