mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-06-07 15:17:43 +00:00
Merge Android support branch
This commit is contained in:
commit
0c45714a45
7 changed files with 164 additions and 14 deletions
|
@ -20,10 +20,14 @@ log = "0.4"
|
||||||
mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
|
mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
|
||||||
nix = { version = "0.26", features = ["process", "signal"] }
|
nix = { version = "0.26", features = ["process", "signal"] }
|
||||||
prctl = "1.0"
|
prctl = "1.0"
|
||||||
smoltcp = { version = "0.9", git = "https://github.com/smoltcp-rs/smoltcp.git", features = ["std"] }
|
smoltcp = { version = "0.9", git = "https://github.com/smoltcp-rs/smoltcp", features = ["std"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
|
|
||||||
|
[target.'cfg(target_os="android")'.dependencies]
|
||||||
|
android_logger = "0.13"
|
||||||
|
jni = { version = "0.21", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
|
|
74
src/android.rs
Normal file
74
src/android.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#![cfg(target_os = "android")]
|
||||||
|
|
||||||
|
use crate::{error::Error, main_entry, shutdown, NetworkInterface, Options, Proxy};
|
||||||
|
use jni::{
|
||||||
|
objects::{JClass, JString},
|
||||||
|
sys::{jboolean, jint},
|
||||||
|
JNIEnv,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Running tun2proxy
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
|
||||||
|
mut env: JNIEnv,
|
||||||
|
_clazz: JClass,
|
||||||
|
proxy_url: JString,
|
||||||
|
tun_fd: jint,
|
||||||
|
tun_mtu: jint,
|
||||||
|
verbose: jboolean,
|
||||||
|
) -> jint {
|
||||||
|
let log_level = if verbose != 0 { "trace" } else { "info" };
|
||||||
|
let filter_str = &format!("off,tun2proxy={log_level}");
|
||||||
|
let filter = android_logger::FilterBuilder::new()
|
||||||
|
.parse(filter_str)
|
||||||
|
.build();
|
||||||
|
android_logger::init_once(
|
||||||
|
android_logger::Config::default()
|
||||||
|
.with_tag("tun2proxy")
|
||||||
|
.with_max_level(log::LevelFilter::Trace)
|
||||||
|
.with_filter(filter),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut block = || -> Result<(), Error> {
|
||||||
|
let proxy_url = get_java_string(&mut env, &proxy_url)?;
|
||||||
|
let proxy = Proxy::from_url(proxy_url)?;
|
||||||
|
|
||||||
|
let addr = proxy.addr;
|
||||||
|
let proxy_type = proxy.proxy_type;
|
||||||
|
log::info!("Proxy {proxy_type} server: {addr}");
|
||||||
|
|
||||||
|
let options = Options::new().with_virtual_dns().with_mtu(tun_mtu as usize);
|
||||||
|
|
||||||
|
let interface = NetworkInterface::Fd(tun_fd);
|
||||||
|
_ = main_entry(&interface, &proxy, options)?;
|
||||||
|
Ok::<(), Error>(())
|
||||||
|
};
|
||||||
|
if let Err(error) = block() {
|
||||||
|
log::error!("failed to run tun2proxy with error: {:?}", error);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Shutdown tun2proxy
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(
|
||||||
|
_env: JNIEnv,
|
||||||
|
_clazz: JClass,
|
||||||
|
) -> jint {
|
||||||
|
if let Err(e) = shutdown() {
|
||||||
|
log::error!("failed to shutdown tun2proxy with error: {:?}", e);
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_java_string<'a>(env: &'a mut JNIEnv, string: &'a JString) -> Result<&'a str, Error> {
|
||||||
|
let str_ptr = env.get_string(string)?.as_ptr();
|
||||||
|
let s: &str = std::ffi::CStr::from_ptr(str_ptr).to_str()?;
|
||||||
|
Ok(s)
|
||||||
|
}
|
|
@ -27,6 +27,13 @@ pub enum Error {
|
||||||
#[error("smoltcp::socket::tcp::SendError {0:?}")]
|
#[error("smoltcp::socket::tcp::SendError {0:?}")]
|
||||||
Send(#[from] smoltcp::socket::tcp::SendError),
|
Send(#[from] smoltcp::socket::tcp::SendError),
|
||||||
|
|
||||||
|
#[error("std::str::Utf8Error {0:?}")]
|
||||||
|
Utf8(#[from] std::str::Utf8Error),
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[error("jni::errors::Error {0:?}")]
|
||||||
|
Jni(#[from] jni::errors::Error),
|
||||||
|
|
||||||
#[error("&str {0}")]
|
#[error("&str {0}")]
|
||||||
Str(String),
|
Str(String),
|
||||||
|
|
||||||
|
|
24
src/lib.rs
24
src/lib.rs
|
@ -3,6 +3,7 @@ use crate::socks::SocksVersion;
|
||||||
use crate::{http::HttpManager, socks::SocksManager, tun2proxy::TunToProxy};
|
use crate::{http::HttpManager, socks::SocksManager, tun2proxy::TunToProxy};
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
|
|
||||||
|
mod android;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod http;
|
mod http;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
|
@ -18,6 +19,11 @@ pub struct Proxy {
|
||||||
pub credentials: Option<Credentials>,
|
pub credentials: Option<Credentials>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum NetworkInterface {
|
||||||
|
Named(String),
|
||||||
|
Fd(std::os::fd::RawFd),
|
||||||
|
}
|
||||||
|
|
||||||
impl Proxy {
|
impl Proxy {
|
||||||
pub fn from_url(s: &str) -> Result<Proxy, Error> {
|
pub fn from_url(s: &str) -> Result<Proxy, Error> {
|
||||||
let e = format!("`{s}` is not a valid proxy URL");
|
let e = format!("`{s}` is not a valid proxy URL");
|
||||||
|
@ -83,6 +89,7 @@ impl std::fmt::Display for ProxyType {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
virtdns: Option<virtdns::VirtualDns>,
|
virtdns: Option<virtdns::VirtualDns>,
|
||||||
|
mtu: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
|
@ -94,6 +101,11 @@ impl Options {
|
||||||
self.virtdns = Some(virtdns::VirtualDns::new());
|
self.virtdns = Some(virtdns::VirtualDns::new());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_mtu(mut self, mtu: usize) -> Self {
|
||||||
|
self.mtu = Some(mtu);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
|
@ -111,8 +123,12 @@ impl Credentials {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_entry(tun: &str, proxy: &Proxy, options: Options) -> Result<(), Error> {
|
pub fn main_entry(
|
||||||
let mut ttp = TunToProxy::new(tun, options)?;
|
interface: &NetworkInterface,
|
||||||
|
proxy: &Proxy,
|
||||||
|
options: Options,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut ttp = TunToProxy::new(interface, options)?;
|
||||||
match proxy.proxy_type {
|
match proxy.proxy_type {
|
||||||
ProxyType::Socks4 => {
|
ProxyType::Socks4 => {
|
||||||
ttp.add_connection_manager(SocksManager::new(
|
ttp.add_connection_manager(SocksManager::new(
|
||||||
|
@ -134,3 +150,7 @@ pub fn main_entry(tun: &str, proxy: &Proxy, options: Options) -> Result<(), Erro
|
||||||
}
|
}
|
||||||
ttp.run()
|
ttp.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shutdown() -> Result<(), Error> {
|
||||||
|
TunToProxy::shutdown()
|
||||||
|
}
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -5,8 +5,8 @@ use std::net::IpAddr;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
use tun2proxy::error::Error;
|
use tun2proxy::error::Error;
|
||||||
use tun2proxy::Options;
|
|
||||||
use tun2proxy::{main_entry, Proxy};
|
use tun2proxy::{main_entry, Proxy};
|
||||||
|
use tun2proxy::{NetworkInterface, Options};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use tun2proxy::setup::{get_default_cidrs, Setup};
|
use tun2proxy::setup::{get_default_cidrs, Setup};
|
||||||
|
@ -19,6 +19,14 @@ struct Args {
|
||||||
#[arg(short, long, value_name = "name", default_value = "tun0")]
|
#[arg(short, long, value_name = "name", default_value = "tun0")]
|
||||||
tun: String,
|
tun: String,
|
||||||
|
|
||||||
|
/// File descriptor of the tun interface
|
||||||
|
#[arg(long, value_name = "fd")]
|
||||||
|
tun_fd: Option<i32>,
|
||||||
|
|
||||||
|
/// MTU of the tun interface (only with tunnel file descriptor)
|
||||||
|
#[arg(long, value_name = "mtu", default_value = "1500")]
|
||||||
|
tun_mtu: usize,
|
||||||
|
|
||||||
/// Proxy URL in the form proto://[username[:password]@]host:port
|
/// Proxy URL in the form proto://[username[:password]@]host:port
|
||||||
#[arg(short, long, value_parser = Proxy::from_url, value_name = "URL")]
|
#[arg(short, long, value_parser = Proxy::from_url, value_name = "URL")]
|
||||||
proxy: Proxy,
|
proxy: Proxy,
|
||||||
|
@ -67,6 +75,14 @@ fn main() -> ExitCode {
|
||||||
options = options.with_virtual_dns();
|
options = options.with_virtual_dns();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interface = match args.tun_fd {
|
||||||
|
None => NetworkInterface::Named(args.tun.clone()),
|
||||||
|
Some(fd) => {
|
||||||
|
options = options.with_mtu(args.tun_mtu);
|
||||||
|
NetworkInterface::Fd(fd)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Err(e) = (|| -> Result<(), Error> {
|
if let Err(e) = (|| -> Result<(), Error> {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
@ -89,7 +105,7 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main_entry(&args.tun, &args.proxy, options)?;
|
main_entry(&interface, &args.proxy, options)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})() {
|
})() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::virtdevice::VirtualTunDevice;
|
use crate::virtdevice::VirtualTunDevice;
|
||||||
use crate::{Credentials, Options};
|
use crate::{Credentials, NetworkInterface, Options};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use mio::event::Event;
|
use mio::event::Event;
|
||||||
use mio::net::TcpStream;
|
use mio::net::TcpStream;
|
||||||
|
@ -244,6 +244,9 @@ pub(crate) trait ConnectionManager {
|
||||||
|
|
||||||
const TCP_TOKEN: Token = Token(0);
|
const TCP_TOKEN: Token = Token(0);
|
||||||
const UDP_TOKEN: Token = Token(1);
|
const UDP_TOKEN: Token = Token(1);
|
||||||
|
const EXIT_TOKEN: Token = Token(2);
|
||||||
|
|
||||||
|
const EXIT_LISTENER: &str = "127.0.0.1:34255";
|
||||||
|
|
||||||
pub(crate) struct TunToProxy<'a> {
|
pub(crate) struct TunToProxy<'a> {
|
||||||
tun: TunTapInterface,
|
tun: TunTapInterface,
|
||||||
|
@ -257,11 +260,17 @@ pub(crate) struct TunToProxy<'a> {
|
||||||
device: VirtualTunDevice,
|
device: VirtualTunDevice,
|
||||||
options: Options,
|
options: Options,
|
||||||
write_sockets: HashSet<Token>,
|
write_sockets: HashSet<Token>,
|
||||||
|
_exit_listener: mio::net::TcpListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TunToProxy<'a> {
|
impl<'a> TunToProxy<'a> {
|
||||||
pub(crate) fn new(interface: &str, options: Options) -> Result<Self, Error> {
|
pub(crate) fn new(interface: &NetworkInterface, options: Options) -> Result<Self, Error> {
|
||||||
let tun = TunTapInterface::new(interface, Medium::Ip)?;
|
let tun = match interface {
|
||||||
|
NetworkInterface::Named(name) => TunTapInterface::new(name.as_str(), Medium::Ip)?,
|
||||||
|
NetworkInterface::Fd(fd) => {
|
||||||
|
TunTapInterface::from_fd(*fd, Medium::Ip, options.mtu.unwrap_or(1500))?
|
||||||
|
}
|
||||||
|
};
|
||||||
let poll = Poll::new()?;
|
let poll = Poll::new()?;
|
||||||
poll.registry().register(
|
poll.registry().register(
|
||||||
&mut SourceFd(&tun.as_raw_fd()),
|
&mut SourceFd(&tun.as_raw_fd()),
|
||||||
|
@ -269,6 +278,10 @@ impl<'a> TunToProxy<'a> {
|
||||||
Interest::READABLE,
|
Interest::READABLE,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let mut _exit_listener = mio::net::TcpListener::bind(EXIT_LISTENER.parse()?)?;
|
||||||
|
poll.registry()
|
||||||
|
.register(&mut _exit_listener, EXIT_TOKEN, Interest::READABLE)?;
|
||||||
|
|
||||||
let config = match tun.capabilities().medium {
|
let config = match tun.capabilities().medium {
|
||||||
Medium::Ethernet => Config::new(
|
Medium::Ethernet => Config::new(
|
||||||
smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into(),
|
smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into(),
|
||||||
|
@ -293,17 +306,24 @@ impl<'a> TunToProxy<'a> {
|
||||||
poll,
|
poll,
|
||||||
iface,
|
iface,
|
||||||
connections: HashMap::default(),
|
connections: HashMap::default(),
|
||||||
next_token: 2,
|
next_token: usize::from(EXIT_TOKEN) + 1,
|
||||||
token_to_connection: HashMap::default(),
|
token_to_connection: HashMap::default(),
|
||||||
connection_managers: Vec::default(),
|
connection_managers: Vec::default(),
|
||||||
sockets: SocketSet::new([]),
|
sockets: SocketSet::new([]),
|
||||||
device: virt,
|
device: virt,
|
||||||
options,
|
options,
|
||||||
write_sockets: HashSet::default(),
|
write_sockets: HashSet::default(),
|
||||||
|
_exit_listener,
|
||||||
};
|
};
|
||||||
Ok(tun)
|
Ok(tun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_token(&mut self) -> Token {
|
||||||
|
let token = Token(self.next_token);
|
||||||
|
self.next_token += 1;
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn add_connection_manager(&mut self, manager: Rc<dyn ConnectionManager>) {
|
pub(crate) fn add_connection_manager(&mut self, manager: Rc<dyn ConnectionManager>) {
|
||||||
self.connection_managers.push(manager);
|
self.connection_managers.push(manager);
|
||||||
}
|
}
|
||||||
|
@ -495,8 +515,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
|
|
||||||
let client = TcpStream::connect(server)?;
|
let client = TcpStream::connect(server)?;
|
||||||
|
|
||||||
let token = Token(self.next_token);
|
let token = self.new_token();
|
||||||
self.next_token += 1;
|
|
||||||
|
|
||||||
let mut state = ConnectionState {
|
let mut state = ConnectionState {
|
||||||
smoltcp_handle: handle,
|
smoltcp_handle: handle,
|
||||||
|
@ -757,6 +776,10 @@ impl<'a> TunToProxy<'a> {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
match event.token() {
|
match event.token() {
|
||||||
|
EXIT_TOKEN => {
|
||||||
|
log::info!("exiting...");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
TCP_TOKEN => self.tun_event(event)?,
|
TCP_TOKEN => self.tun_event(event)?,
|
||||||
UDP_TOKEN => self.udp_event(event),
|
UDP_TOKEN => self.udp_event(event),
|
||||||
_ => self.mio_socket_event(event)?,
|
_ => self.mio_socket_event(event)?,
|
||||||
|
@ -768,10 +791,16 @@ impl<'a> TunToProxy<'a> {
|
||||||
if e.kind() != std::io::ErrorKind::Interrupted {
|
if e.kind() != std::io::ErrorKind::Interrupted {
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Poll interrupted")
|
log::warn!("Poll interrupted: {e}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn shutdown() -> Result<(), Error> {
|
||||||
|
let addr: SocketAddr = EXIT_LISTENER.parse()?;
|
||||||
|
let _ = std::net::TcpStream::connect(addr)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ mod tests {
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
use tun2proxy::setup::{get_default_cidrs, Setup};
|
use tun2proxy::setup::{get_default_cidrs, Setup};
|
||||||
use tun2proxy::{main_entry, Options, Proxy, ProxyType};
|
use tun2proxy::{main_entry, NetworkInterface, Options, Proxy, ProxyType};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Test {
|
struct Test {
|
||||||
|
@ -85,7 +85,7 @@ mod tests {
|
||||||
Ok(Fork::Child) => {
|
Ok(Fork::Child) => {
|
||||||
prctl::set_death_signal(signal::SIGINT as isize).unwrap();
|
prctl::set_death_signal(signal::SIGINT as isize).unwrap();
|
||||||
let _ = main_entry(
|
let _ = main_entry(
|
||||||
TUN_TEST_DEVICE,
|
&NetworkInterface::Named(TUN_TEST_DEVICE.into()),
|
||||||
&test.proxy,
|
&test.proxy,
|
||||||
Options::new().with_virtual_dns(),
|
Options::new().with_virtual_dns(),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue