Skip to content

Commit

Permalink
optimize process info retrieval
Browse files Browse the repository at this point in the history
  • Loading branch information
lemos1235 committed Apr 15, 2023
1 parent 202c882 commit e9c13c2
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 31 deletions.
1 change: 1 addition & 0 deletions leaf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ warp = { version = "0.3", default-features = false, optional = true }
notify = { version = "5.0.0-pre.13", optional = true }

# Process
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os="windows"))'.dependencies]
netstat2 = "0.9.1"
sysinfo = "0.28.4"

Expand Down
59 changes: 41 additions & 18 deletions leaf/src/app/process_finder.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
use log::debug;
use netstat2::{get_sockets_info, AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, TcpState};
use std::net;
use std::net::IpAddr;
use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt};
use std::sync::{Arc, Mutex};

use lazy_static::lazy_static;
use log::debug;
use netstat2::{
get_sockets_info, AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, SocketInfo,
};
use sysinfo::{Pid, PidExt, ProcessExt, ProcessRefreshKind, System, SystemExt};

lazy_static! {
pub static ref SYSTEM: Arc<Mutex<System>> = {
let system = System::new_all();
Arc::new(Mutex::new(system))
};
}

#[derive(Debug)]
pub struct PortInfo {
pub address: net::IpAddr,
pub address: IpAddr,
pub port: u16,
pub protocol: String,
pub process_info: Option<ProcessInfo>,
Expand All @@ -19,14 +30,15 @@ pub struct ProcessInfo {
pub process_path: String,
}

impl From<netstat2::SocketInfo> for PortInfo {
fn from(socket_info: netstat2::SocketInfo) -> Self {
impl From<SocketInfo> for PortInfo {
fn from(socket_info: SocketInfo) -> Self {
let protocol = match socket_info.protocol_socket_info {
ProtocolSocketInfo::Tcp(_) => "TCP",
ProtocolSocketInfo::Udp(_) => "UDP",
};
let system = System::new_all();
let pid = socket_info.associated_pids.first().unwrap();
let mut system = SYSTEM.lock().unwrap();
system.refresh_processes_specifics(ProcessRefreshKind::default());
let process_info =
system
.process(Pid::from(pid.to_owned() as usize))
Expand All @@ -44,8 +56,7 @@ impl From<netstat2::SocketInfo> for PortInfo {
}
}

pub fn find_process(protocol: &str, ip: IpAddr, port: u16) -> Option<PortInfo> {
debug!("looking up process for {}:{}:{}", protocol, ip, port);
fn get_socket_info(protocol: &str, ip: &IpAddr, port: u16) -> Option<SocketInfo> {
let mut af_flags: AddressFamilyFlags = AddressFamilyFlags::from_bits(0).unwrap();
if ip.is_ipv6() {
af_flags |= AddressFamilyFlags::IPV6;
Expand All @@ -60,16 +71,28 @@ pub fn find_process(protocol: &str, ip: IpAddr, port: u16) -> Option<PortInfo> {
if protocol == "tcp" {
proto_flags |= ProtocolFlags::TCP;
}
let start_time = tokio::time::Instant::now();
let sockets = get_sockets_info(af_flags, proto_flags).unwrap_or_default();
let mut port_infos = sockets
let socket_info = sockets
.into_iter()
.filter(|socket_info| match &socket_info.protocol_socket_info {
ProtocolSocketInfo::Tcp(tcp) => tcp.state != TcpState::Closed,
ProtocolSocketInfo::Udp(_) => true,
})
.map(|socket_info| PortInfo::from(socket_info));
let port_info = port_infos.find(|p| p.address == ip && p.port == port);
.find(|p| p.local_addr() == ip.to_owned() && p.local_port() == port);
return socket_info;
}

pub fn find_process_id(protocol: &str, ip: &IpAddr, port: u16) -> Option<u32> {
let start_time = tokio::time::Instant::now();
let socket_info = get_socket_info(protocol, ip, port);
let pid = socket_info.map(|s| s.associated_pids.first().unwrap().to_owned());
if let Some(ref pid) = pid {
let elapsed = tokio::time::Instant::now().duration_since(start_time);
debug!("found process id [{}ms] {:?}", elapsed.as_millis(), pid);
}
pid
}

pub fn find_process(protocol: &str, ip: &IpAddr, port: u16) -> Option<PortInfo> {
let start_time = tokio::time::Instant::now();
let socket_info = get_socket_info(protocol, ip, port);
let port_info = socket_info.map(|socket_info| PortInfo::from(socket_info));
if let Some(ref p) = port_info {
let elapsed = tokio::time::Instant::now().duration_since(start_time);
debug!("found process [{}ms] {:?}", elapsed.as_millis(), p);
Expand Down
25 changes: 12 additions & 13 deletions leaf/src/app/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ use log::*;
use maxminddb::geoip2::Country;
use memmap2::Mmap;

#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
use crate::app::process_finder;
use crate::app::SyncDnsClient;
use crate::config;
use crate::session::{Network, Session, SocksAddr};

#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
use crate::app::process_finder;

pub trait Condition: Send + Sync + Unpin {
fn apply(&self, sess: &Session) -> bool;
}
Expand Down Expand Up @@ -372,17 +371,15 @@ impl ProcessPidMatcher {
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
impl Condition for ProcessPidMatcher {
fn apply(&self, sess: &Session) -> bool {
let port_info = process_finder::find_process(
let process_id = process_finder::find_process_id(
&sess.network.to_string(),
sess.source.ip(),
&sess.source.ip(),
sess.source.port(),
);
if let Some(port) = port_info {
if let Some(process_info) = port.process_info {
if process_info.pid.to_string() == self.value {
debug!("{} matches process id [{}]", process_info.pid, self.value);
return true;
}
if let Some(pid) = process_id {
if pid.to_string() == self.value {
debug!("{} matches process id [{}]", pid, self.value);
return true;
}
}
false
Expand All @@ -406,7 +403,7 @@ impl Condition for ProcessNameMatcher {
fn apply(&self, sess: &Session) -> bool {
let port_info = process_finder::find_process(
&sess.network.to_string(),
sess.source.ip(),
&sess.source.ip(),
sess.source.port(),
);
if let Some(port) = port_info {
Expand Down Expand Up @@ -574,9 +571,11 @@ impl Router {
cond_and.add(Box::new(InboundTagMatcher::new(&mut rr.inbound_tags)));
}

#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
if rr.processes.len() > 0 {
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
cond_and.add(Box::new(ProcessMatcher::new(&mut rr.processes)));
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
warn!("The 'process' rule is not applicable to the current operating system");
}

if cond_and.is_empty() {
Expand Down

0 comments on commit e9c13c2

Please sign in to comment.