diff --git a/Cargo.lock b/Cargo.lock index 1bb463fc..9f58b4a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1819,6 +1819,7 @@ dependencies = [ "env_logger", "log", "mitmproxy", + "nix 0.29.0", "once_cell", "pyo3", "pyo3-asyncio-0-21", diff --git a/mitmproxy-rs/Cargo.toml b/mitmproxy-rs/Cargo.toml index 932f96ef..080860c8 100644 --- a/mitmproxy-rs/Cargo.toml +++ b/mitmproxy-rs/Cargo.toml @@ -30,6 +30,8 @@ boringtun = "0.6" tar = "0.4.43" console-subscriber = { version = "0.4.1", optional = true } +[target.'cfg(target_os = "linux")'.dependencies] +nix = { version = "0.29.0", features = ["user"] } [dev-dependencies] env_logger = "0.11" @@ -40,4 +42,4 @@ tracing = ["console-subscriber"] [[test]] name = "test_task" path = "pytests/test_task.rs" -harness = false \ No newline at end of file +harness = false diff --git a/mitmproxy-rs/mitmproxy_rs/tun.pyi b/mitmproxy-rs/mitmproxy_rs/tun.pyi index 99652906..52d898e8 100644 --- a/mitmproxy-rs/mitmproxy_rs/tun.pyi +++ b/mitmproxy-rs/mitmproxy_rs/tun.pyi @@ -4,6 +4,7 @@ from collections.abc import Awaitable, Callable from typing import final from . import Stream +def unavailable_reason() -> str | None: ... async def create_tun_interface( handle_tcp_stream: Callable[[Stream], Awaitable[None]], handle_udp_stream: Callable[[Stream], Awaitable[None]], diff --git a/mitmproxy-rs/src/lib.rs b/mitmproxy-rs/src/lib.rs index a44c885d..fbe3cebb 100644 --- a/mitmproxy-rs/src/lib.rs +++ b/mitmproxy-rs/src/lib.rs @@ -62,7 +62,7 @@ mod mitmproxy_rs { #[pymodule] mod tun { #[pymodule_export] - use crate::server::{create_tun_interface, TunInterface}; + use crate::server::{create_tun_interface, unavailable_reason, TunInterface}; } #[pymodule] diff --git a/mitmproxy-rs/src/server/mod.rs b/mitmproxy-rs/src/server/mod.rs index cfc29981..2b1aa8d8 100644 --- a/mitmproxy-rs/src/server/mod.rs +++ b/mitmproxy-rs/src/server/mod.rs @@ -5,6 +5,6 @@ mod udp; mod wireguard; pub use local_redirector::{start_local_redirector, LocalRedirector}; -pub use tun::{create_tun_interface, TunInterface}; +pub use tun::{create_tun_interface, unavailable_reason, TunInterface}; pub use udp::{start_udp_server, UdpServer}; pub use wireguard::{start_wireguard_server, WireGuardServer}; diff --git a/mitmproxy-rs/src/server/tun.rs b/mitmproxy-rs/src/server/tun.rs index 3a79b876..4525a4f9 100644 --- a/mitmproxy-rs/src/server/tun.rs +++ b/mitmproxy-rs/src/server/tun.rs @@ -1,6 +1,9 @@ use crate::server::base::Server; use pyo3::prelude::*; +#[cfg(target_os = "linux")] +use nix::unistd; + /// An open TUN interface. /// /// A new tun interface can be created by calling `create_tun_interface`. @@ -59,6 +62,22 @@ pub fn create_tun_interface( } #[cfg(not(target_os = "linux"))] Err(pyo3::exceptions::PyNotImplementedError::new_err( - "TUN proxy mode is only available on Linux", + unavailable_reason(), )) } + +/// Returns a `str` describing why tun mode is unavailable, or `None` if TUN mode is available. +/// +/// Reasons for unavailability may be an unsupported platform, or missing privileges. +#[pyfunction] +pub fn unavailable_reason() -> Option { + #[cfg(target_os = "linux")] + if !unistd::geteuid().is_root() { + Some(String::from("mitmproxy is not running as root")) + } else { + None + } + + #[cfg(not(target_os = "linux"))] + Some(String::from("OS not supported for TUN proxy mode")) +} diff --git a/src/network/virtual_device.rs b/src/network/virtual_device.rs index 62ce2966..ac7cfce2 100755 --- a/src/network/virtual_device.rs +++ b/src/network/virtual_device.rs @@ -29,8 +29,14 @@ impl VirtualDevice { } impl Device for VirtualDevice { - type RxToken<'a> = VirtualRxToken where Self: 'a; - type TxToken<'a> = VirtualTxToken<'a> where Self: 'a; + type RxToken<'a> + = VirtualRxToken + where + Self: 'a; + type TxToken<'a> + = VirtualTxToken<'a> + where + Self: 'a; fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { if self.rx_buffer.is_empty() {