use log::{debug, info, warn};
use std::process::Command;
pub fn has_cap_net_raw() -> bool {
#[cfg(target_family = "unix")]
{
if let Ok(exe_path) = std::env::current_exe() {
if let Ok(output) = Command::new("getcap").arg(exe_path).output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
return output_str.contains("cap_net_raw");
}
}
}
false
}
#[cfg(not(target_family = "unix"))]
{
false
}
}
pub fn try_set_cap_net_raw() -> bool {
#[cfg(target_family = "unix")]
{
if let Ok(exe_path) = std::env::current_exe() {
info!(
"Attempting to set CAP_NET_RAW capability on {}",
exe_path.display()
);
let status = Command::new("sudo")
.arg("setcap")
.arg("cap_net_raw+ep")
.arg(&exe_path)
.status();
match status {
Ok(exit_status) if exit_status.success() => {
info!("Successfully set CAP_NET_RAW capability");
return true;
}
Ok(_) => {
warn!("Failed to set CAP_NET_RAW capability");
}
Err(e) => {
warn!("Error executing sudo command: {}", e);
}
}
} else {
warn!("Could not determine path to executable");
}
false
}
#[cfg(not(target_family = "unix"))]
{
false
}
}
pub fn ensure_cap_net_raw() -> bool {
#[cfg(target_family = "unix")]
{
if has_cap_net_raw() {
debug!("Binary already has CAP_NET_RAW capability");
return true;
}
if unsafe { libc::geteuid() == 0 } {
debug!("Running as root, no need to set capability");
return true;
}
info!("ICMP traceroute requires CAP_NET_RAW capability");
info!("Attempting to set capability (may prompt for sudo password)...");
if try_set_cap_net_raw() {
if let Ok(exe_path) = std::env::current_exe() {
info!("Re-executing with new capabilities: {}", exe_path.display());
let args: Vec<String> = std::env::args().collect();
let err = Command::new(&exe_path).args(&args[1..]).status();
match err {
Ok(status) => {
std::process::exit(status.code().unwrap_or(0));
}
Err(e) => {
warn!("Failed to re-execute binary: {}", e);
}
}
}
}
false
}
#[cfg(not(target_family = "unix"))]
{
true
}
}