Files
unshell/ush-router/src/router.rs
T
Michael Mikovsky fcb3b2be17 feat: complete protocol spec and initial implementation
- Write PROTOCOL.md with full wire format spec and 8 real-world scenario
  analyses (reconnect, multi-operator, large files, AV evasion, router crash,
  malformed packets, future pivoting)

- Rewrite workspace structure:
  - unshell lib: protocol types (PacketHeader, TreeRequest/Response,
    HandshakeMessage/Ack), Transport trait, TcpTransport, Tree routing
  - ush-router: router binary with per-node threads, NodeRegistry with
    longest-prefix path matching, packet relay
  - ush-payload: implant binary with reconnect loop, module tree, InfoModule
  - ush-cli: operator REPL with rustyline, session management, command parser

- Protocol design: two-part rkyv frame [header][payload]; router reads only
  header for routing, payload bytes forwarded opaque

- All code documented with doc comments and examples
- Zero warnings, zero errors across entire workspace
- 32 tests pass (unit tests for tree routing, TCP transport, framing,
  command parsing, node registry)
2026-04-20 23:38:02 -06:00

50 lines
1.4 KiB
Rust

//! # Router Core
//!
//! The main accept loop. Binds a TCP listener and spawns a node thread for
//! each incoming connection.
use std::net::TcpListener;
use std::sync::{Arc, Mutex};
use crate::registry::NodeRegistry;
use crate::node::spawn_node;
/// Start the router, binding to `bind_addr` and accepting connections forever.
///
/// This function blocks until an unrecoverable error occurs.
///
/// # Errors
///
/// Returns an error if the bind fails (e.g., port already in use).
///
/// # Example
///
/// ```rust,no_run
/// ush_router::router::run("0.0.0.0:9000").expect("router failed");
/// ```
pub fn run(bind_addr: &str) -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(bind_addr)?;
eprintln!("[router] listening on {bind_addr}");
let registry = Arc::new(Mutex::new(NodeRegistry::new()));
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let addr = stream
.peer_addr()
.map(|a| a.to_string())
.unwrap_or_else(|_| "unknown".into());
eprintln!("[router] new connection from {addr}");
spawn_node(stream, Arc::clone(&registry));
}
Err(e) => {
eprintln!("[router] accept error: {e}");
// Non-fatal; keep accepting.
}
}
}
Ok(())
}