85 lines
2.8 KiB
Rust
85 lines
2.8 KiB
Rust
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<OsString> = std::env::args_os().skip(1).collect();
|
|
match parse_egress_proxy(&args) {
|
|
Some(code) => code,
|
|
None => run_tool(&args),
|
|
}
|
|
}
|
|
|
|
/// The `egress-proxy --socket <path> --allow <list>` 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<ExitCode> {
|
|
// 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 <path> --allow <list>`, 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()
|
|
}
|