mirror of
https://github.com/arampoire/onetun.git
synced 2026-01-16 08:20: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" ]
|
||||
|
||||
[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")?
|
||||
.to_socket_addrs()
|
||||
.with_context(|| "Invalid address")?
|
||||
|
|
@ -286,7 +286,7 @@ fn parse_addr(s: Option<&str>) -> anyhow::Result<SocketAddr> {
|
|||
.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")?
|
||||
.parse::<IpAddr>()
|
||||
.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 events;
|
||||
pub mod ffi;
|
||||
#[cfg(feature = "pcap")]
|
||||
pub mod pcap;
|
||||
pub mod tunnel;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue