use std::ffi::{self, c_char};
use crate::{TOKIO_RUNTIME, device, ffi_guard, tcp::tcp_listener, util};
#[repr(C)]
pub enum serve_target {
Accept = 0,
Proxy = 1,
}
#[repr(C)]
pub struct serve_config {
pub name: *const c_char,
pub port: u16,
pub kind: serve_target,
pub to: *const c_char,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_get_certificate(dev: &device, name: *const c_char) -> ffi::c_int {
ffi_guard(move || {
let Some(name) = (unsafe { util::str(name) }) else {
tracing::error!("get_certificate: name is null or invalid utf-8");
return -1;
};
match TOKIO_RUNTIME.block_on(dev.0.get_certificate(name)) {
Ok(_key) => 0,
Err(e) => {
tracing::error!(err = %e, "get_certificate (fail-closed in this fork)");
-1
}
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_listen_tls(dev: &device, cfg: &serve_config) -> ffi::c_int {
ffi_guard(move || {
let Some(name) = (unsafe { util::str(cfg.name) }) else {
tracing::error!("listen_tls: name is null or invalid utf-8");
return -1;
};
let target = match cfg.kind {
serve_target::Accept => tailscale::ServeTarget::Accept,
serve_target::Proxy => {
let Some(to) = (unsafe { util::str(cfg.to) }) else {
tracing::error!("listen_tls: proxy target is null or invalid utf-8");
return -1;
};
tailscale::ServeTarget::Proxy { to: to.to_owned() }
}
};
let serve_cfg = tailscale::ServeConfig {
name: name.to_owned(),
port: cfg.port,
target,
};
match TOKIO_RUNTIME.block_on(dev.0.listen_tls(&serve_cfg)) {
Ok(_acceptor) => 0,
Err(e) => {
tracing::error!(err = %e, "listen_tls (fail-closed in this fork)");
-1
}
}
})
}
#[repr(C)]
pub enum service_mode {
Tcp = 0,
Http = 1,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_listen_service(
dev: &device,
name: *const c_char,
mode: service_mode,
port: u16,
) -> Option<Box<tcp_listener>> {
ffi_guard(move || {
let Some(name) = (unsafe { util::str(name) }) else {
tracing::error!("listen_service: name is null or invalid utf-8");
return None;
};
let svc_mode = match mode {
service_mode::Tcp => tailscale::ServiceMode::Tcp { port },
service_mode::Http => tailscale::ServiceMode::Http { port },
};
match TOKIO_RUNTIME.block_on(dev.0.listen_service(name, svc_mode)) {
Ok(listener) => Some(Box::new(tcp_listener::new(listener))),
Err(e) => {
tracing::error!(err = %e, "listen_service (fail-closed in this fork)");
None
}
}
})
}