summaryrefslogtreecommitdiff
path: root/rust/src/websocket/provider.rs
diff options
context:
space:
mode:
authorRubens Brandao <git@rubens.io>2024-06-26 17:24:33 -0300
committerMason Reed <mason@vector35.com>2025-02-07 15:52:59 -0500
commita039d392b54170d236d9d993024e9eca38f701ce (patch)
treeac5900027386c9b4a1197689008c646eb59e0b80 /rust/src/websocket/provider.rs
parent3f0a217d7d097c0502af81e78a01ed3bf9f38761 (diff)
Implement Rust WebsocketProvider
Diffstat (limited to 'rust/src/websocket/provider.rs')
-rw-r--r--rust/src/websocket/provider.rs128
1 files changed, 128 insertions, 0 deletions
diff --git a/rust/src/websocket/provider.rs b/rust/src/websocket/provider.rs
new file mode 100644
index 00000000..0e28afe4
--- /dev/null
+++ b/rust/src/websocket/provider.rs
@@ -0,0 +1,128 @@
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{BnStrCompatible, BnString};
+use crate::websocket::client;
+use crate::websocket::client::{CoreWebsocketClient, WebsocketClient};
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::mem::MaybeUninit;
+use std::ptr::NonNull;
+
+pub fn register_websocket_provider<W>(name: &str) -> &'static mut W
+where
+ W: WebsocketProvider,
+{
+ let name = name.into_bytes_with_nul();
+ let provider_uninit = MaybeUninit::uninit();
+ // SAFETY: Websocket provider is never freed
+ let leaked_provider = Box::leak(Box::new(provider_uninit));
+ let result = unsafe {
+ BNRegisterWebsocketProvider(
+ name.as_ptr() as *const c_char,
+ &mut BNWebsocketProviderCallbacks {
+ context: leaked_provider as *mut _ as *mut c_void,
+ createClient: Some(cb_create_client::<W>),
+ },
+ )
+ };
+
+ let provider_core = unsafe { CoreWebsocketProvider::from_raw(NonNull::new(result).unwrap()) };
+ // We now have the core provider so we can actually construct the object.
+ leaked_provider.write(W::from_core(provider_core));
+ unsafe { leaked_provider.assume_init_mut() }
+}
+
+pub trait WebsocketProvider: Sync + Send + Sized {
+ type Client: WebsocketClient;
+
+ fn handle(&self) -> CoreWebsocketProvider;
+
+ /// Called to construct this provider object with the given core object.
+ fn from_core(core: CoreWebsocketProvider) -> Self;
+
+ /// Create a new instance of the websocket client.
+ fn create_client(&self) -> Result<Ref<CoreWebsocketClient>, ()> {
+ let client_uninit = MaybeUninit::uninit();
+ // SAFETY: Websocket client is freed by cb_destroy_client
+ let leaked_client = Box::leak(Box::new(client_uninit));
+ let mut callbacks = BNWebsocketClientCallbacks {
+ context: leaked_client as *mut _ as *mut c_void,
+ connect: Some(client::cb_connect::<Self::Client>),
+ destroyClient: Some(client::cb_destroy_client::<Self::Client>),
+ disconnect: Some(client::cb_disconnect::<Self::Client>),
+ write: Some(client::cb_write::<Self::Client>),
+ };
+ let client_ptr =
+ unsafe { BNInitWebsocketClient(self.handle().handle.as_ptr(), &mut callbacks) };
+ // TODO: If possible pass a sensible error back...
+ let client_ptr = NonNull::new(client_ptr).ok_or(())?;
+ let client_ref = unsafe { CoreWebsocketClient::ref_from_raw(client_ptr) };
+ // We now have the core client so we can actually construct the object.
+ leaked_client.write(Self::Client::from_core(client_ref.clone()));
+ Ok(client_ref)
+ }
+}
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct CoreWebsocketProvider {
+ handle: NonNull<BNWebsocketProvider>,
+}
+
+impl CoreWebsocketProvider {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNWebsocketProvider>) -> Self {
+ Self { handle }
+ }
+
+ pub fn all() -> Array<Self> {
+ let mut count = 0;
+ let result = unsafe { BNGetWebsocketProviderList(&mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn by_name<S: BnStrCompatible>(name: S) -> Option<CoreWebsocketProvider> {
+ let name = name.into_bytes_with_nul();
+ let result =
+ unsafe { BNGetWebsocketProviderByName(name.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(result).map(|h| unsafe { Self::from_raw(h) })
+ }
+
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNGetWebsocketProviderName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+}
+
+unsafe impl Sync for CoreWebsocketProvider {}
+unsafe impl Send for CoreWebsocketProvider {}
+
+impl CoreArrayProvider for CoreWebsocketProvider {
+ type Raw = *mut BNWebsocketProvider;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreWebsocketProvider {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeWebsocketProviderList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let handle = NonNull::new(*raw).unwrap();
+ Self::from_raw(handle)
+ }
+}
+
+unsafe extern "C" fn cb_create_client<W: WebsocketProvider>(
+ ctxt: *mut c_void,
+) -> *mut BNWebsocketClient {
+ let ctxt: &mut W = &mut *(ctxt as *mut W);
+ match ctxt.create_client() {
+ Ok(owned_client) => {
+ // SAFETY: The caller is assumed to have picked up this ref.
+ Ref::into_raw(owned_client).handle.as_ptr()
+ }
+ Err(_) => std::ptr::null_mut(),
+ }
+}