feat(ffi): add async data plane API (#2321)

* feat(ffi): add async data plane API
* feat(ffi): add async data plane examples
* test(ffi): make async Go dataplane tests self-contained
* docs(ffi): document Go async dataplane API
* docs(android): document dataplane JNI API
This commit is contained in:
KKRainbow
2026-06-06 21:52:50 +08:00
committed by GitHub
parent 4a25ca934b
commit 793b57c2a1
18 changed files with 5219 additions and 74 deletions
@@ -0,0 +1,673 @@
use std::{
ffi::{CStr, c_char},
ptr,
};
use easytier_ffi::{
data_plane_async_op_cancel, data_plane_async_op_free, data_plane_async_op_status,
data_plane_async_op_wait, data_plane_free_bytes, data_plane_tcp_accept_finish,
data_plane_tcp_accept_start, data_plane_tcp_bind_finish, data_plane_tcp_bind_start,
data_plane_tcp_close, data_plane_tcp_connect_finish, data_plane_tcp_connect_start,
data_plane_tcp_listener_close, data_plane_tcp_read_finish, data_plane_tcp_read_start,
data_plane_tcp_write_finish, data_plane_tcp_write_start, data_plane_udp_bind_finish,
data_plane_udp_bind_start, data_plane_udp_close, data_plane_udp_recv_from_finish,
data_plane_udp_recv_from_start, data_plane_udp_send_to_finish, data_plane_udp_send_to_start,
free_string,
};
use jni::{
JNIEnv,
objects::{JByteArray, JClass, JObject, JString, JValue},
sys::{jint, jlong, jobject},
};
use crate::{
error::{get_last_error, throw_exception},
strings::jstring_to_cstring,
};
const SOCKET_ADDR_CLASS: &str = "com/easytier/jni/DataPlaneSocketAddress";
const TCP_CONNECT_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneTcpConnectResult";
const TCP_BIND_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneTcpBindResult";
const TCP_ACCEPT_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneTcpAcceptResult";
const TCP_READ_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneTcpReadResult";
const UDP_BIND_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneUdpBindResult";
const UDP_RECV_RESULT_CLASS: &str = "com/easytier/jni/DataPlaneUdpRecvResult";
fn timeout_from_jlong(timeout_ms: jlong) -> u64 {
timeout_ms.max(0) as u64
}
fn port_from_jint(env: &mut JNIEnv, value: jint, name: &str) -> Option<u16> {
match u16::try_from(value) {
Ok(port) => Some(port),
Err(_) => {
throw_exception(env, &format!("Invalid {}: {}", name, value));
None
}
}
}
fn len_from_jint(env: &mut JNIEnv, value: jint, name: &str) -> Option<u32> {
match u32::try_from(value) {
Ok(len) => Some(len),
Err(_) => {
throw_exception(env, &format!("Invalid {}: {}", name, value));
None
}
}
}
fn throw_last(env: &mut JNIEnv) {
let message = get_last_error().unwrap_or_else(|| "EasyTier data-plane call failed".to_string());
throw_exception(env, &message);
}
unsafe fn take_ffi_string(ptr: *const c_char) -> String {
if ptr.is_null() {
return String::new();
}
let value = unsafe { CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned();
free_string(ptr);
value
}
fn new_socket_addr<'local>(
env: &mut JNIEnv<'local>,
ip: String,
port: u16,
) -> Option<JObject<'local>> {
let class = match env.find_class(SOCKET_ADDR_CLASS) {
Ok(class) => class,
Err(err) => {
throw_exception(
env,
&format!("Failed to find socket address class: {:?}", err),
);
return None;
}
};
let ip = match env.new_string(ip) {
Ok(ip) => ip,
Err(err) => {
throw_exception(env, &format!("Failed to create IP string: {:?}", err));
return None;
}
};
match env.new_object(
class,
"(Ljava/lang/String;I)V",
&[JValue::Object(&ip), JValue::Int(port as jint)],
) {
Ok(addr) => Some(addr),
Err(err) => {
throw_exception(env, &format!("Failed to create socket address: {:?}", err));
None
}
}
}
fn new_handle_addr_result(
env: &mut JNIEnv,
class_name: &str,
handle: u64,
ip: String,
port: u16,
) -> jobject {
let Some(addr) = new_socket_addr(env, ip, port) else {
return ptr::null_mut();
};
let class = match env.find_class(class_name) {
Ok(class) => class,
Err(err) => {
throw_exception(env, &format!("Failed to find result class: {:?}", err));
return ptr::null_mut();
}
};
let sig = format!("(JL{};)V", SOCKET_ADDR_CLASS);
match env.new_object(
class,
sig.as_str(),
&[JValue::Long(handle as jlong), JValue::Object(&addr)],
) {
Ok(result) => result.into_raw(),
Err(err) => {
throw_exception(env, &format!("Failed to create result object: {:?}", err));
ptr::null_mut()
}
}
}
fn close_tcp_stream_on_null(result: jobject, handle: u64) -> jobject {
if result.is_null() {
let _ = data_plane_tcp_close(handle);
}
result
}
fn close_tcp_listener_on_null(result: jobject, handle: u64) -> jobject {
if result.is_null() {
let _ = data_plane_tcp_listener_close(handle);
}
result
}
fn close_udp_socket_on_null(result: jobject, handle: u64) -> jobject {
if result.is_null() {
let _ = data_plane_udp_close(handle);
}
result
}
fn read_owned_bytes(ptr: *const u8, len: u32) -> Vec<u8> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
let bytes = unsafe { std::slice::from_raw_parts(ptr, len as usize) }.to_vec();
data_plane_free_bytes(ptr, len);
bytes
}
pub(crate) fn async_op_status_jni(_env: JNIEnv, _class: JClass, handle: jlong) -> jint {
data_plane_async_op_status(handle as u64)
}
pub(crate) fn async_op_wait_jni(
_env: JNIEnv,
_class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jint {
data_plane_async_op_wait(handle as u64, timeout_ms.max(0) as u64)
}
pub(crate) fn async_op_cancel_jni(_env: JNIEnv, _class: JClass, handle: jlong) -> jint {
data_plane_async_op_cancel(handle as u64)
}
pub(crate) fn async_op_free_jni(_env: JNIEnv, _class: JClass, handle: jlong) -> jint {
data_plane_async_op_free(handle as u64)
}
pub(crate) fn tcp_connect_start_jni(
mut env: JNIEnv,
_class: JClass,
inst_name: JString,
dst_ip: JString,
dst_port: jint,
timeout_ms: jlong,
) -> jlong {
let inst_name = match jstring_to_cstring(&mut env, &inst_name) {
Ok(value) => value,
Err(err) => {
throw_exception(&mut env, &format!("Invalid instance name: {}", err));
return 0;
}
};
let dst_ip = match jstring_to_cstring(&mut env, &dst_ip) {
Ok(value) => value,
Err(err) => {
throw_exception(&mut env, &format!("Invalid destination IP: {}", err));
return 0;
}
};
let Some(dst_port) = port_from_jint(&mut env, dst_port, "destination port") else {
return 0;
};
let op = unsafe {
data_plane_tcp_connect_start(
inst_name.as_ptr(),
dst_ip.as_ptr(),
dst_port,
timeout_ms.max(0) as u64,
)
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn tcp_connect_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut ip: *const c_char = ptr::null();
let mut port = 0u16;
let handle = unsafe { data_plane_tcp_connect_finish(op as u64, &mut ip, &mut port) };
if handle == 0 {
throw_last(&mut env);
return ptr::null_mut();
}
close_tcp_stream_on_null(
new_handle_addr_result(
&mut env,
TCP_CONNECT_RESULT_CLASS,
handle,
unsafe { take_ffi_string(ip) },
port,
),
handle,
)
}
pub(crate) fn tcp_bind_start_jni(
mut env: JNIEnv,
_class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
let inst_name = match jstring_to_cstring(&mut env, &inst_name) {
Ok(value) => value,
Err(err) => {
throw_exception(&mut env, &format!("Invalid instance name: {}", err));
return 0;
}
};
let Some(local_port) = port_from_jint(&mut env, local_port, "local port") else {
return 0;
};
let op = unsafe {
data_plane_tcp_bind_start(
inst_name.as_ptr(),
local_port,
timeout_from_jlong(timeout_ms),
)
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn tcp_bind_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut ip: *const c_char = ptr::null();
let mut port = 0u16;
let handle = unsafe { data_plane_tcp_bind_finish(op as u64, &mut ip, &mut port) };
if handle == 0 {
throw_last(&mut env);
return ptr::null_mut();
}
close_tcp_listener_on_null(
new_handle_addr_result(
&mut env,
TCP_BIND_RESULT_CLASS,
handle,
unsafe { take_ffi_string(ip) },
port,
),
handle,
)
}
pub(crate) fn tcp_accept_start_jni(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jlong {
let op = unsafe { data_plane_tcp_accept_start(handle as u64, timeout_from_jlong(timeout_ms)) };
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn tcp_accept_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut local_ip: *const c_char = ptr::null();
let mut local_port = 0u16;
let mut peer_ip: *const c_char = ptr::null();
let mut peer_port = 0u16;
let handle = unsafe {
data_plane_tcp_accept_finish(
op as u64,
&mut local_ip,
&mut local_port,
&mut peer_ip,
&mut peer_port,
)
};
if handle == 0 {
throw_last(&mut env);
return ptr::null_mut();
}
let Some(local_addr) =
new_socket_addr(&mut env, unsafe { take_ffi_string(local_ip) }, local_port)
else {
free_string(peer_ip);
let _ = data_plane_tcp_close(handle);
return ptr::null_mut();
};
let Some(peer_addr) = new_socket_addr(&mut env, unsafe { take_ffi_string(peer_ip) }, peer_port)
else {
let _ = data_plane_tcp_close(handle);
return ptr::null_mut();
};
let class = match env.find_class(TCP_ACCEPT_RESULT_CLASS) {
Ok(class) => class,
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to find accept result class: {:?}", err),
);
let _ = data_plane_tcp_close(handle);
return ptr::null_mut();
}
};
let sig = format!("(JL{};L{};)V", SOCKET_ADDR_CLASS, SOCKET_ADDR_CLASS);
let result = match env.new_object(
class,
sig.as_str(),
&[
JValue::Long(handle as jlong),
JValue::Object(&local_addr),
JValue::Object(&peer_addr),
],
) {
Ok(result) => result.into_raw(),
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to create accept result: {:?}", err),
);
ptr::null_mut()
}
};
close_tcp_stream_on_null(result, handle)
}
pub(crate) fn tcp_read_start_jni(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
let Some(max_len) = len_from_jint(&mut env, max_len, "max length") else {
return 0;
};
let op = unsafe {
data_plane_tcp_read_start(handle as u64, max_len, timeout_from_jlong(timeout_ms))
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn tcp_read_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut ptr: *const u8 = ptr::null();
let mut len = 0u32;
let ret = unsafe { data_plane_tcp_read_finish(op as u64, &mut ptr, &mut len) };
if ret < 0 {
throw_last(&mut env);
return ptr::null_mut();
}
let bytes = read_owned_bytes(ptr, len);
let array = match env.byte_array_from_slice(&bytes) {
Ok(array) => array,
Err(err) => {
throw_exception(&mut env, &format!("Failed to create byte array: {:?}", err));
return ptr::null_mut();
}
};
let class = match env.find_class(TCP_READ_RESULT_CLASS) {
Ok(class) => class,
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to find read result class: {:?}", err),
);
return ptr::null_mut();
}
};
match env.new_object(class, "([B)V", &[JValue::Object(&array)]) {
Ok(result) => result.into_raw(),
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to create read result: {:?}", err),
);
ptr::null_mut()
}
}
}
pub(crate) fn tcp_write_start_jni(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
let data = match env.convert_byte_array(&data) {
Ok(data) => data,
Err(err) => {
throw_exception(&mut env, &format!("Invalid write buffer: {:?}", err));
return 0;
}
};
let ptr = if data.is_empty() {
ptr::null()
} else {
data.as_ptr()
};
let op = unsafe {
data_plane_tcp_write_start(
handle as u64,
ptr,
data.len() as u32,
timeout_from_jlong(timeout_ms),
)
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn tcp_write_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jint {
let ret = data_plane_tcp_write_finish(op as u64);
if ret < 0 {
throw_last(&mut env);
}
ret
}
pub(crate) fn udp_bind_start_jni(
mut env: JNIEnv,
_class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
let inst_name = match jstring_to_cstring(&mut env, &inst_name) {
Ok(value) => value,
Err(err) => {
throw_exception(&mut env, &format!("Invalid instance name: {}", err));
return 0;
}
};
let Some(local_port) = port_from_jint(&mut env, local_port, "local port") else {
return 0;
};
let op = unsafe {
data_plane_udp_bind_start(
inst_name.as_ptr(),
local_port,
timeout_from_jlong(timeout_ms),
)
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn udp_bind_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut ip: *const c_char = ptr::null();
let mut port = 0u16;
let handle = unsafe { data_plane_udp_bind_finish(op as u64, &mut ip, &mut port) };
if handle == 0 {
throw_last(&mut env);
return ptr::null_mut();
}
close_udp_socket_on_null(
new_handle_addr_result(
&mut env,
UDP_BIND_RESULT_CLASS,
handle,
unsafe { take_ffi_string(ip) },
port,
),
handle,
)
}
pub(crate) fn udp_send_to_start_jni(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
dst_ip: JString,
dst_port: jint,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
let dst_ip = match jstring_to_cstring(&mut env, &dst_ip) {
Ok(value) => value,
Err(err) => {
throw_exception(&mut env, &format!("Invalid destination IP: {}", err));
return 0;
}
};
let Some(dst_port) = port_from_jint(&mut env, dst_port, "destination port") else {
return 0;
};
let data = match env.convert_byte_array(&data) {
Ok(data) => data,
Err(err) => {
throw_exception(&mut env, &format!("Invalid UDP send buffer: {:?}", err));
return 0;
}
};
let ptr = if data.is_empty() {
ptr::null()
} else {
data.as_ptr()
};
let op = unsafe {
data_plane_udp_send_to_start(
handle as u64,
dst_ip.as_ptr(),
dst_port,
ptr,
data.len() as u32,
timeout_from_jlong(timeout_ms),
)
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn udp_send_to_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jint {
let ret = data_plane_udp_send_to_finish(op as u64);
if ret < 0 {
throw_last(&mut env);
}
ret
}
pub(crate) fn udp_recv_from_start_jni(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
let Some(max_len) = len_from_jint(&mut env, max_len, "max length") else {
return 0;
};
let op = unsafe {
data_plane_udp_recv_from_start(handle as u64, max_len, timeout_from_jlong(timeout_ms))
};
if op == 0 {
throw_last(&mut env);
}
op as jlong
}
pub(crate) fn udp_recv_from_finish_jni(mut env: JNIEnv, _class: JClass, op: jlong) -> jobject {
let mut ptr: *const u8 = ptr::null();
let mut len = 0u32;
let mut ip: *const c_char = ptr::null();
let mut port = 0u16;
let ret = unsafe {
data_plane_udp_recv_from_finish(op as u64, &mut ptr, &mut len, &mut ip, &mut port)
};
if ret < 0 {
throw_last(&mut env);
return ptr::null_mut();
}
let bytes = read_owned_bytes(ptr, len);
let array = match env.byte_array_from_slice(&bytes) {
Ok(array) => array,
Err(err) => {
free_string(ip);
throw_exception(&mut env, &format!("Failed to create byte array: {:?}", err));
return ptr::null_mut();
}
};
let Some(peer_addr) = new_socket_addr(&mut env, unsafe { take_ffi_string(ip) }, port) else {
return ptr::null_mut();
};
let class = match env.find_class(UDP_RECV_RESULT_CLASS) {
Ok(class) => class,
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to find UDP recv result class: {:?}", err),
);
return ptr::null_mut();
}
};
let sig = format!("([BL{};)V", SOCKET_ADDR_CLASS);
match env.new_object(
class,
sig.as_str(),
&[JValue::Object(&array), JValue::Object(&peer_addr)],
) {
Ok(result) => result.into_raw(),
Err(err) => {
throw_exception(
&mut env,
&format!("Failed to create UDP recv result: {:?}", err),
);
ptr::null_mut()
}
}
}
pub(crate) fn tcp_close_jni(mut env: JNIEnv, _class: JClass, handle: jlong) -> jint {
let ret = data_plane_tcp_close(handle as u64);
if ret != 0 {
throw_last(&mut env);
}
ret
}
pub(crate) fn tcp_listener_close_jni(mut env: JNIEnv, _class: JClass, handle: jlong) -> jint {
let ret = data_plane_tcp_listener_close(handle as u64);
if ret != 0 {
throw_last(&mut env);
}
ret
}
pub(crate) fn udp_close_jni(mut env: JNIEnv, _class: JClass, handle: jlong) -> jint {
let ret = data_plane_udp_close(handle as u64);
if ret != 0 {
throw_last(&mut env);
}
ret
}
@@ -19,17 +19,22 @@
//!
//! Error API:
//! - `getLastError()`: return the latest FFI/JNI error string for the calling thread.
//!
//! Data-plane APIs:
//! - `EasyTierDataPlaneJNI.*`: low-level async op-handle data-plane JNI.
//! - `EasyTierJNI.dataPlane*`: compatibility exports for older callers.
mod callback;
mod config_server_api;
mod data_plane_api;
mod error;
mod logger;
mod network_api;
mod strings;
use jni::JNIEnv;
use jni::objects::{JClass, JObject, JObjectArray, JString};
use jni::sys::{jboolean, jint, jstring};
use jni::objects::{JByteArray, JClass, JObject, JObjectArray, JString};
use jni::sys::{jboolean, jint, jlong, jobject, jstring};
/// Attach a TUN file descriptor to an EasyTier network instance.
///
@@ -91,7 +96,7 @@ pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_runNetworkInstance(
/// `EasyTierJNI.retainNetworkInstance(instanceNames: Array<String>?): Int`
///
/// Passing `null` or an empty array stops all instances. Null elements inside a
/// non-empty array are skipped. On failure this returns `-1` and throws
/// non-empty array are invalid. On failure this returns `-1` and throws
/// `RuntimeException`.
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_retainNetworkInstance(
@@ -201,3 +206,522 @@ pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_isConfigServerClientCon
logger::init();
config_server_api::is_config_server_client_connected_jni(env, class)
}
macro_rules! export_data_plane_jni {
(
$op_status:ident,
$op_wait:ident,
$op_cancel:ident,
$op_free:ident,
$tcp_connect_start:ident,
$tcp_connect_finish:ident,
$tcp_bind_start:ident,
$tcp_bind_finish:ident,
$tcp_accept_start:ident,
$tcp_accept_finish:ident,
$tcp_read_start:ident,
$tcp_read_finish:ident,
$tcp_write_start:ident,
$tcp_write_finish:ident,
$udp_bind_start:ident,
$udp_bind_finish:ident,
$udp_send_to_start:ident,
$udp_send_to_finish:ident,
$udp_recv_from_start:ident,
$udp_recv_from_finish:ident,
$tcp_close:ident,
$tcp_listener_close:ident,
$udp_close:ident
) => {
#[unsafe(no_mangle)]
pub extern "system" fn $op_status(env: JNIEnv, class: JClass, handle: jlong) -> jint {
logger::init();
data_plane_api::async_op_status_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn $op_wait(
env: JNIEnv,
class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jint {
logger::init();
data_plane_api::async_op_wait_jni(env, class, handle, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $op_cancel(env: JNIEnv, class: JClass, handle: jlong) -> jint {
logger::init();
data_plane_api::async_op_cancel_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn $op_free(env: JNIEnv, class: JClass, handle: jlong) -> jint {
logger::init();
data_plane_api::async_op_free_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_connect_start(
env: JNIEnv,
class: JClass,
inst_name: JString,
dst_ip: JString,
dst_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_connect_start_jni(
env, class, inst_name, dst_ip, dst_port, timeout_ms,
)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_connect_finish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_connect_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_bind_start(
env: JNIEnv,
class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_bind_start_jni(env, class, inst_name, local_port, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_bind_finish(env: JNIEnv, class: JClass, op: jlong) -> jobject {
logger::init();
data_plane_api::tcp_bind_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_accept_start(
env: JNIEnv,
class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_accept_start_jni(env, class, handle, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_accept_finish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_accept_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_read_start(
env: JNIEnv,
class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_read_start_jni(env, class, handle, max_len, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_read_finish(env: JNIEnv, class: JClass, op: jlong) -> jobject {
logger::init();
data_plane_api::tcp_read_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_write_start(
env: JNIEnv,
class: JClass,
handle: jlong,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_write_start_jni(env, class, handle, data, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_write_finish(env: JNIEnv, class: JClass, op: jlong) -> jint {
logger::init();
data_plane_api::tcp_write_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_bind_start(
env: JNIEnv,
class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_bind_start_jni(env, class, inst_name, local_port, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_bind_finish(env: JNIEnv, class: JClass, op: jlong) -> jobject {
logger::init();
data_plane_api::udp_bind_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_send_to_start(
env: JNIEnv,
class: JClass,
handle: jlong,
dst_ip: JString,
dst_port: jint,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_send_to_start_jni(
env, class, handle, dst_ip, dst_port, data, timeout_ms,
)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_send_to_finish(env: JNIEnv, class: JClass, op: jlong) -> jint {
logger::init();
data_plane_api::udp_send_to_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_recv_from_start(
env: JNIEnv,
class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_recv_from_start_jni(env, class, handle, max_len, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_recv_from_finish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::udp_recv_from_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_close(env: JNIEnv, class: JClass, handle: jlong) -> jint {
logger::init();
data_plane_api::tcp_close_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn $tcp_listener_close(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::tcp_listener_close_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn $udp_close(env: JNIEnv, class: JClass, handle: jlong) -> jint {
logger::init();
data_plane_api::udp_close_jni(env, class, handle)
}
};
}
export_data_plane_jni!(
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneAsyncOpStatus,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneAsyncOpWait,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneAsyncOpCancel,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneAsyncOpFree,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpConnectStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpConnectFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpBindStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpBindFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpAcceptStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpAcceptFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpReadStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpReadFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpWriteStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpWriteFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpBindStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpBindFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpSendToStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpSendToFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpRecvFromStart,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpRecvFromFinish,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpClose,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneTcpListenerClose,
Java_com_easytier_jni_EasyTierDataPlaneJNI_dataPlaneUdpClose
);
// Compatibility exports for older Kotlin/Java callers that used EasyTierJNI
// directly for data-plane operations.
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneAsyncOpStatus(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::async_op_status_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneAsyncOpWait(
env: JNIEnv,
class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jint {
logger::init();
data_plane_api::async_op_wait_jni(env, class, handle, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneAsyncOpCancel(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::async_op_cancel_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneAsyncOpFree(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::async_op_free_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpConnectStart(
env: JNIEnv,
class: JClass,
inst_name: JString,
dst_ip: JString,
dst_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_connect_start_jni(env, class, inst_name, dst_ip, dst_port, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpConnectFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_connect_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpBindStart(
env: JNIEnv,
class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_bind_start_jni(env, class, inst_name, local_port, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpBindFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_bind_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpAcceptStart(
env: JNIEnv,
class: JClass,
handle: jlong,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_accept_start_jni(env, class, handle, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpAcceptFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_accept_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpReadStart(
env: JNIEnv,
class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_read_start_jni(env, class, handle, max_len, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpReadFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::tcp_read_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpWriteStart(
env: JNIEnv,
class: JClass,
handle: jlong,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::tcp_write_start_jni(env, class, handle, data, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpWriteFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jint {
logger::init();
data_plane_api::tcp_write_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpBindStart(
env: JNIEnv,
class: JClass,
inst_name: JString,
local_port: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_bind_start_jni(env, class, inst_name, local_port, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpBindFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::udp_bind_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpSendToStart(
env: JNIEnv,
class: JClass,
handle: jlong,
dst_ip: JString,
dst_port: jint,
data: JByteArray,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_send_to_start_jni(env, class, handle, dst_ip, dst_port, data, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpSendToFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jint {
logger::init();
data_plane_api::udp_send_to_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpRecvFromStart(
env: JNIEnv,
class: JClass,
handle: jlong,
max_len: jint,
timeout_ms: jlong,
) -> jlong {
logger::init();
data_plane_api::udp_recv_from_start_jni(env, class, handle, max_len, timeout_ms)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpRecvFromFinish(
env: JNIEnv,
class: JClass,
op: jlong,
) -> jobject {
logger::init();
data_plane_api::udp_recv_from_finish_jni(env, class, op)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpClose(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::tcp_close_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneTcpListenerClose(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::tcp_listener_close_jni(env, class, handle)
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_dataPlaneUdpClose(
env: JNIEnv,
class: JClass,
handle: jlong,
) -> jint {
logger::init();
data_plane_api::udp_close_jni(env, class, handle)
}
@@ -113,7 +113,11 @@ pub(crate) fn retain_network_instance_jni(
};
if java_string.is_null() {
continue;
throw_exception(
&mut env,
&format!("Invalid instance name at index {}: null", i),
);
return -1;
}
let jstring = JString::from(java_string);