diff --git a/vnt-jni/Cargo.toml b/vnt-jni/Cargo.toml index cb80e5f..c6886b7 100644 --- a/vnt-jni/Cargo.toml +++ b/vnt-jni/Cargo.toml @@ -8,7 +8,11 @@ edition = "2021" [dependencies] common = { path = "../common" } vnt = {path="../vnt"} +parking_lot = "0.12.1" jni = { version = "0.21.1", default-features = false } +log = "0.4.20" +spki = { version = "0.7.2", features = ["fingerprint", "alloc","base64","pem"]} + [lib] crate-type = ["staticlib", "cdylib"] \ No newline at end of file diff --git a/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java b/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java new file mode 100644 index 0000000..fa06fe9 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java @@ -0,0 +1,53 @@ +package top.wherewego.vnt.jni; + +import top.wherewego.vnt.jni.param.*; + +/** + * 回调 + * + * @author https://github.com/lbl8603/vnt + */ +public interface CallBack { + /** + * 创建虚拟网卡成功的回调方法 + * + * @param info 网卡信息 + */ + void createTun(DeviceInfo info); + + /** + * 连接服务端 + * + * @param info 将要连接的服务端信息 + */ + void connect(ConnectInfo info); + + /** + * 和服务端握手 + * + * @param info 握手信息 + * @return 是否确认握手 + */ + boolean handshake(HandshakeInfo info); + + /** + * 注册成功回调 + * + * @param info 注册信息 + * @return 是否确认注册信息 + */ + boolean register(RegisterInfo info); + + /** + * 异常回调 + * + * @param info 错误信息 + */ + void error(ErrorInfo info); + + /** + * 服务停止 + */ + void stop(); + +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Config.java b/vnt-jni/java/top/wherewego/vnt/jni/Config.java new file mode 100644 index 0000000..576b62f --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/Config.java @@ -0,0 +1,264 @@ +package top.wherewego.vnt.jni; + +/** + * 启动配置 + * + * @author https://github.com/lbl8603/vnt + */ +public class Config { + /** + * 是否是tap模式,仅支持windows和linux + */ + private boolean tap; + /** + * 组网标识 + */ + private String token; + /** + * 设备名称 + */ + private String name; + /** + * 客户端间加密的密码 + */ + private String password; + /** + * 客户端间加密模式 aes_gcm/aes_cbc/aes_ecb/sm4_cbc + */ + private String cipherModel; + /** + * 打洞模式 ipv4/ipv6/all + */ + private String punchModel; + /** + * mtu 默认自动计算 + */ + private Integer mtu; + /** + * 是否开启服务端加密 + */ + private boolean serverEncrypt; + /** + * 仅使用中继转发 + */ + private boolean relay; + /** + * 设备id,请使用唯一值 + */ + private String deviceId; + /** + * 服务端地址 + */ + private String server; + /** + * stun服务地址 + */ + private String[] stunServer; + /** + * 和服务端使用tcp通信,默认使用udp + */ + private boolean tcp; + /** + * 指定组网IP + */ + private String ip; + /** + * 开启加密指纹校验 + */ + private boolean finger; + /** + * 延迟优先,默认p2p优先 + */ + private boolean firstLatency; + /** + * 点对网入口 格式 192.168.0.0/26,10.26.0.2 + */ + private String[] inIps; + /** + * 点对网出口 格式 192.168.0.0/26 + */ + private String[] outIps; + /** + * 端口组,udp会监听一组端口,tcp监听ports[0]端口 + */ + private int[] ports; + /** + * 虚拟网卡名称 仅在linux、windows、macos上支持 + */ + private String deviceName; + /** + * 虚拟网卡fd 仅在android上支持 + */ + private int deviceFd; + + public Config() { + } + + public boolean isTap() { + return tap; + } + + public void setTap(boolean tap) { + this.tap = tap; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCipherModel() { + return cipherModel; + } + + public void setCipherModel(String cipherModel) { + this.cipherModel = cipherModel; + } + + public String getPunchModel() { + return punchModel; + } + + public void setPunchModel(String punchModel) { + this.punchModel = punchModel; + } + + public Integer getMtu() { + return mtu; + } + + public void setMtu(Integer mtu) { + this.mtu = mtu; + } + + public boolean isServerEncrypt() { + return serverEncrypt; + } + + public void setServerEncrypt(boolean serverEncrypt) { + this.serverEncrypt = serverEncrypt; + } + + public boolean isRelay() { + return relay; + } + + public void setRelay(boolean relay) { + this.relay = relay; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String[] getStunServer() { + return stunServer; + } + + public void setStunServer(String[] stunServer) { + this.stunServer = stunServer; + } + + public boolean isTcp() { + return tcp; + } + + public void setTcp(boolean tcp) { + this.tcp = tcp; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public boolean isFinger() { + return finger; + } + + public void setFinger(boolean finger) { + this.finger = finger; + } + + public boolean isFirstLatency() { + return firstLatency; + } + + public void setFirstLatency(boolean firstLatency) { + this.firstLatency = firstLatency; + } + + public String[] getInIps() { + return inIps; + } + + public void setInIps(String[] inIps) { + this.inIps = inIps; + } + + public String[] getOutIps() { + return outIps; + } + + public void setOutIps(String[] outIps) { + this.outIps = outIps; + } + + public int[] getPorts() { + return ports; + } + + public void setPorts(int[] ports) { + this.ports = ports; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public int getDeviceFd() { + return deviceFd; + } + + public void setDeviceFd(int deviceFd) { + this.deviceFd = deviceFd; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java b/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java new file mode 100644 index 0000000..4f403c3 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java @@ -0,0 +1,29 @@ +package top.wherewego.vnt.jni; + +/** + * @author lubeilin + * @date: 2024/02/27 18:31 + */ +public class IpUtils { + public static String intToIpAddress(int ipAddress) { + + return ((ipAddress & 0xFF000000) >>> 24) + "." + + ((ipAddress & 0x00FF0000) >>> 16) + "." + + ((ipAddress & 0x0000FF00) >>> 8) + "." + + (ipAddress & 0x000000FF); + } + public static int subnetMaskToPrefixLength(int subnetMask) { + int prefixLength = 0; + int bit = 1 << 31; + + while (subnetMask != 0) { + if ((subnetMask & bit) != bit) { + break; + } + prefixLength++; + subnetMask <<= 1; + } + + return prefixLength; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/PeerDeviceInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/PeerDeviceInfo.java new file mode 100644 index 0000000..b7e2126 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/PeerDeviceInfo.java @@ -0,0 +1,46 @@ +package top.wherewego.vnt.jni; + +/** + * 对端设备信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class PeerDeviceInfo { + private final int virtualIp; + private final String name; + private final String status; + private final Route route; + + public PeerDeviceInfo(int virtualIp, String name, String status, Route route) { + this.virtualIp = virtualIp; + this.name = name; + this.status = status; + this.route = route; + } + + public int getVirtualIp() { + return virtualIp; + } + + public String getName() { + return name; + } + + public String getStatus() { + return status; + } + + public Route getRoute() { + return route; + } + + @Override + public String toString() { + return "PeerDeviceInfo{" + + "virtualIp=" + IpUtils.intToIpAddress(virtualIp) + + ", name='" + name + '\'' + + ", status='" + status + '\'' + + ", route=" + route + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Route.java b/vnt-jni/java/top/wherewego/vnt/jni/Route.java new file mode 100644 index 0000000..4b90ffb --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/Route.java @@ -0,0 +1,39 @@ +package top.wherewego.vnt.jni; + +/** + * 路由信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class Route { + private final String address; + private final byte metric; + private final int rt; + + public Route(String address, byte metric, int rt) { + this.address = address; + this.metric = metric; + this.rt = rt; + } + + public String getAddress() { + return address; + } + + public byte getMetric() { + return metric; + } + + public int getRt() { + return rt; + } + + @Override + public String toString() { + return "Route{" + + "address='" + address + '\'' + + ", metric=" + metric + + ", rt=" + rt + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java b/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java new file mode 100644 index 0000000..488695b --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java @@ -0,0 +1,47 @@ +package top.wherewego.vnt.jni; + +import java.io.Closeable; +import java.io.IOException; + +/** + * vnt的Java映射 + * + * @author https://github.com/lbl8603/vnt + */ +public class Vnt implements Closeable { + private final long raw; + + public Vnt(Config config, CallBack callBack) { + this.raw = new0(config, callBack); + if(this.raw == 0){ + throw new RuntimeException(); + } + } + + public void stop() { + stop0(raw); + } + + public void await() { + wait0(raw); + } + + public PeerDeviceInfo[] list() { + return list0(raw); + } + + private native long new0(Config config, CallBack callBack); + + private native void stop0(long raw); + + private native void wait0(long raw); + + private native void drop0(long raw); + + private native PeerDeviceInfo[] list0(long raw); + + @Override + public void close() throws IOException { + drop0(raw); + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java new file mode 100644 index 0000000..414fb96 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java @@ -0,0 +1,32 @@ +package top.wherewego.vnt.jni.param; + +/** + * 连接信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class ConnectInfo { + private final long count; + private final String address; + + public ConnectInfo(long count, String address) { + this.count = count; + this.address = address; + } + + public long getCount() { + return count; + } + + public String getAddress() { + return address; + } + + @Override + public String toString() { + return "ConnectInfo{" + + "count=" + count + + ", address='" + address + '\'' + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java new file mode 100644 index 0000000..020254a --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java @@ -0,0 +1,38 @@ +package top.wherewego.vnt.jni.param; + +/** + * 网卡信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class DeviceInfo { + /** + * 虚拟网卡名称 + */ + private final String name; + /** + * 虚拟网卡版本 + */ + private final String version; + + public DeviceInfo(String name, String version) { + this.name = name; + this.version = version; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return "DeviceInfo{" + + "name='" + name + '\'' + + ", version='" + version + '\'' + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java new file mode 100644 index 0000000..f2ea8b7 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java @@ -0,0 +1,55 @@ +package top.wherewego.vnt.jni.param; + +/** + * 异常回调信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class ErrorInfo { + /** + * 错误码 + */ + public final ErrorCodeEnum code; + /** + * 错误信息,可能为空 + */ + public final String msg; + + public ErrorInfo(int code, String msg) { + this.code = switch (code) { + case 1 -> ErrorCodeEnum.TokenError; + case 2 -> ErrorCodeEnum.Disconnect; + case 3 -> ErrorCodeEnum.AddressExhausted; + case 4 -> ErrorCodeEnum.IpAlreadyExists; + case 5 -> ErrorCodeEnum.InvalidIp; + case 6 -> ErrorCodeEnum.Unknown; + default -> null; + }; + this.msg = msg; + } + + public ErrorCodeEnum getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + public enum ErrorCodeEnum { + TokenError, + Disconnect, + AddressExhausted, + IpAlreadyExists, + InvalidIp, + Unknown, + } + + @Override + public String toString() { + return "ErrorInfo{" + + "code=" + code + + ", msg='" + msg + '\'' + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java new file mode 100644 index 0000000..0bc8148 --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java @@ -0,0 +1,54 @@ +package top.wherewego.vnt.jni.param; + +/** + * 握手回调信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class HandshakeInfo { + /** + * 公钥 pem格式 CRLF分隔,不加密时为空 + */ + private final String publicKey; + /** + * 公钥签名,不加密时为空 + */ + private final String finger; + /** + * 服务端版本 + */ + private final String version; + + public HandshakeInfo() { + this.publicKey = "publicKey"; + this.finger = "finger"; + this.version = "version"; + } + + public HandshakeInfo(String publicKey, String finger, String version) { + this.publicKey = publicKey; + this.finger = finger; + this.version = version; + } + + public String getPublicKey() { + return publicKey; + } + + public String getFinger() { + return finger; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return "HandshakeInfo{" + + "publicKey='" + publicKey + '\'' + + ", finger='" + finger + '\'' + + ", version='" + version + '\'' + + '}'; + } +} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java new file mode 100644 index 0000000..9d51a6d --- /dev/null +++ b/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java @@ -0,0 +1,48 @@ +package top.wherewego.vnt.jni.param; + +/** + * 注册回调信息 + * + * @author https://github.com/lbl8603/vnt + */ +public class RegisterInfo { + /** + * 虚拟IP + */ + public final String virtualIp; + /** + * 掩码 + */ + public final String virtualNetmask; + /** + * 网关 + */ + public final String virtualGateway; + + public RegisterInfo(String virtualIp, String virtualNetmask, String virtualGateway) { + this.virtualIp = virtualIp; + this.virtualNetmask = virtualNetmask; + this.virtualGateway = virtualGateway; + } + + public String getVirtualIp() { + return virtualIp; + } + + public String getVirtualNetmask() { + return virtualNetmask; + } + + public String getVirtualGateway() { + return virtualGateway; + } + + @Override + public String toString() { + return "RegisterInfo{" + + "virtualIp='" + virtualIp + '\'' + + ", virtualNetmask='" + virtualNetmask + '\'' + + ", virtualGateway='" + virtualGateway + '\'' + + '}'; + } +} diff --git a/vnt-jni/src/callback.rs b/vnt-jni/src/callback.rs new file mode 100644 index 0000000..3c3d580 --- /dev/null +++ b/vnt-jni/src/callback.rs @@ -0,0 +1,185 @@ +use std::sync::Arc; + +use jni::objects::{GlobalRef, JString, JValue}; +use jni::{JNIEnv, JavaVM}; +use spki::der::pem::LineEnding; +use spki::EncodePublicKey; + +use vnt::handle::callback::ConnectInfo; +use vnt::{DeviceInfo, ErrorInfo, HandshakeInfo, RegisterInfo, VntCallback}; + +#[derive(Clone)] +pub struct CallBack { + jvm: Arc, + this: GlobalRef, +} + +unsafe impl Send for CallBack {} + +impl CallBack { + pub fn new(jvm: JavaVM, this: GlobalRef) -> Self { + Self { + jvm: Arc::new(jvm), + this, + } + } +} + +impl CallBack { + fn create_tun0(&self, info: DeviceInfo) -> jni::errors::Result<()> { + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + let param = env.new_object( + "top/wherewego/vnt/jni/param/DeviceInfo", + "(Ljava/lang/String;Ljava/lang/String;)V", + &[ + JValue::Object(&env.new_string(info.name)?.into()), + JValue::Object(&env.new_string(info.version)?.into()), + ], + )?; + env.call_method( + &self.this, + "createTun", + "(Ltop/wherewego/vnt/jni/param/DeviceInfo;)V", + &[JValue::Object(¶m)], + )?; + Ok(()) + } + fn connect0(&self, info: ConnectInfo) -> jni::errors::Result<()> { + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + let param = env.new_object( + "top/wherewego/vnt/jni/param/ConnectInfo", + "(JLjava/lang/String;)V", + &[ + JValue::Long(info.count as _), + JValue::Object(&env.new_string(info.address.to_string())?.into()), + ], + )?; + env.call_method( + &self.this, + "connect", + "(Ltop/wherewego/vnt/jni/param/ConnectInfo;)V", + &[JValue::Object(¶m)], + )?; + Ok(()) + } + fn handshake0(&self, info: HandshakeInfo) -> jni::errors::Result { + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + let public_key = if let Some(public_key) = info.public_key { + match public_key.to_public_key_pem(LineEnding::CRLF) { + Ok(public_key) => env.new_string(public_key)?, + Err(e) => { + log::warn!("{:?}", e); + JString::default() + } + } + } else { + JString::default() + }; + let finger = if let Some(finger) = info.finger { + env.new_string(finger)? + } else { + JString::default() + }; + let param = env.new_object( + "top/wherewego/vnt/jni/param/HandshakeInfo", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + &[ + JValue::Object(&public_key), + JValue::Object(&finger), + JValue::Object(&env.new_string(info.version)?.into()), + ], + )?; + let rs = env.call_method( + &self.this, + "handshake", + "(Ltop/wherewego/vnt/jni/param/HandshakeInfo;)Z", + &[JValue::Object(¶m)], + )?; + rs.z() + } + fn register0(&self, info: RegisterInfo) -> jni::errors::Result { + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + let param = env.new_object( + "top/wherewego/vnt/jni/param/RegisterInfo", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + &[ + JValue::Object(&env.new_string(info.virtual_ip.to_string())?.into()), + JValue::Object(&env.new_string(info.virtual_netmask.to_string())?.into()), + JValue::Object(&env.new_string(info.virtual_gateway.to_string())?.into()), + ], + )?; + let rs = env.call_method( + &self.this, + "register", + "(Ltop/wherewego/vnt/jni/param/RegisterInfo;)Z", + &[JValue::Object(¶m)], + )?; + rs.z() + } + fn error0(&self, info: ErrorInfo) -> jni::errors::Result<()> { + let code: u8 = info.code.into(); + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + let msg = if let Some(msg) = info.msg { + env.new_string(msg)? + } else { + JString::default() + }; + let param = env.new_object( + "top/wherewego/vnt/jni/param/ErrorInfo", + "(ILjava/lang/String;)V", + &[JValue::Int(code as _), JValue::Object(&msg.into())], + )?; + env.call_method( + &self.this, + "error", + "(Ltop/wherewego/vnt/jni/param/ErrorInfo;)V", + &[JValue::Object(¶m)], + )?; + Ok(()) + } + fn stop0(&self) -> jni::errors::Result<()> { + let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv; + env.call_method(&self.this, "error", "()V", &[])?; + Ok(()) + } +} + +impl VntCallback for CallBack { + fn create_tun(&self, info: DeviceInfo) { + if let Err(e) = self.create_tun0(info) { + log::warn!("create_tun {:?}", e); + } + } + + fn connect(&self, info: ConnectInfo) { + if let Err(e) = self.connect0(info) { + log::warn!("connect {:?}", e); + } + } + + fn handshake(&self, info: HandshakeInfo) -> bool { + self.handshake0(info).unwrap_or_else(|e| { + log::warn!("handshake {:?}", e); + false + }) + } + + fn register(&self, info: RegisterInfo) -> bool { + self.register0(info).unwrap_or_else(|e| { + log::warn!("register {:?}", e); + false + }) + } + + fn error(&self, info: ErrorInfo) { + if let Err(e) = self.error0(info) { + log::warn!("error {:?}", e); + } + } + + fn stop(&self) { + if let Err(e) = self.stop0() { + log::warn!("stop {:?}", e); + } + } +} diff --git a/vnt-jni/src/config.rs b/vnt-jni/src/config.rs new file mode 100644 index 0000000..1bb3a02 --- /dev/null +++ b/vnt-jni/src/config.rs @@ -0,0 +1,145 @@ +use std::net::ToSocketAddrs; +use std::str::FromStr; + +use jni::errors::Error; +use jni::JNIEnv; +use jni::objects::JObject; + +use vnt::channel::punch::PunchModel; +use vnt::cipher::CipherModel; +use vnt::core::Config; + +use crate::utils::*; + +pub fn new_config(env: &mut JNIEnv, config: JObject) -> Result { + #[cfg(any(target_os = "windows", target_os = "linux"))] + let tap = env.get_field(&config, "tap", "Z")?.z()?; + let token = to_string_not_null(env, &config, "token")?; + let name = to_string_not_null(env, &config, "name")?; + let device_id = to_string_not_null(env, &config, "deviceId")?; + let password = to_string(env, &config, "password")?; + let server_address_str = to_string_not_null(env, &config, "server")?; + let stun_server = to_string_array_not_null(env, &config, "stunServer")?; + let cipher_model = to_string_not_null(env, &config, "cipherModel")?; + let punch_model = to_string(env, &config, "punchModel")?; + let mtu = to_integer(env, &config, "mtu")?.map(|v|v as u32); + let tcp = env.get_field(&config, "tcp", "Z")?.z()?; + let server_encrypt = env.get_field(&config, "serverEncrypt", "Z")?.z()?; + let relay = env.get_field(&config, "relay", "Z")?.z()?; + let finger = env.get_field(&config, "finger", "Z")?.z()?; + let first_latency = env.get_field(&config, "firstLatency", "Z")?.z()?; + let in_ips = to_string_array(env, &config, "inIps")?; + let out_ips = to_string_array(env, &config, "outIps")?; + let ports = + to_i32_array(env, &config, "ports")?.map(|v| v.into_iter().map(|v| v as u16).collect()); + let ip = if let Some(ip) = to_string(env, &config, "ip")? { + match ip.parse() { + Ok(ip) => { + Some(ip) + } + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("ip {} err: {}", ip, e)) + .expect("throw"); + return Err(Error::JavaException); + } + } + } else { + None + }; + let in_ips = if let Some(in_ips) = in_ips { + match common::args_parse::ips_parse(&in_ips) { + Ok(in_ips) => in_ips, + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("in_ips {}", e)) + .expect("throw"); + return Err(Error::JavaException); + } + } + } else { + vec![] + }; + let out_ips = if let Some(out_ips) = out_ips { + match common::args_parse::out_ips_parse(&out_ips) { + Ok(out_ips) => out_ips, + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("out_ips {}", e)) + .expect("throw"); + return Err(Error::JavaException); + } + } + } else { + vec![] + }; + + let server_address = match server_address_str.to_socket_addrs() { + Ok(mut rs) => { + if let Some(addr) = rs.next() { + addr + } else { + env.throw_new("java/lang/RuntimeException", "server address err") + .expect("throw"); + return Err(Error::JavaException); + } + } + Err(e) => { + env.throw_new( + "java/lang/RuntimeException", + format!("server address {}", e), + ) + .expect("throw"); + return Err(Error::JavaException); + } + }; + let cipher_model = match CipherModel::from_str(&cipher_model) { + Ok(cipher_model) => cipher_model, + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("cipher_model {}", e)) + .expect("throw"); + return Err(Error::JavaException); + } + }; + #[cfg(not(target_os = "android"))] + let device_name = to_string(env, &config, "deviceName")?; + #[cfg(target_os = "android")] + let device_fd = env.get_field(&config, "deviceFd", "I")?.i()? as i32; + let config = match Config::new( + #[cfg(any(target_os = "windows", target_os = "linux"))] + tap, + token, + device_id, + name, + server_address, + server_address_str, + stun_server, + in_ips, + out_ips, + password, + mtu, + tcp, + ip, + relay, + false, + server_encrypt, + 1, + cipher_model, + finger, + PunchModel::from_str(&punch_model.unwrap_or_default()).unwrap_or_default(), + ports, + first_latency, + #[cfg(not(target_os = "android"))] + device_name, + #[cfg(target_os = "android")] + device_fd, + ) { + Ok(config) => config, + Err(e) => { + env.throw_new( + "java/lang/RuntimeException", + format!("vnt start error {}", e), + ) + .expect("throw"); + return Err(Error::JavaException); + } + }; + Ok(config) +} diff --git a/vnt-jni/src/lib.rs b/vnt-jni/src/lib.rs index 209cfed..4ac5839 100644 --- a/vnt-jni/src/lib.rs +++ b/vnt-jni/src/lib.rs @@ -1,2 +1,4 @@ +pub mod callback; +pub mod config; pub mod vnt; -pub mod vnt_util; +pub mod utils; \ No newline at end of file diff --git a/vnt-jni/src/utils.rs b/vnt-jni/src/utils.rs new file mode 100644 index 0000000..291faf4 --- /dev/null +++ b/vnt-jni/src/utils.rs @@ -0,0 +1,115 @@ +use jni::errors::Error; +use jni::JNIEnv; +use jni::objects::{JIntArray, JObject, JObjectArray, JString}; + +pub fn to_string_not_null( + env: &mut JNIEnv, + config: &JObject, + name: &'static str, +) -> Result { + let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; + if value.is_null() { + env.throw_new("java/lang/NullPointerException", name) + .expect("throw"); + return Err(Error::NullPtr(name)); + } + let binding = JString::from(value); + let value = env.get_string(binding.as_ref())?; + match value.to_str() { + Ok(value) => Ok(value.to_string()), + Err(_) => { + env.throw_new("java/lang/RuntimeException", "not utf-8") + .expect("throw"); + return Err(Error::JavaException); + } + } +} + +pub fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { + let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; + if value.is_null() { + return Ok(None); + } + let tmp = JString::from(value); + let value = env.get_string(tmp.as_ref())?; + match value.to_str() { + Ok(value) => Ok(Some(value.to_string())), + Err(_) => { + env.throw_new("java/lang/RuntimeException", "not utf-8") + .expect("throw"); + return Err(Error::JavaException); + } + } +} + +pub fn to_string_array_not_null( + env: &mut JNIEnv, + config: &JObject, + name: &str, +) -> Result, Error> { + match to_string_array(env, config, name)? { + None => { + env.throw_new("java/lang/NullPointerException", name) + .expect("throw"); + return Err(Error::JavaException); + } + Some(rs) => Ok(rs), + } +} + +pub fn to_string_array( + env: &mut JNIEnv, + config: &JObject, + name: &str, +) -> Result>, Error> { + let value = env.get_field(config, name, "[Ljava/lang/String;")?.l()?; + if value.is_null() { + return Ok(None); + } + let arr = JObjectArray::from(value); + let len = env.get_array_length(&arr)?; + let mut rs = Vec::with_capacity(len as usize); + for index in 0..len { + let object = env.get_object_array_element(&arr, index)?; + if object.is_null() { + env.throw_new( + "java/lang/NullPointerException", + format!("{},index={}", name, index), + ) + .expect("throw"); + return Err(Error::JavaException); + } + match env.get_string(JString::from(object).as_ref())?.to_str() { + Ok(value) => { + rs.push(value.to_string()); + } + Err(_) => { + env.throw_new("java/lang/RuntimeException", "not utf-8") + .expect("throw"); + return Err(Error::JavaException); + } + } + } + Ok(Some(rs)) +} + +pub fn to_i32_array(env: &mut JNIEnv, config: &JObject, name: &str) -> Result>, Error> { + let obj = env.get_field(&config, name, "[I")?.l()?; + if obj.is_null() { + Ok(None) + } else { + let j_arr = JIntArray::from(obj); + let len = env.get_array_length(&j_arr)?; + let mut arr = vec![0i32; len as usize]; + env.get_int_array_region(j_arr, 0, &mut arr)?; + Ok(Some(arr)) + } +} +pub fn to_integer(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { + let value = env.get_field(config, name, "Ljava/lang/Integer;")?.l()?; + if value.is_null() { + return Ok(None); + } + // 调用 intValue + return Ok(Some(env.call_method(value,"intValue","()I",&[])?.i()? as _)) +} diff --git a/vnt-jni/src/vnt.rs b/vnt-jni/src/vnt.rs index 79fa708..8cb8c3b 100644 --- a/vnt-jni/src/vnt.rs +++ b/vnt-jni/src/vnt.rs @@ -1,45 +1,71 @@ +use std::ptr; + use jni::errors::Error; use jni::objects::{JClass, JObject, JValue}; -use jni::sys::{jboolean, jbyte, jint, jlong, jobject, jobjectArray, jsize}; +use jni::sys::{jbyte, jint, jlong, jobject, jobjectArray, jsize}; use jni::JNIEnv; -use std::ptr; + use vnt::channel::Route; -use vnt::core::sync::VntSync; +use vnt::core::Vnt; use vnt::handle::PeerDeviceInfo; +use crate::callback::CallBack; + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_new0( + mut env: JNIEnv<'static>, + _class: JClass, + config: JObject, + call_back: JObject<'static>, +) -> jlong { + let jvm = if let Ok(jvm) = env.get_java_vm() { + jvm + } else { + return 0; + }; + match crate::config::new_config(&mut env, config) { + Ok(config) => { + let call_back = if let Ok(call_back) = env.new_global_ref(call_back) { + call_back + } else { + return 0; + }; + let vnt_util = match Vnt::new(config, CallBack::new(jvm, call_back)) { + Ok(vnt_util) => vnt_util, + Err(e) => { + env.throw_new( + "java/lang/RuntimeException", + format!("vnt start error {}", e), + ) + .expect("throw"); + return 0; + } + }; + let ptr = Box::into_raw(Box::new(vnt_util)); + return ptr as jlong; + } + Err(_) => {} + } + return 0; +} + #[no_mangle] pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_stop0( _env: JNIEnv, _class: JClass, raw_vnt: jlong, ) { - let vnt = raw_vnt as *mut VntSync; + let vnt = raw_vnt as *mut Vnt; let _ = (&*vnt).stop(); } - #[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStop0( +pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_wait0( _env: JNIEnv, _class: JClass, raw_vnt: jlong, ) { - let vnt = raw_vnt as *mut VntSync; - let _ = (&mut *vnt).wait_stop(); -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStopMs0( - _env: JNIEnv, - _class: JClass, - raw_vnt: jlong, - ms: jlong, -) -> jboolean { - let vnt = raw_vnt as *mut VntSync; - if (&mut *vnt).wait_stop_ms(ms as _) { - jni::sys::JNI_TRUE - } else { - jni::sys::JNI_FALSE - } + let vnt = raw_vnt as *mut Vnt; + let _ = (&*vnt).wait(); } #[no_mangle] @@ -48,7 +74,7 @@ pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_drop0( _class: JClass, raw_vnt: jlong, ) { - let vnt = raw_vnt as *mut VntSync; + let vnt = raw_vnt as *mut Vnt; let _ = Box::from_raw(vnt).stop(); } @@ -58,7 +84,7 @@ pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_list0( _class: JClass, raw_vnt: jlong, ) -> jobjectArray { - let vnt = raw_vnt as *mut VntSync; + let vnt = raw_vnt as *mut Vnt; let vnt = &mut *vnt; let list = vnt.device_list(); diff --git a/vnt-jni/src/vnt_util.rs b/vnt-jni/src/vnt_util.rs deleted file mode 100644 index 7e69881..0000000 --- a/vnt-jni/src/vnt_util.rs +++ /dev/null @@ -1,386 +0,0 @@ -use std::net::ToSocketAddrs; -use std::ptr; -use std::str::FromStr; - -use jni::errors::Error; -use jni::objects::{JClass, JObject, JString, JValue}; -#[cfg(not(target_os = "android"))] -use jni::sys::jboolean; -use jni::sys::{jint, jlong, jobject}; -use jni::JNIEnv; -use vnt::channel::punch::PunchModel; -use vnt::cipher::CipherModel; -use vnt::core::sync::VntUtilSync; -use vnt::core::Config; -use vnt::handle::registration_handler::{RegResponse, ReqEnum}; -#[cfg(not(target_os = "android"))] -use vnt::tun_tap_device::DriverInfo; - -fn to_string_not_null( - env: &mut JNIEnv, - config: &JObject, - name: &'static str, -) -> Result { - let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; - if value.is_null() { - env.throw_new("java/lang/NullPointerException", name) - .expect("throw"); - return Err(Error::NullPtr(name)); - } - let binding = JString::from(value); - let value = env.get_string(binding.as_ref())?; - match value.to_str() { - Ok(value) => Ok(value.to_string()), - Err(_) => { - env.throw_new("java/lang/RuntimeException", "not utf-8") - .expect("throw"); - return Err(Error::JavaException); - } - } -} - -fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { - let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; - if value.is_null() { - return Ok(None); - } - let tmp = JString::from(value); - let value = env.get_string(tmp.as_ref())?; - match value.to_str() { - Ok(value) => Ok(Some(value.to_string())), - Err(_) => { - env.throw_new("java/lang/RuntimeException", "not utf-8") - .expect("throw"); - return Err(Error::JavaException); - } - } -} - -fn new_sync(env: &mut JNIEnv, config: JObject) -> Result { - let token = to_string_not_null(env, &config, "token")?; - let name = to_string_not_null(env, &config, "name")?; - let device_id = to_string_not_null(env, &config, "deviceId")?; - let password = to_string(env, &config, "password")?; - let server_address_str = to_string_not_null(env, &config, "server")?; - let stun_server_str = to_string_not_null(env, &config, "stunServer")?; - let cipher_model = to_string_not_null(env, &config, "cipherModel")?; - let tcp = env.get_field(&config, "tcp", "Z")?.z()?; - let finger = env.get_field(&config, "finger", "Z")?.z()?; - let first_latency = env.get_field(&config, "firstLatency", "Z")?.z()?; - let in_ips = to_string(env, &config, "inIps")?; - let out_ips = to_string(env, &config, "outIps")?; - let port = env.get_field(&config, "port", "I")?.i()? as u16; - let in_ips = if let Some(in_ips) = in_ips { - let in_ips: Vec<&str> = in_ips.split("\n").collect(); - let in_ips = in_ips.iter().map(|v| v.to_string()).collect(); - match common::args_parse::ips_parse(&in_ips) { - Ok(in_ips) => in_ips, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("in_ips {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - } - } else { - vec![] - }; - let out_ips = if let Some(out_ips) = out_ips { - let out_ips: Vec<&str> = out_ips.split("\n").collect(); - let out_ips = out_ips.iter().map(|v| v.to_string()).collect(); - match common::args_parse::out_ips_parse(&out_ips) { - Ok(out_ips) => out_ips, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("out_ips {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - } - } else { - vec![] - }; - - let server_address = match server_address_str.to_socket_addrs() { - Ok(mut rs) => { - if let Some(addr) = rs.next() { - addr - } else { - env.throw_new("java/lang/RuntimeException", "server address err") - .expect("throw"); - return Err(Error::JavaException); - } - } - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("server address {}", e), - ) - .expect("throw"); - return Err(Error::JavaException); - } - }; - let cipher_model = match CipherModel::from_str(&cipher_model) { - Ok(cipher_model) => cipher_model, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("cipher_model {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - }; - let mut stun_server = Vec::new(); - for addr in stun_server_str.split(",") { - stun_server.push(addr.trim().to_string()); - } - let config = match Config::new( - false, - token, - device_id, - name, - server_address, - server_address_str, - stun_server, - in_ips, - out_ips, - password, - false, - None, - tcp, - None, - false, - false, - false, - 1, - cipher_model, - finger, - PunchModel::All, - port, - first_latency, - ) { - Ok(config) => config, - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt start error {}", e), - ) - .expect("throw"); - return Err(Error::JavaException); - } - }; - match VntUtilSync::new(config) { - Ok(vnt_util) => Ok(vnt_util), - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt start error {}", e), - ) - .expect("throw"); - return Err(Error::JavaException); - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_new0( - mut env: JNIEnv, - _class: JClass, - config: JObject, -) -> jlong { - match new_sync(&mut env, config) { - Ok(vnt_util) => { - let ptr = Box::into_raw(Box::new(vnt_util)); - return ptr as jlong; - } - Err(_) => {} - } - return 0; -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_connect0( - mut env: JNIEnv, - _class: JClass, - raw_vnt_util: jlong, -) { - let raw_vnt_util = raw_vnt_util as *mut VntUtilSync; - match (&mut *raw_vnt_util).connect() { - Ok(_) => {} - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt connect error {}", e), - ) - .expect("throw"); - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_register0( - mut env: JNIEnv, - _class: JClass, - raw_vnt_util: jlong, -) -> jobject { - let raw_vnt_util = raw_vnt_util as *mut VntUtilSync; - match (&mut *raw_vnt_util).register() { - Ok(response) => match reg_response(&mut env, response) { - Ok(res) => { - return res; - } - Err(e) => { - env.throw(format!("vnt register error {}", e)) - .expect("throw"); - } - }, - Err(e) => match e { - ReqEnum::TokenError => { - env.throw_new( - "top/wherewego/vnt/jni/exception/TokenErrorException", - "TokenError", - ) - .expect("throw"); - } - ReqEnum::AddressExhausted => { - env.throw_new( - "top/wherewego/vnt/jni/exception/AddressExhaustedException", - "AddressExhausted", - ) - .expect("throw"); - } - ReqEnum::Timeout => { - env.throw_new( - "top/wherewego/vnt/jni/exception/TimeoutException", - "Timeout", - ) - .expect("throw"); - } - ReqEnum::ServerError(str) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt register error {}", str), - ) - .expect("throw"); - } - ReqEnum::Other(str) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt register error {}", str), - ) - .expect("throw"); - } - ReqEnum::IpAlreadyExists => { - env.throw_new( - "top/wherewego/vnt/jni/exception/IpAlreadyExistsException", - "IpAlreadyExists", - ) - .expect("throw"); - } - ReqEnum::InvalidIp => { - env.throw_new( - "top/wherewego/vnt/jni/exception/InvalidIpException", - "InvalidIp", - ) - .expect("throw"); - } - }, - } - return ptr::null_mut(); -} - -#[cfg(target_os = "android")] -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0( - _env: JNIEnv, - _class: JClass, - raw_vnt_util: jlong, - fd: jint, -) { - let raw_vnt_util = raw_vnt_util as *mut VntUtilSync; - - (&mut *raw_vnt_util).create_iface(fd as i32); -} - -#[cfg(not(target_os = "android"))] -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0( - mut env: JNIEnv, - _class: JClass, - raw_vnt_util: jlong, -) -> jobject { - let raw_vnt_util = raw_vnt_util as *mut VntUtilSync; - let rs = (&mut *raw_vnt_util).create_iface(); - match rs { - Ok(driver_info) => match driver_info_e(&mut env, driver_info) { - Ok(res) => { - return res; - } - Err(e) => { - env.throw(format!("vnt create iface error {}", e)) - .expect("throw"); - } - }, - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt create iface error {}", e), - ) - .expect("throw"); - } - } - return ptr::null_mut(); -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_build0( - mut env: JNIEnv, - _class: JClass, - raw_vnt_util: jlong, -) -> jlong { - let raw_vnt_util = Box::from_raw(raw_vnt_util as *mut VntUtilSync); - match raw_vnt_util.build() { - Ok(rs) => { - return Box::into_raw(Box::new(rs)) as jlong; - } - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt start error:{:?}", e), - ) - .expect("throw"); - } - } - return 0; -} - -fn reg_response(env: &mut JNIEnv, response: RegResponse) -> Result { - let virtual_ip = u32::from(response.virtual_ip); - let virtual_gateway = u32::from(response.virtual_gateway); - let virtual_netmask = u32::from(response.virtual_netmask); - let response = env.new_object( - "top/wherewego/vnt/jni/RegResponse", - "(III)V", - &[ - JValue::Int(virtual_ip as jint), - JValue::Int(virtual_gateway as jint), - JValue::Int(virtual_netmask as jint), - ], - )?; - Ok(response.into_raw()) -} - -#[cfg(not(target_os = "android"))] -fn driver_info_e(env: &mut JNIEnv, driver_info: DriverInfo) -> Result { - let is_tun = driver_info.device_type.is_tun(); - let name = driver_info.name; - let version = driver_info.version; - let mac = driver_info.mac.unwrap_or(String::new()); - let response = env.new_object( - "top/wherewego/vnt/jni/DriverInfo", - "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - &[ - JValue::Bool(is_tun as jboolean), - JValue::Object(&env.new_string(name)?.into()), - JValue::Object(&env.new_string(version)?.into()), - JValue::Object(&env.new_string(mac)?.into()), - ], - )?; - Ok(response.into_raw()) -}