mirror of
https://github.com/arampoire/onetun.git
synced 2026-01-16 10:00:23 -05:00
Add C FFI bindings
This commit is contained in:
parent
8cee210ccb
commit
f1275fc0d8
6 changed files with 429 additions and 2 deletions
|
|
@ -32,3 +32,4 @@ default = [ "bin" ]
|
||||||
bin = [ "clap", "pretty_env_logger", "pcap", "tokio/rt-multi-thread" ]
|
bin = [ "clap", "pretty_env_logger", "pcap", "tokio/rt-multi-thread" ]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
crate-type = [ "rlib", "staticlib" ]
|
||||||
144
cbindgen.toml
Normal file
144
cbindgen.toml
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
language = "C"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############## Options for Wrapping the Contents of the Header #################
|
||||||
|
|
||||||
|
# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
|
||||||
|
# trailer = "/* Text to put at the end of the generated file */"
|
||||||
|
# include_guard = "my_bindings_h"
|
||||||
|
# pragma_once = true
|
||||||
|
# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
||||||
|
include_version = false
|
||||||
|
# namespace = "my_namespace"
|
||||||
|
namespaces = []
|
||||||
|
using_namespaces = []
|
||||||
|
sys_includes = []
|
||||||
|
includes = []
|
||||||
|
no_includes = false
|
||||||
|
after_includes = ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################ Code Style Options ################################
|
||||||
|
|
||||||
|
braces = "SameLine"
|
||||||
|
line_length = 100
|
||||||
|
tab_width = 2
|
||||||
|
documentation = true
|
||||||
|
documentation_style = "auto"
|
||||||
|
documentation_length = "full"
|
||||||
|
line_endings = "LF" # also "CR", "CRLF", "Native"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################# Codegen Options ##################################
|
||||||
|
|
||||||
|
style = "both"
|
||||||
|
sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
|
||||||
|
usize_is_size_t = true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[defines]
|
||||||
|
# "target_os = freebsd" = "DEFINE_FREEBSD"
|
||||||
|
# "feature = serde" = "DEFINE_SERDE"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[export]
|
||||||
|
include = []
|
||||||
|
exclude = []
|
||||||
|
# prefix = "CAPI_"
|
||||||
|
item_types = []
|
||||||
|
renaming_overrides_prefixing = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[export.rename]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[export.body]
|
||||||
|
|
||||||
|
|
||||||
|
[export.mangle]
|
||||||
|
|
||||||
|
|
||||||
|
[fn]
|
||||||
|
rename_args = "None"
|
||||||
|
# must_use = "MUST_USE_FUNC"
|
||||||
|
# no_return = "NO_RETURN"
|
||||||
|
# prefix = "START_FUNC"
|
||||||
|
# postfix = "END_FUNC"
|
||||||
|
args = "auto"
|
||||||
|
sort_by = "Name"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[struct]
|
||||||
|
rename_fields = "None"
|
||||||
|
# must_use = "MUST_USE_STRUCT"
|
||||||
|
derive_constructor = false
|
||||||
|
derive_eq = false
|
||||||
|
derive_neq = false
|
||||||
|
derive_lt = false
|
||||||
|
derive_lte = false
|
||||||
|
derive_gt = false
|
||||||
|
derive_gte = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[enum]
|
||||||
|
rename_variants = "None"
|
||||||
|
# must_use = "MUST_USE_ENUM"
|
||||||
|
add_sentinel = false
|
||||||
|
prefix_with_name = false
|
||||||
|
derive_helper_methods = false
|
||||||
|
derive_const_casts = false
|
||||||
|
derive_mut_casts = false
|
||||||
|
# cast_assert_name = "ASSERT"
|
||||||
|
derive_tagged_enum_destructor = false
|
||||||
|
derive_tagged_enum_copy_constructor = false
|
||||||
|
enum_class = true
|
||||||
|
private_default_tagged_enum_constructor = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[const]
|
||||||
|
allow_static_const = true
|
||||||
|
allow_constexpr = false
|
||||||
|
sort_by = "Name"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[macro_expansion]
|
||||||
|
bitflags = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############## Options for How Your Rust library Should Be Parsed ##############
|
||||||
|
|
||||||
|
[parse]
|
||||||
|
parse_deps = false
|
||||||
|
# include = []
|
||||||
|
exclude = []
|
||||||
|
clean = false
|
||||||
|
extra_bindings = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[parse.expand]
|
||||||
|
crates = []
|
||||||
|
all_features = false
|
||||||
|
default_features = true
|
||||||
|
features = []
|
||||||
40
onetun.h
Normal file
40
onetun.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The capacity of the channel for received IP packets.
|
||||||
|
*/
|
||||||
|
#define DISPATCH_CAPACITY 1000
|
||||||
|
|
||||||
|
typedef struct Bus Bus;
|
||||||
|
|
||||||
|
typedef struct Config Config;
|
||||||
|
|
||||||
|
typedef struct PortForwardConfig PortForwardConfig;
|
||||||
|
|
||||||
|
struct Bus *onetun_new_bus(void);
|
||||||
|
|
||||||
|
struct Config *onetun_new_config(struct PortForwardConfig *const *port_forwards,
|
||||||
|
unsigned int port_forwards_len,
|
||||||
|
struct PortForwardConfig *const *remote_forwards,
|
||||||
|
unsigned int remote_forwards_len,
|
||||||
|
const char *private_key,
|
||||||
|
const char *public_key,
|
||||||
|
const char *endpoint_addr,
|
||||||
|
const char *endpoint_bind_addr,
|
||||||
|
const char *source_peer_ip,
|
||||||
|
int keepalive_seconds,
|
||||||
|
int max_transmission_unit,
|
||||||
|
const char *log,
|
||||||
|
const char *pcap_file);
|
||||||
|
|
||||||
|
struct PortForwardConfig *onetun_new_port_forward(const char *source,
|
||||||
|
const char *destination,
|
||||||
|
const char *protocol,
|
||||||
|
unsigned int remote);
|
||||||
|
|
||||||
|
int32_t onetun_start_tunnels(struct Config *config, struct Bus *bus);
|
||||||
|
|
@ -278,7 +278,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_addr(s: Option<&str>) -> anyhow::Result<SocketAddr> {
|
pub(crate) fn parse_addr(s: Option<&str>) -> anyhow::Result<SocketAddr> {
|
||||||
s.with_context(|| "Missing address")?
|
s.with_context(|| "Missing address")?
|
||||||
.to_socket_addrs()
|
.to_socket_addrs()
|
||||||
.with_context(|| "Invalid address")?
|
.with_context(|| "Invalid address")?
|
||||||
|
|
@ -286,7 +286,7 @@ fn parse_addr(s: Option<&str>) -> anyhow::Result<SocketAddr> {
|
||||||
.with_context(|| "Could not lookup address")
|
.with_context(|| "Could not lookup address")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ip(s: Option<&str>) -> anyhow::Result<IpAddr> {
|
pub(crate) fn parse_ip(s: Option<&str>) -> anyhow::Result<IpAddr> {
|
||||||
s.with_context(|| "Missing IP")?
|
s.with_context(|| "Missing IP")?
|
||||||
.parse::<IpAddr>()
|
.parse::<IpAddr>()
|
||||||
.with_context(|| "Invalid IP address")
|
.with_context(|| "Invalid IP address")
|
||||||
|
|
|
||||||
241
src/ffi.rs
Normal file
241
src/ffi.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
// FFI bindings for use in other languages
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
os::raw::{c_char, c_int, c_uint},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{self},
|
||||||
|
events::Bus,
|
||||||
|
start_tunnels,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn onetun_start_tunnels(config: *mut config::Config, bus: *mut Bus) -> i32 {
|
||||||
|
// Unbox the structs
|
||||||
|
let config = unsafe { *(std::boxed::Box::from_raw(config)) };
|
||||||
|
let bus = unsafe { *(std::boxed::Box::from_raw(bus)) };
|
||||||
|
|
||||||
|
// Create a runtime for the future
|
||||||
|
let rt = match tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Ok(rt) => rt,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to create runtime: {}", err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start the future
|
||||||
|
rt.block_on(async move {
|
||||||
|
match start_tunnels(config, bus).await {
|
||||||
|
Ok(_) => 0,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to start tunnels: {}", err);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn onetun_new_config(
|
||||||
|
port_forwards: *const *mut config::PortForwardConfig,
|
||||||
|
port_forwards_len: c_uint,
|
||||||
|
remote_forwards: *const *mut config::PortForwardConfig,
|
||||||
|
remote_forwards_len: c_uint,
|
||||||
|
private_key: *const c_char,
|
||||||
|
public_key: *const c_char,
|
||||||
|
endpoint_addr: *const c_char,
|
||||||
|
endpoint_bind_addr: *const c_char,
|
||||||
|
source_peer_ip: *const c_char,
|
||||||
|
keepalive_seconds: c_int,
|
||||||
|
max_transmission_unit: c_int,
|
||||||
|
log: *const c_char,
|
||||||
|
pcap_file: *const c_char,
|
||||||
|
) -> *mut config::Config {
|
||||||
|
// Convert the port configs to a vector of PortForwardConfigs ending with a null pointer
|
||||||
|
let port_forwards = unsafe {
|
||||||
|
std::slice::from_raw_parts(port_forwards, port_forwards_len as usize)
|
||||||
|
.iter()
|
||||||
|
.filter(|&&x| (!x.is_null() || x != 0 as *mut _))
|
||||||
|
.map(|&x| *(std::boxed::Box::from_raw(x)))
|
||||||
|
.map(|x| x.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
let remote_forwards = unsafe {
|
||||||
|
std::slice::from_raw_parts(remote_forwards, remote_forwards_len as usize)
|
||||||
|
.iter()
|
||||||
|
.filter(|&&x| (!x.is_null() || x != 0 as *mut _))
|
||||||
|
.map(|&x| *(std::boxed::Box::from_raw(x)))
|
||||||
|
.map(|x| x.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert the c_chars to &str's
|
||||||
|
let private_key = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(private_key).to_str() {
|
||||||
|
Ok(x) => match config::X25519SecretKey::from_str(x) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing private key: {}", e);
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let public_key = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(public_key).to_str() {
|
||||||
|
Ok(x) => match config::X25519PublicKey::from_str(x) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing public key: {}", e);
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let endpoint_addr = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(endpoint_addr).to_str() {
|
||||||
|
Ok(x) => match config::parse_addr(Some(x)) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing endpoint address: {}", e);
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let endpoint_bind_addr = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(endpoint_bind_addr).to_str() {
|
||||||
|
Ok(x) => match config::parse_addr(Some(x)) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing endpoint bind address: {}", e);
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let source_peer_ip = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(source_peer_ip).to_str() {
|
||||||
|
Ok(x) => match config::parse_ip(Some(x)) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing source peer IP: {}", e);
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let log = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(log).to_str() {
|
||||||
|
Ok(x) => x.to_string(),
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let pcap_file = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(pcap_file).to_str() {
|
||||||
|
Ok(x) => {
|
||||||
|
if x == "" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(x.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let keepalive_seconds = if keepalive_seconds == -1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(keepalive_seconds as u16)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the config
|
||||||
|
let config = config::Config {
|
||||||
|
port_forwards,
|
||||||
|
remote_port_forwards: remote_forwards,
|
||||||
|
private_key: std::sync::Arc::new(private_key),
|
||||||
|
endpoint_public_key: std::sync::Arc::new(public_key),
|
||||||
|
endpoint_addr,
|
||||||
|
endpoint_bind_addr,
|
||||||
|
source_peer_ip,
|
||||||
|
keepalive_seconds,
|
||||||
|
max_transmission_unit: max_transmission_unit as usize,
|
||||||
|
log,
|
||||||
|
pcap_file,
|
||||||
|
warnings: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a pointer to the config
|
||||||
|
Box::into_raw(Box::new(config)) as *mut config::Config
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn onetun_new_bus() -> *mut Bus {
|
||||||
|
let bus = Bus::new();
|
||||||
|
Box::into_raw(Box::new(bus)) as *mut Bus
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn onetun_new_port_forward(
|
||||||
|
source: *const c_char,
|
||||||
|
destination: *const c_char,
|
||||||
|
protocol: *const c_char,
|
||||||
|
remote: c_uint,
|
||||||
|
) -> *mut config::PortForwardConfig {
|
||||||
|
// Create strings from pointers
|
||||||
|
let source = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(source).to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let destination = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(destination).to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let protocol = unsafe {
|
||||||
|
match std::ffi::CStr::from_ptr(protocol).to_str() {
|
||||||
|
Ok(s) => s.to_lowercase(),
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create config
|
||||||
|
let config = config::PortForwardConfig {
|
||||||
|
source: std::net::SocketAddr::V4(match std::net::SocketAddrV4::from_str(source) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
}),
|
||||||
|
destination: std::net::SocketAddr::V4(
|
||||||
|
match std::net::SocketAddrV4::from_str(destination) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return std::ptr::null_mut(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
protocol: match protocol.as_str() {
|
||||||
|
"tcp" => config::PortProtocol::Tcp,
|
||||||
|
"udp" => config::PortProtocol::Udp,
|
||||||
|
_ => return std::ptr::null_mut(),
|
||||||
|
},
|
||||||
|
remote: remote == 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create pointer to config
|
||||||
|
let config = Box::new(config);
|
||||||
|
Box::into_raw(config) as *mut config::PortForwardConfig
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ use crate::wg::WireGuardTunnel;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod ffi;
|
||||||
#[cfg(feature = "pcap")]
|
#[cfg(feature = "pcap")]
|
||||||
pub mod pcap;
|
pub mod pcap;
|
||||||
pub mod tunnel;
|
pub mod tunnel;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue