use proxy_watch::pac::{PacPolicy, PacRequirement, PacScript, requirement};
use proxy_watch::{Error, ProxyConfig, ProxyConfigSource, ProxyMode, ProxyWatcher, Url};
const SCRIPT: &str = "
function FindProxyForURL(url, host) {
// Unqualified names and the intranet never go through the proxy.
if (isPlainHostName(host)) { return 'DIRECT'; }
if (dnsDomainIs(host, '.corp.example')) { return 'DIRECT'; }
// Anything that resolves inside the corporate network is local too. With the
// default policy `dnsResolve` returns null and this test simply never fires.
var ip = dnsResolve(host);
if (ip != null && isInNet(ip, '10.0.0.0', '255.0.0.0')) { return 'DIRECT'; }
if (shExpMatch(url, 'ftp:*')) { return 'PROXY ftp-gw.corp.example:2121'; }
return 'PROXY edge.corp.example:8080; PROXY backup.corp.example:8080; DIRECT';
}";
const DEFAULT_URLS: &[&str] = &[
"http://intranet/",
"https://wiki.corp.example/start",
"ftp://files.example.net/pub",
"https://example.net/index.html",
];
fn without_credentials(url: &Url) -> String {
let mut safe = url.clone();
if safe.set_username("").is_err() || safe.set_password(None).is_err() {
return format!("<{} URL withheld: credentials not maskable>", url.scheme());
}
safe.into()
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
match ProxyWatcher::new() {
Ok(watcher) => {
let current = watcher.current();
println!("system effective mode: {:?}", current.effective);
match requirement(¤t.effective) {
PacRequirement::Fetch(url) => {
let url = without_credentials(url);
println!(" this machine needs the script at {url} fetched by you");
}
PacRequirement::Inline(script) => {
println!(
" this machine carries the script inline ({} bytes)",
script.len()
);
}
PacRequirement::Discover => println!(" WPAD is on; locating the script is yours"),
other => println!(" no PAC needed here ({other:?})"),
}
}
Err(error) => println!("system configuration unavailable: {error}"),
}
let pac_url = Url::parse("http://wpad.corp.example/proxy.pac")?;
let config =
ProxyConfig::from_source(ProxyConfigSource::Registry, ProxyMode::pac(pac_url.clone()));
let script = PacScript::new(SCRIPT);
let policy = if std::env::var_os("PAC_ALLOW_DNS").is_some() {
println!("\npolicy: DNS resolution ON, internal addresses allowed");
PacPolicy::new()
.with_dns_resolution(true)
.with_internal_addresses(true)
} else {
println!("\npolicy: default (no DNS, myIpAddress() = 127.0.0.1, 5 s budget)");
PacPolicy::new()
};
if let Err(Error::PacFetchRequired { url }) =
proxy_watch::resolve_with_pac(&config, &Url::parse("http://example.net/")?, None, &policy)
{
let url = without_credentials(&url);
println!("without a script body the crate asks for: {url}\n");
}
let args: Vec<String> = std::env::args().skip(1).collect();
let urls: Vec<&str> = if args.is_empty() {
DEFAULT_URLS.to_vec()
} else {
args.iter().map(String::as_str).collect()
};
for input in urls {
let url = Url::parse(input)?;
match proxy_watch::resolve_with_pac(&config, &url, Some(&script), &policy) {
Ok(steps) => {
let decision: Vec<String> = steps
.iter()
.map(|step| match step.endpoint() {
None => "DIRECT".to_owned(),
Some(endpoint) => match endpoint.scheme_hint {
Some(_) => endpoint.to_string(),
None => format!("{}://{endpoint}", step.scheme().unwrap_or("?")),
},
})
.collect();
println!("{input} -> {}", decision.join(", "));
}
Err(error) => println!("{input} -> error: {error}"),
}
}
Ok(())
}