Purpose: Firestack is a VPN and network tunneling system that intercepts device traffic through a TUN device, processes it through a userspace TCP/IP stack (gVisor), and routes it through configurable DNS resolvers and proxy servers. The system provides advanced features including DNS Application Layer Gateway (ALG), NAT64, multiple proxy types (WireGuard, SOCKS5, HTTP), intelligent routing, and connection retry mechanisms.
Scope: This page provides a high-level architectural overview of the firestack codebase. For detailed information about specific subsystems, see:
Firestack is organized into seven primary layers, each responsible for distinct concerns:
Sources: intra/tun2socks.go intra/tunnel.go124-136 tunnel/tunnel.go68-78 intra/common.go69-86 intra/tcp.go47-50 intra/udp.go44-47 intra/icmp.go25-27 intra/dnsx/transport.go184-203 intra/dnsx/alg.go855-874 intra/ipn/proxies.go227-263 intra/ipn/wgproxy.go98-151
The system exposes its functionality through tun2socks.go, which implements the Connect() API. This is the primary entry point for embedding firestack into applications.
| Component | Implementation | Purpose |
|---|---|---|
| Main Entry | tun2socks.Connect() | Initializes the entire system with TUN fd, DNS, and listeners |
| API Surface | Various exported functions | Controls runtime behavior (logging, routing, DNS config) |
Sources: intra/tun2socks.go
The tunnel layer implements a two-tier abstraction:
intra/tunnel.go) that manages lifecycle, coordinates DNS resolver and proxy provider, and handles configuration updatestunnel/tunnel.go) that interfaces with gVisor's TCP/IP stack and the TUN deviceThe gVisor netstack provides a complete userspace TCP/IP implementation, eliminating the need for kernel networking.
Sources: intra/tunnel.go124-136 tunnel/tunnel.go68-78 intra/netstack/netstack.go30-90
Protocol handlers process packets from the network stack:
Sources: intra/common.go69-103 intra/tcp.go47-127 intra/udp.go44-89 intra/icmp.go25-40 intra/udpmux.go69-82
The DNS system is the most complex subsystem, featuring:
| Component | Type | Key Functions |
|---|---|---|
resolver | Core orchestrator | forward(), Add(), Remove(), Serve() |
dnsgateway | ALG & NAT64 | q(), X(), PTR(), RESOLV() |
ctransport | Caching wrapper | Query(), Clear() |
| Transport types | DoH, DoT, DNS53, DNSCrypt, ODoH, mDNS | Per-transport Query() |
The resolver enforces policy through OnQuery() and OnUpstreamAnswer() callbacks, supports multiple concurrent transports, and implements ALG IP translation for split-tunneling.
Sources: intra/dnsx/transport.go184-242 intra/dnsx/alg.go855-899 intra/dnsx/cacher.go92-145
The proxifier manages proxy lifecycle and routing decisions:
The ProxyTo() method implements sticky sessions through IP/UID pinning, health checks, and automatic failover. WireGuard proxies have their own gVisor stacks for nested tunneling.
Sources: intra/ipn/proxies.go227-333 intra/ipn/wgproxy.go98-151 intra/ipn/wgproxy.go160-172 intra/ipn/auto.go28-57
| Component | Responsibility |
|---|---|
dialers package | Connection establishment, retry logic |
retrier | Transparent connection retry with TCP splitting |
ipmap.IPMap | Hostname-to-IP caching with confirmation feedback |
protect.Controller | Network binding and socket protection |
Sources: intra/dialers intra/protect/ipmap
log package with spam filtering and platform-specific console adapterssettings packageSocketListener, DNSListener, ProxyListener) for event reportingSources: intra/log intra/settings intra/listener.go19-66
The following diagram traces a typical connection from application to internet:
Key decision points:
baseHandler.isDNS() + baseHandler.dnsOverride()): Intercepts DNS queries to tunnel's fake DNS addresseslistener.Preflow() + listener.Flow()): Determines blocking/routing rules based on UID, source, destinationproxifier.ProxyTo()): Chooses proxy based on pinning, health, and configurationdialers.ResolveFor() + gateway.X()): Resolves hostnames and undoes ALG translationsretrier): Implements TCP splitting and retry strategiesSources: intra/common.go132-227 intra/tcp.go240-364 intra/udp.go168-386 intra/ipn/proxies.go448-637 intra/dialers
Sources: intra/netstack intra/common.go69-103 intra/tcp.go47-50 intra/udp.go44-47 intra/icmp.go25-27
The Proxy interface requires implementing Dial(), DialBind(), Announce(), Accept(), Hop(), Refresh(), Ping(), and lifecycle methods.
Sources: intra/ipn/proxies.go165-186 intra/ipn/proxy.go intra/ipn/wgproxy.go160-172
Sources: intra/dnsx/transport.go164-182 intra/dnsx/transport.go184-203 intra/dnsx/alg.go83-102 intra/dnsx/cacher.go65-68
Sources: intra/tcp.go240-364 intra/common.go132-311 intra/dnsx/transport.go505-718 intra/ipn/proxies.go448-637 intra/ipn/wgproxy.go189-193
Firestack is designed to be embedded into mobile applications (Android/iOS) through Go Mobile bindings. The build system generates:
| Platform | Artifact | Location |
|---|---|---|
| Android | AAR | intra/tun2socks.aar |
| iOS | XCFramework | Tun2socks.xcframework |
| Linux | Binary | tun2socks |
| Windows | Binary | tun2socks.exe |
The Bridge interface (intra/tunnel.go) defines the contract between firestack and the host application, providing callbacks for:
Sources: Makefile .github/workflows/go.yml intra/tunnel.go61-74
The system maintains several categories of state:
baseHandler.conntracker (connid → [local, remote])proxifier with maps for proxies, RPN proxies, and hop relationshipsdnsgateway ALG/NAT/PTR maps and ctransport cachesipmap.IPMap with confirmation/disconfirmation feedback loopsAll mutable state uses appropriate synchronization primitives (sync.RWMutex, atomic, core.Volatile).
Sources: intra/common.go99 intra/ipn/proxies.go232-240 intra/dnsx/alg.go856-862 intra/protect/ipmap
This overview provides the foundation for understanding firestack's architecture. For detailed information about each subsystem, refer to the linked sections.
Refresh this wiki
This wiki was recently refreshed. Please wait 1 day to refresh again.