burrow/docs/trojan-mihomo-discrepancies.md
JettChenT d1638726ca 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.
2026-06-01 15:23:17 +08:00

26 KiB

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:

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:

  1. Burrow only preserves domain metadata for TCP flows that went through its daemon-owned A-query fake DNS path.
  2. 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:

  1. ws/grpc transport is unsupported by Burrow runtime. The selected node is TCP, so this is not the immediate failure for that node.
  2. 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.