mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
Connlib/forward dns (#2325)
With this we implement DNS forwarding that's specified in #2043 This also solve the DNS story in Android. For the headless client in Linux we still need to implement split dns, but we can make do with this, specially, we can read from resolvconf and use the forward DNS (not ideal but can work if we want a beta headless client). For the resolver I used `trusted-proto-resolver`. The other options were: * Using `domain`'s resolver but while it could work for now, it's no ideal for this since it doesn't support DoH or DoT and doesn't provide us with a DNS cache. * Using `trusted-proto-client`, it doesn't provide us with a DNS cache, though we could eventually replace it since it provides a way to access the underlying buffer which could make our code a bit simpler. * Writing our own. While we could make the API ideal, this is too much work for beta. @pratikvelani I did some refactor in the kotlin side so we can return an array of bytearrays so that we don't require parsing on connlib side, I also tried to make the dns server detector a bit simpler please take a look it's my first time doing kotlin @thomaseizinger please take a look specially at the first commit, I tried to integrate with the `poll_events` and the `ClientState`.
This commit is contained in:
@@ -124,8 +124,8 @@ class TunnelService : VpnService() {
|
||||
return fd
|
||||
}
|
||||
|
||||
override fun getSystemDefaultResolvers(): String {
|
||||
return moshi.adapter<Array<String>>().toJson(DnsServersDetector(this@TunnelService).servers)
|
||||
override fun getSystemDefaultResolvers(): Array<ByteArray> {
|
||||
return DnsServersDetector(this@TunnelService).servers.map { it.address }.toTypedArray()
|
||||
}
|
||||
|
||||
override fun onDisconnect(error: String?): Boolean {
|
||||
|
||||
@@ -27,5 +27,5 @@ interface ConnlibCallback {
|
||||
|
||||
fun onError(error: String): Boolean
|
||||
|
||||
fun getSystemDefaultResolvers(): String
|
||||
fun getSystemDefaultResolvers(): Array<ByteArray>
|
||||
}
|
||||
|
||||
@@ -40,36 +40,23 @@ class DnsServersDetector(
|
||||
) {
|
||||
//region - public //////////////////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////////////////////
|
||||
val servers: Array<String>
|
||||
val servers: Set<InetAddress>
|
||||
/**
|
||||
* Returns android DNS servers used for current connected network
|
||||
* @return Dns servers array
|
||||
*/
|
||||
get() {
|
||||
// use connectivity manager
|
||||
serversMethodConnectivityManager?.run {
|
||||
if (isNotEmpty()) {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
// detect android DNS servers by executing getprop string command in a separate process
|
||||
// This method fortunately works in Oreo too but many people may want to avoid exec
|
||||
// so it's used only as a failsafe scenario
|
||||
serversMethodExec?.run {
|
||||
if (isNotEmpty()) {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back on factory DNS servers
|
||||
return FACTORY_DNS_SERVERS
|
||||
return serversMethodConnectivityManager
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?: serversMethodExec
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?: FACTORY_DNS_SERVERS
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region - private /////////////////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private val serversMethodConnectivityManager: Array<String>?
|
||||
private val serversMethodConnectivityManager: Set<InetAddress>?
|
||||
/**
|
||||
* Detect android DNS servers by using connectivity manager
|
||||
*
|
||||
@@ -81,8 +68,8 @@ class DnsServersDetector(
|
||||
// This code only works on LOLLIPOP and higher
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
try {
|
||||
val priorityServersArrayList = ArrayList<String>()
|
||||
val serversArrayList = ArrayList<String>()
|
||||
val priorityServers: MutableSet<InetAddress> = HashSet(10)
|
||||
val servers: MutableSet<InetAddress> = HashSet(10)
|
||||
val connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||
if (connectivityManager != null) {
|
||||
@@ -93,37 +80,20 @@ class DnsServersDetector(
|
||||
val networkInfo = connectivityManager.getNetworkInfo(network)
|
||||
if (networkInfo!!.isConnected) {
|
||||
val linkProperties = connectivityManager.getLinkProperties(network)
|
||||
val dnsServersList = linkProperties!!.dnsServers
|
||||
val dnsServersList = linkProperties!!.dnsServers.toSet()
|
||||
|
||||
// Prioritize the DNS servers for link which have a default route
|
||||
if (linkPropertiesHasDefaultRoute(linkProperties)) {
|
||||
for (element in dnsServersList) {
|
||||
val dnsHost = element.hostAddress
|
||||
dnsHost?.let {
|
||||
priorityServersArrayList.add(it)
|
||||
}
|
||||
}
|
||||
priorityServers += dnsServersList
|
||||
} else {
|
||||
for (element in dnsServersList) {
|
||||
val dnsHost = element.hostAddress
|
||||
dnsHost?.let {
|
||||
serversArrayList.add(it)
|
||||
}
|
||||
}
|
||||
servers += dnsServersList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append secondary arrays only if priority is empty
|
||||
if (priorityServersArrayList.isEmpty()) {
|
||||
priorityServersArrayList.addAll(serversArrayList)
|
||||
}
|
||||
|
||||
// Stop here if we have at least one DNS server
|
||||
if (priorityServersArrayList.size > 0) {
|
||||
return priorityServersArrayList.toTypedArray()
|
||||
}
|
||||
return priorityServers.takeIf { it.isNotEmpty() } ?: servers
|
||||
} catch (ex: Exception) {
|
||||
Log.d(
|
||||
TAG,
|
||||
@@ -133,10 +103,9 @@ class DnsServersDetector(
|
||||
}
|
||||
}
|
||||
|
||||
// Failure
|
||||
return null
|
||||
}
|
||||
private val serversMethodExec: Array<String>?
|
||||
private val serversMethodExec: Set<InetAddress>?
|
||||
/**
|
||||
* Detect android DNS servers by executing getprop string command in a separate process
|
||||
*
|
||||
@@ -152,15 +121,11 @@ class DnsServersDetector(
|
||||
val process = Runtime.getRuntime().exec("getprop")
|
||||
val inputStream = process.inputStream
|
||||
val lineNumberReader = LineNumberReader(InputStreamReader(inputStream))
|
||||
val serversSet = methodExecParseProps(lineNumberReader)
|
||||
if (serversSet.isNotEmpty()) {
|
||||
return serversSet.toTypedArray()
|
||||
}
|
||||
return methodExecParseProps(lineNumberReader)
|
||||
} catch (ex: Exception) {
|
||||
Log.d(TAG, "Exception in getServersMethodExec", ex)
|
||||
}
|
||||
|
||||
// Failed
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -171,9 +136,9 @@ class DnsServersDetector(
|
||||
* @throws Exception
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
private fun methodExecParseProps(lineNumberReader: BufferedReader): Set<String> {
|
||||
private fun methodExecParseProps(lineNumberReader: BufferedReader): Set<InetAddress> {
|
||||
var line: String
|
||||
val serversSet: MutableSet<String> = HashSet(10)
|
||||
val serversSet: MutableSet<InetAddress> = HashSet(10)
|
||||
while (lineNumberReader.readLine().also { line = it } != null) {
|
||||
val split = line.indexOf(METHOD_EXEC_PROP_DELIM)
|
||||
if (split == -1) {
|
||||
@@ -196,11 +161,10 @@ class DnsServersDetector(
|
||||
property.endsWith(".dns2") || property.endsWith(".dns3") ||
|
||||
property.endsWith(".dns4")
|
||||
) {
|
||||
InetAddress.getByName(value).hostAddress?.takeIf { it.isNotEmpty() }?.let {
|
||||
serversSet.add(it)
|
||||
}
|
||||
serversSet.add(InetAddress.getByName(value))
|
||||
}
|
||||
}
|
||||
|
||||
return serversSet
|
||||
}
|
||||
|
||||
@@ -226,9 +190,9 @@ class DnsServersDetector(
|
||||
* Can be set to null if you want caller to fail in this situation.
|
||||
*/
|
||||
private val FACTORY_DNS_SERVERS =
|
||||
arrayOf(
|
||||
"8.8.8.8",
|
||||
"8.8.4.4",
|
||||
setOf(
|
||||
InetAddress.getByName("8.8.8.8"),
|
||||
InetAddress.getByName("8.8.4.4"),
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
313
rust/Cargo.lock
generated
313
rust/Cargo.lock
generated
@@ -126,9 +126,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -170,15 +170,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
@@ -302,14 +302,14 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -470,9 +470,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
@@ -550,9 +550,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@@ -735,7 +735,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -811,6 +811,8 @@ dependencies = [
|
||||
"chrono",
|
||||
"connlib-shared",
|
||||
"firezone-tunnel",
|
||||
"hickory-resolver",
|
||||
"parking_lot",
|
||||
"reqwest",
|
||||
"secrecy",
|
||||
"serde",
|
||||
@@ -837,6 +839,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"hickory-resolver",
|
||||
"ip_network",
|
||||
"log",
|
||||
"os_info",
|
||||
@@ -1033,7 +1036,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1128,7 +1131,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1191,6 +1194,18 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
@@ -1206,30 +1221,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.3"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
|
||||
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
@@ -1300,6 +1304,7 @@ dependencies = [
|
||||
"futures",
|
||||
"futures-bounded 0.2.0",
|
||||
"futures-util",
|
||||
"hickory-resolver",
|
||||
"ip_network",
|
||||
"ip_network_table",
|
||||
"itertools 0.11.0",
|
||||
@@ -1374,7 +1379,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "futures-bounded"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/libp2p/rust-libp2p?branch=feat/stream-map#0c0349221f3daa697ae871ab6dba5c1f84e84b10"
|
||||
source = "git+https://github.com/libp2p/rust-libp2p?branch=feat/stream-map#07ff4c28f6d7d878641fe9bf464b27f84191dc77"
|
||||
dependencies = [
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
@@ -1421,7 +1426,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1584,6 +1589,51 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||
|
||||
[[package]]
|
||||
name = "hickory-proto"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if 1.0.0",
|
||||
"data-encoding",
|
||||
"enum-as-inner",
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"idna",
|
||||
"ipnet",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hickory-resolver"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"futures-util",
|
||||
"hickory-proto",
|
||||
"ipconfig",
|
||||
"lru-cache",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.3"
|
||||
@@ -1620,6 +1670,17 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
@@ -1712,16 +1773,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
version = "0.1.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
|
||||
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows 0.48.0",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1824,6 +1885,18 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d"
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
|
||||
dependencies = [
|
||||
"socket2 0.5.4",
|
||||
"widestring",
|
||||
"windows-sys 0.48.0",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.8.0"
|
||||
@@ -1938,9 +2011,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
|
||||
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"windows-sys 0.48.0",
|
||||
@@ -1948,9 +2021,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -1960,9 +2039,9 @@ checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
@@ -1974,6 +2053,15 @@ version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.2.3"
|
||||
@@ -1992,6 +2080,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@@ -2003,16 +2097,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"digest",
|
||||
]
|
||||
|
||||
@@ -2024,9 +2119,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.3"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -2165,7 +2260,7 @@ version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
]
|
||||
@@ -2219,9 +2314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
@@ -2379,9 +2474,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "3.9.1"
|
||||
version = "3.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06"
|
||||
checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -2436,13 +2531,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.8"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
@@ -2517,7 +2612,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2566,7 +2661,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2627,9 +2722,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.67"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2642,7 +2737,7 @@ checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"rand",
|
||||
@@ -2773,15 +2868,24 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.5"
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.3.8",
|
||||
"regex-syntax 0.7.5",
|
||||
"regex-automata 0.4.3",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2795,13 +2899,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.8"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.5",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2816,6 +2920,12 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "relay"
|
||||
version = "1.20231001.0"
|
||||
@@ -2900,6 +3010,16 @@ dependencies = [
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resolv-conf"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
|
||||
dependencies = [
|
||||
"hostname",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.3.1"
|
||||
@@ -3016,7 +3136,7 @@ version = "0.38.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -3208,9 +3328,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -3229,7 +3349,7 @@ checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3285,9 +3405,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
@@ -3414,7 +3534,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3425,7 +3545,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3533,9 +3653,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.37"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3589,7 +3709,7 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@@ -3612,7 +3732,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3632,7 +3752,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3726,7 +3846,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3882,7 +4002,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4256,7 +4376,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4290,7 +4410,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4548,6 +4668,12 @@ dependencies = [
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -4579,15 +4705,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.51.1"
|
||||
@@ -4756,10 +4873,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29b83b0eca06dd125dbcd48a45327c708a6da8aada3d95a3f06db0ce4b17e0d4"
|
||||
dependencies = [
|
||||
"c2rust-bitfields",
|
||||
"libloading 0.8.0",
|
||||
"libloading 0.8.1",
|
||||
"log",
|
||||
"thiserror",
|
||||
"windows 0.51.1",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4837,5 +4954,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
@@ -22,6 +22,7 @@ backoff = { version = "0.4", features = ["tokio"] }
|
||||
tracing = { version = "0.1.39" }
|
||||
tracing-subscriber = { version = "0.3.17", features = ["parking_lot"] }
|
||||
secrecy = "0.8"
|
||||
hickory-resolver = { version = "0.24", features = ["tokio-runtime"] }
|
||||
|
||||
connlib-client-android = { path = "connlib/clients/android"}
|
||||
connlib-client-apple = { path = "connlib/clients/apple"}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
use connlib_client_shared::{file_logger, Callbacks, Error, ResourceDescription, Session};
|
||||
use ip_network::IpNetwork;
|
||||
use jni::{
|
||||
objects::{GlobalRef, JClass, JObject, JString, JValue},
|
||||
objects::{GlobalRef, JByteArray, JClass, JObject, JObjectArray, JString, JValue, JValueGen},
|
||||
strings::JNIString,
|
||||
JNIEnv, JavaVM,
|
||||
};
|
||||
use secrecy::SecretString;
|
||||
use std::path::Path;
|
||||
use std::sync::OnceLock;
|
||||
use std::{net::IpAddr, path::Path};
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::RawFd,
|
||||
@@ -305,6 +305,42 @@ impl Callbacks for CallbackHandler {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn get_system_default_resolvers(&self) -> Result<Option<Vec<IpAddr>>, Self::Error> {
|
||||
self.env(|mut env| {
|
||||
let name = "getSystemDefaultResolvers";
|
||||
let addrs = env
|
||||
.call_method(&self.callback_handler, name, "()[[B", &[])
|
||||
.and_then(JValueGen::l)
|
||||
.and_then(|arr| convert_byte_array_array(&mut env, arr.into()))
|
||||
.map_err(|source| CallbackError::CallMethodFailed { name, source })?;
|
||||
|
||||
Ok(Some(addrs.iter().filter_map(|v| to_ip(v)).collect()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn to_ip(val: &[u8]) -> Option<IpAddr> {
|
||||
let addr: Option<[u8; 4]> = val.try_into().ok();
|
||||
if let Some(addr) = addr {
|
||||
return Some(addr.into());
|
||||
}
|
||||
|
||||
let addr: [u8; 16] = val.try_into().ok()?;
|
||||
Some(addr.into())
|
||||
}
|
||||
|
||||
fn convert_byte_array_array(
|
||||
env: &mut JNIEnv,
|
||||
array: JObjectArray,
|
||||
) -> jni::errors::Result<Vec<Vec<u8>>> {
|
||||
let len = env.get_array_length(&array)?;
|
||||
let mut result = Vec::with_capacity(len as usize);
|
||||
for i in 0..len {
|
||||
let arr: JByteArray<'_> = env.get_object_array_element(&array, i)?.into();
|
||||
result.push(env.convert_byte_array(arr)?);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn throw(env: &mut JNIEnv, class: &str, msg: impl Into<JNIString>) {
|
||||
|
||||
@@ -27,6 +27,8 @@ time = { version = "0.3.30", features = ["formatting"] }
|
||||
reqwest = { version = "0.11.22", default-features = false, features = ["stream", "rustls-tls"] }
|
||||
tokio-tungstenite = { version = "0.20", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots"] }
|
||||
async-compression = { version = "0.4.3", features = ["tokio", "gzip"] }
|
||||
hickory-resolver = { workspace = true, features = ["tokio-runtime"] }
|
||||
parking_lot = "0.12"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
tracing = { workspace = true, features = ["std", "attributes"] }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_compression::tokio::bufread::GzipEncoder;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::{io, sync::Arc};
|
||||
|
||||
@@ -15,16 +16,52 @@ use connlib_shared::{
|
||||
};
|
||||
|
||||
use firezone_tunnel::{ClientState, Request, Tunnel};
|
||||
use hickory_resolver::config::{NameServerConfig, Protocol, ResolverConfig};
|
||||
use hickory_resolver::TokioAsyncResolver;
|
||||
use reqwest::header::{CONTENT_ENCODING, CONTENT_TYPE};
|
||||
use tokio::io::BufReader;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||
use url::Url;
|
||||
|
||||
const DNS_PORT: u16 = 53;
|
||||
pub struct ControlPlane<CB: Callbacks> {
|
||||
pub tunnel: Arc<Tunnel<CB, ClientState>>,
|
||||
pub phoenix_channel: PhoenixSenderWithTopic,
|
||||
pub tunnel_init: Mutex<bool>,
|
||||
// It's a Mutex<Option<_>> because we need the init message to initialize the resolver
|
||||
// also, in platforms with split DNS and no configured upstream dns this will be None.
|
||||
//
|
||||
// We could still initialize the resolver with no nameservers in those platforms...
|
||||
pub fallback_resolver: parking_lot::Mutex<Option<TokioAsyncResolver>>,
|
||||
}
|
||||
|
||||
fn create_resolver(
|
||||
upstream_dns: Vec<IpAddr>,
|
||||
callbacks: &impl Callbacks,
|
||||
) -> Option<TokioAsyncResolver> {
|
||||
let dns_servers = if upstream_dns.is_empty() {
|
||||
let Ok(Some(dns_servers)) = callbacks.get_system_default_resolvers() else {
|
||||
return None;
|
||||
};
|
||||
if dns_servers.is_empty() {
|
||||
return None;
|
||||
}
|
||||
dns_servers
|
||||
} else {
|
||||
upstream_dns
|
||||
};
|
||||
|
||||
let mut resolver_config = ResolverConfig::new();
|
||||
for ip in dns_servers.iter() {
|
||||
let name_server = NameServerConfig::new(SocketAddr::new(*ip, DNS_PORT), Protocol::Udp);
|
||||
resolver_config.add_name_server(name_server);
|
||||
}
|
||||
|
||||
Some(TokioAsyncResolver::tokio(
|
||||
resolver_config,
|
||||
Default::default(),
|
||||
))
|
||||
}
|
||||
|
||||
impl<CB: Callbacks + 'static> ControlPlane<CB> {
|
||||
@@ -44,6 +81,8 @@ impl<CB: Callbacks + 'static> ControlPlane<CB> {
|
||||
return Err(e);
|
||||
} else {
|
||||
*init = true;
|
||||
*self.fallback_resolver.lock() =
|
||||
create_resolver(interface.upstream_dns, self.tunnel.callbacks());
|
||||
tracing::info!("Firezoned Started!");
|
||||
}
|
||||
} else {
|
||||
@@ -285,6 +324,22 @@ impl<CB: Callbacks + 'static> ControlPlane<CB> {
|
||||
// TODO: Clean up connection in `ClientState` here?
|
||||
}
|
||||
}
|
||||
firezone_tunnel::Event::DnsQuery(query) => {
|
||||
// Until we handle it better on a gateway-like eventloop, making sure not to block the loop
|
||||
let Some(resolver) = self.fallback_resolver.lock().clone() else {
|
||||
return;
|
||||
};
|
||||
let tunnel = self.tunnel.clone();
|
||||
tokio::spawn(async move {
|
||||
let response = resolver.lookup(query.name, query.record_type).await;
|
||||
if let Err(err) = tunnel
|
||||
.write_dns_lookup_response(response, query.query)
|
||||
.await
|
||||
{
|
||||
tracing::error!(err = ?err, "DNS lookup failed: {err:#}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,12 +158,12 @@ where
|
||||
tunnel: Arc::new(tunnel),
|
||||
phoenix_channel: connection.sender_with_topic("client".to_owned()),
|
||||
tunnel_init: Mutex::new(false),
|
||||
fallback_resolver: parking_lot::Mutex::new(None),
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut log_stats_interval = tokio::time::interval(Duration::from_secs(10));
|
||||
let mut upload_logs_interval = upload_interval();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some((msg, reference)) = control_plane_receiver.recv() => {
|
||||
|
||||
@@ -32,6 +32,7 @@ url = { version = "2.4.1", default-features = false }
|
||||
uuid = { version = "1.5", default-features = false, features = ["std", "v4", "serde"] }
|
||||
webrtc = { version = "0.8" }
|
||||
ring = "0.17"
|
||||
hickory-resolver = { workspace = true }
|
||||
|
||||
# Needed for Android logging until tracing is working
|
||||
log = "0.4"
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::messages::ResourceDescription;
|
||||
use ip_network::IpNetwork;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Avoids having to map types for Windows
|
||||
@@ -71,6 +71,11 @@ pub trait Callbacks: Clone + Send + Sync {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the system's default resolver iff split dns isn't available for platform
|
||||
fn get_system_default_resolvers(&self) -> Result<Option<Vec<IpAddr>>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn roll_log_file(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::messages::ResourceDescription;
|
||||
use crate::{Callbacks, Error, Result};
|
||||
use ip_network::IpNetwork;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
// Avoids having to map types for Windows
|
||||
type RawFd = i32;
|
||||
@@ -93,4 +93,12 @@ impl<CB: Callbacks> Callbacks for CallbackErrorFacade<CB> {
|
||||
// There's nothing we really want to do if `on_error` fails.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_system_default_resolvers(
|
||||
&self,
|
||||
) -> std::result::Result<Option<Vec<IpAddr>>, Self::Error> {
|
||||
self.0
|
||||
.get_system_default_resolvers()
|
||||
.map_err(|err| Error::GetSystemDefaultResolverFailed(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ pub enum ConnlibError {
|
||||
OnRemoveRouteFailed(String),
|
||||
#[error("`on_update_resources` failed: {0}")]
|
||||
OnUpdateResourcesFailed(String),
|
||||
#[error("`get_system_default_resolvers` failed: {0}")]
|
||||
GetSystemDefaultResolverFailed(String),
|
||||
/// Glob for errors without a type.
|
||||
#[error("Other error: {0}")]
|
||||
Other(&'static str),
|
||||
@@ -126,6 +128,13 @@ pub enum ConnlibError {
|
||||
/// Any parse error
|
||||
#[error("parse error")]
|
||||
ParseError,
|
||||
/// DNS lookup error
|
||||
#[error("Error with the DNS fallback lookup")]
|
||||
DNSFallback(#[from] hickory_resolver::error::ResolveError),
|
||||
#[error("Error with the DNS fallback lookup")]
|
||||
DNSFallbackKind(#[from] hickory_resolver::error::ResolveErrorKind),
|
||||
#[error("DNS proto error")]
|
||||
DnsProtoError(#[from] hickory_resolver::proto::error::ProtoError),
|
||||
}
|
||||
|
||||
impl ConnlibError {
|
||||
|
||||
@@ -26,6 +26,7 @@ boringtun = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
pnet_packet = { version = "0.34" }
|
||||
futures-bounded = { git = "https://github.com/libp2p/rust-libp2p", branch = "feat/stream-map" }
|
||||
hickory-resolver = { workspace = true }
|
||||
|
||||
# TODO: research replacing for https://github.com/algesten/str0m
|
||||
webrtc = { version = "0.8" }
|
||||
|
||||
59
rust/connlib/tunnel/src/bounded_queue.rs
Normal file
59
rust/connlib/tunnel/src/bounded_queue.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use core::fmt;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
// Simple bounded queue for one-time events
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BoundedQueue<T> {
|
||||
queue: VecDeque<T>,
|
||||
limit: usize,
|
||||
waker: Option<Waker>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct Full;
|
||||
|
||||
impl fmt::Display for Full {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Queue is full")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BoundedQueue<T> {
|
||||
pub(crate) fn with_capacity(cap: usize) -> BoundedQueue<T> {
|
||||
BoundedQueue {
|
||||
queue: VecDeque::with_capacity(cap),
|
||||
limit: cap,
|
||||
waker: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poll(&mut self, cx: &Context) -> Poll<T> {
|
||||
if let Some(front) = self.queue.pop_front() {
|
||||
return Poll::Ready(front);
|
||||
}
|
||||
|
||||
self.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
fn at_capacity(&self) -> bool {
|
||||
self.queue.len() == self.limit
|
||||
}
|
||||
|
||||
pub(crate) fn push_back(&mut self, x: T) -> Result<(), Full> {
|
||||
if self.at_capacity() {
|
||||
return Err(Full);
|
||||
}
|
||||
|
||||
self.queue.push_back(x);
|
||||
|
||||
if let Some(ref waker) = self.waker {
|
||||
waker.wake_by_ref();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::bounded_queue::BoundedQueue;
|
||||
use crate::device_channel::{create_iface, DeviceIo};
|
||||
use crate::ip_packet::IpPacket;
|
||||
use crate::peer::Peer;
|
||||
use crate::resource_table::ResourceTable;
|
||||
use crate::{
|
||||
dns, peer_by_ip, tokio_util, Device, Event, PeerConfig, RoleState, Tunnel,
|
||||
ICE_GATHERING_TIMEOUT_SECONDS, MAX_CONCURRENT_ICE_GATHERING, MAX_UDP_SIZE,
|
||||
dns, peer_by_ip, tokio_util, Device, DnsQuery, Event, PeerConfig, RoleState, Tunnel,
|
||||
DNS_QUERIES_QUEUE_SIZE, ICE_GATHERING_TIMEOUT_SECONDS, MAX_CONCURRENT_ICE_GATHERING,
|
||||
MAX_UDP_SIZE,
|
||||
};
|
||||
use boringtun::x25519::{PublicKey, StaticSecret};
|
||||
use connlib_shared::error::{ConnlibError as Error, ConnlibError};
|
||||
@@ -15,6 +18,7 @@ use connlib_shared::{Callbacks, DNS_SENTINEL};
|
||||
use futures::channel::mpsc::Receiver;
|
||||
use futures::stream;
|
||||
use futures_bounded::{PushError, StreamMap};
|
||||
use hickory_resolver::lookup::Lookup;
|
||||
use ip_network::IpNetwork;
|
||||
use ip_network_table::IpNetworkTable;
|
||||
use std::collections::hash_map::Entry;
|
||||
@@ -65,6 +69,46 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes the response to a DNS lookup
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn write_dns_lookup_response(
|
||||
self: &Arc<Self>,
|
||||
response: hickory_resolver::error::ResolveResult<Lookup>,
|
||||
query: IpPacket<'static>,
|
||||
) -> connlib_shared::Result<()> {
|
||||
let Some(mut message) = dns::as_dns_message(&query) else {
|
||||
debug_assert!(false, "The original message should be a DNS query for us to ever call write_dns_lookup_response");
|
||||
return Ok(());
|
||||
};
|
||||
let response = match response.map_err(|err| err.kind().clone()) {
|
||||
Ok(response) => message.add_answers(response.records().to_vec()),
|
||||
Err(hickory_resolver::error::ResolveErrorKind::NoRecordsFound {
|
||||
soa,
|
||||
response_code,
|
||||
..
|
||||
}) => {
|
||||
if let Some(soa) = soa {
|
||||
message.add_name_server(soa.clone().into_record_of_rdata());
|
||||
}
|
||||
|
||||
message.set_response_code(response_code)
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(pkt) = dns::build_response(query, response.to_vec()?) {
|
||||
let Some(ref device) = *self.device.read().await else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
send_dns_packet(&device.io, pkt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the interface configuration and starts background tasks.
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn set_interface(
|
||||
@@ -133,15 +177,20 @@ where
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if let Some(dns_packet) =
|
||||
dns::parse(&tunnel.role_state.lock().resources, packet.as_immutable())
|
||||
{
|
||||
if let Err(e) = send_dns_packet(&device_writer, dns_packet) {
|
||||
tracing::error!(err = %e, "failed to send DNS packet");
|
||||
let _ = tunnel.callbacks.on_error(&e.into());
|
||||
}
|
||||
match dns::parse(&tunnel.role_state.lock().resources, packet.as_immutable()) {
|
||||
Some(dns::ResolveStrategy::LocalResponse(pkt)) => {
|
||||
if let Err(e) = send_dns_packet(&device_writer, pkt) {
|
||||
tracing::error!(err = %e, "failed to send DNS packet");
|
||||
let _ = tunnel.callbacks.on_error(&e.into());
|
||||
}
|
||||
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
Some(dns::ResolveStrategy::ForwardQuery(query)) => {
|
||||
tunnel.role_state.lock().dns_query(query);
|
||||
continue;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let dest = packet.destination();
|
||||
@@ -166,10 +215,13 @@ where
|
||||
|
||||
fn send_dns_packet(device_writer: &DeviceIo, packet: dns::Packet) -> io::Result<()> {
|
||||
match packet {
|
||||
dns::Packet::Ipv4(r) => device_writer.write4(&r[..])?,
|
||||
dns::Packet::Ipv6(r) => device_writer.write6(&r[..])?,
|
||||
};
|
||||
|
||||
dns::Packet::Ipv4(r) => {
|
||||
device_writer.write4(&r[..])?;
|
||||
}
|
||||
dns::Packet::Ipv6(r) => {
|
||||
device_writer.write6(&r[..])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -188,6 +240,7 @@ pub struct ClientState {
|
||||
pub gateway_public_keys: HashMap<GatewayId, PublicKey>,
|
||||
resources_gateways: HashMap<ResourceId, GatewayId>,
|
||||
resources: ResourceTable<ResourceDescription>,
|
||||
dns_queries: BoundedQueue<DnsQuery<'static>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -416,6 +469,12 @@ impl ClientState {
|
||||
IpAddr::V6(ipv6) => self.resources.get_by_ip(ipv6),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dns_query(&mut self, query: DnsQuery) {
|
||||
if self.dns_queries.push_back(query.into_owned()).is_err() {
|
||||
tracing::warn!("Too many DNS queries, dropping new ones");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClientState {
|
||||
@@ -432,6 +491,7 @@ impl Default for ClientState {
|
||||
gateway_public_keys: Default::default(),
|
||||
resources_gateways: Default::default(),
|
||||
resources: Default::default(),
|
||||
dns_queries: BoundedQueue::with_capacity(DNS_QUERIES_QUEUE_SIZE),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +509,8 @@ impl RoleState for ClientState {
|
||||
})
|
||||
}
|
||||
Poll::Ready((id, Some(Err(e)))) => {
|
||||
tracing::warn!(gateway_id = %id, "ICE gathering timed out: {e}")
|
||||
tracing::warn!(gateway_id = %id, "ICE gathering timed out: {e}");
|
||||
continue;
|
||||
}
|
||||
Poll::Ready((_, None)) => continue,
|
||||
Poll::Pending => {}
|
||||
@@ -494,7 +555,7 @@ impl RoleState for ClientState {
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
||||
return Poll::Pending;
|
||||
return self.dns_queries.poll(cx).map(Event::DnsQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use crate::ip_packet::{to_dns, IpPacket, MutableIpPacket, Version};
|
||||
use crate::resource_table::ResourceTable;
|
||||
use crate::DnsQuery;
|
||||
use connlib_shared::{messages::ResourceDescription, DNS_SENTINEL};
|
||||
use domain::base::{
|
||||
iana::{Class, Rcode, Rtype},
|
||||
Dname, Message, MessageBuilder, ParsedDname, ToDname,
|
||||
Dname, Message, MessageBuilder, ParsedDname, Question, ToDname,
|
||||
};
|
||||
use hickory_resolver::proto::op::Message as TrustDnsMessage;
|
||||
use hickory_resolver::proto::rr::RecordType;
|
||||
use itertools::Itertools;
|
||||
use pnet_packet::{udp::MutableUdpPacket, MutablePacket, Packet as UdpPacket, PacketSize};
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
const DNS_TTL: u32 = 300;
|
||||
const UDP_HEADER_SIZE: usize = 8;
|
||||
@@ -20,16 +24,45 @@ pub(crate) enum Packet {
|
||||
Ipv6(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ResolveStrategy<T, U> {
|
||||
LocalResponse(T),
|
||||
ForwardQuery(U),
|
||||
}
|
||||
|
||||
struct DnsQueryParams {
|
||||
name: String,
|
||||
record_type: RecordType,
|
||||
}
|
||||
|
||||
impl DnsQueryParams {
|
||||
fn into_query(self, query: IpPacket) -> DnsQuery {
|
||||
DnsQuery {
|
||||
name: self.name,
|
||||
record_type: self.record_type,
|
||||
query,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResolveStrategy<T, DnsQueryParams> {
|
||||
fn new(name: String, record_type: Rtype) -> ResolveStrategy<T, DnsQueryParams> {
|
||||
ResolveStrategy::ForwardQuery(DnsQueryParams {
|
||||
name,
|
||||
record_type: u16::from(record_type).into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to support multiple questions/qname in a single query because
|
||||
// nobody does it and since this run with each packet we want to squeeze as much optimization
|
||||
// as we can therefore we won't do it.
|
||||
//
|
||||
// See: https://stackoverflow.com/a/55093896
|
||||
pub(crate) fn parse(
|
||||
pub(crate) fn parse<'a>(
|
||||
resources: &ResourceTable<ResourceDescription>,
|
||||
packet: IpPacket<'_>,
|
||||
) -> Option<Packet> {
|
||||
let version = packet.version();
|
||||
packet: IpPacket<'a>,
|
||||
) -> Option<ResolveStrategy<Packet, DnsQuery<'a>>> {
|
||||
if packet.destination() != IpAddr::from(DNS_SENTINEL) {
|
||||
return None;
|
||||
}
|
||||
@@ -39,58 +72,23 @@ pub(crate) fn parse(
|
||||
return None;
|
||||
}
|
||||
let question = message.first_question()?;
|
||||
let resource = match question.qtype() {
|
||||
Rtype::A | Rtype::Aaaa => resources
|
||||
.get_by_name(&ToDname::to_cow(question.qname()).to_string())
|
||||
.cloned(),
|
||||
Rtype::Ptr => {
|
||||
let dns_parts = ToDname::to_cow(question.qname()).to_string();
|
||||
let mut dns_parts = dns_parts.split('.').rev();
|
||||
if !dns_parts
|
||||
.next()
|
||||
.is_some_and(|d| d == REVERSE_DNS_ADDRESS_END)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let ip: IpAddr = match dns_parts.next() {
|
||||
Some(REVERSE_DNS_ADDRESS_V4) => {
|
||||
let mut ip = [0u8; 4];
|
||||
for i in ip.iter_mut() {
|
||||
*i = dns_parts.next()?.parse().ok()?;
|
||||
}
|
||||
ip.into()
|
||||
}
|
||||
Some(REVERSE_DNS_ADDRESS_V6) => {
|
||||
let mut ip = [0u8; 16];
|
||||
for i in ip.iter_mut() {
|
||||
*i = u8::from_str_radix(
|
||||
&format!("{}{}", dns_parts.next()?, dns_parts.next()?),
|
||||
16,
|
||||
)
|
||||
.ok()?;
|
||||
}
|
||||
ip.into()
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if dns_parts.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
resources.get_by_ip(ip).cloned()
|
||||
let resource = match resource_from_question(resources, &question)? {
|
||||
ResolveStrategy::LocalResponse(resource) => resource,
|
||||
ResolveStrategy::ForwardQuery(params) => {
|
||||
return Some(ResolveStrategy::ForwardQuery(params.into_query(packet)))
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let response = build_dns_with_answer(message, question.qname(), question.qtype(), &resource?)?;
|
||||
let response = build_response(packet, response);
|
||||
response.map(|pkt| match version {
|
||||
Version::Ipv4 => Packet::Ipv4(pkt),
|
||||
Version::Ipv6 => Packet::Ipv6(pkt),
|
||||
})
|
||||
let response = build_dns_with_answer(message, question.qname(), question.qtype(), &resource)?;
|
||||
Some(ResolveStrategy::LocalResponse(build_response(
|
||||
packet, response,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn build_response(original_pkt: IpPacket<'_>, mut dns_answer: Vec<u8>) -> Option<Vec<u8>> {
|
||||
pub(crate) fn build_response(
|
||||
original_pkt: IpPacket<'_>,
|
||||
mut dns_answer: Vec<u8>,
|
||||
) -> Option<Packet> {
|
||||
let version = original_pkt.version();
|
||||
let response_len = dns_answer.len();
|
||||
let original_dgm = original_pkt.as_udp()?;
|
||||
let hdr_len = original_pkt.packet_size() - original_dgm.payload().len();
|
||||
@@ -113,7 +111,10 @@ fn build_response(original_pkt: IpPacket<'_>, mut dns_answer: Vec<u8>) -> Option
|
||||
let udp_checksum = pkt.to_immutable().udp_checksum(&pkt.as_immutable_udp()?);
|
||||
pkt.as_udp()?.set_checksum(udp_checksum);
|
||||
pkt.set_ipv4_checksum();
|
||||
Some(res_buf)
|
||||
match version {
|
||||
Version::Ipv4 => Some(Packet::Ipv4(res_buf)),
|
||||
Version::Ipv6 => Some(Packet::Ipv6(res_buf)),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_dns_with_answer<N>(
|
||||
@@ -161,3 +162,123 @@ where
|
||||
}
|
||||
Some(answer_builder.finish())
|
||||
}
|
||||
|
||||
fn resource_from_question<N: ToDname>(
|
||||
resources: &ResourceTable<ResourceDescription>,
|
||||
question: &Question<N>,
|
||||
) -> Option<ResolveStrategy<ResourceDescription, DnsQueryParams>> {
|
||||
let name = ToDname::to_cow(question.qname()).to_string();
|
||||
let qtype = question.qtype();
|
||||
|
||||
let resource = match qtype {
|
||||
Rtype::A | Rtype::Aaaa => resources.get_by_name(&name),
|
||||
Rtype::Ptr => {
|
||||
let ip = reverse_dns_addr(&name)?;
|
||||
resources.get_by_ip(ip)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
resource
|
||||
.cloned()
|
||||
.map(ResolveStrategy::LocalResponse)
|
||||
.unwrap_or(ResolveStrategy::new(name, qtype))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn as_dns_message(pkt: &IpPacket) -> Option<TrustDnsMessage> {
|
||||
let datagram = pkt.as_udp()?;
|
||||
TrustDnsMessage::from_vec(datagram.payload()).ok()
|
||||
}
|
||||
|
||||
fn reverse_dns_addr(name: &str) -> Option<IpAddr> {
|
||||
let mut dns_parts = name.split('.').rev();
|
||||
if dns_parts.next()? != REVERSE_DNS_ADDRESS_END {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ip: IpAddr = match dns_parts.next()? {
|
||||
REVERSE_DNS_ADDRESS_V4 => reverse_dns_addr_v4(&mut dns_parts)?.into(),
|
||||
REVERSE_DNS_ADDRESS_V6 => reverse_dns_addr_v6(&mut dns_parts)?.into(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if dns_parts.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ip)
|
||||
}
|
||||
|
||||
fn reverse_dns_addr_v4<'a>(dns_parts: &mut impl Iterator<Item = &'a str>) -> Option<Ipv4Addr> {
|
||||
dns_parts.join(".").parse().ok()
|
||||
}
|
||||
|
||||
fn reverse_dns_addr_v6<'a>(dns_parts: &mut impl Iterator<Item = &'a str>) -> Option<Ipv6Addr> {
|
||||
dns_parts
|
||||
.chunks(4)
|
||||
.into_iter()
|
||||
.map(|mut s| s.join(""))
|
||||
.join(":")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::reverse_dns_addr;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_addr_works_v4() {
|
||||
assert_eq!(
|
||||
reverse_dns_addr("1.2.3.4.in-addr.arpa"),
|
||||
Some(Ipv4Addr::new(4, 3, 2, 1).into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_v4_addr_extra_number() {
|
||||
assert_eq!(reverse_dns_addr("0.1.2.3.4.in-addr.arpa"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_addr_wrong_ending() {
|
||||
assert_eq!(reverse_dns_addr("1.2.3.4.in-addr.carpa"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_v4_addr_with_ip6_ending() {
|
||||
assert_eq!(reverse_dns_addr("1.2.3.4.ip6.arpa"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_addr_v6() {
|
||||
assert_eq!(
|
||||
reverse_dns_addr(
|
||||
"b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
|
||||
),
|
||||
Some("2001:db8::567:89ab".parse().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_addr_v6_extra_number() {
|
||||
assert_eq!(
|
||||
reverse_dns_addr(
|
||||
"0.b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_dns_addr_v6_ipv4_ending() {
|
||||
assert_eq!(
|
||||
reverse_dns_addr(
|
||||
"b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.in-addr.arpa"
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,12 +185,22 @@ pub(crate) enum Version {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum IpPacket<'a> {
|
||||
pub enum IpPacket<'a> {
|
||||
Ipv4Packet(Ipv4Packet<'a>),
|
||||
Ipv6Packet(Ipv6Packet<'a>),
|
||||
}
|
||||
|
||||
impl<'a> IpPacket<'a> {
|
||||
pub(crate) fn owned(data: Vec<u8>) -> Option<IpPacket<'static>> {
|
||||
let packet = match data[0] >> 4 {
|
||||
4 => Ipv4Packet::owned(data)?.into(),
|
||||
6 => Ipv6Packet::owned(data)?.into(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(packet)
|
||||
}
|
||||
|
||||
pub(crate) fn version(&self) -> Version {
|
||||
match self {
|
||||
IpPacket::Ipv4Packet(_) => Version::Ipv4,
|
||||
|
||||
@@ -11,8 +11,11 @@ use bytes::Bytes;
|
||||
use connlib_shared::{messages::Key, CallbackErrorFacade, Callbacks, Error};
|
||||
use ip_network::IpNetwork;
|
||||
use ip_network_table::IpNetworkTable;
|
||||
use ip_packet::IpPacket;
|
||||
use pnet_packet::Packet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use hickory_resolver::proto::rr::RecordType;
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use peer::{Peer, PeerStats};
|
||||
@@ -47,6 +50,7 @@ use crate::ip_packet::MutableIpPacket;
|
||||
use connlib_shared::messages::SecretKey;
|
||||
use index::IndexLfsr;
|
||||
|
||||
mod bounded_queue;
|
||||
mod client;
|
||||
mod control_protocol;
|
||||
mod device_channel;
|
||||
@@ -65,6 +69,8 @@ const MAX_UDP_SIZE: usize = (1 << 16) - 1;
|
||||
const RESET_PACKET_COUNT_INTERVAL: Duration = Duration::from_secs(1);
|
||||
const REFRESH_PEERS_TIMERS_INTERVAL: Duration = Duration::from_secs(1);
|
||||
const REFRESH_MTU_INTERVAL: Duration = Duration::from_secs(30);
|
||||
const DNS_QUERIES_QUEUE_SIZE: usize = 100;
|
||||
|
||||
/// For how long we will attempt to gather ICE candidates before aborting.
|
||||
///
|
||||
/// Chosen arbitrarily.
|
||||
@@ -226,6 +232,34 @@ pub(crate) fn peer_by_ip(peers_by_ip: &IpNetworkTable<Arc<Peer>>, ip: IpAddr) ->
|
||||
peers_by_ip.longest_match(ip).map(|(_, peer)| peer).cloned()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DnsQuery<'a> {
|
||||
pub name: String,
|
||||
pub record_type: RecordType,
|
||||
// We could be much more efficient with this field,
|
||||
// we only need the header to create the response.
|
||||
pub query: IpPacket<'a>,
|
||||
}
|
||||
|
||||
impl<'a> DnsQuery<'a> {
|
||||
pub(crate) fn into_owned(self) -> DnsQuery<'static> {
|
||||
let Self {
|
||||
name,
|
||||
record_type,
|
||||
query,
|
||||
} = self;
|
||||
let buf = query.packet().to_vec();
|
||||
let query =
|
||||
IpPacket::owned(buf).expect("We are constructing the ip packet from an ip packet");
|
||||
|
||||
DnsQuery {
|
||||
name,
|
||||
record_type,
|
||||
query,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event<TId> {
|
||||
SignalIceCandidate {
|
||||
conn_id: TId,
|
||||
@@ -236,6 +270,7 @@ pub enum Event<TId> {
|
||||
connected_gateway_ids: Vec<GatewayId>,
|
||||
reference: usize,
|
||||
},
|
||||
DnsQuery(DnsQuery<'static>),
|
||||
}
|
||||
|
||||
impl<CB, TRoleState> Tunnel<CB, TRoleState>
|
||||
|
||||
@@ -192,6 +192,7 @@ impl Eventloop {
|
||||
Poll::Ready(Event::ConnectionIntent { .. }) => {
|
||||
unreachable!("Not used on the gateway, split the events!")
|
||||
}
|
||||
Poll::Ready(_) => continue,
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user