use objc2::runtime::AnyObject;
use std::ffi::c_void;
use std::ptr;
use std::sync::mpsc as std_mpsc;
use tokio::sync::oneshot;
const BLOCK_HAS_COPY_DISPOSE: i32 = 1 << 25;
#[repr(C)]
pub struct BlockDescriptor {
pub reserved: u64,
pub size: u64,
}
#[repr(C)]
pub struct BlockDescriptorWithHelpers {
pub reserved: u64,
pub size: u64,
pub copy_helper: unsafe extern "C" fn(*mut c_void, *const c_void),
pub dispose_helper: unsafe extern "C" fn(*mut c_void),
}
#[repr(C)]
pub struct CompletionBlock {
pub isa: *const c_void,
pub flags: i32,
pub reserved: i32,
pub invoke: unsafe extern "C" fn(*const c_void, *mut AnyObject),
pub descriptor: *const BlockDescriptor,
}
pub struct VsockConnectionInfo {
pub fd: i32,
pub source_port: u32,
pub destination_port: u32,
}
pub struct VsockConnectionError {
pub message: String,
}
pub type VsockResult = Result<VsockConnectionInfo, VsockConnectionError>;
#[repr(C)]
pub struct VsockContextBlock {
pub isa: *const c_void,
pub flags: i32,
pub reserved: i32,
pub invoke: unsafe extern "C" fn(*mut Self, *mut AnyObject, *mut AnyObject),
pub descriptor: *const BlockDescriptorWithHelpers,
pub sender_ptr: *mut c_void,
}
#[repr(C)]
pub struct BlockingVsockContextBlock {
pub isa: *const c_void,
pub flags: i32,
pub reserved: i32,
pub invoke: unsafe extern "C" fn(*mut Self, *mut AnyObject, *mut AnyObject),
pub descriptor: *const BlockDescriptorWithHelpers,
pub sender_ptr: *mut c_void,
}
static VSOCK_CONTEXT_BLOCK_DESCRIPTOR: BlockDescriptorWithHelpers = BlockDescriptorWithHelpers {
reserved: 0,
size: std::mem::size_of::<VsockContextBlock>() as u64,
copy_helper: vsock_block_copy,
dispose_helper: vsock_block_dispose,
};
static BLOCKING_VSOCK_CONTEXT_BLOCK_DESCRIPTOR: BlockDescriptorWithHelpers =
BlockDescriptorWithHelpers {
reserved: 0,
size: std::mem::size_of::<BlockingVsockContextBlock>() as u64,
copy_helper: blocking_vsock_block_copy,
dispose_helper: blocking_vsock_block_dispose,
};
unsafe extern "C" fn vsock_block_copy(_dst: *mut c_void, _src: *const c_void) {
}
unsafe extern "C" fn blocking_vsock_block_copy(_dst: *mut c_void, _src: *const c_void) {}
unsafe extern "C" fn vsock_block_dispose(block: *mut c_void) {
unsafe {
let block = block as *mut VsockContextBlock;
let sender_ptr = (*block).sender_ptr;
if !sender_ptr.is_null() {
let _ = Box::from_raw(sender_ptr as *mut oneshot::Sender<VsockResult>);
}
}
}
unsafe extern "C" fn blocking_vsock_block_dispose(block: *mut c_void) {
unsafe {
let block = block as *mut BlockingVsockContextBlock;
let sender_ptr = (*block).sender_ptr;
if !sender_ptr.is_null() {
let _ = Box::from_raw(sender_ptr as *mut std_mpsc::Sender<VsockResult>);
}
}
}
unsafe extern "C" fn vsock_block_invoke(
block: *mut VsockContextBlock,
connection: *mut AnyObject,
error: *mut AnyObject,
) {
unsafe {
let sender_ptr = (*block).sender_ptr;
if sender_ptr.is_null() {
tracing::error!("vsock_block_invoke: sender_ptr is null");
return;
}
let sender = Box::from_raw(sender_ptr as *mut oneshot::Sender<VsockResult>);
(*block).sender_ptr = ptr::null_mut();
let result = if !error.is_null() {
let desc: *mut AnyObject = crate::msg_send!(error, localizedDescription);
let message = crate::ffi::nsstring_to_string(desc);
tracing::debug!("vsock connection error: {}", message);
Err(VsockConnectionError { message })
} else if !connection.is_null() {
let original_fd: i32 = crate::msg_send_i32!(connection, fileDescriptor);
let fd = libc::dup(original_fd);
if fd < 0 {
let err = std::io::Error::last_os_error();
tracing::error!("Failed to dup vsock fd: {}", err);
let _ = sender.send(Err(VsockConnectionError {
message: format!("Failed to dup fd: {err}"),
}));
return;
}
tracing::debug!("Duplicated vsock fd: {} -> {}", original_fd, fd);
let src_port: u32 = {
let sel = objc2::sel!(sourcePort);
let func: unsafe extern "C" fn(*const AnyObject, objc2::runtime::Sel) -> u32 =
std::mem::transmute(crate::ffi::runtime::objc_msgSend as *const c_void);
func(connection as *const AnyObject, sel)
};
let dst_port: u32 = {
let sel = objc2::sel!(destinationPort);
let func: unsafe extern "C" fn(*const AnyObject, objc2::runtime::Sel) -> u32 =
std::mem::transmute(crate::ffi::runtime::objc_msgSend as *const c_void);
func(connection as *const AnyObject, sel)
};
tracing::debug!(
"vsock connection success: fd={}, src_port={}, dst_port={}",
fd,
src_port,
dst_port
);
Ok(VsockConnectionInfo {
fd,
source_port: src_port,
destination_port: dst_port,
})
} else {
tracing::error!("vsock_block_invoke: both connection and error are null");
Err(VsockConnectionError {
message: "Connection and error are both null".to_string(),
})
};
let _ = sender.send(result);
}
}
unsafe extern "C" fn blocking_vsock_block_invoke(
block: *mut BlockingVsockContextBlock,
connection: *mut AnyObject,
error: *mut AnyObject,
) {
unsafe {
let sender_ptr = (*block).sender_ptr;
if sender_ptr.is_null() {
tracing::error!("blocking_vsock_block_invoke: sender_ptr is null");
return;
}
let sender = Box::from_raw(sender_ptr as *mut std_mpsc::Sender<VsockResult>);
(*block).sender_ptr = ptr::null_mut();
let result = if !error.is_null() {
let desc: *mut AnyObject = crate::msg_send!(error, localizedDescription);
let message = crate::ffi::nsstring_to_string(desc);
tracing::debug!("vsock connection error: {}", message);
Err(VsockConnectionError { message })
} else if !connection.is_null() {
let original_fd: i32 = crate::msg_send_i32!(connection, fileDescriptor);
let fd = libc::dup(original_fd);
if fd < 0 {
let err = std::io::Error::last_os_error();
tracing::error!("Failed to dup vsock fd: {}", err);
let _ = sender.send(Err(VsockConnectionError {
message: format!("Failed to dup fd: {err}"),
}));
return;
}
tracing::debug!("Duplicated vsock fd: {} -> {}", original_fd, fd);
let src_port: u32 = {
let sel = objc2::sel!(sourcePort);
let func: unsafe extern "C" fn(*const AnyObject, objc2::runtime::Sel) -> u32 =
std::mem::transmute(crate::ffi::runtime::objc_msgSend as *const c_void);
func(connection as *const AnyObject, sel)
};
let dst_port: u32 = {
let sel = objc2::sel!(destinationPort);
let func: unsafe extern "C" fn(*const AnyObject, objc2::runtime::Sel) -> u32 =
std::mem::transmute(crate::ffi::runtime::objc_msgSend as *const c_void);
func(connection as *const AnyObject, sel)
};
tracing::debug!(
"vsock connection success: fd={}, src_port={}, dst_port={}",
fd,
src_port,
dst_port
);
Ok(VsockConnectionInfo {
fd,
source_port: src_port,
destination_port: dst_port,
})
} else {
tracing::error!("blocking_vsock_block_invoke: both connection and error are null");
Err(VsockConnectionError {
message: "Connection and error are both null".to_string(),
})
};
let _ = sender.send(result);
}
}
#[must_use]
pub fn create_vsock_context_block(sender: oneshot::Sender<VsockResult>) -> *const c_void {
let sender_box = Box::new(sender);
let sender_ptr = Box::into_raw(sender_box) as *mut c_void;
unsafe {
let stack_block = VsockContextBlock {
isa: _NSConcreteStackBlock,
flags: BLOCK_HAS_COPY_DISPOSE,
reserved: 0,
invoke: vsock_block_invoke,
descriptor: &VSOCK_CONTEXT_BLOCK_DESCRIPTOR,
sender_ptr,
};
_Block_copy(&stack_block as *const VsockContextBlock as *const c_void)
}
}
#[must_use]
pub fn create_blocking_vsock_context_block(sender: std_mpsc::Sender<VsockResult>) -> *const c_void {
let sender_box = Box::new(sender);
let sender_ptr = Box::into_raw(sender_box) as *mut c_void;
unsafe {
let stack_block = BlockingVsockContextBlock {
isa: _NSConcreteStackBlock,
flags: BLOCK_HAS_COPY_DISPOSE,
reserved: 0,
invoke: blocking_vsock_block_invoke,
descriptor: &BLOCKING_VSOCK_CONTEXT_BLOCK_DESCRIPTOR,
sender_ptr,
};
_Block_copy(&stack_block as *const BlockingVsockContextBlock as *const c_void)
}
}
pub type StateResult = Result<(), String>;
#[repr(C)]
pub struct StateContextBlock {
pub isa: *const c_void,
pub flags: i32,
pub reserved: i32,
pub invoke: unsafe extern "C" fn(*mut Self, *mut AnyObject),
pub descriptor: *const BlockDescriptorWithHelpers,
pub sender_ptr: *mut c_void,
}
unsafe extern "C" fn state_block_copy(_dst: *mut c_void, _src: *const c_void) {
}
unsafe extern "C" fn state_block_dispose(block: *mut c_void) {
unsafe {
let block = block as *mut StateContextBlock;
let sender_ptr = (*block).sender_ptr;
if !sender_ptr.is_null() {
let _ = Box::from_raw(sender_ptr as *mut oneshot::Sender<StateResult>);
}
}
}
unsafe extern "C" fn state_block_invoke(block: *mut StateContextBlock, error: *mut AnyObject) {
unsafe {
let sender_ptr = (*block).sender_ptr;
if sender_ptr.is_null() {
return;
}
let sender = Box::from_raw(sender_ptr as *mut oneshot::Sender<StateResult>);
(*block).sender_ptr = ptr::null_mut();
let result = if error.is_null() {
Ok(())
} else {
let desc: *mut AnyObject = crate::msg_send!(error, localizedDescription);
Err(crate::ffi::nsstring_to_string(desc))
};
let _ = sender.send(result);
}
}
static STATE_CONTEXT_BLOCK_DESCRIPTOR: BlockDescriptorWithHelpers = BlockDescriptorWithHelpers {
reserved: 0,
size: std::mem::size_of::<StateContextBlock>() as u64,
copy_helper: state_block_copy,
dispose_helper: state_block_dispose,
};
#[must_use]
pub fn create_state_completion_block(sender: oneshot::Sender<StateResult>) -> *const c_void {
let sender_box = Box::new(sender);
let sender_ptr = Box::into_raw(sender_box) as *mut c_void;
unsafe {
let stack_block = StateContextBlock {
isa: _NSConcreteStackBlock,
flags: BLOCK_HAS_COPY_DISPOSE,
reserved: 0,
invoke: state_block_invoke,
descriptor: &STATE_CONTEXT_BLOCK_DESCRIPTOR,
sender_ptr,
};
_Block_copy(&stack_block as *const StateContextBlock as *const c_void)
}
}
unsafe extern "C" {
pub static _NSConcreteStackBlock: *const c_void;
pub fn _Block_copy(block: *const c_void) -> *const c_void;
pub fn _Block_release(block: *const c_void);
}
pub struct BlockPtr(pub *const c_void);
unsafe impl Send for BlockPtr {}
unsafe impl Sync for BlockPtr {}
pub static SIMPLE_BLOCK_DESCRIPTOR: BlockDescriptor = BlockDescriptor {
reserved: 0,
size: 40, };
pub unsafe fn create_completion_block(
invoke: unsafe extern "C" fn(*const c_void, *mut AnyObject),
) -> *const c_void {
unsafe {
let stack_block = CompletionBlock {
isa: _NSConcreteStackBlock,
flags: 0,
reserved: 0,
invoke,
descriptor: &SIMPLE_BLOCK_DESCRIPTOR,
};
_Block_copy(&stack_block as *const CompletionBlock as *const c_void)
}
}