summaryrefslogtreecommitdiff
path: root/rust/src/download/provider.rs
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-09-27 17:39:19 -0400
committerMason Reed <mason@vector35.com>2025-10-22 00:36:24 -0400
commit31ec051fd683d3747f10a0981b497d90f08d25a3 (patch)
tree039f28498656a427297406d90955766981bc0b06 /rust/src/download/provider.rs
parent058800a116bf25f1f215b758663366d4695f0e31 (diff)
[Rust] Refactor download provider module to allow for custom implementations
Diffstat (limited to 'rust/src/download/provider.rs')
-rw-r--r--rust/src/download/provider.rs124
1 files changed, 124 insertions, 0 deletions
diff --git a/rust/src/download/provider.rs b/rust/src/download/provider.rs
new file mode 100644
index 00000000..795c4a7c
--- /dev/null
+++ b/rust/src/download/provider.rs
@@ -0,0 +1,124 @@
+use crate::download::{CustomDownloadInstance, DownloadInstance};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref};
+use crate::settings::Settings;
+use crate::string::IntoCStr;
+use binaryninjacore_sys::*;
+use std::ffi::c_void;
+use std::mem::MaybeUninit;
+
+/// Register a new download provider type, which is used by the core (and other plugins) to make HTTP requests.
+pub fn register_download_provider<C>(name: &str) -> &'static mut C
+where
+ C: CustomDownloadProvider,
+{
+ let name = name.to_cstr();
+ let provider_uninit = MaybeUninit::uninit();
+ // SAFETY: Download provider is never freed
+ let leaked_provider = Box::leak(Box::new(provider_uninit));
+ let result = unsafe {
+ BNRegisterDownloadProvider(
+ name.as_ptr(),
+ &mut BNDownloadProviderCallbacks {
+ context: leaked_provider as *mut _ as *mut c_void,
+ createInstance: Some(cb_create_instance::<C>),
+ },
+ )
+ };
+
+ let provider_core = DownloadProvider::from_raw(result);
+ // We now have the core provider so we can actually construct the object.
+ leaked_provider.write(C::from_core(provider_core));
+ unsafe { leaked_provider.assume_init_mut() }
+}
+
+pub trait CustomDownloadProvider: 'static + Sync {
+ type Instance: CustomDownloadInstance;
+
+ fn handle(&self) -> DownloadProvider;
+
+ /// Called to construct this provider object with the given core object.
+ fn from_core(core: DownloadProvider) -> Self;
+
+ fn create_instance(&self) -> Result<Ref<DownloadInstance>, ()> {
+ Self::Instance::new_with_provider(self.handle())
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct DownloadProvider {
+ pub(crate) handle: *mut BNDownloadProvider,
+}
+
+impl DownloadProvider {
+ pub(crate) fn from_raw(handle: *mut BNDownloadProvider) -> DownloadProvider {
+ Self { handle }
+ }
+
+ pub fn get(name: &str) -> Option<DownloadProvider> {
+ let name = name.to_cstr();
+ let result = unsafe { BNGetDownloadProviderByName(name.as_ptr()) };
+ if result.is_null() {
+ return None;
+ }
+ Some(DownloadProvider { handle: result })
+ }
+
+ pub fn list() -> Result<Array<DownloadProvider>, ()> {
+ let mut count = 0;
+ let list: *mut *mut BNDownloadProvider = unsafe { BNGetDownloadProviderList(&mut count) };
+
+ if list.is_null() {
+ return Err(());
+ }
+
+ Ok(unsafe { Array::new(list, count, ()) })
+ }
+
+ /// TODO: We may want to `impl Default`, error checking might be preventing us from doing so
+ pub fn try_default() -> Result<DownloadProvider, ()> {
+ let s = Settings::new();
+ let dp_name = s.get_string("network.downloadProviderName");
+ Self::get(&dp_name).ok_or(())
+ }
+
+ pub fn create_instance(&self) -> Result<Ref<DownloadInstance>, ()> {
+ let result: *mut BNDownloadInstance =
+ unsafe { BNCreateDownloadProviderInstance(self.handle) };
+ if result.is_null() {
+ return Err(());
+ }
+
+ Ok(unsafe { DownloadInstance::ref_from_raw(result) })
+ }
+}
+
+impl CoreArrayProvider for DownloadProvider {
+ type Raw = *mut BNDownloadProvider;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, DownloadProvider>;
+}
+
+unsafe impl CoreArrayProviderInner for DownloadProvider {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeDownloadProviderList(raw);
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(DownloadProvider::from_raw(*raw), &())
+ }
+}
+
+unsafe impl Send for DownloadProvider {}
+unsafe impl Sync for DownloadProvider {}
+
+unsafe extern "C" fn cb_create_instance<C: CustomDownloadProvider>(
+ ctxt: *mut c_void,
+) -> *mut BNDownloadInstance {
+ ffi_wrap!("CustomDownloadProvider::cb_create_instance", unsafe {
+ let provider = &*(ctxt as *const C);
+ match provider.create_instance() {
+ Ok(instance) => Ref::into_raw(instance).handle,
+ Err(_) => std::ptr::null_mut(),
+ }
+ })
+}