#![allow(non_camel_case_types)]
use std::{
ffi::{self, CStr, c_char},
sync::{LazyLock, Once},
};
use tracing::level_filters::LevelFilter;
mod net_types;
mod tcp;
mod udp;
pub use net_types::{
AF_INET, AF_INET6, in_addr_t, in6_addr_t, sa_family_t, sockaddr, sockaddr_data, sockaddr_in,
sockaddr_in6,
};
pub use tcp::{
tcp_listener, tcp_stream, ts_tcp_close, ts_tcp_close_listener, ts_tcp_connect, ts_tcp_listen,
ts_tcp_listener_local_addr, ts_tcp_local_addr, ts_tcp_recv, ts_tcp_remote_addr, ts_tcp_send,
};
pub use udp::{ts_udp_bind, ts_udp_close, ts_udp_recvfrom, ts_udp_sendto, udp_socket};
static TOKIO_RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
tracing::info!("started tokio runtime");
rt
});
type Result<T> = std::result::Result<T, Box<dyn core::error::Error + Send + Sync + 'static>>;
pub struct device(tailscale::Device);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_init(
config_path: *const c_char,
auth_token: *const c_char,
) -> Option<Box<device>> {
static TRACING_ONCE: Once = Once::new();
TRACING_ONCE.call_once(|| {
let env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
tracing_subscriber::fmt().with_env_filter(env_filter).init();
});
async fn _ts_init(config_path: &CStr, auth_token: Option<&CStr>) -> Result<device> {
let config_path = config_path.to_str()?.to_string();
tracing::info!(config_path);
let auth_token = auth_token
.and_then(|x| x.to_str().ok())
.map(ToOwned::to_owned);
let dev = tailscale::Device::new(
&tailscale::Config {
key_state: tailscale::load_key_file(&config_path, Default::default()).await?,
..Default::default()
},
auth_token,
)
.await?;
Result::<_>::Ok(device(dev))
}
unsafe {
TOKIO_RUNTIME.block_on(_ts_init(
CStr::from_ptr(config_path),
if auth_token.is_null() {
None
} else {
Some(CStr::from_ptr(auth_token))
},
))
}
.inspect_err(|e| {
tracing::error!(err = %e, "ts_init failed");
})
.ok()
.map(Box::new)
}
#[unsafe(no_mangle)]
pub extern "C" fn ts_deinit(dev: Box<device>) {
drop(dev)
}
#[unsafe(no_mangle)]
pub extern "C" fn ts_ipv4_addr(dev: &device, dst: &mut in_addr_t) -> ffi::c_int {
let addr = match TOKIO_RUNTIME.block_on(dev.0.ipv4_addr()) {
Ok(addr) => addr,
Err(e) => {
tracing::error!(error = %e, "getting ipv4");
return -1;
}
};
dst.0 = addr.octets();
0
}
#[unsafe(no_mangle)]
pub extern "C" fn ts_ipv6_addr(dev: &device, dst: &mut in6_addr_t) -> ffi::c_int {
let addr = match TOKIO_RUNTIME.block_on(dev.0.ipv6_addr()) {
Ok(addr) => addr,
Err(e) => {
tracing::error!(error = %e, "getting ipv6");
return -1;
}
};
dst.0 = addr.segments();
0
}