SmsForwarder's FRP (Reverse Proxy) Integration for NAT Traversal: A Technical Deep Dive
SmsForwarder embeds the FRP (Fast Reverse Proxy) client library to establish outbound TCP tunnels through NAT firewalls, exposing local Android services to the public internet via a reverse proxy connection maintained by a foreground service.
SmsForwarder is an open-source Android application (available at pppscn/SmsForwarder) that forwards SMS messages to various destinations. To overcome NAT traversal challenges and allow remote access to devices behind corporate or home firewalls, the project integrates a complete FRP (Fast Reverse Proxy) client implementation that wraps the native frpclib binary, enabling seamless reverse proxy connectivity without manual port forwarding.
What is FRP and Why NAT Traversal Matters
FRP (Fast Reverse Proxy) is a popular open-source tool that exposes local servers behind NAT or firewalls to the public internet. Traditional port forwarding requires router configuration, but FRP works by establishing an outbound TCP connection from the client to a public FRP server (frps). Because the connection originates from inside the network, it traverses NAT automatically. The public server then forwards inbound traffic to the device, creating a secure reverse proxy tunnel.
In SmsForwarder, this integration allows the app to expose local web services or API endpoints from Android devices that are typically unreachable due to carrier-grade NAT or restrictive firewalls.
Core Architecture Components
The FRP integration in SmsForwarder follows a layered architecture that combines native binaries, Room database persistence, and Android foreground services.
The Native frpclib Library (app/libs/frpclib.aar)
At the foundation lies the pre-built native library frpclib.aar, packaged in app/libs/. This binary contains the complete FRP client implementation compiled for Android architectures. The app loads this library at runtime and exposes three critical static methods:
Frpclib.runContent(uid, config)– Initializes and starts the FRP client with a specific configurationFrpclib.isRunning(uid)– Checks if a specific FRP profile is currently activeFrpclib.close(uid)– Terminates the running FRP client instance
Configuration Management (Frpc Entity)
Configurations are persisted using the Frpc entity defined in app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt. This Room database entity stores:
uid: Unique identifier for the profilename: Human-readable labelconfig: Raw TOML configuration string (typically mirroringfrpc.tomlstructure)autorun: Flag indicating whether the profile should start automatically when the app launchestime: Creation timestamp
The entity includes a computed status property that queries Frpclib.isRunning(uid) to reflect real-time connection state in the UI.
User Interface (FrpcFragment)
The FrpcFragment located at app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt serves as the primary management interface. It displays saved profiles, handles start/stop actions, and coordinates with the background service. When a user initiates a connection, the fragment:
- Validates that
App.FrpclibInitedis true (native library loaded) - Ensures
ForegroundServiceis running - Calls
Frpclib.isRunning(uid)to determine whether to start or stop - Dispatches events via
LiveEventBusto trigger the actual connection
Background Execution (ForegroundService)
To prevent Android from killing the FRP process during memory pressure, SmsForwarder implements ForegroundService in app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt. This service maintains a persistent notification and hosts the actual native client execution. It listens for INTENT_FRPC_APPLY_FILE broadcast events, which signal the service to invoke Frpclib.runContent() with the specified configuration.
How the FRP Client Lifecycle Works
The integration follows a precise orchestration between UI components and background execution:
-
Configuration Phase: Users create profiles using the default template from
app/src/main/res/raw/frpc.toml, which defines the remotefrpsendpoint, authentication tokens, and tunneling rules. -
Service Initialization: When a user enables a profile,
FrpcUtils.waitService()(defined inapp/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt) ensures the foreground service is running before proceeding. -
Connection Establishment: The UI posts a
LiveEventBusevent withINTENT_FRPC_APPLY_FILE, which the foreground service receives. The service then callsFrpclib.runContent(uid, config), spawning the nativefrpcclient process. -
NAT Traversal: The native client establishes an outbound TCP connection to the configured
frpsserver. Because this connection originates from the device, it bypasses inbound firewall restrictions and NAT boundaries. -
Status Monitoring: The UI observes
LiveEventBusevents such asEVENT_FRPC_RUNNING_SUCCESSandEVENT_FRPC_RUNNING_ERRORto update connection status indicators in real-time. -
Auto-Start Behavior: Profiles with
autorun = 1are automatically initiated when the foreground service starts, ensuring persistent connectivity across device reboots or app restarts.
Key Source Files and Implementation Details
| File Path | Purpose |
|---|---|
app/src/main/res/raw/frpc.toml |
Default configuration template for new FRP profiles |
app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt |
Room entity storing FRP configuration and metadata |
app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt |
Main UI for managing FRP profiles and connection states |
app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcEditFragment.kt |
Configuration editor for raw TOML settings |
app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt |
Foreground service hosting the native FRP process |
app/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt |
Helper utilities for service coordination and resource reading |
app/libs/frpclib.aar |
Native FRP client library binary |
Practical Implementation Examples
The following Kotlin examples demonstrate how SmsForwarder interacts with the FRP integration:
Starting a FRP Profile from the UI
// Check library initialization before attempting connection
if (!App.FrpclibInited) {
XToastUtils.error(String.format(getString(R.string.frpclib_download_title), FRPC_LIB_VERSION))
return
}
// Toggle connection state based on current status
if (Frpclib.isRunning(profile.uid)) {
Frpclib.close(profile.uid) // Stop existing connection
} else {
// Trigger start via ForegroundService through event bus
LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE)
.postAcrossProcess(profile.uid)
}
Frpc Entity Definition
@Entity(tableName = "Frpc")
data class Frpc(
@PrimaryKey @ColumnInfo(name = "uid") var uid: String = "",
@ColumnInfo(name = "name") var name: String = "",
@ColumnInfo(name = "config") var config: String = "",
@ColumnInfo(name = "autorun", defaultValue = "0") var autorun: Int = 0,
@ColumnInfo(name = "time") var time: Date = Date(),
@Ignore var connecting: Boolean = false
) {
// Computed status checks native library state
val status: Int
get() = if (connecting || (App.FrpclibInited && Frpclib.isRunning(uid)))
STATUS_ON else STATUS_OFF
}
Service Coordination Helper
fun startFrpcWhenReady(uid: String, context: Context) {
FrpcUtils.waitService(ForegroundService::class.java.name, context)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CompletableObserver {
override fun onSubscribe(d: Disposable) {}
override fun onComplete() {
// Service confirmed running; trigger FRP start
LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE)
.postAcrossProcess(uid)
}
override fun onError(e: Throwable) {
// Handle service initialization failure
}
})
}
Summary
- SmsForwarder integrates FRP through a native
frpclib.aarwrapper that exposesrunContent(),isRunning(), andclose()methods. - Configurations are stored as Room database entities in the
Frpctable, supporting auto-start functionality via theautoruncolumn. - The ForegroundService maintains persistent connections by hosting the native process in a foreground notification, preventing Android system termination.
- NAT traversal is achieved through outbound TCP connections to remote
frpsservers, eliminating the need for router port forwarding. - Real-time status updates flow through LiveEventBus events, keeping the UI synchronized with background connection states.
Frequently Asked Questions
What is the frpclib.aar file in SmsForwarder?
The frpclib.aar file in app/libs/ is the pre-compiled Android library containing the native FRP (Fast Reverse Proxy) client implementation. This binary provides the core tunneling functionality that allows SmsForwarder to establish reverse proxy connections through NAT firewalls. The Kotlin wrapper class Frpclib loads this native library and exposes managed methods for starting, stopping, and querying connection status.
How does SmsForwarder maintain FRP connections when the app is backgrounded?
SmsForwarder uses a ForegroundService (ForegroundService.kt) that runs with a persistent notification, keeping the app in the foreground service state. This prevents the Android system from killing the process when the app is not visible. The service hosts the native frpc client process, ensuring the reverse proxy tunnel remains active even when the user switches to other applications or the device enters doze mode.
Can FRP profiles auto-start when the app launches?
Yes, SmsForwarder supports auto-start functionality through the autorun field in the Frpc entity (defined in app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt). When the foreground service initializes, it checks for profiles where autorun = 1 and automatically initiates connections for those configurations. This ensures critical tunnels are re-established after device reboots or app restarts without manual intervention.
Where is the FRP configuration stored in SmsForwarder?
FRP configurations are stored in SQLite via Room in the Frpc table, with the raw TOML configuration string saved in the config column. Additionally, a default template is included as a raw resource at app/src/main/res/raw/frpc.toml, which serves as the baseline when users create new profiles through the FrpcEditFragment interface.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →