diff --git a/.gitmodules b/.gitmodules index 91ffedf1..9bf3dd0a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "external/hashlink"] path = external/hashlink url = ../hashlink.git +[submodule "external/rust-igd"] + path = external/rust-igd + url = git@gitlab.hackers.town:veilid/rust-igd.git diff --git a/Cargo.lock b/Cargo.lock index dfef122d..641a5d46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -218,7 +218,7 @@ dependencies = [ "slab", "socket2", "waker-fn", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -244,7 +244,7 @@ dependencies = [ "libc", "once_cell", "signal-hook", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -269,7 +269,7 @@ dependencies = [ "log", "memchr", "once_cell", - "pin-project-lite", + "pin-project-lite 0.2.9", "pin-utils", "slab", "wasm-bindgen-futures", @@ -364,7 +364,7 @@ dependencies = [ "futures-io", "futures-util", "log", - "pin-project-lite", + "pin-project-lite 0.2.9", "tungstenite 0.17.2", ] @@ -382,7 +382,7 @@ dependencies = [ "futures-util", "pin-project 1.0.11", "rustc_version", - "tokio", + "tokio 1.19.2", "wasm-bindgen-futures", ] @@ -412,6 +412,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +[[package]] +name = "attohttpc" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" +dependencies = [ + "http", + "log", + "url", + "wildmatch", +] + [[package]] name = "atty" version = "0.2.14" @@ -420,7 +432,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -782,7 +794,7 @@ dependencies = [ "num-integer", "num-traits", "time 0.1.44", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1067,11 +1079,11 @@ dependencies = [ "bitflags", "crossterm_winapi", "libc", - "mio", + "mio 0.8.4", "parking_lot 0.12.1", "signal-hook", "signal-hook-mio", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1080,7 +1092,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1148,7 +1160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" dependencies = [ "nix 0.24.1", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1165,7 +1177,7 @@ dependencies = [ "libc", "log", "signal-hook", - "tokio", + "tokio 1.19.2", "unicode-segmentation", "unicode-width", ] @@ -1210,7 +1222,7 @@ dependencies = [ "num", "owning_ref", "time 0.3.11", - "tokio", + "tokio 1.19.2", "toml", "unicode-segmentation", "unicode-width", @@ -1350,7 +1362,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1670,9 +1682,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cef5c93884e5cef757f63446122c2f420713c3e03f85540d09485b9415983b4a" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "funty" version = "2.0.0" @@ -1738,7 +1766,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite", + "pin-project-lite 0.2.9", "waker-fn", ] @@ -1788,7 +1816,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -1904,7 +1932,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio", + "tokio 1.19.2", "tokio-util 0.7.3", "tracing", ] @@ -2008,7 +2036,7 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2030,7 +2058,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes 1.1.0", "http", - "pin-project-lite", + "pin-project-lite 0.2.9", ] [[package]] @@ -2061,9 +2089,9 @@ dependencies = [ "httparse", "httpdate", "itoa 1.0.2", - "pin-project-lite", + "pin-project-lite 0.2.9", "socket2", - "tokio", + "tokio 1.19.2", "tower-service", "tracing", "want", @@ -2076,8 +2104,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.19.2", "tokio-io-timeout", ] @@ -2108,6 +2136,25 @@ dependencies = [ "libc", ] +[[package]] +name = "igd" +version = "0.12.0" +dependencies = [ + "attohttpc", + "bytes 1.1.0", + "futures", + "http", + "hyper", + "log", + "rand 0.8.5", + "simplelog 0.9.0", + "tokio 0.2.25", + "tokio 0.3.7", + "tokio 1.19.2", + "url", + "xmltree", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -2192,6 +2239,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "ipconfig" version = "0.3.0" @@ -2200,7 +2256,7 @@ checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" dependencies = [ "socket2", "widestring 0.5.1", - "winapi", + "winapi 0.3.9", "winreg", ] @@ -2287,6 +2343,16 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "keychain-services" version = "0.0.2" @@ -2324,10 +2390,10 @@ dependencies = [ "serde", "serde_cbor", "serial_test", - "simplelog", + "simplelog 0.12.0", "snailquote", "tempfile", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2427,7 +2493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ "cfg-if 1.0.0", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2560,6 +2626,38 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow 0.3.7", + "ntapi", + "winapi 0.3.9", +] + [[package]] name = "mio" version = "0.8.4" @@ -2572,6 +2670,38 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "multimap" version = "0.8.3" @@ -2654,6 +2784,17 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "netlink-packet-core" version = "0.4.1" @@ -2695,7 +2836,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "tokio", + "tokio 1.19.2", ] [[package]] @@ -2707,7 +2848,7 @@ dependencies = [ "futures", "libc", "log", - "tokio", + "tokio 1.19.2", ] [[package]] @@ -2769,7 +2910,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2939,7 +3080,7 @@ dependencies = [ "pin-project 1.0.11", "rand 0.8.5", "thiserror", - "tokio", + "tokio 1.19.2", "tokio-stream", ] @@ -2958,7 +3099,7 @@ dependencies = [ "prost", "protobuf", "thiserror", - "tokio", + "tokio 1.19.2", "tonic", "tonic-build", ] @@ -3067,7 +3208,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3210,6 +3351,12 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -3266,7 +3413,7 @@ dependencies = [ "libc", "log", "wepoll-ffi", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3578,7 +3725,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3603,7 +3750,7 @@ dependencies = [ "spin 0.5.2", "untrusted", "web-sys", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3634,7 +3781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3646,7 +3793,7 @@ dependencies = [ "libc", "serde", "serde_json", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3660,7 +3807,7 @@ dependencies = [ "netlink-proto", "nix 0.22.3", "thiserror", - "tokio", + "tokio 1.19.2", ] [[package]] @@ -4079,7 +4226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "mio", + "mio 0.8.4", "signal-hook", ] @@ -4098,6 +4245,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" +[[package]] +name = "simplelog" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" +dependencies = [ + "chrono", + "log", + "termcolor", +] + [[package]] name = "simplelog" version = "0.12.0" @@ -4138,7 +4296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4177,7 +4335,7 @@ dependencies = [ "async-channel", "cfg-if 1.0.0", "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.9", ] [[package]] @@ -4233,7 +4391,7 @@ dependencies = [ "ntapi", "once_cell", "rayon", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4253,7 +4411,7 @@ dependencies = [ "libc", "redox_syscall", "remove_dir_all", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4317,7 +4475,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4372,6 +4530,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +dependencies = [ + "bytes 0.5.6", + "iovec", + "lazy_static", + "libc", + "mio 0.6.23", + "mio-uds", + "pin-project-lite 0.1.12", + "slab", +] + +[[package]] +name = "tokio" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46409491c9375a693ce7032101970a54f8a2010efb77e13f70788f0d84489e39" +dependencies = [ + "autocfg", + "libc", + "mio 0.7.14", + "pin-project-lite 0.2.9", +] + [[package]] name = "tokio" version = "1.19.2" @@ -4381,15 +4567,15 @@ dependencies = [ "bytes 1.1.0", "libc", "memchr", - "mio", + "mio 0.8.4", "num_cpus", "once_cell", "parking_lot 0.12.1", - "pin-project-lite", + "pin-project-lite 0.2.9", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4398,8 +4584,8 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.19.2", ] [[package]] @@ -4420,8 +4606,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.19.2", ] [[package]] @@ -4434,8 +4620,8 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.19.2", ] [[package]] @@ -4448,8 +4634,8 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.19.2", "tracing", ] @@ -4483,7 +4669,7 @@ dependencies = [ "pin-project 1.0.11", "prost", "prost-derive", - "tokio", + "tokio 1.19.2", "tokio-stream", "tokio-util 0.6.10", "tower", @@ -4515,10 +4701,10 @@ dependencies = [ "futures-util", "indexmap", "pin-project 1.0.11", - "pin-project-lite", + "pin-project-lite 0.2.9", "rand 0.8.5", "slab", - "tokio", + "tokio 1.19.2", "tokio-util 0.7.3", "tower-layer", "tower-service", @@ -4545,7 +4731,7 @@ checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] @@ -4688,7 +4874,7 @@ dependencies = [ "smallvec", "thiserror", "tinyvec", - "tokio", + "tokio 1.19.2", "url", ] @@ -4708,7 +4894,7 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", - "tokio", + "tokio 1.19.2", "trust-dns-proto", ] @@ -4907,7 +5093,7 @@ dependencies = [ "serde_derive", "serial_test", "thiserror", - "tokio", + "tokio 1.19.2", "tokio-util 0.7.3", "veilid-core", ] @@ -4950,6 +5136,7 @@ dependencies = [ "hashlink 0.8.0", "hex", "ifstructs", + "igd", "jni", "jni-sys", "js-sys", @@ -4981,12 +5168,12 @@ dependencies = [ "serde_cbor", "serde_json", "serial_test", - "simplelog", + "simplelog 0.12.0", "socket2", "static_assertions", "stop-token", "thiserror", - "tokio", + "tokio 1.19.2", "tokio-stream", "tokio-util 0.7.3", "tracing", @@ -5002,7 +5189,7 @@ dependencies = [ "webpki 0.22.0", "webpki-roots 0.22.3", "wee_alloc", - "winapi", + "winapi 0.3.9", "windows", "windows-permissions", "ws_stream_wasm", @@ -5028,7 +5215,7 @@ dependencies = [ "parking_lot 0.12.1", "serde", "serde_json", - "tokio", + "tokio 1.19.2", "tokio-stream", "tokio-util 0.7.3", "tracing", @@ -5072,7 +5259,7 @@ dependencies = [ "signal-hook", "signal-hook-async-std", "stop-token", - "tokio", + "tokio 1.19.2", "tokio-stream", "tokio-util 0.7.3", "tracing", @@ -5126,7 +5313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi", + "winapi 0.3.9", "winapi-util", ] @@ -5318,7 +5505,7 @@ dependencies = [ "cfg-if 0.1.10", "libc", "memory_units", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5353,6 +5540,18 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +[[package]] +name = "wildmatch" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -5363,6 +5562,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -5375,7 +5580,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5404,7 +5609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2ccdc3c6bf4d4a094e031b63fadd08d8e42abd259940eb8aa5fdc09d4bf9be" dependencies = [ "bitflags", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5416,7 +5621,7 @@ dependencies = [ "bitflags", "err-derive", "widestring 0.4.3", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5498,7 +5703,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", ] [[package]] @@ -5546,6 +5761,21 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Earthfile b/Earthfile index 1ec9a0cb..8ce1a7bd 100644 --- a/Earthfile +++ b/Earthfile @@ -4,8 +4,6 @@ VERSION 0.6 # Ensure we are using an amd64 platform because some of these targets use cross-platform tooling FROM --platform amd64 ubuntu:16.04 -# Choose where Rust ends up - # Install build prerequisites deps-base: RUN apt-get -y update diff --git a/external/rust-igd b/external/rust-igd new file mode 160000 index 00000000..330c8e2e --- /dev/null +++ b/external/rust-igd @@ -0,0 +1 @@ +Subproject commit 330c8e2ea33f6b9bd34809bb1c504459920f4fe2 diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index a3b2ae70..d9016ba0 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -73,6 +73,7 @@ config = { version = "^0", features = ["yaml"] } keyring-manager = { path = "../external/keyring-manager" } lru = "^0" async-tls = "^0.11" +igd = { path = "../external/rust-igd" } webpki = "^0" webpki-roots = "^0" rustls = "^0.19" diff --git a/veilid-core/src/intf/native/system.rs b/veilid-core/src/intf/native/system.rs index c2314761..56154e47 100644 --- a/veilid-core/src/intf/native/system.rs +++ b/veilid-core/src/intf/native/system.rs @@ -150,6 +150,23 @@ where } } +pub async fn blocking_wrapper(blocking_task: F, err_result: R) -> R +where + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, +{ + // run blocking stuff in blocking thread + cfg_if! { + if #[cfg(feature="rt-async-std")] { + async_std::task::spawn_blocking(blocking_task).await + } else if #[cfg(feature="rt-tokio")] { + tokio::task::spawn_blocking(blocking_task).await.unwrap_or(err_result) + } else { + #[compile_error("must use an executor")] + } + } +} + pub fn get_concurrency() -> u32 { std::thread::available_parallelism() .map(|x| x.get()) diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index 4820c9dc..222e47b6 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -64,7 +64,7 @@ pub fn veilid_version() -> (u32, u32, u32) { #[cfg(target_os = "android")] pub use intf::utils::android::{veilid_core_setup_android, veilid_core_setup_android_no_log}; -pub static DEFAULT_LOG_IGNORE_LIST: [&str; 18] = [ +pub static DEFAULT_LOG_IGNORE_LIST: [&str; 19] = [ "mio", "h2", "hyper", @@ -83,4 +83,5 @@ pub static DEFAULT_LOG_IGNORE_LIST: [&str; 18] = [ "netlink_sys", "trust_dns_resolver", "trust_dns_proto", + "attohttpc", ]; diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index fddc8324..395946a4 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -1642,8 +1642,8 @@ impl NetworkManager { connection_descriptor: ConnectionDescriptor, // the connection descriptor used reporting_peer: NodeRef, // the peer's noderef reporting the socket address ) { - // xxx debug code - info!("report_global_socket_address\nsocket_address: {:#?}\nconnection_descriptor: {:#?}\nreporting_peer: {:#?}", socket_address, connection_descriptor, reporting_peer); + // debug code + //info!("report_global_socket_address\nsocket_address: {:#?}\nconnection_descriptor: {:#?}\nreporting_peer: {:#?}", socket_address, connection_descriptor, reporting_peer); let key = PublicAddressCheckCacheKey( connection_descriptor.protocol_type(), @@ -1671,80 +1671,78 @@ impl NetworkManager { let network_class = net.get_network_class().unwrap_or(NetworkClass::Invalid); // Determine if our external address has likely changed - let needs_public_address_detection = if matches!( - network_class, - NetworkClass::InboundCapable - ) { - // Get the dial info filter for this connection so we can check if we have any public dialinfo that may have changed - let dial_info_filter = connection_descriptor.make_dial_info_filter(); + let needs_public_address_detection = + if matches!(network_class, NetworkClass::InboundCapable) { + // Get the dial info filter for this connection so we can check if we have any public dialinfo that may have changed + let dial_info_filter = connection_descriptor.make_dial_info_filter(); - // Get current external ip/port from registered global dialinfo - let current_addresses: BTreeSet = routing_table - .all_filtered_dial_info_details( - Some(RoutingDomain::PublicInternet), - &dial_info_filter, - ) - .iter() - .map(|did| did.dial_info.socket_address()) - .collect(); + // Get current external ip/port from registered global dialinfo + let current_addresses: BTreeSet = routing_table + .all_filtered_dial_info_details( + Some(RoutingDomain::PublicInternet), + &dial_info_filter, + ) + .iter() + .map(|did| did.dial_info.socket_address()) + .collect(); - // If we are inbound capable, but start to see inconsistent socket addresses from multiple reporting peers - // then we zap the network class and re-detect it - let mut inner = self.inner.lock(); - let mut inconsistencies = 0; - let mut changed = false; - // Iteration goes from most recent to least recent node/address pair - let pacc = inner - .public_address_check_cache - .entry(key) - .or_insert_with(|| LruCache::new(8)); - for (_, a) in pacc { - if !current_addresses.contains(a) { - inconsistencies += 1; - if inconsistencies >= GLOBAL_ADDRESS_CHANGE_DETECTION_COUNT { - changed = true; - break; - } - } - } - - // xxx debug code - if changed { - info!("XXX\npublic_address_check_cache: {:#?}\ncurrent_addresses: {:#?}\ninconsistencies: {}", inner - .public_address_check_cache, current_addresses, inconsistencies); - } - - changed - } else { - // If we are currently outbound only, we don't have any public dial info - // but if we are starting to see consistent socket address from multiple reporting peers - // then we may be become inbound capable, so zap the network class so we can re-detect it and any public dial info - - let mut inner = self.inner.lock(); - let mut consistencies = 0; - let mut consistent = false; - let mut current_address = Option::::None; - // Iteration goes from most recent to least recent node/address pair - let pacc = inner - .public_address_check_cache - .entry(key) - .or_insert_with(|| LruCache::new(8)); - - for (_, a) in pacc { - if let Some(current_address) = current_address { - if current_address == *a { - consistencies += 1; - if consistencies >= GLOBAL_ADDRESS_CHANGE_DETECTION_COUNT { - consistent = true; + // If we are inbound capable, but start to see inconsistent socket addresses from multiple reporting peers + // then we zap the network class and re-detect it + let mut inner = self.inner.lock(); + let mut inconsistencies = 0; + let mut changed = false; + // Iteration goes from most recent to least recent node/address pair + let pacc = inner + .public_address_check_cache + .entry(key) + .or_insert_with(|| LruCache::new(8)); + for (_, a) in pacc { + if !current_addresses.contains(a) { + inconsistencies += 1; + if inconsistencies >= GLOBAL_ADDRESS_CHANGE_DETECTION_COUNT { + changed = true; break; } } - } else { - current_address = Some(*a); } - } - consistent - }; + + // // debug code + // if changed { + // trace!("public_address_check_cache: {:#?}\ncurrent_addresses: {:#?}\ninconsistencies: {}", inner + // .public_address_check_cache, current_addresses, inconsistencies); + // } + + changed + } else { + // If we are currently outbound only, we don't have any public dial info + // but if we are starting to see consistent socket address from multiple reporting peers + // then we may be become inbound capable, so zap the network class so we can re-detect it and any public dial info + + let mut inner = self.inner.lock(); + let mut consistencies = 0; + let mut consistent = false; + let mut current_address = Option::::None; + // Iteration goes from most recent to least recent node/address pair + let pacc = inner + .public_address_check_cache + .entry(key) + .or_insert_with(|| LruCache::new(8)); + + for (_, a) in pacc { + if let Some(current_address) = current_address { + if current_address == *a { + consistencies += 1; + if consistencies >= GLOBAL_ADDRESS_CHANGE_DETECTION_COUNT { + consistent = true; + break; + } + } + } else { + current_address = Some(*a); + } + } + consistent + }; if needs_public_address_detection { if detect_address_changes { diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs new file mode 100644 index 00000000..43ba7a7e --- /dev/null +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -0,0 +1,380 @@ +use super::*; +use crate::xx::*; +use igd::*; +use std::net::UdpSocket; + +const UPNP_GATEWAY_DETECT_TIMEOUT_MS: u32 = 5_000; +const UPNP_MAPPING_LIFETIME_MS: u32 = 120_000; +const UPNP_MAPPING_ATTEMPTS: u32 = 3; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct PortMapKey { + llpt: LowLevelProtocolType, + at: AddressType, + local_port: u16, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct PortMapValue { + ext_ip: IpAddr, + mapped_port: u16, + timestamp: u64, + renewal_lifetime: u64, + renewal_attempts: u32, +} + +struct IGDManagerInner { + local_ip_addrs: BTreeMap, + gateways: BTreeMap>, + port_maps: BTreeMap, +} + +#[derive(Clone)] +pub struct IGDManager { + config: VeilidConfig, + inner: Arc>, +} + + +fn convert_llpt(llpt: LowLevelProtocolType) -> PortMappingProtocol { + match llpt { + LowLevelProtocolType::UDP => PortMappingProtocol::UDP, + LowLevelProtocolType::TCP => PortMappingProtocol::TCP, + } +} + + +impl IGDManager { + // + + pub fn new(config: VeilidConfig) -> Self { + Self { + config, + inner: Arc::new(Mutex::new(IGDManagerInner { + local_ip_addrs: BTreeMap::new(), + gateways: BTreeMap::new(), + port_maps: BTreeMap::new(), + })), + } + } + + fn get_routed_local_ip_address(address_type: AddressType) -> Option { + let socket = match UdpSocket::bind(match address_type { + AddressType::IPV4 => SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0), + AddressType::IPV6 => SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0), + }) { + Ok(s) => s, + Err(_) => return None, + }; + + // can be any routable ip address, + // this is just to make the system routing table calculate the appropriate local ip address + // using google's dns, but it wont actually send any packets to it + socket + .connect(match address_type { + AddressType::IPV4 => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 0), + AddressType::IPV6 => SocketAddr::new( + IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)), + 0, + ), + }) + .ok()?; + + Some(socket.local_addr().ok()?.ip()) + } + + fn find_local_ip(inner: &mut IGDManagerInner, + address_type: AddressType, + ) -> Option { + if let Some(ip) = inner.local_ip_addrs.get(&address_type) { + return Some(*ip); + } + + let ip = match Self::get_routed_local_ip_address(address_type) { + Some(x) => x, + None => { + log_net!("failed to get local ip address"); + return None; + } + }; + + inner.local_ip_addrs.insert(address_type, ip); + Some(ip) + } + + fn get_local_ip( + inner: &mut IGDManagerInner, + address_type: AddressType, + ) -> Option { + if let Some(ip) = inner.local_ip_addrs.get(&address_type) { + return Some(*ip); + } + None + } + + fn find_gateway( + inner: &mut IGDManagerInner, + address_type: AddressType, + ) -> Option> { + if let Some(gw) = inner.gateways.get(&address_type) { + return Some(gw.clone()); + } + + let gateway = match address_type { + AddressType::IPV4 => { + match igd::search_gateway(SearchOptions::new_v4( + UPNP_GATEWAY_DETECT_TIMEOUT_MS as u64, + )) { + Ok(v) => v, + Err(e) => { + log_net!("couldn't find ipv4 igd: {}", e); + return None; + } + } + } + AddressType::IPV6 => { + match igd::search_gateway(SearchOptions::new_v6( + Ipv6SearchScope::LinkLocal, + UPNP_GATEWAY_DETECT_TIMEOUT_MS as u64, + )) { + Ok(v) => v, + Err(e) => { + log_net!("couldn't find ipv6 igd: {}", e); + return None; + } + } + } + }; + let gw = Arc::new(gateway); + inner.gateways.insert(address_type, gw.clone()); + Some(gw) + } + + fn get_gateway( + inner: &mut IGDManagerInner, + address_type: AddressType, + ) -> Option> { + if let Some(gw) = inner.gateways.get(&address_type) { + return Some(gw.clone()); + } + None + } + + fn get_description(&self, llpt: LowLevelProtocolType, local_port:u16) -> String { + format!("{} map {} for port {}", self.config.get().program_name, convert_llpt(llpt), local_port ) + } + + pub async fn map_any_port( + &self, + llpt: LowLevelProtocolType, + at: AddressType, + local_port: u16, + expected_external_address: Option, + ) -> Option { + let this = self.clone(); + intf::blocking_wrapper(move || { + let mut inner = this.inner.lock(); + + // If we already have this port mapped, just return the existing portmap + let pmkey = PortMapKey { + llpt, + at, + local_port, + }; + if let Some(pmval) = inner.port_maps.get(&pmkey) { + return Some(SocketAddr::new(pmval.ext_ip, pmval.mapped_port)); + } + + // Get local ip address + let local_ip = Self::find_local_ip(&mut *inner, at)?; + + // Find gateway + let gw = Self::find_gateway(&mut *inner, at)?; + + // Get external address + let ext_ip = match gw.get_external_ip() { + Ok(ip) => ip, + Err(e) => { + log_net!(debug "couldn't get external ip from igd: {}", e); + return None; + } + }; + + // Ensure external IP matches address type + if ext_ip.is_ipv4() { + if at != AddressType::IPV4 { + log_net!(debug "mismatched ip address type from igd, wanted v4, got v6"); + return None; + } + } else if ext_ip.is_ipv6() { + if at != AddressType::IPV6 { + log_net!(debug "mismatched ip address type from igd, wanted v6, got v4"); + return None; + } + } + + if let Some(expected_external_address) = expected_external_address { + if ext_ip != expected_external_address { + log_net!(debug "gateway external address does not match calculated external address: expected={} vs gateway={}", expected_external_address, ext_ip); + return None; + } + } + + // Map any port + let desc = this.get_description(llpt, local_port); + let mapped_port = match gw.add_any_port(convert_llpt(llpt), SocketAddr::new(local_ip, local_port), (UPNP_MAPPING_LIFETIME_MS + 999) / 1000, &desc) { + Ok(mapped_port) => mapped_port, + Err(e) => { + // Failed to map external port + log_net!(debug "upnp failed to map external port: {}", e); + return None; + } + }; + + // Add to mapping list to keep alive + let timestamp = intf::get_timestamp(); + inner.port_maps.insert(PortMapKey { + llpt, + at, + local_port, + }, PortMapValue { + ext_ip, + mapped_port, + timestamp, + renewal_lifetime: (UPNP_MAPPING_LIFETIME_MS / 2) as u64 * 1000u64, + renewal_attempts: 0, + }); + + // Succeeded, return the externally mapped port + Some(SocketAddr::new(ext_ip, mapped_port)) + }, None) + .await + } + + pub async fn tick(&self) -> EyreResult { + // Refresh mappings if we have them + // If an error is received, then return false to restart the local network + let mut full_renews: Vec<(PortMapKey, PortMapValue)> = Vec::new(); + let mut renews: Vec<(PortMapKey, PortMapValue)> = Vec::new(); + let now = intf::get_timestamp(); + const UPNP_MAPPING_LIFETIME_US:u64 = (UPNP_MAPPING_LIFETIME_MS as u64) * 1000u64; + + { + let inner = self.inner.lock(); + + for (k, v) in &inner.port_maps { + if (now - v.timestamp) >= UPNP_MAPPING_LIFETIME_US || v.renewal_attempts >= UPNP_MAPPING_ATTEMPTS { + // Past expiration time or tried N times, do a full renew and fail out if we can't + full_renews.push((*k, *v)); + } + else if (now - v.timestamp) >= v.renewal_lifetime { + // Attempt a normal renewal + renews.push((*k, *v)); + } + } + + // See if we need to do some blocking operations + if full_renews.is_empty() && renews.is_empty() { + // Just return now since there's nothing to renew + return Ok(true); + } + } + + let this = self.clone(); + intf::blocking_wrapper(move || { + let mut inner = this.inner.lock(); + + // Process full renewals + for (k, v) in full_renews { + + // Get gateway for address type + let gw = match Self::get_gateway(&mut inner, k.at) { + Some(gw) => gw, + None => { + return Err(eyre!("gateway missing for address type")); + } + }; + + // Get local ip for address type + let local_ip = match Self::get_local_ip(&mut inner, k.at) { + Some(ip) => ip, + None => { + return Err(eyre!("local ip missing for address type")); + } + }; + + // Delete the mapping if it exists, ignore any errors here + let _ = gw.remove_port(convert_llpt(k.llpt), v.mapped_port); + inner.port_maps.remove(&k); + + let desc = this.get_description(k.llpt, k.local_port); + match gw.add_any_port(convert_llpt(k.llpt), SocketAddr::new(local_ip, k.local_port), (UPNP_MAPPING_LIFETIME_MS + 999) / 1000, &desc) { + Ok(mapped_port) => { + log_net!(debug "full-renewed mapped port {:?} -> {:?}", v, k); + inner.port_maps.insert(k, PortMapValue { + ext_ip: v.ext_ip, + mapped_port, + timestamp: intf::get_timestamp(), + renewal_lifetime: (UPNP_MAPPING_LIFETIME_MS / 2) as u64 * 1000u64, + renewal_attempts: 0, + }); + }, + Err(e) => { + info!("failed to full-renew mapped port {:?} -> {:?}: {}", v, k, e); + + // Must restart network now :( + return Ok(false); + } + }; + + } + // Process normal renewals + for (k, mut v) in renews { + + // Get gateway for address type + let gw = match Self::get_gateway(&mut inner, k.at) { + Some(gw) => gw, + None => { + return Err(eyre!("gateway missing for address type")); + } + }; + + // Get local ip for address type + let local_ip = match Self::get_local_ip(&mut inner, k.at) { + Some(ip) => ip, + None => { + return Err(eyre!("local ip missing for address type")); + } + }; + + let desc = this.get_description(k.llpt, k.local_port); + match gw.add_port(convert_llpt(k.llpt), v.mapped_port, SocketAddr::new(local_ip, k.local_port), (UPNP_MAPPING_LIFETIME_MS + 999) / 1000, &desc) { + Ok(()) => { + log_net!(debug "renewed mapped port {:?} -> {:?}", v, k); + + inner.port_maps.insert(k, PortMapValue { + ext_ip: v.ext_ip, + mapped_port: v.mapped_port, + timestamp: intf::get_timestamp(), + renewal_lifetime: (UPNP_MAPPING_LIFETIME_MS / 2) as u64 * 1000u64, + renewal_attempts: 0, + }); + }, + Err(e) => { + log_net!(debug "failed to renew mapped port {:?} -> {:?}: {}", v, k, e); + + // Get closer to the maximum renewal timeline by a factor of two each time + v.renewal_lifetime = (v.renewal_lifetime + UPNP_MAPPING_LIFETIME_US) / 2; + v.renewal_attempts += 1; + + // Store new value to try again + inner.port_maps.insert(k, v); + } + }; + } + + // Normal exit, no restart + Ok(true) + }, Err(eyre!("failed to process blocking task"))).await + } +} diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index 28fc15b9..9428e2eb 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -1,3 +1,5 @@ +mod igd_manager; +mod natpmp_manager; mod network_class_discovery; mod network_tcp; mod network_udp; @@ -67,6 +69,12 @@ struct NetworkUnlockedInner { // Background processes update_network_class_task: TickTask, network_interfaces_task: TickTask, + upnp_task: TickTask, + natpmp_task: TickTask, + + // Managers + igd_manager: igd_manager::IGDManager, + natpmp_manager: natpmp_manager::NATPMPManager, } #[derive(Clone)] @@ -108,6 +116,7 @@ impl Network { routing_table: RoutingTable, connection_manager: ConnectionManager, ) -> NetworkUnlockedInner { + let config = network_manager.config(); NetworkUnlockedInner { network_manager, routing_table, @@ -115,6 +124,10 @@ impl Network { interfaces: NetworkInterfaces::new(), update_network_class_task: TickTask::new(1), network_interfaces_task: TickTask::new(5), + upnp_task: TickTask::new(1), + natpmp_task: TickTask::new(1), + igd_manager: igd_manager::IGDManager::new(config.clone()), + natpmp_manager: natpmp_manager::NATPMPManager::new(config), } } @@ -151,6 +164,20 @@ impl Network { Box::pin(this2.clone().network_interfaces_task_routine(s, l, t)) }); } + // Set upnp tick task + { + let this2 = this.clone(); + this.unlocked_inner + .upnp_task + .set_routine(move |s, l, t| Box::pin(this2.clone().upnp_task_routine(s, l, t))); + } + // Set natpmp tick task + { + let this2 = this.clone(); + this.unlocked_inner + .natpmp_task + .set_routine(move |s, l, t| Box::pin(this2.clone().natpmp_task_routine(s, l, t))); + } this } @@ -257,6 +284,17 @@ impl Network { } } + pub fn get_local_port(&self, protocol_type: ProtocolType) -> u16 { + let inner = self.inner.lock(); + let local_port = match protocol_type { + ProtocolType::UDP => inner.udp_port, + ProtocolType::TCP => inner.tcp_port, + ProtocolType::WS => inner.ws_port, + ProtocolType::WSS => inner.wss_port, + }; + local_port + } + fn get_preferred_local_address(&self, dial_info: &DialInfo) -> SocketAddr { let inner = self.inner.lock(); @@ -749,11 +787,47 @@ impl Network { Ok(()) } + #[instrument(level = "trace", skip(self), err)] + pub async fn upnp_task_routine( + self, + stop_token: StopToken, + _l: u64, + _t: u64, + ) -> EyreResult<()> { + if !self.unlocked_inner.igd_manager.tick().await? { + info!("upnp failed, restarting local network"); + let mut inner = self.inner.lock(); + inner.network_needs_restart = true; + } + + Ok(()) + } + + #[instrument(level = "trace", skip(self), err)] + pub async fn natpmp_task_routine( + self, + stop_token: StopToken, + _l: u64, + _t: u64, + ) -> EyreResult<()> { + if !self.unlocked_inner.natpmp_manager.tick().await? { + info!("natpmp failed, restarting local network"); + let mut inner = self.inner.lock(); + inner.network_needs_restart = true; + } + + Ok(()) + } + pub async fn tick(&self) -> EyreResult<()> { - let detect_address_changes = { + let (detect_address_changes, upnp, natpmp) = { let config = self.network_manager().config(); let c = config.get(); - c.network.detect_address_changes + ( + c.network.detect_address_changes, + c.network.upnp, + c.network.natpmp, + ) }; // If we need to figure out our network class, tick the task for it @@ -776,6 +850,16 @@ impl Network { } } + // If we need to tick upnp, do it + if upnp && !self.needs_restart() { + self.unlocked_inner.upnp_task.tick().await?; + } + + // If we need to tick natpmp, do it + if natpmp && !self.needs_restart() { + self.unlocked_inner.natpmp_task.tick().await?; + } + Ok(()) } } diff --git a/veilid-core/src/network_manager/native/natpmp_manager.rs b/veilid-core/src/network_manager/native/natpmp_manager.rs new file mode 100644 index 00000000..4342abfc --- /dev/null +++ b/veilid-core/src/network_manager/native/natpmp_manager.rs @@ -0,0 +1,18 @@ +use super::*; + +pub struct NATPMPManager { + config: VeilidConfig, +} + +impl NATPMPManager { + // + + pub fn new(config: VeilidConfig) -> Self { + Self { config } + } + + pub async fn tick(&self) -> EyreResult { + // xxx + Ok(true) + } +} diff --git a/veilid-core/src/network_manager/native/network_class_discovery.rs b/veilid-core/src/network_manager/native/network_class_discovery.rs index b1c4d14c..d3f3dcff 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -3,6 +3,7 @@ use super::*; use futures_util::stream::FuturesUnordered; use futures_util::FutureExt; use stop_token::future::FutureExt as StopTokenFutureExt; +use tokio::task::spawn_blocking; struct DetectedPublicDialInfo { dial_info: DialInfo, @@ -213,7 +214,39 @@ impl DiscoveryContext { #[instrument(level = "trace", skip(self), ret)] async fn try_port_mapping(&self) -> Option { - //xxx + let (enable_upnp, enable_natpmp) = { + let c = self.net.config.get(); + (c.network.upnp, c.network.natpmp) + }; + + if enable_upnp { + let (pt, llpt, at, external_address_1, local_port) = { + let inner = self.inner.lock(); + let pt = inner.protocol_type.unwrap(); + let llpt = pt.low_level_protocol_type(); + let at = inner.address_type.unwrap(); + let external_address_1 = inner.external_1_address.unwrap(); + let local_port = self.net.get_local_port(pt); + (pt, llpt, at, external_address_1, local_port) + }; + + if let Some(mapped_external_address) = self + .net + .unlocked_inner + .igd_manager + .map_any_port(llpt, at, local_port, Some(external_address_1.to_ip_addr())) + .await + { + // make dial info from the port + return Some( + self.make_dial_info( + SocketAddress::from_socket_addr(mapped_external_address), + pt, + ), + ); + } + } + None } diff --git a/veilid-core/src/xx/mod.rs b/veilid-core/src/xx/mod.rs index d85d7aa1..0f72f7b6 100644 --- a/veilid-core/src/xx/mod.rs +++ b/veilid-core/src/xx/mod.rs @@ -103,6 +103,8 @@ cfg_if! { pub use tokio::sync::Mutex as AsyncMutex; pub use tokio::sync::MutexGuard as AsyncMutexGuard; pub use tokio::task::JoinHandle as LowLevelJoinHandle; + } else { + #[compile_error("must use an executor")] } } pub use std::net::{ SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, IpAddr, Ipv4Addr, Ipv6Addr }; diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index f1bb3147..c9001fd1 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -96,7 +96,7 @@ core: min_peer_count: 20 min_peer_refresh_time_ms: 2000 validate_dial_info_receipt_time_ms: 2000 - upnp: false + upnp: true natpmp: false detect_address_changes: true enable_local_peer_scope: false @@ -1500,7 +1500,7 @@ mod tests { 2_000u32 ); // - assert_eq!(s.core.network.upnp, false); + assert_eq!(s.core.network.upnp, true); assert_eq!(s.core.network.natpmp, false); assert_eq!(s.core.network.detect_address_changes, true); assert_eq!(s.core.network.enable_local_peer_scope, false);