use anyhow::{Context, Result};
use tokio::net::windows::named_pipe::{NamedPipeServer, ServerOptions};
use tracing::{debug, info, warn};
use super::{ListenerContext, serve_connection};
use crate::klp::auth::resolve_peer;
use crate::klp::security::PipeSecurity;
pub const PIPE_NAME: &str = r"\\.\pipe\kanade-agent";
pub fn spawn(ctx: ListenerContext) -> tokio::task::JoinHandle<Result<()>> {
tokio::spawn(run(ctx))
}
async fn run(ctx: ListenerContext) -> Result<()> {
let security = PipeSecurity::new().context("build KLP pipe SECURITY_DESCRIPTOR")?;
let mut server = unsafe {
ServerOptions::new()
.first_pipe_instance(true)
.create_with_security_attributes_raw(PIPE_NAME, security.as_ptr())
}
.with_context(|| format!("create Named Pipe {PIPE_NAME}"))?;
info!(
pipe = PIPE_NAME,
sd = "Authenticated Users RW, deny Anonymous",
"KLP listener ready",
);
loop {
if let Err(e) = server.connect().await {
warn!(error = %e, "KLP server.connect() failed; reseating listener");
server = create_with_retry(&security).await;
continue;
}
let next = create_with_retry(&security).await;
let connected = std::mem::replace(&mut server, next);
let task_ctx = ctx.clone();
tokio::spawn(async move {
if let Err(e) = handle_connection(connected, task_ctx).await {
warn!(error = %e, "KLP connection task failed");
}
});
}
}
async fn create_with_retry(security: &PipeSecurity) -> NamedPipeServer {
let mut delay_ms: u64 = 200;
loop {
let result = unsafe {
ServerOptions::new().create_with_security_attributes_raw(PIPE_NAME, security.as_ptr())
};
match result {
Ok(server) => return server,
Err(e) => {
warn!(
error = %e,
delay_ms,
pipe = PIPE_NAME,
"KLP create() failed; backing off and retrying",
);
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
delay_ms = (delay_ms * 2).min(30_000);
}
}
}
}
async fn handle_connection(pipe: NamedPipeServer, ctx: ListenerContext) -> Result<()> {
let peer = match resolve_peer(&pipe) {
Ok(p) => p,
Err(e) => {
warn!(error = %e, "KLP peer auth failed; closing connection");
return Ok(());
}
};
debug!(
user = %peer.user_display,
pid = peer.pid,
session = ?peer.session,
"KLP peer connected",
);
serve_connection(pipe, peer, ctx).await
}