This commit is contained in:
lbl
2026-02-24 18:31:40 +08:00
parent ecdb359bbd
commit 5a31ad89f4
14 changed files with 2290 additions and 4 deletions
Generated
+121
View File
@@ -360,6 +360,12 @@ dependencies = [
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -452,6 +458,16 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -1182,6 +1198,28 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "js-sys"
version = "0.3.85"
@@ -3206,6 +3244,23 @@ dependencies = [
"vnt-core",
]
[[package]]
name = "vnt-jni"
version = "2.0.0"
dependencies = [
"anyhow",
"hostname",
"ipnet",
"jni",
"lazy_static",
"log",
"parking_lot 0.12.5",
"serde",
"serde_json",
"tokio",
"vnt-core",
]
[[package]]
name = "vnt-web"
version = "2.0.0"
@@ -3528,6 +3583,15 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -3564,6 +3628,21 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -3621,6 +3700,12 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -3639,6 +3724,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -3657,6 +3748,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -3687,6 +3784,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -3705,6 +3808,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -3723,6 +3832,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -3741,6 +3856,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
+1 -1
View File
@@ -43,7 +43,7 @@ path = "src/main_web.rs"
required-features = ["vnt-web"]
[workspace]
members = ["vnt-web", "vnt-ipc", "vnt-core"]
members = ["vnt-web", "vnt-ipc", "vnt-core", "vnt-jni"]
[profile.release]
opt-level = 'z'
+1 -1
View File
@@ -108,7 +108,7 @@ async fn main0() -> anyhow::Result<()> {
log::info!("启动网络:{}/{}", reg_msg.ip, reg_msg.prefix_len);
network_manager.start_tun().await.context("start tun")?;
network_manager
.set_network_ip(reg_msg.ip, reg_msg.prefix_len)
.set_tun_network_ip(reg_msg.ip, reg_msg.prefix_len)
.await
.context("set network ip")?;
if !sub_input.is_empty() {
+1 -1
View File
@@ -324,7 +324,7 @@ impl NetworkManager {
.await
}
#[cfg(not(target_os = "android"))]
pub async fn set_network_ip(&self, ip: Ipv4Addr, prefix_len: u8) -> anyhow::Result<()> {
pub async fn set_tun_network_ip(&self, ip: Ipv4Addr, prefix_len: u8) -> anyhow::Result<()> {
self.device_io_manager.set_network(ip, prefix_len).await?;
Ok(())
}
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "vnt-jni"
version = "2.0.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
vnt-core = { path = "../vnt-core" }
jni = "0.21"
tokio = { version = "1", features = ["full"] }
parking_lot = "0.12"
log = "0.4"
anyhow = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
ipnet = { version = "2.11", features = ["serde"] }
lazy_static = "1.5"
hostname = "0.4.2"
+144
View File
@@ -0,0 +1,144 @@
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import com.vnt.*;
/**
* Android VPN服务示例
*
* 演示如何正确使用VNT JNI接口:
* 1. 创建网络
* 2. 注册获取IP/掩码
* 3. 用获取的参数建立Android VPN接口
* 4. 传入tunFd启动VNT
*/
public class AndroidVpnExample extends VpnService {
private VntNetwork network;
private ParcelFileDescriptor vpnInterface;
@Override
public int onStartCommand(android.content.Intent intent, int flags, int startId) {
try {
startVpn();
return START_STICKY;
} catch (Exception e) {
e.printStackTrace();
stopSelf();
return START_NOT_STICKY;
}
}
private void startVpn() throws Exception {
// 1. 初始化VNT(全局初始化,只需一次)
if (!VntManager.init()) {
throw new VntException("Failed to initialize VNT");
}
// 2. 构建配置
VntConfig config = new VntConfig.Builder()
.addServer("tcp://101.35.230.139:6660")
.setNetworkCode("your_network_code")
.setPassword("123456")
.setDeviceName("AndroidDevice")
.setCompress(true)
.setMtu(1380)
.build();
// 3. 创建网络实例
network = VntManager.createNetwork(config);
if (network == null) {
throw new VntException("Failed to create network");
}
// 4. 注册网络(连接服务器,获取分配的IP和掩码)
RegisterResult result = network.register();
System.out.println("Registration successful: " + result);
// 5. 使用注册返回的IP和掩码,建立Android VPN接口
VpnService.Builder builder = new Builder();
builder.setMtu(1380);
builder.addAddress(result.getIp(), result.getPrefixLen());
builder.addRoute("0.0.0.0", 0); // 全局路由
builder.setSession("VNT VPN");
// 建立VPN接口,获取文件描述符
vpnInterface = builder.establish();
if (vpnInterface == null) {
throw new VntException("Failed to establish VPN interface");
}
int tunFd = vpnInterface.getFd();
System.out.println("VPN interface established, fd: " + tunFd);
// 6. 将tunFd传给VNT,启动数据转发
network.startTun(tunFd);
System.out.println("VNT started successfully!");
// 7. 获取API用于查询状态
VntApi api = network.getApi();
// 8. 查询网络信息
VntApi.NetworkInfo networkInfo = api.getNetwork();
System.out.println("Network info: " + networkInfo);
// 9. 查询NAT信息
VntApi.NatInfo natInfo = api.getNatInfo();
System.out.println("NAT info: " + natInfo);
}
@Override
public void onDestroy() {
super.onDestroy();
// 清理资源
if (network != null) {
network.stop();
network = null;
}
if (vpnInterface != null) {
try {
vpnInterface.close();
} catch (Exception e) {
e.printStackTrace();
}
vpnInterface = null;
}
VntManager.destroy();
}
/**
* 查询客户端列表(可在UI线程定期调用)
*/
public void queryClients() {
if (network == null) {
return;
}
try {
VntApi api = network.getApi();
for (VntApi.ClientInfo client : api.getClientList()) {
System.out.println("Client: " + client);
// 检查是否直连
boolean direct = api.isDirect(client.getIp());
System.out.println(" Direct: " + direct);
// 获取丢包信息
VntApi.PacketLossInfo loss = api.getPacketLoss(client.getIp());
if (loss != null) {
System.out.println(" Packet loss: " + loss);
}
// 获取流量信息
VntApi.TrafficInfo traffic = api.getTrafficInfo(client.getIp());
if (traffic != null) {
System.out.println(" Traffic: " + traffic);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,97 @@
package com.vnt;
import org.json.JSONObject;
/**
* 注册结果
*
* 包含服务器分配的IP地址、掩码等信息
* 注意:如果能创建此对象,说明注册一定成功了(失败会抛异常)
*/
public class RegisterResult {
private final String ip;
private final int prefixLen;
private final String gateway;
private final String broadcast;
private RegisterResult(String ip, int prefixLen, String gateway, String broadcast) {
this.ip = ip;
this.prefixLen = prefixLen;
this.gateway = gateway;
this.broadcast = broadcast;
}
/**
* 从JSON字符串解析注册结果
* @throws VntException 如果注册失败或解析失败
*/
static RegisterResult fromJson(String json) throws VntException {
try {
JSONObject obj = new JSONObject(json);
boolean success = obj.getBoolean("success");
if (success) {
return new RegisterResult(
obj.getString("ip"),
obj.getInt("prefix_len"),
obj.getString("gateway"),
obj.getString("broadcast")
);
} else {
// 注册失败,抛出异常
String error = obj.getString("error");
throw new VntException("Registration failed: " + error);
}
} catch (VntException e) {
throw e; // 重新抛出VntException
} catch (Exception e) {
throw new VntException("Failed to parse register result: " + e.getMessage(), e);
}
}
/**
* 获取分配的IP地址
*/
public String getIp() {
return ip;
}
/**
* 获取前缀长度(掩码位数)
*/
public int getPrefixLen() {
return prefixLen;
}
/**
* 获取网关地址
*/
public String getGateway() {
return gateway;
}
/**
* 获取广播地址
*/
public String getBroadcast() {
return broadcast;
}
/**
* 转换为CIDR格式字符串(例如:10.0.0.2/24
*/
public String toCidr() {
return ip + "/" + prefixLen;
}
@Override
public String toString() {
return "RegisterResult{" +
"ip='" + ip + '\'' +
", prefixLen=" + prefixLen +
", gateway='" + gateway + '\'' +
", broadcast='" + broadcast + '\'' +
'}';
}
}
+428
View File
@@ -0,0 +1,428 @@
package com.vnt;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* VNT API接口 - 用于查询网络状态和信息
*
* 通过VntNetwork.getApi()获取实例
*/
public class VntApi {
private final long nativeHandle;
// 包内构造,只能通过VntNetwork创建
VntApi(long handle) {
this.nativeHandle = handle;
}
/**
* 获取客户端列表
* @return 客户端信息列表
*/
public List<ClientInfo> getClientList() throws VntException {
try {
String json = nativeGetClientList(nativeHandle);
JSONArray array = new JSONArray(json);
List<ClientInfo> clients = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
clients.add(new ClientInfo(
obj.getString("ip"),
obj.getBoolean("online")
));
}
return clients;
} catch (Exception e) {
throw new VntException("Failed to get client list: " + e.getMessage(), e);
}
}
/**
* 获取当前网络配置
* @return 网络信息,未连接返回null
*/
public NetworkInfo getNetwork() throws VntException {
try {
String json = nativeGetNetwork(nativeHandle);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new NetworkInfo(
obj.getString("ip"),
obj.getInt("prefix_len"),
obj.getString("gateway"),
obj.getString("broadcast")
);
} catch (Exception e) {
throw new VntException("Failed to get network info: " + e.getMessage(), e);
}
}
/**
* 获取本地NAT信息
* @return NAT信息,未检测到返回null
*/
public NatInfo getNatInfo() throws VntException {
try {
String json = nativeGetNatInfo(nativeHandle);
if ("null".equals(json)) {
return null;
}
return NatInfo.fromJson(json);
} catch (Exception e) {
throw new VntException("Failed to get NAT info: " + e.getMessage(), e);
}
}
/**
* 获取服务器节点列表
* @return 服务器信息列表
*/
public List<ServerInfo> getServerList() throws VntException {
try {
String json = nativeGetServerList(nativeHandle);
JSONArray array = new JSONArray(json);
List<ServerInfo> servers = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
servers.add(new ServerInfo(
obj.getInt("server_id"),
obj.getString("server_addr"),
obj.getBoolean("connected"),
obj.isNull("rtt") ? null : obj.getInt("rtt"),
obj.getLong("data_version"),
obj.isNull("server_version") ? null : obj.getString("server_version")
));
}
return servers;
} catch (Exception e) {
throw new VntException("Failed to get server list: " + e.getMessage(), e);
}
}
/**
* 获取路由表
* @return 路由信息列表
*/
public List<RouteInfo> getRouteTable() throws VntException {
try {
String json = nativeGetRouteTable(nativeHandle);
JSONArray array = new JSONArray(json);
List<RouteInfo> routes = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
String ip = obj.getString("ip");
JSONArray routesArray = obj.getJSONArray("routes");
List<RouteDetail> details = new ArrayList<>();
for (int j = 0; j < routesArray.length(); j++) {
JSONObject route = routesArray.getJSONObject(j);
details.add(new RouteDetail(
route.getString("route_key"),
route.getString("protocol"),
route.getInt("metric"),
route.getInt("rtt")
));
}
routes.add(new RouteInfo(ip, details));
}
return routes;
} catch (Exception e) {
throw new VntException("Failed to get route table: " + e.getMessage(), e);
}
}
/**
* 检查目标IP是否直连(P2P)
* @param ip 目标IP地址
* @return true表示直连,false表示通过服务器中转
*/
public boolean isDirect(String ip) {
return nativeIsDirect(nativeHandle, ip);
}
/**
* 获取对端NAT信息
* @param ip 目标IP地址
* @return NAT信息,未知返回null
*/
public NatInfo getPeerNatInfo(String ip) throws VntException {
try {
String json = nativeGetPeerNatInfo(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
return NatInfo.fromJson(json);
} catch (Exception e) {
throw new VntException("Failed to get peer NAT info: " + e.getMessage(), e);
}
}
/**
* 获取对端丢包信息
* @param ip 目标IP地址
* @return 丢包信息,未知返回null
*/
public PacketLossInfo getPacketLoss(String ip) throws VntException {
try {
String json = nativeGetPacketLoss(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new PacketLossInfo(
obj.getString("ip"),
obj.getLong("sent"),
obj.getLong("received"),
obj.getDouble("loss_rate")
);
} catch (Exception e) {
throw new VntException("Failed to get packet loss: " + e.getMessage(), e);
}
}
/**
* 获取对端流量统计
* @param ip 目标IP地址
* @return 流量信息,未知返回null
*/
public TrafficInfo getTrafficInfo(String ip) throws VntException {
try {
String json = nativeGetTrafficInfo(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new TrafficInfo(
obj.getString("ip"),
obj.getLong("tx_bytes"),
obj.getLong("rx_bytes")
);
} catch (Exception e) {
throw new VntException("Failed to get traffic info: " + e.getMessage(), e);
}
}
// ========== Native 方法 ==========
private static native String nativeGetClientList(long apiHandle);
private static native String nativeGetNetwork(long apiHandle);
private static native String nativeGetNatInfo(long apiHandle);
private static native String nativeGetServerList(long apiHandle);
private static native String nativeGetRouteTable(long apiHandle);
private static native boolean nativeIsDirect(long apiHandle, String ip);
private static native String nativeGetPeerNatInfo(long apiHandle, String ip);
private static native String nativeGetPacketLoss(long apiHandle, String ip);
private static native String nativeGetTrafficInfo(long apiHandle, String ip);
// ========== 数据类 ==========
public static class ClientInfo {
private final String ip;
private final boolean online;
public ClientInfo(String ip, boolean online) {
this.ip = ip;
this.online = online;
}
public String getIp() { return ip; }
public boolean isOnline() { return online; }
@Override
public String toString() {
return "ClientInfo{ip='" + ip + "', online=" + online + "}";
}
}
public static class NetworkInfo {
private final String ip;
private final int prefixLen;
private final String gateway;
private final String broadcast;
public NetworkInfo(String ip, int prefixLen, String gateway, String broadcast) {
this.ip = ip;
this.prefixLen = prefixLen;
this.gateway = gateway;
this.broadcast = broadcast;
}
public String getIp() { return ip; }
public int getPrefixLen() { return prefixLen; }
public String getGateway() { return gateway; }
public String getBroadcast() { return broadcast; }
@Override
public String toString() {
return "NetworkInfo{ip='" + ip + "', prefixLen=" + prefixLen +
", gateway='" + gateway + "', broadcast='" + broadcast + "'}";
}
}
public static class NatInfo {
private final String natType;
private final List<String> publicIps;
private final String ipv6;
private NatInfo(String natType, List<String> publicIps, String ipv6) {
this.natType = natType;
this.publicIps = publicIps;
this.ipv6 = ipv6;
}
static NatInfo fromJson(String json) throws Exception {
JSONObject obj = new JSONObject(json);
JSONArray ipsArray = obj.getJSONArray("public_ips");
List<String> publicIps = new ArrayList<>();
for (int i = 0; i < ipsArray.length(); i++) {
publicIps.add(ipsArray.getString(i));
}
return new NatInfo(
obj.getString("nat_type"),
publicIps,
obj.isNull("ipv6") ? null : obj.getString("ipv6")
);
}
public String getNatType() { return natType; }
public List<String> getPublicIps() { return publicIps; }
public String getIpv6() { return ipv6; }
@Override
public String toString() {
return "NatInfo{natType='" + natType + "', publicIps=" + publicIps +
", ipv6='" + ipv6 + "'}";
}
}
public static class ServerInfo {
private final int serverId;
private final String serverAddr;
private final boolean connected;
private final Integer rtt;
private final long dataVersion;
private final String serverVersion;
public ServerInfo(int serverId, String serverAddr, boolean connected,
Integer rtt, long dataVersion, String serverVersion) {
this.serverId = serverId;
this.serverAddr = serverAddr;
this.connected = connected;
this.rtt = rtt;
this.dataVersion = dataVersion;
this.serverVersion = serverVersion;
}
public int getServerId() { return serverId; }
public String getServerAddr() { return serverAddr; }
public boolean isConnected() { return connected; }
public Integer getRtt() { return rtt; }
public long getDataVersion() { return dataVersion; }
public String getServerVersion() { return serverVersion; }
@Override
public String toString() {
return "ServerInfo{serverId=" + serverId + ", serverAddr='" + serverAddr +
"', connected=" + connected + ", rtt=" + rtt + "}";
}
}
public static class RouteInfo {
private final String ip;
private final List<RouteDetail> routes;
public RouteInfo(String ip, List<RouteDetail> routes) {
this.ip = ip;
this.routes = routes;
}
public String getIp() { return ip; }
public List<RouteDetail> getRoutes() { return routes; }
@Override
public String toString() {
return "RouteInfo{ip='" + ip + "', routes=" + routes + "}";
}
}
public static class RouteDetail {
private final String routeKey;
private final String protocol;
private final int metric;
private final int rtt;
public RouteDetail(String routeKey, String protocol, int metric, int rtt) {
this.routeKey = routeKey;
this.protocol = protocol;
this.metric = metric;
this.rtt = rtt;
}
public String getRouteKey() { return routeKey; }
public String getProtocol() { return protocol; }
public int getMetric() { return metric; }
public int getRtt() { return rtt; }
@Override
public String toString() {
return "RouteDetail{routeKey='" + routeKey + "', protocol='" + protocol +
"', metric=" + metric + ", rtt=" + rtt + "}";
}
}
public static class PacketLossInfo {
private final String ip;
private final long sent;
private final long received;
private final double lossRate;
public PacketLossInfo(String ip, long sent, long received, double lossRate) {
this.ip = ip;
this.sent = sent;
this.received = received;
this.lossRate = lossRate;
}
public String getIp() { return ip; }
public long getSent() { return sent; }
public long getReceived() { return received; }
public double getLossRate() { return lossRate; }
@Override
public String toString() {
return "PacketLossInfo{ip='" + ip + "', sent=" + sent +
", received=" + received + ", lossRate=" + lossRate + "}";
}
}
public static class TrafficInfo {
private final String ip;
private final long txBytes;
private final long rxBytes;
public TrafficInfo(String ip, long txBytes, long rxBytes) {
this.ip = ip;
this.txBytes = txBytes;
this.rxBytes = rxBytes;
}
public String getIp() { return ip; }
public long getTxBytes() { return txBytes; }
public long getRxBytes() { return rxBytes; }
@Override
public String toString() {
return "TrafficInfo{ip='" + ip + "', txBytes=" + txBytes +
", rxBytes=" + rxBytes + "}";
}
}
}
+310
View File
@@ -0,0 +1,310 @@
package com.vnt;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* VNT网络配置
*
* 使用Builder模式构建配置
*/
public class VntConfig {
private final List<String> servers;
private final String networkCode;
private final String password;
private final String deviceId;
private final String deviceName;
private final String tunName;
private final String ip;
private final String certMode;
private final boolean noPunch;
private final boolean compress;
private final boolean rtx;
private final boolean fec;
private final boolean noNat;
private final boolean noTun;
private final Integer mtu;
private final boolean allowMapping;
private final List<String> portMapping;
private final List<String> udpStun;
private final List<String> tcpStun;
private VntConfig(Builder builder) {
this.servers = builder.servers;
this.networkCode = builder.networkCode;
this.password = builder.password;
this.deviceId = builder.deviceId;
this.deviceName = builder.deviceName;
this.tunName = builder.tunName;
this.ip = builder.ip;
this.certMode = builder.certMode;
this.noPunch = builder.noPunch;
this.compress = builder.compress;
this.rtx = builder.rtx;
this.fec = builder.fec;
this.noNat = builder.noNat;
this.noTun = builder.noTun;
this.mtu = builder.mtu;
this.allowMapping = builder.allowMapping;
this.portMapping = builder.portMapping;
this.udpStun = builder.udpStun;
this.tcpStun = builder.tcpStun;
}
/**
* 转换为JSON字符串
*/
String toJson() {
JSONObject json = new JSONObject();
// 必填项
JSONArray serverArray = new JSONArray();
for (String server : servers) {
serverArray.put(server);
}
json.put("server", serverArray);
json.put("network_code", networkCode);
// 可选项
if (password != null) json.put("password", password);
if (deviceId != null) json.put("device_id", deviceId);
if (deviceName != null) json.put("device_name", deviceName);
if (tunName != null) json.put("tun_name", tunName);
if (ip != null) json.put("ip", ip);
if (certMode != null) json.put("cert_mode", certMode);
if (mtu != null) json.put("mtu", mtu);
// 布尔值
json.put("no_punch", noPunch);
json.put("compress", compress);
json.put("rtx", rtx);
json.put("fec", fec);
json.put("no_nat", noNat);
json.put("no_tun", noTun);
json.put("allow_mapping", allowMapping);
// 数组
if (!portMapping.isEmpty()) {
JSONArray mappingArray = new JSONArray();
for (String mapping : portMapping) {
mappingArray.put(mapping);
}
json.put("port_mapping", mappingArray);
}
if (!udpStun.isEmpty()) {
JSONArray stunArray = new JSONArray();
for (String stun : udpStun) {
stunArray.put(stun);
}
json.put("udp_stun", stunArray);
}
if (!tcpStun.isEmpty()) {
JSONArray stunArray = new JSONArray();
for (String stun : tcpStun) {
stunArray.put(stun);
}
json.put("tcp_stun", stunArray);
}
return json.toString();
}
/**
* 配置构建器
*/
public static class Builder {
private List<String> servers = new ArrayList<>();
private String networkCode;
private String password;
private String deviceId;
private String deviceName;
private String tunName;
private String ip;
private String certMode;
private boolean noPunch = false;
private boolean compress = false;
private boolean rtx = false;
private boolean fec = false;
private boolean noNat = false;
private boolean noTun = false;
private Integer mtu;
private boolean allowMapping = false;
private List<String> portMapping = new ArrayList<>();
private List<String> udpStun = new ArrayList<>();
private List<String> tcpStun = new ArrayList<>();
/**
* 添加服务器地址(必填)
* @param server 服务器地址,格式:tcp://host:port 或 wss://host:port
*/
public Builder addServer(String server) {
this.servers.add(server);
return this;
}
/**
* 设置网络代码(必填)
* @param networkCode 组网代码
*/
public Builder setNetworkCode(String networkCode) {
this.networkCode = networkCode;
return this;
}
/**
* 设置密码(可选)
*/
public Builder setPassword(String password) {
this.password = password;
return this;
}
/**
* 设置设备ID(可选,默认自动生成)
*/
public Builder setDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
/**
* 设置设备名称(可选)
*/
public Builder setDeviceName(String deviceName) {
this.deviceName = deviceName;
return this;
}
/**
* 设置TUN设备名称(可选)
*/
public Builder setTunName(String tunName) {
this.tunName = tunName;
return this;
}
/**
* 设置固定IP(可选)
*/
public Builder setIp(String ip) {
this.ip = ip;
return this;
}
/**
* 设置证书验证模式(可选)
* @param certMode "insecure" | "system" | "embedded"
*/
public Builder setCertMode(String certMode) {
this.certMode = certMode;
return this;
}
/**
* 禁用打洞(默认false)
*/
public Builder setNoPunch(boolean noPunch) {
this.noPunch = noPunch;
return this;
}
/**
* 启用压缩(默认false)
*/
public Builder setCompress(boolean compress) {
this.compress = compress;
return this;
}
/**
* 启用QUIC重传(默认false
*/
public Builder setRtx(boolean rtx) {
this.rtx = rtx;
return this;
}
/**
* 启用FEC冗余传输(默认false)
*/
public Builder setFec(boolean fec) {
this.fec = fec;
return this;
}
/**
* 禁用NAT(默认false
*/
public Builder setNoNat(boolean noNat) {
this.noNat = noNat;
return this;
}
/**
* 无TUN模式(默认false
*/
public Builder setNoTun(boolean noTun) {
this.noTun = noTun;
return this;
}
/**
* 设置MTU(可选,默认1380)
*/
public Builder setMtu(int mtu) {
this.mtu = mtu;
return this;
}
/**
* 允许端口映射(默认false)
*/
public Builder setAllowMapping(boolean allowMapping) {
this.allowMapping = allowMapping;
return this;
}
/**
* 添加端口映射规则(可选)
* @param mapping 格式:tcp:80->192.168.1.100:8080
*/
public Builder addPortMapping(String mapping) {
this.portMapping.add(mapping);
return this;
}
/**
* 添加UDP STUN服务器(可选)
*/
public Builder addUdpStun(String stun) {
this.udpStun.add(stun);
return this;
}
/**
* 添加TCP STUN服务器(可选)
*/
public Builder addTcpStun(String stun) {
this.tcpStun.add(stun);
return this;
}
/**
* 构建配置对象
*/
public VntConfig build() {
if (servers.isEmpty()) {
throw new IllegalArgumentException("At least one server must be specified");
}
if (networkCode == null || networkCode.isEmpty()) {
throw new IllegalArgumentException("Network code must be specified");
}
return new VntConfig(this);
}
}
}
@@ -0,0 +1,21 @@
package com.vnt;
/**
* VNT异常
*
* VNT操作失败时抛出的异常
*/
public class VntException extends Exception {
public VntException(String message) {
super(message);
}
public VntException(String message, Throwable cause) {
super(message, cause);
}
public VntException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,56 @@
package com.vnt;
/**
* VNT网络管理器 - 主入口类
*
* 使用示例:
* 1. 初始化: VntManager.init()
* 2. 创建网络: VntNetwork network = VntManager.createNetwork(config)
* 3. 注册: RegisterResult result = network.register()
* 4. (Android端用result的IP/掩码创建VPN接口,获取tunFd)
* 5. 启动TUN: network.startTun(tunFd)
* 6. 获取API: VntApi api = network.getApi()
* 7. 关闭: network.stop()
*/
public class VntManager {
static {
// 加载JNI库
System.loadLibrary("vnt_jni");
}
/**
* 初始化VNT模块(全局初始化,只需调用一次)
* @return true表示成功,false表示失败
*/
public static boolean init() {
return nativeInit();
}
/**
* 销毁VNT模块(全局清理)
*/
public static void destroy() {
nativeDestroy();
}
/**
* 创建网络实例
* @param config 网络配置对象
* @return VntNetwork实例,失败返回null
*/
public static VntNetwork createNetwork(VntConfig config) {
String configJson = config.toJson();
long handle = nativeCreateNetwork(configJson);
if (handle < 0) {
return null;
}
return new VntNetwork(handle);
}
// ========== Native 方法 ==========
private static native boolean nativeInit();
private static native void nativeDestroy();
private static native long nativeCreateNetwork(String configJson);
}
@@ -0,0 +1,122 @@
package com.vnt;
/**
* VNT网络实例
*
* 代表一个VNT网络连接,持有native资源
*/
public class VntNetwork {
private long nativeHandle;
private boolean closed = false;
// 包内构造,只能通过VntManager创建
VntNetwork(long handle) {
this.nativeHandle = handle;
}
/**
* 注册网络(连接服务器)
* @return 注册结果,包含分配的IP、掩码等信息
* @throws VntException 注册失败时抛出异常
*/
public RegisterResult register() throws VntException {
checkClosed();
String resultJson = nativeRegister(nativeHandle);
return RegisterResult.fromJson(resultJson);
}
/**
* 启动TUN设备
* @param tunFd TUN设备文件描述符(Android VpnService.Builder.establish()返回的fd
* 传入-1表示让VNT自动创建(仅非Android平台支持)
* @throws VntException 启动失败时抛出异常
*/
public void startTun(int tunFd) throws VntException {
checkClosed();
if (!nativeStartTun(nativeHandle, tunFd)) {
throw new VntException("Failed to start TUN device");
}
}
/**
* 设置网络IP(仅非Android平台使用)
* @param ip IP地址
* @param prefixLen 前缀长度
* @throws VntException 设置失败时抛出异常
*/
public void setNetworkIp(String ip, int prefixLen) throws VntException {
checkClosed();
if (!nativeSetNetworkIp(nativeHandle, ip, prefixLen)) {
throw new VntException("Failed to set network IP");
}
}
/**
* 获取VNT API实例
* @return VntApi实例
* @throws VntException 获取失败时抛出异常
*/
public VntApi getApi() throws VntException {
checkClosed();
long apiHandle = nativeGetApi(nativeHandle);
if (apiHandle < 0) {
throw new VntException("Failed to get VntApi");
}
return new VntApi(apiHandle);
}
/**
* 检查是否为无TUN模式
* @return true表示无TUN模式
*/
public boolean isNoTun() {
checkClosed();
return nativeIsNoTun(nativeHandle);
}
/**
* 停止并关闭网络
*/
public void stop() {
if (closed) {
return;
}
nativeStop(nativeHandle);
closed = true;
}
/**
* 获取native句柄(供内部使用)
*/
long getNativeHandle() {
return nativeHandle;
}
/**
* 检查是否已关闭
*/
private void checkClosed() {
if (closed) {
throw new IllegalStateException("VntNetwork has been closed");
}
}
@Override
protected void finalize() throws Throwable {
try {
stop();
} finally {
super.finalize();
}
}
// ========== Native 方法 ==========
private static native String nativeRegister(long handle);
private static native boolean nativeStartTun(long handle, int tunFd);
private static native boolean nativeSetNetworkIp(long handle, String ip, int prefixLen);
private static native long nativeGetApi(long handle);
private static native boolean nativeIsNoTun(long handle);
private static native boolean nativeStop(long handle);
}
+966
View File
@@ -0,0 +1,966 @@
use anyhow::Context;
use jni::objects::{JClass, JObject, JString};
use jni::sys::{jboolean, jint, jlong, jstring};
use jni::JNIEnv;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tokio::runtime::Runtime;
use vnt_core::api::VntApi;
use vnt_core::context::config::Config;
use vnt_core::core::{NetworkManager, RegisterResponse};
use vnt_core::nat::NetInput;
use vnt_core::port_mapping::PortMapping;
use vnt_core::tls::verifier::CertValidationMode;
use vnt_core::tunnel_core::server::transport::config::ProtocolAddress;
use vnt_core::utils::task_control::TaskGroupManager;
/// 全局状态管理
struct GlobalState {
/// Tokio运行时(Arc包装以便多线程访问)
runtime: Arc<Runtime>,
/// 网络管理器实例
network_managers: HashMap<i64, Arc<Mutex<Option<NetworkManager>>>>,
/// API实例
vnt_apis: HashMap<i64, VntApi>,
/// 任务组管理器
task_group_managers: HashMap<i64, TaskGroupManager>,
/// 下一个实例ID
next_id: i64,
}
impl GlobalState {
fn new() -> anyhow::Result<Self> {
Ok(Self {
runtime: Arc::new(Runtime::new()?),
network_managers: HashMap::new(),
vnt_apis: HashMap::new(),
task_group_managers: HashMap::new(),
next_id: 1,
})
}
}
lazy_static::lazy_static! {
static ref GLOBAL_STATE: Mutex<Option<GlobalState>> = Mutex::new(None);
}
/// 初始化JNI模块
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
mut env: JNIEnv,
_class: JClass,
) -> jboolean {
let mut state = GLOBAL_STATE.lock();
if state.is_some() {
return 1; // 已经初始化
}
match GlobalState::new() {
Ok(global_state) => {
*state = Some(global_state);
1
}
Err(e) => {
let _ = env.throw(format!("Failed to initialize VNT: {:?}", e));
0
}
}
}
/// 销毁JNI模块
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(
_env: JNIEnv,
_class: JClass,
) {
let mut state = GLOBAL_STATE.lock();
*state = None;
}
/// 创建网络实例
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntManager_nativeCreateNetwork<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
config_json: JString<'local>,
) -> jlong {
let result: anyhow::Result<i64> = (|| {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
// 解析配置JSON
let config_str: String = env
.get_string(&config_json)?
.into();
let config = parse_config_from_json(&config_str)?;
// 创建任务组
let task_group_manager = TaskGroupManager::new();
let (task_group, _task_group_guard) = task_group_manager
.create_task()
.context("create task group")?;
// 获取runtime的clone
let runtime = state.runtime.clone();
// 创建网络管理器
let network_manager = runtime.block_on(async {
NetworkManager::create_network(Box::new(config), task_group).await
})?;
// 分配ID
let id = state.next_id;
state.next_id += 1;
// 保存实例
state.network_managers.insert(id, Arc::new(Mutex::new(Some(network_manager))));
state.task_group_managers.insert(id, task_group_manager);
Ok(id)
})();
match result {
Ok(id) => id,
Err(e) => {
let _ = env.throw(format!("Failed to create network: {:?}", e));
-1
}
}
}
/// 注册网络
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let (network_manager_arc, runtime) = {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
let network_manager_arc = state
.network_managers
.get(&handle)
.context("Invalid handle")?
.clone();
let runtime = state.runtime.clone();
(network_manager_arc, runtime)
};
let response = {
let mut manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_mut()
.context("Network manager already destroyed")?;
runtime.block_on(async {
manager.register().await
})?
};
match response {
RegisterResponse::Success(network_addr) => {
let response_json = serde_json::json!({
"success": true,
"ip": network_addr.ip.to_string(),
"prefix_len": network_addr.prefix_len,
"gateway": network_addr.gateway.to_string(),
"broadcast": network_addr.broadcast.to_string(),
});
Ok(response_json.to_string())
}
RegisterResponse::Failed(error_msg) => {
let response_json = serde_json::json!({
"success": false,
"error": error_msg.message,
});
Ok(response_json.to_string())
}
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to register: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 启动TUN设备(Android使用,需要传入fd)
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
tun_fd: jint,
) -> jboolean {
let result: anyhow::Result<()> = (|| {
let (network_manager_arc, runtime) = {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
let network_manager_arc = state
.network_managers
.get(&handle)
.context("Invalid handle")?
.clone();
let runtime = state.runtime.clone();
(network_manager_arc, runtime)
};
let mut manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_mut()
.context("Network manager already destroyed")?;
#[cfg(unix)]
{
let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) };
runtime.block_on(async {
manager.start_tun_fd(tun_fd).await
})?;
}
#[cfg(not(unix))]
{
let _ = tun_fd; // 避免未使用警告
runtime.block_on(async {
manager.start_tun().await
})?;
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to start TUN: {:?}", e));
0
}
}
}
/// 设置网络IP(非Android系统)
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
handle: jlong,
ip: JString<'local>,
prefix_len: jint,
) -> jboolean {
let result: anyhow::Result<()> = (|| {
let (network_manager_arc, runtime) = {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
let network_manager_arc = state
.network_managers
.get(&handle)
.context("Invalid handle")?
.clone();
let runtime = state.runtime.clone();
(network_manager_arc, runtime)
};
let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse()
.context("Invalid IP address")?;
let manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_ref()
.context("Network manager already destroyed")?;
#[cfg(not(target_os = "android"))]
{
runtime.block_on(async {
manager.set_tun_network_ip(ip_addr, prefix_len as u8).await
})?;
}
#[cfg(target_os = "android")]
{
let _ = (ip_addr, prefix_len); // 避免未使用警告
anyhow::bail!("set_network_ip is not supported on Android");
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to set network IP: {:?}", e));
0
}
}
}
/// 获取VntApi实例
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jlong {
let result: anyhow::Result<i64> = (|| {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
let network_manager_arc = state
.network_managers
.get(&handle)
.context("Invalid handle")?
.clone();
let api = {
let manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_ref()
.context("Network manager already destroyed")?;
manager.vnt_api()
};
state.vnt_apis.insert(handle, api);
Ok(handle)
})();
match result {
Ok(id) => id,
Err(e) => {
let _ = env.throw(format!("Failed to get API: {:?}", e));
-1
}
}
}
/// 检查是否为无TUN模式
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jboolean {
let result: anyhow::Result<bool> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let network_manager_arc = state
.network_managers
.get(&handle)
.context("Invalid handle")?
.clone();
let manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_ref()
.context("Network manager already destroyed")?;
Ok(manager.is_no_tun())
})();
match result {
Ok(is_no_tun) => if is_no_tun { 1 } else { 0 },
Err(e) => {
let _ = env.throw(format!("Failed to check no_tun: {:?}", e));
0
}
}
}
/// 关闭网络
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeStop(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jboolean {
let result: anyhow::Result<()> = (|| {
let mut global_state = GLOBAL_STATE.lock();
let state = global_state
.as_mut()
.context("VNT not initialized")?;
// 停止任务组
if let Some(task_group_manager) = state.task_group_managers.get(&handle) {
task_group_manager.stop();
}
// 移除网络管理器
state.network_managers.remove(&handle);
state.vnt_apis.remove(&handle);
state.task_group_managers.remove(&handle);
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to stop network: {:?}", e));
0
}
}
}
// ==================== VntApi 接口 ====================
/// 获取客户端列表
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let client_ips = api.client_ips();
let json_array: Vec<_> = client_ips
.into_iter()
.map(|client| {
serde_json::json!({
"ip": client.ip.to_string(),
"online": client.online,
})
})
.collect();
Ok(serde_json::to_string(&json_array)?)
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get client list: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取网络配置信息
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
if let Some(network) = api.network() {
let json = serde_json::json!({
"ip": network.ip.to_string(),
"prefix_len": network.prefix_len,
"gateway": network.gateway.to_string(),
"broadcast": network.broadcast.to_string(),
});
Ok(json.to_string())
} else {
Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get network info: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取NAT信息
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
if let Some(nat_info) = api.nat_info() {
let json = serde_json::json!({
"nat_type": format!("{:?}", nat_info.nat_type),
"public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(),
"ipv6": nat_info.ipv6.map(|ip| ip.to_string()),
});
Ok(json.to_string())
} else {
Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get NAT info: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取服务器节点列表
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetServerList<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let servers = api.server_node_list();
let json_array: Vec<_> = servers
.into_iter()
.map(|server| {
serde_json::json!({
"server_id": server.server_id,
"server_addr": server.server_addr.to_string(),
"connected": server.connected,
"rtt": server.rtt,
"data_version": server.data_version,
"server_version": server.server_version,
})
})
.collect();
Ok(serde_json::to_string(&json_array)?)
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get server list: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取路由表
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let route_table = api.route_table();
let json_data: Vec<_> = route_table
.into_iter()
.map(|(ip, routes)| {
let routes_json: Vec<_> = routes
.into_iter()
.map(|route| {
serde_json::json!({
"route_key": route.route_key().to_string(),
"protocol": route.route_key().protocol().to_string(),
"metric": route.metric(),
"rtt": route.rtt(),
})
})
.collect();
serde_json::json!({
"ip": ip.to_string(),
"routes": routes_json,
})
})
.collect();
Ok(serde_json::to_string(&json_data)?)
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get route table: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 检查目标IP是否直连
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jboolean {
let result: anyhow::Result<bool> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse()
.context("Invalid IP address")?;
Ok(api.is_direct(&ip_addr))
})();
match result {
Ok(is_direct) => if is_direct { 1 } else { 0 },
Err(e) => {
let _ = env.throw(format!("Failed to check direct: {:?}", e));
0
}
}
}
/// 获取对端NAT信息
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse()
.context("Invalid IP address")?;
if let Some(nat_info) = api.peer_nat_info(&ip_addr) {
let json = serde_json::json!({
"nat_type": format!("{:?}", nat_info.nat_type),
"public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(),
"ipv6": nat_info.ipv6.map(|ip| ip.to_string()),
});
Ok(json.to_string())
} else {
Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get peer NAT info: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取丢包信息
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetPacketLoss<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse()
.context("Invalid IP address")?;
if let Some(loss_info) = api.packet_loss_info(&ip_addr) {
let json = serde_json::json!({
"ip": loss_info.ip.to_string(),
"sent": loss_info.sent,
"received": loss_info.received,
"loss_rate": loss_info.loss_rate,
});
Ok(json.to_string())
} else {
Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get packet loss: {:?}", e));
JObject::null().into_raw()
}
}
}
/// 获取流量信息
#[no_mangle]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetTrafficInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
let result: anyhow::Result<String> = (|| {
let global_state = GLOBAL_STATE.lock();
let state = global_state
.as_ref()
.context("VNT not initialized")?;
let api = state
.vnt_apis
.get(&api_handle)
.context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse()
.context("Invalid IP address")?;
if let Some(traffic_info) = api.traffic_info(&ip_addr) {
let json = serde_json::json!({
"ip": traffic_info.ip.to_string(),
"tx_bytes": traffic_info.tx_bytes,
"rx_bytes": traffic_info.rx_bytes,
});
Ok(json.to_string())
} else {
Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get traffic info: {:?}", e));
JObject::null().into_raw()
}
}
}
// ==================== 辅助函数 ====================
/// 从JSON字符串解析配置
fn parse_config_from_json(json_str: &str) -> anyhow::Result<Config> {
#[derive(serde::Deserialize)]
struct ConfigJson {
server: Vec<String>,
network_code: String,
#[serde(default)]
device_id: Option<String>,
#[serde(default)]
device_name: Option<String>,
#[serde(default)]
tun_name: Option<String>,
#[serde(default)]
ip: Option<Ipv4Addr>,
#[serde(default)]
password: Option<String>,
#[serde(default)]
cert_mode: Option<String>,
#[serde(default)]
no_punch: bool,
#[serde(default)]
compress: bool,
#[serde(default)]
rtx: bool,
#[serde(default)]
fec: bool,
#[serde(default)]
input: Vec<NetInput>,
#[serde(default)]
output: Vec<ipnet::Ipv4Net>,
#[serde(default)]
no_nat: bool,
#[serde(default)]
no_tun: bool,
#[serde(default)]
mtu: Option<u16>,
#[serde(default)]
port_mapping: Vec<String>,
#[serde(default)]
allow_mapping: bool,
#[serde(default)]
udp_stun: Vec<String>,
#[serde(default)]
tcp_stun: Vec<String>,
}
let cfg: ConfigJson = serde_json::from_str(json_str)?;
let server_addrs: Vec<ProtocolAddress> = cfg
.server
.iter()
.map(|s| {
s.parse()
.map_err(|e| anyhow::anyhow!("invalid server address '{}': {}", s, e))
})
.collect::<anyhow::Result<_>>()?;
let port_mapping: Vec<PortMapping> = cfg
.port_mapping
.iter()
.map(|s| {
s.parse()
.map_err(|e| anyhow::anyhow!("invalid port_mapping '{}': {}", s, e))
})
.collect::<anyhow::Result<_>>()?;
let cert_mode = match cfg.cert_mode.as_deref() {
Some(s) => s
.parse()
.map_err(|e| anyhow::anyhow!("invalid cert_mode '{}': {}", s, e))?,
None => CertValidationMode::InsecureSkipVerification,
};
let device_id = match cfg.device_id {
Some(id) => id,
None => vnt_core::utils::device_id::get_device_id()
.map_err(|e| anyhow::anyhow!("failed to get device_id: {}", e))?,
};
let device_name = cfg.device_name.unwrap_or_else(|| {
hostname::get()
.ok()
.and_then(|v| v.into_string().ok())
.unwrap_or_default()
});
let mut udp_stun = cfg.udp_stun;
for x in udp_stun.iter_mut() {
if !x.contains(':') {
x.push_str(":3478");
}
}
let mut tcp_stun = cfg.tcp_stun;
for x in tcp_stun.iter_mut() {
if !x.contains(':') {
x.push_str(":3478");
}
}
Ok(Config {
server_addr: server_addrs,
network_code: cfg.network_code,
ip: cfg.ip,
no_punch: cfg.no_punch,
rtx: cfg.rtx,
compress: cfg.compress,
device_id,
device_name,
tun_name: cfg.tun_name,
password: cfg.password,
cert_mode,
input: cfg.input,
output: cfg.output,
no_nat: cfg.no_nat,
no_tun: cfg.no_tun,
mtu: cfg.mtu,
port_mapping,
allow_port_mapping: cfg.allow_mapping,
udp_stun,
tcp_stun,
fec: cfg.fec,
})
}
+1 -1
View File
@@ -591,7 +591,7 @@ async fn start_vnt_network(
state.record_log("创建 TUN 虚拟网卡成功,设置 IP");
network_manager
.set_network_ip(reg_msg.ip, reg_msg.prefix_len)
.set_tun_network_ip(reg_msg.ip, reg_msg.prefix_len)
.await?;
state.record_log("设置 IP 成功");