move all "admin_netcat" stuff to a new admin_netcat module in the gateway-bin, not in the gateway lib

This commit is contained in:
Chris Beck
2025-12-05 03:45:50 -07:00
parent eee48100c1
commit ef1b5ed4ca
9 changed files with 137 additions and 72 deletions
+1
View File
@@ -19,6 +19,7 @@ rustls-tls = ["signal-gateway/rustls-tls"]
signal-gateway = { path = "../signal-gateway" }
conf = { workspace = true }
conf-extra = { workspace = true }
dotenvy = { workspace = true }
hyper = { workspace = true }
hyper-util = { workspace = true }
@@ -0,0 +1,78 @@
//! Admin netcat TCP client for forwarding messages to a TCP server
//!
//! This module handles admin messages not handled by the gateway by opening a TCP connection,
//! writing the message terminated with CRLF, and reading the response until CRLF.
use conf::Conf;
use signal_gateway::MessageHandlerResult;
use std::time::Duration;
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
net::TcpStream,
time::timeout,
};
/// Configuration for the admin netcat TCP client
#[derive(Clone, Conf, Debug)]
pub struct AdminNetcatConfig {
/// TCP address to forward admin commands to
#[conf(long, env)]
pub tcp_addr: String,
/// Timeout for connecting, writing, and reading
#[conf(long, env, default_value = "5s", value_parser = conf_extra::parse_duration)]
pub timeout: Duration,
}
impl AdminNetcatConfig {
/// Create a message handler function from this config.
///
/// The returned handler opens a TCP connection to the configured address,
/// writes the message terminated with CRLF, and reads the response until CRLF.
pub fn into_handler(
self,
) -> Box<
dyn Fn(
String,
)
-> std::pin::Pin<Box<dyn std::future::Future<Output = MessageHandlerResult> + Send>>
+ Send
+ Sync,
> {
Box::new(move |message: String| {
let config = self.clone();
Box::pin(async move { handle_message(&config, message).await })
})
}
}
/// Handle a message by forwarding it to the configured TCP server
async fn handle_message(config: &AdminNetcatConfig, message: String) -> MessageHandlerResult {
// Connect to server
let mut stream = timeout(config.timeout, TcpStream::connect(&config.tcp_addr))
.await
.map_err(|_| (504u16, "connecting: timeout".into()))?
.map_err(|err| (502u16, format!("connecting: {err}").into()))?;
// Write message with CRLF terminator
let message = format!("{message}\r\n");
timeout(config.timeout, stream.write_all(message.as_bytes()))
.await
.map_err(|_| (504u16, "writing: timeout".into()))?
.map_err(|err| (502u16, format!("writing: {err}").into()))?;
// Read response until CR
let mut reader = BufReader::new(stream);
let mut buf = Vec::new();
timeout(config.timeout, reader.read_until(b'\r', &mut buf))
.await
.map_err(|_| (504u16, "reading: timeout".into()))?
.map_err(|err| (502u16, format!("reading: {err}").into()))?;
// Convert to string and trim the trailing CR
let text = std::str::from_utf8(&buf)
.map_err(|err| (502u16, format!("utf8: {err}").into()))?
.trim_end_matches(['\r', '\n'])
.to_owned();
Ok((text, vec![]))
}
+7 -1
View File
@@ -9,6 +9,9 @@ use tokio_util::sync::CancellationToken;
use tracing::{error, info, warn};
use tracing_subscriber::EnvFilter;
mod admin_netcat;
use admin_netcat::AdminNetcatConfig;
mod syslog;
use syslog::SyslogUdpConfig;
@@ -22,6 +25,8 @@ struct Config {
http_listen_addr: SocketAddr,
#[conf(flatten, prefix)]
syslog_udp: Option<SyslogUdpConfig>,
#[conf(flatten, prefix)]
admin_netcat: Option<AdminNetcatConfig>,
#[conf(flatten)]
gateway: GatewayConfig,
}
@@ -68,7 +73,8 @@ async fn main() {
let token = CancellationToken::new();
let gateway = Arc::new(Gateway::new(config.gateway, token.clone()).await);
let message_handler = config.admin_netcat.map(|c| c.into_handler());
let gateway = Arc::new(Gateway::new(config.gateway, token.clone(), message_handler).await);
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
info!("Listening for http on {}", config.http_listen_addr);