diff --git a/Cargo.lock b/Cargo.lock index d23ac3bdd8..6c1feec38b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -44,15 +44,15 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.12" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.2.16", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -93,9 +93,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -108,36 +108,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "once_cell_polyfill", + "once_cell", "windows-sys 0.59.0", ] @@ -198,7 +198,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -210,7 +210,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -234,7 +234,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" @@ -343,7 +343,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -370,9 +370,9 @@ dependencies = [ [[package]] name = "backon" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302eaff5357a264a2c42f127ecb8bac761cf99749fc3dc95677e2743991f99e7" +checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" dependencies = [ "fastrand", "gloo-timers 0.3.0", @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.8.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bincode" @@ -453,9 +453,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake3" @@ -493,9 +493,9 @@ checksum = "387e80962b798815a2b5c4bcfdb6bf626fa922ffe9f74e373103b858738e9f31" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -517,9 +517,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.29" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "shlex", ] @@ -532,9 +532,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" @@ -606,9 +606,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -616,9 +616,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -628,30 +628,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cobs" -version = "0.3.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror 2.0.12", -] +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "color-backtrace" @@ -666,9 +663,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -717,9 +714,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cordyceps" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +checksum = "a0392f465ceba1713d30708f61c160ebf4dc1cf86bb166039d16b11ad4f3b5b6" dependencies = [ "loom", "tracing", @@ -737,9 +734,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -762,9 +759,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -853,9 +850,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -926,7 +923,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -983,7 +980,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1021,7 +1018,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "unicode-xid", ] @@ -1033,7 +1030,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "unicode-xid", ] @@ -1089,7 +1086,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1131,9 +1128,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", @@ -1177,27 +1174,27 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "enumflags2" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1217,12 +1214,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.13" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -1231,6 +1228,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fastbloom" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ec576c163744bef8707859f6aeb322bcf56b8da61215d99f77d6e33160ff01" +dependencies = [ + "getrandom 0.3.2", + "rand 0.9.1", + "siphasher", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1287,9 +1295,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.1.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683" +checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" dependencies = [ "autocfg", "tokio", @@ -1377,7 +1385,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1418,16 +1426,15 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ - "cc", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows 0.58.0", ] [[package]] @@ -1450,15 +1457,15 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "js-sys", @@ -1669,7 +1676,7 @@ dependencies = [ "futures-sink", "futures-timer", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.2", "no-std-compat", "nonzero_ext", "parking_lot", @@ -1683,9 +1690,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -1727,9 +1734,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -1768,9 +1775,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -1882,9 +1889,9 @@ dependencies = [ [[package]] name = "hmac-sha256" -version = "1.1.12" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6880c8d4a9ebf39c6e8b77007ce223f646a4d21ce29d99f70cb16420545425" +checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb" [[package]] name = "hostname-validator" @@ -1988,10 +1995,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ + "futures-util", "http 1.3.1", "hyper", "hyper-util", @@ -2000,26 +2008,22 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.1", + "webpki-roots 0.26.11", ] [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ - "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.3.1", "http-body", "hyper", - "ipnet", "libc", - "percent-encoding", "pin-project-lite", "socket2 0.5.10", "tokio", @@ -2039,7 +2043,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -2053,22 +2057,21 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.0.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -2077,11 +2080,31 @@ dependencies = [ "zerovec", ] +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", @@ -2089,54 +2112,67 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" -version = "2.0.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "potential_utf", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "zerovec", ] +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "idna" version = "1.0.3" @@ -2150,9 +2186,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -2181,12 +2217,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.3", ] [[package]] @@ -2224,17 +2260,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "io-uring" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -2256,16 +2281,6 @@ dependencies = [ "serde", ] -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "iroh" version = "0.91.0" @@ -2284,7 +2299,7 @@ dependencies = [ "ed25519-dalek", "futures-buffered", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.2", "hickory-resolver", "http 1.3.1", "igd-next", @@ -2320,7 +2335,7 @@ dependencies = [ "smallvec", "snafu", "spki", - "strum 0.27.1", + "strum 0.27.2", "stun-rs", "surge-ping", "swarm-discovery", @@ -2373,7 +2388,7 @@ dependencies = [ "n0-future", "n0-snafu", "rand 0.8.5", - "rcgen 0.14.2", + "rcgen 0.14.3", "rustls", "tokio", "tracing", @@ -2461,14 +2476,13 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "iroh-quinn" version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde160ebee7aabede6ae887460cd303c8b809054224815addf1469d54a6fcf7" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "bytes", "cfg_aliases", @@ -2477,7 +2491,7 @@ dependencies = [ "pin-project-lite", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.0", "thiserror 2.0.12", "tokio", "tracing", @@ -2487,12 +2501,13 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929d5d8fa77d5c304d3ee7cae9aede31f13908bd049f9de8c7c0094ad6f7c535" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "bytes", - "getrandom 0.2.16", - "rand 0.8.5", + "fastbloom", + "getrandom 0.3.2", + "lru-slab", + "rand 0.9.1", "ring", "rustc-hash", "rustls", @@ -2507,14 +2522,13 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53afaa1049f7c83ea1331f5ebb9e6ebc5fdd69c468b7a22dd598b02c9bcc973" +version = "0.5.12" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", "windows-sys 0.59.0", ] @@ -2532,7 +2546,7 @@ dependencies = [ "dashmap", "data-encoding", "derive_more 2.0.1", - "getrandom 0.3.3", + "getrandom 0.3.2", "governor", "hickory-proto", "hickory-resolver", @@ -2555,7 +2569,7 @@ dependencies = [ "proptest", "rand 0.8.5", "rand_chacha 0.3.1", - "rcgen 0.14.2", + "rcgen 0.14.3", "regex", "reloadable-state", "reqwest", @@ -2571,7 +2585,7 @@ dependencies = [ "sha1", "simdutf8", "snafu", - "strum 0.27.1", + "strum 0.27.2", "time", "tokio", "tokio-rustls", @@ -2660,15 +2674,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags", "libc", @@ -2688,9 +2702,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "litrs" @@ -2700,9 +2714,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2733,7 +2747,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.3", ] [[package]] @@ -2787,9 +2801,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -2815,22 +2829,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", ] [[package]] @@ -3009,8 +3023,7 @@ dependencies = [ [[package]] name = "netwatch" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901dbb408894af3df3fc51420ba0c6faf3a7d896077b797c39b7001e2f787bd" +source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#e824fd0fb03839c4523c18cd3ab861d6b3e66a16" dependencies = [ "atomic-waker", "bytes", @@ -3036,8 +3049,8 @@ dependencies = [ "tokio-util", "tracing", "web-sys", - "windows", - "windows-result", + "windows 0.61.3", + "windows-result 0.3.4", "wmi", ] @@ -3136,24 +3149,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", - "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3193,12 +3205,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - [[package]] name = "oorandom" version = "11.1.5" @@ -3231,9 +3237,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3241,9 +3247,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", @@ -3291,9 +3297,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror 2.0.12", @@ -3302,9 +3308,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -3312,23 +3318,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ + "once_cell", "pest", "sha2", ] @@ -3360,7 +3367,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3377,9 +3384,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "3.8.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a50f65a2b97031863fbdff2f085ba832360b4bef3106d1fcff9ab5bf4063fe" +checksum = "e32222ae3d617bf92414db29085f8a959a4515effce916e038e9399a335a0d6d" dependencies = [ "async-compat", "base32", @@ -3463,7 +3470,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3500,9 +3507,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "portmapper" @@ -3537,9 +3544,9 @@ dependencies = [ [[package]] name = "postcard" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1de96e20f51df24ca73cafcc4690e044854d803259db27a00a461cb3b9d17a" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -3551,22 +3558,13 @@ dependencies = [ [[package]] name = "postcard-derive" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f049d94cb6dda6938cc8a531d2898e7c08d71c6de63d8e67123cca6cdde2cc" +checksum = "0239fa9c1d225d4b7eb69925c25c5e082307a141e470573fbbe3a817ce6a7a37" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "potential_utf" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" -dependencies = [ - "zerovec", + "syn 1.0.109", ] [[package]] @@ -3581,7 +3579,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.25", ] [[package]] @@ -3658,17 +3656,17 @@ dependencies = [ [[package]] name = "proptest" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", "bitflags", "lazy_static", "num-traits", - "rand 0.9.1", - "rand_chacha 0.9.0", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -3678,15 +3676,15 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.6" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -3699,9 +3697,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ "bytes", "cfg_aliases", @@ -3719,13 +3717,12 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", - "getrandom 0.3.3", - "lru-slab", + "getrandom 0.3.2", "rand 0.9.1", "ring", "rustc-hash", @@ -3740,9 +3737,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases", "libc", @@ -3773,9 +3770,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.3.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" @@ -3833,16 +3830,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.2", ] [[package]] name = "rand_xorshift" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.6.4", ] [[package]] @@ -3889,9 +3886,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49bc8ffa8a832eb1d7c8000337f8b0d2f4f2f5ec3cf4ddc26f125e3ad2451824" +checksum = "0068c5b3cab1d4e271e0bb6539c87563c43411cad90b057b15c79958fbeb41f7" dependencies = [ "pem", "ring", @@ -3911,9 +3908,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -3998,9 +3995,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64", "bytes", @@ -4012,12 +4009,16 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", + "ipnet", "js-sys", "log", + "mime", + "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -4027,21 +4028,21 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", - "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.1", + "webpki-roots 0.26.11", + "windows-registry", ] [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" [[package]] name = "ring" @@ -4059,9 +4060,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -4102,9 +4103,8 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +version = "0.23.27" +source = "git+https://github.com/n0-computer/rustls?rev=c636f89ae00aee19ddd5e6df4150cec5c031fa31#c636f89ae00aee19ddd5e6df4150cec5c031fa31" dependencies = [ "log", "once_cell", @@ -4183,11 +4183,11 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" dependencies = [ - "core-foundation 0.10.1", + "core-foundation 0.10.0", "core-foundation-sys", "jni", "log", @@ -4198,7 +4198,7 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 0.26.11", + "webpki-root-certs", "windows-sys 0.59.0", ] @@ -4210,9 +4210,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" dependencies = [ "ring", "rustls-pki-types", @@ -4221,9 +4221,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rusty-fork" @@ -4289,7 +4289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", - "core-foundation 0.10.1", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -4370,7 +4370,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4511,17 +4511,26 @@ dependencies = [ "bitflags", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smol_str" @@ -4548,7 +4557,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4631,7 +4640,7 @@ dependencies = [ "proc-macro2", "quote", "struct_iterable_internal", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4651,11 +4660,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.1", + "strum_macros 0.27.2", ] [[package]] @@ -4668,20 +4677,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4758,9 +4766,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -4784,7 +4792,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4816,12 +4824,12 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4862,7 +4870,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4873,16 +4881,17 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", + "once_cell", ] [[package]] @@ -4921,9 +4930,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -4956,19 +4965,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", @@ -4982,7 +4989,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5046,7 +5053,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "hashbrown 0.15.4", + "hashbrown 0.15.3", "pin-project-lite", "tokio", ] @@ -5061,7 +5068,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "getrandom 0.3.3", + "getrandom 0.3.2", "http 1.3.1", "httparse", "rand 0.9.1", @@ -5090,9 +5097,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" [[package]] name = "toml_datetime" @@ -5105,12 +5112,12 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", - "toml_datetime 0.6.11", + "toml_datetime 0.6.9", "winnow", ] @@ -5147,18 +5154,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "bitflags", "bytes", - "futures-util", "http 1.3.1", "http-body", - "iri-string", "pin-project-lite", - "tower", "tower-layer", "tower-service", "tracing", @@ -5206,20 +5210,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -5294,7 +5298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5356,9 +5360,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode-xid" @@ -5400,6 +5404,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -5414,13 +5424,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.3", - "js-sys", - "wasm-bindgen", + "getrandom 0.3.2", ] [[package]] @@ -5465,9 +5473,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" @@ -5500,7 +5508,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -5535,7 +5543,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5570,7 +5578,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5608,18 +5616,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.1", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] @@ -5630,14 +5629,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.1", + "webpki-roots 1.0.0", ] [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -5679,6 +5678,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.61.3" @@ -5686,7 +5695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", "windows-link", "windows-numerics", @@ -5698,7 +5707,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -5707,11 +5729,11 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.0", + "windows-interface 0.59.1", "windows-link", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -5720,11 +5742,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link", "windows-threading", ] +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "windows-implement" version = "0.60.0" @@ -5733,7 +5766,18 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -5744,7 +5788,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5759,10 +5803,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link", ] +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.2", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -5772,6 +5836,25 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -6079,9 +6162,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -6116,21 +6199,27 @@ dependencies = [ "log", "serde", "thiserror 2.0.12", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + [[package]] name = "writeable" -version = "0.6.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "ws_stream_wasm" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" dependencies = [ "async_io_stream", "futures", @@ -6139,7 +6228,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 2.0.12", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -6164,9 +6253,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xmltree" @@ -6194,9 +6283,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -6206,13 +6295,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -6224,22 +6313,42 @@ checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.8.25", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -6259,7 +6368,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -6269,22 +6378,11 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - [[package]] name = "zerovec" -version = "0.11.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -6293,11 +6391,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] diff --git a/Cargo.toml b/Cargo.toml index 593f1d0ec7..5d06c0f4ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,13 @@ unexpected_cfgs = { level = "warn", check-cfg = ["cfg(iroh_docsrs)", "cfg(iroh_l [workspace.lints.clippy] unused-async = "warn" + + +[patch.crates-io] +rustls = { git = "https://github.com/n0-computer/rustls", rev = "c636f89ae00aee19ddd5e6df4150cec5c031fa31" } +netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-multipath" } + +[patch."https://github.com/n0-computer/quinn"] +# iroh-quinn = { path = "../iroh-quinn/quinn" } +# iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } +# iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } diff --git a/iroh-relay/Cargo.toml b/iroh-relay/Cargo.toml index 9f7bc43c93..8424cfeac9 100644 --- a/iroh-relay/Cargo.toml +++ b/iroh-relay/Cargo.toml @@ -42,8 +42,8 @@ postcard = { version = "1", default-features = false, features = [ "use-std", "experimental-derive", ] } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["rustls-ring"] } -quinn-proto = { package = "iroh-quinn-proto", version = "0.13.0" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["rustls-ring"] } +quinn-proto = { package = "iroh-quinn-proto", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", diff --git a/iroh/Cargo.toml b/iroh/Cargo.toml index ce02aa4d2d..713e0ebb67 100644 --- a/iroh/Cargo.toml +++ b/iroh/Cargo.toml @@ -48,9 +48,9 @@ pin-project = "1" pkarr = { version = "3.7", default-features = false, features = [ "relays", ] } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["rustls-ring"] } -quinn-proto = { package = "iroh-quinn-proto", version = "0.13.0" } -quinn-udp = { package = "iroh-quinn-udp", version = "0.5.7" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["rustls-ring"] } +quinn-proto = { package = "iroh-quinn-proto", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } +quinn-udp = { package = "iroh-quinn-udp", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", @@ -109,7 +109,7 @@ hickory-resolver = "0.25.1" igd-next = { version = "0.16", features = ["aio_tokio"] } netdev = { version = "0.36.0" } portmapper = { version = "0.8", default-features = false } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["runtime-tokio", "rustls-ring"] } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["runtime-tokio", "rustls-ring"] } tokio = { version = "1", features = [ "io-util", "macros", diff --git a/iroh/bench/Cargo.toml b/iroh/bench/Cargo.toml index 8070e6259a..97c46065a7 100644 --- a/iroh/bench/Cargo.toml +++ b/iroh/bench/Cargo.toml @@ -12,7 +12,7 @@ iroh = { path = ".." } iroh-metrics = "0.35" n0-future = "0.1.1" n0-snafu = "0.2.0" -quinn = { package = "iroh-quinn", version = "0.14" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" rcgen = "0.14" rustls = { version = "0.23", default-features = false, features = ["ring"] } diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index cc9a1cc1fd..c3a73b74ab 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -28,28 +28,6 @@ use n0_future::{Stream, time::Duration}; use n0_watcher::Watcher; use nested_enum_utils::common_fields; use pin_project::pin_project; -use snafu::{ResultExt, Snafu, ensure}; -use tracing::{debug, instrument, trace, warn}; -use url::Url; - -#[cfg(wasm_browser)] -use crate::discovery::pkarr::PkarrResolver; -#[cfg(not(wasm_browser))] -use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; -use crate::{ - discovery::{ - ConcurrentDiscovery, Discovery, DiscoveryContext, DiscoveryError, DiscoveryItem, - DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, - Lagged, UserData, pkarr::PkarrPublisher, - }, - magicsock::{self, Handle, NodeIdMappedAddr, OwnAddressSnafu}, - metrics::EndpointMetrics, - net_report::Report, - tls, -}; - -mod rtt_actor; - // Missing still: SendDatagram and ConnectionClose::frame_type's Type. pub use quinn::{ AcceptBi, AcceptUni, AckFrequencyConfig, ApplicationClose, Chunk, ClosedStream, @@ -66,12 +44,29 @@ pub use quinn_proto::{ ServerConfig as CryptoServerConfig, UnsupportedVersion, }, }; +use snafu::{ResultExt, Snafu, ensure}; +use tracing::{debug, instrument, trace, warn}; +use url::Url; -use self::rtt_actor::RttMessage; pub use super::magicsock::{ AddNodeAddrError, ConnectionType, ControlMsg, DirectAddr, DirectAddrInfo, DirectAddrType, RemoteInfo, Source, }; +#[cfg(wasm_browser)] +use crate::discovery::pkarr::PkarrResolver; +#[cfg(not(wasm_browser))] +use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; +use crate::{ + discovery::{ + ConcurrentDiscovery, Discovery, DiscoveryContext, DiscoveryError, DiscoveryItem, + DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, + Lagged, UserData, pkarr::PkarrPublisher, + }, + magicsock::{self, AllPathsMappedAddr, Handle, OwnAddressSnafu}, + metrics::EndpointMetrics, + net_report::Report, + tls, +}; /// The delay to fall back to discovery when direct addresses fail. /// @@ -526,8 +521,6 @@ impl StaticConfig { pub struct Endpoint { /// Handle to the magicsocket/actor msock: Handle, - /// Handle to the actor that resets the quinn RTT estimator - rtt_actor: Arc, /// Configuration structs for quinn, holds the transport config, certificate setup, secret key etc. static_config: Arc, } @@ -634,10 +627,8 @@ impl Endpoint { trace!("created magicsock"); debug!(version = env!("CARGO_PKG_VERSION"), "iroh Endpoint created"); - let metrics = msock.metrics.magicsock.clone(); let ep = Self { msock, - rtt_actor: Arc::new(rtt_actor::RttHandle::new(metrics)), static_config: Arc::new(static_config), }; Ok(ep) @@ -735,13 +726,12 @@ impl Endpoint { self.add_node_addr(node_addr.clone())?; } let node_id = node_addr.node_id; - let direct_addresses = node_addr.direct_addresses.clone(); - let relay_url = node_addr.relay_url.clone(); - // Get the mapped IPv6 address from the magic socket. Quinn will connect to this - // address. Start discovery for this node if it's enabled and we have no valid or - // verified address information for this node. Dropping the discovery cancels any - // still running task. + // When we start a connection we want to send the QUIC Initial packets on all the + // known paths for the remote node. For this we use an AllPathsMappedAddr as + // destination for Quinn. Start discovery for this node if it's enabled and we have + // no valid or verified address information for this node. Dropping the discovery + // cancels any still running task. let (mapped_addr, _discovery_drop_guard) = self .get_mapping_addr_and_maybe_start_discovery(node_addr) .await @@ -754,12 +744,7 @@ impl Endpoint { // Start connecting via quinn. This will time out after 10 seconds if no reachable // address is available. - debug!( - ?mapped_addr, - ?direct_addresses, - ?relay_url, - "Attempting connection..." - ); + debug!(?mapped_addr, "Attempting connection..."); let client_config = { let mut alpn_protocols = vec![alpn.to_vec()]; alpn_protocols.extend(options.additional_alpns); @@ -772,15 +757,12 @@ impl Endpoint { client_config }; + let dest_addr = mapped_addr.private_socket_addr(); let server_name = &tls::name::encode(node_id); let connect = self .msock .endpoint() - .connect_with( - client_config, - mapped_addr.private_socket_addr(), - server_name, - ) + .connect_with(client_config, dest_addr, server_name) .context(QuinnSnafu)?; Ok(Connecting { @@ -1381,7 +1363,7 @@ impl Endpoint { async fn get_mapping_addr_and_maybe_start_discovery( &self, node_addr: NodeAddr, - ) -> Result<(NodeIdMappedAddr, Option), GetMappingAddressError> { + ) -> Result<(AllPathsMappedAddr, Option), GetMappingAddressError> { let node_id = node_addr.node_id; // Only return a mapped addr if we have some way of dialing this node, in other @@ -1392,7 +1374,7 @@ impl Endpoint { None }; match addr { - Some(addr) => { + Some(maddr) => { // We have some way of dialing this node, but that doesn't actually mean // we can actually connect to any of these addresses. // Therefore, we will invoke the discovery service if we haven't received from the @@ -1404,7 +1386,7 @@ impl Endpoint { let discovery = DiscoveryTask::maybe_start_after_delay(self, node_id, delay) .ok() .flatten(); - Ok((addr, discovery)) + Ok((maddr, discovery)) } None => { @@ -1632,8 +1614,7 @@ impl Future for IncomingFuture { Poll::Pending => Poll::Pending, Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { - let conn = Connection { inner }; - try_send_rtt_msg(&conn, this.ep, None); + let conn = Connection::new(inner, None, &this.ep); Poll::Ready(Ok(conn)) } } @@ -1720,18 +1701,18 @@ impl Connecting { pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self> { match self.inner.into_0rtt() { Ok((inner, zrtt_accepted)) => { - let conn = Connection { inner }; - let zrtt_accepted = ZeroRttAccepted { - inner: zrtt_accepted, - _discovery_drop_guard: self._discovery_drop_guard, - }; // This call is why `self.remote_node_id` was introduced. // When we `Connecting::into_0rtt`, then we don't yet have `handshake_data` - // in our `Connection`, thus `try_send_rtt_msg` won't be able to pick up + // in our `Connection`, thus we won't be able to pick up // `Connection::remote_node_id`. // Instead, we provide `self.remote_node_id` here - we know it in advance, // after all. - try_send_rtt_msg(&conn, &self.ep, self.remote_node_id); + let conn = Connection::new(inner, self.remote_node_id, &self.ep); + let zrtt_accepted = ZeroRttAccepted { + inner: zrtt_accepted, + _discovery_drop_guard: self._discovery_drop_guard, + }; + Ok((conn, zrtt_accepted)) } Err(inner) => Err(Self { @@ -1770,8 +1751,7 @@ impl Future for Connecting { Poll::Pending => Poll::Pending, Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { - let conn = Connection { inner }; - try_send_rtt_msg(&conn, this.ep, *this.remote_node_id); + let conn = Connection::new(inner, *this.remote_node_id, &this.ep); Poll::Ready(Ok(conn)) } } @@ -1829,6 +1809,21 @@ pub struct RemoteNodeIdError { } impl Connection { + fn new(inner: quinn::Connection, remote_id: Option, ep: &Endpoint) -> Self { + let conn = Connection { inner }; + + // Grab the remote identity and register this connection + if let Some(remote) = remote_id { + ep.msock.register_connection(remote, &conn.inner); + } else if let Ok(remote) = conn.remote_node_id() { + ep.msock.register_connection(remote, &conn.inner); + } else { + warn!("unable to determine node id for the remote"); + } + + conn + } + /// Initiates a new outgoing unidirectional stream. /// /// Streams are cheap and instantaneous to open unless blocked by flow control. As a @@ -2125,30 +2120,6 @@ impl Connection { } } -/// Try send a message to the rtt-actor. -/// -/// If we can't notify the actor that will impact performance a little, but we can still -/// function. -fn try_send_rtt_msg(conn: &Connection, magic_ep: &Endpoint, remote_node_id: Option) { - // If we can't notify the rtt-actor that's not great but not critical. - let Some(node_id) = remote_node_id.or_else(|| conn.remote_node_id().ok()) else { - warn!(?conn, "failed to get remote node id"); - return; - }; - let Some(conn_type_changes) = magic_ep.conn_type(node_id) else { - warn!(?conn, "failed to create conn_type stream"); - return; - }; - let rtt_msg = RttMessage::NewConnection { - connection: conn.inner.weak_handle(), - conn_type_changes: conn_type_changes.stream(), - node_id, - }; - if let Err(err) = magic_ep.rtt_actor.msg_tx.try_send(rtt_msg) { - warn!(?conn, "rtt-actor not reachable: {err:#}"); - } -} - /// Read a proxy url from the environment, in this order /// /// - `HTTP_PROXY` diff --git a/iroh/src/endpoint/rtt_actor.rs b/iroh/src/endpoint/rtt_actor.rs deleted file mode 100644 index 5bc9e6310f..0000000000 --- a/iroh/src/endpoint/rtt_actor.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Actor which coordinates the congestion controller for the magic socket - -use std::{pin::Pin, sync::Arc, task::Poll}; - -use iroh_base::NodeId; -use n0_future::{ - MergeUnbounded, Stream, StreamExt, - task::{self, AbortOnDropHandle}, -}; -use tokio::sync::mpsc; -use tracing::{Instrument, debug, info_span}; - -use crate::{magicsock::ConnectionType, metrics::MagicsockMetrics}; - -#[derive(Debug)] -pub(super) struct RttHandle { - // We should and some point use this to propagate panics and errors. - pub(super) _handle: AbortOnDropHandle<()>, - pub(super) msg_tx: mpsc::Sender, -} - -impl RttHandle { - pub(super) fn new(metrics: Arc) -> Self { - let mut actor = RttActor { - connection_events: Default::default(), - metrics, - }; - let (msg_tx, msg_rx) = mpsc::channel(16); - let handle = task::spawn( - async move { - actor.run(msg_rx).await; - } - .instrument(info_span!("rtt-actor")), - ); - Self { - _handle: AbortOnDropHandle::new(handle), - msg_tx, - } - } -} - -/// Messages to send to the [`RttActor`]. -#[derive(Debug)] -pub(super) enum RttMessage { - /// Informs the [`RttActor`] of a new connection is should monitor. - NewConnection { - /// The connection. - connection: quinn::WeakConnectionHandle, - /// Path changes for this connection from the magic socket. - conn_type_changes: n0_watcher::Stream>, - /// For reporting-only, the Node ID of this connection. - node_id: NodeId, - }, -} - -/// Actor to coordinate congestion controller state with magic socket state. -/// -/// The magic socket can change the underlying network path, between two nodes. If we can -/// inform the QUIC congestion controller of this event it will work much more efficiently. -#[derive(derive_more::Debug)] -struct RttActor { - /// Stream of connection type changes. - #[debug("MergeUnbounded>")] - connection_events: MergeUnbounded, - metrics: Arc, -} - -#[derive(Debug)] -struct MappedStream { - stream: n0_watcher::Stream>, - node_id: NodeId, - /// Reference to the connection. - connection: quinn::WeakConnectionHandle, - /// This an indiciator of whether this connection was direct before. - /// This helps establish metrics on number of connections that became direct. - was_direct_before: bool, -} - -struct ConnectionEvent { - became_direct: bool, -} - -impl Stream for MappedStream { - type Item = ConnectionEvent; - - /// Performs the congestion controller reset for a magic socket path change. - /// - /// Regardless of which kind of path we are changed to, the congestion controller needs - /// resetting. Even when switching to mixed we should reset the state as e.g. switching - /// from direct to mixed back to direct should be a rare exception and is a bug if this - /// happens commonly. - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - match Pin::new(&mut self.stream).poll_next(cx) { - Poll::Ready(Some(new_conn_type)) => { - let mut became_direct = false; - if self.connection.network_path_changed() { - debug!( - node_id = %self.node_id.fmt_short(), - new_type = ?new_conn_type, - "Congestion controller state reset", - ); - if !self.was_direct_before && matches!(new_conn_type, ConnectionType::Direct(_)) - { - self.was_direct_before = true; - became_direct = true - } - }; - Poll::Ready(Some(ConnectionEvent { became_direct })) - } - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - -impl RttActor { - /// Runs the actor main loop. - /// - /// The main loop will finish when the sender is dropped. - async fn run(&mut self, mut msg_rx: mpsc::Receiver) { - loop { - tokio::select! { - biased; - msg = msg_rx.recv() => { - match msg { - Some(msg) => self.handle_msg(msg), - None => break, - } - } - event = self.connection_events.next(), if !self.connection_events.is_empty() => { - if event.map(|e| e.became_direct).unwrap_or(false) { - self.metrics.connection_became_direct.inc(); - } - } - } - } - debug!("rtt-actor finished"); - } - - /// Handle actor messages. - fn handle_msg(&mut self, msg: RttMessage) { - match msg { - RttMessage::NewConnection { - connection, - conn_type_changes, - node_id, - } => { - self.handle_new_connection(connection, conn_type_changes, node_id); - } - } - } - - /// Handles the new connection message. - fn handle_new_connection( - &mut self, - connection: quinn::WeakConnectionHandle, - conn_type_changes: n0_watcher::Stream>, - node_id: NodeId, - ) { - self.connection_events.push(MappedStream { - stream: conn_type_changes, - connection, - node_id, - was_direct_before: false, - }); - self.metrics.connection_handshake_success.inc(); - } -} diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index ba6f50f507..fe6efdf12d 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -29,7 +29,6 @@ use std::{ }; use bytes::Bytes; -use data_encoding::HEXLOWER; use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl, SecretKey}; use iroh_relay::RelayMap; use n0_future::{ @@ -43,8 +42,9 @@ use nested_enum_utils::common_fields; use netwatch::netmon; #[cfg(not(wasm_browser))] use netwatch::{UdpSocket, ip::LocalAddresses}; -use quinn::{AsyncUdpSocket, ServerConfig}; +use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; use rand::Rng; +use relay_mapped_addrs::{RelayAddrMap, RelayMappedAddr}; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; use tokio::sync::{Mutex as AsyncMutex, mpsc}; @@ -59,7 +59,7 @@ use url::Url; use self::transports::IpTransport; use self::{ metrics::Metrics as MagicsockMetrics, - node_map::{NodeMap, PingAction, PingRole, SendPing}, + node_map::{NodeMap, PingAction}, transports::{RelayActorConfig, RelayTransport, Transports, UdpSender}, }; #[cfg(not(wasm_browser))] @@ -67,18 +67,19 @@ use crate::dns::DnsResolver; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; #[cfg(not(wasm_browser))] -use crate::net_report::{IpMappedAddr, QuicConfig}; +use crate::net_report::QuicConfig; use crate::{ defaults::timeouts::NET_REPORT_TIMEOUT, disco::{self, SendAddr}, discovery::{Discovery, DiscoveryItem, DiscoverySubscribers, NodeData, UserData}, key::{DecryptionError, SharedSecret, public_ed_box, secret_ed_box}, metrics::EndpointMetrics, - net_report::{self, IfStateDetails, IpMappedAddresses, Report}, + net_report::{self, IfStateDetails, Report}, }; mod metrics; mod node_map; +mod relay_mapped_addrs; pub(crate) mod transports; @@ -93,8 +94,18 @@ pub use self::{ /// expire at 30 seconds, so this is a few seconds shy of that. const ENDPOINTS_FRESH_ENOUGH_DURATION: Duration = Duration::from_secs(27); +/// The duration in which we send keep-alives. +/// +/// If a path is idle for this long, a PING frame will be sent to keep the connection +/// alive. const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); +/// The maximum time a path can stay idle before being closed. +/// +/// This is [`HEARTBEAT_INTERVAL`] + 1.5s. This gives us a chance to send a PING frame and +/// some retries. +const MAX_IDLE_TIMEOUT: Duration = Duration::from_millis(6500); + /// Contains options for `MagicSock::listen`. #[derive(derive_more::Debug)] pub(crate) struct Options { @@ -195,8 +206,11 @@ pub(crate) struct MagicSock { ipv6_reported: Arc, /// Tracks the networkmap node entity for each node discovery key. node_map: NodeMap, + /// Tracks existing connections + connection_map: ConnectionMap, + /// Tracks the mapped IP addresses - ip_mapped_addrs: IpMappedAddresses, + relay_mapped_addrs: RelayAddrMap, /// Local addresses local_addrs_watch: LocalAddrsWatch, /// Currently bound IP addresses of all sockets @@ -221,6 +235,22 @@ pub(crate) struct MagicSock { pub(crate) metrics: EndpointMetrics, } +#[derive(Default, Debug)] +struct ConnectionMap { + map: std::sync::Mutex>>, +} + +impl ConnectionMap { + fn insert(&self, remote: NodeId, handle: WeakConnectionHandle) { + self.map + .lock() + .expect("poisoned") + .entry(remote) + .or_default() + .push(handle); + } +} + #[allow(missing_docs)] #[common_fields({ backtrace: Option, @@ -271,6 +301,42 @@ impl MagicSock { self.local_addrs_watch.clone().get() } + /// Registers the connection in the connection map and opens additional paths. + /// + /// In addition to storing the connection reference this requests the current + /// [`NodeAddr`] for remote node from the [`NodeMap`] and adds all paths to the + /// connection. It also listens and logs path events. + pub(crate) fn register_connection(&self, remote: NodeId, conn: &quinn::Connection) { + debug!(%remote, "register connection"); + let weak_handle = conn.weak_handle(); + self.connection_map.insert(remote, weak_handle); + + // TODO: track task + // TODO: find a good home for this + let mut path_events = conn.path_events(); + let _task = task::spawn( + async move { + loop { + match path_events.recv().await { + Ok(event) => { + info!(remote = %remote, "path event: {:?}", event); + } + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { + warn!("lagged path events"); + } + Err(tokio::sync::broadcast::error::RecvError::Closed) => break, + } + } + } + .instrument(info_span!("path events", %remote)), + ); + + // open additional paths + if let Some(addr) = self.node_map.get_current_addr(remote) { + self.add_paths(addr); + } + } + #[cfg(not(wasm_browser))] fn ip_bind_addrs(&self) -> &[SocketAddr] { &self.ip_bind_addrs @@ -373,9 +439,13 @@ impl MagicSock { self.node_map.conn_type(node_id) } - /// Returns the socket address which can be used by the QUIC layer to dial this node. - pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { - self.node_map.get_quic_mapped_addr_for_node_key(node_id) + /// Returns the socket address which can be used by the QUIC layer to *dial* this node. + pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { + self.node_map.get_all_paths_addr_for_node(node_id) + } + + pub(crate) fn get_direct_addrs(&self, node_id: NodeId) -> Vec { + self.node_map.get_direct_addrs(node_id) } /// Add addresses for a node to the magic socket's addresbook. @@ -393,8 +463,18 @@ impl MagicSock { } } if !addr.is_empty() { + // Add addr to the internal NodeMap self.node_map - .add_node_addr(addr, source, &self.metrics.magicsock); + .add_node_addr(addr.clone(), source, &self.metrics.magicsock); + + if let Some(url) = addr.relay_url() { + self.relay_mapped_addrs + .get_or_register(url.clone(), addr.node_id); + } + + // Add paths to the existing connections + self.add_paths(addr); + Ok(()) } else if pruned != 0 { Err(EmptyPrunedSnafu { pruned }.build()) @@ -403,6 +483,74 @@ impl MagicSock { } } + /// Adds all available addresses in the given `addr` as paths + fn add_paths(&self, addr: NodeAddr) { + let mut map = self.connection_map.map.lock().expect("poisoned"); + let mut to_delete = Vec::new(); + if let Some(conns) = map.get_mut(&addr.node_id) { + for (i, conn) in conns.into_iter().enumerate() { + if let Some(conn) = conn.upgrade() { + for addr in addr.direct_addresses() { + let conn = conn.clone(); + let addr = *addr; + task::spawn( + async move { + debug!(%addr, "open path IP"); + match conn + .open_path_ensure(addr, quinn_proto::PathStatus::Available) + .await + { + Ok(path) => { + path.set_max_idle_timeout(Some(MAX_IDLE_TIMEOUT)).ok(); + path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } + } + } + .instrument(info_span!("open path IP")), + ); + } + // Insert the relay addr + if let Some(addr) = self.get_mapping_addr(addr.node_id) { + let conn = conn.clone(); + let addr = addr.private_socket_addr(); + task::spawn( + async move { + debug!(%addr, "open path relay"); + match conn + .open_path_ensure(addr, quinn_proto::PathStatus::Backup) + .await + { + Ok(path) => { + // Keep the relay path open + path.set_max_idle_timeout(None).ok(); + path.set_keep_alive_interval(None).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } + } + } + .instrument(info_span!("open path relay")), + ); + } + } else { + to_delete.push(i); + } + } + // cleanup dead connections + let mut i = 0; + conns.retain(|_| { + let remove = to_delete.contains(&i); + i += 1; + + !remove + }); + } + } + /// Stores a new set of direct addresses. /// /// If the direct addresses have changed from the previous set, they are published to @@ -480,7 +628,11 @@ impl MagicSock { } } - /// Searches the `node_map` to determine the current transports to be used. + /// Returns the transport addresses for the [`quinn_udp::Transmit`]'s destination. + /// + /// Because Quinn does only know about IP transports we map other transports to private + /// IPv6 Unique Local Address ranges. This extracts the transport addresses out of the + /// transmit's destination. #[instrument(skip_all)] fn prepare_send( &self, @@ -504,16 +656,13 @@ impl MagicSock { let mut active_paths = SmallVec::<[_; 3]>::new(); - match MappedAddr::from(transmit.destination) { - MappedAddr::None(dest) => { - error!(%dest, "Cannot convert to a mapped address."); - } - MappedAddr::NodeId(dest) => { + match MultipathMappedAddr::from(transmit.destination) { + MultipathMappedAddr::Mixed(dest) => { trace!( dst = %dest, src = ?transmit.src_ip, len = %transmit.contents.len(), - "sending", + "sending mixed", ); // Get the node's relay address and best direct address, as well @@ -523,17 +672,21 @@ impl MagicSock { self.ipv6_reported.load(Ordering::Relaxed), &self.metrics.magicsock, ) { - Some((node_id, udp_addr, relay_url, ping_actions)) => { + Some((node_id, _udp_addr, _relay_url, ping_actions)) => { if !ping_actions.is_empty() { self.actor_sender .try_send(ActorMessage::PingActions(ping_actions)) .ok(); } - if let Some(addr) = udp_addr { - active_paths.push(transports::Addr::from(addr)); - } - if let Some(url) = relay_url { - active_paths.push(transports::Addr::Relay(url, node_id)); + + if let Some(addr) = self.node_map.get_current_addr(node_id) { + // Mixed will send all available addrs + if let Some(ref url) = addr.relay_url { + active_paths.push(transports::Addr::Relay(url.clone(), node_id)); + } + for ip in addr.direct_addresses() { + active_paths.push(transports::Addr::Ip(*ip)); + } } } None => { @@ -542,19 +695,28 @@ impl MagicSock { } } #[cfg(not(wasm_browser))] - MappedAddr::Ip(dest) => { + MultipathMappedAddr::Ip(addr) => { + trace!( + dst = %addr, + src = ?transmit.src_ip, + len = %transmit.contents.len(), + "sending IP", + ); + active_paths.push(transports::Addr::Ip(addr)); + } + MultipathMappedAddr::Relay(dest) => { trace!( dst = %dest, src = ?transmit.src_ip, len = %transmit.contents.len(), - "sending", + "sending relay", ); // Check if this is a known IpMappedAddr, and if so, send over UDP // Get the socket addr - match self.ip_mapped_addrs.get_ip_addr(&dest) { - Some(addr) => { - active_paths.push(transports::Addr::from(addr)); + match self.relay_mapped_addrs.get_url(&dest) { + Some((relay, node_id)) => { + active_paths.push(transports::Addr::Relay(relay, node_id)); } None => { error!(%dest, "unknown mapped address"); @@ -566,12 +728,12 @@ impl MagicSock { Ok(active_paths) } - /// Process datagrams received from UDP sockets. + /// Process datagrams received from all the transports. /// /// All the `bufs` and `metas` should have initialized packets in them. /// - /// This fixes up the datagrams to use the correct [`NodeIdMappedAddr`] and extracts DISCO - /// packets, processing them inside the magic socket. + /// This fixes up the datagrams to use the correct [`MultipathMappedAddr`] and extracts + /// DISCO packets, processing them inside the magic socket. fn process_datagrams( &self, bufs: &mut [io::IoSliceMut<'_>], @@ -677,29 +839,15 @@ impl MagicSock { // Update the NodeMap and remap RecvMeta to the NodeIdMappedAddr. match self.node_map.receive_udp(*addr) { None => { - // Check if this address is mapped to an IpMappedAddr - if let Some(ip_mapped_addr) = - self.ip_mapped_addrs.get_mapped_addr(addr) - { - trace!( - src = %addr, - count = %quic_datagram_count, - len = quinn_meta.len, - "UDP recv QUIC address discovery packets", - ); - quic_packets_total += quic_datagram_count; - quinn_meta.addr = ip_mapped_addr.private_socket_addr(); - } else { - warn!( - src = %addr, - count = %quic_datagram_count, - len = quinn_meta.len, - "UDP recv quic packets: no node state found, skipping", - ); - // If we have no node state for the from addr, set len to 0 to make - // quinn skip the buf completely. - quinn_meta.len = 0; - } + trace!( + src = %addr, + count = %quic_datagram_count, + len = quinn_meta.len, + "UDP recv quic packets", + ); + + quic_packets_total += quic_datagram_count; + quinn_meta.addr = *addr; } Some((node_id, quic_mapped_addr)) => { trace!( @@ -716,8 +864,11 @@ impl MagicSock { } transports::Addr::Relay(src_url, src_node) => { // Relay - let quic_mapped_addr = self.node_map.receive_relay(src_url, *src_node); - quinn_meta.addr = quic_mapped_addr.private_socket_addr(); + let _quic_mapped_addr = self.node_map.receive_relay(src_url, *src_node); + let mapped_addr = self + .relay_mapped_addrs + .get_or_register(src_url.clone(), *src_node); + quinn_meta.addr = mapped_addr.private_socket_addr(); } } } else { @@ -789,13 +940,8 @@ impl MagicSock { let _guard = span.enter(); trace!("receive disco message"); match dm { - disco::Message::Ping(ping) => { - self.metrics.magicsock.recv_disco_ping.inc(); - self.handle_ping(ping, sender, src); - } - disco::Message::Pong(pong) => { - self.metrics.magicsock.recv_disco_pong.inc(); - self.node_map.handle_pong(sender, src, pong); + disco::Message::Ping(..) | disco::Message::Pong(..) => { + unreachable!("not used anymore"); } disco::Message::CallMeMaybe(cm) => { self.metrics.magicsock.recv_disco_call_me_maybe.inc(); @@ -814,98 +960,27 @@ impl MagicSock { return; } } - let ping_actions = - self.node_map - .handle_call_me_maybe(sender, cm, &self.metrics.magicsock); - for action in ping_actions { - match action { - PingAction::SendCallMeMaybe { .. } => { - warn!("Unexpected CallMeMaybe as response of handling a CallMeMaybe"); - } - PingAction::SendPing(ping) => { - self.send_ping_queued(ping); - } - } - } - } - } - trace!("disco message handled"); - } - - /// Handle a ping message. - fn handle_ping(&self, dm: disco::Ping, sender: NodeId, src: &transports::Addr) { - // Insert the ping into the node map, and return whether a ping with this tx_id was already - // received. - let addr: SendAddr = src.clone().into(); - let handled = self.node_map.handle_ping(sender, addr.clone(), dm.tx_id); - match handled.role { - PingRole::Duplicate => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: path already confirmed, skip"); - return; - } - PingRole::LikelyHeartbeat => {} - PingRole::NewPath => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: new path"); - } - PingRole::Activate => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: path active"); - } - } - // Send a pong. - debug!(tx = %HEXLOWER.encode(&dm.tx_id), %addr, dstkey = %sender.fmt_short(), - "sending pong"); - let pong = disco::Message::Pong(disco::Pong { - tx_id: dm.tx_id, - ping_observed_addr: addr.clone(), - }); - event!( - target: "iroh::_events::pong::sent", - Level::DEBUG, - remote_node = %sender.fmt_short(), - dst = ?addr, - txn = ?dm.tx_id, - ); - - if !self.disco.try_send(addr.clone(), sender, pong) { - warn!(%addr, "failed to queue pong"); - } - - if let Some(ping) = handled.needs_ping_back { - debug!( - %addr, - dstkey = %sender.fmt_short(), - "sending direct ping back", - ); - self.send_ping_queued(ping); - } - } + // Add new addresses as paths + self.add_paths(NodeAddr { + node_id: sender, + relay_url: None, + direct_addresses: cm.my_numbers.iter().copied().collect(), + }); - fn send_ping_queued(&self, ping: SendPing) { - let SendPing { - id, - dst, - dst_node, - tx_id, - purpose, - } = ping; - let msg = disco::Message::Ping(disco::Ping { - tx_id, - node_key: self.public_key, - }); - let sent = self.disco.try_send(dst.clone(), dst_node, msg); - if sent { - let msg_sender = self.actor_sender.clone(); - trace!(%dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "ping sent (queued)"); - self.node_map - .notify_ping_sent(id, dst, tx_id, purpose, msg_sender); - } else { - warn!(dst = ?dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "failed to send ping: queues full"); + self.node_map + .handle_call_me_maybe(sender, cm, &self.metrics.magicsock); + } } + trace!("disco message handled"); } /// Send the given ping actions out. - async fn send_ping_actions(&self, sender: &UdpSender, msgs: Vec) -> io::Result<()> { + async fn send_ping_actions( + &self, + _sender: &UdpSender, + msgs: Vec, + ) -> io::Result<()> { for msg in msgs { // Abort sending as soon as we know we are shutting down. if self.is_closing() || self.is_closed() { @@ -950,25 +1025,6 @@ impl MagicSock { } } } - PingAction::SendPing(SendPing { - id, - dst, - dst_node, - tx_id, - purpose, - }) => { - let msg = disco::Message::Ping(disco::Ping { - tx_id, - node_key: self.public_key, - }); - - self.send_disco_message(sender, dst.clone(), dst_node, msg) - .await?; - debug!(%dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "ping sent"); - let msg_sender = self.actor_sender.clone(); - self.node_map - .notify_ping_sent(id, dst, tx_id, purpose, msg_sender); - } } } Ok(()) @@ -1042,27 +1098,35 @@ impl MagicSock { } } +/// Definies the translation of addresses in quinn land vs iroh land. +/// +/// This is necessary, because quinn can only reason about `SocketAddr`s. #[derive(Clone, Debug)] -enum MappedAddr { - NodeId(NodeIdMappedAddr), +pub(crate) enum MultipathMappedAddr { + /// Used for the initial connection. + /// - Only used for sending + /// - This means send on all known paths/transports + Mixed(AllPathsMappedAddr), + /// Relay based transport address + Relay(RelayMappedAddr), + /// IP based transport address #[cfg(not(wasm_browser))] - Ip(IpMappedAddr), - None(SocketAddr), + Ip(SocketAddr), } -impl From for MappedAddr { +impl From for MultipathMappedAddr { fn from(value: SocketAddr) -> Self { match value.ip() { - IpAddr::V4(_) => MappedAddr::None(value), + IpAddr::V4(_) => Self::Ip(value), IpAddr::V6(addr) => { - if let Ok(node_id_mapped_addr) = NodeIdMappedAddr::try_from(addr) { - return MappedAddr::NodeId(node_id_mapped_addr); + if let Ok(node_id_mapped_addr) = AllPathsMappedAddr::try_from(addr) { + return Self::Mixed(node_id_mapped_addr); } #[cfg(not(wasm_browser))] - if let Ok(ip_mapped_addr) = IpMappedAddr::try_from(addr) { - return MappedAddr::Ip(ip_mapped_addr); + if let Ok(ip_mapped_addr) = RelayMappedAddr::try_from(addr) { + return Self::Relay(ip_mapped_addr); } - MappedAddr::None(value) + Self::Ip(value) } } } @@ -1250,7 +1314,7 @@ impl Handle { let (ip_transports, port_mapper) = bind_ip(addr_v4, addr_v6, &metrics).context(BindSocketsSnafu)?; - let ip_mapped_addrs = IpMappedAddresses::default(); + let relay_mapped_addrs = RelayAddrMap::default(); let (actor_sender, actor_receiver) = mpsc::channel(256); @@ -1298,7 +1362,8 @@ impl Handle { actor_sender: actor_sender.clone(), ipv6_reported, node_map, - ip_mapped_addrs: ip_mapped_addrs.clone(), + connection_map: Default::default(), + relay_mapped_addrs, discovery, discovery_user_data: RwLock::new(discovery_user_data), direct_addrs: Default::default(), @@ -1370,7 +1435,6 @@ impl Handle { #[cfg(not(wasm_browser))] dns_resolver, #[cfg(not(wasm_browser))] - Some(ip_mapped_addrs), relay_map.clone(), net_report_config, metrics.net_report.clone(), @@ -1644,7 +1708,6 @@ impl AsyncUdpSocket for MagicUdpSocket { #[derive(Debug)] enum ActorMessage { PingActions(Vec), - EndpointPingExpired(usize, stun_rs::TransactionId), NetworkChange, ScheduleDirectAddrUpdate(UpdateReason, Option<(NodeId, RelayUrl)>), #[cfg(test)] @@ -1939,9 +2002,6 @@ impl Actor { /// Returns `true` if it was a shutdown. async fn handle_actor_message(&mut self, msg: ActorMessage, sender: &UdpSender) { match msg { - ActorMessage::EndpointPingExpired(id, txid) => { - self.msock.node_map.notify_ping_timeout(id, txid); - } ActorMessage::NetworkChange => { self.network_monitor.network_change().await.ok(); } @@ -2255,21 +2315,17 @@ impl DiscoveredDirectAddrs { } } -/// The fake address used by the QUIC layer to address a node. +/// An address used by the QUIC layer to address a node on all paths. /// -/// You can consider this as nothing more than a lookup key for a node the [`MagicSock`] knows -/// about. -/// -/// [`MagicSock`] can reach a node by several real socket addresses, or maybe even via the relay -/// node. The QUIC layer however needs to address a node by a stable [`SocketAddr`] so -/// that normal socket APIs can function. Thus when a new node is introduced to a [`MagicSock`] -/// it is given a new fake address. This is the type of that address. +/// This is only used for initially connecting to a remote node. We instruct Quinn to send +/// to this address, and duplicate all packets for this address to send on all paths we know +/// for the node. /// /// It is but a newtype. And in our QUIC-facing socket APIs like [`AsyncUdpSocket`] it /// comes in as the inner [`Ipv6Addr`], in those interfaces we have to be careful to do /// the conversion to this type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct NodeIdMappedAddr(Ipv6Addr); +pub(crate) struct AllPathsMappedAddr(Ipv6Addr); /// Can occur when converting a [`SocketAddr`] to an [`NodeIdMappedAddr`] #[derive(Debug, Snafu)] @@ -2279,7 +2335,7 @@ pub struct NodeIdMappedAddrError; /// Counter to always generate unique addresses for [`NodeIdMappedAddr`]. static NODE_ID_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); -impl NodeIdMappedAddr { +impl AllPathsMappedAddr { /// The Prefix/L of our Unique Local Addresses. const ADDR_PREFIXL: u8 = 0xfd; /// The Global ID used in our Unique Local Addresses. @@ -2317,7 +2373,7 @@ impl NodeIdMappedAddr { } } -impl TryFrom for NodeIdMappedAddr { +impl TryFrom for AllPathsMappedAddr { type Error = NodeIdMappedAddrError; fn try_from(value: Ipv6Addr) -> Result { @@ -2332,7 +2388,7 @@ impl TryFrom for NodeIdMappedAddr { } } -impl std::fmt::Display for NodeIdMappedAddr { +impl std::fmt::Display for AllPathsMappedAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "NodeIdMappedAddr({})", self.0) } @@ -2422,7 +2478,7 @@ mod tests { use tracing::{Instrument, error, info, info_span, instrument}; use tracing_test::traced_test; - use super::{NodeIdMappedAddr, Options}; + use super::{AllPathsMappedAddr, Options}; use crate::{ Endpoint, RelayMap, RelayMode, SecretKey, dns::DnsResolver, @@ -2624,10 +2680,12 @@ mod tests { info!("stats: {:#?}", stats); // TODO: ensure panics in this function are reported ok if matches!(loss, ExpectedLoss::AlmostNone) { - assert!( - stats.path.lost_packets < 10, - "[receiver] should not loose many packets", - ); + for (id, path) in &stats.paths { + assert!( + path.lost_packets < 10, + "[receiver] path {id:?} should not loose many packets", + ); + } } info!("close"); @@ -2675,10 +2733,12 @@ mod tests { let stats = conn.stats(); info!("stats: {:#?}", stats); if matches!(loss, ExpectedLoss::AlmostNone) { - assert!( - stats.path.lost_packets < 10, - "[sender] should not loose many packets", - ); + for (id, path) in &stats.paths { + assert!( + path.lost_packets < 10, + "[sender] path {id:?} should not loose many packets", + ); + } } info!("close"); @@ -2995,7 +3055,7 @@ mod tests { async fn magicsock_connect( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - addr: NodeIdMappedAddr, + addr: AllPathsMappedAddr, node_id: NodeId, ) -> Result { // Endpoint::connect sets this, do the same to have similar behaviour. @@ -3021,7 +3081,7 @@ mod tests { async fn magicsock_connect_with_transport_config( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - mapped_addr: NodeIdMappedAddr, + mapped_addr: AllPathsMappedAddr, node_id: NodeId, transport_config: Arc, ) -> Result { @@ -3056,7 +3116,7 @@ mod tests { let msock_1 = magicsock_ep(secret_key_1.clone()).await.unwrap(); // Generate an address not present in the NodeMap. - let bad_addr = NodeIdMappedAddr::generate(); + let bad_addr = AllPathsMappedAddr::generate(); // 500ms is rather fast here. Running this locally it should always be the correct // timeout. If this is too slow however the test will not become flaky as we are @@ -3185,6 +3245,7 @@ mod tests { }, &msock_1.metrics.magicsock, ); + let addr_2 = msock_1.get_mapping_addr(node_id_2).unwrap(); // Set a low max_idle_timeout so quinn gives up on this quickly and our test does @@ -3211,23 +3272,24 @@ mod tests { info!("first connect timed out as expected"); // Provide correct addressing information - msock_1.node_map.add_node_addr( - NodeAddr { - node_id: node_id_2, - relay_url: None, - direct_addresses: msock_2 - .direct_addresses() - .initialized() - .await - .into_iter() - .map(|x| x.addr) - .collect(), - }, - Source::NamedApp { - name: "test".into(), - }, - &msock_1.metrics.magicsock, - ); + msock_1 + .add_node_addr( + NodeAddr { + node_id: node_id_2, + relay_url: None, + direct_addresses: msock_2 + .direct_addresses() + .initialized() + .await + .into_iter() + .map(|x| x.addr) + .collect(), + }, + Source::NamedApp { + name: "test".into(), + }, + ) + .unwrap(); // We can now connect tokio::time::timeout(Duration::from_secs(10), async move { diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index a67a570c3d..96acd7ea14 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -8,12 +8,11 @@ use std::{ use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl}; use n0_future::time::Instant; use serde::{Deserialize, Serialize}; -use stun_rs::TransactionId; use tracing::{debug, info, instrument, trace, warn}; -use self::node_state::{NodeState, Options, PingHandled}; -use super::{ActorMessage, NodeIdMappedAddr, metrics::Metrics, transports}; -use crate::disco::{CallMeMaybe, Pong, SendAddr}; +use self::node_state::{NodeState, Options}; +use super::{AllPathsMappedAddr, metrics::Metrics}; +use crate::disco::CallMeMaybe; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; @@ -22,8 +21,8 @@ mod path_state; mod path_validity; mod udp_paths; +pub(super) use node_state::PingAction; pub use node_state::{ConnectionType, ControlMsg, DirectAddrInfo, RemoteInfo}; -pub(super) use node_state::{DiscoPingPurpose, PingAction, PingRole, SendPing}; /// Number of nodes that are inactive for which we keep info about. This limit is enforced /// periodically via [`NodeMap::prune_inactive`]. @@ -55,7 +54,7 @@ pub(super) struct NodeMap { pub(super) struct NodeMapInner { by_node_key: HashMap, by_ip_port: HashMap, - by_quic_mapped_addr: HashMap, + by_quic_mapped_addr: HashMap, by_id: HashMap, next_id: usize, #[cfg(any(test, feature = "test-utils"))] @@ -68,9 +67,8 @@ pub(super) struct NodeMapInner { /// have for the node. These are all the keys the [`NodeMap`] can use. #[derive(Debug, Clone)] enum NodeStateKey { - Idx(usize), NodeId(NodeId), - NodeIdMappedAddr(NodeIdMappedAddr), + NodeIdMappedAddr(AllPathsMappedAddr), IpPort(IpPort), } @@ -157,95 +155,62 @@ impl NodeMap { pub(super) fn receive_udp( &self, udp_addr: SocketAddr, - ) -> Option<(PublicKey, NodeIdMappedAddr)> { + ) -> Option<(PublicKey, AllPathsMappedAddr)> { self.inner.lock().expect("poisoned").receive_udp(udp_addr) } - pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { + pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> AllPathsMappedAddr { self.inner .lock() .expect("poisoned") .receive_relay(relay_url, src) } - pub(super) fn notify_ping_sent( - &self, - id: usize, - dst: SendAddr, - tx_id: stun_rs::TransactionId, - purpose: DiscoPingPurpose, - msg_sender: tokio::sync::mpsc::Sender, - ) { - if let Some(ep) = self - .inner - .lock() - .expect("poisoned") - .get_mut(NodeStateKey::Idx(id)) - { - ep.ping_sent(dst, tx_id, purpose, msg_sender); - } - } - - pub(super) fn notify_ping_timeout(&self, id: usize, tx_id: stun_rs::TransactionId) { - if let Some(ep) = self - .inner - .lock() - .expect("poisoned") - .get_mut(NodeStateKey::Idx(id)) - { - ep.ping_timeout(tx_id, Instant::now()); - } - } - - pub(super) fn get_quic_mapped_addr_for_node_key( + pub(super) fn get_all_paths_addr_for_node( &self, node_key: NodeId, - ) -> Option { + ) -> Option { self.inner .lock() .expect("poisoned") .get(NodeStateKey::NodeId(node_key)) - .map(|ep| *ep.quic_mapped_addr()) + .map(|ep| *ep.all_paths_mapped_addr()) } - /// Insert a received ping into the node map, and return whether a ping with this tx_id was already - /// received. - pub(super) fn handle_ping( - &self, - sender: PublicKey, - src: SendAddr, - tx_id: TransactionId, - ) -> PingHandled { + pub(super) fn get_direct_addrs(&self, node_key: NodeId) -> Vec { self.inner .lock() .expect("poisoned") - .handle_ping(sender, src, tx_id) + .get(NodeStateKey::NodeId(node_key)) + .map(|ep| ep.direct_addresses().map(Into::into).collect()) + .unwrap_or_default() } - pub(super) fn handle_pong(&self, sender: PublicKey, src: &transports::Addr, pong: Pong) { + /// Returns a [`NodeAddr`] with all the currently known direct addresses and the relay URL. + pub(super) fn get_current_addr(&self, node_key: NodeId) -> Option { self.inner .lock() .expect("poisoned") - .handle_pong(sender, src, pong) + .get(NodeStateKey::NodeId(node_key)) + .map(|ep| ep.get_current_addr()) } - #[must_use = "actions must be handled"] pub(super) fn handle_call_me_maybe( &self, sender: PublicKey, cm: CallMeMaybe, metrics: &Metrics, - ) -> Vec { + ) { self.inner .lock() .expect("poisoned") - .handle_call_me_maybe(sender, cm, metrics) + .handle_call_me_maybe(sender, cm, metrics); } #[allow(clippy::type_complexity)] pub(super) fn get_send_addrs( &self, - addr: NodeIdMappedAddr, + addr: AllPathsMappedAddr, have_ipv6: bool, metrics: &Metrics, ) -> Option<( @@ -394,7 +359,7 @@ impl NodeMapInner { node.remove_direct_addr(&ipp, now, why); if node.direct_addresses().count() == 0 { let node_id = node.public_key(); - let mapped_addr = node.quic_mapped_addr(); + let mapped_addr = node.all_paths_mapped_addr(); self.by_node_key.remove(node_id); self.by_quic_mapped_addr.remove(mapped_addr); debug!(node_id=%node_id.fmt_short(), why, "removing node"); @@ -406,7 +371,6 @@ impl NodeMapInner { fn get_id(&self, id: NodeStateKey) -> Option { match id { - NodeStateKey::Idx(id) => Some(id), NodeStateKey::NodeId(node_key) => self.by_node_key.get(&node_key).copied(), NodeStateKey::NodeIdMappedAddr(addr) => self.by_quic_mapped_addr.get(&addr).copied(), NodeStateKey::IpPort(ipp) => self.by_ip_port.get(&ipp).copied(), @@ -440,18 +404,21 @@ impl NodeMapInner { /// Marks the node we believe to be at `ipp` as recently used. #[cfg(not(wasm_browser))] - fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, NodeIdMappedAddr)> { + fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, AllPathsMappedAddr)> { let ip_port: IpPort = udp_addr.into(); let Some(node_state) = self.get_mut(NodeStateKey::IpPort(ip_port)) else { trace!(src=%udp_addr, "receive_udp: no node_state found for addr, ignore"); return None; }; node_state.receive_udp(ip_port, Instant::now()); - Some((*node_state.public_key(), *node_state.quic_mapped_addr())) + Some(( + *node_state.public_key(), + *node_state.all_paths_mapped_addr(), + )) } #[instrument(skip_all, fields(src = %src.fmt_short()))] - fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { + fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> AllPathsMappedAddr { #[cfg(any(test, feature = "test-utils"))] let path_selection = self.path_selection; let node_state = self.get_or_insert_with(NodeStateKey::NodeId(src), || { @@ -466,7 +433,7 @@ impl NodeMapInner { } }); node_state.receive_relay(relay_url, src, Instant::now()); - *node_state.quic_mapped_addr() + *node_state.all_paths_mapped_addr() } fn node_states(&self) -> impl Iterator { @@ -502,25 +469,7 @@ impl NodeMapInner { .map(|ep| ep.conn_type()) } - fn handle_pong(&mut self, sender: NodeId, src: &transports::Addr, pong: Pong) { - if let Some(ns) = self.get_mut(NodeStateKey::NodeId(sender)).as_mut() { - let insert = ns.handle_pong(&pong, src.clone().into()); - if let Some((src, key)) = insert { - self.set_node_key_for_ip_port(src, &key); - } - trace!(?insert, "received pong") - } else { - warn!("received pong: node unknown, ignore") - } - } - - #[must_use = "actions must be handled"] - fn handle_call_me_maybe( - &mut self, - sender: NodeId, - cm: CallMeMaybe, - metrics: &Metrics, - ) -> Vec { + fn handle_call_me_maybe(&mut self, sender: NodeId, cm: CallMeMaybe, metrics: &Metrics) { let ns_id = NodeStateKey::NodeId(sender); if let Some(id) = self.get_id(ns_id.clone()) { for number in &cm.my_numbers { @@ -532,43 +481,13 @@ impl NodeMapInner { None => { debug!("received call-me-maybe: ignore, node is unknown"); metrics.recv_disco_call_me_maybe_bad_disco.inc(); - vec![] } Some(ns) => { debug!(endpoints = ?cm.my_numbers, "received call-me-maybe"); - ns.handle_call_me_maybe(cm) - } - } - } - - fn handle_ping(&mut self, sender: NodeId, src: SendAddr, tx_id: TransactionId) -> PingHandled { - #[cfg(any(test, feature = "test-utils"))] - let path_selection = self.path_selection; - let node_state = self.get_or_insert_with(NodeStateKey::NodeId(sender), || { - debug!("received ping: node unknown, add to node map"); - let source = if src.is_relay() { - Source::Relay - } else { - Source::Udp - }; - Options { - node_id: sender, - relay_url: src.relay_url(), - active: true, - source, - #[cfg(any(test, feature = "test-utils"))] - path_selection, - } - }); - - let handled = node_state.handle_ping(src.clone(), tx_id); - if let SendAddr::Udp(ref addr) = src { - if matches!(handled.role, PingRole::NewPath) { - self.set_node_key_for_ip_port(*addr, &sender); + ns.handle_call_me_maybe(cm); } } - handled } /// Inserts a new node into the [`NodeMap`]. @@ -585,7 +504,7 @@ impl NodeMapInner { // update indices self.by_quic_mapped_addr - .insert(*node_state.quic_mapped_addr(), id); + .insert(*node_state.all_paths_mapped_addr(), id); self.by_node_key.insert(*node_state.public_key(), id); self.by_id.insert(id, node_state); @@ -656,7 +575,7 @@ impl NodeMapInner { self.by_ip_port.remove(&ip_port); } - self.by_quic_mapped_addr.remove(ep.quic_mapped_addr()); + self.by_quic_mapped_addr.remove(ep.all_paths_mapped_addr()); } } } @@ -706,6 +625,7 @@ mod tests { use tracing_test::traced_test; use super::{node_state::MAX_INACTIVE_DIRECT_ADDRESSES, *}; + use crate::disco::SendAddr; impl NodeMap { #[track_caller] @@ -838,7 +758,7 @@ mod tests { let txid = stun_rs::TransactionId::from([i as u8; 12]); // Note that this already invokes .prune_direct_addresses() because these are // new UDP paths. - endpoint.handle_ping(addr, txid); + // endpoint.handle_ping(addr, txid); } info!("Pruning addresses"); diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 5a84f5ae37..b86d2f8802 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -1,19 +1,13 @@ use std::{ - collections::{BTreeSet, HashMap, btree_map::Entry}, - hash::Hash, + collections::{BTreeSet, HashMap}, net::{IpAddr, SocketAddr}, sync::atomic::AtomicBool, }; -use data_encoding::HEXLOWER; use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl}; -use n0_future::{ - task::{self, AbortOnDropHandle}, - time::{self, Duration, Instant}, -}; +use n0_future::time::{Duration, Instant}; use n0_watcher::Watchable; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; use tracing::{Level, debug, event, info, instrument, trace, warn}; use super::{ @@ -26,7 +20,7 @@ use crate::endpoint::PathSelection; use crate::{ disco::{self, SendAddr}, magicsock::{ - ActorMessage, HEARTBEAT_INTERVAL, MagicsockMetrics, NodeIdMappedAddr, + AllPathsMappedAddr, HEARTBEAT_INTERVAL, MagicsockMetrics, node_map::path_validity::PathValidity, }, }; @@ -39,9 +33,6 @@ pub(super) const MAX_INACTIVE_DIRECT_ADDRESSES: usize = 20; /// How long since an endpoint path was last alive before it might be pruned. const LAST_ALIVE_PRUNE_DURATION: Duration = Duration::from_secs(120); -/// How long we wait for a pong reply before assuming it's never coming. -const PING_TIMEOUT_DURATION: Duration = Duration::from_secs(5); - /// The latency at or under which we don't try to upgrade to a better path. const GOOD_ENOUGH_LATENCY: Duration = Duration::from_millis(5); @@ -52,46 +43,12 @@ pub(super) const SESSION_ACTIVE_TIMEOUT: Duration = Duration::from_secs(45); /// How often we try to upgrade to a better patheven if we have some non-relay route that works. const UPGRADE_INTERVAL: Duration = Duration::from_secs(60); -/// How long until we send a stayin alive ping -const STAYIN_ALIVE_MIN_ELAPSED: Duration = Duration::from_secs(2); - #[derive(Debug)] pub(in crate::magicsock) enum PingAction { SendCallMeMaybe { relay_url: RelayUrl, dst_node: NodeId, }, - SendPing(SendPing), -} - -#[derive(Debug)] -pub(in crate::magicsock) struct SendPing { - pub id: usize, - pub dst: SendAddr, - pub dst_node: NodeId, - pub tx_id: stun_rs::TransactionId, - pub purpose: DiscoPingPurpose, -} - -/// Indicating an [`NodeState`] has handled a ping. -#[derive(Debug)] -pub struct PingHandled { - /// What this ping did to the [`NodeState`]. - pub role: PingRole, - /// Whether the sender path should also be pinged. - /// - /// This is the case if an [`NodeState`] does not yet have a direct path, i.e. it has no - /// best_addr. In this case we want to ping right back to open the direct path in this - /// direction as well. - pub needs_ping_back: Option, -} - -#[derive(Debug)] -pub enum PingRole { - Duplicate, - NewPath, - LikelyHeartbeat, - Activate, } /// An iroh node, which we can have connections with. @@ -106,17 +63,14 @@ pub(super) struct NodeState { /// [`NodeMap`]: super::NodeMap id: usize, /// The UDP address used on the QUIC-layer to address this node. - quic_mapped_addr: NodeIdMappedAddr, + quic_mapped_addr: AllPathsMappedAddr, /// The global identifier for this endpoint. node_id: NodeId, - /// The last time we pinged all endpoints. - last_full_ping: Option, /// The url of relay node that we can relay over to communicate. /// /// The fallback/bootstrap path, if non-zero (non-zero for well-behaved clients). relay_url: Option<(RelayUrl, PathState)>, udp_paths: NodeUdpPaths, - sent_pings: HashMap, /// Last time this node was used. /// /// A node is marked as in use when sending datagrams to them, or when having received @@ -159,7 +113,7 @@ pub(super) struct Options { impl NodeState { pub(super) fn new(id: usize, options: Options) -> Self { - let quic_mapped_addr = NodeIdMappedAddr::generate(); + let quic_mapped_addr = AllPathsMappedAddr::generate(); // TODO(frando): I don't think we need to track the `num_relay_conns_added` // metric here. We do so in `Self::addr_for_send`. @@ -174,7 +128,6 @@ impl NodeState { id, quic_mapped_addr, node_id: options.node_id, - last_full_ping: None, relay_url: options.relay_url.map(|url| { ( url.clone(), @@ -182,7 +135,6 @@ impl NodeState { ) }), udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), last_used: options.active.then(Instant::now), last_call_me_maybe: None, conn_type: Watchable::new(ConnectionType::None), @@ -196,7 +148,7 @@ impl NodeState { &self.node_id } - pub(super) fn quic_mapped_addr(&self) -> &NodeIdMappedAddr { + pub(super) fn all_paths_mapped_addr(&self) -> &AllPathsMappedAddr { &self.quic_mapped_addr } @@ -211,32 +163,7 @@ impl NodeState { /// Returns info about this node. pub(super) fn info(&self, now: Instant) -> RemoteInfo { let conn_type = self.conn_type.get(); - let latency = match conn_type { - ConnectionType::Direct(addr) => self - .udp_paths - .paths - .get(&addr.into()) - .and_then(|state| state.latency()), - ConnectionType::Relay(ref url) => self - .relay_url - .as_ref() - .filter(|(relay_url, _)| relay_url == url) - .and_then(|(_, state)| state.latency()), - ConnectionType::Mixed(addr, ref url) => { - let addr_latency = self - .udp_paths - .paths - .get(&addr.into()) - .and_then(|state| state.latency()); - let relay_latency = self - .relay_url - .as_ref() - .filter(|(relay_url, _)| relay_url == url) - .and_then(|(_, state)| state.latency()); - addr_latency.min(relay_latency) - } - ConnectionType::None => None, - }; + let latency = None; let addrs = self .udp_paths @@ -396,10 +323,6 @@ impl NodeState { #[instrument("want_call_me_maybe", skip_all)] fn want_call_me_maybe(&self, now: &Instant) -> bool { trace!("full ping: wanted?"); - let Some(last_full_ping) = self.last_full_ping else { - debug!("no previous full ping: need full ping"); - return true; - }; match &self.udp_paths.best { UdpSendAddr::None | UdpSendAddr::Unconfirmed(_) => { debug!("best addr not set: need full ping"); @@ -417,7 +340,7 @@ impl NodeState { .expect("send path not tracked?") .latency() .expect("send_addr marked valid incorrectly"); - if latency > GOOD_ENOUGH_LATENCY && *now - last_full_ping >= UPGRADE_INTERVAL { + if latency > GOOD_ENOUGH_LATENCY { debug!( "full ping interval expired and latency is only {}ms: need full ping", latency.as_millis() @@ -437,131 +360,6 @@ impl NodeState { false } - /// Cleanup the expired ping for the passed in txid. - #[instrument("disco", skip_all, fields(node = %self.node_id.fmt_short()))] - pub(super) fn ping_timeout(&mut self, txid: stun_rs::TransactionId, now: Instant) { - if let Some(sp) = self.sent_pings.remove(&txid) { - debug!(tx = %HEXLOWER.encode(&txid), addr = %sp.to, "pong not received in timeout"); - match sp.to { - SendAddr::Udp(addr) => { - if let Some(path_state) = self.udp_paths.paths.get_mut(&addr.into()) { - path_state.last_ping = None; - let consider_alive = path_state - .last_alive() - .map(|last_alive| last_alive.elapsed() <= PING_TIMEOUT_DURATION) - .unwrap_or(false); - if !consider_alive { - // If there was no sign of life from this path during the time - // which we should have received the pong, clear best addr and - // pong. Both are used to select this path again, but we know - // it's not a usable path now. - path_state.validity = PathValidity::empty(); - self.udp_paths.update_to_best_addr(now); - } - } else { - // If we have no state for the best addr it should have been cleared - // anyway. - self.udp_paths.update_to_best_addr(now); - } - } - SendAddr::Relay(ref url) => { - if let Some((home_relay, relay_state)) = self.relay_url.as_mut() { - if home_relay == url { - // lost connectivity via relay - relay_state.last_ping = None; - } - } - } - } - } - } - - #[must_use = "pings must be handled"] - fn start_ping(&self, dst: SendAddr, purpose: DiscoPingPurpose) -> Option { - #[cfg(any(test, feature = "test-utils"))] - if self.path_selection == PathSelection::RelayOnly && !dst.is_relay() { - // don't attempt any hole punching in relay only mode - warn!("in `RelayOnly` mode, ignoring request to start a hole punching attempt."); - return None; - } - #[cfg(wasm_browser)] - if !dst.is_relay() { - return None; // Similar to `RelayOnly` mode, we don't send UDP pings for hole-punching. - } - - let tx_id = stun_rs::TransactionId::default(); - trace!(tx = %HEXLOWER.encode(&tx_id), %dst, ?purpose, - dst = %self.node_id.fmt_short(), "start ping"); - event!( - target: "iroh::_events::ping::sent", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - ?dst, - txn = ?tx_id, - ?purpose, - ); - Some(SendPing { - id: self.id, - dst, - dst_node: self.node_id, - tx_id, - purpose, - }) - } - - /// Record the fact that a ping has been sent out. - pub(super) fn ping_sent( - &mut self, - to: SendAddr, - tx_id: stun_rs::TransactionId, - purpose: DiscoPingPurpose, - sender: mpsc::Sender, - ) { - trace!(%to, tx = %HEXLOWER.encode(&tx_id), ?purpose, "record ping sent"); - - let now = Instant::now(); - let mut path_found = false; - match to { - SendAddr::Udp(addr) => { - if let Some(st) = self.udp_paths.paths.get_mut(&addr.into()) { - st.last_ping.replace(now); - path_found = true - } - } - SendAddr::Relay(ref url) => { - if let Some((home_relay, relay_state)) = self.relay_url.as_mut() { - if home_relay == url { - relay_state.last_ping.replace(now); - path_found = true - } - } - } - } - if !path_found { - // Shouldn't happen. But don't ping an endpoint that's not active for us. - warn!(%to, ?purpose, "unexpected attempt to ping no longer live path"); - return; - } - - let id = self.id; - let _expiry_task = AbortOnDropHandle::new(task::spawn(async move { - time::sleep(PING_TIMEOUT_DURATION).await; - sender - .send(ActorMessage::EndpointPingExpired(id, tx_id)) - .await - .ok(); - })); - self.sent_pings.insert( - tx_id, - SentPing { - to, - at: now, - purpose, - _expiry_task, - }, - ); - } - /// Send a DISCO call-me-maybe message to the peer. /// /// This takes care of sending the needed pings beforehand. This ensures that we open @@ -588,11 +386,7 @@ impl NodeState { } } } - // We send pings regardless of whether we have a RelayUrl. If we were given any - // direct address paths to contact but no RelayUrl, we still need to send a DISCO - // ping to the direct address paths so that the other node will learn about us and - // accepts the connection. - let mut msgs = self.send_pings(now); + let mut msgs = Vec::new(); if let Some(url) = self.relay_url() { debug!(%url, "queue call-me-maybe"); @@ -608,59 +402,6 @@ impl NodeState { msgs } - /// Send DISCO Pings to all the paths of this node. - /// - /// Any paths to the node which have not been recently pinged will be sent a disco - /// ping. - /// - /// The caller is responsible for sending the messages. - #[must_use = "actions must be handled"] - fn send_pings(&mut self, now: Instant) -> Vec { - // We allocate +1 in case the caller wants to add a call-me-maybe message. - let mut ping_msgs = Vec::with_capacity(self.udp_paths.paths.len() + 1); - - if let Some((url, state)) = self.relay_url.as_ref() { - if state.needs_ping(&now) { - debug!(%url, "relay path needs ping"); - if let Some(msg) = - self.start_ping(SendAddr::Relay(url.clone()), DiscoPingPurpose::Discovery) - { - ping_msgs.push(PingAction::SendPing(msg)) - } - } - } - - #[cfg(any(test, feature = "test-utils"))] - if self.path_selection == PathSelection::RelayOnly { - warn!("in `RelayOnly` mode, ignoring request to respond to a hole punching attempt."); - return ping_msgs; - } - - self.prune_direct_addresses(now); - let mut ping_dsts = String::from("["); - self.udp_paths - .paths - .iter() - .filter_map(|(ipp, state)| state.needs_ping(&now).then_some(*ipp)) - .filter_map(|ipp| { - self.start_ping(SendAddr::Udp(ipp.into()), DiscoPingPurpose::Discovery) - }) - .for_each(|msg| { - use std::fmt::Write; - write!(&mut ping_dsts, " {} ", msg.dst).ok(); - ping_msgs.push(PingAction::SendPing(msg)); - }); - ping_dsts.push(']'); - debug!( - %ping_dsts, - dst = %self.node_id.fmt_short(), - paths = %summarize_node_paths(&self.udp_paths.paths), - "sending pings to node", - ); - self.last_full_ping.replace(now); - ping_msgs - } - pub(super) fn update_from_node_addr( &mut self, new_relay_url: Option<&RelayUrl>, @@ -713,114 +454,6 @@ impl NodeState { debug!(new = ?new_addrs , %paths, "added new direct paths for endpoint"); } - /// Handle a received Disco Ping. - /// - /// - Ensures the paths the ping was received on is a known path for this endpoint. - /// - /// - If there is no best_addr for this endpoint yet, sends a ping itself to try and - /// establish one. - /// - /// This is called once we've already verified that we got a valid discovery message - /// from `self` via ep. - pub(super) fn handle_ping( - &mut self, - path: SendAddr, - tx_id: stun_rs::TransactionId, - ) -> PingHandled { - let now = Instant::now(); - - let role = match path { - SendAddr::Udp(addr) => match self.udp_paths.paths.entry(addr.into()) { - Entry::Occupied(mut occupied) => occupied.get_mut().handle_ping(tx_id, now), - Entry::Vacant(vacant) => { - info!(%addr, "new direct addr for node"); - vacant.insert(PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Udp, - now, - )); - PingRole::NewPath - } - }, - SendAddr::Relay(ref url) => { - match self.relay_url.as_mut() { - Some((home_url, _state)) if home_url != url => { - // either the node changed relays or we didn't have a relay address for the - // node. In both cases, trust the new confirmed url - info!(%url, "new relay addr for node"); - self.relay_url = Some(( - url.clone(), - PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Relay, - now, - ), - )); - PingRole::NewPath - } - Some((_home_url, state)) => state.handle_ping(tx_id, now), - None => { - info!(%url, "new relay addr for node"); - self.relay_url = Some(( - url.clone(), - PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Relay, - now, - ), - )); - PingRole::NewPath - } - } - } - }; - event!( - target: "iroh::_events::ping::recv", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - src = ?path, - txn = ?tx_id, - ?role, - ); - - if matches!(path, SendAddr::Udp(_)) && matches!(role, PingRole::NewPath) { - self.prune_direct_addresses(now); - } - - // if the endpoint does not yet have a best_addr - let needs_ping_back = if matches!(path, SendAddr::Udp(_)) - && matches!( - self.udp_paths.best, - UdpSendAddr::None | UdpSendAddr::Unconfirmed(_) | UdpSendAddr::Outdated(_) - ) { - // We also need to send a ping to make this path available to us as well. This - // is always sent together with a pong. So in the worst case the pong gets lost - // and this ping does not. In that case we ping-pong until both sides have - // received at least one pong. Once both sides have received one pong they both - // have a best_addr and this ping will stop being sent. - self.start_ping(path, DiscoPingPurpose::PingBack) - } else { - None - }; - - debug!( - ?role, - needs_ping_back = ?needs_ping_back.is_some(), - paths = %summarize_node_paths(&self.udp_paths.paths), - "endpoint handled ping", - ); - PingHandled { - role, - needs_ping_back, - } - } - /// Prune inactive paths. /// /// This trims the list of inactive paths for an endpoint. At most @@ -873,105 +506,6 @@ impl NodeState { self.udp_paths.update_to_best_addr(now); } - /// Handles a Pong message (a reply to an earlier ping). - /// - /// It reports the address and key that should be inserted for the endpoint if any. - #[instrument(skip(self))] - pub(super) fn handle_pong( - &mut self, - m: &disco::Pong, - src: SendAddr, - ) -> Option<(SocketAddr, PublicKey)> { - event!( - target: "iroh::_events::pong::recv", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - ?src, - txn = ?m.tx_id, - ); - let is_relay = src.is_relay(); - match self.sent_pings.remove(&m.tx_id) { - None => { - // This is not a pong for a ping we sent. In reality however we probably - // did send this ping but it has timed-out by the time we receive this pong - // so we removed the state already. - debug!(tx = %HEXLOWER.encode(&m.tx_id), "received unknown pong (did it timeout?)"); - None - } - Some(sp) => { - let mut node_map_insert = None; - - let now = Instant::now(); - let latency = now - sp.at; - - debug!( - tx = %HEXLOWER.encode(&m.tx_id), - src = %src, - reported_ping_src = %m.ping_observed_addr, - ping_dst = %sp.to, - is_relay = %src.is_relay(), - latency = %latency.as_millis(), - "received pong", - ); - - match src { - SendAddr::Udp(addr) => { - match self.udp_paths.paths.get_mut(&addr.into()) { - None => { - warn!("ignoring pong: no state for src addr"); - // This is no longer an endpoint we care about. - return node_map_insert; - } - Some(st) => { - node_map_insert = Some((addr, self.node_id)); - st.add_pong_reply(PongReply { - latency, - pong_at: now, - from: src, - pong_src: m.ping_observed_addr.clone(), - }); - } - } - debug!( - paths = %summarize_node_paths(&self.udp_paths.paths), - "handled pong", - ); - } - SendAddr::Relay(ref url) => match self.relay_url.as_mut() { - Some((home_url, state)) if home_url == url => { - state.add_pong_reply(PongReply { - latency, - pong_at: now, - from: src, - pong_src: m.ping_observed_addr.clone(), - }); - } - other => { - // if we are here then we sent this ping, but the url changed - // waiting for the response. It was either set to None or changed to - // another relay. This should either never happen or be extremely - // unlikely. Log and ignore for now - warn!( - stored=?other, - received=?url, - "ignoring pong via relay for different relay from last one", - ); - } - }, - } - - // Promote this pong response to our current best address if it's lower latency. - // TODO(bradfitz): decide how latency vs. preference order affects decision - if let SendAddr::Udp(_to) = sp.to { - debug_assert!(!is_relay, "mismatching relay & udp"); - self.udp_paths.update_to_best_addr(now); - } - - node_map_insert - } - } - } - /// Handles a DISCO CallMeMaybe discovery message. /// /// The contract for use of this message is that the node has already pinged to us via @@ -982,7 +516,7 @@ impl NodeState { /// had any [`IpPort`]s to send pings to and our pings might end up blocked. But at /// least open the firewalls on our side, giving the other side another change of making /// it through when it pings in response. - pub(super) fn handle_call_me_maybe(&mut self, m: disco::CallMeMaybe) -> Vec { + pub(super) fn handle_call_me_maybe(&mut self, m: disco::CallMeMaybe) { let now = Instant::now(); let mut call_me_maybe_ipps = BTreeSet::new(); @@ -1015,7 +549,6 @@ impl NodeState { // it's been less than 5 seconds ago. Also clear pongs for direct addresses not // included in the updated set. for (ipp, st) in self.udp_paths.paths.iter_mut() { - st.last_ping = None; if !call_me_maybe_ipps.contains(ipp) { // TODO: This seems like a weird way to signal that the endpoint no longer // thinks it has this IpPort as an available path. @@ -1035,7 +568,6 @@ impl NodeState { paths = %summarize_node_paths(&self.udp_paths.paths), "updated endpoint paths from call-me-maybe", ); - self.send_pings(now) } /// Marks this node as having received a UDP payload message. @@ -1074,21 +606,6 @@ impl NodeState { self.last_used = Some(now); } - pub(super) fn last_ping(&self, addr: &SendAddr) -> Option { - match addr { - SendAddr::Udp(addr) => self - .udp_paths - .paths - .get(&(*addr).into()) - .and_then(|ep| ep.last_ping), - SendAddr::Relay(url) => self - .relay_url - .as_ref() - .filter(|(home_url, _state)| home_url == url) - .and_then(|(_home_url, state)| state.last_ping), - } - } - /// Checks if this `Endpoint` is currently actively being used. pub(super) fn is_active(&self, now: &Instant) -> bool { match self.last_used { @@ -1114,29 +631,6 @@ impl NodeState { return self.send_call_me_maybe(now, SendCallMeMaybe::Always); } - // Send heartbeat ping to keep the current addr going as long as we need it. - if let Some(udp_addr) = self.udp_paths.best.get_addr() { - let elapsed = self.last_ping(&SendAddr::Udp(udp_addr)).map(|l| now - l); - // Send a ping if the last ping is older than 2 seconds. - let needs_ping = match elapsed { - Some(e) => e >= STAYIN_ALIVE_MIN_ELAPSED, - None => false, - }; - - if needs_ping { - debug!( - dst = %udp_addr, - since_last_ping=?elapsed, - "send stayin alive ping", - ); - if let Some(msg) = - self.start_ping(SendAddr::Udp(udp_addr), DiscoPingPurpose::StayinAlive) - { - return vec![PingAction::SendPing(msg)]; - } - } - } - Vec::new() } @@ -1173,6 +667,18 @@ impl NodeState { (udp_addr, relay_url, ping_msgs) } + /// Returns a [`NodeAddr`] with all the currently known direct addresses and the relay URL. + pub(crate) fn get_current_addr(&self) -> NodeAddr { + // TODO: more selective? + let mut node_addr = + NodeAddr::new(self.node_id).with_direct_addresses(self.udp_paths.addrs()); + if let Some((url, _)) = &self.relay_url { + node_addr = node_addr.with_relay_url(url.clone()); + } + + node_addr + } + /// Get the direct addresses for this endpoint. pub(super) fn direct_addresses(&self) -> impl Iterator + '_ { self.udp_paths.paths.keys().copied() @@ -1214,26 +720,6 @@ enum SendCallMeMaybe { IfNoRecent, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(super) struct PongReply { - pub(super) latency: Duration, - /// When we received the pong. - pub(super) pong_at: Instant, - /// The pong's src (usually same as endpoint map key). - pub(super) from: SendAddr, - /// What they reported they heard. - pub(super) pong_src: SendAddr, -} - -#[derive(Debug)] -pub(super) struct SentPing { - pub(super) to: SendAddr, - pub(super) at: Instant, - #[allow(dead_code)] - pub(super) purpose: DiscoPingPurpose, - pub(super) _expiry_task: AbortOnDropHandle<()>, -} - /// The reason why a discovery ping message was sent. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DiscoPingPurpose { @@ -1330,7 +816,7 @@ impl From<(RelayUrl, PathState)> for RelayUrlInfo { RelayUrlInfo { relay_url: value.0, last_alive: value.1.last_alive().map(|i| i.elapsed()), - latency: value.1.latency(), + latency: None, } } } @@ -1435,263 +921,269 @@ pub enum ConnectionType { #[cfg(test)] mod tests { - use std::{collections::BTreeMap, net::Ipv4Addr}; + use std::net::Ipv4Addr; use iroh_base::SecretKey; use super::*; - use crate::magicsock::node_map::{NodeMap, NodeMapInner}; - - #[test] - fn test_remote_infos() { - let now = Instant::now(); - let elapsed = Duration::from_secs(3); - let later = now + elapsed; - let send_addr: RelayUrl = "https://my-relay.com".parse().unwrap(); - let pong_src = SendAddr::Udp("0.0.0.0:1".parse().unwrap()); - let latency = Duration::from_millis(50); - - let relay_and_state = |node_id: NodeId, url: RelayUrl| { - let relay_state = PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Relay(send_addr.clone()), - pong_src: pong_src.clone(), - }, - ); - Some((url, relay_state)) - }; - - // endpoint with a `best_addr` that has a latency but no relay - let (a_endpoint, a_socket_addr) = { - let key = SecretKey::generate(rand::thread_rng()); - let node_id = key.public(); - let ip_port = IpPort { - ip: Ipv4Addr::UNSPECIFIED.into(), - port: 10, - }; - let endpoint_state = BTreeMap::from([( - ip_port, - PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Udp(ip_port.into()), - pong_src: pong_src.clone(), - }, - ), - )]); - ( - NodeState { - id: 0, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: None, - udp_paths: NodeUdpPaths::from_parts( - endpoint_state, - UdpSendAddr::Valid(ip_port.into()), - ), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Direct(ip_port.into())), - has_been_direct: AtomicBool::new(true), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - }, - ip_port.into(), - ) - }; - // endpoint w/ no best addr but a relay w/ latency - let b_endpoint = { - // let socket_addr = "0.0.0.0:9".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - NodeState { - id: 1, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: relay_and_state(key.public(), send_addr.clone()), - udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - } - }; - - // endpoint w/ no best addr but a relay w/ no latency - let c_endpoint = { - // let socket_addr = "0.0.0.0:8".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - NodeState { - id: 2, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: Some(( - send_addr.clone(), - PathState::new( - key.public(), - SendAddr::from(send_addr.clone()), - Source::App, - now, - ), - )), - udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - } - }; - - // endpoint w/ expired best addr and relay w/ latency - let (d_endpoint, d_socket_addr) = { - let socket_addr: SocketAddr = "0.0.0.0:7".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - let node_id = key.public(); - let endpoint_state = BTreeMap::from([( - IpPort::from(socket_addr), - PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Udp(socket_addr), - pong_src: pong_src.clone(), - }, - ), - )]); - ( - NodeState { - id: 3, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: relay_and_state(key.public(), send_addr.clone()), - udp_paths: NodeUdpPaths::from_parts( - endpoint_state, - UdpSendAddr::Outdated(socket_addr), - ), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Mixed( - socket_addr, - send_addr.clone(), - )), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - }, - socket_addr, - ) - }; - - let mut expect = Vec::from([ - RemoteInfo { - node_id: a_endpoint.node_id, - relay_url: None, - addrs: Vec::from([DirectAddrInfo { - addr: a_socket_addr, - latency: Some(latency), - last_control: Some((elapsed, ControlMsg::Pong)), - last_payload: None, - last_alive: Some(elapsed), - sources: HashMap::new(), - }]), - conn_type: ConnectionType::Direct(a_socket_addr), - latency: Some(latency), - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: b_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: b_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: Some(latency), - }), - addrs: Vec::new(), - conn_type: ConnectionType::Relay(send_addr.clone()), - latency: Some(latency), - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: c_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: c_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: None, - }), - addrs: Vec::new(), - conn_type: ConnectionType::Relay(send_addr.clone()), - latency: None, - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: d_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: d_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: Some(latency), - }), - addrs: Vec::from([DirectAddrInfo { - addr: d_socket_addr, - latency: Some(latency), - last_control: Some((elapsed, ControlMsg::Pong)), - last_payload: None, - last_alive: Some(elapsed), - sources: HashMap::new(), - }]), - conn_type: ConnectionType::Mixed(d_socket_addr, send_addr.clone()), - latency: Some(Duration::from_millis(50)), - last_used: Some(elapsed), - }, - ]); - - let node_map = NodeMap::from_inner(NodeMapInner { - by_node_key: HashMap::from([ - (a_endpoint.node_id, a_endpoint.id), - (b_endpoint.node_id, b_endpoint.id), - (c_endpoint.node_id, c_endpoint.id), - (d_endpoint.node_id, d_endpoint.id), - ]), - by_ip_port: HashMap::from([ - (a_socket_addr.into(), a_endpoint.id), - (d_socket_addr.into(), d_endpoint.id), - ]), - by_quic_mapped_addr: HashMap::from([ - (a_endpoint.quic_mapped_addr, a_endpoint.id), - (b_endpoint.quic_mapped_addr, b_endpoint.id), - (c_endpoint.quic_mapped_addr, c_endpoint.id), - (d_endpoint.quic_mapped_addr, d_endpoint.id), - ]), - by_id: HashMap::from([ - (a_endpoint.id, a_endpoint), - (b_endpoint.id, b_endpoint), - (c_endpoint.id, c_endpoint), - (d_endpoint.id, d_endpoint), - ]), - next_id: 5, - path_selection: PathSelection::default(), - }); - let mut got = node_map.list_remote_infos(later); - got.sort_by_key(|p| p.node_id); - expect.sort_by_key(|p| p.node_id); - remove_non_deterministic_fields(&mut got); - assert_eq!(expect, got); - } + // use crate::magicsock::node_map::{NodeMap, NodeMapInner}; + + // #[test] + // fn test_remote_infos() { + // let now = Instant::now(); + // let elapsed = Duration::from_secs(3); + // let later = now + elapsed; + // let send_addr: RelayUrl = "https://my-relay.com".parse().unwrap(); + // let pong_src = SendAddr::Udp("0.0.0.0:1".parse().unwrap()); + // let latency = Duration::from_millis(50); + + // let relay_and_state = |node_id: NodeId, url: RelayUrl| { + // let relay_state = PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Relay(send_addr.clone()), + // pong_src: pong_src.clone(), + // }, + // ); + // Some((url, relay_state)) + // }; + + // // endpoint with a `best_addr` that has a latency but no relay + // let (a_endpoint, a_socket_addr) = { + // let key = SecretKey::generate(rand::thread_rng()); + // let node_id = key.public(); + // let ip_port = IpPort { + // ip: Ipv4Addr::UNSPECIFIED.into(), + // port: 10, + // }; + // let endpoint_state = BTreeMap::from([( + // ip_port, + // PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Udp(ip_port.into()), + // pong_src: pong_src.clone(), + // }, + // ), + // )]); + // ( + // NodeState { + // id: 0, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: None, + // udp_paths: NodeUdpPaths::from_parts( + // endpoint_state, + // BestAddr::from_parts( + // ip_port.into(), + // latency, + // now, + // now + Duration::from_secs(100), + // ), + // ), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Direct(ip_port.into())), + // has_been_direct: true, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // }, + // ip_port.into(), + // ) + // }; + // // endpoint w/ no best addr but a relay w/ latency + // let b_endpoint = { + // // let socket_addr = "0.0.0.0:9".parse().unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // NodeState { + // id: 1, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: relay_and_state(key.public(), send_addr.clone()), + // udp_paths: NodeUdpPaths::new(), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // } + // }; + + // // endpoint w/ no best addr but a relay w/ no latency + // let c_endpoint = { + // // let socket_addr = "0.0.0.0:8".parse().unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // NodeState { + // id: 2, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: Some(( + // send_addr.clone(), + // PathState::new( + // key.public(), + // SendAddr::from(send_addr.clone()), + // Source::App, + // now, + // ), + // )), + // udp_paths: NodeUdpPaths::new(), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // } + // }; + + // // endpoint w/ expired best addr and relay w/ latency + // let (d_endpoint, d_socket_addr) = { + // let socket_addr: SocketAddr = "0.0.0.0:7".parse().unwrap(); + // let expired = now.checked_sub(Duration::from_secs(100)).unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // let node_id = key.public(); + // let endpoint_state = BTreeMap::from([( + // IpPort::from(socket_addr), + // PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Udp(socket_addr), + // pong_src: pong_src.clone(), + // }, + // ), + // )]); + // ( + // NodeState { + // id: 3, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: relay_and_state(key.public(), send_addr.clone()), + // udp_paths: NodeUdpPaths::from_parts( + // endpoint_state, + // BestAddr::from_parts(socket_addr, Duration::from_millis(80), now, expired), + // ), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Mixed( + // socket_addr, + // send_addr.clone(), + // )), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // }, + // socket_addr, + // ) + // }; + + // let mut expect = Vec::from([ + // RemoteInfo { + // node_id: a_endpoint.node_id, + // relay_url: None, + // addrs: Vec::from([DirectAddrInfo { + // addr: a_socket_addr, + // latency: Some(latency), + // last_control: Some((elapsed, ControlMsg::Pong)), + // last_payload: None, + // last_alive: Some(elapsed), + // sources: HashMap::new(), + // }]), + // conn_type: ConnectionType::Direct(a_socket_addr), + // latency: Some(latency), + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: b_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: b_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: Some(latency), + // }), + // addrs: Vec::new(), + // conn_type: ConnectionType::Relay(send_addr.clone()), + // latency: Some(latency), + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: c_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: c_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: None, + // }), + // addrs: Vec::new(), + // conn_type: ConnectionType::Relay(send_addr.clone()), + // latency: None, + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: d_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: d_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: Some(latency), + // }), + // addrs: Vec::from([DirectAddrInfo { + // addr: d_socket_addr, + // latency: Some(latency), + // last_control: Some((elapsed, ControlMsg::Pong)), + // last_payload: None, + // last_alive: Some(elapsed), + // sources: HashMap::new(), + // }]), + // conn_type: ConnectionType::Mixed(d_socket_addr, send_addr.clone()), + // latency: Some(Duration::from_millis(50)), + // last_used: Some(elapsed), + // }, + // ]); + + // let node_map = NodeMap::from_inner(NodeMapInner { + // by_node_key: HashMap::from([ + // (a_endpoint.node_id, a_endpoint.id), + // (b_endpoint.node_id, b_endpoint.id), + // (c_endpoint.node_id, c_endpoint.id), + // (d_endpoint.node_id, d_endpoint.id), + // ]), + // by_ip_port: HashMap::from([ + // (a_socket_addr.into(), a_endpoint.id), + // (d_socket_addr.into(), d_endpoint.id), + // ]), + // by_quic_mapped_addr: HashMap::from([ + // (a_endpoint.quic_mapped_addr, a_endpoint.id), + // (b_endpoint.quic_mapped_addr, b_endpoint.id), + // (c_endpoint.quic_mapped_addr, c_endpoint.id), + // (d_endpoint.quic_mapped_addr, d_endpoint.id), + // ]), + // by_id: HashMap::from([ + // (a_endpoint.id, a_endpoint), + // (b_endpoint.id, b_endpoint), + // (c_endpoint.id, c_endpoint), + // (d_endpoint.id, d_endpoint), + // ]), + // next_id: 5, + // path_selection: PathSelection::default(), + // }); + // let mut got = node_map.list_remote_infos(later); + // got.sort_by_key(|p| p.node_id); + // expect.sort_by_key(|p| p.node_id); + // remove_non_deterministic_fields(&mut got); + // assert_eq!(expect, got); + // } fn remove_non_deterministic_fields(infos: &mut [RemoteInfo]) { for info in infos.iter_mut() { @@ -1724,10 +1216,6 @@ mod tests { .collect(); let call_me_maybe = disco::CallMeMaybe { my_numbers }; - let ping_messages = ep.handle_call_me_maybe(call_me_maybe); - - // We have no relay server and no previous direct addresses, so we should get the same - // number of pings as direct addresses in the call-me-maybe. - assert_eq!(ping_messages.len(), my_numbers_count as usize); + ep.handle_call_me_maybe(call_me_maybe); } } diff --git a/iroh/src/magicsock/node_map/path_state.rs b/iroh/src/magicsock/node_map/path_state.rs index b504712927..7fd4fc7fe7 100644 --- a/iroh/src/magicsock/node_map/path_state.rs +++ b/iroh/src/magicsock/node_map/path_state.rs @@ -4,26 +4,16 @@ use std::collections::{BTreeMap, HashMap}; use iroh_base::NodeId; use n0_future::time::{Duration, Instant}; -use tracing::{Level, debug, event}; use super::{ - IpPort, PingRole, Source, - node_state::{ControlMsg, PongReply, SESSION_ACTIVE_TIMEOUT}, + IpPort, Source, + node_state::{ControlMsg, SESSION_ACTIVE_TIMEOUT}, }; use crate::{ disco::SendAddr, - magicsock::{ - HEARTBEAT_INTERVAL, - node_map::path_validity::{self, PathValidity}, - }, + magicsock::node_map::path_validity::{self, PathValidity}, }; -/// The minimum time between pings to an endpoint. -/// -/// Except in the case of CallMeMaybe frames resetting the counter, as the first pings -/// likely didn't through the firewall. -const DISCO_PING_INTERVAL: Duration = Duration::from_secs(5); - /// State about a particular path to another [`NodeState`]. /// /// This state is used for both the relay path and any direct UDP paths. @@ -35,13 +25,6 @@ pub(super) struct PathState { node_id: NodeId, /// The path this applies for. path: SendAddr, - /// The last (outgoing) ping time. - pub(super) last_ping: Option, - - /// If non-zero, means that this was an endpoint that we learned about at runtime (from an - /// incoming ping). If so, we keep the time updated and use it to discard old candidates. - // NOTE: tx_id Originally added in tailscale due to . - last_got_ping: Option<(Instant, stun_rs::TransactionId)>, /// The time this endpoint was last advertised via a call-me-maybe DISCO message. pub(super) call_me_maybe_time: Option, @@ -52,6 +35,7 @@ pub(super) struct PathState { /// /// See [`PathValidity`] docs. pub(super) validity: PathValidity, + /// When the last payload data was **received** via this path. /// /// This excludes DISCO messages. @@ -71,8 +55,6 @@ impl PathState { Self { node_id, path, - last_ping: None, - last_got_ping: None, call_me_maybe_time: None, validity: PathValidity::empty(), last_payload_msg: None, @@ -91,8 +73,6 @@ impl PathState { PathState { node_id, path, - last_ping: None, - last_got_ping: None, call_me_maybe_time: None, validity: PathValidity::empty(), last_payload_msg: Some(now), @@ -100,54 +80,6 @@ impl PathState { } } - pub(super) fn with_ping( - node_id: NodeId, - path: SendAddr, - tx_id: stun_rs::TransactionId, - source: Source, - now: Instant, - ) -> Self { - let mut new = PathState::new(node_id, path, source, now); - new.handle_ping(tx_id, now); - new - } - - pub(super) fn add_pong_reply(&mut self, r: PongReply) { - if let SendAddr::Udp(ref path) = self.path { - if self.validity.is_empty() { - event!( - target: "iroh::_events::holepunched", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - path = ?path, - direction = "outgoing", - ); - } - } - - self.validity = PathValidity::new(r.pong_at, r.latency); - } - - pub(super) fn receive_payload(&mut self, now: Instant) { - self.last_payload_msg = Some(now); - self.validity - .receive_payload(now, path_validity::Source::QuicPayload); - } - - #[cfg(test)] - pub(super) fn with_pong_reply(node_id: NodeId, r: PongReply) -> Self { - PathState { - node_id, - path: r.from.clone(), - last_ping: None, - last_got_ping: None, - call_me_maybe_time: None, - validity: PathValidity::new(r.pong_at, r.latency), - last_payload_msg: None, - sources: HashMap::new(), - } - } - /// Check whether this path is considered active. /// /// Active means the path has received payload messages within the last @@ -162,11 +94,11 @@ impl PathState { .unwrap_or(false) } - /// Returns the instant the last incoming ping was received. - pub(super) fn last_incoming_ping(&self) -> Option<&Instant> { - self.last_got_ping.as_ref().map(|(time, _tx_id)| time) + pub(super) fn receive_payload(&mut self, now: Instant) { + self.last_payload_msg = Some(now); + self.validity + .receive_payload(now, path_validity::Source::QuicPayload); } - /// Reports the last instant this path was considered alive. /// /// Alive means the path is considered in use by the remote endpoint. Either because we @@ -184,36 +116,25 @@ impl PathState { .into_iter() .chain(self.last_payload_msg) .chain(self.call_me_maybe_time) - .chain(self.last_incoming_ping().cloned()) .max() } /// The last control or DISCO message **about** this path. /// /// This is the most recent instant among: - /// - when last pong was received. /// - when this path was last advertised in a received CallMeMaybe message. /// - when the last ping from them was received. /// /// Returns the time elapsed since the last control message, and the type of control message. pub(super) fn last_control_msg(&self, now: Instant) -> Option<(Duration, ControlMsg)> { // get every control message and assign it its kind - let last_pong = self - .validity - .latest_pong() - .map(|pong_at| (pong_at, ControlMsg::Pong)); let last_call_me_maybe = self .call_me_maybe_time .as_ref() .map(|call_me| (*call_me, ControlMsg::CallMeMaybe)); - let last_ping = self - .last_incoming_ping() - .map(|ping| (*ping, ControlMsg::Ping)); - last_pong + last_call_me_maybe .into_iter() - .chain(last_call_me_maybe) - .chain(last_ping) .max_by_key(|(instant, _kind)| *instant) .map(|(instant, kind)| (now.duration_since(instant), kind)) } @@ -223,63 +144,11 @@ impl PathState { self.validity.latency() } - pub(super) fn needs_ping(&self, now: &Instant) -> bool { - match self.last_ping { - None => true, - Some(last_ping) => { - let elapsed = now.duration_since(last_ping); - - // TODO: remove! - // This logs "ping is too new" for each send whenever the endpoint does *not* need - // a ping. Pretty sure this is not a useful log, but maybe there was a reason? - // if !needs_ping { - // debug!("ping is too new: {}ms", elapsed.as_millis()); - // } - elapsed > DISCO_PING_INTERVAL - } - } - } - - pub(super) fn handle_ping(&mut self, tx_id: stun_rs::TransactionId, now: Instant) -> PingRole { - if Some(&tx_id) == self.last_got_ping.as_ref().map(|(_t, tx_id)| tx_id) { - PingRole::Duplicate - } else { - let prev = self.last_got_ping.replace((now, tx_id)); - let heartbeat_deadline = HEARTBEAT_INTERVAL + (HEARTBEAT_INTERVAL / 2); - match prev { - Some((prev_time, _tx)) if now.duration_since(prev_time) <= heartbeat_deadline => { - PingRole::LikelyHeartbeat - } - Some((prev_time, _tx)) => { - debug!( - elapsed = ?now.duration_since(prev_time), - "heartbeat missed, reactivating", - ); - PingRole::Activate - } - None => { - if let SendAddr::Udp(ref addr) = self.path { - event!( - target: "iroh::_events::holepunched", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - path = ?addr, - direction = "incoming", - ); - } - PingRole::Activate - } - } - } - } - pub(super) fn add_source(&mut self, source: Source, now: Instant) { self.sources.insert(source, now); } pub(super) fn clear(&mut self) { - self.last_ping = None; - self.last_got_ping = None; self.call_me_maybe_time = None; self.validity = PathValidity::empty(); } @@ -292,12 +161,7 @@ impl PathState { if let Some(pong_at) = self.validity.latest_pong() { write!(w, "pong-received({:?} ago) ", pong_at.elapsed())?; } - if let Some(when) = self.last_incoming_ping() { - write!(w, "ping-received({:?} ago) ", when.elapsed())?; - } - if let Some(ref when) = self.last_ping { - write!(w, "ping-sent({:?} ago) ", when.elapsed())?; - } + if let Some(last_source) = self.sources.iter().max_by_key(|&(_, instant)| instant) { write!( w, diff --git a/iroh/src/magicsock/node_map/udp_paths.rs b/iroh/src/magicsock/node_map/udp_paths.rs index 29c43ccc99..2c72a95c84 100644 --- a/iroh/src/magicsock/node_map/udp_paths.rs +++ b/iroh/src/magicsock/node_map/udp_paths.rs @@ -105,6 +105,9 @@ impl NodeUdpPaths { best, } } + pub(super) fn addrs(&self) -> Vec { + self.paths.keys().map(|ip| (*ip).into()).collect() + } /// Returns the current UDP address to send on. pub(super) fn send_addr(&self, have_ipv6: bool) -> &UdpSendAddr { diff --git a/iroh/src/net_report/ip_mapped_addrs.rs b/iroh/src/magicsock/relay_mapped_addrs.rs similarity index 50% rename from iroh/src/net_report/ip_mapped_addrs.rs rename to iroh/src/magicsock/relay_mapped_addrs.rs index be7da1867a..1b21f1377e 100644 --- a/iroh/src/net_report/ip_mapped_addrs.rs +++ b/iroh/src/magicsock/relay_mapped_addrs.rs @@ -7,6 +7,7 @@ use std::{ }, }; +use iroh_base::{NodeId, RelayUrl}; use snafu::Snafu; /// Can occur when converting a [`SocketAddr`] to an [`IpMappedAddr`] @@ -14,16 +15,19 @@ use snafu::Snafu; #[snafu(display("Failed to convert"))] pub struct IpMappedAddrError; -/// A map fake Ipv6 address with an actual IP address. +/// An Ipv6 ULA address, identifying a relay path for a [`NodeId`]. /// -/// It is essentially a lookup key for an IP that iroh's magicsocket knows about. +/// Since iroh nodes are reachable via a relay server we have a network path indicated by +/// the `(NodeId, RelayUrl)`. However Quinn can only handle socket addresses, so we use +/// IPv6 addresses in a private IPv6 Unique Local Address range, which map to a unique +/// `(NodeId, RelayUrl)` pair. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub(crate) struct IpMappedAddr(Ipv6Addr); +pub(crate) struct RelayMappedAddr(Ipv6Addr); /// Counter to always generate unique addresses for [`IpMappedAddr`]. static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); -impl IpMappedAddr { +impl RelayMappedAddr { /// The Prefix/L of our Unique Local Addresses. const ADDR_PREFIXL: u8 = 0xfd; /// The Global ID used in our Unique Local Addresses. @@ -38,7 +42,7 @@ impl IpMappedAddr { /// /// This generates a new IPv6 address in the Unique Local Address range (RFC 4193) /// which is recognised by iroh as an IP mapped address. - pub(super) fn generate() -> Self { + pub(crate) fn generate() -> Self { let mut addr = [0u8; 16]; addr[0] = Self::ADDR_PREFIXL; addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); @@ -50,19 +54,19 @@ impl IpMappedAddr { Self(Ipv6Addr::from(addr)) } - /// Returns a consistent [`SocketAddr`] for the [`IpMappedAddr`]. + /// Returns a consistent [`SocketAddr`] for the [`RelayMappedAddr`]. /// /// This does not have a routable IP address. /// - /// This uses a made-up, but fixed port number. The [IpMappedAddresses`] map this is - /// made for creates a unique [`IpMappedAddr`] for each IP+port and thus does not use - /// the port to map back to the original [`SocketAddr`]. + /// This uses a made-up, but fixed port number. The [`RelayAddrMap`] creates a unique + /// [`RelayMappedAddr`] for each `(NodeId, RelayUrl)` pair and thus does not use the + /// port to map back to the original [`SocketAddr`]. pub(crate) fn private_socket_addr(&self) -> SocketAddr { SocketAddr::new(IpAddr::from(self.0), Self::MAPPED_ADDR_PORT) } } -impl TryFrom for IpMappedAddr { +impl TryFrom for RelayMappedAddr { type Error = IpMappedAddrError; fn try_from(value: Ipv6Addr) -> std::result::Result { @@ -77,58 +81,56 @@ impl TryFrom for IpMappedAddr { } } -impl std::fmt::Display for IpMappedAddr { +impl std::fmt::Display for RelayMappedAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "IpMappedAddr({})", self.0) } } -/// A Map of [`IpMappedAddresses`] to [`SocketAddr`]. -// TODO(ramfox): before this is ready to be used beyond QAD, we should add -// mechanisms for keeping track of "aliveness" and pruning address, as we do -// with the `NodeMap` +/// Can occur when converting a [`SocketAddr`] to an [`RelayMappedAddr`] +#[derive(Debug, Snafu)] +#[snafu(display("Failed to convert"))] +pub struct RelayAddrMapError; + +/// A Map of [`RelayMappedAddr`] to `(RelayUrl, NodeId)`. #[derive(Debug, Clone, Default)] -pub(crate) struct IpMappedAddresses(Arc>); +pub(crate) struct RelayAddrMap(Arc>); #[derive(Debug, Default)] pub(super) struct Inner { - by_mapped_addr: BTreeMap, - /// Because [`std::net::SocketAddrV6`] contains extra fields besides the IP - /// address and port (ie, flow_info and scope_id), the a [`std::net::SocketAddrV6`] - /// with the same IP addr and port might Hash to something different. - /// So to get a hashable key for the map, we are using `(IpAddr, u6)`. - by_ip_port: BTreeMap<(IpAddr, u16), IpMappedAddr>, + by_mapped_addr: BTreeMap, + by_url: BTreeMap<(RelayUrl, NodeId), RelayMappedAddr>, } -impl IpMappedAddresses { - /// Adds a [`SocketAddr`] to the map and returns the generated [`IpMappedAddr`]. +impl RelayAddrMap { + /// Adds a new entry to the map and returns the generated [`RelayMappedAddr`]. /// - /// If this [`SocketAddr`] already exists in the map, it returns its - /// associated [`IpMappedAddr`]. + /// If this `(RelayUrl, NodeId)` already exists in the map, it returns its associated + /// [`RelayMappedAddr`]. /// - /// Otherwise a new [`IpMappedAddr`] is generated for it and returned. - pub(super) fn get_or_register(&self, socket_addr: SocketAddr) -> IpMappedAddr { - let ip_port = (socket_addr.ip(), socket_addr.port()); + /// Otherwise a new [`RelayMappedAddr`] is generated for it and returned. + pub(super) fn get_or_register(&self, relay: RelayUrl, node: NodeId) -> RelayMappedAddr { let mut inner = self.0.lock().expect("poisoned"); - if let Some(mapped_addr) = inner.by_ip_port.get(&ip_port) { + if let Some(mapped_addr) = inner.by_url.get(&(relay.clone(), node)) { return *mapped_addr; } - let ip_mapped_addr = IpMappedAddr::generate(); - inner.by_mapped_addr.insert(ip_mapped_addr, socket_addr); - inner.by_ip_port.insert(ip_port, ip_mapped_addr); + let ip_mapped_addr = RelayMappedAddr::generate(); + inner + .by_mapped_addr + .insert(ip_mapped_addr, (relay.clone(), node)); + inner.by_url.insert((relay, node), ip_mapped_addr); ip_mapped_addr } - /// Returns the [`IpMappedAddr`] for the given [`SocketAddr`]. - pub(crate) fn get_mapped_addr(&self, socket_addr: &SocketAddr) -> Option { - let ip_port = (socket_addr.ip(), socket_addr.port()); + /// Returns the [`RelayMappedAddr`] for the given [`RelayUrl`] and [`NodeId`]. + pub(crate) fn get_mapped_addr(&self, relay: RelayUrl, node: NodeId) -> Option { let inner = self.0.lock().expect("poisoned"); - inner.by_ip_port.get(&ip_port).copied() + inner.by_url.get(&(relay, node)).copied() } - /// Returns the [`SocketAddr`] for the given [`IpMappedAddr`]. - pub(crate) fn get_ip_addr(&self, mapped_addr: &IpMappedAddr) -> Option { + /// Returns the [`RelayUrl`] and [`NodeId`] for the given [`IpMappedAddr`]. + pub(crate) fn get_url(&self, mapped_addr: &RelayMappedAddr) -> Option<(RelayUrl, NodeId)> { let inner = self.0.lock().expect("poisoned"); - inner.by_mapped_addr.get(mapped_addr).copied() + inner.by_mapped_addr.get(mapped_addr).cloned() } } diff --git a/iroh/src/magicsock/transports/relay.rs b/iroh/src/magicsock/transports/relay.rs index 295266ec55..33760fdcde 100644 --- a/iroh/src/magicsock/transports/relay.rs +++ b/iroh/src/magicsock/transports/relay.rs @@ -126,7 +126,7 @@ impl RelayTransport { .segment_size .map_or(dm.datagrams.contents.len(), |s| u16::from(s) as usize); meta_out.ecn = None; - meta_out.dst_ip = None; // TODO: insert the relay url for this relay + meta_out.dst_ip = None; *addr = (dm.url, dm.src).into(); num_msgs += 1; diff --git a/iroh/src/net_report.rs b/iroh/src/net_report.rs index e3ab4f5bb4..78b1c28c5e 100644 --- a/iroh/src/net_report.rs +++ b/iroh/src/net_report.rs @@ -46,7 +46,6 @@ use self::reportgen::QadProbeReport; use self::reportgen::{ProbeFinished, ProbeReport}; mod defaults; -mod ip_mapped_addrs; mod metrics; mod probes; mod report; @@ -73,8 +72,6 @@ pub(crate) mod portmapper { } } -pub(crate) use ip_mapped_addrs::{IpMappedAddr, IpMappedAddresses}; - pub(crate) use self::reportgen::IfStateDetails; #[cfg(not(wasm_browser))] use self::reportgen::SocketState; @@ -215,7 +212,6 @@ impl Client { /// Creates a new net_report client. pub(crate) fn new( #[cfg(not(wasm_browser))] dns_resolver: DnsResolver, - #[cfg(not(wasm_browser))] ip_mapped_addrs: Option, relay_map: RelayMap, opts: Options, metrics: Arc, @@ -233,7 +229,6 @@ impl Client { let socket_state = SocketState { quic_client, dns_resolver, - ip_mapped_addrs, }; Client { @@ -438,7 +433,6 @@ impl Client { for relay_node in self.relay_map.nodes().take(MAX_RELAYS) { if if_state.have_v4 { debug!(?relay_node.url, "v4 QAD probe"); - let ip_mapped_addrs = self.socket_state.ip_mapped_addrs.clone(); let relay_node = relay_node.clone(); let dns_resolver = self.socket_state.dns_resolver.clone(); let quic_client = quic_client.clone(); @@ -448,7 +442,7 @@ impl Client { .child_token() .run_until_cancelled_owned(time::timeout( PROBES_TIMEOUT, - run_probe_v4(ip_mapped_addrs, relay_node, quic_client, dns_resolver), + run_probe_v4(relay_node, quic_client, dns_resolver), )) .instrument(info_span!("QAD-IPv4", %relay_url)), ); @@ -456,7 +450,6 @@ impl Client { if if_state.have_v6 { debug!(?relay_node.url, "v6 QAD probe"); - let ip_mapped_addrs = self.socket_state.ip_mapped_addrs.clone(); let relay_node = relay_node.clone(); let dns_resolver = self.socket_state.dns_resolver.clone(); let quic_client = quic_client.clone(); @@ -466,7 +459,7 @@ impl Client { .child_token() .run_until_cancelled_owned(time::timeout( PROBES_TIMEOUT, - run_probe_v6(ip_mapped_addrs, relay_node, quic_client, dns_resolver), + run_probe_v6(relay_node, quic_client, dns_resolver), )) .instrument(info_span!("QAD-IPv6", %relay_url)), ); @@ -682,20 +675,17 @@ impl Client { #[cfg(not(wasm_browser))] async fn run_probe_v4( - ip_mapped_addrs: Option, relay_node: Arc, quic_client: QuicClient, dns_resolver: DnsResolver, ) -> n0_snafu::Result<(QadProbeReport, QadConn)> { use n0_snafu::ResultExt; - let relay_addr_orig = reportgen::get_relay_addr_ipv4(&dns_resolver, &relay_node).await?; - let relay_addr = - reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into()); + let relay_addr = reportgen::get_relay_addr_ipv4(&dns_resolver, &relay_node).await?; - debug!(?relay_addr_orig, ?relay_addr, "relay addr v4"); + debug!(?relay_addr, "relay addr v4"); let host = relay_node.url.host_str().context("missing host url")?; - let conn = quic_client.create_conn(relay_addr, host).await?; + let conn = quic_client.create_conn(relay_addr.into(), host).await?; let mut receiver = conn.observed_external_addr(); // wait for an addr @@ -750,19 +740,16 @@ async fn run_probe_v4( #[cfg(not(wasm_browser))] async fn run_probe_v6( - ip_mapped_addrs: Option, relay_node: Arc, quic_client: QuicClient, dns_resolver: DnsResolver, ) -> n0_snafu::Result<(QadProbeReport, QadConn)> { use n0_snafu::ResultExt; - let relay_addr_orig = reportgen::get_relay_addr_ipv6(&dns_resolver, &relay_node).await?; - let relay_addr = - reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into()); + let relay_addr = reportgen::get_relay_addr_ipv6(&dns_resolver, &relay_node).await?; - debug!(?relay_addr_orig, ?relay_addr, "relay addr v6"); + debug!(?relay_addr, "relay addr v6"); let host = relay_node.url.host_str().context("missing host url")?; - let conn = quic_client.create_conn(relay_addr, host).await?; + let conn = quic_client.create_conn(relay_addr.into(), host).await?; let mut receiver = conn.observed_external_addr(); // wait for an addr @@ -885,7 +872,6 @@ mod tests { .insecure_skip_relay_cert_verify(true); let mut client = Client::new( resolver.clone(), - None, relay_map.clone(), opts.clone(), Default::default(), @@ -1086,8 +1072,7 @@ mod tests { println!("test: {}", tt.name); let relay_map = RelayMap::empty(); let opts = Options::default(); - let mut client = - Client::new(resolver.clone(), None, relay_map, opts, Default::default()); + let mut client = Client::new(resolver.clone(), relay_map, opts, Default::default()); for s in &mut tt.steps { // trigger the timer tokio::time::advance(Duration::from_secs(s.after)).await; diff --git a/iroh/src/net_report/reportgen.rs b/iroh/src/net_report/reportgen.rs index 4c26bbea32..2829b9ab8f 100644 --- a/iroh/src/net_report/reportgen.rs +++ b/iroh/src/net_report/reportgen.rs @@ -45,6 +45,8 @@ use tokio_util::sync::CancellationToken; use tracing::{Instrument, debug, debug_span, error, info_span, trace, warn}; use url::Host; +#[cfg(not(wasm_browser))] +use super::defaults::timeouts::DNS_TIMEOUT; #[cfg(wasm_browser)] use super::portmapper; // We stub the library use super::{ @@ -52,8 +54,6 @@ use super::{ probes::{Probe, ProbePlan}, }; #[cfg(not(wasm_browser))] -use super::{defaults::timeouts::DNS_TIMEOUT, ip_mapped_addrs::IpMappedAddresses}; -#[cfg(not(wasm_browser))] use crate::discovery::dns::DNS_STAGGERING_MS; use crate::net_report::defaults::timeouts::{ CAPTIVE_PORTAL_DELAY, CAPTIVE_PORTAL_TIMEOUT, OVERALL_REPORT_TIMEOUT, PROBES_TIMEOUT, @@ -105,8 +105,6 @@ pub(crate) struct SocketState { pub(crate) quic_client: Option, /// The DNS resolver to use for probes that need to resolve DNS records. pub(crate) dns_resolver: DnsResolver, - /// Optional [`IpMappedAddresses`] used to enable QAD in iroh - pub(crate) ip_mapped_addrs: Option, } impl Client { @@ -518,17 +516,6 @@ impl Probe { } } -#[cfg(not(wasm_browser))] -pub(super) fn maybe_to_mapped_addr( - ip_mapped_addrs: Option<&IpMappedAddresses>, - addr: SocketAddr, -) -> SocketAddr { - if let Some(ip_mapped_addrs) = ip_mapped_addrs { - return ip_mapped_addrs.get_or_register(addr).private_socket_addr(); - } - addr -} - #[cfg(not(wasm_browser))] #[derive(Debug, Snafu)] #[snafu(module)] @@ -874,7 +861,7 @@ mod tests { let quic_client = iroh_relay::quic::QuicClient::new(ep.clone(), client_config); let dns_resolver = DnsResolver::default(); - let (report, conn) = super::super::run_probe_v4(None, relay, quic_client, dns_resolver) + let (report, conn) = super::super::run_probe_v4(relay, quic_client, dns_resolver) .await .unwrap();