Add proxy subscription runtime support
Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling. Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
This commit is contained in:
parent
97c569fb35
commit
d1638726ca
46 changed files with 15079 additions and 456 deletions
601
docs/trojan-mihomo-discrepancies.md
Normal file
601
docs/trojan-mihomo-discrepancies.md
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
# Burrow vs Mihomo Trojan Implementation Discrepancies
|
||||
|
||||
Date: 2026-05-31
|
||||
|
||||
Scope: code-path comparison between the local Burrow checkout at
|
||||
`/Users/jettchen/dev/burrow` and the local Mihomo checkout at
|
||||
`/Users/jettchen/dev/vpn-ref/mihomo`. This is not based on current live system
|
||||
network state.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The most important differences for the stored Hong Kong Trojan node are:
|
||||
|
||||
1. Mihomo uses `client-fingerprint: chrome` for Trojan URIs when no `fp` query is
|
||||
present and switches to uTLS when that fingerprint is configured. Burrow
|
||||
stores `client_fingerprint` but the Trojan runtime does not use it.
|
||||
2. Mihomo distinguishes absent ALPN from explicitly empty ALPN. For Trojan TCP,
|
||||
absent ALPN defaults to `["h2", "http/1.1"]`; explicitly empty ALPN remains
|
||||
empty. Burrow now carries an `alpn_present` marker for new imports and treats
|
||||
legacy missing metadata as absent.
|
||||
3. Mihomo has a dedicated proxy-server DNS path (`proxy-server-nameserver` /
|
||||
`ProxyServerHostResolver`). Burrow resolves proxy server names with the
|
||||
process/system resolver through `ToSocketAddrs`.
|
||||
4. Mihomo preserves and reconstructs destination hostname metadata in fake-IP
|
||||
and mapping modes. Burrow's packet runtime receives `SocketAddr` values from
|
||||
its userspace stack and sends IP-form SOCKS addresses to Trojan, not domain
|
||||
names.
|
||||
5. Mihomo supports Trojan `tcp`, `ws`, and `grpc` runtimes, plus additional TLS
|
||||
features such as ECH, REALITY, pinned certificate fingerprint, and optional
|
||||
Trojan-over-Shadowsocks wrapping. Burrow parses some of that metadata but the
|
||||
packet runtime only supports Trojan TCP.
|
||||
6. Mihomo's dial path has timeout, retry, dual-stack fallback, optional parallel
|
||||
dialing, interface/routing-mark controls, DNS cache/fallback, and UDP
|
||||
fragmentation behavior. Burrow now matches Trojan UDP fragmentation, but its
|
||||
dial path remains a much narrower serial dialer plus a fixed packet bridge.
|
||||
|
||||
Implementation note:
|
||||
|
||||
- `BEP-0011` and the current runtime patch address ALPN absence/defaulting,
|
||||
Trojan UDP import/runtime gating, Trojan UDP fragmentation, and certificate
|
||||
fingerprint pinning.
|
||||
- Burrow still does not implement Mihomo's uTLS/browser ClientHello
|
||||
impersonation, ws/grpc Trojan transports, REALITY/ECH, proxy-server DNS plane,
|
||||
fake-IP metadata, or policy/rule pipeline.
|
||||
|
||||
## Stored Node Context
|
||||
|
||||
Current Burrow database state inspected from the app group DB:
|
||||
|
||||
```text
|
||||
network type: ProxySubscription
|
||||
subscription: bitz
|
||||
selected ordinal: 2
|
||||
selected name: Hong Kong Guangdong BGP 2
|
||||
protocol: trojan
|
||||
server: ce1081d2-df04-48d7-80bf-46b8c50b4f33.bnsepserv.com
|
||||
port: 32445
|
||||
network: tcp
|
||||
sni: pull-flv-q1-admin.douyincdn.com
|
||||
skip_cert_verify: true
|
||||
alpn: []
|
||||
client_fingerprint: chrome
|
||||
fingerprint: null
|
||||
```
|
||||
|
||||
Evidence:
|
||||
|
||||
- Burrow stores Trojan node fields in `TrojanNode`: `password`, `sni`, `alpn`,
|
||||
`alpn_present`, `skip_cert_verify`, `network`, `udp`,
|
||||
`client_fingerprint`, and `fingerprint`
|
||||
(`burrow/src/proxy_subscription.rs:82`).
|
||||
- The runtime `TrojanOutbound` carries endpoint, password, SNI, ALPN, skip-cert,
|
||||
UDP enablement, and parsed certificate fingerprint. It still does not apply
|
||||
`client_fingerprint`.
|
||||
|
||||
## 1. Subscription and Config Parsing
|
||||
|
||||
### Supported subscription shapes
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Parses individual proxies through a generic adapter parser
|
||||
(`adapter/parser.go:11`).
|
||||
- For `type: trojan`, decodes the full `TrojanOption` with the common structure
|
||||
decoder and constructs `NewTrojan` (`adapter/parser.go:79`).
|
||||
- Supports proxy providers with `file`, `http`, and `inline` vehicles
|
||||
(`adapter/provider/parser.go:77`).
|
||||
- Provider schemas include filter, exclude-filter, exclude-type, dialer-proxy,
|
||||
size-limit, headers, override, payload, health-check, and refresh interval
|
||||
(`adapter/provider/parser.go:28`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Parses either a top-level YAML `proxies` field or a plaintext/base64 URI list
|
||||
(`burrow/src/proxy_subscription.rs:235`, `burrow/src/proxy_subscription.rs:274`,
|
||||
`burrow/src/proxy_subscription.rs:312`).
|
||||
- Does not parse Mihomo `proxy-providers`, `proxy-groups`, provider refresh
|
||||
options, health-checks, overrides, or group selectors.
|
||||
- For URI-list content, only `trojan://` and `ss://` are accepted; other schemes
|
||||
become rejected entries (`burrow/src/proxy_subscription.rs:323`).
|
||||
|
||||
Impact:
|
||||
|
||||
- Burrow can import the selected node, but it does not preserve Mihomo's full
|
||||
provider/group behavior. Selection is Burrow-owned, not Mihomo group semantics.
|
||||
|
||||
### URI conversion
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Trojan URI conversion maps fragment to name, host/port/user to server/port/
|
||||
password, `allowInsecure` to `skip-cert-verify`, optional `sni`, optional
|
||||
`alpn`, optional `type`, ws/grpc options, `pcs` to certificate fingerprint,
|
||||
and `fp` to `client-fingerprint` (`common/convert/converter.go:149`).
|
||||
- The URI converter also sets `udp: true` for Trojan URI imports
|
||||
(`common/convert/converter.go:162`).
|
||||
- If `fp` is absent, Mihomo sets `client-fingerprint` to `chrome`
|
||||
(`common/convert/converter.go:198`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Maps similar basics and also defaults missing `fp` to `chrome`
|
||||
(`burrow/src/proxy_subscription.rs:491`).
|
||||
- Stores `pcs` as `fingerprint` (`burrow/src/proxy_subscription.rs:517`).
|
||||
- Stores ALPN with an `alpn_present` marker, so new imports can distinguish
|
||||
missing ALPN from explicit empty ALPN (`burrow/src/proxy_subscription.rs:83`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo can distinguish "ALPN field absent" from "ALPN field present but empty"
|
||||
at the decoded option/runtime layer because the option slice can be nil or
|
||||
non-nil. Burrow now records that distinction for new imports.
|
||||
- Mihomo URI conversion only sets `alpn` when the URI query value is non-empty;
|
||||
this is still different from Burrow because Burrow imports absent URI ALPN as
|
||||
a concrete empty vector that the runtime later treats as intentional.
|
||||
- Mihomo carries Trojan's `udp` option through the base outbound model. Burrow
|
||||
now stores the Trojan `udp` field and gates Trojan UDP sessions on it.
|
||||
|
||||
## 2. Runtime Transport Support
|
||||
|
||||
Mihomo:
|
||||
|
||||
- `TrojanOption.Network` supports default TCP, `ws`, and `grpc`
|
||||
(`adapter/outbound/trojan.go:51`).
|
||||
- Runtime branches:
|
||||
- `ws`: wraps the TCP connection in websocket over TLS
|
||||
(`adapter/outbound/trojan.go:67`).
|
||||
- `grpc`: uses gun transport and a gRPC client pool
|
||||
(`adapter/outbound/trojan.go:175`, `adapter/outbound/trojan.go:298`).
|
||||
- default TCP: TLS, then Trojan header (`adapter/outbound/trojan.go:119`).
|
||||
- Trojan options also include `ECHOpts`, `RealityOpts`, `SSOpts`, and
|
||||
`ClientFingerprint` (`adapter/outbound/trojan.go:52`).
|
||||
- If `ss-opts.enabled` is set, Mihomo wraps Trojan streams in Shadowsocks using
|
||||
the configured method/password, defaulting the method to `AES-128-GCM`
|
||||
(`adapter/outbound/trojan.go:284`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Parser represents `Tcp`, `Ws`, and `Grpc` (`burrow/src/proxy_subscription.rs:93`).
|
||||
- Runtime rejects any Trojan network except `Tcp`
|
||||
(`burrow/src/proxy_runtime.rs:553`).
|
||||
- Runtime preview can mark unsupported transports via `runtime_support_error`
|
||||
(`burrow/src/proxy_runtime.rs:279`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Burrow parses ws/grpc metadata but cannot run ws/grpc Trojan nodes.
|
||||
- Mihomo can run ws and grpc Trojan nodes.
|
||||
- Burrow has no Trojan-over-Shadowsocks wrapper equivalent for Mihomo
|
||||
`ss-opts`.
|
||||
- Burrow has no Trojan REALITY or ECH runtime support.
|
||||
|
||||
## 3. ALPN Behavior
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Trojan TCP default ALPN is `["h2", "http/1.1"]`
|
||||
(`transport/trojan/trojan.go:23`).
|
||||
- For default TCP, Mihomo starts with that default, then overrides only if
|
||||
`option.ALPN != nil` (`adapter/outbound/trojan.go:122`).
|
||||
- For websocket, default ALPN is `["http/1.1"]` and is overridden only if
|
||||
`option.ALPN != nil` (`adapter/outbound/trojan.go:95`).
|
||||
- For grpc, TLS `NextProtos` is fixed to `["h2"]`
|
||||
(`adapter/outbound/trojan.go:307`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Runtime ALPN uses Mihomo's TCP default when `alpn_present` is false, and uses
|
||||
`TrojanNode.alpn` exactly when `alpn_present` is true
|
||||
(`burrow/src/proxy_runtime.rs:695`).
|
||||
- TLS config writes those strings directly into rustls `alpn_protocols`
|
||||
(`burrow/src/proxy_runtime.rs:820`).
|
||||
- Parser stores missing ALPN as `alpn_present: false`; legacy stored nodes that
|
||||
lack `alpn_present` deserialize the same way.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- For a Trojan URI that omits `alpn`, Mihomo sends `h2,http/1.1`; Burrow now
|
||||
applies the same default through the presence marker.
|
||||
- Explicit empty ALPN is possible through Mihomo's decoded YAML/options path,
|
||||
but not through its URI converter because empty URI `alpn` is not emitted.
|
||||
|
||||
Likely relevance:
|
||||
|
||||
- The selected node has `alpn: []` in Burrow. If the subscription omitted ALPN,
|
||||
Mihomo would use the default ALPN list, while Burrow now cannot know that and
|
||||
sends none.
|
||||
|
||||
## 4. Client Fingerprint and uTLS
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Trojan options include `ClientFingerprint` (`adapter/outbound/trojan.go:57`).
|
||||
- URI conversion defaults missing `fp` to `chrome`
|
||||
(`common/convert/converter.go:198`).
|
||||
- TLS connection code checks `GetFingerprint`; when recognized, it converts the
|
||||
TLS config to a uTLS config and creates a uTLS client
|
||||
(`transport/vmess/tls.go:46`).
|
||||
- uTLS preserves `NextProtos`, `ServerName`, cert settings, and version bounds
|
||||
from the config (`component/tls/utls.go:175`).
|
||||
- `GetFingerprint` falls back to a global fingerprint if the node omits one,
|
||||
returns no uTLS for empty/`none`, supports `random`, and recognizes browser
|
||||
profiles such as `chrome`, `firefox`, `safari`, `ios`, `android`, `edge`,
|
||||
`360`, and `qq` (`component/tls/utls.go:42`, `component/tls/utls.go:65`,
|
||||
`component/tls/utls.go:81`).
|
||||
- For websocket, Mihomo forces the uTLS ALPN extension to `http/1.1`
|
||||
(`component/tls/utls.go:258`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Parser stores `client_fingerprint` (`burrow/src/proxy_subscription.rs:89`).
|
||||
- Runtime logs it when present, but does not use it to build a uTLS ClientHello.
|
||||
- Runtime uses OpenSSL on macOS and rustls/tokio-rustls elsewhere, not Mihomo's
|
||||
uTLS implementation.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo sends a Chrome-like TLS ClientHello for this selected node
|
||||
(`client_fingerprint: chrome`).
|
||||
- Burrow sends the platform TLS stack's ClientHello.
|
||||
|
||||
Likely relevance:
|
||||
|
||||
- Many Trojan subscriptions use `fp=chrome` or Mihomo's default `chrome` because
|
||||
the server or fronting path expects that TLS fingerprint. Burrow ignoring it is
|
||||
a high-probability compatibility gap.
|
||||
|
||||
## 5. Certificate Verification and Pinning
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Trojan options include `SkipCertVerify`, `Fingerprint`, `Certificate`, and
|
||||
`PrivateKey` (`adapter/outbound/trojan.go:46`).
|
||||
- TLS setup passes those into `ca.GetTLSConfig`
|
||||
(`adapter/outbound/trojan.go:101`, `transport/vmess/tls.go:27`).
|
||||
- URI conversion maps `pcs` to `fingerprint`
|
||||
(`common/convert/converter.go:204`).
|
||||
- Mihomo treats `fingerprint` as SHA-256 certificate pinning. Browser names such
|
||||
as `chrome` are rejected there with an instruction to use
|
||||
`client-fingerprint` instead (`component/ca/fingerprint.go:13`).
|
||||
- A pinned fingerprint may match any certificate in the chain; if it matches a
|
||||
non-leaf certificate, Mihomo verifies the leaf against that pinned certificate
|
||||
as a trusted root (`component/ca/fingerprint.go:29`).
|
||||
- `GetTLSConfig` installs `VerifyConnection` and sets `InsecureSkipVerify` when
|
||||
pinning is enabled, so Go's default verifier is bypassed in favor of the pin
|
||||
verifier (`component/ca/config.go:100`).
|
||||
- Mihomo supports TLS client certificates through `certificate`/`private-key`,
|
||||
including inline material, safe file paths, file watching, or generated random
|
||||
key material when both are empty (`component/ca/config.go:114`,
|
||||
`component/ca/keypair.go:25`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Parser stores `fingerprint` (`burrow/src/proxy_subscription.rs:90`).
|
||||
- Runtime parses `fingerprint` as a SHA-256 certificate pin and checks the
|
||||
presented certificate chain against it (`burrow/src/proxy_runtime.rs:865`).
|
||||
- Runtime root store is webpki roots; `skip_cert_verify` installs a verifier
|
||||
that accepts the certificate/signature checks (`burrow/src/proxy_runtime.rs:820`,
|
||||
`burrow/src/proxy_runtime.rs:835`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo supports pinned certificate fingerprint and custom certificate material.
|
||||
- Burrow now supports SHA-256 certificate fingerprint pinning, normal root
|
||||
validation, or all-cert skip verification.
|
||||
- Burrow has no client certificate fields for Trojan.
|
||||
|
||||
## 6. SNI Behavior
|
||||
|
||||
Mihomo:
|
||||
|
||||
- `NewTrojan` defaults empty SNI to the server host
|
||||
(`adapter/outbound/trojan.go:251`).
|
||||
- Trojan URI conversion sets SNI only when the `sni` query is present
|
||||
(`common/convert/converter.go:168`).
|
||||
- TLS config uses `option.SNI` as host/server name
|
||||
(`adapter/outbound/trojan.go:126`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Parser accepts URI `sni`, `peer`, or `host` as SNI
|
||||
(`burrow/src/proxy_subscription.rs:505`).
|
||||
- Runtime defaults SNI to node server if absent
|
||||
(`burrow/src/proxy_runtime.rs:561`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Burrow accepts more SNI aliases in URI parsing than Mihomo's converter path.
|
||||
- Runtime defaulting is effectively aligned for TCP.
|
||||
|
||||
## 7. Trojan Header and Password Hash
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Password is converted with `trojan.Key` (`adapter/outbound/trojan.go:269`).
|
||||
- Trojan header writes hex password, CRLF, command byte, SOCKS address, CRLF
|
||||
(`transport/trojan/trojan.go:39`).
|
||||
- TCP command is `1`, UDP command is `3`
|
||||
(`transport/trojan/trojan.go:31`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Password hash is SHA-224 hex (`burrow/src/proxy_runtime.rs:903`).
|
||||
- Header writes hash, CRLF, command byte, SOCKS address, CRLF as a single
|
||||
coalesced buffer, matching Mihomo's one-write `trojan.WriteHeader` behavior.
|
||||
- TCP uses command `0x01`; UDP uses `0x03`
|
||||
(`burrow/src/proxy_runtime.rs:583`, `burrow/src/proxy_runtime.rs:604`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- No remaining framing discrepancy for basic TCP/UDP header shape. A live
|
||||
provider check showed that splitting the Trojan TCP header across multiple TLS
|
||||
writes could produce EOF before any target response bytes; Burrow now
|
||||
coalesces the TCP header before writing it.
|
||||
|
||||
## 8. Destination Address and Hostname Metadata
|
||||
|
||||
Mihomo:
|
||||
|
||||
- `serializesSocksAddr` writes a domain-form SOCKS address when
|
||||
`metadata.AddrType()` is domain (`adapter/outbound/util.go:15`).
|
||||
- Fake-IP and mapping preprocessing can translate a destination fake IP back to
|
||||
hostname, clear `DstIP`, and mark `DNSFakeIP`
|
||||
(`tunnel/tunnel.go:287`).
|
||||
- If fake-IP metadata is missing, Mihomo may sniff TCP to recover a domain
|
||||
(`tunnel/tunnel.go:511`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Netstack TCP accept yields `remote_addr: SocketAddr`; Burrow now checks its
|
||||
daemon-owned fake-IP DNS cache and writes a domain-form SOCKS address when the
|
||||
accepted destination IP maps back to a hostname. It falls back to IP-form when
|
||||
no hostname is known.
|
||||
- UDP sessions key on local/remote `SocketAddr`. DNS queries to Burrow's
|
||||
daemon-owned resolver are answered locally before they become proxy UDP flows.
|
||||
Other UDP flows still use socket-address metadata.
|
||||
- Burrow can parse a domain-form SOCKS address in Trojan UDP responses, but it
|
||||
resolves that domain immediately through the process/system resolver and
|
||||
returns a `SocketAddr` (`burrow/src/proxy_runtime.rs:1187`).
|
||||
- Burrow has a basic fake-IP map for A queries answered by the daemon resolver,
|
||||
but no TCP sniffer or rules engine.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo can preserve domain targets through fake-IP/mapping paths.
|
||||
- Burrow now preserves DNS-backed domain targets for TCP, but only for domains
|
||||
resolved through its daemon-owned A-query fake DNS path.
|
||||
|
||||
Compatibility impact:
|
||||
|
||||
- For ordinary HTTPS this may still work because the application supplies TLS
|
||||
SNI to the target site. It breaks or weakens Mihomo-style rule selection,
|
||||
fake-IP DNS, and any proxy behavior that needs the destination hostname at the
|
||||
outbound layer.
|
||||
|
||||
## 9. Proxy Server DNS Resolution
|
||||
|
||||
Mihomo:
|
||||
|
||||
- The generic dialer resolves proxy server hostnames through
|
||||
`resolver.ProxyServerHostResolver` by default (`component/dialer/dialer.go:358`).
|
||||
- `ProxyServerHostResolver` is wired from `proxy-server-nameserver` when present,
|
||||
otherwise from the normal resolver (`hub/executor/executor.go:290`).
|
||||
- Resolver lookup checks hosts first, IP literals second, then configured
|
||||
resolver or system resolver fallback (`component/resolver/resolver.go:154`).
|
||||
- DNS exchange uses cache, singleflight, retry monitor, main/fallback
|
||||
nameservers, and policy routing (`dns/resolver.go:150`,
|
||||
`dns/resolver.go:220`, `dns/resolver.go:298`).
|
||||
- Resolver caches are TTL based, can serve stale records with background refresh,
|
||||
and use a default max size of 4096 (`dns/resolver.go:150`,
|
||||
`dns/resolver.go:452`, `dns/util.go:48`).
|
||||
- The system resolver refreshes `/etc/resolv.conf` periodically and can fall
|
||||
back to public DNS servers if no system DNS clients are available
|
||||
(`dns/system_common.go:15`, `dns/system.go:68`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Proxy endpoint resolution starts with `(host, port).to_socket_addrs()` via the
|
||||
process/system resolver.
|
||||
- The current workspace caches resolved proxy server addresses in
|
||||
`ProxyServerEndpoint` when the outbound is configured
|
||||
(`burrow/src/proxy_runtime.rs:1263`).
|
||||
- If system DNS returns only `198.18.0.0/15` fake-IP addresses, Burrow falls
|
||||
back to direct DNS using physical/public DNS servers and excludes the resolved
|
||||
real proxy-server host routes from the tunnel.
|
||||
- There is no user-configurable proxy-server resolver equivalent.
|
||||
- There is no DNS TTL refresh or resolver cache for proxy server hosts beyond
|
||||
the current cached address list.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo has a first-class proxy-server DNS plane; Burrow does not.
|
||||
- Mihomo has richer policy and cache controls for proxy-server DNS. Burrow now
|
||||
has a narrower fake-IP fallback that avoids recursive proxy-server dials for
|
||||
observed `198.18.0.0/15` answers.
|
||||
|
||||
## 10. Dialing Strategy, Interface Binding, and Socket Options
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Dialer parses all resolved IPs, normalizes IPv4-in-IPv6, and supports IPv4/
|
||||
IPv6 preference (`component/dialer/dialer.go:358`).
|
||||
- It can dial serially, in dual-stack fallback mode, or in parallel depending on
|
||||
`tcpConcurrent` (`component/dialer/dialer.go:58`,
|
||||
`component/dialer/dialer.go:207`).
|
||||
- Dialer applies keepalive and MPTCP settings (`component/dialer/dialer.go:139`).
|
||||
- Dialer supports explicit `interface-name`, default interface, interface
|
||||
finder, routing mark, TFO, MPTCP, and custom net dialers
|
||||
(`component/dialer/dialer.go:143`, `adapter/outbound/base.go:145`).
|
||||
- The dialer option model includes `interfaceName`, `fallbackBind`,
|
||||
`routingMark`, preferred IP family, TFO, MPTCP, resolver, and custom net
|
||||
dialer (`component/dialer/options.go:33`).
|
||||
- Darwin interface binding uses `IP_BOUND_IF` / `IPV6_BOUND_IF`
|
||||
(`component/dialer/bind_darwin.go:14`).
|
||||
- Linux interface binding uses bind-to-device, and Linux routing marks use
|
||||
`SO_MARK` (`component/dialer/bind_linux.go:12`,
|
||||
`component/dialer/mark_linux.go:20`).
|
||||
- Mihomo wraps outbound connection creation in a 5 second context and a retry
|
||||
loop with up to 10 attempts, stopping early for non-retryable DNS/reject
|
||||
errors (`tunnel/tunnel.go:560`, `tunnel/tunnel.go:702`).
|
||||
- The dialer has a 300 ms dual-stack fallback window and optional parallel dial
|
||||
mode (`component/dialer/dialer.go:25`, `component/dialer/dialer.go:58`,
|
||||
`component/dialer/dialer.go:207`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Dials cached addresses sequentially (`burrow/src/proxy_runtime.rs:1294`).
|
||||
- No explicit TCP connect timeout wrapper is applied in this path.
|
||||
- No TFO, MPTCP, routing mark, custom dialer, or configurable interface name.
|
||||
- On Apple, Burrow chooses a default physical interface by scanning interfaces,
|
||||
preferring `en*` and skipping `utun`, loopback, bridge, awdl, etc.
|
||||
(`burrow/src/proxy_runtime.rs:1501`).
|
||||
- Burrow skips physical-interface binding for loopback proxy-server addresses so
|
||||
controlled local proxy runtime tests can use `127.0.0.1`.
|
||||
- On non-Apple, `bind_proxy_outbound_socket` is a no-op
|
||||
(`burrow/src/proxy_runtime.rs:1495`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo has a mature, configurable dialer. Burrow has a narrow Apple-focused
|
||||
physical-interface bind path and a sequential dial loop.
|
||||
- Burrow has no high-level per-flow retry loop comparable to Mihomo's tunnel
|
||||
retry.
|
||||
|
||||
## 11. UDP Behavior
|
||||
|
||||
Mihomo:
|
||||
|
||||
- Trojan supports UDP through `ListenPacketContext`
|
||||
(`adapter/outbound/trojan.go:202`).
|
||||
- UDP exposure is gated by the node's `udp` option through `Base.SupportUDP`
|
||||
(`adapter/outbound/trojan.go:50`, `adapter/outbound/base.go:105`).
|
||||
- Before UDP, it resolves destination metadata if needed
|
||||
(`adapter/outbound/trojan.go:203`, `adapter/outbound/base.go:178`).
|
||||
- `SupportUOT` returns true (`adapter/outbound/trojan.go:225`).
|
||||
- UDP packet handling maintains mappings from origin metadata to target address
|
||||
(`tunnel/connection.go:70`).
|
||||
- Trojan UDP packet writes fragment payloads larger than 8192 bytes into
|
||||
multiple Trojan UDP frames (`transport/trojan/trojan.go:54`,
|
||||
`transport/trojan/trojan.go:66`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- UDP is enabled in the userspace netstack (`burrow/src/proxy_runtime.rs:379`).
|
||||
- Burrow's import/runtime model has no Trojan `udp` enable flag, so the runtime
|
||||
does not mirror Mihomo's per-node UDP gating.
|
||||
- UDP sessions are keyed by local/remote socket address and use a channel per
|
||||
flow (`burrow/src/proxy_runtime.rs:516`).
|
||||
- Trojan UDP opens a TLS connection per UDP flow, writes a Trojan UDP header,
|
||||
and relays Trojan UDP packets over that stream
|
||||
(`burrow/src/proxy_runtime.rs:598`).
|
||||
- UDP flow idle timeout is 30 seconds (`burrow/src/proxy_runtime.rs:45`).
|
||||
- No UOT support is exposed.
|
||||
- Burrow now fragments single Trojan UDP payloads above 8192 bytes into multiple
|
||||
Trojan UDP frames (`burrow/src/proxy_runtime.rs:1050`).
|
||||
- Burrow now carries a Trojan `udp` enable/disable flag.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo has metadata-aware UDP resolution/mapping and UOT support. Burrow has
|
||||
direct per-flow Trojan UDP over TLS without hostname metadata.
|
||||
- Mihomo and Burrow both fragment oversized Trojan UDP payloads after
|
||||
`BEP-0011`.
|
||||
|
||||
## 12. Packet Tunnel and DNS Model
|
||||
|
||||
Mihomo:
|
||||
|
||||
- TUN mode has metadata preprocessing, fake-IP reverse mapping, sniffing, rule
|
||||
resolution, and then proxy dial (`tunnel/tunnel.go:287`,
|
||||
`tunnel/tunnel.go:540`).
|
||||
- DNS service and resolver enhancer support fake-IP/mapping modes
|
||||
(`dns/service.go:27`, `dns/enhancer.go:23`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Apple Network Extension forwards packets to a daemon packet stream and writes
|
||||
daemon responses back to `packetFlow` (`Apple/NetworkExtension/PacketTunnelProvider.swift:91`).
|
||||
- Burrow's proxy tunnel settings use virtual addresses and point DNS at the
|
||||
daemon-owned resolver address `100.64.0.2`.
|
||||
- The daemon answers A queries with stable fake IPs and records fake-IP to
|
||||
hostname mappings for later TCP proxy requests. There is still no rules engine
|
||||
or TCP sniffer in the packet runtime.
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo is a policy-aware packet tunnel with DNS hijack/fake-IP metadata.
|
||||
- Burrow is a simpler packet-to-selected-outbound bridge with daemon-owned fake
|
||||
DNS for A queries. It does not yet implement Mihomo's policy routing,
|
||||
sniffing, resolver modes, or per-rule metadata pipeline.
|
||||
|
||||
## 13. Logging and Observability
|
||||
|
||||
Mihomo:
|
||||
|
||||
- DNS cache hits, DNS resolution, metadata preprocessing failures, process lookup
|
||||
failures, and UDP resolution failures are logged in the relevant paths
|
||||
(`dns/resolver.go:170`, `tunnel/tunnel.go:339`, `tunnel/tunnel.go:506`,
|
||||
`tunnel/connection.go:88`).
|
||||
|
||||
Burrow:
|
||||
|
||||
- Runtime now logs selected node, configured Trojan outbound metadata, resolved
|
||||
proxy addresses, fake-IP/benchmark-range warning, and socket bind/connect
|
||||
failures (`burrow/src/proxy_runtime.rs:215`,
|
||||
`burrow/src/proxy_runtime.rs:552`, `burrow/src/proxy_runtime.rs:1263`,
|
||||
`burrow/src/proxy_runtime.rs:1294`).
|
||||
- `Scripts/burrow-proxy-selftest` provides a controlled daemon packet-runtime
|
||||
test against a local Trojan server, and `proxy-tcp-probe --dns-name` exercises
|
||||
daemon DNS plus raw TCP packet streaming without changing system proxy state.
|
||||
- On macOS, Rust tracing goes to stderr by default; OSLog is opt-in via
|
||||
`BURROW_ENABLE_OSLOG=1` (`burrow/src/tracing.rs:50`).
|
||||
|
||||
Discrepancy:
|
||||
|
||||
- Mihomo logs more of the full metadata/rule/DNS pipeline.
|
||||
- Burrow now logs the outbound path and has standalone packet-runtime probes,
|
||||
but still lacks Mihomo's full metadata/rule diagnostics.
|
||||
|
||||
## 14. Likely Compatibility Gaps for the Selected Node
|
||||
|
||||
Highest probability:
|
||||
|
||||
1. `client_fingerprint: chrome` is stored but not implemented by Burrow runtime.
|
||||
Mihomo would use uTLS Chrome, so this remains a compatibility gap for
|
||||
providers that strictly require browser ClientHello impersonation.
|
||||
2. The selected provider node succeeds through Mihomo when Mihomo is configured
|
||||
from Burrow's stored DB payload, pinned to real proxy-server IPs, and bound to
|
||||
`en0`. Burrow's daemon packet probe now also succeeds for `google.com:80`
|
||||
after macOS OpenSSL TLS and coalesced Trojan header writes.
|
||||
3. Older stored nodes may have `alpn: []` from pre-`BEP-0011` imports. Burrow now
|
||||
treats missing `alpn_present` as absent ALPN and applies Mihomo's TCP default.
|
||||
4. Burrow's proxy-server resolver is narrower than Mihomo's configurable
|
||||
proxy-server DNS plane.
|
||||
|
||||
Medium probability:
|
||||
|
||||
5. Burrow only preserves domain metadata for TCP flows that went through its
|
||||
daemon-owned A-query fake DNS path.
|
||||
6. Burrow has no Mihomo-style sniffing or rule metadata. This is
|
||||
less likely to break simple all-traffic proxying, but it is a major semantic
|
||||
difference.
|
||||
|
||||
Lower probability for this exact selected node:
|
||||
|
||||
7. ws/grpc transport is unsupported by Burrow runtime. The selected node is TCP,
|
||||
so this is not the immediate failure for that node.
|
||||
8. Certificate pinning is now implemented by Burrow. The selected node has no
|
||||
stored pinned fingerprint, so this does not explain that exact node.
|
||||
|
||||
## Suggested Burrow Follow-Ups
|
||||
|
||||
1. Implement `client_fingerprint` for Trojan, at least `chrome`, or reject nodes
|
||||
requiring runtime fingerprints until uTLS-style support exists.
|
||||
2. Expand the proxy-server resolver path toward Mihomo's configurable
|
||||
`proxy-server-nameserver` behavior and TTL/cache controls.
|
||||
3. Add sniffing or broader DNS metadata handling for flows that do not use the
|
||||
daemon-owned A-query fake DNS path.
|
||||
4. Either implement Trojan ws/grpc or make unsupported transport status more
|
||||
prominent when importing/selecting nodes.
|
||||
5. Add Trojan client certificate/private-key support if subscriptions need it.
|
||||
Loading…
Add table
Add a link
Reference in a new issue