Files
MeowOS-For-R3P2/patch_v2/meow_netmgrd/test_netmgrd.sh
T
worker-m49 370c290160 fix(bt): M49 蓝牙控制面重建 — paired-devices/a2dp list-pcms+info/run_cmd 四态+写后真态复查+扫描 ANSI 解析
真机 0.8.49 现场 /usr/bin/meow_netmgrd 是 P1(eb07415) 之前的旧件:`bt status` 无
connected= 字段、`bt connected|devices|a2dp` 连子命令都不存在(被当成写级拒绝)、
`bt paired` 落到 `bluetoothctl paired` → "Invalid command in menu main"。上游只有
NETMGR_UNREACH 才回退直连,INVAL/PERM 直接返回 → 三个列表恒空。

本单修掉「部署新件后依然存在」的三个真 bug + 一个解析缺陷:

1. `bluetoothctl paired` → `paired-devices`(bt_status 计数、bt paired 子命令两处);
   `devices` 只用于补名,不再当配对权威。
2. A2DP:v4.1.1 没有 `a2dp list`。改为 `list-pcms` 取端点 → 每端点 `info <path>`;
   MAC 只从 D-Bus 路径的 `dev_XX_..` 段解(真机路径带 `/a2dpsrc/source` 段,
   按「最后一段取 token」会拿到 "source")。info 查不到就跳过,不编造。
3. `run_cmd` 四态(成功有输出/成功无输出/非 0 退出/超时/起不来)+ stderr 有界抓取
   (≤256B):旧实现只回字节数、失败也返回 0 → on/off/connect/disconnect/pair 恒报成功。
   写操作改为下发后复查真态(hcitool con / bluetoothctl info Paired: / hciconfig
   power),超时或未收敛如实报 MEOW_ERR_NODEV。兼容外壳 run_cmd() 契约不变。
4. 扫描缓存:实时 `[NEW] Device` 行以 ANSI ESC 开头(真机 od -c 证据),旧解析器只认
   行首 → 实时行全丢。新增 bt_clean_ansi()/bt_scan_line_parse():剥 ESC/CSI/控制符、
   去 `[bluetooth]# ` 提示符、只认 [NEW] 与裸 Device、排除 [CHG]/[DEL];bt_scan_on 改用
   g_bluetoothctl(原来硬编码,桩对 scan 无效)。

解析器全部抽成纯函数并用真机原始字节做单测(test_bt_ctl.c 扩充:四态、ANSI 样本、
截断/空输出、list-pcms/info、hcitool con)。test_netmgrd.sh 桩改 list-pcms/info +
真机 ANSI 缓存样本;顺带修掉两处宿主可移植性问题(/tmp 不可写→$TMPDIR、桩脚本在
PATH=/bin:/usr/bin 下用了 cat/rm→改 shell 内建,test_wifi_connect 的 g_wpa_cli 悬垂
栈指针)。

按塔要求:本 commit 不含二进制、不改 fw_content.conf(netmgrd 产物由 M51 统一重编)。
build_netmgrd.sh 只做 ROOT/ZIGCC/OUT 环境变量化(旧硬编码 $HOME/Project/R3ProII),
M51 复用;time32 接入仍归 M43。
2026-11-11 18:56:06 +08:00

472 lines
22 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# test_netmgrd.sh — meow_netmgrd 宿主桩测试(假 sysfs/procfs 节点 + 桩后端命令,MEOW_* env 覆盖)。
# 用法:./test_netmgrd.sh [host二进制路径](默认 ./meow_netmgrd_host;可传 mipsel 二进制 + QEMU 前缀)
# QEMU="qemu-mipsel" ./test_netmgrd.sh ./meow_netmgrd # qemu 验证
set -u
cd "$(dirname "$0")"
BIN="${1:-./meow_netmgrd_host}"
Q="${QEMU:-}"
T=$(mktemp -d "${TMPDIR:-/tmp}/netmgrdtest.XXXXXX")
trap 'kill $DPID 2>/dev/null; rm -rf "$T"' EXIT
PASS=0; FAIL=0
ck() { # ck <名> <期望子串> <实际>
if echo "$3" | grep -qF "$2"; then PASS=$((PASS+1)); echo "PASS $1";
else FAIL=$((FAIL+1)); echo "FAIL $1: 期望含 [$2] 实际=[$3]"; fi
}
# ---- 搭假节点 ----
NET="$T/net"; RF="$T/rfkill"; BT="$T/bt"
mkdir -p "$NET/lo" "$NET/wlan0" "$RF/rfkill0" "$RF/rfkill1" "$BT/hci0"
echo up > "$NET/lo/operstate"
echo up > "$NET/wlan0/operstate"
echo wlan > "$RF/rfkill0/type"; echo 0 > "$RF/rfkill0/soft"; echo 0 > "$RF/rfkill0/hard"
echo bluetooth > "$RF/rfkill1/type"; echo 1 > "$RF/rfkill1/soft"; echo 0 > "$RF/rfkill1/hard"
echo 00:1A:7D:DA:71:13 > "$BT/hci0/address"
echo on > "$BT/hci0/power"
cat > "$T/netdev" <<'EOF'
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 1000 10 0 0 0 0 0 0 1000 10 0 0 0 0 0 0
wlan0: 5000000 5000 0 0 0 0 0 0 1000000 1000 0 0 0 0 0 0
EOF
cat > "$T/route" <<'EOF'
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
wlan0 005EC20A 6F3DC20A 0003 0 0 0 00FFFFFF 0 0 0
lo 0100007F 00000000 0001 0 0 0 000000FF 0 0 0
EOF
printf 'nameserver 223.5.5.5\nnameserver 10.78.193.1\n' > "$T/resolv"
echo meow > "$T/boot_mode"
# ---- 期2 观测层打桩:/proc/net/{tcp,udp}(IPv4 LE hex)+ 域名规则 conf + amsd/svcd 缺省 ----
cat > "$T/tcp" <<'EOF'
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 32C14E0A:5137 0905CD8C:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12345 1 0 0 10 0
1: 32C14E0A:4F32 32C34E0A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12346 1 0 0 10 0
2: 32C14E0A:0A00 00000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 67890 1 0 0 10 0
3: 32C14E0A:4F33 32C34E0A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 0 1 0 0 10 0
EOF
cat > "$T/udp" <<'EOF'
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0100007F:D301 0100007F:0035 07 00000000:00000000 00:00000000 00000000 0 0 99999 1 0 0 10 0
EOF
printf 'app:dev.player.ncm domain=-ads.example.com\nproc:dropbear domain=+x\n' > "$T/policy"
# 桩后端命令(popen stubbing)
cat > "$T/wpa_cli" <<'EOF'
#!/bin/sh
case "$1" in
status) echo "wpa_state=COMPLETED"; echo "ssid=MyWiFi"; echo "ip_address=10.78.193.50"; echo "signal=-45";;
scan) echo OK;;
scan_results) printf 'bssid / frequency / signal level / flags / ssid\nxx:xx / 2412 / -50 / WPA2 / TestAP\n';;
list_networks) printf 'network id / ssid / bssid / flags\n0\tMyWiFi\tany\t[CURRENT]\n';;
add_network) echo 1;;
set_network) echo OK;;
enable_network) echo OK;;
select_network) echo OK;;
disconnect) echo OK;;
esac
exit 0
EOF
cat > "$T/hciconfig" <<'EOF'
#!/bin/sh
echo "hci0: Type: Primary Bus: USB"
echo " BD Address: 00:1A:7D:DA:71:13 ACL MTU: 1021:8 SCO MTU: 64:1"
echo " UP RUNNING PSCAN"
EOF
cat > "$T/bluetoothctl" <<'EOF'
#!/bin/sh
case "${1:-}" in
paired|paired-devices)
echo "Device 00:1A:7D:DA:71:13 My Headset"
echo "Device 11:22:33:44:55:66 Speaker";;
devices)
echo "Device 00:1A:7D:DA:71:13 My Headset"
echo "Device 11:22:33:44:55:66 Speaker"
echo "Device 33:DE:2E:21:AD:30 Pudding";;
connect|disconnect|pair|remove)
echo "Controller 00:1A:7D:DA:71:13 Connected: yes";;
*) echo "Device 00:1A:7D:DA:71:13 My Headset"; echo "Device 11:22:33:44:55:66 Speaker";;
esac
exit 0
EOF
cat > "$T/hcitool" <<'EOF'
#!/bin/sh
echo " < ACL 33:DE:2E:21:AD:30 handle 12 state 1 lm MASTER"
echo " < ACL 11:22:33:44:55:66 handle 13 state 1 lm SLAVE"
exit 0
EOF
cat > "$T/bluealsa-cli" <<'EOF'
#!/bin/sh
# v4.1.1 实际命令表:list-services/list-pcms/status/info/codec/volume/...(没有 a2dp 子命令)
case "${1:-}" in
list-pcms)
# 真机路径带末端段,且结尾字面量不可依赖(首连实测是 /a2dpsrc/sink):
# /org/bluealsa/hci0/dev_XX/a2dpsrc/sink
echo "/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/sink";;
info)
# 真实 info 是按 PCM 路径查属性;桩固定回同一组字段
echo "BlueALSA PCM: ${2:-/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc}"
echo " Transport: A2DP-source"
echo " Codec: SBC"
echo " CodecConfiguration: 0x3f"
echo " Sampling: 44100 Hz"
echo " Channels: 2"
echo " Bits per Sample: 16"
echo " Running: true"
echo " State: RUNNING";;
esac
exit 0
EOF
cat > "$T/ip" <<'EOF'
#!/bin/sh
for a in "$@"; do
if [ "$a" = "wlan0" ]; then echo "2: wlan0 inet 10.78.193.50/24 brd 255.255.255.255 scope global wlan0"; fi
if [ "$a" = "lo" ]; then echo "1: lo inet 127.0.0.1/8 scope host lo"; fi
done
exit 0
EOF
for f in wpa_cli hciconfig bluetoothctl hcitool bluealsa-cli ip; do chmod +x "$T/$f"; done
mkdir -p "$T/logs" "$T/pid"
# 期2:DNS 禁用(宿主无 root 不绑 53),观测层经 MEOW_TCP/UDP 打桩;amsd/svcd 缺省(实体归因走 proc:comm→unknown)。
ENVS="MEOW_NET_DIR=$NET MEOW_NETDEV=$T/netdev MEOW_NET_ROUTE=$T/route MEOW_RESOLV=$T/resolv
MEOW_RFKILL_DIR=$RF MEOW_BT_DIR=$BT MEOW_BOOT_MODE=$T/boot_mode MEOW_BOOTTS_CACHE=$T/bootts
MEOW_IP=$T/ip MEOW_WPA_CLI=$T/wpa_cli MEOW_HCICONFIG=$T/hciconfig MEOW_BLUETOOTHCTL=$T/bluetoothctl
MEOW_HCITOOL=$T/hcitool MEOW_BLUEALSA_CLI=$T/bluealsa-cli MEOW_BT_SCAN_CACHE=$T/bt_scan.txt
MEOW_NET_SOCK=$T/net.sock MEOW_NET_POLL_SEC=1 MEOW_LOG_DIR=$T/logs
MEOW_TCP=$T/tcp MEOW_UDP=$T/udp MEOW_NET_POLICY=$T/policy
MEOW_AMS_SOCK=$T/no_ams MEOW_SVCD_SOCK=$T/no_svcd MEOW_DNS_DISABLE=1"
ln -sf "$(readlink -f "$BIN")" "$T/meow_net" # argv[0] 双形态 → CLI
# ---- T0: 守护起 ----
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon.log" 2>&1 &
DPID=$!
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
[ -S "$T/net.sock" ] || { echo "FAIL T0: daemon socket 未出现"; cat "$T/daemon.log"; exit 1; }
PASS=$((PASS+1)); echo "PASS T0 daemon up"
# ---- T1: status(读级,任意调用者)----
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
ck T1a "mode=meow src=daemon" "$OUT"
ck T1b "iface:lo kind=lo up=true" "$OUT"
ck T1c "iface:wlan0 kind=wifi up=true" "$OUT"
ck T1d "ip=10.78.193.50" "$OUT" # 若 iface 行 ip 解析成功(ip 桩已修)——实际由 wlan gw 行给,见下
ck T1e "gw=10.194.61.111" "$OUT"
ck T1f "wifi connected=1 state=COMPLETED ssid=MyWiFi" "$OUT"
ck T1g "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2" "$OUT"
ck T1h "rfkill:wlan soft=0 hard=0" "$OUT"
ck T1i "rfkill:bluetooth soft=1 hard=0" "$OUT"
# ---- T2: iface 单/全 ----
OUT=$(env $ENVS $Q "$T/meow_net" iface wlan0 2>&1)
ck T2a '"name":"wlan0"' "$OUT"
ck T2b '"kind":"wifi"' "$OUT"
ck T2c '"ip":"10.78.193.50"' "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" iface 2>&1)
ck T2d '"name":"lo"' "$OUT"
# ---- T3: wifi networks(读级)----
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
ck T3a "MyWiFi" "$OUT"
# ---- T4: wifi scan(写级;宿主 CLI=CALL_OTHER → 拒)----
OUT=$(env $ENVS $Q "$T/meow_net" wifi scan 2>&1)
ck T4a "permission denied" "$OUT"
# ---- T5: bt status / bt paired(读级)----
OUT=$(env $ENVS $Q "$T/meow_net" bt status 2>&1)
ck T5a "available=1 powered=1" "$OUT"
ck T5b "paired=2" "$OUT"
ck T5b2 "connected=2" "$OUT" # P1:内核 ACL 权威已连接数
OUT=$(env $ENVS $Q "$T/meow_net" bt paired 2>&1)
ck T5c "My Headset" "$OUT"
ck T5d "Speaker" "$OUT"
# ---- T5e: bt connected(P1:内核 ACL 权威,名经 devices 缓存兜底)----
OUT=$(env $ENVS $Q "$T/meow_net" bt connected 2>&1)
ck T5e "Device 33:DE:2E:21:AD:30 Pudding" "$OUT"
ck T5e2 "Device 11:22:33:44:55:66 Speaker" "$OUT"
# ---- T5f: bt a2dp(M49:bluealsa-cli list-pcms → info <path> → codec/rate/acquired/role)----
# v4.1.1 没有 `a2dp list` 子命令,桩只认 list-pcms/info;acquired 由 `Running: true` 推出。
OUT=$(env $ENVS $Q "$T/meow_net" bt a2dp 2>&1)
ck T5f "A2DP 33:DE:2E:21:AD:30" "$OUT"
ck T5f2 "codec=SBC" "$OUT"
ck T5f3 "rate=44100 Hz" "$OUT"
ck T5f4 "channels=2" "$OUT"
ck T5f5 "bits=16" "$OUT"
ck T5f6 "acquired=1" "$OUT"
ck T5f7 "role=src" "$OUT"
# ---- T5g: bt devices(P1:netmgrd 权威扫描缓存;M49:实时 [NEW] 行带 ANSI ESC 也要能解析)----
# 真机 /tmp/meow_bt_scan.txt 的实时行以 ESC[0;94m[bluetooth]ESC[0m# \rESC[K[001 ESC[0;92m 002NEW 001 ESC[0m 002] 开头
{ printf '%s\n' 'Waiting to connect to bluetoothd...'
printf '\033[0;94m[bluetooth]\033[0m# \r\033[K[\001\033[0;92m\002NEW\001\033[0m\002] Device 79:85:D6:7B:73:63 79-85-D6-7B-73-63\n'
printf '\033[0;94m[bluetooth]\033[0m# scan off\n'
printf '\033[0;94m[bluetooth]\033[0m# devices\n'
printf 'Device AA:BB:CC:DD:EE:FF Earbuds\n'
printf '[DEL] Device 00:11:22:33:44:55\n'
} > "$T/bt_scan.txt"
OUT=$(env $ENVS $Q "$T/meow_net" bt devices 2>&1)
ck T5g "Device 79:85:D6:7B:73:63 79-85-D6-7B-73-63" "$OUT" # 实时 ANSI 行(旧实现全丢)
ck T5g2 "Device AA:BB:CC:DD:EE:FF Earbuds" "$OUT" # 尾部 devices 块
if echo "$OUT" | grep -q "00:11:22:33:44:55"; then
FAIL=$((FAIL+1)); echo "FAIL T5g3 [DEL] 行被误当成附近设备: $OUT"
else PASS=$((PASS+1)); echo "PASS T5g3 [DEL] 不算附近设备"; fi
# ---- T5h: bt 写级(connect/pair 等)在宿主 CLI=CALL_OTHER → 拒(仅系统身份+MeowOS 放行,见 T6)----
OUT=$(env $ENVS $Q "$T/meow_net" bt connect 33:DE:2E:21:AD:30 2>&1); RC=$?
ck T5h "permission denied" "$OUT"
ck T5h2 "rc=1" "rc=$RC"
OUT=$(env $ENVS $Q "$T/meow_net" bt scan on 2>&1)
ck T5i "permission denied" "$OUT"
# ---- T6: HibyOS 只读降级(读可查,写拒)----
echo hiby > "$T/boot_mode"
sleep 2 # 等守护下一拍热切换
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
ck T6a "mode=readonly" "$OUT"
ck T6b "iface:wlan0" "$OUT" # 读侧仍可查
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
ck T6c "MyWiFi" "$OUT" # 读级子命令仍可用
OUT=$(env $ENVS $Q "$T/meow_net" wifi on 2>&1); RC=$?
ck T6d "permission denied" "$OUT"
echo meow > "$T/boot_mode"
sleep 2
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
ck T6e "mode=meow" "$OUT" # 热切换回全功能
# ---- T7: 守护日志(每次开机新文件;接口 add/写拒绝 必记)----
sleep 1
LOGS=$(ls "$T/logs"/net_*.log 2>/dev/null | head -1)
[ -n "$LOGS" ] && { PASS=$((PASS+1)); echo "PASS T7a log file: $(basename "$LOGS")"; } || { FAIL=$((FAIL+1)); echo "FAIL T7a 无日志文件"; }
[ -n "$LOGS" ] && { grep -q "iface add kind=wifi name=wlan0" "$LOGS" && { PASS=$((PASS+1)); echo "PASS T7b iface add 已记"; } || { FAIL=$((FAIL+1)); echo "FAIL T7b 未记 iface add"; }; }
# ---- T8: 守护停 → CLI help 本地可用;status 报守护不可达 ----
kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; DPID=""
sleep 0.5
OUT=$(env $ENVS $Q "$T/meow_net" help 2>&1)
ck T8a "usage: meow_net" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
ck T8b "守护不可达" "$OUT"
# ---- T9: 无参 → help;未知子命令 → usage ----
OUT=$(env $ENVS $Q "$T/meow_net" 2>&1)
ck T9a "usage: meow_net" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" bogus 2>&1); RC=$?
ck T9b "usage: meow_net" "$OUT"
# ---- 重启 daemon A(T8 已停)供期2 观测/连接表/日志测试 ----
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon2.log" 2>&1 &
DPID=$!
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
[ -S "$T/net.sock" ] || { echo "FAIL T10-pre: daemon 未重启"; cat "$T/daemon2.log"; exit 1; }
# ---- T10: 期2 conns(读级;观测层经 MEOW_TCP/UDP 桩 + amsd/svcd 缺省 → proc:unknown 归因)----
sleep 1 # 等守护普查一拍
OUT=$(env $ENVS $Q "$T/meow_net" conns 2>&1)
ck T10a "proc:unknown" "$OUT"
ck T10b "10.78.193.50:20791" "$OUT"
ck T10b2 "140.205.5.9:443" "$OUT"
ck T10c "ESTAB" "$OUT"
ck T10d "UDP" "$OUT"
ck T10e "connection" "$OUT"
# ---- T11: apps(实体聚合)----
OUT=$(env $ENVS $Q "$T/meow_net" apps 2>&1)
ck T11a "entity" "$OUT"
ck T11b "proc:unknown" "$OUT"
# ---- T12: dns status/stats(DNS 禁用 → 显示 disabled)----
OUT=$(env $ENVS $Q "$T/meow_net" dns status 2>&1)
ck T12a "proxy disabled" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" dns stats 2>&1)
ck T12b "proxy disabled" "$OUT"
# ---- T13: dns flush(写级;宿主 CLI=CALL_OTHER → 拒)----
OUT=$(env $ENVS $Q "$T/meow_net" dns flush 2>&1)
ck T13a "permission denied" "$OUT"
# ---- T13b: 端到端 JSON 合法性(STATUS_0xE0 / IFACE_0xE2 可被完整解析,括号配对/无尾逗号)----
JOUT=$(python3 - "$T/net.sock" <<'PYEOF'
import socket,struct,sys,json
s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.connect(sys.argv[1]); s.settimeout(2)
def recvn(n):
d=b""
while len(d)<n:
c=s.recv(n-len(d))
if not c: return d
d+=c
return d
def req(typ,payload=b""):
s.sendall(struct.pack('<IHHII',0x4D4F5701,1,typ,1,len(payload))+payload)
H=struct.unpack('<IHHII', recvn(16)); body=recvn(H[4])
code,jlen=struct.unpack('<IH',body[:6]); return body[8:8+jlen].decode('utf-8','replace')
ok=1
for nm,typ,pl in [("STATUS",0x00E0,b""),("IFACE_ALL",0x00E2,b"\x00"*32),("IFACE_WLAN",0x00E2,b"wlan0"+b"\x00"*27)]:
js=req(typ,pl)
try:
json.loads(js); sys.stdout.write("%s VALID\n" % nm)
except Exception as e:
sys.stdout.write("%s INVALID %s\n" % (nm,e))
PYEOF
)
ck T13b "STATUS VALID" "$JOUT"
ck T13c "IFACE_ALL VALID" "$JOUT"
ck T13d "IFACE_WLAN VALID" "$JOUT"
echo "$JOUT" | grep -q "INVALID" && { FAIL=$((FAIL+1)); echo "FAIL T13e 存在无效 JSON"; } || { PASS=$((PASS+1)); echo "PASS T13e no INVALID"; }
# ---- T15: apps 无原始端点(tcp 桩含 inode=0 未归因 → proc:unknown,绝不落成 IP:port entity)----
OUT=$(env $ENVS $Q "$T/meow_net" conns 2>&1)
ck T15a "10.78.195.50:443" "$OUT" # inode=0 的连接仍在表中
OUT=$(env $ENVS $Q "$T/meow_net" apps 2>&1)
ck T15b "proc:unknown" "$OUT"
echo "$OUT" | grep -qE '^[0-9][0-9.]*:' && { FAIL=$((FAIL+1)); echo "FAIL T15c apps 出现原始端点行"; } || { PASS=$((PASS+1)); echo "PASS T15c no raw endpoint row"; }
# ---- T16: policy(读级 show 任意调用者;写级仅系统身份 → 宿主 CLI=CALL_OTHER 拒)----
OUT=$(env $ENVS $Q "$T/meow_net" policy show 2>&1)
ck T16a "app:dev.player.ncm" "$OUT"
ck T16b "domain=" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" policy set app:dev.player.ncm allow 2>&1); RC=$?
ck T16c "permission denied" "$OUT"
[ $RC -eq 1 ] && { PASS=$((PASS+1)); echo "PASS T16d rc=1"; } || { FAIL=$((FAIL+1)); echo "FAIL T16d rc=$RC"; }
OUT=$(env $ENVS $Q "$T/meow_net" policy domain app:dev.player.ncm +x 2>&1)
ck T16e "permission denied" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_net" policy del app:dev.player.ncm 2>&1)
ck T16f "permission denied" "$OUT"
# ---- T17: SDK 自愿通道 end-to-end(meow_net_policy_check/report 对守护 A)----
SDK_BIN="$T/test_net_ipc"
if [ -n "$Q" ]; then
"$HOME/Project/R3ProII/.tools/zig-mipsel-cc" -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
else
cc -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
fi
if [ -x "$SDK_BIN" ]; then
OUT=$(env MEOW_LOG_APP=dev.player.ncm MEOW_NET_SOCK="$T/net.sock" $Q "$SDK_BIN" 2>&1)
ck T17a "CHECK_GOOD=0" "$OUT" # music.163.com 放行
ck T17b "CHECK_BLOCK=1" "$OUT" # ads.example.com 被策略 deny → block
ck T17c "REPORT_DONE" "$OUT"
else
FAIL=$((FAIL+1)); echo "FAIL T17 SDK test 构建失败"
fi
# ---- T18: SDK report → status JSON entity_bytes 回读 ----
JOUT2=$(python3 - "$T/net.sock" <<'PYEOF'
import socket,struct,sys,json
s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.connect(sys.argv[1]); s.settimeout(2)
def recvn(n):
d=b""
while len(d)<n:
c=s.recv(n-len(d))
if not c: return d
d+=c
return d
s.sendall(struct.pack('<IHHII',0x4D4F5701,1,0x00E0,1,0))
H=struct.unpack('<IHHII', recvn(16)); body=recvn(H[4])
code,jlen=struct.unpack('<IH',body[:6]); js=body[8:8+jlen].decode('utf-8','replace')
o=json.loads(js)
eb=o.get("entity_bytes") or []
hit=[e for e in eb if e["entity"]=="app:dev.player.ncm"]
sys.stdout.write("EB_HIT=%d RX=%d TX=%d\n" % (len(hit), hit[0]["rx"] if hit else -1, hit[0]["tx"] if hit else -1))
PYEOF
)
ck T18a "EB_HIT=1" "$JOUT2"
ck T18b "RX=12345" "$JOUT2"
ck T18c "TX=67890" "$JOUT2"
# ---- RSSI 回读:status wifi rssi 应 >=0(signal_poll 桩已给)----
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
ck T18d "rssi=-45" "$OUT"
# ---- T14: DNS 代理 wire 实测(守护 B:高端口 + 桩上游 + 全局 deny;测 deny/forward/cache)----
# 需要「非回环的本机地址」当上游:daemon 明确把 127.0.0.1 排除在上游之外(resolv_backup_apply
# 防环路,meow_netmgrd.c:2588)。宿主拿不到这样的地址(例如 Android/Termux 的 hostname 不支持
# -I)时这套 wire 测试无法成立 —— 显式 SKIP,而不是留一堆假失败。
HOSTIP=$(hostname -I 2>/dev/null | awk '{print $1}')
if [ -z "$HOSTIP" ] || [ "$HOSTIP" = "127.0.0.1" ]; then
echo "SKIP T14 DNS wire(本机无 hostname -I 或只有回环地址;daemon 拒绝把 127.0.0.1 当上游以防环路)"
else
cat > "$T/policy_dns" <<'EOF'
global domain=-ads.example.com dns.filter_aaaa=1
EOF
printf 'nameserver %s\n' "$HOSTIP" > "$T/resolv_dns"
cat > "$T/dns_test.py" <<'PYEOF'
import socket, struct, threading, sys, os
UP_IP = os.environ.get("T_UP_IP", "0.0.0.0")
up = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
up.bind((UP_IP, 5311)) # 0.0.0.0 通配,绑到宿主自身 IP
up.settimeout(6)
def serve():
try:
while True:
d,a = up.recvfrom(1536)
off=12
while d[off]!=0: off += 1+d[off]
off += 1+4
qsec = d[12:off]
resp = d[:2] + b'\x81\x80' + b'\x00\x01\x00\x01\x00\x00\x00\x00' + qsec
resp += b'\xc0\x0c' + b'\x00\x01\x00\x01' + struct.pack('>I',60) + b'\x00\x04' + socket.inet_aton('11.22.33.44')
up.sendto(resp, a)
except Exception:
pass
threading.Thread(target=serve, daemon=True).start()
def query(name, qtype=1):
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(3)
hdr=b'\x12\x34'+b'\x01\x00'+b'\x00\x01\x00\x00\x00\x00\x00\x00'
q=b''.join(bytes([len(l)])+l.encode() for l in name.split('.'))+b'\x00'+struct.pack('>HH',qtype,1)
s.sendto(hdr+q, ("127.0.0.1", 5300))
r,_=s.recvfrom(1536); rco=r[3]&0xf; an=struct.unpack('>H',r[6:8])[0]
return rco,an
try:
d1,a1 = query("ads.example.com") # deny → NXDOMAIN (rcode 3)
d2,a2 = query("music.163.com") # allow → forward → A 11.22.33.44
da,aa = query("music.163.com", 28) # AAAA + dns.filter_aaaa=1 → 空应答(回落 A)
sys.stdout.write("DENY_RCODE=%d\nALLOW_AN=%d\nAAAA_RCODE=%d AAAA_AN=%d\n" % (d1, a2, da, aa))
except Exception as e:
sys.stdout.write("ERR=%s\n" % e)
PYEOF
# 追加式覆写(env 左→右,后者胜出):DNS 启用、换独立 socket/resolv/policy、上游端口。
DNSENV="$ENVS MEOW_DNS_DISABLE=0 MEOW_NET_SOCK=$T/dns.sock MEOW_RESOLV=$T/resolv_dns
MEOW_NET_POLICY=$T/policy_dns MEOW_DNS_PORT=5300 MEOW_DNS_UPSTREAM_PORT=5311"
ln -sf "$(readlink -f "$BIN")" "$T/dns_netmgrd"
env $DNSENV $Q "$BIN" --sock "$T/dns.sock" >"$T/dnsd.log" 2>&1 &
D2PID=$!
for i in $(seq 1 30); do [ -S "$T/dns.sock" ] && break; sleep 0.2; done
sleep 1
OUT=$(env $DNSENV $Q "$T/meow_net" dns status 2>&1)
ck T14a "enabled=1 upstream=$HOSTIP" "$OUT"
WIRE=$(python3 "$T/dns_test.py" 2>&1)
ck T14b "DENY_RCODE=3" "$WIRE"
ck T14c "ALLOW_AN=1" "$WIRE"
ck T14c2 "AAAA_RCODE=0" "$WIRE" # 期4 AAAA 过滤:rcode 0
ck T14c3 "AAAA_AN=0" "$WIRE" # 且 0 条应答(回落 A)
OUT=$(env $DNSENV $Q "$T/meow_net" dns stats 2>&1)
ck T14d "q=3" "$OUT" # deny + allow + AAAA = 3 查询
ck T14e "deny=1" "$OUT"
kill $D2PID 2>/dev/null; wait $D2PID 2>/dev/null
# 恢复守护 A 的 MEOW_RESOLV(其未被 DNS 触碰,此处确认)
grep -q "nameserver 223.5.5.5" "$T/resolv" && { PASS=$((PASS+1)); echo "PASS T14f daemonA resolv untouched"; } || { FAIL=$((FAIL+1)); echo "FAIL T14f daemonA resolv touched"; }
fi # T14 上游能力守卫
# ---- T19: WiFi connect 状态机/持久化/隐藏网络/中文 SSID(宿主桩直驱 wifi_connect_ex)----
# daemon IPC 写级需系统身份(SO_PEERCRED /proc/exe),宿主 CLI=CALL_OTHER 必拒;故用 #include
# meow_netmgrd.c 的独立宿主二进制直驱 static wifi_connect_ex,桩 wpa_cli 由二进制自建。
if [ -z "$Q" ]; then
WCON="$T/test_wifi_connect"
if cc -Os -Wall -Wextra -Wno-format-truncation -I../meow_launcherd \
test_wifi_connect.c -o "$WCON" >/dev/null 2>&1; then
if "$WCON" >"$T/wcon.out" 2>/dev/null && grep -q "FAIL=0" "$T/wcon.out"; then
PASS=$((PASS+1)); echo "PASS T19 wifi-connect host (12 asserts, FAIL=0)"
else
FAIL=$((FAIL+1)); echo "FAIL T19 wifi-connect host"; cat "$T/wcon.out"
fi
else
FAIL=$((FAIL+1)); echo "FAIL T19 wifi-connect host build"
fi
else
echo "SKIP T19 wifi-connect(qemu 下 fork/exec 宿主 /bin/sh 不便,宿主已覆盖)"
fi
echo "=============================="
echo "PASS=$PASS FAIL=$FAIL"
[ $FAIL -eq 0 ]