summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-10-01 10:59:45 +0200
committerDavid Lönnhager <david.l@mullvad.net>2024-10-03 15:54:02 +0200
commite32f027b8f5f7c5907722a03a11554b0bf0c4bfe (patch)
treeacac29423dec9df3a2ba6cdc614722c77d179093
parent72364332cb98f20fbd6d5e6e43e2a59ddf9d0fdf (diff)
downloadmullvadvpn-e32f027b8f5f7c5907722a03a11554b0bf0c4bfe.tar.xz
mullvadvpn-e32f027b8f5f7c5907722a03a11554b0bf0c4bfe.zip
Move shared macos code to talpid-macos
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml1
-rw-r--r--talpid-core/Cargo.toml3
-rw-r--r--talpid-core/src/resolver.rs72
-rw-r--r--talpid-core/src/split_tunnel/macos/mod.rs2
-rw-r--r--talpid-core/src/split_tunnel/macos/process.rs60
-rw-r--r--talpid-macos/Cargo.toml15
-rw-r--r--talpid-macos/src/lib.rs7
-rw-r--r--talpid-macos/src/process.rs67
9 files changed, 116 insertions, 120 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d07de1332c..c34a60d2fb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4107,6 +4107,7 @@ dependencies = [
"subslice",
"system-configuration",
"talpid-dbus",
+ "talpid-macos",
"talpid-net",
"talpid-openvpn",
"talpid-platform-metadata",
@@ -4150,6 +4151,14 @@ dependencies = [
]
[[package]]
+name = "talpid-macos"
+version = "0.0.0"
+dependencies = [
+ "libc",
+ "log",
+]
+
+[[package]]
name = "talpid-net"
version = "0.0.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index 3157d70d1b..32757afc78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,6 +29,7 @@ members = [
"talpid-core",
"talpid-dbus",
"talpid-future",
+ "talpid-macos",
"talpid-net",
"talpid-openvpn",
"talpid-openvpn-plugin",
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 5fab3022ab..a0ddb45825 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -56,9 +56,10 @@ talpid-platform-metadata = { path = "../talpid-platform-metadata" }
pcap = { version = "2.1", features = ["capture-stream"] }
pnet_packet = "0.34"
tun = { version = "0.5.5", features = ["async"] }
-nix = { version = "0.28", features = ["socket"] }
+nix = { version = "0.28", features = ["socket", "signal"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
+talpid-macos = { path = "../talpid-macos" }
talpid-net = { path = "../talpid-net" }
[target.'cfg(windows)'.dependencies]
diff --git a/talpid-core/src/resolver.rs b/talpid-core/src/resolver.rs
index 0d07888877..d4900a6e14 100644
--- a/talpid-core/src/resolver.rs
+++ b/talpid-core/src/resolver.rs
@@ -7,7 +7,6 @@
use std::{
io,
net::{IpAddr, Ipv4Addr, SocketAddr},
- path::{Path, PathBuf},
str::FromStr,
sync::{Arc, Weak},
time::{Duration, Instant},
@@ -40,7 +39,6 @@ use hickory_server::{
server::{Request, RequestHandler, ResponseHandler, ResponseInfo},
ServerFuture,
};
-use libc::{c_void, kill, pid_t, proc_listallpids, proc_pidpath, SIGHUP};
use std::sync::LazyLock;
const ALLOWED_RECORD_TYPES: &[RecordType] =
@@ -358,75 +356,15 @@ const MDNS_RESPONDER_PATH: &str = "/usr/sbin/mDNSResponder";
/// Find and kill mDNSResponder. The OS will restart the service.
fn kill_mdnsresponder() -> io::Result<()> {
- if let Some(mdns_pid) = pid_of_path(MDNS_RESPONDER_PATH) {
- if unsafe { kill(mdns_pid as i32, SIGHUP) } != 0 {
- return Err(io::Error::last_os_error());
- }
+ if let Some(mdns_pid) = talpid_macos::process::pid_of_path(MDNS_RESPONDER_PATH) {
+ nix::sys::signal::kill(
+ nix::unistd::Pid::from_raw(mdns_pid),
+ nix::sys::signal::SIGHUP,
+ )?;
}
Ok(())
}
-/// Return the first process identifier matching a specified path, if one exists.
-fn pid_of_path(find_path: impl AsRef<Path>) -> Option<pid_t> {
- match list_pids() {
- Ok(pids) => {
- for pid in pids {
- if let Ok(path) = process_path(pid) {
- if path == find_path.as_ref() {
- return Some(pid);
- }
- }
- }
- None
- }
- Err(error) => {
- log::error!("Failed to list processes: {error}");
- None
- }
- }
-}
-
-/// Obtain a list of all pids
-fn list_pids() -> io::Result<Vec<pid_t>> {
- // SAFETY: Passing in null and 0 returns the number of processes
- let num_pids = unsafe { proc_listallpids(std::ptr::null_mut(), 0) };
- if num_pids <= 0 {
- return Err(io::Error::last_os_error());
- }
- let num_pids = usize::try_from(num_pids).unwrap();
- let mut pids = vec![0i32; num_pids];
-
- let buf_sz = (num_pids * std::mem::size_of::<pid_t>()) as i32;
- // SAFETY: 'pids' is large enough to contain 'num_pids' processes
- let num_pids = unsafe { proc_listallpids(pids.as_mut_ptr() as *mut c_void, buf_sz) };
- if num_pids == -1 {
- return Err(io::Error::last_os_error());
- }
-
- pids.resize(usize::try_from(num_pids).unwrap(), 0);
-
- Ok(pids)
-}
-
-fn process_path(pid: pid_t) -> io::Result<PathBuf> {
- let mut buffer = [0u8; libc::MAXPATHLEN as usize];
- // SAFETY: `proc_pidpath` returns at most `buffer.len()` bytes
- let buf_len = unsafe {
- proc_pidpath(
- pid,
- buffer.as_mut_ptr() as *mut c_void,
- u32::try_from(buffer.len()).unwrap(),
- )
- };
- if buf_len == -1 {
- return Err(io::Error::last_os_error());
- }
- Ok(PathBuf::from(
- std::str::from_utf8(&buffer[0..buf_len as usize])
- .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid process path"))?,
- ))
-}
-
type LookupResponse<'a> = MessageResponse<
'a,
'a,
diff --git a/talpid-core/src/split_tunnel/macos/mod.rs b/talpid-core/src/split_tunnel/macos/mod.rs
index b3acdb0136..38c4201ea0 100644
--- a/talpid-core/src/split_tunnel/macos/mod.rs
+++ b/talpid-core/src/split_tunnel/macos/mod.rs
@@ -614,7 +614,7 @@ impl State {
Some(vpn_interface.clone()),
route_manager.clone(),
Box::new(move |packet| {
- match states.get_process_status(packet.header.pth_pid as u32) {
+ match states.get_process_status(packet.header.pth_pid) {
ExclusionStatus::Excluded => tun::RoutingDecision::DefaultInterface,
ExclusionStatus::Included => tun::RoutingDecision::VpnTunnel,
ExclusionStatus::Unknown => {
diff --git a/talpid-core/src/split_tunnel/macos/process.rs b/talpid-core/src/split_tunnel/macos/process.rs
index 52e0fb1862..7069e5fdd8 100644
--- a/talpid-core/src/split_tunnel/macos/process.rs
+++ b/talpid-core/src/split_tunnel/macos/process.rs
@@ -6,18 +6,17 @@
//! Endpoint Security framework.
use futures::channel::oneshot;
-use libc::{proc_listallpids, proc_pidpath};
+use libc::pid_t;
use serde::Deserialize;
use std::{
collections::{HashMap, HashSet},
- ffi::c_void,
io,
path::PathBuf,
process::Stdio,
- ptr,
sync::{Arc, LazyLock, Mutex},
time::Duration,
};
+use talpid_macos::process::{list_pids, process_path};
use talpid_platform_metadata::MacosVersion;
use talpid_types::tunnel::ErrorStateCause;
use tokio::io::{AsyncBufReadExt, BufReader};
@@ -52,7 +51,7 @@ pub enum Error {
InitializePids(#[source] io::Error),
/// Failed to find path for a process
#[error("Failed to find path for a process: {}", _0)]
- FindProcessPath(#[source] io::Error, u32),
+ FindProcessPath(#[source] io::Error, pid_t),
}
impl From<&Error> for ErrorStateCause {
@@ -231,7 +230,7 @@ pub enum ExclusionStatus {
#[derive(Debug)]
struct InnerProcessStates {
- processes: HashMap<u32, ProcessInfo>,
+ processes: HashMap<pid_t, ProcessInfo>,
exclude_paths: HashSet<PathBuf>,
}
@@ -277,7 +276,7 @@ impl ProcessStates {
inner.exclude_paths = paths;
}
- pub fn get_process_status(&self, pid: u32) -> ExclusionStatus {
+ pub fn get_process_status(&self, pid: pid_t) -> ExclusionStatus {
let inner = self.inner.lock().unwrap();
match inner.processes.get(&pid) {
Some(val) if val.is_excluded() => ExclusionStatus::Excluded,
@@ -300,7 +299,7 @@ impl InnerProcessStates {
// For new processes, inherit all exclusion state from the parent, if there is one.
// Otherwise, look up excluded paths
- fn handle_fork(&mut self, parent_pid: u32, exec_path: PathBuf, msg: ESForkEvent) {
+ fn handle_fork(&mut self, parent_pid: pid_t, exec_path: PathBuf, msg: ESForkEvent) {
let pid = msg.child.audit_token.pid;
if self.processes.contains_key(&pid) {
@@ -327,7 +326,7 @@ impl InnerProcessStates {
self.processes.insert(pid, base_info);
}
- fn handle_exec(&mut self, pid: u32, msg: ESExecEvent) {
+ fn handle_exec(&mut self, pid: pid_t, msg: ESExecEvent) {
let Some(info) = self.processes.get_mut(&pid) else {
log::error!("exec received for unknown pid {pid}");
return;
@@ -354,54 +353,13 @@ impl InnerProcessStates {
}
}
- fn handle_exit(&mut self, pid: u32) {
+ fn handle_exit(&mut self, pid: pid_t) {
if self.processes.remove(&pid).is_none() {
log::error!("exit syscall for unknown pid {pid}");
}
}
}
-/// Obtain a list of all pids
-fn list_pids() -> io::Result<Vec<u32>> {
- // SAFETY: Passing in null and 0 returns the number of processes
- let num_pids = unsafe { proc_listallpids(ptr::null_mut(), 0) };
- if num_pids <= 0 {
- return Err(io::Error::last_os_error());
- }
- let num_pids = usize::try_from(num_pids).unwrap();
- let mut pids = vec![0u32; num_pids];
-
- let buf_sz = (num_pids * std::mem::size_of::<u32>()) as i32;
- // SAFETY: 'pids' is large enough to contain 'num_pids' processes
- let num_pids = unsafe { proc_listallpids(pids.as_mut_ptr() as *mut c_void, buf_sz) };
- if num_pids == -1 {
- return Err(io::Error::last_os_error());
- }
-
- pids.resize(usize::try_from(num_pids).unwrap(), 0);
-
- Ok(pids)
-}
-
-fn process_path(pid: u32) -> io::Result<PathBuf> {
- let mut buffer = [0u8; libc::MAXPATHLEN as usize];
- // SAFETY: `proc_pidpath` returns at most `buffer.len()` bytes
- let buf_len = unsafe {
- proc_pidpath(
- pid as i32,
- buffer.as_mut_ptr() as *mut c_void,
- buffer.len() as u32,
- )
- };
- if buf_len == -1 {
- return Err(io::Error::last_os_error());
- }
- Ok(PathBuf::from(
- std::str::from_utf8(&buffer[0..buf_len as usize])
- .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid process path"))?,
- ))
-}
-
#[derive(Debug, Clone)]
struct ProcessInfo {
exec_path: PathBuf,
@@ -480,7 +438,7 @@ struct ESExecutable {
/// https://developer.apple.com/documentation/endpointsecurity/es_process_t/3228975-audit_token?language=objc
#[derive(Debug, Deserialize)]
struct ESAuditToken {
- pid: u32,
+ pid: pid_t,
}
/// Process information for the message returned by `eslogger`.
diff --git a/talpid-macos/Cargo.toml b/talpid-macos/Cargo.toml
new file mode 100644
index 0000000000..7868707b4c
--- /dev/null
+++ b/talpid-macos/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "talpid-macos"
+description = "Abstractions for macOS"
+authors.workspace = true
+repository.workspace = true
+license.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[lints]
+workspace = true
+
+[target.'cfg(target_os="macos")'.dependencies]
+libc = "0.2"
+log = { workspace = true }
diff --git a/talpid-macos/src/lib.rs b/talpid-macos/src/lib.rs
new file mode 100644
index 0000000000..964dd689e0
--- /dev/null
+++ b/talpid-macos/src/lib.rs
@@ -0,0 +1,7 @@
+//! Interface with macOS-specific bits.
+
+#![deny(missing_docs)]
+#![cfg(target_os = "macos")]
+
+/// Processes
+pub mod process;
diff --git a/talpid-macos/src/process.rs b/talpid-macos/src/process.rs
new file mode 100644
index 0000000000..69722e054c
--- /dev/null
+++ b/talpid-macos/src/process.rs
@@ -0,0 +1,67 @@
+use libc::{c_void, pid_t, proc_listallpids, proc_pidpath};
+use std::{
+ io,
+ path::{Path, PathBuf},
+};
+
+/// Return the first process identifier matching a specified path, if one exists.
+pub fn pid_of_path(find_path: impl AsRef<Path>) -> Option<pid_t> {
+ match list_pids() {
+ Ok(pids) => {
+ for pid in pids {
+ if let Ok(path) = process_path(pid) {
+ if path == find_path.as_ref() {
+ return Some(pid);
+ }
+ }
+ }
+ None
+ }
+ Err(error) => {
+ log::error!("Failed to list processes: {error}");
+ None
+ }
+ }
+}
+
+/// Obtain a list of all process identifiers
+pub fn list_pids() -> io::Result<Vec<pid_t>> {
+ // SAFETY: Passing in null and 0 returns the number of processes
+ let num_pids = unsafe { proc_listallpids(std::ptr::null_mut(), 0) };
+ if num_pids <= 0 {
+ return Err(io::Error::last_os_error());
+ }
+ let num_pids = usize::try_from(num_pids).unwrap();
+ let mut pids = vec![0i32; num_pids];
+
+ let buf_sz = (num_pids * std::mem::size_of::<pid_t>()) as i32;
+ // SAFETY: 'pids' is large enough to contain 'num_pids' processes
+ let num_pids = unsafe { proc_listallpids(pids.as_mut_ptr() as *mut c_void, buf_sz) };
+ if num_pids == -1 {
+ return Err(io::Error::last_os_error());
+ }
+
+ pids.resize(usize::try_from(num_pids).unwrap(), 0);
+
+ Ok(pids)
+}
+
+/// Return the path of the process `pid`
+pub fn process_path(pid: pid_t) -> io::Result<PathBuf> {
+ let mut buffer = [0u8; libc::MAXPATHLEN as usize];
+ // SAFETY: `proc_pidpath` returns at most `buffer.len()` bytes
+ let buf_len = unsafe {
+ proc_pidpath(
+ pid,
+ buffer.as_mut_ptr() as *mut c_void,
+ u32::try_from(buffer.len()).unwrap(),
+ )
+ };
+ if buf_len == -1 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(PathBuf::from(
+ std::str::from_utf8(&buffer[0..buf_len as usize])
+ .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid process path"))?,
+ ))
+}