Skip to main content

binaryninja/similarity/
node.rs

1use super::{
2    SimilarityApplyStatus, SimilarityEntityId, SimilarityEntityInfo, SimilarityEntityRef,
3    SimilarityResult, SimilarityResultId, SimilaritySessionNodeId,
4};
5use crate::binary_view::BinaryView;
6use crate::file_metadata::FileMetadata;
7use crate::function::Function;
8use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
9use crate::settings::Settings;
10use binaryninjacore_sys::*;
11use std::ffi::{CStr, CString};
12
13/// The main unit of similarity processing.
14pub struct SimilaritySessionNode {
15    pub(crate) handle: *mut BNSimilaritySessionNode,
16}
17
18impl SimilaritySessionNode {
19    /// Creates a node for an open view and schedules its functions.
20    pub fn new(view: &BinaryView) -> Ref<Self> {
21        let handle = unsafe { BNCreateSimilaritySessionNode(view.handle) };
22        unsafe { Ref::new(Self { handle }) }
23    }
24
25    /// Creates a node that opens its view and schedules its functions when run.
26    ///
27    /// The session closes the view when the run no longer needs it, so the view may be unavailable at
28    /// other times.
29    pub fn new_from_file(file: &FileMetadata) -> Ref<Self> {
30        let handle = unsafe { BNCreateSimilaritySessionNodeFromFile(file.handle) };
31        unsafe { Ref::new(Self { handle }) }
32    }
33
34    pub unsafe fn from_raw(handle: *mut BNSimilaritySessionNode) -> Self {
35        Self { handle }
36    }
37
38    pub unsafe fn ref_from_raw(handle: *mut BNSimilaritySessionNode) -> Ref<Self> {
39        Ref::new(Self { handle })
40    }
41
42    /// Returns the node's open view if one is available.
43    ///
44    /// If the node was created with [`SimilaritySessionNode::new_from_file`], its view may be
45    /// unavailable outside a session run. The session closes the view when the run no longer needs
46    /// it.
47    pub fn view(&self) -> Option<Ref<BinaryView>> {
48        let view = unsafe { BNSimilaritySessionNodeGetView(self.handle) };
49        if view.is_null() {
50            None
51        } else {
52            Some(unsafe { BinaryView::ref_from_raw(view) })
53        }
54    }
55
56    /// Replaces the node's view and creates entities for its functions.
57    ///
58    /// A view backed by a different [`FileMetadata`] is ignored. Do not mix files.
59    pub fn set_view(&self, view: Option<&BinaryView>) {
60        unsafe {
61            BNSimilaritySessionNodeSetView(
62                self.handle,
63                view.map(|view| view.handle).unwrap_or(std::ptr::null_mut()),
64            )
65        }
66    }
67
68    /// Returns the file used by the node.
69    pub fn file(&self) -> Ref<FileMetadata> {
70        let file = unsafe { BNSimilaritySessionNodeGetFile(self.handle) };
71        FileMetadata::ref_from_raw(file)
72    }
73
74    /// Returns the settings used when this node opens its view.
75    ///
76    /// Modify the returned settings before running the session.
77    pub fn load_options(&self) -> Ref<Settings> {
78        let settings = unsafe { BNSimilaritySessionNodeGetLoadOptions(self.handle) };
79        unsafe { Settings::ref_from_raw(settings) }
80    }
81
82    /// Returns the node's ID.
83    pub fn id(&self) -> SimilaritySessionNodeId {
84        unsafe { BNSimilaritySessionNodeGetId(self.handle) }.into()
85    }
86
87    /// Adds an entity without scheduling it.
88    ///
89    /// An existing entity with the same type and address is reused. A non-empty name refreshes its
90    /// display name.
91    pub fn create_entity(&self, info: SimilarityEntityInfo) -> SimilarityEntityId {
92        let name =
93            CString::new(info.name).expect("similarity entity names cannot contain null bytes");
94        let raw_info = BNSimilarityEntityInfo {
95            type_: info.entity_type,
96            address: info.address,
97            name: name.as_ptr(),
98        };
99        unsafe { BNSimilaritySessionNodeCreateEntity(self.handle, &raw_info) }.into()
100    }
101
102    /// Removes an entity, its schedule, its provider results, and its selected result.
103    ///
104    /// If you are looking to unschedule an entity, use [`SimilaritySessionNode::remove_scheduled_entity`].
105    pub fn remove_entity(&self, id: SimilarityEntityId) -> bool {
106        unsafe { BNSimilaritySessionNodeRemoveEntity(self.handle, id.into()) }
107    }
108
109    /// Returns information about an entity.
110    pub fn entity(&self, id: SimilarityEntityId) -> Option<SimilarityEntityInfo> {
111        let mut info = BNSimilarityEntityInfo::default();
112        let success =
113            unsafe { BNSimilaritySessionNodeGetEntity(self.handle, id.into(), &mut info) };
114        if success {
115            let result = SimilarityEntityInfo {
116                entity_type: info.type_,
117                address: info.address,
118                name: if info.name.is_null() {
119                    String::new()
120                } else {
121                    unsafe { CStr::from_ptr(info.name) }
122                        .to_string_lossy()
123                        .into_owned()
124                },
125            };
126            unsafe { BNFreeSimilarityEntityInfo(&mut info) };
127            Some(result)
128        } else {
129            None
130        }
131    }
132
133    /// Returns all entities in the node, including entities used only as match targets.
134    pub fn entities(&self) -> Array<SimilarityEntityId> {
135        let mut count = 0;
136        let entities = unsafe { BNSimilaritySessionNodeGetEntities(self.handle, &mut count) };
137        unsafe { Array::new(entities, count, ()) }
138    }
139
140    /// Schedules an entity for the next provider round.
141    ///
142    /// The session consumes each scheduled batch before resolution. Resolvers can schedule an entity again to
143    /// request another round.
144    ///
145    /// New nodes schedule all available entities, so this is mainly needed for entities added later.
146    pub fn add_scheduled_entity(&self, id: SimilarityEntityId) -> bool {
147        unsafe { BNSimilaritySessionNodeAddScheduledEntity(self.handle, id.into()) }
148    }
149
150    /// Unschedules an entity without removing it from the node.
151    ///
152    /// If you are looking to remove an entity, use [`SimilaritySessionNode::remove_entity`].
153    pub fn remove_scheduled_entity(&self, id: SimilarityEntityId) -> bool {
154        unsafe { BNSimilaritySessionNodeRemoveScheduledEntity(self.handle, id.into()) }
155    }
156
157    /// Returns the entities waiting for provider processing.
158    pub fn scheduled_entities(&self) -> Array<SimilarityEntityId> {
159        let mut count = 0;
160        let entities =
161            unsafe { BNSimilaritySessionNodeGetScheduledEntities(self.handle, &mut count) };
162        unsafe { Array::new(entities, count, ()) }
163    }
164
165    /// Returns the function represented by an entity, if it is available.
166    pub fn entity_function(&self, id: SimilarityEntityId) -> Option<Ref<Function>> {
167        let function = unsafe { BNSimilaritySessionNodeGetEntityFunction(self.handle, id.into()) };
168        if function.is_null() {
169            None
170        } else {
171            Some(unsafe { Function::ref_from_raw(function) })
172        }
173    }
174
175    /// Returns the result IDs for an entity.
176    pub fn results(&self, entity: SimilarityEntityId) -> Vec<SimilarityResultId> {
177        let mut count = 0;
178        let results =
179            unsafe { BNSimilaritySessionNodeGetResults(self.handle, entity.into(), &mut count) };
180        let output = unsafe { std::slice::from_raw_parts(results, count) }
181            .iter()
182            .copied()
183            .map(SimilarityResultId::from)
184            .collect();
185        unsafe { BNFreeSimilarityResultIdList(results) };
186        output
187    }
188
189    /// Returns a stored result by its ID, which is unique within the node.
190    pub fn result(&self, result: SimilarityResultId) -> Option<SimilarityResult> {
191        let mut output = BNSimilarityResult::default();
192        unsafe {
193            BNSimilaritySessionNodeGetResult(self.handle, result.into(), &mut output)
194                .then(|| output.into())
195        }
196    }
197
198    /// Applies the standard metadata transfer from a target entity.
199    pub fn apply_target(
200        &self,
201        entity: SimilarityEntityId,
202        target: SimilarityEntityRef,
203    ) -> SimilarityApplyStatus {
204        let target = target.into();
205        unsafe { BNSimilaritySessionNodeApplyTarget(self.handle, entity.into(), &target) }
206    }
207
208    /// Selects a provider result for an entity.
209    ///
210    pub fn set_resolved_result(
211        &self,
212        entity: SimilarityEntityId,
213        result: SimilarityResultId,
214    ) -> bool {
215        unsafe {
216            BNSimilaritySessionNodeSetResolvedResult(self.handle, entity.into(), result.into())
217        }
218    }
219
220    /// Returns the selected result for an entity.
221    pub fn resolved_result(&self, entity: SimilarityEntityId) -> Option<SimilarityResultId> {
222        let mut result = BNSimilarityResultId::default();
223        let success = unsafe {
224            BNSimilaritySessionNodeGetResolvedResult(self.handle, entity.into(), &mut result)
225        };
226        success.then(|| result.into())
227    }
228
229    /// Clears the selected result for an entity.
230    pub fn clear_resolved_result(&self, entity: SimilarityEntityId) -> bool {
231        unsafe { BNSimilaritySessionNodeClearResolvedResult(self.handle, entity.into()) }
232    }
233
234    /// Returns the IDs of nodes with edges into this node, in ascending order.
235    pub fn incoming_edges(&self) -> Vec<SimilaritySessionNodeId> {
236        let mut count = 0;
237        let edges = unsafe { BNSimilaritySessionNodeGetIncomingEdges(self.handle, &mut count) };
238        let result = unsafe { std::slice::from_raw_parts(edges, count) }
239            .iter()
240            .copied()
241            .map(Into::into)
242            .collect();
243        unsafe { BNFreeSimilaritySessionNodeEdgeList(edges) };
244        result
245    }
246
247    /// Returns the IDs of nodes with edges out of this node, in ascending order.
248    pub fn outgoing_edges(&self) -> Vec<SimilaritySessionNodeId> {
249        let mut count = 0;
250        let edges = unsafe { BNSimilaritySessionNodeGetOutgoingEdges(self.handle, &mut count) };
251        let result = unsafe { std::slice::from_raw_parts(edges, count) }
252            .iter()
253            .copied()
254            .map(Into::into)
255            .collect();
256        unsafe { BNFreeSimilaritySessionNodeEdgeList(edges) };
257        result
258    }
259
260    /// Returns nodes with edges into this node, ordered by ID.
261    pub fn incoming_nodes(&self) -> Array<SimilaritySessionNode> {
262        let mut count = 0;
263        let result = unsafe { BNSimilaritySessionNodeGetIncomingNodes(self.handle, &mut count) };
264        unsafe { Array::new(result, count, ()) }
265    }
266
267    /// Returns nodes with edges out of this node, ordered by ID.
268    pub fn outgoing_nodes(&self) -> Array<SimilaritySessionNode> {
269        let mut count = 0;
270        let result = unsafe { BNSimilaritySessionNodeGetOutgoingNodes(self.handle, &mut count) };
271        unsafe { Array::new(result, count, ()) }
272    }
273}
274
275unsafe impl RefCountable for SimilaritySessionNode {
276    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
277        Ref::new(Self {
278            handle: BNNewSimilaritySessionNodeReference(handle.handle),
279        })
280    }
281    unsafe fn dec_ref(handle: &Self) {
282        BNFreeSimilaritySessionNode(handle.handle);
283    }
284}
285
286impl ToOwned for SimilaritySessionNode {
287    type Owned = Ref<Self>;
288
289    fn to_owned(&self) -> Self::Owned {
290        unsafe { RefCountable::inc_ref(self) }
291    }
292}
293
294impl CoreArrayProvider for SimilaritySessionNode {
295    type Raw = *mut BNSimilaritySessionNode;
296    type Context = ();
297    type Wrapped<'a> = Guard<'a, Self>;
298}
299
300impl CoreArrayProvider for SimilarityEntityId {
301    type Raw = BNSimilarityEntityId;
302    type Context = ();
303    type Wrapped<'a> = SimilarityEntityId;
304}
305
306unsafe impl CoreArrayProviderInner for SimilarityEntityId {
307    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
308        BNFreeSimilarityEntityList(raw)
309    }
310
311    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
312        (*raw).into()
313    }
314}
315
316unsafe impl CoreArrayProviderInner for SimilaritySessionNode {
317    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
318        BNFreeSimilaritySessionNodeList(raw, count)
319    }
320
321    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
322        Guard::new(Self::from_raw(*raw), context)
323    }
324}