# tcptun

tcptun 是一个使用 Go 编写的、配置驱动的多 inbound / 多 outbound 代理运行时。一个进程读取一份严格 JSON，编译出口图和路由规则，准备全部入口，然后统一启动。

[Reverse Subnet / 家庭网络访问](docs/reverse-subnet.zh-CN.md)：家庭连接器主动连接 Edge，
通过双端 network、CIDR 和目标端口 ACL 访问家庭网段的 IPv4/IPv6 TCP/UDP 服务，
无需家庭端口转发。

[Reverse Subnet Direct QUIC](docs/reverse-subnet-p2p.zh-CN.md)：可选保留 Edge 授权与 per-flow permit，把成功连接的 application payload 改为 Remote 到 Home 的 authenticated QUIC，并自动回落 relay。

Go module `pkg.tcptun.com/net` 同时提供可嵌入的网络库。新的编程式集成应组合
`flow`、`endpoint`、`route`、`outbound`、`discovery`、`transport` 和 `engine`
等公共包，而不是从 JSON 配置开始。详见 [网络库架构](docs/library-architecture.md)。
流连接直接使用 `net.Conn`；outbound dialer 和 packet session 可分别适配标准
`Dial`/`DialContext` 与 `net.PacketConn` 接口。
公共 `engine.PacketForwarder` 提供有界 UDP 路由，并支持 Direct 与 SOCKS5 UDP
ASSOCIATE packet outbound。

## 启动

```sh
tcptun --config config.json
tcptun -c config.json
```

tcptun 只有在通过 `--config/-c` 显式指定路径时才读取配置文件，不会在当前目录自动查找默认配置。

```sh
tcptun
```

未传入 `--config` 时，tcptun 会在内存中构造等价的正式 `FileConfig`，先预留 `127.0.0.1:1080`，再扫描本机私有 IPv4 局域网中第一个可通过 1080 端口访问的 SOCKS5 服务，成功后启动 mixed SOCKS/HTTP 代理。先预留监听可立即报告端口冲突，但发现成功前不会接受流量。扫描会执行真实 SOCKS5 握手，并在首次成功后取消剩余探测；没有发现任何上游时启动失败并释放监听。使用 `tcptun --retry` 可在保留监听的同时无限重试。`--retry` 仅用于该自动模式，不能与 `--config` 同时使用。

automatic LAN 模式可以通过命令行凭据认证发现到的 SOCKS5 upstream：

```sh
tcptun --username alice --password 'secret'
tcptun --retry --username alice --password 'secret'
```

同一组值也可以来自环境变量：

```sh
TCPTUN_USERNAME=alice \
TCPTUN_PASSWORD='secret' \
tcptun
```

`--username` 独立覆盖 `TCPTUN_USERNAME`，`--password` 独立覆盖 `TCPTUN_PASSWORD`；空环境变量按未设置处理。这些凭据只用于 automatic `lan-socks5` outbound，不会给本地 `127.0.0.1:1080` mixed proxy 增加认证，环境变量也不会覆盖 `--config` 加载的凭据。显式 `--username` 或 `--password` 不能与 `--config` 组合。生成的 outbound 不设置 `auth_mode`，因此带凭据的 SOCKS5 outbound 仍使用现有的 secure authentication 默认值。

直接传入 `--password` 可能让 secret 出现在 shell history 或进程列表中。脚本、容器和服务管理器中更推荐使用 `TCPTUN_PASSWORD`；环境变量可以减少命令行暴露，但并不是绝对安全的 secret storage。

根命令提供 `--config/-c`、`--verbose/-v`，以及仅控制无配置自动模式的 `--retry`、`--username` 和 `--password`。其他监听、认证、协议、transport、TLS、REALITY、mux 和 route 选项都写入 JSON。

## 配置模型

```json
{
  "log": { "level": "info" },
  "inbounds": [
    {
      "tag": "local",
      "type": "mixed",
      "address": ["127.0.0.1:1080"],
      "network": ["tcp", "udp"]
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "type": "native",
      "address": ["proxy.example.com:9443"],
      "token": "change-me",
      "transport": { "type": "raw" },
      "mux": { "enabled": true }
    }
  ],
  "route": { "default_outbound": "proxy", "rules": [] },
  "dns": {
    "servers": ["1.1.1.1", "[2606:4700:4700::1111]:53"],
    "strategy": "prefer_ipv4",
    "outbound": "proxy",
    "fake_ip": {
      "enabled": true,
      "ipv4_range": "198.18.0.0/15",
      "ipv6_range": "fc00::/18",
      "capacity": 65536,
      "ttl": "10m"
    }
  },
  "resources": {
    "mux_receive_buffer_budget": 268435456,
    "resumable_buffer_budget": 1073741824
  }
}
```

`FileConfig` 使用拒绝未知字段的 decoder，并与编译后的 `RuntimeConfig` 分离。inbound/outbound tag 必须唯一；入口引用、路由引用、链式出口、TCP/UDP capability、协议认证、transport、TLS 和 REALITY 都在监听端口前校验。

平台注入的 TUN inbound 使用 gVisor netstack 处理 IPv4/IPv6 TCP 和 UDP，并直接进入
现有 compiled router，不会绕过本地 mixed/SOCKS listener。TUN 截获 TCP 或 UDP 53 端口的
DNS 时，`dns.servers` 可替换客户端选择的 resolver，`dns.outbound` 可把 DNS 固定到一个
具备 TCP/UDP 能力的出口；固定出口不可用时会 fail closed，不会泄漏到其他路由。启用
`dns.fake_ip.enabled` 后，A/AAAA 查询使用有界内存 fake-IP 映射，后续连接在路由前还原
为原域名；其他 DNS 类型继续通过配置的 packet 或 stream outbound 转发。fake-IP 范围、
容量和 TTL 会在启动时校验，映射按 runtime 隔离并在停止时清除。

`resources.mux_receive_buffer_budget` 限制一个 Runtime 内所有 Native TCP mux
carrier 尚未被应用消费的 payload 总量，默认 256 MiB，可配置 1–256 MiB；进程级
256 MiB 硬上限仍会约束多个 Runtime 的总和。`Runtime.Stats` 同时暴露上限、当前值、
峰值和拒绝次数。Android 等内存敏感嵌入场景应按宿主内存等级显式降低该预算。

嵌入方可以使用 `tun.NewWithOptions` 限制 packet queue、待处理和
活跃 TCP flow、活跃 UDP flow 以及 UDP 空闲生命周期；`tun.New` 保持相同的正式默认值。
`tun.Inbound.Stats` 与 `Runtime.Stats` 无需解析日志即可读取平台 flow、准入结果、
packet/byte 总量和拒绝包分类。格式错误的 IP envelope 与 TCP/UDP 之外的 transport
会被计数并丢弃，不会终止 TUN，也不会静默进入 direct 路由。

不提供 Unix TUN 文件描述符的平台可以实现 `tun.PacketDevice`，并使用
`tun.NewPacketDevice` 或 `tun.NewPacketDeviceWithOptions`。读写都必须恰好携带一个完整
IP packet，`Close` 必须解除阻塞 I/O。Linux 仍使用 fd-backed gVisor fast path；Windows
上的 `tun.New` 返回 `tun.ErrUnsupported`，可使用 Wintun packet-device adapter。匹配架构
的 `wintun.dll` 必须能被官方绑定加载；嵌入方负责配置接口地址、路由和 DNS。Wintun ring
capacity 限制为 128 KiB–64 MiB 的 2 的幂，默认 8 MiB。

`inbound.address` 和 `outbound.address` 都严格使用字符串数组。一个 outbound 的多个地址必须指向共享凭据和协议参数的同一逻辑服务，不代表负载均衡；首次连接以 150ms 错峰竞争完整 transport 握手（包括 TLS、REALITY、HTTP 和 QUIC 认证），选出首选地址后会保持硬粘滞，只有首选明确失败才重新竞争并提升备用地址。首选切换时，已有 mux 流可以在旧地址上自然结束，但新流只进入新首选地址。独立服务节点仍应分别配置并交给 `balance`。

支持的 inbound：`mixed`、`socks5`、`native`。

支持的 outbound：`direct`、`balance`、`blackhole`、`socks5`、`mixed`、`native`。

### Mixed 与 SOCKS5 代理认证

所有需要认证的 inbound 都接受有界的 `users` 数组（最多 256 项），各协议只允许以下字段：

| inbound | `users[]` 允许字段 |
| --- | --- |
| `mixed` | `username`, `password` |
| `socks5` | `username`, `password` |
| `native` | `id`；既有 REALITY/Vision 配置可选 `flow` |

例如，一个 mixed listener 可让两个账号共同用于 SOCKS5、HTTP、HTTPS `CONNECT` 和
SOCKS5 UDP：

```json
{
  "tag": "local",
  "type": "mixed",
  "address": ["127.0.0.1:1080"],
  "users": [
    { "username": "alice", "password": "secret-a" },
    { "username": "bob", "password": "secret-b" }
  ]
}
```

顶层 `username`/`password` 继续兼容 mixed/socks5 单账号旧配置，但不能与 `users`
同时出现。没有 legacy 凭据时，空 `users` 数组仍表示 no-auth；tunnel inbound 至少需要
一个 user。outbound 仍代表单个客户端身份，不改为 `users`。

`socks5` inbound 的 users 会保护 SOCKS5 TCP 与 UDP 会话；`mixed` inbound 的同一组
users 会同时保护 SOCKS5、普通 HTTP proxy 请求和 HTTPS `CONNECT`。HTTP 客户端使用标准
`Proxy-Authorization: Basic ...`；缺失、格式错误、重复或不匹配时返回 `407 Proxy
Authentication Required`。代理会在路由前消费该 header，不会把它发送给 origin，
也不会放进 raw-mixed HTTP handoff。`CONNECT` 始终是 opaque 字节隧道，不做 TLS
MITM。

配置凭据的 `socks5` 或 `mixed` outbound 支持 `auth_mode` 策略：

- `secure` 精确发送 `[05 01 80]`，只允许 tcptun 私有 method `0x80`；不会声明或发送
  RFC1929 凭据，server 选择其它 method 或 proof 失败都会关闭连接。该模式能防止通过
  method selection 降级窃取密码，也是配置凭据但省略 `auth_mode` 时的默认值。
- `standard` 精确发送 `[05 01 02]`，用于显式兼容第三方 RFC1929 server。username 和
  password 在 SOCKS5 wire 上可被恢复，机密性依赖外层 TLS、VPN、native tunnel 或其它
  可信安全 transport；该模式不提供 tcptun secure auth。
- `auto` 发送 `[05 02 80 02]` 并接受任一 method。它只是可降级的兼容模式：主动攻击者
  可以强制使用 RFC1929。只应在可信外层加密存在或迁移旧部署时使用，不能视为安全模式。

outbound 未配置凭据时仍只发送 `[05 01 00]`，此时设置 `auth_mode` 会被拒绝。inbound
兼容性保持不变：新版 tcptun inbound 优先选择 `0x80`，但只声明 `0x02` 的 curl、Clash、
浏览器和系统 SOCKS5 客户端仍使用 RFC1929；普通 HTTP proxy 客户端继续使用 Basic，
未配置凭据时 HTTP 也保持无需认证。

Secure Auth V2 保持私有 method `0x80`，并在以下有界帧中使用 version byte `0x02`：

```text
client -> server: version | username-length | username | client-nonce[32]
server -> client: version | status | salt[16] | server-nonce[32]
client -> server: version | client-proof[32]
server -> client: version | status | server-proof[32]
```

V2 使用 HKDF-SHA256 派生 32-byte 认证 key：输入密钥材料为配置的 `password` bytes，
salt 为 16-byte server credential salt，info 为
`tcptun-socks5-secure-auth-v2/auth-key`。区分 client/server role 的 HMAC-SHA256
proof 会绑定 `tcptun-socks5-secure-auth-v2`、version、带长度的 username、两个每连接
新生成的 32-byte nonce 和 salt。server 为每个配置 credential 独立缓存 salt 和派生 key；
不可变 client 每次连接重新派生，不再使用锁或 key cache。这消除了 V1 的
64 MiB Argon2id 开销。

V2 的安全模型是**高熵预共享 secret 认证**。private method `0x80` 使用公共 `password`
字段时，应把该字段视作高熵 secret：至少使用 128-bit 随机熵，更推荐把 `crypto/rand`
生成的 24 或 32 bytes 编码成 Base64URL、Base64 或 hex。字符串长度本身不能证明熵。
V2 不传输原始 secret，fresh nonce 能阻止直接重放 proof，但被动抓包者仍能离线验证猜测
的 secret。它不是 TLS、transport encryption 或 PAKE；在重视凭据机密性时，不应使用
弱人类密码。V1（`0x01`、Argon2id）和 V2（`0x02`、HKDF）有意不兼容并 fail closed，
runtime 不提供 V1 fallback。详见 [Secure Auth V2 wire 与固定测试向量](docs/socks5-secure-auth-v2.md)。

HKDF V2 解决认证资源开销，不负责认证 SOCKS5 method selection。独立的
`auth_mode: "secure"` 通过永不声明 RFC1929 提供降级边界；`auto` 仍是明确可降级模式。
RFC1929 和 HTTP Basic 仍会发送可恢复的凭据，需要安全外层才能提供机密性。

native + raw 的 TCP mux 和 QUIC mux 还支持反向 TCP/UDP 服务发布：在服务端 tunnel inbound 配置
`publish`，并在客户端对应的 tunnel outbound 配置 `expose`。详见
[反向服务发布](docs/reverse-publishing.zh-CN.md)。

隧道端点保留 TCP/UDP、mux、raw/ws/h2/h3、TLS 和 REALITY。`via` 链会检查长度和环。blackhole 的 TCP 明确拒绝，UDP 直接丢弃，不会误走 direct。

以吞吐为优先的 tcptun-to-tcptun 部署建议使用 Native + raw + mux。Native TCP mux 会在 stream OPEN 帧中携带目标地址，并等待服务端目标拨号结果后再向本地代理报告成功。它的大帧 wire 格式要求两端运行相同的当前版本；从旧版滚动升级时应暂时关闭 mux。连接池、内存上限、transport 取舍、benchmark 与兼容性说明见 [Native 协议文档](docs/protocol-native.zh-CN.md)。

## 配置工具

完整加载、校验、编译但不监听端口：

```sh
tcptun config check --config config.json
```

稳定且幂等地格式化：

```sh
tcptun config format --config config.json
```

一次生成匹配的 `server.json`、`client.json` 和新协议凭据。普通协议命令生成相互匹配的 REALITY 密钥；Native 的可选 ECH 形式改用 raw TCP、`security.type: "none"` 和新 X25519 密钥对，只保护承载的 TLS 1.3 ClientHello SNI：

```sh
tcptun config native --server proxy.example.com --port 9443
tcptun config native --ech --server proxy.example.com --server-name public.example
```

也可以用统一的 topology 生成器生成经过 `Validate + Compile` 校验的配置。`full` 会覆盖文件配置模型中的全部入口、出口、传输、安全、carrier、mux、回落、ECH、反向发布、路由、DNS 和资源字段；TUN/Android 平台注入入口仍需由嵌入方配置：

```sh
tcptun config generate full --output config.json --force
tcptun config generate client --protocol native --security reality --carrier auto --resume --output client.json
tcptun config generate reverse-server --publish-service web --publish-address 0.0.0.0:8080 --output server.json
```

可选 topology 包括 `full`、`client`、`server`、`relay`、`chain`、`reverse-server` 和 `reverse-client`。生成器支持 `native`、`raw/ws/h2/h3`、`none/tls/reality`、TCP/QUIC/auto carrier、mux/resume、TLS fallback、TCP/UDP 反向发布，以及 DNS 和资源限制。协议快捷命令也接受相同的 transport、security、carrier、mux、fallback、reverse 和 DNS 选项。

值类型 flag 使用 `pkg.gostartkit.com/cmd` 的 enum 元数据和动态 completion；可用内建命令生成 Bash、Zsh、Fish 或 PowerShell 补全：

```sh
tcptun completion zsh > "${fpath[1]}/_tcptun"
```

服务端默认监听 `0.0.0.0:9443`，客户端默认在 `127.0.0.1:1080` 提供 mixed 代理。Native + raw
的 `security.type: "reality"` 默认使用 `carrier.mode: "auto"`，在同一地址监听 TCP/UDP，
优先 QUIC，失败后带退避回落到 Reality TCP；也可设为 `tcp` 或 `quic`。`quic` 与 `auto`
必须启用 `"mux": {"enabled": true}`，双模式伪装目标应同时支持 TCP HTTPS 和 UDP HTTP/3。
使用 `--server-name` 和 `--dest` 选择 REALITY 伪装端点，使用 `--force` 同时覆盖全部输出文件。
生成器短参数为 `-S/-C`、`-s/-p/-l`、`-L/-P`、`-n`、`-D`、`-q` 和 `-e`。

Native 是 tcptun 私有协议，使用 tcptun-to-tcptun wire 测试覆盖。顶层 `uri` 命令支持 Native 端点。导入或导出单个端点：

```sh
tcptun uri export --config client.json --output client.uri
tcptun uri export --config client.json --qr-output client.png
tcptun uri export --config client.json --qr-output client.png --qr-format t3
tcptun uri export --config client.json --qr-output client.png --qr-format t3 --qr-compact
tcptun uri export --config client.json --qr-output client.png --qr-level high --qr-module-size 8
tcptun uri import --input client.uri --output outbound.json
tcptun uri import --input client.uri --client --output client.json
cat client.uri | tcptun uri import --input - --client --output client.json
```

导出默认处理全部 tunnel outbound；`--inbound` 改为处理全部 tunnel inbound/listen endpoint，
并为每个配置用户与监听地址分别生成一条客户端 URI，与配置文件名无关。多个 URI 用换行分隔，
多个二维码输出为 `client-1.png`、`client-2.png`……。
二维码默认承载普通协议 URI。`--qr-format t3` 改用当前的 `T3:` 二进制 Base45 profile，可显著降低二维码版本，并完整保留 TCP-only/UDP-only、carrier 选择、QUIC UDP mode 与 receive-window 覆盖。T3 严格编码，失败时不会回退普通 URI。`--qr-format t2` 仅供旧端 Native 互操作；导入仍识别旧 Native T2。完整布局见 [T3 格式说明](docs/profile-t3.zh-CN.md)。

二维码渲染默认使用 medium（15%）纠错和每 module 8 像素。`--qr-level` 支持 `low`/`medium`/`high`/`highest`，分别对应 QR L/M/Q/H（约 7%/15%/25%/30%）；`--qr-module-size` 支持 4 到 12 像素。`--qr-compact` 仅适用于 T3，会从二维码 payload 中省略纯展示名称，导入后使用服务器 host 作为名称；单独导出的 URI 文本仍保留指定名称。三个参数的短形式分别为 `-R`、`-s` 和 `-C`。普通相机扫描建议保留 medium，只有在受控屏幕环境中更重视降低二维码版本时才使用 low。

Android gomobile bridge 提供无状态的 `EncodeProfile(profileJSON)`、`EncodeProfileQRCode(profileJSON, recoveryLevel, moduleSize, compact)` 与 `DecodeProfile(payload)`。二维码 API 直接返回 PNG 字节（Java/Kotlin 为 `byte[]`/`ByteArray`）；空纠错等级和 `0` module size 使用 medium 与每 module 8 像素。当前编码格式为 `T3:`，解码仍兼容旧 `T2:`，bridge 按前缀严格分派且不做格式回退；二维码扫描仍由 Android 应用负责。解码会移除 Android 本地 `id`，未知 JSON 字段会被拒绝。

单个本地代理凭据使用完全独立的 `A1:` 格式和 `EncodeProxyAccount(accountJSON)`、`DecodeProxyAccount(payload)`、`EncodeProxyAccountQRCode(accountJSON, recoveryLevel, moduleSize)`。一个 A1 永远只包含一组 username/password；它复用 T3 的 binary → Base45 → QR alphanumeric 外层策略，但使用独立冻结 wire，绝不会被解析为 tunnel profile。A1 不是加密，必须作为 bearer secret 处理。完整说明见 [A1 格式文档](docs/proxy-account-a1.zh-CN.md)。

`--client` 会生成可以直接运行的 mixed inbound、route 和 tunnel outbound。非 tunnel 出口（如
`direct`）会跳过；包含多个 `outbound.address` 的出口会为每个候选地址导出一条 URI 或一个二维码。
URI 与二维码都包含连接凭据，默认文件权限为 `0600`，不应发布到不受信任的位置。单条 URI
无法表达候选地址数组、自定义 route、discovery 或出口链；这些场景必须使用 JSON。旧的
`tcptun://` Native URI 仍可导入，但带 `mode` 查询参数的 URI 会被拒绝。URI flag 的短形式
包括导出 `-i/-n/-o/-q/-Q/-R/-s/-C/-f`，导入 `-t/-i/-o/-C/-f/-l/-p`。

## 旧概念映射

| 旧概念 | 统一拓扑 |
| --- | --- |
| local 拓扑 | 本地 mixed/SOCKS inbound + LAN SOCKS/mixed outbound |
| client 拓扑 | 本地 mixed/SOCKS inbound + 远端 tunnel outbound |
| server 拓扑 | tunnel inbound + 指定的 direct/SOCKS/mixed/tunnel outbound |

正式 loader 会明确拒绝旧顶层 `mode` 格式，不会猜测、迁移或提供兼容命令。

## 示例与开发

[`examples/`](examples/) 包含 local、native client/server、native TLS 回落 server、反向发布、relay、chain 和 blackhole 示例；测试会逐个执行 Load + Validate + Compile。

```sh
go test ./...
go vet ./...
```

CLI 基于 `pkg.gostartkit.com/cmd` v0.2.1。第一次终止信号触发优雅退出，第二次信号立即退出。
# 按应用路由

本地平台可以为新 flow 提供跨平台的应用身份。tcptun 本身不查询 Android
UID、包名或进程；身份只供本地 Router 选择 outbound，不会写入 native、mux 或 UDP 协议帧。

路由规则可使用嵌套的 `app` 条件：`ids`、`id_prefixes`、`platforms`，以及
多值 `attributes`。同一数组内为 OR，不同字段、不同 attribute key 以及
普通 inbound/network/domain/IP 条件之间为 AND。没有应用身份时，带 `app`
的规则不匹配，并继续普通路由。

Android 推荐把 package name 作为稳定 `id`；UID、shared package、profile
等信息可放入 `attributes`。UID 可能随安装变化，显示名称不适合作为长期
规则键。Android bridge 的可选 `AppIdentityProvider` 接收 flow JSON 并返回
identity JSON；TCP 每连接查询一次，UDP 按 flow 缓存，不会逐 packet 查询。

Android `VpnService`、TUN/tun2socks 与 tcptun 的所有权、启动、停止和进程重建
顺序见 [Android VPN 生命周期集成](docs/android-vpn-integration.zh-CN.md)。

## 许可证

TcpTun Inc 原创代码使用 [TcpTun Inc 专有免费使用协议](LICENSE) 发布。任何人都可以
免费使用获得授权的副本，包括商业使用；但该协议不授予修改、再分发、再许可或复用源代码
的权利。使用者自行承担使用风险，TcpTun Inc 不提供任何担保或技术支持。仓库内的第三方
组件继续遵循各自的许可证，详见对应目录中的许可证文件。
