Pure C FRP tunneling client — compatible with frps v0.52+. TCP, HTTP, HTTPS, STCP proxy types with Yamux multiplexing, AES-128-CFB encryption, and auto-reconnect. No Go, no OpenSSL.
scorpiox-frp is a pure C implementation of an FRP (Fast Reverse Proxy) client. It speaks the native FRP wire protocol — JSON messages over length-prefixed binary frames — and connects to any standard frps server v0.52 or later. Unlike the official Go client, scorpiox-frp compiles to a single static binary with zero runtime dependencies. It uses mbedTLS for AES-128-CFB encryption on the control channel and implements Yamux session multiplexing for efficient connection handling.
source layoutscorpiox/scorpiox-frp.c # CLI entry point, arg parsing, proxy config
scorpiox/libsxnet/sx_frp.c # FRP protocol engine, Yamux, auth, crypto
scorpiox/libsxnet/sx_frp.h # Public API and data structures
scorpiox-frp supports six protocol types for tunneling traffic through a remote frps server. Each proxy type handles a different traffic pattern.
| Protocol | Description | Use Case |
|---|---|---|
TCP |
Raw TCP port forwarding | SSH, databases, custom services |
HTTP |
HTTP reverse proxy with virtual hosting | Web apps, APIs, dev servers |
HTTPS |
TLS-terminated HTTPS proxy | Secure web services |
STCP |
Secret TCP — visitor must know secret key | Private services, peer-to-peer |
TLS |
Optional TLS wrap on control channel to frps | Secure control plane |
FRP |
Native wire protocol (JSON + length-prefixed frames) | Internal frps communication |
The client establishes a single control connection to frps, authenticates with MD5-hashed token, then multiplexes all proxy traffic over Yamux sessions through that connection.
All traffic shares a single TCP connection to frps. Yamux (Yet another Multiplexer) creates virtual streams over this connection, each identified by a 32-bit stream ID. This avoids the overhead of separate TCP connections per proxy while maintaining flow control per stream.
Each FRP message is a length-prefixed JSON frame:
frp frame format┌──────────┬──────────┬─────────────────────────┐
│ type (1B) │ len (8B) │ JSON payload (len bytes) │
└──────────┴──────────┴─────────────────────────┘
# Message types:
TypeLogin = 'o' # Client login request
TypeLoginResp = '1' # Server login response
TypeNewProxy = 'p' # Register a proxy
TypeNewProxyResp = '2' # Proxy registration ack
TypeReqWorkConn = 'r' # Server requests work connection
TypeNewWorkConn = 'w' # Client opens work connection
TypePing = 'h' # Heartbeat ping
TypePong = '4' # Heartbeat pong
JSON over length-prefixed frames. Native frps v0.52+ compatibility without the Go runtime.
Token + timestamp hashed with MD5 for frps authentication. Same algorithm as the official client.
Control stream encryption via mbedTLS. No OpenSSL dependency — bundled at compile time.
Session multiplexing over a single TCP connection. Efficient stream management with flow control.
TCP, HTTP, HTTPS, STCP — custom domains, subdomains, secret keys.
Exponential backoff reconnection. Survives network interruptions without manual restart.
Periodic ping/pong with frps. Detects dead connections and triggers auto-reconnect.
Wrap the entire control connection in TLS for frps servers that require encrypted transport.
Pre-established work connections reduce latency for new proxy requests from frps.
Route HTTP/HTTPS traffic by domain name. Use custom_domains or subdomain configuration.
Pure C with mbedTLS compiled in. Single static binary — no Go, no OpenSSL, no runtime.
Cross-compiled for Linux, macOS, Windows. Drop in and run — no installation required.
http proxyscorpiox-frp --server your-server.com:7000 --token my-secret-token --type http --local-port 8080 --custom-domain app.example.com
tcp proxy — sshscorpiox-frp --server frps.example.com:7000 --token my-secret-token --type tcp --local-port 22 --remote-port 6022
# Then connect from anywhere:
ssh -p 6022 user@frps.example.com
https proxyscorpiox-frp --server frps.example.com:7000 --token my-secret-token --type https --local-port 443 --subdomain myapp
# Accessible at: https://myapp.frps.example.com
stcp proxy# Server side (machine behind NAT):
scorpiox-frp --server frps.example.com:7000 --token my-secret-token --type stcp --local-port 3306 --sk shared-secret-key --name my-database
# Visitor side (connect without frps exposing a port):
# Uses the same --sk to authenticate with frps relay
tls transportscorpiox-frp --server frps-secure.example.com:7000 --token my-secret-token --tls --type tcp --local-port 80 --remote-port 8080
scorpiox-frp reads configuration from CLI flags. All flags can also be set as environment variables with the SCORPIOX_FRP_ prefix.
| Flag | Default | Description |
|---|---|---|
--server |
127.0.0.1:7000 |
frps server address (host:port) |
--token |
— | Authentication token (shared with frps) |
--type |
tcp |
Proxy type: tcp, http, https, stcp |
--local-port |
— | Local service port to expose |
--local-ip |
127.0.0.1 |
Local bind address for the proxied service |
--remote-port |
— | Remote port on frps (TCP proxy type only) |
--custom-domain |
— | Custom domain for HTTP/HTTPS proxies |
--subdomain |
— | Subdomain for HTTP/HTTPS proxies |
--name |
auto | Proxy name (must be unique on frps) |
--sk |
— | Secret key for STCP proxy type |
--tls |
off | Enable TLS on control connection |
--pool-count |
1 |
Number of pre-established work connections |
scorpiox-frp authenticates with frps using the same MD5 scheme as the official client. The token is hashed with the current timestamp: MD5(token + timestamp). This prevents replay attacks since the timestamp window is validated server-side.
The control stream is encrypted with AES-128-CFB using a key derived from the shared token. This is implemented via mbedTLS, compiled statically into the binary. The encryption covers all control messages (login, proxy registration, heartbeats) but not the data stream — that's handled by the application-layer protocol (e.g., HTTPS, SSH).
encryption flowtoken = "my-secret-token"
key = MD5(token) # 16 bytes → AES-128 key
iv = MD5(key) # 16 bytes → CFB IV
cipher = AES-128-CFB(key, iv) # Stream cipher on control channel
When --tls is enabled, the entire connection (control + data) is wrapped in TLS before the FRP protocol layer. This provides end-to-end encryption including work connections. Use this when the network between scorpiox-frp and frps is untrusted.
| Aspect | scorpiox-frp (C) | Official frpc (Go) |
|---|---|---|
| Language | Pure C | Go |
| Binary size | ~200 KB static | ~15 MB |
| Memory usage | ~2 MB RSS | ~20 MB RSS |
| Dependencies | Zero (mbedTLS embedded) | Go runtime, modules |
| Startup time | <1 ms | ~50 ms |
| TLS library | mbedTLS (static) | Go crypto/tls |
| Multiplexing | Yamux | Yamux |
| Proxy types | TCP, HTTP, HTTPS, STCP | TCP, HTTP, HTTPS, STCP, SUDP, XTCP, TCPMUX |
| Config format | CLI flags | TOML / CLI |
| frps compatibility | v0.52+ | v0.52+ |