use std::net::{SocketAddr, TcpListener};
use std::process::Command;
use procage_policy::Network;
use procage_proxy::{Protocol, Proxy, Run};
use crate::violation::{Sink, Violation, lock};
#[derive(Debug, Clone)]
pub(crate) struct Proxied {
proxy: Proxy,
protocols: Vec<(Protocol, u16)>,
}
impl Proxied {
pub(crate) fn new(proxy: &Proxy, net: &Network) -> Option<Proxied> {
let Network::Proxy { http, socks } = net else {
return None;
};
let protocols = [(Protocol::Http, http), (Protocol::Socks5, socks)]
.into_iter()
.filter_map(|(p, port)| port.map(|port| (p, port.get())))
.collect();
Some(Proxied {
proxy: proxy.clone(),
protocols,
})
}
#[cfg(target_os = "linux")]
pub(crate) fn protocols(&self) -> &[(Protocol, u16)] {
&self.protocols
}
#[cfg(target_os = "macos")]
pub(crate) fn bind(&self) -> std::io::Result<Vec<(Protocol, TcpListener)>> {
self.protocols
.iter()
.map(|&(p, _)| Ok((p, TcpListener::bind("127.0.0.1:0")?)))
.collect()
}
pub(crate) fn serve(
&self,
listeners: Vec<(Protocol, TcpListener)>,
record: Option<Sink>,
) -> Option<Run> {
self.proxy
.serve(listeners, move |refusal| {
if let Some(record) = &record {
lock(record).push(Violation::refused(&refusal));
}
})
.ok()
}
}
pub(crate) fn set_env(command: &mut Command, addrs: &[(Protocol, SocketAddr)]) {
let url = |p: Protocol| {
addrs.iter().find(|(q, _)| *q == p).map(|(_, a)| match p {
Protocol::Http => format!("http://{a}"),
Protocol::Socks5 => format!("socks5h://{a}"),
})
};
let http = url(Protocol::Http);
let all = url(Protocol::Socks5).or_else(|| http.clone());
for (names, value) in [
(["HTTP_PROXY", "http_proxy"], &http),
(["HTTPS_PROXY", "https_proxy"], &http),
(["ALL_PROXY", "all_proxy"], &all),
] {
if let Some(v) = value {
for name in names {
command.env(name, v);
}
}
}
for name in ["NO_PROXY", "no_proxy"] {
command.env_remove(name);
}
}