# Kill-switch 故障注入清单 — M5 验收

任务 ID：tsk_xAQhC1xuCd8x  
执行原则：**每项注入期间持续循环 `curl --max-time 2 ifconfig.me`，断言要么超时要么走节点 IP，绝无真实 IP 泄露。**

---

## 通用准备

```bash
# 注入测试辅助脚本（三端通用，在 shell 里循环执行）
while true; do
  result=$(curl --max-time 2 -s ifconfig.me 2>&1)
  echo "[$(date +%T)] $result"
  sleep 1
done
```

预期：
- 正常连接时：输出节点出口 IP（非本机真实 IP）
- Kill-switch 生效时：`curl: (28) Operation timed out` 或连接拒绝
- **绝不允许输出本机真实 IP**

WebRTC 泄露复验（浏览器内手动执行）：
- 访问 https://browserleaks.com/webrtc 观察"Local IP"和"Public IP"字段
- Kill-switch 激活时，Public IP 应为空或节点 IP

DNS 泄露复验：
- 访问 https://dnsleaktest.com → Run Extended Test
- Kill-switch 激活时，所有 DNS 服务器应为节点侧 IP（非国内 ISP DNS）

---

## 平台一：桌面端 macOS（11D 已完成）

实现：sing-box TUN `strict_route: true` + `DesktopVpnBridge.setKillSwitch()`

### 1. 内核进程被 kill -9

```bash
# 找到 sing-box 进程 PID
pgrep -f sing-box
# 强制终止
kill -9 <PID>
```

- [ ] curl 循环输出变为超时（不泄露真实 IP）
- [ ] Flutter UI 显示 `error` 状态（不崩溃）
- [ ] 约 1s 后触发自动重连（状态切换为 `connecting`）
- [ ] 退避序列正确：第1次1s，第2次2s，第3次4s…上限30s
- [ ] 重连成功后 UI 恢复 `on` 状态
- [ ] 重连后 curl 再次输出节点 IP

### 2. 物理断网（关闭 Wi-Fi / 拔以太网）

- [ ] curl 循环输出变为超时
- [ ] UI 状态切换为 `error` 或保持 `on`（取决于 sing-box keepalive 行为）
- [ ] 恢复网络后，自动重连成功
- [ ] 恢复后 curl 再次输出节点 IP，无真实 IP 泄露

### 3. Kill-switch 关闭态下断网

```bash
# 先关闭 Kill-switch（strict_route=false）
# 再断网
```

- [ ] curl 超时（无网络时正常超时，但不应泄露路由绕过节点的 IP）
- [ ] 注意区分：关闭 Kill-switch = 断网后流量不被隧道保护，可能走直连
- [ ] 关闭 Kill-switch 时，断网后 curl 应超时或走直连（记录观察结果，不强制要求节点 IP）

### 4. Kill-switch ON ↔ OFF 切换验证

```bash
# 连接后调用 setKillSwitch(on: true)
# 查看 config 的 strict_route 字段
```

- [ ] `setKillSwitch(on: true)` 触发内核重载，重载期间 curl 短暂超时
- [ ] 重载完成后 curl 再次输出节点 IP
- [ ] `setKillSwitch(on: false)` 同样触发重载，重载完成后 curl 输出节点 IP
- [ ] `getActiveOutbound()` 在切换前后返回正确 tag

### 5. 节点端主动断连（服务器侧 reset 连接）

```bash
# 在服务端执行（模拟节点故障）：
# iptables -I INPUT -p tcp --dport 443 -j REJECT
```

- [ ] sing-box 内核检测到连接断开后推送 `error`
- [ ] 自动重连定时器触发，尝试重连
- [ ] 恢复后（服务端恢复策略），curl 再次输出节点 IP

### 已完成打勾记录

执行日期：___________  
执行人：___________

- [x] 测试环境：macOS Ventura 13.x，Apple Silicon M2
- [ ] 1. kill -9 内核进程
- [ ] 2. 物理断网
- [ ] 3. Kill-switch OFF + 断网
- [ ] 4. Kill-switch ON/OFF 切换
- [ ] 5. 节点端主动断连
- [ ] WebRTC 复验（browserleaks.com）
- [ ] DNS 泄露复验（dnsleaktest.com）

---

## 平台二：iOS（11E M3 已完成；M4/M5 欠账）

**当前状态：`selectOutbound` / `getActiveOutbound` / `setKillSwitch` 仍为 stub（TODO 11F），以下清单为下阶段完成后验收。**

实现路径（欠账）：
- `AppDelegate.swift` `selectOutbound` → `sendProviderMessage` → `PacketTunnelProvider.handleAppMessage`
- `setKillSwitch` → 通过 `NEPacketTunnelNetworkSettings.includedRoutes` / `excludedRoutes` 控制泄露路由

### 1. PacketTunnel Extension 被系统终止

- [ ] iOS 杀后台，或 Xcode Debug > Simulate Memory Warning
- [ ] curl 循环（在终端 SSH 到 iOS 或通过 TestFlight 辅助 App 执行）结果为超时
- [ ] 主 App UI 显示 `error` 状态
- [ ] 自动重连（NETunnelProviderSession 重启）触发
- [ ] 恢复后 curl 输出节点 IP

### 2. Wi-Fi ↔ 蜂窝切换

- [ ] 切换期间 curl 超时（不泄露真实 IP）
- [ ] NEPathMonitor 触发重连
- [ ] 切换完成后 curl 输出节点 IP

### 3. Kill-switch ON（includedRoutes 覆盖所有流量）

- [ ] `setKillSwitch(on: true)` 触发 NetworkExtension 重配置
- [ ] 断网后 curl 超时（不走直连）
- [ ] 恢复后 curl 输出节点 IP

### 4. WebRTC / DNS 泄露复验

- [ ] Safari 访问 browserleaks.com/webrtc — Public IP 为节点 IP 或空
- [ ] dnsleaktest.com — 无国内 ISP DNS

### 已完成打勾记录

执行日期：___________  
执行人：___________  
**欠账说明：M4/M5 iOS stub 尚未实现（需先完成 11F），本清单留待后续补全。**

---

## 平台三：Android（11F M2 已完成；M4/M5 欠账）

**当前状态：`selectOutbound` / `getActiveOutbound` / `setKillSwitch` 仍为 stub（TODO 11G），以下清单为下阶段完成后验收。**

实现路径（欠账）：
- `MainActivity.kt` `selectOutbound` → libbox CommandClient selector 命令
- `setKillSwitch` → `VpnService.Builder.addDisallowedApplication` 或 `BlockVpnRoute` 路由覆盖

### 1. PangolinVpnService 被 kill -9

```bash
# adb shell
adb shell am kill com.pangolin.pangolin_vpn
```

- [ ] curl 超时（Android 侧通过 adb shell curl 执行）
- [ ] Flutter UI 收到 VpnEventBus `error` 推送，UI 不崩
- [ ] 自动重连触发（PangolinVpnService 重启）
- [ ] 恢复后 curl 输出节点 IP

### 2. Wi-Fi ↔ 蜂窝切换

- [ ] 切换期间 curl 超时
- [ ] ConnectivityManager 触发重连
- [ ] 恢复后输出节点 IP

### 3. Kill-switch ON（DISALLOW_APPLICATION 覆盖）

- [ ] `setKillSwitch(on: true)` 触发 VpnService 重配置
- [ ] 断开 Wi-Fi 后 curl 超时（不走直连）
- [ ] 恢复 Wi-Fi 后 curl 输出节点 IP

### 4. 电池优化 / Doze 下后台存活

- [ ] 强制进入 Doze 模式：`adb shell dumpsys deviceidle force-idle`
- [ ] 10 分钟内确认 VPN 服务仍在运行（`adb shell dumpsys vpn`）
- [ ] 退出 Doze：`adb shell dumpsys deviceidle unforce`

### 5. WebRTC / DNS 泄露复验

- [ ] Chrome 访问 browserleaks.com/webrtc
- [ ] dnsleaktest.com

### 已完成打勾记录

执行日期：___________  
执行人：___________  
**欠账说明：M4/M5 Android stub 尚未实现（需先完成 11G），本清单留待后续补全。**

---

## 欠账汇总（供后续任务参考）

| 平台 | Kill-switch 实现 | URLTest/selectOutbound | 说明 |
|------|-----------------|----------------------|------|
| 桌面 macOS | ✅ 已实现（strict_route + 重载） | ✅ Clash API | 本任务完成 |
| iOS | ❌ 欠账 | ❌ 欠账 | TODO(11F): sendProviderMessage + includedRoutes |
| Android | ❌ 欠账 | ❌ 欠账 | TODO(11G): libbox CommandClient + VpnService 路由 |
