Skip to content

Commit 5d81fd6

Browse files
authored
Feature: Support Android Platform.
1 parent 4177abf commit 5d81fd6

File tree

6 files changed

+450
-8
lines changed

6 files changed

+450
-8
lines changed

Cargo.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ chrono = { version = "0.4.41", default-features = false, features = [
2424
lazy_static = "1.5.0"
2525
libc = "0.2.174"
2626
local-ip-address = "0.6.5"
27-
native-dialog = "0.9.0"
2827
num-bigint = "0.4.6"
2928
open = { version = "5.3.2", default-features = false }
3029
rand_core = { version = "0.9.3", features = ["os_rng"] }
@@ -47,6 +46,13 @@ objc2 = "0.6.1"
4746
objc2-app-kit = "0.3.1"
4847
objc2-foundation = "0.3.1"
4948
objc2-web-kit = "0.3.1"
49+
native-dialog = "0.9.0"
50+
51+
[target.'cfg(target_os = "android")'.dependencies]
52+
easytier = { git = "https://github.com/burningtnt/EasyTier.git", branch = "develop"}
53+
uuid = "1.18.1" # This library are the necessities to interact with EasyTier. DO NOT upgrade their version.
54+
toml = "0.9.8"
55+
tokio = "1.48.0"
5056

5157
[build-dependencies]
5258
winresource = "0.1"
@@ -60,6 +66,13 @@ reqwest = { version = "0.12.22", default-features = false, features = [
6066
zip = "4.3.0"
6167
toml = "0.9.4"
6268

69+
[lib]
70+
crate-type = ["cdylib"]
71+
72+
[[bin]]
73+
name = "terracotta"
74+
path = "src/main.rs"
75+
6376
[profile.dev]
6477
panic = "abort"
6578

build.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ use std::{
1111
fn main() {
1212
println!("cargo::rerun-if-changed=Cargo.toml");
1313

14-
println!("cargo::rerun-if-changed=.easytier");
1514
download_easytier();
1615

1716
sevenz_rust2::compress_to_path(
1817
"web",
1918
Path::new(&get_var("OUT_DIR").unwrap()).join("webstatics.7z"),
2019
)
21-
.unwrap();
20+
.unwrap();
2221
println!("cargo::rerun-if-changed=web");
2322

2423
let desc = get_var("TARGET").unwrap().replace('-', "_").to_uppercase();
@@ -84,6 +83,7 @@ fn download_easytier() {
8483

8584
input.get("version").unwrap().as_str().unwrap().to_string()
8685
};
86+
println!("cargo::rustc-env=TERRACOTTA_ET_VERSION={}", version);
8787

8888
let target_os = get_var("CARGO_CFG_TARGET_OS").unwrap();
8989
let target_arch = get_var("CARGO_CFG_TARGET_ARCH").unwrap();
@@ -181,9 +181,15 @@ fn download_easytier() {
181181
cli: "easytier-cli",
182182
desc: "freebsd-x86_64",
183183
},
184-
_ => panic!("Cannot compile Terracotta on {}-{}: Cannot find valid EasyTier binary.", target_os, target_arch)
184+
("android", "aarch64") => return,
185+
_ => panic!(
186+
"Cannot compile Terracotta on {}-{}: Cannot find valid EasyTier binary.",
187+
target_os, target_arch
188+
),
185189
};
186190

191+
println!("cargo::rerun-if-changed=.easytier");
192+
187193
let base = Path::new(&get_var("CARGO_MANIFEST_DIR").unwrap())
188194
.join(".easytier")
189195
.join(&version)
@@ -203,7 +209,6 @@ fn download_easytier() {
203209
"cargo::rustc-env=TERRACOTTA_ET_ARCHIVE={}",
204210
entry_archive.as_path().to_str().unwrap()
205211
);
206-
println!("cargo::rustc-env=TERRACOTTA_ET_VERSION={}", version);
207212

208213
if fs::metadata(entry_conf.clone()).is_ok() {
209214
return;

src/easytier/linkage_impl.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
use std::cell::UnsafeCell;
2+
use crate::easytier::argument::{Argument, PortForward};
3+
use easytier::common::config::{PortForwardConfig, TomlConfigLoader};
4+
use easytier::launcher::NetworkInstance;
5+
use easytier::proto::api::instance::{ListRouteRequest, Route};
6+
use easytier::proto::rpc_types::controller::BaseController;
7+
use easytier::socks5::Socks5Server;
8+
use std::fmt::Write;
9+
use std::net::Ipv4Addr;
10+
use std::sync::Arc;
11+
use tokio::runtime::Handle;
12+
use toml::{Table, Value};
13+
14+
lazy_static::lazy_static! {
15+
pub static ref FACTORY: EasytierFactory = create();
16+
}
17+
18+
pub struct EasytierFactory();
19+
20+
pub struct Easytier {
21+
instance: Option<(NetworkInstance, Arc<Socks5Server>)>,
22+
}
23+
24+
fn create() -> EasytierFactory {
25+
EasytierFactory()
26+
}
27+
28+
impl EasytierFactory {
29+
pub fn create(&self, args: Vec<Argument>) -> Easytier {
30+
let table = UnsafeCell::new(Table::new());
31+
let acquire_table = || {
32+
unsafe {
33+
table.as_mut_unchecked()
34+
}
35+
};
36+
37+
acquire_table().insert("flags".into(), Value::Table(Table::new()));
38+
let flags = || acquire_table().get_mut("flags").unwrap().as_table_mut().unwrap();
39+
40+
acquire_table().insert("network_identity".into(), Value::Table(Table::new()));
41+
let identity = || acquire_table().get_mut("network_identity").unwrap().as_table_mut().unwrap();
42+
43+
acquire_table().insert("listeners".into(), Value::Array(vec![]));
44+
let listeners = || acquire_table().get_mut("listeners").unwrap().as_array_mut().unwrap();
45+
46+
acquire_table().insert("peer".into(), Value::Array(vec![]));
47+
let peer = || acquire_table().get_mut("peer").unwrap().as_array_mut().unwrap();
48+
49+
acquire_table().insert("port_forward".into(), Value::Array(vec![]));
50+
let forwards = || acquire_table().get_mut("port_forward").unwrap().as_array_mut().unwrap();
51+
52+
acquire_table().insert("tcp_whitelist".into(), Value::Array(vec![]));
53+
let tcp_whitelist = || acquire_table().get_mut("tcp_whitelist").unwrap().as_array_mut().unwrap();
54+
55+
acquire_table().insert("udp_whitelist".into(), Value::Array(vec![]));
56+
let udp_whitelist = || acquire_table().get_mut("udp_whitelist").unwrap().as_array_mut().unwrap();
57+
58+
for arg in args {
59+
match arg {
60+
Argument::NoTun => {
61+
flags().insert("no_tun".into(), Value::Boolean(true));
62+
}
63+
Argument::Compression(name) => {
64+
flags().insert("data_compress_algo".into(), Value::Integer(match name.as_ref() {
65+
"zstd" => 2,
66+
_ => unimplemented!(),
67+
}));
68+
}
69+
Argument::MultiThread => {
70+
flags().insert("multi_thread".into(), Value::Boolean(true));
71+
}
72+
Argument::LatencyFirst => {
73+
flags().insert("latency_first".into(), Value::Boolean(true));
74+
}
75+
Argument::EnableKcpProxy => {
76+
flags().insert("enable_kcp_proxy".into(), Value::Boolean(true));
77+
}
78+
Argument::PublicServer(server) => {
79+
peer().push(Value::String(server.into()));
80+
}
81+
Argument::NetworkName(name) => {
82+
identity().insert("network_name".into(), Value::String(name.into()));
83+
}
84+
Argument::NetworkSecret(secret) => {
85+
identity().insert("network_secret".into(), Value::String(secret.into()));
86+
}
87+
Argument::Listener { address, proto } => {
88+
listeners().push(Value::String(format!("{}://{}", proto.name(), address)));
89+
}
90+
Argument::PortForward(PortForward { local, remote, proto }) => {
91+
let mut forward = Table::new();
92+
forward.insert("bind_addr".into(), Value::String(local.to_string()));
93+
forward.insert("dst_addr".into(), Value::String(remote.to_string()));
94+
forward.insert("proto".into(), Value::String(proto.name().into()));
95+
forwards().push(Value::Table(forward));
96+
}
97+
Argument::DHCP => {
98+
acquire_table().insert("dhcp".into(), Value::Boolean(true));
99+
}
100+
Argument::HostName(name) => {
101+
acquire_table().insert("hostname".into(), Value::String(name.into()));
102+
}
103+
Argument::IPv4(address) => {
104+
acquire_table().insert("ipv4".into(), Value::String(address.to_string()));
105+
}
106+
Argument::TcpWhitelist(port) => {
107+
tcp_whitelist().push(Value::Integer(port as i64));
108+
}
109+
Argument::UdpWhitelist(port) => {
110+
udp_whitelist().push(Value::Integer(port as i64));
111+
}
112+
}
113+
}
114+
115+
let instance = toml::to_string(&Value::Table(table.into_inner())).ok()
116+
.and_then(|str| TomlConfigLoader::new_from_str(str.as_str()).ok())
117+
.map(|config| NetworkInstance::new(config));
118+
let instance = if let Some(mut instance) = instance && let Ok((_, server)) = instance.start() {
119+
Some((instance, server.unwrap()))
120+
} else {
121+
None
122+
};
123+
Easytier { instance }
124+
}
125+
126+
pub fn remove(&self) {}
127+
}
128+
129+
impl Easytier {
130+
pub fn is_alive(&mut self) -> bool {
131+
self.instance.as_ref().is_some_and(|(instance, _)| instance.is_easytier_running())
132+
}
133+
134+
pub fn get_players(&mut self) -> Option<Vec<(String, Ipv4Addr)>> {
135+
self.instance.as_ref()
136+
.and_then(|(instance, _)| {
137+
instance.get_api_service()
138+
.and_then(|service| {
139+
Handle::current().block_on(service.get_peer_manage_service()
140+
.list_route(BaseController::default(), ListRouteRequest::default())
141+
).ok()
142+
})
143+
.map(|response| response.routes)
144+
})
145+
.map(|info: Vec<Route>| {
146+
info.into_iter()
147+
.filter_map(|route| route.ipv4_addr
148+
.and_then(|address| address.address)
149+
.map(|address| (route.hostname, Ipv4Addr::from_octets(address.addr.to_be_bytes())))
150+
)
151+
.collect::<Vec<_>>()
152+
})
153+
}
154+
155+
pub fn add_port_forward(
156+
&mut self,
157+
forwards: &[PortForward],
158+
) -> bool {
159+
if let Some((_, socks5)) = self.instance.as_ref() {
160+
let mut stream = forwards.iter().map(|forward| {
161+
let task = socks5.add_port_forward(PortForwardConfig {
162+
bind_addr: forward.local,
163+
dst_addr: forward.remote,
164+
proto: forward.proto.name().into(),
165+
});
166+
167+
(task, forward)
168+
}).filter_map(|(task, forward)| {
169+
Handle::current().block_on(task).err().map(|e| (e, forward))
170+
});
171+
172+
if let Some(mut item) = stream.next() {
173+
let mut msg = "Cannot adding port-forward rules: ".to_string();
174+
loop {
175+
let (e, PortForward { local, remote, proto }) = item;
176+
write!(&mut msg, "{} -> {} ({}): {:?}", local, remote, proto.name(), e).unwrap();
177+
178+
if let Some(item2) = stream.next() {
179+
msg.push_str(", ");
180+
item = item2;
181+
} else {
182+
break;
183+
}
184+
}
185+
logging!("EasyTier CLI", "{}", msg);
186+
} else {
187+
return true;
188+
}
189+
}
190+
return false;
191+
}
192+
}
193+
194+
impl Drop for Easytier {
195+
fn drop(&mut self) {
196+
logging!("EasyTier", "Killing EasyTier.");
197+
198+
self.instance.take()
199+
.and_then(|(instance, _)| instance.get_stop_notifier())
200+
.map(|stop| {
201+
Handle::current().block_on(stop.notified());
202+
});
203+
}
204+
}

src/easytier/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#[cfg(not(target_os = "android"))]
22
mod executable_impl;
3-
pub mod argument;
4-
53
#[cfg(not(target_os = "android"))]
6-
pub use executable_impl::*;
4+
pub use executable_impl::*;
5+
6+
#[cfg(target_os = "android")]
7+
mod linkage_impl;
8+
#[cfg(target_os = "android")]
9+
pub use linkage_impl::*;
10+
11+
pub mod argument;

0 commit comments

Comments
 (0)