prt_core/core/
firewall.rs1use std::net::IpAddr;
10use std::process::Command;
11
12pub fn block_command(ip: IpAddr) -> String {
14 if cfg!(target_os = "linux") {
15 format!("sudo iptables -A INPUT -s {ip} -j DROP")
16 } else if cfg!(target_os = "macos") {
17 format!("sudo pfctl -t prt_blocked -T add {ip}")
18 } else {
19 format!("# unsupported platform: block {ip}")
20 }
21}
22
23pub fn unblock_command(ip: IpAddr) -> String {
25 if cfg!(target_os = "linux") {
26 format!("sudo iptables -D INPUT -s {ip} -j DROP")
27 } else if cfg!(target_os = "macos") {
28 format!("sudo pfctl -t prt_blocked -T delete {ip}")
29 } else {
30 format!("# unsupported platform: unblock {ip}")
31 }
32}
33
34pub fn execute_block(ip: IpAddr, sudo_password: Option<&str>) -> Result<(), String> {
37 let ip_str = ip.to_string();
38 let (cmd, args) = if cfg!(target_os = "linux") {
39 (
40 "iptables",
41 vec!["-A", "INPUT", "-s", ip_str.as_str(), "-j", "DROP"],
42 )
43 } else if cfg!(target_os = "macos") {
44 (
45 "pfctl",
46 vec!["-t", "prt_blocked", "-T", "add", ip_str.as_str()],
47 )
48 } else {
49 return Err("unsupported platform".into());
50 };
51
52 let output = if let Some(password) = sudo_password {
53 Command::new("sudo")
54 .args(["-S", cmd])
55 .args(&args)
56 .stdin(std::process::Stdio::piped())
57 .stdout(std::process::Stdio::piped())
58 .stderr(std::process::Stdio::piped())
59 .spawn()
60 .and_then(|mut child| {
61 use std::io::Write;
62 if let Some(ref mut stdin) = child.stdin {
63 let _ = writeln!(stdin, "{password}");
64 }
65 child.wait_with_output()
66 })
67 .map_err(|e| e.to_string())?
68 } else {
69 Command::new("sudo")
70 .args(["-n", cmd])
71 .args(&args)
72 .output()
73 .map_err(|e| e.to_string())?
74 };
75
76 if output.status.success() {
77 Ok(())
78 } else {
79 let stderr = String::from_utf8_lossy(&output.stderr);
80 Err(format!("command failed: {}", stderr.trim()))
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use std::net::{Ipv4Addr, Ipv6Addr};
88
89 #[test]
90 fn block_command_ipv4() {
91 let cmd = block_command(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)));
92 assert!(cmd.contains("10.0.0.1"));
93 assert!(cmd.contains("sudo"));
94 }
95
96 #[test]
97 fn block_command_ipv6() {
98 let cmd = block_command(IpAddr::V6(Ipv6Addr::LOCALHOST));
99 assert!(cmd.contains("::1"));
100 }
101
102 #[test]
103 fn unblock_command_contains_ip() {
104 let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
105 let cmd = unblock_command(ip);
106 assert!(cmd.contains("192.168.1.1"));
107 }
108
109 #[test]
110 fn block_and_unblock_are_different() {
111 let ip = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1));
112 assert_ne!(block_command(ip), unblock_command(ip));
113 }
114}