/* test_netmgrd_p3.c — meow_netmgrd 期3 管控层 纯逻辑宿主自测(本程序非生产代码)。 * * 通过 #include 生产源,同一 TU 直接访问 FILE-STATIC:policy_cmd / rules_load / policy_persist_entity / * policy_net_blocked / dns_query_domain_denied / is_self_lock / policy_find / g_policy_log_level。 * 覆盖:policy set net(per-entity 持久化读改写)、防自锁拒绝 + 加载校验剔除、domain 白/黑、 * net=block → DNS 层全量 deny、del、global log_level、RMW 保留他行。真实 IPC 鉴权(CALL_OTHER 拒) * 由 test_netmgrd.sh 覆盖。 * * 用法:cc -Wall -Wextra -I../meow_launcherd test_netmgrd_p3.c -o test_netmgrd_p3 && ./test_netmgrd_p3 */ #define main meow_netmgrd_prod_main_unused #include "meow_netmgrd.c" #undef main #include #include #include #include #include static int g_fail = 0; #define CHECK(cond) do { if (!(cond)) { printf(" FAIL: %s (line %d)\n", #cond, __LINE__); g_fail++; } \ else { printf(" ok : %s\n", #cond); } } while (0) static char rd[512]; static const char *readfile(const char *p) { FILE *f = fopen(p, "r"); if (!f) { rd[0] = 0; return rd; } size_t n = fread(rd, 1, sizeof rd - 1, f); rd[n] = 0; fclose(f); return rd; } int main(void) { static const char *T = "/tmp/nmg_p3"; char policy[192]; mkdir(T, 0755); snprintf(policy, sizeof policy, "%s/policy", T); g_net_policy[0] = 0; snprintf(g_net_policy, sizeof g_net_policy, "%s", policy); FILE *f = fopen(policy, "w"); /* 先建空 policy 文件 */ if (f) { fputs("# boot\n", f); fclose(f); } char out[512]; int rc; printf("== policy_cmd SET_NET(per-entity 持久化)==\n"); rules_load(); CHECK(g_rule_n == 0); rc = policy_cmd(NET_POLICY_SET_NET, "proc:thttpd", "block", out, sizeof out); CHECK(rc == MEOW_OK); CHECK(strstr(readfile(policy), "proc:thttpd net=block") != NULL); CHECK(policy_net_blocked("proc:thttpd") == 1); CHECK(policy_net_blocked("proc:unknown") == 0); /* 未显式规则 → 默认 allow */ printf("== 防自锁:拒绝 block + 加载校验剔除 ==\n"); rc = policy_cmd(NET_POLICY_SET_NET, "proc:dropbear", "block", out, sizeof out); CHECK(rc == MEOW_ERR_PERM); CHECK(strstr(out, "防自锁") != NULL); CHECK(policy_net_blocked("proc:dropbear") == 0); CHECK(policy_net_blocked("svc:meow_netmgrd") == 0); /* 手工改坏文件塞入 dropbear net=block → 加载校验剔除 */ f = fopen(policy, "w"); if (f) { fputs("proc:dropbear net=block\nproc:thttpd net=block\n", f); fclose(f); } rules_load(); CHECK(policy_net_blocked("proc:dropbear") == 0); CHECK(policy_net_blocked("proc:thttpd") == 1); printf("== policy SET_DOMAIN(白/黑;allow 覆盖)+ net=block → DNS 全量 deny ==\n"); rc = policy_cmd(NET_POLICY_SET_DOMAIN, "proc:web", "+music.163.com", out, sizeof out); CHECK(rc == MEOW_OK); rc = policy_cmd(NET_POLICY_SET_DOMAIN, "proc:web", "-ads.example.com", out, sizeof out); CHECK(rc == MEOW_OK); CHECK(strstr(readfile(policy), "proc:web domain=+music.163.com,-ads.example.com") != NULL); CHECK(dns_query_domain_denied("proc:web", "music.163.com") == 0); /* 白名单允许 */ CHECK(dns_query_domain_denied("proc:web", "p.music.163.com") == 0); /* 子域也允许 */ CHECK(dns_query_domain_denied("proc:web", "ads.example.com") == 1); /* 黑名单拒绝 */ CHECK(dns_query_domain_denied("proc:web", "other.com") == 0); /* net=block 的实体 → DNS 全量 deny(即使白名单);且已有 domain 规则的实体被 block 后同样全 deny */ rc = policy_cmd(NET_POLICY_SET_NET, "proc:web", "block", out, sizeof out); CHECK(rc == MEOW_OK); CHECK(dns_query_domain_denied("proc:web", "music.163.com") == 1); /* block 覆盖白名单 */ rc = policy_cmd(NET_POLICY_SET_NET, "app:dev.player.ncm", "block", out, sizeof out); CHECK(rc == MEOW_OK); CHECK(dns_query_domain_denied("app:dev.player.ncm", "music.163.com") == 1); printf("== del 移除实体(RMW 保留其它行)==\n"); rc = policy_cmd(NET_POLICY_DEL, "proc:thttpd", NULL, out, sizeof out); CHECK(rc == MEOW_OK); CHECK(strstr(readfile(policy), "proc:thttpd") == NULL); CHECK(strstr(readfile(policy), "app:dev.player.ncm net=block") != NULL); /* 其它实体行保留 */ /* RMW 保留注释/无关行(写回前 seed 应有 # 头) */ f = fopen(policy, "w"); if (f) { fputs("# commentary\nproc:thttpd net=block\napp:dev.player.ncm domain=-tracker.net\n", f); fclose(f); } rules_load(); rc = policy_cmd(NET_POLICY_SET_NET, "app:dev.player.ncm", "block", out, sizeof out); CHECK(rc == MEOW_OK); CHECK(strstr(readfile(policy), "# commentary") != NULL); CHECK(strstr(readfile(policy), "app:dev.player.ncm domain=-tracker.net") != NULL); /* domain 行保留 */ CHECK(strstr(readfile(policy), "app:dev.player.ncm net=block") != NULL); printf("== global log_level ==\n"); f = fopen(policy, "w"); if (f) { fputs("global log_level=debug\n", f); fclose(f); } rules_load(); CHECK(strcmp(g_policy_log_level, "debug") == 0); printf("== del 到空文件(write 0 字节场景)==\n"); f = fopen(policy, "w"); if (f) { fputs("proc:thttpd net=block\n", f); fclose(f); } rules_load(); rc = policy_cmd(NET_POLICY_DEL, "proc:thttpd", NULL, out, sizeof out); CHECK(rc == MEOW_OK); CHECK(strstr(readfile(policy), "proc:thttpd") == NULL); /* 文件已无该实体(可为空) */ CHECK(g_rule_n == 0); if (g_fail) { printf("\nNETMGR P3 TEST: %d FAIL\n", g_fail); return 1; } printf("\nNETMGR P3 TEST: ALL PASS\n"); return 0; }