use std::ffi::{c_char, c_void};
use std::os::raw::c_int;
use std::ptr;
pub type VmnetInterfaceRef = *mut c_void;
pub type DispatchQueue = *mut c_void;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmnetReturnT {
Success = 1000,
Failure = 1001,
MemFailure = 1002,
InvalidArgument = 1003,
SetupIncomplete = 1004,
InvalidAccess = 1005,
PacketTooBig = 1006,
BufferExhausted = 1007,
TooManyPackets = 1008,
SharingServiceBusy = 1009,
}
impl VmnetReturnT {
#[must_use]
pub fn is_success(self) -> bool {
self == Self::Success
}
#[must_use]
pub fn message(self) -> &'static str {
match self {
Self::Success => "success",
Self::Failure => "operation failed",
Self::MemFailure => "memory allocation failed",
Self::InvalidArgument => "invalid argument",
Self::SetupIncomplete => "setup incomplete",
Self::InvalidAccess => "invalid access",
Self::PacketTooBig => "packet too big",
Self::BufferExhausted => "buffer exhausted",
Self::TooManyPackets => "too many packets",
Self::SharingServiceBusy => "sharing service busy",
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmnetOperatingMode {
Host = 1000,
Shared = 1001,
Bridged = 1002,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmnetInterfaceEvent {
PacketsAvailable = 1,
}
#[repr(C)]
#[derive(Debug)]
pub struct VmnetPacket {
pub vm_pkt_iov: *mut iovec,
pub vm_pkt_iovcnt: u32,
pub vm_pkt_size: u32,
pub vm_flags: u32,
}
#[repr(C)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub struct iovec {
pub iov_base: *mut c_void,
pub iov_len: usize,
}
pub type CFDictionaryRef = *const c_void;
pub type CFMutableDictionaryRef = *mut c_void;
pub type CFStringRef = *const c_void;
pub type CFNumberRef = *const c_void;
pub type CFTypeRef = *const c_void;
pub type CFAllocatorRef = *const c_void;
pub type CFUUIDRef = *const c_void;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum CFNumberType {
SInt8 = 1,
SInt16 = 2,
SInt32 = 3,
SInt64 = 4,
Float32 = 5,
Float64 = 6,
Char = 7,
Short = 8,
Int = 9,
Long = 10,
LongLong = 11,
Float = 12,
Double = 13,
}
pub type DispatchBlock = extern "C" fn();
#[link(name = "vmnet", kind = "framework")]
unsafe extern "C" {
pub fn vmnet_start_interface(
interface_desc: XpcObjectT, queue: DispatchQueue,
handler: *const c_void, ) -> VmnetInterfaceRef;
pub fn vmnet_stop_interface(
interface: VmnetInterfaceRef,
queue: DispatchQueue,
handler: *const c_void, ) -> VmnetReturnT;
pub fn vmnet_read(
interface: VmnetInterfaceRef,
packets: *mut VmnetPacket,
pktcnt: *mut c_int,
) -> VmnetReturnT;
pub fn vmnet_write(
interface: VmnetInterfaceRef,
packets: *mut VmnetPacket,
pktcnt: *mut c_int,
) -> VmnetReturnT;
pub fn vmnet_interface_set_event_callback(
interface: VmnetInterfaceRef,
event_mask: VmnetInterfaceEvent,
queue: DispatchQueue,
handler: *const c_void, ) -> VmnetReturnT;
pub fn vmnet_copy_shared_interface_list() -> *mut c_void; }
#[link(name = "vmnet", kind = "framework")]
unsafe extern "C" {
pub static vmnet_operation_mode_key: *const c_char;
pub static vmnet_shared_interface_name_key: *const c_char;
pub static vmnet_mac_address_key: *const c_char;
pub static vmnet_start_address_key: *const c_char;
pub static vmnet_end_address_key: *const c_char;
pub static vmnet_subnet_mask_key: *const c_char;
pub static vmnet_interface_id_key: *const c_char;
pub static vmnet_enable_isolation_key: *const c_char;
pub static vmnet_nat66_prefix_key: *const c_char;
pub static vmnet_mtu_key: *const c_char;
pub static vmnet_max_packet_size_key: *const c_char;
}
#[link(name = "CoreFoundation", kind = "framework")]
unsafe extern "C" {
pub static kCFAllocatorDefault: CFAllocatorRef;
pub static kCFBooleanTrue: CFTypeRef;
pub static kCFBooleanFalse: CFTypeRef;
pub fn CFDictionaryCreateMutable(
allocator: CFAllocatorRef,
capacity: isize,
key_callbacks: *const c_void,
value_callbacks: *const c_void,
) -> CFMutableDictionaryRef;
pub fn CFDictionarySetValue(dict: CFMutableDictionaryRef, key: CFTypeRef, value: CFTypeRef);
pub fn CFDictionaryGetValue(dict: CFDictionaryRef, key: CFTypeRef) -> CFTypeRef;
pub fn CFRelease(cf: CFTypeRef);
pub fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
pub fn CFStringCreateWithCString(
alloc: CFAllocatorRef,
cstr: *const c_char,
encoding: u32,
) -> CFStringRef;
pub fn CFNumberCreate(
allocator: CFAllocatorRef,
type_: CFNumberType,
value_ptr: *const c_void,
) -> CFNumberRef;
pub fn CFNumberGetValue(
number: CFNumberRef,
type_: CFNumberType,
value_ptr: *mut c_void,
) -> bool;
pub fn CFUUIDCreate(allocator: CFAllocatorRef) -> CFUUIDRef;
pub fn CFUUIDCreateString(allocator: CFAllocatorRef, uuid: CFUUIDRef) -> CFStringRef;
}
#[link(name = "System")]
unsafe extern "C" {
pub fn dispatch_queue_create(label: *const c_char, attr: *const c_void) -> DispatchQueue;
pub fn dispatch_release(object: *mut c_void);
pub fn dispatch_async(queue: DispatchQueue, block: *const c_void);
pub fn dispatch_sync(queue: DispatchQueue, block: *const c_void);
pub static _dispatch_queue_attr_concurrent: *const c_void;
}
pub type XpcObjectT = *mut c_void;
#[link(name = "System")]
unsafe extern "C" {
pub fn xpc_dictionary_create(
keys: *const *const c_char,
values: *const XpcObjectT,
count: usize,
) -> XpcObjectT;
pub fn xpc_dictionary_set_uint64(dict: XpcObjectT, key: *const c_char, value: u64);
pub fn xpc_dictionary_set_string(dict: XpcObjectT, key: *const c_char, value: *const c_char);
pub fn xpc_dictionary_set_bool(dict: XpcObjectT, key: *const c_char, value: bool);
pub fn xpc_dictionary_set_value(dict: XpcObjectT, key: *const c_char, value: XpcObjectT);
pub fn xpc_dictionary_get_string(dict: XpcObjectT, key: *const c_char) -> *const c_char;
pub fn xpc_dictionary_get_uint64(dict: XpcObjectT, key: *const c_char) -> u64;
pub fn xpc_uuid_create(uuid: *const u8) -> XpcObjectT;
pub fn xpc_retain(object: XpcObjectT) -> XpcObjectT;
pub fn xpc_release(object: XpcObjectT);
}
#[cfg(feature = "vmnet")]
pub type DispatchSemaphore = *mut c_void;
#[cfg(feature = "vmnet")]
pub type DispatchTime = u64;
#[cfg(feature = "vmnet")]
pub const DISPATCH_TIME_FOREVER: DispatchTime = !0;
#[cfg(feature = "vmnet")]
pub const DISPATCH_TIME_NOW: DispatchTime = 0;
#[cfg(feature = "vmnet")]
pub const NSEC_PER_SEC: u64 = 1_000_000_000;
#[cfg(feature = "vmnet")]
#[link(name = "System")]
unsafe extern "C" {
pub fn dispatch_semaphore_create(value: isize) -> DispatchSemaphore;
pub fn dispatch_semaphore_signal(dsema: DispatchSemaphore) -> isize;
pub fn dispatch_semaphore_wait(dsema: DispatchSemaphore, timeout: DispatchTime) -> isize;
pub fn dispatch_time(when: DispatchTime, delta: i64) -> DispatchTime;
}
#[cfg(feature = "vmnet")]
#[repr(C)]
pub struct BlockDescriptor {
pub reserved: usize,
pub size: usize,
pub copy_helper: unsafe extern "C" fn(*mut c_void, *const c_void),
pub dispose_helper: unsafe extern "C" fn(*mut c_void),
}
#[cfg(feature = "vmnet")]
#[repr(C)]
pub struct VmnetCompletionBlock {
pub isa: *const c_void,
pub flags: i32,
pub reserved: i32,
pub invoke: unsafe extern "C" fn(*mut Self, VmnetReturnT, XpcObjectT),
pub descriptor: *const BlockDescriptor,
pub status: VmnetReturnT,
pub interface_param: XpcObjectT,
pub semaphore: DispatchSemaphore,
}
#[cfg(feature = "vmnet")]
unsafe extern "C" {
#[link_name = "_NSConcreteStackBlock"]
pub static NS_CONCRETE_STACK_BLOCK: *const c_void;
pub fn _Block_copy(block: *const c_void) -> *mut c_void;
pub fn _Block_release(block: *const c_void);
}
#[cfg(feature = "vmnet")]
unsafe extern "C" fn vmnet_block_invoke(
block: *mut VmnetCompletionBlock,
status: VmnetReturnT,
interface_param: XpcObjectT,
) {
unsafe {
(*block).status = status;
if !interface_param.is_null() {
(*block).interface_param = xpc_retain(interface_param);
}
dispatch_semaphore_signal((*block).semaphore);
}
}
#[cfg(feature = "vmnet")]
unsafe extern "C" fn vmnet_block_copy(dst: *mut c_void, _src: *const c_void) {
unsafe {
let block = dst.cast::<VmnetCompletionBlock>();
if !(*block).interface_param.is_null() {
xpc_retain((*block).interface_param);
}
}
}
#[cfg(feature = "vmnet")]
unsafe extern "C" fn vmnet_block_dispose(block: *mut c_void) {
unsafe {
let block = block.cast::<VmnetCompletionBlock>();
if !(*block).interface_param.is_null() {
xpc_release((*block).interface_param);
}
}
}
#[cfg(feature = "vmnet")]
static VMNET_BLOCK_DESCRIPTOR: BlockDescriptor = BlockDescriptor {
reserved: 0,
size: std::mem::size_of::<VmnetCompletionBlock>(),
copy_helper: vmnet_block_copy,
dispose_helper: vmnet_block_dispose,
};
#[cfg(feature = "vmnet")]
const BLOCK_HAS_COPY_DISPOSE: i32 = 1 << 25;
#[cfg(feature = "vmnet")]
#[must_use]
pub unsafe fn create_vmnet_completion_block(sema: DispatchSemaphore) -> *mut c_void {
let stack_block = VmnetCompletionBlock {
isa: unsafe { NS_CONCRETE_STACK_BLOCK },
flags: BLOCK_HAS_COPY_DISPOSE,
reserved: 0,
invoke: vmnet_block_invoke,
descriptor: &raw const VMNET_BLOCK_DESCRIPTOR,
status: VmnetReturnT::Failure,
interface_param: ptr::null_mut(),
semaphore: sema,
};
unsafe { _Block_copy((&raw const stack_block).cast()) }
}
pub const K_CF_STRING_ENCODING_UTF8: u32 = 0x08000100;
#[must_use]
pub unsafe fn cfstring_from_str(s: &str) -> CFStringRef {
let cstr = std::ffi::CString::new(s).unwrap();
unsafe {
CFStringCreateWithCString(
kCFAllocatorDefault,
cstr.as_ptr(),
K_CF_STRING_ENCODING_UTF8,
)
}
}
#[must_use]
pub unsafe fn cfnumber_from_i64(value: i64) -> CFNumberRef {
unsafe {
CFNumberCreate(
kCFAllocatorDefault,
CFNumberType::SInt64,
(&raw const value).cast::<c_void>(),
)
}
}
#[must_use]
pub unsafe fn create_vmnet_config_dict() -> CFMutableDictionaryRef {
unsafe { CFDictionaryCreateMutable(kCFAllocatorDefault, 0, ptr::null(), ptr::null()) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vmnet_return_messages() {
assert_eq!(VmnetReturnT::Success.message(), "success");
assert_eq!(VmnetReturnT::Failure.message(), "operation failed");
assert!(VmnetReturnT::Success.is_success());
assert!(!VmnetReturnT::Failure.is_success());
}
#[test]
fn test_operating_modes() {
assert_eq!(VmnetOperatingMode::Host as i32, 1000);
assert_eq!(VmnetOperatingMode::Shared as i32, 1001);
assert_eq!(VmnetOperatingMode::Bridged as i32, 1002);
}
}