From dfd246d01117ca5b300d73f75a8d2e8a04c8e0d4 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Tue, 11 Aug 2026 22:22:00 +0300 Subject: [PATCH] refactor(activator): model hooks as typed sets --- crates/activator/src/lib.rs | 4 +- crates/activator/src/model.rs | 86 +++++++++++++++-------- crates/protocol/src/generated/hook_ids.rs | 71 +++++++++++-------- scripts/codegen-hooks.py | 23 ++++-- zygisk/src/generated/hook_ids.rs | 71 +++++++++++-------- 5 files changed, 158 insertions(+), 97 deletions(-) diff --git a/crates/activator/src/lib.rs b/crates/activator/src/lib.rs index ffb451d..2e0528d 100644 --- a/crates/activator/src/lib.rs +++ b/crates/activator/src/lib.rs @@ -26,7 +26,7 @@ use vpnhide_apatch_abi::{ parse_kernel_version_hint as parse_apatch_kernel_version_hint, }; use vpnhide_protocol::Target; -use vpnhide_protocol::hook_ids::{HOOK_NAMES, KERNEL_HOOK_MASK, ZYGISK_HOOK_MASK}; +use vpnhide_protocol::hook_ids::{Hook, KERNEL_HOOK_MASK, ZYGISK_HOOK_MASK}; use vpnhide_protocol::{ KPM_ARGS_LEN, Kind, MAX_TARGET_UIDS, format_config, parse_config, peek_kind, }; @@ -392,7 +392,7 @@ pub fn boot_wait_requested_from_env() -> Result { fn has_native_targets(cfg: &CanonicalConfig, family: NativeHookFamily) -> bool { cfg.apps .values() - .any(|app| app.native.hookmask(family).is_some()) + .any(|app| app.native.hooks(family).is_some()) } fn has_ports_targets(cfg: &CanonicalConfig) -> bool { diff --git a/crates/activator/src/model.rs b/crates/activator/src/model.rs index ad43f60..b30c6f4 100644 --- a/crates/activator/src/model.rs +++ b/crates/activator/src/model.rs @@ -130,30 +130,66 @@ pub(crate) enum NativeHookFamily { } impl NativeHookFamily { - fn full_mask(self) -> u32 { + fn full_set(self) -> HookSet { match self { - NativeHookFamily::Kernel => KERNEL_HOOK_MASK, - NativeHookFamily::Zygisk => ZYGISK_HOOK_MASK, + NativeHookFamily::Kernel => HookSet::from_bits(KERNEL_HOOK_MASK), + NativeHookFamily::Zygisk => HookSet::from_bits(ZYGISK_HOOK_MASK), } } } +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub(crate) struct HookSet(u32); + +impl HookSet { + const fn from_bits(bits: u32) -> Self { + Self(bits) + } + + fn from_names(names: &[String]) -> Self { + names + .iter() + .filter_map(|name| Hook::from_name(name)) + .fold(Self::default(), |hooks, hook| hooks.with(hook)) + } + + const fn with(self, hook: Hook) -> Self { + Self(self.0 | hook.bit()) + } + + const fn restricted_to(self, owner: Self) -> Self { + Self(self.0 & owner.0) + } + + fn merge(&mut self, other: Self) { + self.0 |= other.0; + } + + const fn is_empty(self) -> bool { + self.0 == 0 + } + + const fn bits(self) -> u32 { + self.0 + } +} + impl NativeSelection { - pub(crate) fn hookmask(&self, family: NativeHookFamily) -> Option { + pub(crate) fn hooks(&self, family: NativeHookFamily) -> Option { match self { NativeSelection::Enabled(false) => None, - NativeSelection::Enabled(true) => Some(family.full_mask()), + NativeSelection::Enabled(true) => Some(family.full_set()), NativeSelection::Hooks(names) => { if names.is_empty() { return None; } - let mask = match family { + let hooks = match family { NativeHookFamily::Kernel => { - names.iter().fold(0u32, |acc, name| acc | hook_bit(name)) & KERNEL_HOOK_MASK + HookSet::from_names(names).restricted_to(family.full_set()) } - NativeHookFamily::Zygisk => ZYGISK_HOOK_MASK, + NativeHookFamily::Zygisk => family.full_set(), }; - (mask != 0).then_some(mask) + (!hooks.is_empty()).then_some(hooks) } NativeSelection::Detailed(detail) => { if !detail.enabled { @@ -164,11 +200,10 @@ impl NativeSelection { NativeHookFamily::Zygisk => &detail.zygisk, }; let Some(names) = selected else { - return Some(family.full_mask()); + return Some(family.full_set()); }; - let mask = - names.iter().fold(0u32, |acc, name| acc | hook_bit(name)) & family.full_mask(); - (mask != 0).then_some(mask) + let hooks = HookSet::from_names(names).restricted_to(family.full_set()); + (!hooks.is_empty()).then_some(hooks) } } } @@ -198,14 +233,6 @@ where }) } -fn hook_bit(name: &str) -> u32 { - HOOK_NAMES - .iter() - .position(|known| *known == name) - .map(|id| 1u32 << id) - .unwrap_or(0) -} - #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PackageUidMap { packages: BTreeMap>, @@ -350,9 +377,9 @@ pub(crate) fn project_native_with_resolver_for_family( resolver: &PackageUidMap, family: NativeHookFamily, ) -> String { - let mut by_uid = BTreeMap::::new(); + let mut by_uid = BTreeMap::::new(); for (pkg, app) in &cfg.apps { - let Some(mask) = app.native.hookmask(family) else { + let Some(hooks) = app.native.hooks(family) else { continue; }; // The APK is intentionally single-owner: only its main-profile copy @@ -368,8 +395,8 @@ pub(crate) fn project_native_with_resolver_for_family( { by_uid .entry(uid) - .and_modify(|existing| *existing |= mask) - .or_insert(mask); + .and_modify(|existing| existing.merge(hooks)) + .or_insert(hooks); } continue; } @@ -389,8 +416,8 @@ pub(crate) fn project_native_with_resolver_for_family( } by_uid .entry(*uid) - .and_modify(|existing| *existing |= mask) - .or_insert(mask); + .and_modify(|existing| existing.merge(hooks)) + .or_insert(hooks); } } // `by_uid` is a BTreeMap, so iteration is ascending by UID; truncating would @@ -403,7 +430,10 @@ pub(crate) fn project_native_with_resolver_for_family( let targets = by_uid .into_iter() .take(MAX_NATIVE_TARGETS) - .map(|(uid, hookmask)| Target { uid, hookmask }) + .map(|(uid, hooks)| Target { + uid, + hookmask: hooks.bits(), + }) .collect::>(); format_config(cfg.debug, NO_DEFAULT_MASK, &targets) } diff --git a/crates/protocol/src/generated/hook_ids.rs b/crates/protocol/src/generated/hook_ids.rs index e7d9751..d4a1968 100644 --- a/crates/protocol/src/generated/hook_ids.rs +++ b/crates/protocol/src/generated/hook_ids.rs @@ -62,6 +62,47 @@ pub enum Hook { ZygiskSetsockopt = 26, } +impl Hook { + /// This hook's bit in the control/stats wire mask. + pub const fn bit(self) -> u32 { + 1u32 << self as u32 + } + + /// Resolve a canonical config hook name. + pub fn from_name(name: &str) -> Option { + match name { + "fib_route_seq_show" => Some(Self::FibRouteSeqShow), + "ipv6_route_seq_show" => Some(Self::Ipv6RouteSeqShow), + "rtnl_fill_ifinfo" => Some(Self::RtnlFillIfinfo), + "inet_fill_ifaddr" => Some(Self::InetFillIfaddr), + "inet6_fill_ifaddr" => Some(Self::Inet6FillIfaddr), + "dev_ioctl" => Some(Self::DevIoctl), + "sock_ioctl" => Some(Self::SockIoctl), + "fib_dump_info" => Some(Self::FibDumpInfo), + "rt6_fill_node" => Some(Self::Rt6FillNode), + "fib_nl_fill_rule" => Some(Self::FibNlFillRule), + "lsposed_link_properties" => Some(Self::LsposedLinkProperties), + "lsposed_network_capabilities" => Some(Self::LsposedNetworkCapabilities), + "lsposed_network_info" => Some(Self::LsposedNetworkInfo), + "lsposed_network" => Some(Self::LsposedNetwork), + "lsposed_connectivity_result" => Some(Self::LsposedConnectivityResult), + "lsposed_connectivity_callback" => Some(Self::LsposedConnectivityCallback), + "lsposed_connectivity_network" => Some(Self::LsposedConnectivityNetwork), + "lsposed_package_visibility" => Some(Self::LsposedPackageVisibility), + "zygisk_ioctl" => Some(Self::ZygiskIoctl), + "zygisk_getifaddrs" => Some(Self::ZygiskGetifaddrs), + "zygisk_openat" => Some(Self::ZygiskOpenat), + "zygisk_recvmsg" => Some(Self::ZygiskRecvmsg), + "zygisk_recv" => Some(Self::ZygiskRecv), + "zygisk_recvfrom" => Some(Self::ZygiskRecvfrom), + "zygisk_recvfrom_chk" => Some(Self::ZygiskRecvfromChk), + "socket_bind_interface" => Some(Self::SocketBindInterface), + "zygisk_setsockopt" => Some(Self::ZygiskSetsockopt), + _ => None, + } + } +} + pub const HOOK_COUNT: u32 = 27; /// Hooks owned by each backend: apply `mask & own`. @@ -98,33 +139,3 @@ pub enum Backend { /// LSPosed Java-hook backend (system_server) Lsposed = 3, } - -pub const HOOK_NAMES: [&str; 27] = [ - "fib_route_seq_show", - "ipv6_route_seq_show", - "rtnl_fill_ifinfo", - "inet_fill_ifaddr", - "inet6_fill_ifaddr", - "dev_ioctl", - "sock_ioctl", - "fib_dump_info", - "rt6_fill_node", - "fib_nl_fill_rule", - "lsposed_link_properties", - "lsposed_network_capabilities", - "lsposed_network_info", - "lsposed_network", - "lsposed_connectivity_result", - "lsposed_connectivity_callback", - "lsposed_connectivity_network", - "lsposed_package_visibility", - "zygisk_ioctl", - "zygisk_getifaddrs", - "zygisk_openat", - "zygisk_recvmsg", - "zygisk_recv", - "zygisk_recvfrom", - "zygisk_recvfrom_chk", - "socket_bind_interface", - "zygisk_setsockopt", -]; diff --git a/scripts/codegen-hooks.py b/scripts/codegen-hooks.py index 9062f2b..f7dc6c6 100755 --- a/scripts/codegen-hooks.py +++ b/scripts/codegen-hooks.py @@ -209,6 +209,22 @@ def emit_rust(hooks: list[Hook], errs: list[Err], backends: list[Backend]) -> st L.append(f" {pascal(h.name)} = {h.id},") L.append("}") L.append("") + L.append("impl Hook {") + L.append(" /// This hook's bit in the control/stats wire mask.") + L.append(" pub const fn bit(self) -> u32 {") + L.append(" 1u32 << self as u32") + L.append(" }") + L.append("") + L.append(" /// Resolve a canonical config hook name.") + L.append(" pub fn from_name(name: &str) -> Option {") + L.append(" match name {") + for h in hooks: + L.append(f' "{h.name}" => Some(Self::{pascal(h.name)}),') + L.append(" _ => None,") + L.append(" }") + L.append(" }") + L.append("}") + L.append("") L.append(f"pub const HOOK_COUNT: u32 = {len(hooks)};") L.append("") L.append("/// Hooks owned by each backend: apply `mask & own`.") @@ -235,13 +251,6 @@ def emit_rust(hooks: list[Hook], errs: list[Err], backends: list[Backend]) -> st L.append(f" {pascal(b.name)} = {b.id},") L.append("}") L.append("") - # One name per line so the output is rustfmt-clean (CI runs `cargo fmt - # --check`); rustfmt wraps an over-width array into exactly this shape. - L.append(f"pub const HOOK_NAMES: [&str; {len(hooks)}] = [") - for h in hooks: - L.append(f' "{h.name}",') - L.append("];") - L.append("") return "\n".join(L) diff --git a/zygisk/src/generated/hook_ids.rs b/zygisk/src/generated/hook_ids.rs index e7d9751..d4a1968 100644 --- a/zygisk/src/generated/hook_ids.rs +++ b/zygisk/src/generated/hook_ids.rs @@ -62,6 +62,47 @@ pub enum Hook { ZygiskSetsockopt = 26, } +impl Hook { + /// This hook's bit in the control/stats wire mask. + pub const fn bit(self) -> u32 { + 1u32 << self as u32 + } + + /// Resolve a canonical config hook name. + pub fn from_name(name: &str) -> Option { + match name { + "fib_route_seq_show" => Some(Self::FibRouteSeqShow), + "ipv6_route_seq_show" => Some(Self::Ipv6RouteSeqShow), + "rtnl_fill_ifinfo" => Some(Self::RtnlFillIfinfo), + "inet_fill_ifaddr" => Some(Self::InetFillIfaddr), + "inet6_fill_ifaddr" => Some(Self::Inet6FillIfaddr), + "dev_ioctl" => Some(Self::DevIoctl), + "sock_ioctl" => Some(Self::SockIoctl), + "fib_dump_info" => Some(Self::FibDumpInfo), + "rt6_fill_node" => Some(Self::Rt6FillNode), + "fib_nl_fill_rule" => Some(Self::FibNlFillRule), + "lsposed_link_properties" => Some(Self::LsposedLinkProperties), + "lsposed_network_capabilities" => Some(Self::LsposedNetworkCapabilities), + "lsposed_network_info" => Some(Self::LsposedNetworkInfo), + "lsposed_network" => Some(Self::LsposedNetwork), + "lsposed_connectivity_result" => Some(Self::LsposedConnectivityResult), + "lsposed_connectivity_callback" => Some(Self::LsposedConnectivityCallback), + "lsposed_connectivity_network" => Some(Self::LsposedConnectivityNetwork), + "lsposed_package_visibility" => Some(Self::LsposedPackageVisibility), + "zygisk_ioctl" => Some(Self::ZygiskIoctl), + "zygisk_getifaddrs" => Some(Self::ZygiskGetifaddrs), + "zygisk_openat" => Some(Self::ZygiskOpenat), + "zygisk_recvmsg" => Some(Self::ZygiskRecvmsg), + "zygisk_recv" => Some(Self::ZygiskRecv), + "zygisk_recvfrom" => Some(Self::ZygiskRecvfrom), + "zygisk_recvfrom_chk" => Some(Self::ZygiskRecvfromChk), + "socket_bind_interface" => Some(Self::SocketBindInterface), + "zygisk_setsockopt" => Some(Self::ZygiskSetsockopt), + _ => None, + } + } +} + pub const HOOK_COUNT: u32 = 27; /// Hooks owned by each backend: apply `mask & own`. @@ -98,33 +139,3 @@ pub enum Backend { /// LSPosed Java-hook backend (system_server) Lsposed = 3, } - -pub const HOOK_NAMES: [&str; 27] = [ - "fib_route_seq_show", - "ipv6_route_seq_show", - "rtnl_fill_ifinfo", - "inet_fill_ifaddr", - "inet6_fill_ifaddr", - "dev_ioctl", - "sock_ioctl", - "fib_dump_info", - "rt6_fill_node", - "fib_nl_fill_rule", - "lsposed_link_properties", - "lsposed_network_capabilities", - "lsposed_network_info", - "lsposed_network", - "lsposed_connectivity_result", - "lsposed_connectivity_callback", - "lsposed_connectivity_network", - "lsposed_package_visibility", - "zygisk_ioctl", - "zygisk_getifaddrs", - "zygisk_openat", - "zygisk_recvmsg", - "zygisk_recv", - "zygisk_recvfrom", - "zygisk_recvfrom_chk", - "socket_bind_interface", - "zygisk_setsockopt", -];