Finish verion 0.1.0

This commit is contained in:
DaZuo0122
2026-01-16 13:27:07 +08:00
parent 240107e00f
commit b63bcd405b
17 changed files with 4788 additions and 26 deletions

View File

@@ -1,6 +1,7 @@
use async_trait::async_trait;
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig};
use sha2::Digest;
use std::collections::HashMap;
use std::sync::Arc;
use wtfnet_core::ErrorCode;
use wtfnet_platform::{
@@ -189,7 +190,11 @@ fn parse_ipv6_hex(value: &str) -> Option<std::net::Ipv6Addr> {
Some(std::net::Ipv6Addr::from(bytes))
}
fn parse_linux_tcp(path: &str, is_v6: bool) -> Result<Vec<ListenSocket>, PlatformError> {
fn parse_linux_tcp_with_inode_map(
path: &str,
is_v6: bool,
inode_map: &HashMap<String, ProcInfo>,
) -> Result<Vec<ListenSocket>, PlatformError> {
let contents = std::fs::read_to_string(path)
.map_err(|err| PlatformError::new(ErrorCode::IoError, err.to_string()))?;
let mut sockets = Vec::new();
@@ -206,15 +211,28 @@ fn parse_linux_tcp(path: &str, is_v6: bool) -> Result<Vec<ListenSocket>, Platfor
if state != "0A" {
continue;
}
let inode = parts.get(9).copied();
if let Some(local_addr) = parse_proc_socket_addr(local, is_v6) {
let (pid, ppid, process_name, process_path) =
inode.and_then(|value| inode_map.get(value)).map_or(
(None, None, None, None),
|info| {
(
Some(info.pid),
info.ppid,
info.name.clone(),
info.path.clone(),
)
},
);
sockets.push(ListenSocket {
proto: "tcp".to_string(),
local_addr,
state: Some("LISTEN".to_string()),
pid: None,
ppid: None,
process_name: None,
process_path: None,
pid,
ppid,
process_name,
process_path,
owner: None,
});
}
@@ -222,7 +240,11 @@ fn parse_linux_tcp(path: &str, is_v6: bool) -> Result<Vec<ListenSocket>, Platfor
Ok(sockets)
}
fn parse_linux_udp(path: &str, is_v6: bool) -> Result<Vec<ListenSocket>, PlatformError> {
fn parse_linux_udp_with_inode_map(
path: &str,
is_v6: bool,
inode_map: &HashMap<String, ProcInfo>,
) -> Result<Vec<ListenSocket>, PlatformError> {
let contents = std::fs::read_to_string(path)
.map_err(|err| PlatformError::new(ErrorCode::IoError, err.to_string()))?;
let mut sockets = Vec::new();
@@ -235,15 +257,28 @@ fn parse_linux_udp(path: &str, is_v6: bool) -> Result<Vec<ListenSocket>, Platfor
continue;
}
let local = parts[1];
let inode = parts.get(9).copied();
if let Some(local_addr) = parse_proc_socket_addr(local, is_v6) {
let (pid, ppid, process_name, process_path) =
inode.and_then(|value| inode_map.get(value)).map_or(
(None, None, None, None),
|info| {
(
Some(info.pid),
info.ppid,
info.name.clone(),
info.path.clone(),
)
},
);
sockets.push(ListenSocket {
proto: "udp".to_string(),
local_addr,
state: None,
pid: None,
ppid: None,
process_name: None,
process_path: None,
pid,
ppid,
process_name,
process_path,
owner: None,
});
}
@@ -298,6 +333,80 @@ fn extract_port(value: &str) -> Option<u16> {
None
}
#[derive(Clone)]
struct ProcInfo {
pid: u32,
ppid: Option<u32>,
name: Option<String>,
path: Option<String>,
}
fn build_inode_map() -> HashMap<String, ProcInfo> {
let mut map = HashMap::new();
let entries = match std::fs::read_dir("/proc") {
Ok(entries) => entries,
Err(_) => return map,
};
for entry in entries.flatten() {
let file_name = entry.file_name();
let name = match file_name.to_str() {
Some(name) => name,
None => continue,
};
let pid = match name.parse::<u32>() {
Ok(pid) => pid,
Err(_) => continue,
};
let comm = std::fs::read_to_string(format!("/proc/{}/comm", pid))
.ok()
.map(|value| value.trim().to_string());
let path = std::fs::read_link(format!("/proc/{}/exe", pid))
.ok()
.and_then(|value| value.to_str().map(|s| s.to_string()));
let ppid = read_ppid(pid);
let info = ProcInfo {
pid,
ppid,
name: comm,
path,
};
let fd_dir = match std::fs::read_dir(format!("/proc/{}/fd", pid)) {
Ok(dir) => dir,
Err(_) => continue,
};
for fd in fd_dir.flatten() {
if let Ok(target) = std::fs::read_link(fd.path()) {
if let Some(target) = target.to_str() {
if let Some(inode) = parse_socket_inode(target) {
map.entry(inode).or_insert_with(|| info.clone());
}
}
}
}
}
map
}
fn parse_socket_inode(value: &str) -> Option<String> {
let value = value.strip_prefix("socket:[")?;
let value = value.strip_suffix(']')?;
Some(value.to_string())
}
fn read_ppid(pid: u32) -> Option<u32> {
let stat = std::fs::read_to_string(format!("/proc/{}/stat", pid)).ok()?;
let end = stat.rfind(')')?;
let rest = stat.get(end + 2..)?;
let mut parts = rest.split_whitespace();
let _state = parts.next()?;
let ppid = parts.next()?.parse::<u32>().ok()?;
Some(ppid)
}
fn load_native_roots(store: &str) -> Result<Vec<RootCert>, PlatformError> {
let certs = rustls_native_certs::load_native_certs()
.map_err(|err| PlatformError::new(ErrorCode::IoError, err.to_string()))?;
@@ -377,11 +486,28 @@ fn format_fingerprint(bytes: &[u8]) -> String {
#[async_trait]
impl PortsProvider for LinuxPortsProvider {
async fn listening(&self) -> Result<Vec<ListenSocket>, PlatformError> {
let inode_map = build_inode_map();
let mut sockets = Vec::new();
sockets.extend(parse_linux_tcp("/proc/net/tcp", false)?);
sockets.extend(parse_linux_tcp("/proc/net/tcp6", true)?);
sockets.extend(parse_linux_udp("/proc/net/udp", false)?);
sockets.extend(parse_linux_udp("/proc/net/udp6", true)?);
sockets.extend(parse_linux_tcp_with_inode_map(
"/proc/net/tcp",
false,
&inode_map,
)?);
sockets.extend(parse_linux_tcp_with_inode_map(
"/proc/net/tcp6",
true,
&inode_map,
)?);
sockets.extend(parse_linux_udp_with_inode_map(
"/proc/net/udp",
false,
&inode_map,
)?);
sockets.extend(parse_linux_udp_with_inode_map(
"/proc/net/udp6",
true,
&inode_map,
)?);
Ok(sockets)
}