mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 07:34:36 +03:00
feat: bypass Apps Script tunnel for DoH endpoints on TCP/443
This commit is contained in:
@@ -104,6 +104,23 @@ data class MhrvConfig(
|
||||
*/
|
||||
val passthroughHosts: List<String> = emptyList(),
|
||||
|
||||
/**
|
||||
* Opt-out for the DoH bypass. The Rust default is to bypass DoH
|
||||
* traffic (chrome.cloudflare-dns.com, dns.google, etc.) directly
|
||||
* instead of routing it through the Apps Script tunnel — DoH
|
||||
* already encrypts queries, so the tunnel was just adding ~2 s
|
||||
* per name lookup with no real privacy gain. Set this to true to
|
||||
* keep DoH inside the tunnel. See `src/config.rs` `tunnel_doh`.
|
||||
*/
|
||||
val tunnelDoh: Boolean = false,
|
||||
|
||||
/**
|
||||
* Extra hostnames added to the built-in DoH default list. Same
|
||||
* matching shape as `passthroughHosts` (exact or leading-dot
|
||||
* suffix). Use to cover private / enterprise DoH endpoints.
|
||||
*/
|
||||
val bypassDohHosts: List<String> = emptyList(),
|
||||
|
||||
/** VPN_TUN (everything routed) vs PROXY_ONLY (user configures per-app). */
|
||||
val connectionMode: ConnectionMode = ConnectionMode.VPN_TUN,
|
||||
|
||||
@@ -186,6 +203,18 @@ data class MhrvConfig(
|
||||
if (passthroughHosts.isNotEmpty()) {
|
||||
put("passthrough_hosts", JSONArray().apply { passthroughHosts.forEach { put(it) } })
|
||||
}
|
||||
if (tunnelDoh) put("tunnel_doh", true)
|
||||
// Trim/drop-empty/dedupe before serializing — symmetric with the
|
||||
// read-side normalization in loadFromJson(), so a user typing
|
||||
// " doh.foo " or accidentally adding a duplicate doesn't end up
|
||||
// in the saved JSON.
|
||||
val cleanBypassDohHosts = bypassDohHosts
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.distinct()
|
||||
if (cleanBypassDohHosts.isNotEmpty()) {
|
||||
put("bypass_doh_hosts", JSONArray().apply { cleanBypassDohHosts.forEach { put(it) } })
|
||||
}
|
||||
|
||||
// Phone-scoped scan defaults. We don't expose these in the UI
|
||||
// because a phone isn't where you'd run a full /16 scan; users
|
||||
@@ -277,6 +306,14 @@ object ConfigStore {
|
||||
if (cfg.parallelRelay != defaults.parallelRelay) obj.put("parallel_relay", cfg.parallelRelay)
|
||||
if (cfg.upstreamSocks5.isNotBlank()) obj.put("upstream_socks5", cfg.upstreamSocks5)
|
||||
if (cfg.passthroughHosts.isNotEmpty()) obj.put("passthrough_hosts", JSONArray().apply { cfg.passthroughHosts.forEach { put(it) } })
|
||||
if (cfg.tunnelDoh != defaults.tunnelDoh) obj.put("tunnel_doh", cfg.tunnelDoh)
|
||||
val cleanBypassDohHosts = cfg.bypassDohHosts
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.distinct()
|
||||
if (cleanBypassDohHosts.isNotEmpty()) {
|
||||
obj.put("bypass_doh_hosts", JSONArray().apply { cleanBypassDohHosts.forEach { put(it) } })
|
||||
}
|
||||
|
||||
// Compress with DEFLATE then base64.
|
||||
val jsonBytes = obj.toString().toByteArray(Charsets.UTF_8)
|
||||
@@ -367,6 +404,10 @@ object ConfigStore {
|
||||
passthroughHosts = obj.optJSONArray("passthrough_hosts")?.let { arr ->
|
||||
buildList { for (i in 0 until arr.length()) add(arr.optString(i)) }
|
||||
}?.filter { it.isNotBlank() }.orEmpty(),
|
||||
tunnelDoh = obj.optBoolean("tunnel_doh", false),
|
||||
bypassDohHosts = obj.optJSONArray("bypass_doh_hosts")?.let { arr ->
|
||||
buildList { for (i in 0 until arr.length()) add(arr.optString(i)) }
|
||||
}?.filter { it.isNotBlank() }.orEmpty(),
|
||||
connectionMode = when (obj.optString("connection_mode", "vpn_tun")) {
|
||||
"proxy_only" -> ConnectionMode.PROXY_ONLY
|
||||
else -> ConnectionMode.VPN_TUN
|
||||
|
||||
Reference in New Issue
Block a user