rns-hooks 0.1.9

Programmable hook system for the Reticulum Network Stack
Documentation
#![cfg(all(feature = "native", target_os = "linux"))]

use std::fs;
use std::path::PathBuf;
use std::process::Command;

use rns_hooks::{HookBackend, HookContext, HookManager, NullEngine, Verdict};

#[test]
fn native_hook_loads_and_executes() {
    let lib_path = build_native_fixture();
    let manager = HookManager::new().expect("manager");
    let mut program = manager
        .load_file_backend("native-test".into(), &lib_path, 0, HookBackend::Native)
        .expect("load native hook");

    assert_eq!(program.backend_name(), "native");

    let exec = manager
        .execute_program(&mut program, &HookContext::Tick, &NullEngine, 0.0, None)
        .expect("native hook result");
    let result = exec.hook_result.expect("hook result");
    assert_eq!(Verdict::from_u32(result.verdict), Some(Verdict::Continue));
    assert!(exec.injected_actions.is_empty());
    assert!(exec.provider_events.is_empty());
    assert!(exec.modified_data.is_none());
}

fn build_native_fixture() -> PathBuf {
    let dir = std::env::temp_dir().join(format!("rns-hooks-native-test-{}", std::process::id()));
    fs::create_dir_all(&dir).expect("create fixture dir");

    let source = dir.join("native_hook.c");
    let lib_path = dir.join(format!(
        "libnative_hook.{}",
        std::env::consts::DLL_EXTENSION
    ));
    fs::write(
        &source,
        r#"
#include <stddef.h>
#include <stdint.h>

typedef struct HookResult {
    uint32_t verdict;
    uint32_t modified_data_offset;
    uint32_t modified_data_len;
    uint32_t inject_actions_offset;
    uint32_t inject_actions_count;
    uint32_t log_offset;
    uint32_t log_len;
} HookResult;

typedef struct RnsNativeHostApi RnsNativeHostApi;

int rns_hook_abi_version(void) {
    return 1;
}

int rns_hook_on_call(
    const uint8_t *ctx,
    size_t ctx_len,
    const RnsNativeHostApi *host_api,
    HookResult *result
) {
    (void)host_api;
    if (ctx == NULL || ctx_len < 4 || result == NULL) {
        return -1;
    }
    uint32_t ctx_type =
        ((uint32_t)ctx[0]) |
        ((uint32_t)ctx[1] << 8) |
        ((uint32_t)ctx[2] << 16) |
        ((uint32_t)ctx[3] << 24);
    if (ctx_type != 2) {
        return -2;
    }
    result->verdict = 0;
    result->modified_data_offset = 0;
    result->modified_data_len = 0;
    result->inject_actions_offset = 0;
    result->inject_actions_count = 0;
    result->log_offset = 0;
    result->log_len = 0;
    return 0;
}
"#,
    )
    .expect("write fixture source");

    let output = Command::new("cc")
        .arg("-shared")
        .arg("-fPIC")
        .arg(&source)
        .arg("-o")
        .arg(&lib_path)
        .output()
        .expect("run cc");
    assert!(
        output.status.success(),
        "cc failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    lib_path
}