use std::ffi::OsString; use std::io::Write; use std::os::unix::ffi::OsStrExt; use std::os::unix::net::UnixListener; use std::process::ExitCode; use std::sync::Arc; use toolkit::egress::{Allow, Proxy, SystemDial}; fn main() -> ExitCode { let args: Vec = std::env::args_os().skip(1).collect(); match parse_egress_proxy(&args) { Some(code) => code, None => run_tool(&args), } } /// The `egress-proxy --socket --allow ` form. Anything else, including a program name /// that is not `egress-proxy`, returns `None` so the tool form handles it. fn parse_egress_proxy(args: &[OsString]) -> Option { // Exactly five words: a longer list is not this form, and goes to the tool form (exit 2). if args.len() != 5 || args.first()?.as_bytes() != b"egress-proxy" { return None; } // The form is `egress-proxy --socket --allow `, in that order. let socket = match (args.get(1), args.get(2)) { (Some(flag), Some(path)) if flag.as_bytes() == b"--socket" => path, _ => return None, }; let list = match (args.get(3), args.get(4)) { (Some(flag), Some(value)) if flag.as_bytes() == b"--allow" => value, _ => return None, }; let list = match list.to_str() { Some(s) => s, None => { eprint!("toolkit: egress-proxy: --allow: not valid UTF-8"); return Some(ExitCode::from(2)); } }; let allow = match Allow::parse(list) { Ok(a) => a, Err(e) => { eprint!("toolkit: egress-proxy: --allow: {e}"); return Some(ExitCode::from(2)); } }; // The directory is fresh for each call, so a file already at the path is a mistake; do not // remove it first. let listener = match UnixListener::bind(socket) { Ok(l) => l, Err(e) => { eprint!( "toolkit: egress-proxy: cannot listen on {path}: {e}", path = display(socket) ); return Some(ExitCode::from(2)); } }; let proxy = Arc::new(Proxy::new(allow, Arc::new(SystemDial))); if let Err(e) = proxy.serve(listener) { eprint!("toolkit: egress-proxy: {e}"); return Some(ExitCode::from(2)); } Some(ExitCode::SUCCESS) } fn run_tool(args: &[OsString]) -> ExitCode { let mut rest = args.iter(); let name = match (rest.next(), rest.next()) { (Some(arg), None) => arg.to_str().unwrap_or("").to_string(), _ => String::new(), }; let outcome = toolkit::run(&name, &mut std::io::stdin().lock()); let _ = std::io::stdout().write_all(outcome.stdout.as_bytes()); let _ = std::io::stderr().write_all(outcome.stderr.as_bytes()); ExitCode::from(outcome.exit.code()) } fn display(o: &OsString) -> String { String::from_utf8_lossy(o.as_bytes()).into_owned() }