Loading Cargo.lock +159 −627 File changed.Preview size limit exceeded, changes collapsed. Show changes crates/onionmasq-apple/Cargo.toml +1 −5 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] # Enable verbose logcat logging, only useful for debugging purposes. # Enable verbose logging, only useful for debugging purposes. # You should NOT enable this in production builds for privacy reasons. verbose = [] Loading @@ -15,12 +15,8 @@ anyhow = "1.0" backtrace = "0.3.69" lazy_static = "1.4.0" onion-tunnel = { path = "../onion-tunnel", version = "0.1.0" } # NOTE(eta): the below 2 are only required for working around arti#988; we don't actually use them ;w; tor-keymgr = { git = "https://gitlab.torproject.org/eta/arti", branch = "exit-selection-draft", features = ["keymgr"] } tor-config = { git = "https://gitlab.torproject.org/eta/arti", branch = "exit-selection-draft" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" simple_logger = "1" tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } tokio-stream = "0.1" tracing = "0.1.37" Loading crates/onionmasq-apple/src/ffi.rs +10 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,16 @@ pub extern "C" fn runProxy( ) { panic_handling::capture_unwind!({ let oma = OnionmasqApple::get(); let cache_dir = unsafe { CStr::from_ptr(cache_dir) .to_str() .expect("invalid UTF-8 in cache_dir") }; let data_dir = unsafe { CStr::from_ptr(data_dir) .to_str() .expect("invalid UTF-8 in data_dir") }; if let Err(e) = oma.run_proxy(cache_dir, data_dir, reader, writer) { error!("runProxy() returned error: {e:?}"); } Loading crates/onionmasq-apple/src/lib.rs +22 −29 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ use ffi_event::TunnelEvent; use futures::StreamExt; use once_cell::sync::OnceCell; use onion_tunnel::accounting::BandwidthCounter; use onion_tunnel::config::TunnelConfig; use onion_tunnel::scaffolding::TunnelCommand; use onion_tunnel::{CountryCode, OnionTunnel}; use std::ffi::{c_char, CStr, CString}; Loading @@ -25,8 +26,6 @@ use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; use tokio::runtime::Runtime; use tokio::sync::mpsc; use tor_config::{BoolOrAuto, CfgPath}; use tor_keymgr::config::arti::ArtiNativeKeystoreConfig; use tracing::{debug, error, info, warn}; use tracing_subscriber::fmt::{Layer, Subscriber}; use tracing_subscriber::layer::SubscriberExt; Loading @@ -37,9 +36,9 @@ static ONIONMASQ_APPLE_SINGLETON: OnceCell<OnionmasqApple> = OnceCell::new(); // We want to avoid logging detailed connection-level stuff in production builds to avoid // embarrassing privacy issues. #[cfg(feature = "verbose")] static LOG_FILTER: &str = "info,onionmasq_mobile=trace,onion_tunnel=trace,arti_client=debug,tor_chanmgr=debug,tor_proto=debug,smoltcp=debug"; static LOG_FILTER: &str = "info,onionmasq_apple=trace,onion_tunnel=trace,arti_client=debug,tor_chanmgr=debug,tor_proto=debug,smoltcp=debug"; #[cfg(not(feature = "verbose"))] static LOG_FILTER: &str = "info,onionmasq_mobile=debug"; static LOG_FILTER: &str = "info,onionmasq_apple=debug"; pub(crate) type BandwidthCounterMap = Arc<DashMap<u64, Arc<BandwidthCounter>>>; Loading Loading @@ -77,6 +76,7 @@ impl OnionmasqApple { panic!("OnionmasqApple::new() called twice!"); } // FIXME(eta): We could just pass the C thing in directly maybe. let log_fn = Arc::new(move |log: *const c_char| { (log_fn)(log); }); Loading Loading @@ -126,18 +126,11 @@ impl OnionmasqApple { /// Run the `onion-tunnel` proxy. pub fn run_proxy( &self, cache_dir: *const c_char, data_dir: *const c_char, cache_dir: &str, data_dir: &str, reader: ReaderCallback, writer: WriterCallback, ) -> Result<()> { let cache_dir = unsafe { CStr::from_ptr(cache_dir) } .to_str() .context("failed to get string cache_dir")?; let data_dir = unsafe { CStr::from_ptr(data_dir) } .to_str() .context("failed to get string for data_dir")?; let (command_sender, command_receiver) = mpsc::unbounded_channel(); let (bootstrap_done_sender, mut bootstrap_done_receiver) = mpsc::unbounded_channel(); Loading @@ -145,21 +138,15 @@ impl OnionmasqApple { let pcap_path = self.pcap_path.lock().unwrap().clone(); let mut config = onion_tunnel::TorClientConfigBuilder::from_directories( format!("{data_dir}/arti-data"), format!("{cache_dir}/arti-cache"), ); // HACK(eta): work around for arti#988 let aks = ArtiNativeKeystoreConfig::builder() .enabled(BoolOrAuto::Explicit(false)) .path(CfgPath::new("/hopefully-ignored/".into())) .build() .expect("arti#988 workaround failed"); let mut config = TunnelConfig::default(); config.storage().keystore(aks); config.state_dir = Some(format!("{data_dir}/arti-data").into()); config.cache_dir = Some(format!("{cache_dir}/arti-cache").into()); config.pt_dir = Some(format!("{cache_dir}/arti-pts").into()); let config = config.build().context("failed to make a TorClientConfig")?; if let Some(pp) = pcap_path { config.pcap_path = Some(pp.into()); } let counters = self.bandwidth_counters.clone(); let country_code = self.country_code.clone(); Loading @@ -176,7 +163,6 @@ impl OnionmasqApple { }, NePacketTunnelFlow::new(reader, writer), config, pcap_path, ) .await .context("couldn't start onionmasq proxy")?; Loading Loading @@ -294,7 +280,7 @@ impl OnionmasqApple { fn on_tunnel_event(&self, evt: TunnelEvent) -> Result<()> { let serialized = serde_json::to_string(&evt).context("failed to serialize TunnelEvent")?; let cstr = CString::new(serialized.to_owned()) let cstr = CString::new(serialized) .context("failed to create CString from serialized TunnelEvent")?; (self.event_fn)(cstr.as_ptr()); Loading Loading @@ -335,6 +321,10 @@ impl OnionmasqApple { } } /// This implements `Write` and forwards all written data to a C callback function. /// // NOTE(eta): I'm pretty sure I wrote this during the Brussels hacking session over tla's // shoulder. Looking back at it now, there's a few issues with it... #[derive(Clone)] struct CallbackWriter<F> { func: Arc<F>, Loading @@ -354,12 +344,15 @@ where F: Fn(*const c_char) + Send + Sync + 'static, { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { // FIXME(eta): should we not buffer each line? let cstr = CString::new(buf.to_owned()) .context("failed to create CString from log string") .unwrap(); (self.func)(cstr.as_ptr()); return Ok(buf.len()); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Loading crates/onionmasq-apple/src/nepackettunnelflow.rs +28 −35 Original line number Diff line number Diff line //! A custom tunnel device type that interfaces with Apple's `NEPacketTunnelFlow` [1]. //! //! (Really, it just lets you write and read packets with C callbacks, but that's what it's designed //! to be used for.) //! //! [1]: https://developer.apple.com/documentation/networkextension/nepackettunnelflow use crate::{ReaderCallback, WriterCallback}; use std::collections::VecDeque; use std::io::{Error, ErrorKind}; use std::pin::Pin; use std::slice::from_raw_parts; use std::slice; use std::sync::Mutex; use std::task::{Context, Poll, Waker}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; static BUFFER: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new()); static WAKER: Mutex<Option<Waker>> = Mutex::new(None); pub struct NePacketTunnelFlow { reader: ReaderCallback, writer: WriterCallback, } Loading @@ -22,31 +27,23 @@ impl NePacketTunnelFlow { NePacketTunnelFlow { reader, writer } } pub unsafe fn receive(packets: *const *const u8, lens: *const usize, len: usize) { match BUFFER.lock() { Ok(mut data) => { for i in 0..len { let packet = *packets.offset(i as isize); let len = *lens.offset(i as isize); let slice = from_raw_parts(packet, len); pub unsafe fn receive( packets: *const *const u8, packet_lengths: *const usize, packet_count: usize, ) { let mut data = BUFFER.lock().expect("buffer poisoned"); for i in 0..packet_count { let packet = *packets.add(i); let len = *packet_lengths.add(i); let slice = slice::from_raw_parts(packet, len); data.push_back(slice.to_vec()); } } Err(_) => { // Ignore? } } match WAKER.lock() { Ok(mut waker) => { if waker.is_some() { waker.take().unwrap().wake(); } } Err(_) => { // Well. At least, *when* Onionmasq wants to read again, there's going to be data available. } if let Some(waker) = WAKER.lock().expect("waker poisoned").take() { waker.wake(); } } } Loading @@ -57,26 +54,22 @@ impl AsyncRead for NePacketTunnelFlow { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>> { let packet = match BUFFER.lock() { Ok(mut guard) => guard.pop_front(), Err(_) => None, }; let mut buffer = BUFFER.lock().expect("buffer poisoned"); return match packet { match buffer.pop_front() { Some(packet) => { buf.put_slice(packet.as_slice()); Poll::Ready(Ok(())) } None => { // Force unwrap here. If an error should happen, everything is already FUBAR'd anyway. let mut guard = WAKER.lock().unwrap(); let mut waker = WAKER.lock().expect("waker poisoned"); // Check, if we're already waiting for a read to complete. let already_reading = guard.is_some(); let already_reading = waker.is_some(); // Replace the waker with the new one in any case. *guard = Some(cx.waker().clone()); *waker = Some(cx.waker().clone()); if !already_reading { (self.reader)(); Loading @@ -84,7 +77,7 @@ impl AsyncRead for NePacketTunnelFlow { Poll::Pending } }; } } } Loading Loading
crates/onionmasq-apple/Cargo.toml +1 −5 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] # Enable verbose logcat logging, only useful for debugging purposes. # Enable verbose logging, only useful for debugging purposes. # You should NOT enable this in production builds for privacy reasons. verbose = [] Loading @@ -15,12 +15,8 @@ anyhow = "1.0" backtrace = "0.3.69" lazy_static = "1.4.0" onion-tunnel = { path = "../onion-tunnel", version = "0.1.0" } # NOTE(eta): the below 2 are only required for working around arti#988; we don't actually use them ;w; tor-keymgr = { git = "https://gitlab.torproject.org/eta/arti", branch = "exit-selection-draft", features = ["keymgr"] } tor-config = { git = "https://gitlab.torproject.org/eta/arti", branch = "exit-selection-draft" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" simple_logger = "1" tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } tokio-stream = "0.1" tracing = "0.1.37" Loading
crates/onionmasq-apple/src/ffi.rs +10 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,16 @@ pub extern "C" fn runProxy( ) { panic_handling::capture_unwind!({ let oma = OnionmasqApple::get(); let cache_dir = unsafe { CStr::from_ptr(cache_dir) .to_str() .expect("invalid UTF-8 in cache_dir") }; let data_dir = unsafe { CStr::from_ptr(data_dir) .to_str() .expect("invalid UTF-8 in data_dir") }; if let Err(e) = oma.run_proxy(cache_dir, data_dir, reader, writer) { error!("runProxy() returned error: {e:?}"); } Loading
crates/onionmasq-apple/src/lib.rs +22 −29 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ use ffi_event::TunnelEvent; use futures::StreamExt; use once_cell::sync::OnceCell; use onion_tunnel::accounting::BandwidthCounter; use onion_tunnel::config::TunnelConfig; use onion_tunnel::scaffolding::TunnelCommand; use onion_tunnel::{CountryCode, OnionTunnel}; use std::ffi::{c_char, CStr, CString}; Loading @@ -25,8 +26,6 @@ use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; use tokio::runtime::Runtime; use tokio::sync::mpsc; use tor_config::{BoolOrAuto, CfgPath}; use tor_keymgr::config::arti::ArtiNativeKeystoreConfig; use tracing::{debug, error, info, warn}; use tracing_subscriber::fmt::{Layer, Subscriber}; use tracing_subscriber::layer::SubscriberExt; Loading @@ -37,9 +36,9 @@ static ONIONMASQ_APPLE_SINGLETON: OnceCell<OnionmasqApple> = OnceCell::new(); // We want to avoid logging detailed connection-level stuff in production builds to avoid // embarrassing privacy issues. #[cfg(feature = "verbose")] static LOG_FILTER: &str = "info,onionmasq_mobile=trace,onion_tunnel=trace,arti_client=debug,tor_chanmgr=debug,tor_proto=debug,smoltcp=debug"; static LOG_FILTER: &str = "info,onionmasq_apple=trace,onion_tunnel=trace,arti_client=debug,tor_chanmgr=debug,tor_proto=debug,smoltcp=debug"; #[cfg(not(feature = "verbose"))] static LOG_FILTER: &str = "info,onionmasq_mobile=debug"; static LOG_FILTER: &str = "info,onionmasq_apple=debug"; pub(crate) type BandwidthCounterMap = Arc<DashMap<u64, Arc<BandwidthCounter>>>; Loading Loading @@ -77,6 +76,7 @@ impl OnionmasqApple { panic!("OnionmasqApple::new() called twice!"); } // FIXME(eta): We could just pass the C thing in directly maybe. let log_fn = Arc::new(move |log: *const c_char| { (log_fn)(log); }); Loading Loading @@ -126,18 +126,11 @@ impl OnionmasqApple { /// Run the `onion-tunnel` proxy. pub fn run_proxy( &self, cache_dir: *const c_char, data_dir: *const c_char, cache_dir: &str, data_dir: &str, reader: ReaderCallback, writer: WriterCallback, ) -> Result<()> { let cache_dir = unsafe { CStr::from_ptr(cache_dir) } .to_str() .context("failed to get string cache_dir")?; let data_dir = unsafe { CStr::from_ptr(data_dir) } .to_str() .context("failed to get string for data_dir")?; let (command_sender, command_receiver) = mpsc::unbounded_channel(); let (bootstrap_done_sender, mut bootstrap_done_receiver) = mpsc::unbounded_channel(); Loading @@ -145,21 +138,15 @@ impl OnionmasqApple { let pcap_path = self.pcap_path.lock().unwrap().clone(); let mut config = onion_tunnel::TorClientConfigBuilder::from_directories( format!("{data_dir}/arti-data"), format!("{cache_dir}/arti-cache"), ); // HACK(eta): work around for arti#988 let aks = ArtiNativeKeystoreConfig::builder() .enabled(BoolOrAuto::Explicit(false)) .path(CfgPath::new("/hopefully-ignored/".into())) .build() .expect("arti#988 workaround failed"); let mut config = TunnelConfig::default(); config.storage().keystore(aks); config.state_dir = Some(format!("{data_dir}/arti-data").into()); config.cache_dir = Some(format!("{cache_dir}/arti-cache").into()); config.pt_dir = Some(format!("{cache_dir}/arti-pts").into()); let config = config.build().context("failed to make a TorClientConfig")?; if let Some(pp) = pcap_path { config.pcap_path = Some(pp.into()); } let counters = self.bandwidth_counters.clone(); let country_code = self.country_code.clone(); Loading @@ -176,7 +163,6 @@ impl OnionmasqApple { }, NePacketTunnelFlow::new(reader, writer), config, pcap_path, ) .await .context("couldn't start onionmasq proxy")?; Loading Loading @@ -294,7 +280,7 @@ impl OnionmasqApple { fn on_tunnel_event(&self, evt: TunnelEvent) -> Result<()> { let serialized = serde_json::to_string(&evt).context("failed to serialize TunnelEvent")?; let cstr = CString::new(serialized.to_owned()) let cstr = CString::new(serialized) .context("failed to create CString from serialized TunnelEvent")?; (self.event_fn)(cstr.as_ptr()); Loading Loading @@ -335,6 +321,10 @@ impl OnionmasqApple { } } /// This implements `Write` and forwards all written data to a C callback function. /// // NOTE(eta): I'm pretty sure I wrote this during the Brussels hacking session over tla's // shoulder. Looking back at it now, there's a few issues with it... #[derive(Clone)] struct CallbackWriter<F> { func: Arc<F>, Loading @@ -354,12 +344,15 @@ where F: Fn(*const c_char) + Send + Sync + 'static, { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { // FIXME(eta): should we not buffer each line? let cstr = CString::new(buf.to_owned()) .context("failed to create CString from log string") .unwrap(); (self.func)(cstr.as_ptr()); return Ok(buf.len()); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Loading
crates/onionmasq-apple/src/nepackettunnelflow.rs +28 −35 Original line number Diff line number Diff line //! A custom tunnel device type that interfaces with Apple's `NEPacketTunnelFlow` [1]. //! //! (Really, it just lets you write and read packets with C callbacks, but that's what it's designed //! to be used for.) //! //! [1]: https://developer.apple.com/documentation/networkextension/nepackettunnelflow use crate::{ReaderCallback, WriterCallback}; use std::collections::VecDeque; use std::io::{Error, ErrorKind}; use std::pin::Pin; use std::slice::from_raw_parts; use std::slice; use std::sync::Mutex; use std::task::{Context, Poll, Waker}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; static BUFFER: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new()); static WAKER: Mutex<Option<Waker>> = Mutex::new(None); pub struct NePacketTunnelFlow { reader: ReaderCallback, writer: WriterCallback, } Loading @@ -22,31 +27,23 @@ impl NePacketTunnelFlow { NePacketTunnelFlow { reader, writer } } pub unsafe fn receive(packets: *const *const u8, lens: *const usize, len: usize) { match BUFFER.lock() { Ok(mut data) => { for i in 0..len { let packet = *packets.offset(i as isize); let len = *lens.offset(i as isize); let slice = from_raw_parts(packet, len); pub unsafe fn receive( packets: *const *const u8, packet_lengths: *const usize, packet_count: usize, ) { let mut data = BUFFER.lock().expect("buffer poisoned"); for i in 0..packet_count { let packet = *packets.add(i); let len = *packet_lengths.add(i); let slice = slice::from_raw_parts(packet, len); data.push_back(slice.to_vec()); } } Err(_) => { // Ignore? } } match WAKER.lock() { Ok(mut waker) => { if waker.is_some() { waker.take().unwrap().wake(); } } Err(_) => { // Well. At least, *when* Onionmasq wants to read again, there's going to be data available. } if let Some(waker) = WAKER.lock().expect("waker poisoned").take() { waker.wake(); } } } Loading @@ -57,26 +54,22 @@ impl AsyncRead for NePacketTunnelFlow { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>> { let packet = match BUFFER.lock() { Ok(mut guard) => guard.pop_front(), Err(_) => None, }; let mut buffer = BUFFER.lock().expect("buffer poisoned"); return match packet { match buffer.pop_front() { Some(packet) => { buf.put_slice(packet.as_slice()); Poll::Ready(Ok(())) } None => { // Force unwrap here. If an error should happen, everything is already FUBAR'd anyway. let mut guard = WAKER.lock().unwrap(); let mut waker = WAKER.lock().expect("waker poisoned"); // Check, if we're already waiting for a read to complete. let already_reading = guard.is_some(); let already_reading = waker.is_some(); // Replace the waker with the new one in any case. *guard = Some(cx.waker().clone()); *waker = Some(cx.waker().clone()); if !already_reading { (self.reader)(); Loading @@ -84,7 +77,7 @@ impl AsyncRead for NePacketTunnelFlow { Poll::Pending } }; } } } Loading