diff --git a/mycelium/include/mycelium.h b/mycelium/include/mycelium.h index 8db19c6..75c1add 100644 --- a/mycelium/include/mycelium.h +++ b/mycelium/include/mycelium.h @@ -216,82 +216,324 @@ const char *mycelium_last_error_message(void); Start a mycelium node. Returns an opaque handle on success, or NULL on failure — call `mycelium_last_error_message` for details. The returned handle must eventually be released with `mycelium_node_free`. + + # Safety + + `cfg` must point to a fully-initialised `mycelium_start_config_t`. + Pointer fields inside (`peers`, `peer_discovery_mode`, + `peer_discovery_interfaces`, `tun_name`) follow the conventions in the + module-level safety docs. The struct and any strings it points at are + not retained beyond the call — the caller may free them as soon as + this function returns. */ struct mycelium_node_t *mycelium_start(const struct mycelium_start_config_t *cfg); /* Halt the node and release its internal resources. The handle remains valid for `mycelium_is_running` queries (which will return false) until - `mycelium_node_free` is called. No-op on a NULL or already-stopped node. + `mycelium_node_free` is called. No-op on an already-stopped node. + + # Safety + + `node` must be NULL or a live handle returned by `mycelium_start`. */ int32_t mycelium_stop(struct mycelium_node_t *node); /* Write `true`/`false` into `out` reflecting whether the node is running. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `bool`. */ int32_t mycelium_is_running(struct mycelium_node_t *node, bool *out); +/* + Populate `out` with the node's subnet and public key. The returned + strings are owned by the caller and must be released with + `mycelium_node_info_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_node_info_t`. Any + previous contents of `*out` are overwritten without being freed — + callers must release prior contents first or pass a fresh struct. + */ int32_t mycelium_get_node_info(struct mycelium_node_t *node, struct mycelium_node_info_t *out); +/* + Look up the public key of the peer that owns the given mycelium IP. + On success writes a newly-allocated string to `*out` (empty if no + match); release with `mycelium_string_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `ip` must + be a NUL-terminated C string. `out` must point to a writable + `*mut c_char`. + */ int32_t mycelium_get_public_key_from_ip(struct mycelium_node_t *node, const char *ip, char **out); +/* + Populate `out` with one entry per known peer. Release the array with + `mycelium_peer_info_array_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_peer_info_array_t`; + any previous contents are overwritten without being freed. + */ int32_t mycelium_get_peers(struct mycelium_node_t *node, struct mycelium_peer_info_array_t *out); +/* + Add a peer endpoint. Writes `true` to `*out` if the peer was newly + added, `false` if it was already known. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `endpoint` + must be a NUL-terminated C string. `out` must point to a writable + `bool`. + */ int32_t mycelium_add_peer(struct mycelium_node_t *node, const char *endpoint, bool *out); +/* + Remove a peer endpoint. Writes `true` to `*out` if the peer existed + and was removed, `false` if no such peer was registered. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `endpoint` + must be a NUL-terminated C string. `out` must point to a writable + `bool`. + */ int32_t mycelium_remove_peer(struct mycelium_node_t *node, const char *endpoint, bool *out); +/* + Populate `out` with the currently selected (in-use) routes. Release + the array with `mycelium_route_array_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_route_array_t`; any + previous contents are overwritten without being freed. + */ int32_t mycelium_get_selected_routes(struct mycelium_node_t *node, struct mycelium_route_array_t *out); +/* + Populate `out` with the routing table's fallback (non-selected) routes. + Release the array with `mycelium_route_array_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_route_array_t`; any + previous contents are overwritten without being freed. + */ int32_t mycelium_get_fallback_routes(struct mycelium_node_t *node, struct mycelium_route_array_t *out); +/* + Populate `out` with subnets the router is currently querying. Release + the array with `mycelium_queried_subnet_array_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_queried_subnet_array_t`; + any previous contents are overwritten without being freed. + */ int32_t mycelium_get_queried_subnets(struct mycelium_node_t *node, struct mycelium_queried_subnet_array_t *out); +/* + Populate `out` with aggregated packet statistics. Release with + `mycelium_packet_stats_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_packet_stats_t`; any + previous contents are overwritten without being freed. + */ int32_t mycelium_get_packet_stats(struct mycelium_node_t *node, struct mycelium_packet_stats_t *out); +/* + Generate a fresh 32-byte node secret key into `out`. Stateless — no + node handle required. + + # Safety + + `out` must point to a writable, properly aligned + `mycelium_secret_key_t`. + */ int32_t mycelium_generate_secret_key(struct mycelium_secret_key_t *out); +/* + Derive the mycelium address (text form) of the public key for the + given secret key. Writes a newly-allocated string to `*out`; release + with `mycelium_string_free`. Stateless — no node handle required. + + # Safety + + `key` must point to an initialised `mycelium_secret_key_t`. `out` must + point to a writable `*mut c_char`. + */ int32_t mycelium_address_from_secret_key(const struct mycelium_secret_key_t *key, char **out); +/* + Begin the periodic SOCKS5 proxy probe scan. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. + */ int32_t mycelium_start_proxy_probe(struct mycelium_node_t *node); +/* + Stop the periodic SOCKS5 proxy probe scan. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. + */ int32_t mycelium_stop_proxy_probe(struct mycelium_node_t *node); +/* + List the currently known SOCKS5 proxy endpoints (formatted as + `addr:port` strings). Release with `mycelium_string_array_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `out` must + point to a writable, properly aligned `mycelium_string_array_t`; any + previous contents are overwritten without being freed. + */ int32_t mycelium_list_proxies(struct mycelium_node_t *node, struct mycelium_string_array_t *out); +/* + Connect to a remote SOCKS5 proxy. Writes the negotiated remote address + to `*out` as a newly-allocated string; release with + `mycelium_string_free`. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. `remote` + must be a NUL-terminated C string. `out` must point to a writable + `*mut c_char`. + */ int32_t mycelium_proxy_connect(struct mycelium_node_t *node, const char *remote, char **out); +/* + Disconnect from the currently connected SOCKS5 proxy, if any. + + # Safety + + `node` must be a live handle returned by `mycelium_start`. + */ int32_t mycelium_proxy_disconnect(struct mycelium_node_t *node); /* Release a node handle returned by `mycelium_start`. If the node is still running it is stopped first. Safe to call with NULL. + + # Safety + + `node` must be NULL or a pointer previously returned by `mycelium_start` + that has not already been passed to this function. Double-free or + use-after-free is undefined behaviour. */ void mycelium_node_free(struct mycelium_node_t *node); /* Free a single C string returned by the library. Safe to call with NULL. + + # Safety + + `ptr` must be NULL or a value returned by a `mycelium_*` entry point + that hands the caller ownership of a `char *` (e.g. + `mycelium_get_public_key_from_ip`, `mycelium_address_from_secret_key`, + `mycelium_proxy_connect`). Strings retrieved via `mycelium_last_error_message` + are owned by the library and must NOT be passed here. */ void mycelium_string_free(char *ptr); /* - Free a string array returned by the library. The struct itself is - caller-allocated; this releases the contents (each item plus the array). + Free a string array returned by the library. The outer struct is + caller-allocated; this releases the items and the backing storage, then + resets `items` to NULL and `len` to 0. + + # Safety + + `arr` must be NULL or point to a `mycelium_string_array_t` populated by + a successful library call (currently only `mycelium_list_proxies`). + Calling this twice on the same struct is safe — the second call sees + `items == NULL` and does nothing. */ void mycelium_string_array_free(struct mycelium_string_array_t *arr); +/* + Free a `mycelium_node_info_t` populated by `mycelium_get_node_info`. + Releases the inner `subnet` and `pubkey` strings and resets them to + NULL. + + # Safety + + `info` must be NULL or point to a struct populated by a successful call + to `mycelium_get_node_info`. Calling this twice on the same struct is + safe — the second call sees the fields already NULL. + */ void mycelium_node_info_free(struct mycelium_node_info_t *info); +/* + Free a peer-info array populated by `mycelium_get_peers`. Releases each + item's strings, then the backing array, then resets the struct. + + # Safety + + `arr` must be NULL or point to a `mycelium_peer_info_array_t` populated + by a successful call to `mycelium_get_peers`. Idempotent. + */ void mycelium_peer_info_array_free(struct mycelium_peer_info_array_t *arr); +/* + Free a route array populated by `mycelium_get_selected_routes` or + `mycelium_get_fallback_routes`. + + # Safety + + `arr` must be NULL or point to a `mycelium_route_array_t` populated by + a successful call to one of those two functions. Idempotent. + */ void mycelium_route_array_free(struct mycelium_route_array_t *arr); +/* + Free a queried-subnet array populated by `mycelium_get_queried_subnets`. + + # Safety + + `arr` must be NULL or point to a `mycelium_queried_subnet_array_t` + populated by a successful call to `mycelium_get_queried_subnets`. + Idempotent. + */ void mycelium_queried_subnet_array_free(struct mycelium_queried_subnet_array_t *arr); +/* + Free a `mycelium_packet_stats_t` populated by + `mycelium_get_packet_stats`. Releases both the by-source and + by-destination arrays. + + # Safety + + `stats` must be NULL or point to a `mycelium_packet_stats_t` populated + by a successful call to `mycelium_get_packet_stats`. Idempotent. + */ void mycelium_packet_stats_free(struct mycelium_packet_stats_t *stats); #endif /* MYCELIUM_H */ diff --git a/mycelium/src/ffi/exports.rs b/mycelium/src/ffi/exports.rs index e95f9cb..6edebe9 100644 --- a/mycelium/src/ffi/exports.rs +++ b/mycelium/src/ffi/exports.rs @@ -11,7 +11,6 @@ //! the out-parameter. #![allow(non_camel_case_types)] -#![allow(clippy::missing_safety_doc)] use core::ffi::c_char; use std::ffi::CStr; @@ -124,6 +123,15 @@ macro_rules! with_running_node { /// Start a mycelium node. Returns an opaque handle on success, or NULL on /// failure — call `mycelium_last_error_message` for details. The returned /// handle must eventually be released with `mycelium_node_free`. +/// +/// # Safety +/// +/// `cfg` must point to a fully-initialised `mycelium_start_config_t`. +/// Pointer fields inside (`peers`, `peer_discovery_mode`, +/// `peer_discovery_interfaces`, `tun_name`) follow the conventions in the +/// module-level safety docs. The struct and any strings it points at are +/// not retained beyond the call — the caller may free them as soon as +/// this function returns. #[no_mangle] pub unsafe extern "C" fn mycelium_start( cfg: *const mycelium_start_config_t, @@ -231,7 +239,11 @@ pub unsafe extern "C" fn mycelium_start( /// Halt the node and release its internal resources. The handle remains /// valid for `mycelium_is_running` queries (which will return false) until -/// `mycelium_node_free` is called. No-op on a NULL or already-stopped node. +/// `mycelium_node_free` is called. No-op on an already-stopped node. +/// +/// # Safety +/// +/// `node` must be NULL or a live handle returned by `mycelium_start`. #[no_mangle] pub unsafe extern "C" fn mycelium_stop(node: *mut mycelium_node_t) -> i32 { error::clear(); @@ -257,6 +269,11 @@ pub unsafe extern "C" fn mycelium_stop(node: *mut mycelium_node_t) -> i32 { } /// Write `true`/`false` into `out` reflecting whether the node is running. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `bool`. #[no_mangle] pub unsafe extern "C" fn mycelium_is_running(node: *mut mycelium_node_t, out: *mut bool) -> i32 { error::clear(); @@ -279,6 +296,16 @@ pub unsafe extern "C" fn mycelium_is_running(node: *mut mycelium_node_t, out: *m // Identity / introspection // --------------------------------------------------------------------------- +/// Populate `out` with the node's subnet and public key. The returned +/// strings are owned by the caller and must be released with +/// `mycelium_node_info_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_node_info_t`. Any +/// previous contents of `*out` are overwritten without being freed — +/// callers must release prior contents first or pass a fresh struct. #[no_mangle] pub unsafe extern "C" fn mycelium_get_node_info( node: *mut mycelium_node_t, @@ -300,6 +327,15 @@ pub unsafe extern "C" fn mycelium_get_node_info( }) } +/// Look up the public key of the peer that owns the given mycelium IP. +/// On success writes a newly-allocated string to `*out` (empty if no +/// match); release with `mycelium_string_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `ip` must +/// be a NUL-terminated C string. `out` must point to a writable +/// `*mut c_char`. #[no_mangle] pub unsafe extern "C" fn mycelium_get_public_key_from_ip( node: *mut mycelium_node_t, @@ -339,6 +375,14 @@ pub unsafe extern "C" fn mycelium_get_public_key_from_ip( // Peers // --------------------------------------------------------------------------- +/// Populate `out` with one entry per known peer. Release the array with +/// `mycelium_peer_info_array_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_peer_info_array_t`; +/// any previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_get_peers( node: *mut mycelium_node_t, @@ -365,6 +409,14 @@ pub unsafe extern "C" fn mycelium_get_peers( }) } +/// Add a peer endpoint. Writes `true` to `*out` if the peer was newly +/// added, `false` if it was already known. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `endpoint` +/// must be a NUL-terminated C string. `out` must point to a writable +/// `bool`. #[no_mangle] pub unsafe extern "C" fn mycelium_add_peer( node: *mut mycelium_node_t, @@ -391,6 +443,14 @@ pub unsafe extern "C" fn mycelium_add_peer( }) } +/// Remove a peer endpoint. Writes `true` to `*out` if the peer existed +/// and was removed, `false` if no such peer was registered. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `endpoint` +/// must be a NUL-terminated C string. `out` must point to a writable +/// `bool`. #[no_mangle] pub unsafe extern "C" fn mycelium_remove_peer( node: *mut mycelium_node_t, @@ -421,6 +481,14 @@ pub unsafe extern "C" fn mycelium_remove_peer( // Routes // --------------------------------------------------------------------------- +/// Populate `out` with the currently selected (in-use) routes. Release +/// the array with `mycelium_route_array_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_route_array_t`; any +/// previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_get_selected_routes( node: *mut mycelium_node_t, @@ -447,6 +515,14 @@ pub unsafe extern "C" fn mycelium_get_selected_routes( }) } +/// Populate `out` with the routing table's fallback (non-selected) routes. +/// Release the array with `mycelium_route_array_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_route_array_t`; any +/// previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_get_fallback_routes( node: *mut mycelium_node_t, @@ -473,6 +549,14 @@ pub unsafe extern "C" fn mycelium_get_fallback_routes( }) } +/// Populate `out` with subnets the router is currently querying. Release +/// the array with `mycelium_queried_subnet_array_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_queried_subnet_array_t`; +/// any previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_get_queried_subnets( node: *mut mycelium_node_t, @@ -508,6 +592,14 @@ pub unsafe extern "C" fn mycelium_get_queried_subnets( // Packet statistics // --------------------------------------------------------------------------- +/// Populate `out` with aggregated packet statistics. Release with +/// `mycelium_packet_stats_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_packet_stats_t`; any +/// previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_get_packet_stats( node: *mut mycelium_node_t, @@ -547,6 +639,13 @@ pub unsafe extern "C" fn mycelium_get_packet_stats( // Key utilities (no node required) // --------------------------------------------------------------------------- +/// Generate a fresh 32-byte node secret key into `out`. Stateless — no +/// node handle required. +/// +/// # Safety +/// +/// `out` must point to a writable, properly aligned +/// `mycelium_secret_key_t`. #[no_mangle] pub unsafe extern "C" fn mycelium_generate_secret_key(out: *mut mycelium_secret_key_t) -> i32 { error::clear(); @@ -560,6 +659,14 @@ pub unsafe extern "C" fn mycelium_generate_secret_key(out: *mut mycelium_secret_ MYCELIUM_OK } +/// Derive the mycelium address (text form) of the public key for the +/// given secret key. Writes a newly-allocated string to `*out`; release +/// with `mycelium_string_free`. Stateless — no node handle required. +/// +/// # Safety +/// +/// `key` must point to an initialised `mycelium_secret_key_t`. `out` must +/// point to a writable `*mut c_char`. #[no_mangle] pub unsafe extern "C" fn mycelium_address_from_secret_key( key: *const mycelium_secret_key_t, @@ -583,6 +690,11 @@ pub unsafe extern "C" fn mycelium_address_from_secret_key( // Proxy // --------------------------------------------------------------------------- +/// Begin the periodic SOCKS5 proxy probe scan. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. #[no_mangle] pub unsafe extern "C" fn mycelium_start_proxy_probe(node: *mut mycelium_node_t) -> i32 { error::clear(); @@ -594,6 +706,11 @@ pub unsafe extern "C" fn mycelium_start_proxy_probe(node: *mut mycelium_node_t) }) } +/// Stop the periodic SOCKS5 proxy probe scan. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. #[no_mangle] pub unsafe extern "C" fn mycelium_stop_proxy_probe(node: *mut mycelium_node_t) -> i32 { error::clear(); @@ -605,6 +722,14 @@ pub unsafe extern "C" fn mycelium_stop_proxy_probe(node: *mut mycelium_node_t) - }) } +/// List the currently known SOCKS5 proxy endpoints (formatted as +/// `addr:port` strings). Release with `mycelium_string_array_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `out` must +/// point to a writable, properly aligned `mycelium_string_array_t`; any +/// previous contents are overwritten without being freed. #[no_mangle] pub unsafe extern "C" fn mycelium_list_proxies( node: *mut mycelium_node_t, @@ -634,6 +759,15 @@ pub unsafe extern "C" fn mycelium_list_proxies( }) } +/// Connect to a remote SOCKS5 proxy. Writes the negotiated remote address +/// to `*out` as a newly-allocated string; release with +/// `mycelium_string_free`. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. `remote` +/// must be a NUL-terminated C string. `out` must point to a writable +/// `*mut c_char`. #[no_mangle] pub unsafe extern "C" fn mycelium_proxy_connect( node: *mut mycelium_node_t, @@ -672,6 +806,11 @@ pub unsafe extern "C" fn mycelium_proxy_connect( }) } +/// Disconnect from the currently connected SOCKS5 proxy, if any. +/// +/// # Safety +/// +/// `node` must be a live handle returned by `mycelium_start`. #[no_mangle] pub unsafe extern "C" fn mycelium_proxy_disconnect(node: *mut mycelium_node_t) -> i32 { error::clear(); diff --git a/mycelium/src/ffi/mod.rs b/mycelium/src/ffi/mod.rs index 5b3fc42..53e10be 100644 --- a/mycelium/src/ffi/mod.rs +++ b/mycelium/src/ffi/mod.rs @@ -8,6 +8,31 @@ //! `mycelium_node_t *` that the caller passes back into every other entry //! point. Lifecycle (singleton-or-not, lifetime, threading) is the //! consumer's choice, not ours. +//! +//! # Safety conventions +//! +//! Common to every `extern "C"` entry point in this module; per-function +//! `# Safety` sections only document the bits that go beyond these: +//! +//! - Pointer arguments are either NULL (only where the per-function doc +//! says NULL is accepted) or valid: properly aligned, non-dangling, +//! pointing to an initialised value of the documented type. +//! - C string inputs (`*const c_char`) must be NUL-terminated. UTF-8 is +//! validated at runtime; invalid bytes yield `MYCELIUM_ERR_INVALID_ARG` +//! rather than UB. +//! - Out-pointers must point to writable, properly aligned memory of the +//! right type. Writes happen only on success (return code 0, or non-NULL +//! handle from `mycelium_start`). +//! - `mycelium_node_t *` arguments must come from `mycelium_start` and +//! must not have been passed to `mycelium_node_free`. Use-after-free, +//! double-free, or freeing on one thread while another thread is using +//! the same handle is undefined behaviour. +//! - Strings and structs that the library hands back are owned by the +//! caller and must be released with the matching `mycelium_*_free` +//! function. Reading the contents from multiple threads is safe; freeing +//! must not race with reads. +//! - The library is not async-signal safe; do not call entry points from +//! a signal handler. mod conversions; mod error; diff --git a/mycelium/src/ffi/types.rs b/mycelium/src/ffi/types.rs index 31f5c5a..60fb7a7 100644 --- a/mycelium/src/ffi/types.rs +++ b/mycelium/src/ffi/types.rs @@ -186,6 +186,12 @@ pub(super) unsafe fn drain_array(ptr: *mut T, len: usize) -> Vec { /// Release a node handle returned by `mycelium_start`. If the node is still /// running it is stopped first. Safe to call with NULL. +/// +/// # Safety +/// +/// `node` must be NULL or a pointer previously returned by `mycelium_start` +/// that has not already been passed to this function. Double-free or +/// use-after-free is undefined behaviour. #[no_mangle] pub unsafe extern "C" fn mycelium_node_free(node: *mut mycelium_node_t) { if node.is_null() { @@ -195,13 +201,29 @@ pub unsafe extern "C" fn mycelium_node_free(node: *mut mycelium_node_t) { } /// Free a single C string returned by the library. Safe to call with NULL. +/// +/// # Safety +/// +/// `ptr` must be NULL or a value returned by a `mycelium_*` entry point +/// that hands the caller ownership of a `char *` (e.g. +/// `mycelium_get_public_key_from_ip`, `mycelium_address_from_secret_key`, +/// `mycelium_proxy_connect`). Strings retrieved via `mycelium_last_error_message` +/// are owned by the library and must NOT be passed here. #[no_mangle] pub unsafe extern "C" fn mycelium_string_free(ptr: *mut c_char) { free_cstring(ptr); } -/// Free a string array returned by the library. The struct itself is -/// caller-allocated; this releases the contents (each item plus the array). +/// Free a string array returned by the library. The outer struct is +/// caller-allocated; this releases the items and the backing storage, then +/// resets `items` to NULL and `len` to 0. +/// +/// # Safety +/// +/// `arr` must be NULL or point to a `mycelium_string_array_t` populated by +/// a successful library call (currently only `mycelium_list_proxies`). +/// Calling this twice on the same struct is safe — the second call sees +/// `items == NULL` and does nothing. #[no_mangle] pub unsafe extern "C" fn mycelium_string_array_free(arr: *mut mycelium_string_array_t) { if arr.is_null() { @@ -215,6 +237,15 @@ pub unsafe extern "C" fn mycelium_string_array_free(arr: *mut mycelium_string_ar arr.len = 0; } +/// Free a `mycelium_node_info_t` populated by `mycelium_get_node_info`. +/// Releases the inner `subnet` and `pubkey` strings and resets them to +/// NULL. +/// +/// # Safety +/// +/// `info` must be NULL or point to a struct populated by a successful call +/// to `mycelium_get_node_info`. Calling this twice on the same struct is +/// safe — the second call sees the fields already NULL. #[no_mangle] pub unsafe extern "C" fn mycelium_node_info_free(info: *mut mycelium_node_info_t) { if info.is_null() { @@ -227,6 +258,13 @@ pub unsafe extern "C" fn mycelium_node_info_free(info: *mut mycelium_node_info_t info.pubkey = std::ptr::null_mut(); } +/// Free a peer-info array populated by `mycelium_get_peers`. Releases each +/// item's strings, then the backing array, then resets the struct. +/// +/// # Safety +/// +/// `arr` must be NULL or point to a `mycelium_peer_info_array_t` populated +/// by a successful call to `mycelium_get_peers`. Idempotent. #[no_mangle] pub unsafe extern "C" fn mycelium_peer_info_array_free(arr: *mut mycelium_peer_info_array_t) { if arr.is_null() { @@ -243,6 +281,13 @@ pub unsafe extern "C" fn mycelium_peer_info_array_free(arr: *mut mycelium_peer_i arr.len = 0; } +/// Free a route array populated by `mycelium_get_selected_routes` or +/// `mycelium_get_fallback_routes`. +/// +/// # Safety +/// +/// `arr` must be NULL or point to a `mycelium_route_array_t` populated by +/// a successful call to one of those two functions. Idempotent. #[no_mangle] pub unsafe extern "C" fn mycelium_route_array_free(arr: *mut mycelium_route_array_t) { if arr.is_null() { @@ -257,6 +302,13 @@ pub unsafe extern "C" fn mycelium_route_array_free(arr: *mut mycelium_route_arra arr.len = 0; } +/// Free a queried-subnet array populated by `mycelium_get_queried_subnets`. +/// +/// # Safety +/// +/// `arr` must be NULL or point to a `mycelium_queried_subnet_array_t` +/// populated by a successful call to `mycelium_get_queried_subnets`. +/// Idempotent. #[no_mangle] pub unsafe extern "C" fn mycelium_queried_subnet_array_free( arr: *mut mycelium_queried_subnet_array_t, @@ -278,6 +330,14 @@ unsafe fn free_packet_stat_entries(ptr: *mut mycelium_packet_stat_entry_t, len: } } +/// Free a `mycelium_packet_stats_t` populated by +/// `mycelium_get_packet_stats`. Releases both the by-source and +/// by-destination arrays. +/// +/// # Safety +/// +/// `stats` must be NULL or point to a `mycelium_packet_stats_t` populated +/// by a successful call to `mycelium_get_packet_stats`. Idempotent. #[no_mangle] pub unsafe extern "C" fn mycelium_packet_stats_free(stats: *mut mycelium_packet_stats_t) { if stats.is_null() {