Compare commits

..

2 Commits

Author SHA1 Message Date
sad
ff3fc643c1 clippy clip 2024-10-07 17:26:11 +00:00
sad
2f15b601bc fix some issue with server/client ident 2024-10-07 17:25:38 +00:00
3 changed files with 438 additions and 38 deletions

332
Cargo.lock generated Normal file
View File

@ -0,0 +1,332 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "blackwall"
version = "0.1.0"
dependencies = [
"colored",
"pnet",
]
[[package]]
name = "colored"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
dependencies = [
"lazy_static",
"windows-sys",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "ipnetwork"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e"
dependencies = [
"serde",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "no-std-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]]
name = "pnet"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d"
dependencies = [
"ipnetwork",
"pnet_base",
"pnet_datalink",
"pnet_packet",
"pnet_sys",
"pnet_transport",
]
[[package]]
name = "pnet_base"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7"
dependencies = [
"no-std-net",
]
[[package]]
name = "pnet_datalink"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7"
dependencies = [
"ipnetwork",
"libc",
"pnet_base",
"pnet_sys",
"winapi",
]
[[package]]
name = "pnet_macros"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "pnet_macros_support"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab"
dependencies = [
"pnet_base",
]
[[package]]
name = "pnet_packet"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f"
dependencies = [
"glob",
"pnet_base",
"pnet_macros",
"pnet_macros_support",
]
[[package]]
name = "pnet_sys"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "pnet_transport"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf"
dependencies = [
"libc",
"pnet_base",
"pnet_packet",
"pnet_sys",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View File

@ -1,3 +1,4 @@
# BlackWall
A WIP next gen firewall for Linux.
![blackwall demo](/docs/demo.png)

View File

@ -1,10 +1,10 @@
use colored::*;
use pnet::datalink;
use std::fs::{File, read_dir};
use std::io::{BufRead, BufReader};
use std::collections::{HashMap, HashSet};
use std::fs::{read_dir, read_to_string, File};
use std::io::{BufRead, BufReader};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::Path;
use colored::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum PortType {
@ -33,7 +33,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let open_ports = get_open_ports()?;
for interface in interfaces {
println!("{}", format!("Interface: {}", interface.name).green().bold());
println!(
"{}",
format!("Interface: {}", interface.name).green().bold()
);
let mut interface_ports: HashSet<PortInfo> = HashSet::new();
@ -48,18 +51,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if !interface_ports.is_empty() {
println!(" {}", "Open ports:".yellow());
for port in interface_ports {
let port_type_color = if port.port_type == PortType::TCP { "TCP".blue() } else { "UDP".magenta() };
let port_type_color = if port.port_type == PortType::TCP {
"TCP".blue()
} else {
"UDP".magenta()
};
let conn_type_color = match port.connection_type {
ConnectionType::Server => "Server".green(),
ConnectionType::Client => "Client".yellow(),
ConnectionType::Unknown => "Unknown".red(),
};
println!(" {} ({}) - {} - {} ({})",
port.number.to_string().white().bold(),
port_type_color,
port.process_name.cyan(),
conn_type_color,
port.state.white());
println!(
" {} ({}) - {} - {} ({})",
port.number.to_string().white().bold(),
port_type_color,
port.process_name.cyan(),
conn_type_color,
port.state.white()
);
}
} else {
println!(" {}", "No open ports found".red());
@ -80,7 +89,11 @@ fn get_open_ports() -> Result<HashMap<IpAddr, HashSet<PortInfo>>, Box<dyn std::e
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if let Some(pid) = path.file_name().and_then(|n| n.to_str()).and_then(|s| s.parse::<u32>().ok()) {
if let Some(pid) = path
.file_name()
.and_then(|n| n.to_str())
.and_then(|s| s.parse::<u32>().ok())
{
let fd_dir = path.join("fd");
if fd_dir.is_dir() {
for fd_entry in read_dir(fd_dir)? {
@ -89,7 +102,9 @@ fn get_open_ports() -> Result<HashMap<IpAddr, HashSet<PortInfo>>, Box<dyn std::e
if let Ok(target) = std::fs::read_link(&fd_path) {
let target_str = target.to_string_lossy();
if target_str.starts_with("socket:[") {
let inode = target_str.trim_start_matches("socket:[").trim_end_matches(']');
let inode = target_str
.trim_start_matches("socket:[")
.trim_end_matches(']');
inode_to_pid.insert(inode.to_string(), pid);
}
}
@ -100,10 +115,34 @@ fn get_open_ports() -> Result<HashMap<IpAddr, HashSet<PortInfo>>, Box<dyn std::e
}
// Parse TCP and UDP connections
parse_connections("/proc/net/tcp", &mut open_ports, &inode_to_pid, PortType::TCP, false)?;
parse_connections("/proc/net/tcp6", &mut open_ports, &inode_to_pid, PortType::TCP, true)?;
parse_connections("/proc/net/udp", &mut open_ports, &inode_to_pid, PortType::UDP, false)?;
parse_connections("/proc/net/udp6", &mut open_ports, &inode_to_pid, PortType::UDP, true)?;
parse_connections(
"/proc/net/tcp",
&mut open_ports,
&inode_to_pid,
PortType::TCP,
false,
)?;
parse_connections(
"/proc/net/tcp6",
&mut open_ports,
&inode_to_pid,
PortType::TCP,
true,
)?;
parse_connections(
"/proc/net/udp",
&mut open_ports,
&inode_to_pid,
PortType::UDP,
false,
)?;
parse_connections(
"/proc/net/udp6",
&mut open_ports,
&inode_to_pid,
PortType::UDP,
true,
)?;
Ok(open_ports)
}
@ -113,7 +152,7 @@ fn parse_connections(
open_ports: &mut HashMap<IpAddr, HashSet<PortInfo>>,
inode_to_pid: &HashMap<String, u32>,
port_type: PortType,
is_ipv6: bool
is_ipv6: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
@ -129,13 +168,14 @@ fn parse_connections(
let address_parts: Vec<&str> = local_address.split(':').collect();
if address_parts.len() == 2 {
let ip = if is_ipv6 {
IpAddr::V6(parse_ipv6(&address_parts[0])?)
IpAddr::V6(parse_ipv6(address_parts[0])?)
} else {
IpAddr::V4(parse_ipv4(&address_parts[0])?)
IpAddr::V4(parse_ipv4(address_parts[0])?)
};
let port = u16::from_str_radix(address_parts[1], 16)?;
let process_name = get_process_name(inode_to_pid.get(inode))?;
let (connection_type, state) = determine_connection_type(&port_type, state_hex, local_address, remote_address);
let (connection_type, state) =
determine_connection_type(&port_type, state_hex, local_address, remote_address);
let port_info = PortInfo {
number: port,
port_type: port_type.clone(),
@ -143,14 +183,22 @@ fn parse_connections(
connection_type,
state,
};
open_ports.entry(ip).or_insert_with(HashSet::new).insert(port_info);
open_ports
.entry(ip)
.or_default()
.insert(port_info);
}
}
}
Ok(())
}
fn determine_connection_type(port_type: &PortType, state_hex: &str, local_address: &str, remote_address: &str) -> (ConnectionType, String) {
fn determine_connection_type(
port_type: &PortType,
state_hex: &str,
local_address: &str,
remote_address: &str,
) -> (ConnectionType, String) {
let state = u8::from_str_radix(state_hex, 16).unwrap_or(0);
match port_type {
@ -158,13 +206,15 @@ fn determine_connection_type(port_type: &PortType, state_hex: &str, local_addres
match state {
1 => {
// ESTABLISHED: Check if the remote port is 0 (unlikely for a client)
let remote_port = u16::from_str_radix(remote_address.split(':').last().unwrap_or("0"), 16).unwrap_or(0);
let remote_port =
u16::from_str_radix(remote_address.split(':').last().unwrap_or("0"), 16)
.unwrap_or(0);
if remote_port == 0 {
(ConnectionType::Server, "ESTABLISHED".to_string())
} else {
(ConnectionType::Client, "ESTABLISHED".to_string())
}
},
}
2 => (ConnectionType::Client, "SYN_SENT".to_string()),
3 => (ConnectionType::Server, "SYN_RECV".to_string()),
4 => (ConnectionType::Unknown, "FIN_WAIT1".to_string()),
@ -177,26 +227,43 @@ fn determine_connection_type(port_type: &PortType, state_hex: &str, local_addres
11 => (ConnectionType::Unknown, "CLOSING".to_string()),
_ => (ConnectionType::Unknown, format!("UNKNOWN ({})", state)),
}
},
}
PortType::UDP => {
if remote_address == "00000000:0000" {
(ConnectionType::Server, "UNCONN".to_string())
} else {
(ConnectionType::Client, "ESTABLISHED".to_string())
}
},
}
}
}
fn get_process_name(pid: Option<&u32>) -> Result<String, Box<dyn std::error::Error>> {
match pid {
Some(&pid) => {
let comm_path = Path::new("/proc").join(pid.to_string()).join("comm");
let mut name = std::fs::read_to_string(comm_path)?;
name.truncate(name.trim_end().len());
Ok(name)
},
let cmdline_path = Path::new("/proc").join(pid.to_string()).join("cmdline");
// Try to read from comm file first
if let Ok(mut name) = read_to_string(&comm_path) {
name.truncate(name.trim_end().len());
if !name.is_empty() {
return Ok(name);
}
}
// If comm is empty or unreadable, try cmdline
if let Ok(cmdline) = read_to_string(&cmdline_path) {
let parts: Vec<&str> = cmdline.split('\0').collect();
if !parts.is_empty() {
if let Some(name) = Path::new(parts[0]).file_name() {
return Ok(name.to_string_lossy().into_owned());
}
}
}
Ok("Unknown".to_string())
}
None => Ok("Unknown".to_string()),
}
}