use crate::ffi::{block_on, clear_last_error, cstr_to_str, set_io_error, set_last_error};
use crate::io::{DtactTcpListener, DtactTcpStream, DtactUdpSocket};
use std::ffi::c_char;
use std::net::SocketAddr;
use std::sync::OnceLock;
static IO_INIT: OnceLock<()> = OnceLock::new();
fn ensure_io_init(workers: usize) {
IO_INIT.get_or_init(|| {
crate::io::init(workers.max(1));
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_init(workers: usize) {
clear_last_error();
ensure_io_init(workers);
}
fn parse_addr(s: &str) -> Option<SocketAddr> {
s.parse::<SocketAddr>().map_or_else(
|_| {
set_last_error(format!("invalid socket address: {s}"));
None
},
Some,
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_listener_bind(addr: *const c_char) -> *mut DtactTcpListener {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
let Some(addr) = parse_addr(addr) else {
return std::ptr::null_mut();
};
let std_listener = match std::net::TcpListener::bind(addr) {
Ok(l) => l,
Err(e) => {
set_io_error(&e);
return std::ptr::null_mut();
}
};
match DtactTcpListener::from_std(std_listener) {
Ok(l) => Box::into_raw(Box::new(l)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_listener_accept(
listener: *mut DtactTcpListener,
) -> *mut DtactTcpStream {
clear_last_error();
if listener.is_null() {
set_last_error("null listener handle");
return std::ptr::null_mut();
}
let listener = unsafe { &*listener };
match block_on(listener.accept()) {
Ok((stream, _addr)) => Box::into_raw(Box::new(stream)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_listener_close(listener: *mut DtactTcpListener) {
if !listener.is_null() {
drop(unsafe { Box::from_raw(listener) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_stream_connect(addr: *const c_char) -> *mut DtactTcpStream {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
let Some(addr) = parse_addr(addr) else {
return std::ptr::null_mut();
};
match block_on(DtactTcpStream::connect(addr)) {
Ok(s) => Box::into_raw(Box::new(s)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_stream_read(
stream: *mut DtactTcpStream,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if stream.is_null() || buf.is_null() {
set_last_error("null stream handle or buffer");
return -1;
}
let stream = unsafe { &*stream };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(stream.read(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_stream_write(
stream: *mut DtactTcpStream,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if stream.is_null() || buf.is_null() {
set_last_error("null stream handle or buffer");
return -1;
}
let stream = unsafe { &*stream };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(stream.write(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_stream_close(stream: *mut DtactTcpStream) {
if !stream.is_null() {
drop(unsafe { Box::from_raw(stream) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_bind(addr: *const c_char) -> *mut DtactUdpSocket {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
let Some(addr) = parse_addr(addr) else {
return std::ptr::null_mut();
};
match block_on(DtactUdpSocket::bind(addr)) {
Ok(s) => Box::into_raw(Box::new(s)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_send_to(
sock: *mut DtactUdpSocket,
buf: *const u8,
len: usize,
target: *const c_char,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let Some(target) = (unsafe { cstr_to_str(target) }) else {
return -1;
};
let Some(target) = parse_addr(target) else {
return -1;
};
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(sock.send_to(slice, target)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_recv_from(
sock: *mut DtactUdpSocket,
buf: *mut u8,
len: usize,
out_addr: *mut c_char,
out_addr_cap: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(sock.recv_from(slice)) {
Ok((n, from)) => {
write_addr_out(from, out_addr, out_addr_cap);
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_connect(
sock: *mut DtactUdpSocket,
addr: *const c_char,
) -> i32 {
clear_last_error();
if sock.is_null() {
set_last_error("null socket handle");
return -1;
}
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return -1;
};
let Some(addr) = parse_addr(addr) else {
return -1;
};
let sock = unsafe { &*sock };
match block_on(sock.connect(addr)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_send(
sock: *mut DtactUdpSocket,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(sock.send(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_recv(
sock: *mut DtactUdpSocket,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(sock.recv(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_udp_close(sock: *mut DtactUdpSocket) {
if !sock.is_null() {
drop(unsafe { Box::from_raw(sock) });
}
}
fn write_addr_out(addr: SocketAddr, out: *mut c_char, cap: usize) {
if out.is_null() || cap == 0 {
return;
}
let s = addr.to_string();
let bytes = s.as_bytes();
let n = bytes.len().min(cap - 1);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr().cast::<c_char>(), out, n);
*out.add(n) = 0;
}
}
#[cfg(unix)]
use crate::io::{DtactUnixDatagram, DtactUnixListener, DtactUnixStream};
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_listener_bind(
addr: *const c_char,
) -> *mut DtactUnixListener {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
match DtactUnixListener::bind(addr) {
Ok(l) => Box::into_raw(Box::new(l)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_listener_accept(
listener: *mut DtactUnixListener,
) -> *mut DtactUnixStream {
clear_last_error();
if listener.is_null() {
set_last_error("null listener handle");
return std::ptr::null_mut();
}
let listener = unsafe { &*listener };
match block_on(listener.accept()) {
Ok((stream, _addr)) => Box::into_raw(Box::new(stream)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_listener_close(listener: *mut DtactUnixListener) {
if !listener.is_null() {
drop(unsafe { Box::from_raw(listener) });
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_stream_connect(
addr: *const c_char,
) -> *mut DtactUnixStream {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
match block_on(DtactUnixStream::connect(addr)) {
Ok(s) => Box::into_raw(Box::new(s)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_stream_read(
stream: *mut DtactUnixStream,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if stream.is_null() || buf.is_null() {
set_last_error("null stream handle or buffer");
return -1;
}
let stream = unsafe { &*stream };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(stream.read(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_stream_write(
stream: *mut DtactUnixStream,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if stream.is_null() || buf.is_null() {
set_last_error("null stream handle or buffer");
return -1;
}
let stream = unsafe { &*stream };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(stream.write(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[allow(clippy::similar_names)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_stream_peer_cred(
stream: *mut DtactUnixStream,
out_uid: *mut u32,
out_gid: *mut u32,
out_pid: *mut i32,
) -> i32 {
clear_last_error();
if stream.is_null() {
set_last_error("null stream handle");
return -1;
}
let stream = unsafe { &*stream };
match stream.peer_cred() {
Ok(cred) => {
if !out_uid.is_null() {
unsafe { *out_uid = cred.uid() };
}
if !out_gid.is_null() {
unsafe { *out_gid = cred.gid() };
}
if !out_pid.is_null() {
unsafe { *out_pid = cred.pid().unwrap_or(-1) };
}
0
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_stream_close(stream: *mut DtactUnixStream) {
if !stream.is_null() {
drop(unsafe { Box::from_raw(stream) });
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_bind(
addr: *const c_char,
) -> *mut DtactUnixDatagram {
clear_last_error();
ensure_io_init(1);
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return std::ptr::null_mut();
};
match DtactUnixDatagram::bind(addr) {
Ok(s) => Box::into_raw(Box::new(s)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_send_to(
sock: *mut DtactUnixDatagram,
buf: *const u8,
len: usize,
target: *const c_char,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let Some(target) = (unsafe { cstr_to_str(target) }) else {
return -1;
};
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(sock.send_to(slice, target)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_recv_from(
sock: *mut DtactUnixDatagram,
buf: *mut u8,
len: usize,
out_addr: *mut c_char,
out_addr_cap: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(sock.recv_from(slice)) {
Ok((n, from)) => {
let path_str = from
.as_pathname()
.map(|p| p.to_string_lossy().into_owned())
.unwrap_or_default();
write_str_bytes_out(path_str.as_bytes(), out_addr, out_addr_cap);
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_connect(
sock: *mut DtactUnixDatagram,
addr: *const c_char,
) -> i32 {
clear_last_error();
if sock.is_null() {
set_last_error("null socket handle");
return -1;
}
let Some(addr) = (unsafe { cstr_to_str(addr) }) else {
return -1;
};
let sock = unsafe { &*sock };
match block_on(sock.connect(addr)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_send(
sock: *mut DtactUnixDatagram,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(sock.send(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_recv(
sock: *mut DtactUnixDatagram,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if sock.is_null() || buf.is_null() {
set_last_error("null socket handle or buffer");
return -1;
}
let sock = unsafe { &*sock };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(sock.recv(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_unix_datagram_close(sock: *mut DtactUnixDatagram) {
if !sock.is_null() {
drop(unsafe { Box::from_raw(sock) });
}
}
#[cfg(unix)]
fn write_str_bytes_out(bytes: &[u8], out: *mut c_char, cap: usize) {
if out.is_null() || cap == 0 {
return;
}
let n = bytes.len().min(cap - 1);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr().cast::<c_char>(), out, n);
*out.add(n) = 0;
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_lookup_host(
host: *const c_char,
out: *mut c_char,
out_cap: usize,
) -> isize {
clear_last_error();
let Some(host) = (unsafe { cstr_to_str(host) }) else {
return -1;
};
match block_on(crate::io::lookup_host(host)) {
Ok(iter) => {
let addrs: Vec<SocketAddr> = iter.collect();
let joined = addrs
.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join(";");
write_addr_list_out(&joined, out, out_cap);
addrs.len() as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
fn write_addr_list_out(s: &str, out: *mut c_char, cap: usize) {
if out.is_null() || cap == 0 {
return;
}
let bytes = s.as_bytes();
let n = bytes.len().min(cap - 1);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr().cast::<c_char>(), out, n);
*out.add(n) = 0;
}
}
#[cfg(windows)]
use crate::io::{DtactNamedPipeHandle, DtactNamedPipeServer};
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_server_create(
name: *const c_char,
) -> *mut DtactNamedPipeServer {
clear_last_error();
let Some(name) = (unsafe { cstr_to_str(name) }) else {
return std::ptr::null_mut();
};
match DtactNamedPipeServer::create(name) {
Ok(s) => Box::into_raw(Box::new(s)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_server_connect(
server: *mut DtactNamedPipeServer,
) -> *mut DtactNamedPipeHandle {
clear_last_error();
if server.is_null() {
set_last_error("null pipe server handle");
return std::ptr::null_mut();
}
let server = unsafe { *Box::from_raw(server) };
match block_on(server.connect()) {
Ok(handle) => Box::into_raw(Box::new(handle)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_server_close(server: *mut DtactNamedPipeServer) {
if !server.is_null() {
drop(unsafe { Box::from_raw(server) });
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_client_connect(
name: *const c_char,
) -> *mut DtactNamedPipeHandle {
clear_last_error();
let Some(name) = (unsafe { cstr_to_str(name) }) else {
return std::ptr::null_mut();
};
match block_on(crate::io::DtactNamedPipeClient::connect(name)) {
Ok(handle) => Box::into_raw(Box::new(handle)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_read(
pipe: *mut DtactNamedPipeHandle,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if pipe.is_null() || buf.is_null() {
set_last_error("null pipe handle or buffer");
return -1;
}
let pipe = unsafe { &*pipe };
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
match block_on(pipe.read(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_write(
pipe: *mut DtactNamedPipeHandle,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if pipe.is_null() || buf.is_null() {
set_last_error("null pipe handle or buffer");
return -1;
}
let pipe = unsafe { &*pipe };
let slice = unsafe { std::slice::from_raw_parts(buf, len) };
match block_on(pipe.write(slice)) {
Ok(n) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_io_pipe_close(pipe: *mut DtactNamedPipeHandle) {
if !pipe.is_null() {
drop(unsafe { Box::from_raw(pipe) });
}
}