diff --git a/Cargo.lock b/Cargo.lock index a9eb7648..76dbdfe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -218,13 +218,13 @@ dependencies = [ [[package]] name = "async-io" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" +checksum = "6fe557ebe0829511ddff4ad3011d159c0e6f144e05e3e8c3ab5095a131900a7b" dependencies = [ "async-lock", "autocfg", - "concurrent-queue 1.2.4", + "concurrent-queue 2.0.0", "futures-lite", "libc", "log", @@ -399,7 +399,7 @@ dependencies = [ "futures-util", "pin-project 1.0.12", "rustc_version", - "tokio 1.21.2", + "tokio 1.22.0", "wasm-bindgen-futures", ] @@ -467,7 +467,7 @@ dependencies = [ "async-trait", "axum-core", "bitflags", - "bytes 1.2.1", + "bytes 1.3.0", "futures-util", "http", "http-body", @@ -480,7 +480,7 @@ dependencies = [ "pin-project-lite 0.2.9", "serde", "sync_wrapper", - "tokio 1.21.2", + "tokio 1.22.0", "tower", "tower-http", "tower-layer", @@ -494,7 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", - "bytes 1.2.1", + "bytes 1.3.0", "futures-util", "http", "http-body", @@ -513,7 +513,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.5.4", "object", "rustc-demangle", ] @@ -569,9 +569,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec", @@ -627,16 +627,16 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blocking" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" dependencies = [ "async-channel", + "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", - "once_cell", ] [[package]] @@ -707,9 +707,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cache-padded" @@ -719,9 +719,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "capnp" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e850735c543306805e2ba8ee0a9632b0f62bb05872a8be2e2674e9903a1c048" +checksum = "f4929d71efc55aa42759793d853ecdfa6bb034419d22884e3e9871f0f593ac8d" [[package]] name = "capnp-futures" @@ -761,9 +761,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.76" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cesu8" @@ -830,15 +830,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi 0.3.9", ] @@ -962,7 +962,7 @@ version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "memchr", ] @@ -1031,7 +1031,7 @@ dependencies = [ "serde", "serde_json", "thread_local", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", "tonic", "tracing", @@ -1061,9 +1061,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" [[package]] name = "core-foundation" @@ -1174,22 +1174,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "memoffset", + "memoffset 0.7.1", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if 1.0.0", ] @@ -1285,7 +1285,7 @@ dependencies = [ "libc", "log", "signal-hook", - "tokio 1.21.2", + "tokio 1.22.0", "unicode-segmentation", "unicode-width", ] @@ -1330,7 +1330,7 @@ dependencies = [ "num", "owning_ref", "time 0.3.17", - "tokio 1.21.2", + "tokio 1.22.0", "toml", "unicode-segmentation", "unicode-width", @@ -1374,9 +1374,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" +checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" dependencies = [ "cc", "cxxbridge-flags", @@ -1386,9 +1386,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" +checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" dependencies = [ "cc", "codespan-reporting", @@ -1401,15 +1401,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" +checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" [[package]] name = "cxxbridge-macro" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" +checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" dependencies = [ "proc-macro2", "quote", @@ -1536,9 +1536,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -1813,12 +1813,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -2120,7 +2120,7 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "fnv", "futures-core", "futures-sink", @@ -2128,7 +2128,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-util", "tracing", ] @@ -2236,7 +2236,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "fnv", "itoa", ] @@ -2247,7 +2247,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "http", "pin-project-lite 0.2.9", ] @@ -2282,7 +2282,7 @@ version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "futures-channel", "futures-core", "futures-util", @@ -2294,7 +2294,7 @@ dependencies = [ "itoa", "pin-project-lite 0.2.9", "socket2", - "tokio 1.21.2", + "tokio 1.22.0", "tower-service", "tracing", "want", @@ -2308,7 +2308,7 @@ checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", "pin-project-lite 0.2.9", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-io-timeout", ] @@ -2378,7 +2378,7 @@ name = "igd" version = "0.12.0" dependencies = [ "attohttpc", - "bytes 1.2.1", + "bytes 1.3.0", "futures", "http", "hyper", @@ -2387,7 +2387,7 @@ dependencies = [ "simplelog 0.9.0", "tokio 0.2.25", "tokio 0.3.7", - "tokio 1.21.2", + "tokio 1.22.0", "url", "xmltree", ] @@ -2438,9 +2438,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -2487,9 +2487,9 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" dependencies = [ "socket2", "widestring 0.5.1", @@ -2842,6 +2842,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memory_units" version = "0.4.0" @@ -2869,6 +2878,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.6.23" @@ -2991,7 +3009,7 @@ checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ "bitflags", "jni-sys", - "ndk-sys 0.4.0", + "ndk-sys 0.4.1+23.1.7779620", "num_enum", "raw-window-handle", "thiserror", @@ -3015,7 +3033,7 @@ dependencies = [ "ndk 0.7.0", "ndk-context", "ndk-macro", - "ndk-sys 0.4.0", + "ndk-sys 0.4.1+23.1.7779620", "once_cell", "parking_lot 0.12.1", ] @@ -3044,9 +3062,9 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.4.0" +version = "0.4.1+23.1.7779620" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" dependencies = [ "jni-sys", ] @@ -3098,12 +3116,12 @@ dependencies = [ name = "netlink-proto" version = "0.9.1" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "futures", "log", "netlink-packet-core", "netlink-sys", - "tokio 1.21.2", + "tokio 1.22.0", ] [[package]] @@ -3111,11 +3129,11 @@ name = "netlink-sys" version = "0.8.1" dependencies = [ "async-io", - "bytes 1.2.1", + "bytes 1.3.0", "futures", "libc", "log", - "tokio 1.21.2", + "tokio 1.22.0", ] [[package]] @@ -3128,7 +3146,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -3141,7 +3159,7 @@ dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.5", "pin-utils", ] @@ -3362,7 +3380,7 @@ dependencies = [ "prost", "protobuf", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", "tonic", ] @@ -3426,7 +3444,7 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", ] @@ -3442,9 +3460,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "overload" @@ -3573,9 +3591,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a" dependencies = [ "thiserror", "ucd-trie", @@ -3583,9 +3601,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fd9bc6500181952d34bd0b2b0163a54d794227b498be0b7afa7698d0a7b18f" +checksum = "423c2ba011d6e27b02b482a3707c773d19aec65cc024637aec44e19652e66f63" dependencies = [ "pest", "pest_generator", @@ -3593,9 +3611,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2610d5ac5156217b4ff8e46ddcef7cdf44b273da2ac5bca2ecbfa86a330e7c4" +checksum = "3e64e6c2c85031c02fdbd9e5c72845445ca0a724d419aa0bc068ac620c9935c1" dependencies = [ "pest", "pest_meta", @@ -3606,9 +3624,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824749bf7e21dd66b36fbe26b3f45c713879cccd4a009a917ab8e045ca8246fe" +checksum = "57959b91f0a133f89a68be874a5c88ed689c19cd729ecdb5d762ebf16c64d662" dependencies = [ "once_cell", "pest", @@ -3840,7 +3858,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0841812012b2d4a6145fae9a6af1534873c32aa67fff26bd09f8fa42c83f95a" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "prost-derive", ] @@ -3850,7 +3868,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d8b442418ea0822409d9e7d047cbf1e7e9e1760b172bf9982cf29d517c93511" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "heck", "itertools", "lazy_static", @@ -3885,7 +3903,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "747761bc3dc48f9a34553bf65605cf6cb6288ba219f3450b4275dbd81539551a" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "prost", ] @@ -4018,11 +4036,10 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" dependencies = [ - "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -4030,9 +4047,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -4158,7 +4175,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "rustc-hex", ] @@ -4206,7 +4223,7 @@ dependencies = [ "netlink-proto", "nix 0.22.3", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", ] [[package]] @@ -4476,9 +4493,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", @@ -4556,7 +4573,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4567,7 +4584,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4791,9 +4808,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.26.7" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c375d5fd899e32847b8566e10598d6e9f1d9b55ec6de3cdf9e7da4bdc51371bc" +checksum = "29ddf41e393a9133c81d5f0974195366bd57082deac6e0eb02ed39b8341c2bb6" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.3", @@ -4870,9 +4887,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -4972,12 +4989,12 @@ dependencies = [ [[package]] name = "tokio" -version = "1.21.2" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" dependencies = [ "autocfg", - "bytes 1.2.1", + "bytes 1.3.0", "libc", "memchr", "mio 0.8.5", @@ -4998,7 +5015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite 0.2.9", - "tokio 1.21.2", + "tokio 1.22.0", ] [[package]] @@ -5020,7 +5037,7 @@ checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite 0.2.9", - "tokio 1.21.2", + "tokio 1.22.0", ] [[package]] @@ -5029,12 +5046,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ - "bytes 1.2.1", + "bytes 1.3.0", "futures-core", "futures-io", "futures-sink", "pin-project-lite 0.2.9", - "tokio 1.21.2", + "tokio 1.22.0", "tracing", ] @@ -5057,7 +5074,7 @@ dependencies = [ "async-trait", "axum", "base64 0.13.1", - "bytes 1.2.1", + "bytes 1.3.0", "futures-core", "futures-util", "h2", @@ -5069,7 +5086,7 @@ dependencies = [ "pin-project 1.0.12", "prost", "prost-derive", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", "tokio-util", "tower", @@ -5105,7 +5122,7 @@ dependencies = [ "pin-project-lite 0.2.9", "rand 0.8.5", "slab", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-util", "tower-layer", "tower-service", @@ -5119,7 +5136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes 1.2.1", + "bytes 1.3.0", "futures-core", "futures-util", "http", @@ -5304,7 +5321,7 @@ dependencies = [ "smallvec", "thiserror", "tinyvec", - "tokio 1.21.2", + "tokio 1.22.0", "tracing", "url", ] @@ -5324,7 +5341,7 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", "tracing", "trust-dns-proto", ] @@ -5362,7 +5379,7 @@ checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.2.1", + "bytes 1.3.0", "http", "httparse", "log", @@ -5532,7 +5549,7 @@ dependencies = [ "serde_derive", "serial_test", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-util", "veilid-core", ] @@ -5611,7 +5628,7 @@ dependencies = [ "static_assertions", "stop-token", "thiserror", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", "tokio-util", "tracing", @@ -5655,7 +5672,7 @@ dependencies = [ "parking_lot 0.12.1", "serde", "serde_json", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", "tokio-util", "tracing", @@ -5702,7 +5719,7 @@ dependencies = [ "signal-hook", "signal-hook-async-std", "stop-token", - "tokio 1.21.2", + "tokio 1.22.0", "tokio-stream", "tokio-util", "tracing", @@ -6201,9 +6218,9 @@ checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "winreg" -version = "0.7.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi 0.3.9", ] @@ -6238,9 +6255,9 @@ dependencies = [ [[package]] name = "wyz" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] diff --git a/veilid-core/src/core_context.rs b/veilid-core/src/core_context.rs index 6f39129a..8af47ab5 100644 --- a/veilid-core/src/core_context.rs +++ b/veilid-core/src/core_context.rs @@ -4,6 +4,7 @@ use crate::crypto::Crypto; use crate::veilid_api::*; use crate::veilid_config::*; use crate::xx::*; +use crate::*; pub type UpdateCallback = Arc; @@ -203,7 +204,7 @@ impl VeilidCoreContext { if #[cfg(target_os = "android")] { if crate::intf::utils::android::ANDROID_GLOBALS.lock().is_none() { error!("Android globals are not set up"); - return Err(VeilidAPIError::Internal { message: "Android globals are not set up".to_owned() }); + apibail_internal!("Android globals are not set up"); } } } @@ -251,7 +252,7 @@ pub async fn api_startup( // See if we have an API started up already let mut initialized_lock = INITIALIZED.lock().await; if *initialized_lock { - return Err(VeilidAPIError::AlreadyInitialized); + apibail_already_initialized!(); } // Create core context @@ -274,7 +275,7 @@ pub async fn api_startup_json( // See if we have an API started up already let mut initialized_lock = INITIALIZED.lock().await; if *initialized_lock { - return Err(VeilidAPIError::AlreadyInitialized); + apibail_already_initialized!(); } // Create core context diff --git a/veilid-core/src/crypto/tests/test_crypto.rs b/veilid-core/src/crypto/tests/test_crypto.rs index e1d09294..c1a03800 100644 --- a/veilid-core/src/crypto/tests/test_crypto.rs +++ b/veilid-core/src/crypto/tests/test_crypto.rs @@ -1,7 +1,6 @@ use super::*; use crate::tests::common::test_veilid_config::*; use crate::xx::*; -use crate::*; static LOREM_IPSUM:&[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. "; diff --git a/veilid-core/src/crypto/tests/test_envelope_receipt.rs b/veilid-core/src/crypto/tests/test_envelope_receipt.rs index 01b7ddf7..1a221dd3 100644 --- a/veilid-core/src/crypto/tests/test_envelope_receipt.rs +++ b/veilid-core/src/crypto/tests/test_envelope_receipt.rs @@ -1,7 +1,6 @@ use super::*; use crate::tests::common::test_veilid_config::*; use crate::xx::*; -use crate::*; pub async fn test_envelope_round_trip() { info!("--- test envelope round trip ---"); diff --git a/veilid-core/src/network_manager/connection_manager.rs b/veilid-core/src/network_manager/connection_manager.rs index e83f069d..30217cce 100644 --- a/veilid-core/src/network_manager/connection_manager.rs +++ b/veilid-core/src/network_manager/connection_manager.rs @@ -1,5 +1,4 @@ use super::*; -use crate::xx::*; use connection_table::*; use network_connection::*; use stop_token::future::FutureExt; diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index f9a22da3..52bc61a5 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -1,4 +1,5 @@ use crate::*; +use crate::xx::*; #[cfg(not(target_arch = "wasm32"))] mod native; @@ -33,7 +34,6 @@ use routing_table::*; use rpc_processor::*; #[cfg(target_arch = "wasm32")] use wasm::*; -use xx::*; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs index fe06c70f..fcc46e64 100644 --- a/veilid-core/src/network_manager/native/igd_manager.rs +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -1,5 +1,4 @@ use super::*; -use crate::xx::*; use igd::*; use std::net::UdpSocket; diff --git a/veilid-core/src/network_manager/tasks/public_address_check.rs b/veilid-core/src/network_manager/tasks/public_address_check.rs index 92303369..2f7ccc46 100644 --- a/veilid-core/src/network_manager/tasks/public_address_check.rs +++ b/veilid-core/src/network_manager/tasks/public_address_check.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; impl NetworkManager { // Clean up the public address check tables, removing entries that have timed out diff --git a/veilid-core/src/network_manager/tasks/rolling_transfers.rs b/veilid-core/src/network_manager/tasks/rolling_transfers.rs index 4007a4a9..c3774b6a 100644 --- a/veilid-core/src/network_manager/tasks/rolling_transfers.rs +++ b/veilid-core/src/network_manager/tasks/rolling_transfers.rs @@ -1,6 +1,4 @@ -use super::super::*; - -use crate::xx::*; +use super::*; impl NetworkManager { // Compute transfer statistics for the low level network diff --git a/veilid-core/src/network_manager/tests/test_connection_table.rs b/veilid-core/src/network_manager/tests/test_connection_table.rs index 79306051..6f720107 100644 --- a/veilid-core/src/network_manager/tests/test_connection_table.rs +++ b/veilid-core/src/network_manager/tests/test_connection_table.rs @@ -1,8 +1,7 @@ +use super::*; + use super::connection_table::*; -use super::network_connection::*; use crate::tests::common::test_veilid_config::*; -use crate::xx::*; -use crate::*; pub async fn test_add_get_remove() { let config = get_config(); diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index 2489e191..4aca58e4 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -1,6 +1,7 @@ mod protocol; use super::*; + use crate::routing_table::*; use connection_manager::*; use protocol::ws::WebsocketProtocolHandler; diff --git a/veilid-core/src/network_manager/wasm/protocol/mod.rs b/veilid-core/src/network_manager/wasm/protocol/mod.rs index 16edb5ee..bc4966ca 100644 --- a/veilid-core/src/network_manager/wasm/protocol/mod.rs +++ b/veilid-core/src/network_manager/wasm/protocol/mod.rs @@ -2,7 +2,6 @@ pub mod wrtc; pub mod ws; use super::*; -use crate::xx::*; use std::io; #[derive(Debug)] diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index c680eb1a..bb8a4058 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -11,11 +11,12 @@ mod routing_table_inner; mod stats_accounting; mod tasks; +use crate::xx::*; +use crate::*; + use crate::crypto::*; use crate::network_manager::*; use crate::rpc_processor::*; -use crate::xx::*; -use crate::*; use bucket::*; pub use bucket_entry::*; pub use debug::*; diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index d7d3da7f..6b16f84a 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; use futures_util::stream::{FuturesUnordered, StreamExt}; use stop_token::future::FutureExt as StopFutureExt; diff --git a/veilid-core/src/routing_table/tasks/kick_buckets.rs b/veilid-core/src/routing_table/tasks/kick_buckets.rs index 730bad1d..38eef8af 100644 --- a/veilid-core/src/routing_table/tasks/kick_buckets.rs +++ b/veilid-core/src/routing_table/tasks/kick_buckets.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; impl RoutingTable { // Kick the queued buckets in the routing table to free dead nodes if necessary diff --git a/veilid-core/src/routing_table/tasks/peer_minimum_refresh.rs b/veilid-core/src/routing_table/tasks/peer_minimum_refresh.rs index 7733755c..157e6030 100644 --- a/veilid-core/src/routing_table/tasks/peer_minimum_refresh.rs +++ b/veilid-core/src/routing_table/tasks/peer_minimum_refresh.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; use futures_util::stream::{FuturesOrdered, StreamExt}; use stop_token::future::FutureExt as StopFutureExt; diff --git a/veilid-core/src/routing_table/tasks/ping_validator.rs b/veilid-core/src/routing_table/tasks/ping_validator.rs index 976fb79b..2460d29f 100644 --- a/veilid-core/src/routing_table/tasks/ping_validator.rs +++ b/veilid-core/src/routing_table/tasks/ping_validator.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; use futures_util::stream::{FuturesUnordered, StreamExt}; use futures_util::FutureExt; diff --git a/veilid-core/src/routing_table/tasks/private_route_management.rs b/veilid-core/src/routing_table/tasks/private_route_management.rs index 3718366f..f9929e2c 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; use futures_util::stream::{FuturesUnordered, StreamExt}; use futures_util::FutureExt; diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index 85f4fd8a..056479e2 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; impl RoutingTable { // Keep relays assigned and accessible diff --git a/veilid-core/src/routing_table/tasks/rolling_transfers.rs b/veilid-core/src/routing_table/tasks/rolling_transfers.rs index b97b8afc..04177c01 100644 --- a/veilid-core/src/routing_table/tasks/rolling_transfers.rs +++ b/veilid-core/src/routing_table/tasks/rolling_transfers.rs @@ -1,5 +1,4 @@ -use super::super::*; -use crate::xx::*; +use super::*; impl RoutingTable { // Compute transfer statistics to determine how 'fast' a node is diff --git a/veilid-core/src/rpc_processor/coders/address.rs b/veilid-core/src/rpc_processor/coders/address.rs index ff3aabea..7e18ac02 100644 --- a/veilid-core/src/rpc_processor/coders/address.rs +++ b/veilid-core/src/rpc_processor/coders/address.rs @@ -1,7 +1,5 @@ -use crate::xx::*; -use crate::*; +use super::*; use core::convert::TryInto; -use rpc_processor::*; pub fn encode_address( address: &Address, diff --git a/veilid-core/src/rpc_processor/coders/address_type_set.rs b/veilid-core/src/rpc_processor/coders/address_type_set.rs index 67a40159..f341d980 100644 --- a/veilid-core/src/rpc_processor/coders/address_type_set.rs +++ b/veilid-core/src/rpc_processor/coders/address_type_set.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_address_type_set( address_type_set: &AddressTypeSet, diff --git a/veilid-core/src/rpc_processor/coders/dht_key.rs b/veilid-core/src/rpc_processor/coders/dht_key.rs index dd7a8909..bc1e3f22 100644 --- a/veilid-core/src/rpc_processor/coders/dht_key.rs +++ b/veilid-core/src/rpc_processor/coders/dht_key.rs @@ -1,7 +1,5 @@ -use crate::crypto::*; -use crate::*; +use super::*; use core::convert::TryInto; -use rpc_processor::*; pub fn decode_dht_key(public_key: &veilid_capnp::key256::Reader) -> DHTKey { let u0 = public_key.get_u0().to_be_bytes(); diff --git a/veilid-core/src/rpc_processor/coders/dht_signature.rs b/veilid-core/src/rpc_processor/coders/dht_signature.rs index 9e008faa..5b7427b2 100644 --- a/veilid-core/src/rpc_processor/coders/dht_signature.rs +++ b/veilid-core/src/rpc_processor/coders/dht_signature.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_signature(sig: &DHTSignature, builder: &mut veilid_capnp::signature512::Builder) { let sig = &sig.bytes; diff --git a/veilid-core/src/rpc_processor/coders/dial_info.rs b/veilid-core/src/rpc_processor/coders/dial_info.rs index c178bb0c..400075b8 100644 --- a/veilid-core/src/rpc_processor/coders/dial_info.rs +++ b/veilid-core/src/rpc_processor/coders/dial_info.rs @@ -1,7 +1,5 @@ -use crate::xx::*; -use crate::*; +use super::*; use core::convert::TryInto; -use rpc_processor::*; pub fn decode_dial_info(reader: &veilid_capnp::dial_info::Reader) -> Result { match reader diff --git a/veilid-core/src/rpc_processor/coders/dial_info_class.rs b/veilid-core/src/rpc_processor/coders/dial_info_class.rs index 71197171..835f060e 100644 --- a/veilid-core/src/rpc_processor/coders/dial_info_class.rs +++ b/veilid-core/src/rpc_processor/coders/dial_info_class.rs @@ -1,4 +1,4 @@ -use crate::*; +use super::*; pub fn encode_dial_info_class(dial_info_class: DialInfoClass) -> veilid_capnp::DialInfoClass { match dial_info_class { diff --git a/veilid-core/src/rpc_processor/coders/dial_info_detail.rs b/veilid-core/src/rpc_processor/coders/dial_info_detail.rs index c3dc1b27..7b012fe3 100644 --- a/veilid-core/src/rpc_processor/coders/dial_info_detail.rs +++ b/veilid-core/src/rpc_processor/coders/dial_info_detail.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_dial_info_detail( dial_info_detail: &DialInfoDetail, diff --git a/veilid-core/src/rpc_processor/coders/network_class.rs b/veilid-core/src/rpc_processor/coders/network_class.rs index 88d17fce..65bb9e74 100644 --- a/veilid-core/src/rpc_processor/coders/network_class.rs +++ b/veilid-core/src/rpc_processor/coders/network_class.rs @@ -1,4 +1,4 @@ -use crate::*; +use super::*; pub fn encode_network_class(network_class: NetworkClass) -> veilid_capnp::NetworkClass { match network_class { diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 02f9bd78..14ab95b3 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_node_info( node_info: &NodeInfo, diff --git a/veilid-core/src/rpc_processor/coders/node_status.rs b/veilid-core/src/rpc_processor/coders/node_status.rs index a07e977f..ed72123b 100644 --- a/veilid-core/src/rpc_processor/coders/node_status.rs +++ b/veilid-core/src/rpc_processor/coders/node_status.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_public_internet_node_status( public_internet_node_status: &PublicInternetNodeStatus, diff --git a/veilid-core/src/rpc_processor/coders/nonce.rs b/veilid-core/src/rpc_processor/coders/nonce.rs index 5eb39dce..ac7d48c0 100644 --- a/veilid-core/src/rpc_processor/coders/nonce.rs +++ b/veilid-core/src/rpc_processor/coders/nonce.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_nonce(nonce: &Nonce, builder: &mut veilid_capnp::nonce24::Builder) { builder.set_u0(u64::from_be_bytes( diff --git a/veilid-core/src/rpc_processor/coders/operations/answer.rs b/veilid-core/src/rpc_processor/coders/operations/answer.rs index e6d801a3..7dd429a2 100644 --- a/veilid-core/src/rpc_processor/coders/operations/answer.rs +++ b/veilid-core/src/rpc_processor/coders/operations/answer.rs @@ -1,6 +1,4 @@ use super::*; -use crate::*; -use rpc_processor::*; #[derive(Debug, Clone)] pub struct RPCAnswer { diff --git a/veilid-core/src/rpc_processor/coders/operations/mod.rs b/veilid-core/src/rpc_processor/coders/operations/mod.rs index 687ca68e..3c91d344 100644 --- a/veilid-core/src/rpc_processor/coders/operations/mod.rs +++ b/veilid-core/src/rpc_processor/coders/operations/mod.rs @@ -45,3 +45,5 @@ pub use operation_watch_value::*; pub use question::*; pub use respond_to::*; pub use statement::*; + +use super::*; diff --git a/veilid-core/src/rpc_processor/coders/operations/operation.rs b/veilid-core/src/rpc_processor/coders/operations/operation.rs index a33ab29c..aeeacf08 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub enum RPCOperationKind { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_app_call.rs b/veilid-core/src/rpc_processor/coders/operations/operation_app_call.rs index 609999bb..b1360b9a 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_app_call.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_app_call.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationAppCallQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_app_message.rs b/veilid-core/src/rpc_processor/coders/operations/operation_app_message.rs index 5a844f02..5c969be7 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_app_message.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_app_message.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationAppMessage { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_cancel_tunnel.rs b/veilid-core/src/rpc_processor/coders/operations/operation_cancel_tunnel.rs index c9ca4e96..d5ca9ad2 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_cancel_tunnel.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationCancelTunnelQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_complete_tunnel.rs b/veilid-core/src/rpc_processor/coders/operations/operation_complete_tunnel.rs index 453c38c0..49dc90a8 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_complete_tunnel.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationCompleteTunnelQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs index 5503ad31..ce42da3f 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationFindBlockQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs index 95ca3ea5..cf6bb675 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationFindNodeQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs index bb800511..f9fc9959 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationGetValueQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs b/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs index 5f077816..386805a3 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationNodeInfoUpdate { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs b/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs index 31c1d213..bd7517a7 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationReturnReceipt { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs index 68c97191..29e56035 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RoutedOperation { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs index 18c430d1..23a34421 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationSetValueQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_signal.rs b/veilid-core/src/rpc_processor/coders/operations/operation_signal.rs index e31a6f47..4b8a6fd3 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_signal.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_signal.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationSignal { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_start_tunnel.rs b/veilid-core/src/rpc_processor/coders/operations/operation_start_tunnel.rs index 08c1982b..d58e625a 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_start_tunnel.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationStartTunnelQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_status.rs b/veilid-core/src/rpc_processor/coders/operations/operation_status.rs index 71ed2117..9ab480a8 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_status.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_status.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationStatusQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs b/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs index 67b7ab00..d593650d 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationSupplyBlockQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs b/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs index a11f2501..63a8bd40 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationValidateDialInfo { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_value_changed.rs b/veilid-core/src/rpc_processor/coders/operations/operation_value_changed.rs index c1847118..3d1c08cf 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_value_changed.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_value_changed.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationValueChanged { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs index cbb08fcb..00c0199c 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub struct RPCOperationWatchValueQ { diff --git a/veilid-core/src/rpc_processor/coders/operations/question.rs b/veilid-core/src/rpc_processor/coders/operations/question.rs index 02995b26..4e7e3966 100644 --- a/veilid-core/src/rpc_processor/coders/operations/question.rs +++ b/veilid-core/src/rpc_processor/coders/operations/question.rs @@ -1,6 +1,4 @@ use super::*; -use crate::*; -use rpc_processor::*; #[derive(Debug, Clone)] pub struct RPCQuestion { diff --git a/veilid-core/src/rpc_processor/coders/operations/respond_to.rs b/veilid-core/src/rpc_processor/coders/operations/respond_to.rs index 79c4e358..f05b6d08 100644 --- a/veilid-core/src/rpc_processor/coders/operations/respond_to.rs +++ b/veilid-core/src/rpc_processor/coders/operations/respond_to.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; #[derive(Debug, Clone)] pub enum RespondTo { diff --git a/veilid-core/src/rpc_processor/coders/operations/statement.rs b/veilid-core/src/rpc_processor/coders/operations/statement.rs index 96c71a4c..3a019dce 100644 --- a/veilid-core/src/rpc_processor/coders/operations/statement.rs +++ b/veilid-core/src/rpc_processor/coders/operations/statement.rs @@ -1,6 +1,4 @@ use super::*; -use crate::*; -use rpc_processor::*; #[derive(Debug, Clone)] pub struct RPCStatement { diff --git a/veilid-core/src/rpc_processor/coders/peer_info.rs b/veilid-core/src/rpc_processor/coders/peer_info.rs index 5c7e67ab..5844b8ab 100644 --- a/veilid-core/src/rpc_processor/coders/peer_info.rs +++ b/veilid-core/src/rpc_processor/coders/peer_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_peer_info( peer_info: &PeerInfo, diff --git a/veilid-core/src/rpc_processor/coders/protocol_type_set.rs b/veilid-core/src/rpc_processor/coders/protocol_type_set.rs index 37db7e19..6ebf71e7 100644 --- a/veilid-core/src/rpc_processor/coders/protocol_type_set.rs +++ b/veilid-core/src/rpc_processor/coders/protocol_type_set.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_protocol_type_set( protocol_type_set: &ProtocolTypeSet, diff --git a/veilid-core/src/rpc_processor/coders/sender_info.rs b/veilid-core/src/rpc_processor/coders/sender_info.rs index 5fbea344..45fd96c8 100644 --- a/veilid-core/src/rpc_processor/coders/sender_info.rs +++ b/veilid-core/src/rpc_processor/coders/sender_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_sender_info( sender_info: &SenderInfo, diff --git a/veilid-core/src/rpc_processor/coders/signal_info.rs b/veilid-core/src/rpc_processor/coders/signal_info.rs index 7a272973..5e9edc84 100644 --- a/veilid-core/src/rpc_processor/coders/signal_info.rs +++ b/veilid-core/src/rpc_processor/coders/signal_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_signal_info( signal_info: &SignalInfo, diff --git a/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs index a04d3445..7b583e21 100644 --- a/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_signed_direct_node_info( signed_direct_node_info: &SignedDirectNodeInfo, diff --git a/veilid-core/src/rpc_processor/coders/signed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_node_info.rs index 64ae9c80..2af7cefd 100644 --- a/veilid-core/src/rpc_processor/coders/signed_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_node_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_signed_node_info( signed_node_info: &SignedNodeInfo, diff --git a/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs index 646f6597..924a00ad 100644 --- a/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_signed_relayed_node_info( signed_relayed_node_info: &SignedRelayedNodeInfo, diff --git a/veilid-core/src/rpc_processor/coders/socket_address.rs b/veilid-core/src/rpc_processor/coders/socket_address.rs index 3a9c2a42..41542550 100644 --- a/veilid-core/src/rpc_processor/coders/socket_address.rs +++ b/veilid-core/src/rpc_processor/coders/socket_address.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_socket_address( socket_address: &SocketAddress, diff --git a/veilid-core/src/rpc_processor/coders/tunnel.rs b/veilid-core/src/rpc_processor/coders/tunnel.rs index da12781c..72d2470a 100644 --- a/veilid-core/src/rpc_processor/coders/tunnel.rs +++ b/veilid-core/src/rpc_processor/coders/tunnel.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_tunnel_mode(tunnel_mode: TunnelMode) -> veilid_capnp::TunnelEndpointMode { match tunnel_mode { diff --git a/veilid-core/src/rpc_processor/coders/value_data.rs b/veilid-core/src/rpc_processor/coders/value_data.rs index cd3ad900..ba859423 100644 --- a/veilid-core/src/rpc_processor/coders/value_data.rs +++ b/veilid-core/src/rpc_processor/coders/value_data.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_value_data( value_data: &ValueData, diff --git a/veilid-core/src/rpc_processor/coders/value_key.rs b/veilid-core/src/rpc_processor/coders/value_key.rs index 7561b3c6..fcb1c0a2 100644 --- a/veilid-core/src/rpc_processor/coders/value_key.rs +++ b/veilid-core/src/rpc_processor/coders/value_key.rs @@ -1,5 +1,4 @@ -use crate::*; -use rpc_processor::*; +use super::*; pub fn encode_value_key( value_key: &ValueKey, diff --git a/veilid-core/src/tests/common/mod.rs b/veilid-core/src/tests/common/mod.rs index ab47cdd5..a2e63d10 100644 --- a/veilid-core/src/tests/common/mod.rs +++ b/veilid-core/src/tests/common/mod.rs @@ -4,8 +4,3 @@ pub mod test_protected_store; pub mod test_table_store; pub mod test_veilid_config; pub mod test_veilid_core; - -use super::*; - -pub use crypto::tests::*; -pub use network_manager::tests::*; diff --git a/veilid-core/src/tests/mod.rs b/veilid-core/src/tests/mod.rs index a4bdf876..0b6b6216 100644 --- a/veilid-core/src/tests/mod.rs +++ b/veilid-core/src/tests/mod.rs @@ -1,5 +1,3 @@ pub mod common; #[cfg(not(target_arch = "wasm32"))] mod native; - -use super::*; diff --git a/veilid-core/src/tests/native/mod.rs b/veilid-core/src/tests/native/mod.rs index d693eaa5..b41ab132 100644 --- a/veilid-core/src/tests/native/mod.rs +++ b/veilid-core/src/tests/native/mod.rs @@ -3,10 +3,11 @@ mod test_async_peek_stream; +use crate::xx::*; + use crate::crypto::tests::*; use crate::network_manager::tests::*; use crate::tests::common::*; -use crate::xx::*; #[cfg(all(target_os = "android", feature = "android_tests"))] use jni::{objects::JClass, objects::JObject, JNIEnv}; diff --git a/veilid-core/src/tests/native/test_async_peek_stream.rs b/veilid-core/src/tests/native/test_async_peek_stream.rs index f73f95b2..2aaa5d97 100644 --- a/veilid-core/src/tests/native/test_async_peek_stream.rs +++ b/veilid-core/src/tests/native/test_async_peek_stream.rs @@ -1,4 +1,4 @@ -use super::*; +use crate::xx::*; cfg_if! { if #[cfg(feature="rt-async-std")] { diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs new file mode 100644 index 00000000..2028023e --- /dev/null +++ b/veilid-core/src/veilid_api/api.rs @@ -0,0 +1,284 @@ +use super::*; + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +struct VeilidAPIInner { + context: Option, +} + +impl fmt::Debug for VeilidAPIInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VeilidAPIInner") + } +} + +impl Drop for VeilidAPIInner { + fn drop(&mut self) { + if let Some(context) = self.context.take() { + intf::spawn_detached(api_shutdown(context)); + } + } +} + +#[derive(Clone, Debug)] +pub struct VeilidAPI { + inner: Arc>, +} + +impl VeilidAPI { + #[instrument(skip_all)] + pub(crate) fn new(context: VeilidCoreContext) -> Self { + Self { + inner: Arc::new(Mutex::new(VeilidAPIInner { + context: Some(context), + })), + } + } + + #[instrument(skip_all)] + pub async fn shutdown(self) { + let context = { self.inner.lock().context.take() }; + if let Some(context) = context { + api_shutdown(context).await; + } + } + + pub fn is_shutdown(&self) -> bool { + self.inner.lock().context.is_none() + } + + //////////////////////////////////////////////////////////////// + // Accessors + pub fn config(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.config.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn crypto(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.crypto.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn table_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.table_store.clone()); + } + Err(VeilidAPIError::not_initialized()) + } + pub fn block_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.block_store.clone()); + } + Err(VeilidAPIError::not_initialized()) + } + pub fn protected_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.protected_store.clone()); + } + Err(VeilidAPIError::not_initialized()) + } + pub fn attachment_manager(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.clone()); + } + Err(VeilidAPIError::not_initialized()) + } + pub fn network_manager(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.network_manager()); + } + Err(VeilidAPIError::not_initialized()) + } + pub fn rpc_processor(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.network_manager().rpc_processor()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn routing_table(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.network_manager().routing_table()); + } + Err(VeilidAPIError::NotInitialized) + } + + //////////////////////////////////////////////////////////////// + // Attach/Detach + + // get a full copy of the current state + pub async fn get_state(&self) -> Result { + let attachment_manager = self.attachment_manager()?; + let network_manager = attachment_manager.network_manager(); + let config = self.config()?; + + let attachment = attachment_manager.get_veilid_state(); + let network = network_manager.get_veilid_state(); + let config = config.get_veilid_state(); + + Ok(VeilidState { + attachment, + network, + config, + }) + } + + // get network connectedness + + // connect to the network + #[instrument(level = "debug", err, skip_all)] + pub async fn attach(&self) -> Result<(), VeilidAPIError> { + let attachment_manager = self.attachment_manager()?; + attachment_manager + .request_attach() + .await + .map_err(|e| VeilidAPIError::internal(e)) + } + + // disconnect from the network + #[instrument(level = "debug", err, skip_all)] + pub async fn detach(&self) -> Result<(), VeilidAPIError> { + let attachment_manager = self.attachment_manager()?; + attachment_manager + .request_detach() + .await + .map_err(|e| VeilidAPIError::internal(e)) + } + + //////////////////////////////////////////////////////////////// + // Routing Context + + #[instrument(level = "debug", skip(self))] + pub fn routing_context(&self) -> RoutingContext { + RoutingContext::new(self.clone()) + } + + //////////////////////////////////////////////////////////////// + // Private route allocation + + #[instrument(level = "debug", skip(self))] + pub async fn new_private_route(&self) -> Result<(DHTKey, Vec), VeilidAPIError> { + self.new_custom_private_route(Stability::default(), Sequencing::default()) + .await + } + + #[instrument(level = "debug", skip(self))] + pub async fn new_custom_private_route( + &self, + stability: Stability, + sequencing: Sequencing, + ) -> Result<(DHTKey, Vec), VeilidAPIError> { + let default_route_hop_count: usize = { + let config = self.config()?; + let c = config.get(); + c.network.rpc.default_route_hop_count.into() + }; + + let rss = self.routing_table()?.route_spec_store(); + let r = rss + .allocate_route( + stability, + sequencing, + default_route_hop_count, + Direction::Inbound.into(), + &[], + ) + .map_err(VeilidAPIError::internal)?; + let Some(pr_pubkey) = r else { + apibail_generic!("unable to allocate route"); + }; + if !rss + .test_route(&pr_pubkey) + .await + .map_err(VeilidAPIError::no_connection)? + { + rss.release_route(&pr_pubkey); + apibail_generic!("allocated route failed to test"); + } + let private_route = rss + .assemble_private_route(&pr_pubkey, Some(true)) + .map_err(VeilidAPIError::generic)?; + let blob = match RouteSpecStore::private_route_to_blob(&private_route) { + Ok(v) => v, + Err(e) => { + rss.release_route(&pr_pubkey); + apibail_internal!(e); + } + }; + + rss.mark_route_published(&pr_pubkey, true) + .map_err(VeilidAPIError::internal)?; + + Ok((pr_pubkey, blob)) + } + + #[instrument(level = "debug", skip(self))] + pub fn import_remote_private_route(&self, blob: Vec) -> Result { + let rss = self.routing_table()?.route_spec_store(); + rss.import_remote_private_route(blob) + .map_err(|e| VeilidAPIError::invalid_argument(e, "blob", "private route blob")) + } + + #[instrument(level = "debug", skip(self))] + pub fn release_private_route(&self, key: &DHTKey) -> Result<(), VeilidAPIError> { + let rss = self.routing_table()?.route_spec_store(); + if rss.release_route(key) { + Ok(()) + } else { + Err(VeilidAPIError::invalid_argument( + "release_private_route", + "key", + key, + )) + } + } + + //////////////////////////////////////////////////////////////// + // App Calls + + #[instrument(level = "debug", skip(self))] + pub async fn app_call_reply(&self, id: u64, message: Vec) -> Result<(), VeilidAPIError> { + let rpc_processor = self.rpc_processor()?; + rpc_processor + .app_call_reply(id, message) + .await + .map_err(|e| e.into()) + } + + //////////////////////////////////////////////////////////////// + // Tunnel Building + + #[instrument(level = "debug", err, skip(self))] + pub async fn start_tunnel( + &self, + _endpoint_mode: TunnelMode, + _depth: u8, + ) -> Result { + panic!("unimplemented"); + } + + #[instrument(level = "debug", err, skip(self))] + pub async fn complete_tunnel( + &self, + _endpoint_mode: TunnelMode, + _depth: u8, + _partial_tunnel: PartialTunnel, + ) -> Result { + panic!("unimplemented"); + } + + #[instrument(level = "debug", err, skip(self))] + pub async fn cancel_tunnel(&self, _tunnel_id: TunnelId) -> Result { + panic!("unimplemented"); + } +} diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 93d4beaf..626f2860 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -276,15 +276,10 @@ fn get_debug_argument Option>( argument: &str, getter: G, ) -> Result { - if let Some(val) = getter(value) { - Ok(val) - } else { - Err(VeilidAPIError::InvalidArgument { - context: context.to_owned(), - argument: argument.to_owned(), - value: value.to_owned(), - }) - } + let Some(val) = getter(value) else { + apibail_invalid_argument!(context, argument, value); + }; + Ok(val) } fn get_debug_argument_at Option>( debug_args: &[String], @@ -294,21 +289,13 @@ fn get_debug_argument_at Option>( getter: G, ) -> Result { if pos >= debug_args.len() { - return Err(VeilidAPIError::MissingArgument { - context: context.to_owned(), - argument: argument.to_owned(), - }); + apibail_missing_argument!(context, argument); } let value = &debug_args[pos]; - if let Some(val) = getter(value) { - Ok(val) - } else { - Err(VeilidAPIError::InvalidArgument { - context: context.to_owned(), - argument: argument.to_owned(), - value: value.to_owned(), - }) - } + let Some(val) = getter(value) else { + apibail_invalid_argument!(context, argument, value); + }; + Ok(val) } impl VeilidAPI { @@ -351,11 +338,7 @@ impl VeilidAPI { } else if let Some(lim) = get_number(&arg) { limit = lim; } else { - return Err(VeilidAPIError::InvalidArgument { - context: "debug_entries".to_owned(), - argument: "unknown".to_owned(), - value: arg, - }); + apibail_invalid_argument!("debug_entries", "unknown", arg); } } @@ -412,7 +395,7 @@ impl VeilidAPI { async fn debug_restart(&self, args: String) -> Result { let args = args.trim_start(); if args.is_empty() { - return Err(VeilidAPIError::missing_argument("debug_restart", "arg_0")); + apibail_missing_argument!("debug_restart", "arg_0"); } let (arg, _rest) = args.split_once(' ').unwrap_or((args, "")); // let rest = rest.trim_start().to_owned(); @@ -431,11 +414,7 @@ impl VeilidAPI { Ok("Network restarted".to_owned()) } else { - Err(VeilidAPIError::invalid_argument( - "debug_restart", - "arg_1", - arg, - )) + apibail_invalid_argument!("debug_restart", "arg_1", arg); } } @@ -654,11 +633,7 @@ impl VeilidAPI { if full_val == "full" { true } else { - return Err(VeilidAPIError::invalid_argument( - "debug_route", - "full", - full_val, - )); + apibail_invalid_argument!("debug_route", "full", full_val); } } else { false diff --git a/veilid-core/src/veilid_api/error.rs b/veilid-core/src/veilid_api/error.rs new file mode 100644 index 00000000..6d6f1a25 --- /dev/null +++ b/veilid-core/src/veilid_api/error.rs @@ -0,0 +1,186 @@ +use super::*; + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_timeout { + () => { + return Err(VeilidAPIError::timeout()) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_generic { + ($x:expr) => { + return Err(VeilidAPIError::generic($x)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_internal { + ($x:expr) => { + return Err(VeilidAPIError::internal($x)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_parse_error { + ($x:expr, $y:expr) => { + return Err(VeilidAPIError::parse_error($x, $y)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_missing_argument { + ($x:expr, $y:expr) => { + return Err(VeilidAPIError::missing_argument($x, $y)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_invalid_argument { + ($x:expr, $y:expr, $z:expr) => { + return Err(VeilidAPIError::invalid_argument($x, $y, $z)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_no_connection { + ($x:expr) => { + return Err(VeilidAPIError::no_connection($x)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_key_not_found { + ($x:expr) => { + return Err(VeilidAPIError::key_not_found($x)) + }; +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! apibail_already_initialized { + () => { + return Err(VeilidAPIError::already_initialized()) + }; +} + +#[derive( + ThisError, + Clone, + Debug, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +#[serde(tag = "kind")] +pub enum VeilidAPIError { + #[error("Not initialized")] + NotInitialized, + #[error("Already initialized")] + AlreadyInitialized, + #[error("Timeout")] + Timeout, + #[error("Shutdown")] + Shutdown, + #[error("Key not found: {key}")] + KeyNotFound { key: DHTKey }, + #[error("No connection: {message}")] + NoConnection { message: String }, + #[error("No peer info: {node_id}")] + NoPeerInfo { node_id: NodeId }, + #[error("Internal: {message}")] + Internal { message: String }, + #[error("Unimplemented: {message}")] + Unimplemented { message: String }, + #[error("Parse error: '{message}' with value '{value}'")] + ParseError { message: String, value: String }, + #[error("Invalid argument: '{argument}' for '{context}' with value '{value}'")] + InvalidArgument { + context: String, + argument: String, + value: String, + }, + #[error("Missing argument: '{argument}' for '{context}'")] + MissingArgument { context: String, argument: String }, + #[error("Generic: {message}")] + Generic { message: String }, +} + +impl VeilidAPIError { + pub fn not_initialized() -> Self { + Self::NotInitialized + } + pub fn already_initialized() -> Self { + Self::AlreadyInitialized + } + pub fn timeout() -> Self { + Self::Timeout + } + pub fn shutdown() -> Self { + Self::Shutdown + } + pub fn key_not_found(key: DHTKey) -> Self { + Self::KeyNotFound { key } + } + pub fn no_connection(msg: T) -> Self { + Self::NoConnection { + message: msg.to_string(), + } + } + pub fn no_peer_info(node_id: NodeId) -> Self { + Self::NoPeerInfo { node_id } + } + pub fn internal(msg: T) -> Self { + Self::Internal { + message: msg.to_string(), + } + } + pub fn unimplemented(msg: T) -> Self { + Self::Unimplemented { + message: msg.to_string(), + } + } + pub fn parse_error(msg: T, value: S) -> Self { + Self::ParseError { + message: msg.to_string(), + value: value.to_string(), + } + } + pub fn invalid_argument( + context: T, + argument: S, + value: R, + ) -> Self { + Self::InvalidArgument { + context: context.to_string(), + argument: argument.to_string(), + value: value.to_string(), + } + } + pub fn missing_argument(context: T, argument: S) -> Self { + Self::MissingArgument { + context: context.to_string(), + argument: argument.to_string(), + } + } + pub fn generic(msg: T) -> Self { + Self::Generic { + message: msg.to_string(), + } + } +} diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index 7c83fe34..a293cd46 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -1,12 +1,18 @@ #![allow(dead_code)] +mod api; mod debug; +mod error; mod routing_context; mod serialize_helpers; +mod types; +pub use api::*; pub use debug::*; +pub use error::*; pub use routing_context::*; pub use serialize_helpers::*; +pub use types::*; use crate::*; @@ -35,2824 +41,3 @@ use serde::*; use xx::*; ///////////////////////////////////////////////////////////////////////////////////////////////////// - -#[allow(unused_macros)] -#[macro_export] -macro_rules! apibail_generic { - ($x:expr) => { - return Err(VeilidAPIError::generic($x)) - }; -} - -#[allow(unused_macros)] -#[macro_export] -macro_rules! apibail_internal { - ($x:expr) => { - return Err(VeilidAPIError::internal($x)) - }; -} - -#[allow(unused_macros)] -#[macro_export] -macro_rules! apibail_parse_error { - ($x:expr, $y:expr) => { - return Err(VeilidAPIError::parse_error($x, $y)) - }; -} - -#[derive( - ThisError, - Clone, - Debug, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -#[serde(tag = "kind")] -pub enum VeilidAPIError { - #[error("Not initialized")] - NotInitialized, - #[error("Already initialized")] - AlreadyInitialized, - #[error("Timeout")] - Timeout, - #[error("Shutdown")] - Shutdown, - #[error("Key not found: {key}")] - KeyNotFound { key: DHTKey }, - #[error("No connection: {message}")] - NoConnection { message: String }, - #[error("No peer info: {node_id}")] - NoPeerInfo { node_id: NodeId }, - #[error("Internal: {message}")] - Internal { message: String }, - #[error("Unimplemented: {message}")] - Unimplemented { message: String }, - #[error("Parse error: '{message}' with value '{value}'")] - ParseError { message: String, value: String }, - #[error("Invalid argument: '{argument}' for '{context}' with value '{value}'")] - InvalidArgument { - context: String, - argument: String, - value: String, - }, - #[error("Missing argument: '{argument}' for '{context}'")] - MissingArgument { context: String, argument: String }, - #[error("Generic: {message}")] - Generic { message: String }, -} - -impl VeilidAPIError { - pub fn not_initialized() -> Self { - Self::NotInitialized - } - pub fn already_initialized() -> Self { - Self::AlreadyInitialized - } - pub fn timeout() -> Self { - Self::Timeout - } - pub fn shutdown() -> Self { - Self::Shutdown - } - pub fn key_not_found(key: DHTKey) -> Self { - Self::KeyNotFound { key } - } - pub fn no_connection(msg: T) -> Self { - Self::NoConnection { - message: msg.to_string(), - } - } - pub fn no_peer_info(node_id: NodeId) -> Self { - Self::NoPeerInfo { node_id } - } - pub fn internal(msg: T) -> Self { - Self::Internal { - message: msg.to_string(), - } - } - pub fn unimplemented(msg: T) -> Self { - Self::Unimplemented { - message: msg.to_string(), - } - } - pub fn parse_error(msg: T, value: S) -> Self { - Self::ParseError { - message: msg.to_string(), - value: value.to_string(), - } - } - pub fn invalid_argument( - context: T, - argument: S, - value: R, - ) -> Self { - Self::InvalidArgument { - context: context.to_string(), - argument: argument.to_string(), - value: value.to_string(), - } - } - pub fn missing_argument(context: T, argument: S) -> Self { - Self::MissingArgument { - context: context.to_string(), - argument: argument.to_string(), - } - } - pub fn generic(msg: T) -> Self { - Self::Generic { - message: msg.to_string(), - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Copy, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum VeilidLogLevel { - Error = 1, - Warn, - Info, - Debug, - Trace, -} - -impl VeilidLogLevel { - pub fn from_tracing_level(level: tracing::Level) -> VeilidLogLevel { - match level { - tracing::Level::ERROR => VeilidLogLevel::Error, - tracing::Level::WARN => VeilidLogLevel::Warn, - tracing::Level::INFO => VeilidLogLevel::Info, - tracing::Level::DEBUG => VeilidLogLevel::Debug, - tracing::Level::TRACE => VeilidLogLevel::Trace, - } - } - pub fn from_log_level(level: log::Level) -> VeilidLogLevel { - match level { - log::Level::Error => VeilidLogLevel::Error, - log::Level::Warn => VeilidLogLevel::Warn, - log::Level::Info => VeilidLogLevel::Info, - log::Level::Debug => VeilidLogLevel::Debug, - log::Level::Trace => VeilidLogLevel::Trace, - } - } - pub fn to_tracing_level(&self) -> tracing::Level { - match self { - Self::Error => tracing::Level::ERROR, - Self::Warn => tracing::Level::WARN, - Self::Info => tracing::Level::INFO, - Self::Debug => tracing::Level::DEBUG, - Self::Trace => tracing::Level::TRACE, - } - } - pub fn to_log_level(&self) -> log::Level { - match self { - Self::Error => log::Level::Error, - Self::Warn => log::Level::Warn, - Self::Info => log::Level::Info, - Self::Debug => log::Level::Debug, - Self::Trace => log::Level::Trace, - } - } -} - -impl fmt::Display for VeilidLogLevel { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let text = match self { - Self::Error => "ERROR", - Self::Warn => "WARN", - Self::Info => "INFO", - Self::Debug => "DEBUG", - Self::Trace => "TRACE", - }; - write!(f, "{}", text) - } -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidLog { - pub log_level: VeilidLogLevel, - pub message: String, - pub backtrace: Option, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidAppMessage { - /// Some(sender) if the message was sent directly, None if received via a private/safety route - #[serde(with = "opt_json_as_string")] - pub sender: Option, - /// The content of the message to deliver to the application - #[serde(with = "json_as_base64")] - pub message: Vec, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidAppCall { - /// Some(sender) if the request was sent directly, None if received via a private/safety route - #[serde(with = "opt_json_as_string")] - pub sender: Option, - /// The content of the request to deliver to the application - #[serde(with = "json_as_base64")] - pub message: Vec, - /// The id to reply to - #[serde(with = "json_as_string")] - pub id: u64, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidStateAttachment { - pub state: AttachmentState, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PeerTableData { - pub node_id: DHTKey, - pub peer_address: PeerAddress, - pub peer_stats: PeerStats, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidStateNetwork { - pub started: bool, - #[serde(with = "json_as_string")] - pub bps_down: u64, - #[serde(with = "json_as_string")] - pub bps_up: u64, - pub peers: Vec, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidStateRoute { - pub dead_routes: Vec, - pub dead_remote_routes: Vec, -} - -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidStateConfig { - pub config: VeilidConfigInner, -} - -#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(u8), derive(CheckBytes))] -#[serde(tag = "kind")] -pub enum VeilidUpdate { - Log(VeilidLog), - AppMessage(VeilidAppMessage), - AppCall(VeilidAppCall), - Attachment(VeilidStateAttachment), - Network(VeilidStateNetwork), - Config(VeilidStateConfig), - Route(VeilidStateRoute), - Shutdown, -} - -#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct VeilidState { - pub attachment: VeilidStateAttachment, - pub network: VeilidStateNetwork, - pub config: VeilidStateConfig, -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// -/// -#[derive( - Clone, - Debug, - Default, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct NodeId { - pub key: DHTKey, -} -impl NodeId { - pub fn new(key: DHTKey) -> Self { - Self { key } - } -} -impl fmt::Display for NodeId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.key.encode()) - } -} -impl FromStr for NodeId { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - Ok(Self { - key: DHTKey::try_decode(s)?, - }) - } -} - -#[derive( - Clone, - Debug, - Default, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct ValueKey { - pub key: DHTKey, - pub subkey: Option, -} -impl ValueKey { - pub fn new(key: DHTKey) -> Self { - Self { key, subkey: None } - } - pub fn new_subkey(key: DHTKey, subkey: String) -> Self { - Self { - key, - subkey: if subkey.is_empty() { - None - } else { - Some(subkey) - }, - } - } -} - -#[derive( - Clone, - Debug, - Default, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct ValueData { - pub data: Vec, - pub seq: u32, -} -impl ValueData { - pub fn new(data: Vec) -> Self { - Self { data, seq: 0 } - } - pub fn new_with_seq(data: Vec, seq: u32) -> Self { - Self { data, seq } - } - pub fn change(&mut self, data: Vec) { - self.data = data; - self.seq += 1; - } -} - -#[derive( - Clone, - Debug, - Default, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct BlockId { - pub key: DHTKey, -} -impl BlockId { - pub fn new(key: DHTKey) -> Self { - Self { key } - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -// Keep member order appropriate for sorting < preference -#[derive( - Copy, - Clone, - Debug, - Eq, - PartialEq, - Ord, - PartialOrd, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum DialInfoClass { - Direct = 0, // D = Directly reachable with public IP and no firewall, with statically configured port - Mapped = 1, // M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port - FullConeNAT = 2, // F = Directly reachable device without portmap behind full-cone NAT - Blocked = 3, // B = Inbound blocked at firewall but may hole punch with public address - AddressRestrictedNAT = 4, // A = Device without portmap behind address-only restricted NAT - PortRestrictedNAT = 5, // P = Device without portmap behind address-and-port restricted NAT -} - -impl DialInfoClass { - // Is a signal required to do an inbound hole-punch? - pub fn requires_signal(&self) -> bool { - matches!( - self, - Self::Blocked | Self::AddressRestrictedNAT | Self::PortRestrictedNAT - ) - } - - // Does a relay node need to be allocated for this dial info? - // For full cone NAT, the relay itself may not be used but the keepalive sent to it - // is required to keep the NAT mapping valid in the router state table - pub fn requires_relay(&self) -> bool { - matches!( - self, - Self::FullConeNAT - | Self::Blocked - | Self::AddressRestrictedNAT - | Self::PortRestrictedNAT - ) - } -} - -// Ordering here matters, >= is used to check strength of sequencing requirement -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum Sequencing { - NoPreference, - PreferOrdered, - EnsureOrdered, -} - -impl Default for Sequencing { - fn default() -> Self { - Self::NoPreference - } -} - -// Ordering here matters, >= is used to check strength of stability requirement -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum Stability { - LowLatency, - Reliable, -} - -impl Default for Stability { - fn default() -> Self { - Self::LowLatency - } -} - -/// The choice of safety route to include in compiled routes -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum SafetySelection { - /// Don't use a safety route, only specify the sequencing preference - Unsafe(Sequencing), - /// Use a safety route and parameters specified by a SafetySpec - Safe(SafetySpec), -} - -/// Options for safety routes (sender privacy) -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct SafetySpec { - /// preferred safety route if it still exists - pub preferred_route: Option, - /// must be greater than 0 - pub hop_count: usize, - /// prefer reliability over speed - pub stability: Stability, - /// prefer connection-oriented sequenced protocols - pub sequencing: Sequencing, -} - -// Keep member order appropriate for sorting < preference -#[derive( - Debug, - Clone, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoDetail { - pub class: DialInfoClass, - pub dial_info: DialInfo, -} - -impl MatchesDialInfoFilter for DialInfoDetail { - fn matches_filter(&self, filter: &DialInfoFilter) -> bool { - self.dial_info.matches_filter(filter) - } -} - -impl DialInfoDetail { - pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering { - if a.class < b.class { - return core::cmp::Ordering::Less; - } - if a.class > b.class { - return core::cmp::Ordering::Greater; - } - DialInfo::ordered_sequencing_sort(&a.dial_info, &b.dial_info) - } - pub const NO_SORT: std::option::Option< - for<'r, 's> fn( - &'r veilid_api::DialInfoDetail, - &'s veilid_api::DialInfoDetail, - ) -> std::cmp::Ordering, - > = None:: core::cmp::Ordering>; -} - -#[derive( - Copy, - Clone, - Debug, - Eq, - PartialEq, - Ord, - PartialOrd, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum NetworkClass { - InboundCapable = 0, // I = Inbound capable without relay, may require signal - OutboundOnly = 1, // O = Outbound only, inbound relay required except with reverse connect signal - WebApp = 2, // W = PWA, outbound relay is required in most cases - Invalid = 3, // X = Invalid network class, we don't know how to reach this node -} - -impl Default for NetworkClass { - fn default() -> Self { - Self::Invalid - } -} - -impl NetworkClass { - // Should an outbound relay be kept available? - pub fn outbound_wants_relay(&self) -> bool { - matches!(self, Self::WebApp) - } -} - -/// RoutingDomain-specific status for each node -/// is returned by the StatusA call - -/// PublicInternet RoutingDomain Status -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PublicInternetNodeStatus { - pub will_route: bool, - pub will_tunnel: bool, - pub will_signal: bool, - pub will_relay: bool, - pub will_validate_dial_info: bool, -} - -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct LocalNetworkNodeStatus { - pub will_relay: bool, - pub will_validate_dial_info: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum NodeStatus { - PublicInternet(PublicInternetNodeStatus), - LocalNetwork(LocalNetworkNodeStatus), -} - -impl NodeStatus { - pub fn will_route(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_route, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_tunnel(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_tunnel, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_signal(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_signal, - NodeStatus::LocalNetwork(_) => false, - } - } - pub fn will_relay(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_relay, - NodeStatus::LocalNetwork(ln) => ln.will_relay, - } - } - pub fn will_validate_dial_info(&self) -> bool { - match self { - NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info, - NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct NodeInfo { - pub network_class: NetworkClass, - #[with(RkyvEnumSet)] - pub outbound_protocols: ProtocolTypeSet, - #[with(RkyvEnumSet)] - pub address_types: AddressTypeSet, - pub min_version: u8, - pub max_version: u8, - pub dial_info_detail_list: Vec, -} - -impl NodeInfo { - pub fn first_filtered_dial_info_detail( - &self, - sort: Option, - filter: F, - ) -> Option - where - S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, - F: Fn(&DialInfoDetail) -> bool, - { - if let Some(sort) = sort { - let mut dids = self.dial_info_detail_list.clone(); - dids.sort_by(sort); - for did in dids { - if filter(&did) { - return Some(did); - } - } - } else { - for did in &self.dial_info_detail_list { - if filter(did) { - return Some(did.clone()); - } - } - }; - None - } - - pub fn all_filtered_dial_info_details( - &self, - sort: Option, - filter: F, - ) -> Vec - where - S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, - F: Fn(&DialInfoDetail) -> bool, - { - let mut dial_info_detail_list = Vec::new(); - - if let Some(sort) = sort { - let mut dids = self.dial_info_detail_list.clone(); - dids.sort_by(sort); - for did in dids { - if filter(&did) { - dial_info_detail_list.push(did); - } - } - } else { - for did in &self.dial_info_detail_list { - if filter(did) { - dial_info_detail_list.push(did.clone()); - } - } - }; - dial_info_detail_list - } - - /// Does this node has some dial info - pub fn has_dial_info(&self) -> bool { - !self.dial_info_detail_list.is_empty() - } - - /// Is some relay required either for signal or inbound relay or outbound relay? - pub fn requires_relay(&self) -> bool { - match self.network_class { - NetworkClass::InboundCapable => { - for did in &self.dial_info_detail_list { - if did.class.requires_relay() { - return true; - } - } - } - NetworkClass::OutboundOnly => { - return true; - } - NetworkClass::WebApp => { - return true; - } - NetworkClass::Invalid => {} - } - false - } - - /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. - pub fn can_signal(&self) -> bool { - // Must be inbound capable - if !matches!(self.network_class, NetworkClass::InboundCapable) { - return false; - } - // Do any of our dial info require signalling? if so, we can't offer signalling - for did in &self.dial_info_detail_list { - if did.class.requires_signal() { - return false; - } - } - true - } - - /// Can this node relay be an inbound relay? - pub fn can_inbound_relay(&self) -> bool { - // For now this is the same - self.can_signal() - } - - /// Is this node capable of validating dial info - pub fn can_validate_dial_info(&self) -> bool { - // For now this is the same - self.can_signal() - } -} - -#[allow(clippy::derive_hash_xor_eq)] -#[derive( - Debug, - PartialOrd, - Ord, - Hash, - EnumSetType, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[enumset(repr = "u8")] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum Direction { - Inbound, - Outbound, -} -pub type DirectionSet = EnumSet; - -// Keep member order appropriate for sorting < preference -// Must match DialInfo order -#[allow(clippy::derive_hash_xor_eq)] -#[derive( - Debug, - PartialOrd, - Ord, - Hash, - EnumSetType, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[enumset(repr = "u8")] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum LowLevelProtocolType { - UDP, - TCP, -} - -impl LowLevelProtocolType { - pub fn is_connection_oriented(&self) -> bool { - matches!(self, LowLevelProtocolType::TCP) - } -} -pub type LowLevelProtocolTypeSet = EnumSet; - -// Keep member order appropriate for sorting < preference -// Must match DialInfo order -#[allow(clippy::derive_hash_xor_eq)] -#[derive( - Debug, - PartialOrd, - Ord, - Hash, - EnumSetType, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[enumset(repr = "u8")] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum ProtocolType { - UDP, - TCP, - WS, - WSS, -} - -impl ProtocolType { - pub fn is_connection_oriented(&self) -> bool { - matches!( - self, - ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS - ) - } - pub fn low_level_protocol_type(&self) -> LowLevelProtocolType { - match self { - ProtocolType::UDP => LowLevelProtocolType::UDP, - ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS => LowLevelProtocolType::TCP, - } - } - pub fn sort_order(&self, sequencing: Sequencing) -> usize { - match self { - ProtocolType::UDP => { - if sequencing != Sequencing::NoPreference { - 3 - } else { - 0 - } - } - ProtocolType::TCP => { - if sequencing != Sequencing::NoPreference { - 0 - } else { - 1 - } - } - ProtocolType::WS => { - if sequencing != Sequencing::NoPreference { - 1 - } else { - 2 - } - } - ProtocolType::WSS => { - if sequencing != Sequencing::NoPreference { - 2 - } else { - 3 - } - } - } - } - pub fn all_ordered_set() -> ProtocolTypeSet { - ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS - } -} - -pub type ProtocolTypeSet = EnumSet; - -#[allow(clippy::derive_hash_xor_eq)] -#[derive( - Debug, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, - EnumSetType, -)] -#[enumset(repr = "u8")] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum AddressType { - IPV4, - IPV6, -} -pub type AddressTypeSet = EnumSet; - -// Routing domain here is listed in order of preference, keep in order -#[allow(clippy::derive_hash_xor_eq)] -#[derive( - Debug, - Ord, - PartialOrd, - Hash, - EnumSetType, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[enumset(repr = "u8")] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum RoutingDomain { - LocalNetwork = 0, - PublicInternet = 1, -} -impl RoutingDomain { - pub const fn count() -> usize { - 2 - } - pub const fn all() -> [RoutingDomain; RoutingDomain::count()] { - // Routing domain here is listed in order of preference, keep in order - [RoutingDomain::LocalNetwork, RoutingDomain::PublicInternet] - } -} -pub type RoutingDomainSet = EnumSet; - -#[derive( - Copy, - Clone, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum Address { - IPV4(Ipv4Addr), - IPV6(Ipv6Addr), -} - -impl Default for Address { - fn default() -> Self { - Address::IPV4(Ipv4Addr::new(0, 0, 0, 0)) - } -} - -impl Address { - pub fn from_socket_addr(sa: SocketAddr) -> Address { - match sa { - SocketAddr::V4(v4) => Address::IPV4(*v4.ip()), - SocketAddr::V6(v6) => Address::IPV6(*v6.ip()), - } - } - pub fn from_ip_addr(addr: IpAddr) -> Address { - match addr { - IpAddr::V4(v4) => Address::IPV4(v4), - IpAddr::V6(v6) => Address::IPV6(v6), - } - } - pub fn address_type(&self) -> AddressType { - match self { - Address::IPV4(_) => AddressType::IPV4, - Address::IPV6(_) => AddressType::IPV6, - } - } - pub fn address_string(&self) -> String { - match self { - Address::IPV4(v4) => v4.to_string(), - Address::IPV6(v6) => v6.to_string(), - } - } - pub fn address_string_with_port(&self, port: u16) -> String { - match self { - Address::IPV4(v4) => format!("{}:{}", v4, port), - Address::IPV6(v6) => format!("[{}]:{}", v6, port), - } - } - pub fn is_unspecified(&self) -> bool { - match self { - Address::IPV4(v4) => ipv4addr_is_unspecified(v4), - Address::IPV6(v6) => ipv6addr_is_unspecified(v6), - } - } - pub fn is_global(&self) -> bool { - match self { - Address::IPV4(v4) => ipv4addr_is_global(v4) && !ipv4addr_is_multicast(v4), - Address::IPV6(v6) => ipv6addr_is_unicast_global(v6), - } - } - pub fn is_local(&self) -> bool { - match self { - Address::IPV4(v4) => { - ipv4addr_is_private(v4) - || ipv4addr_is_link_local(v4) - || ipv4addr_is_ietf_protocol_assignment(v4) - } - Address::IPV6(v6) => { - ipv6addr_is_unicast_site_local(v6) - || ipv6addr_is_unicast_link_local(v6) - || ipv6addr_is_unique_local(v6) - } - } - } - pub fn to_ip_addr(&self) -> IpAddr { - match self { - Self::IPV4(a) => IpAddr::V4(*a), - Self::IPV6(a) => IpAddr::V6(*a), - } - } - pub fn to_socket_addr(&self, port: u16) -> SocketAddr { - SocketAddr::new(self.to_ip_addr(), port) - } - pub fn to_canonical(&self) -> Address { - match self { - Address::IPV4(v4) => Address::IPV4(*v4), - Address::IPV6(v6) => match v6.to_ipv4() { - Some(v4) => Address::IPV4(v4), - None => Address::IPV6(*v6), - }, - } - } -} - -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Address::IPV4(v4) => write!(f, "{}", v4), - Address::IPV6(v6) => write!(f, "{}", v6), - } - } -} - -impl FromStr for Address { - type Err = VeilidAPIError; - fn from_str(host: &str) -> Result { - if let Ok(addr) = Ipv4Addr::from_str(host) { - Ok(Address::IPV4(addr)) - } else if let Ok(addr) = Ipv6Addr::from_str(host) { - Ok(Address::IPV6(addr)) - } else { - Err(VeilidAPIError::parse_error( - "Address::from_str failed", - host, - )) - } - } -} - -#[derive( - Copy, - Default, - Clone, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct SocketAddress { - address: Address, - port: u16, -} - -impl SocketAddress { - pub fn new(address: Address, port: u16) -> Self { - Self { address, port } - } - pub fn from_socket_addr(sa: SocketAddr) -> SocketAddress { - Self { - address: Address::from_socket_addr(sa), - port: sa.port(), - } - } - pub fn address(&self) -> Address { - self.address - } - pub fn address_type(&self) -> AddressType { - self.address.address_type() - } - pub fn port(&self) -> u16 { - self.port - } - pub fn to_canonical(&self) -> SocketAddress { - SocketAddress { - address: self.address.to_canonical(), - port: self.port, - } - } - pub fn to_ip_addr(&self) -> IpAddr { - self.address.to_ip_addr() - } - pub fn to_socket_addr(&self) -> SocketAddr { - self.address.to_socket_addr(self.port) - } -} - -impl fmt::Display for SocketAddress { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.to_socket_addr()) - } -} - -impl FromStr for SocketAddress { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let sa = SocketAddr::from_str(s) - .map_err(|e| VeilidAPIError::parse_error("Failed to parse SocketAddress", e))?; - Ok(SocketAddress::from_socket_addr(sa)) - } -} - -////////////////////////////////////////////////////////////////// - -#[derive( - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoFilter { - #[with(RkyvEnumSet)] - pub protocol_type_set: ProtocolTypeSet, - #[with(RkyvEnumSet)] - pub address_type_set: AddressTypeSet, -} - -impl Default for DialInfoFilter { - fn default() -> Self { - Self { - protocol_type_set: ProtocolTypeSet::all(), - address_type_set: AddressTypeSet::all(), - } - } -} - -impl DialInfoFilter { - pub fn all() -> Self { - Self { - protocol_type_set: ProtocolTypeSet::all(), - address_type_set: AddressTypeSet::all(), - } - } - pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self { - self.protocol_type_set = ProtocolTypeSet::only(protocol_type); - self - } - pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self { - self.protocol_type_set = protocol_set; - self - } - pub fn with_address_type(mut self, address_type: AddressType) -> Self { - self.address_type_set = AddressTypeSet::only(address_type); - self - } - pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self { - self.address_type_set = address_set; - self - } - pub fn filtered(mut self, other_dif: &DialInfoFilter) -> Self { - self.protocol_type_set &= other_dif.protocol_type_set; - self.address_type_set &= other_dif.address_type_set; - self - } - pub fn is_dead(&self) -> bool { - self.protocol_type_set.is_empty() || self.address_type_set.is_empty() - } -} - -impl fmt::Debug for DialInfoFilter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let mut out = String::new(); - if self.protocol_type_set != ProtocolTypeSet::all() { - out += &format!("+{:?}", self.protocol_type_set); - } else { - out += "*"; - } - if self.address_type_set != AddressTypeSet::all() { - out += &format!("+{:?}", self.address_type_set); - } else { - out += "*"; - } - write!(f, "[{}]", out) - } -} - -pub trait MatchesDialInfoFilter { - fn matches_filter(&self, filter: &DialInfoFilter) -> bool; -} - -#[derive( - Clone, - Default, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoUDP { - pub socket_address: SocketAddress, -} - -#[derive( - Clone, - Default, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoTCP { - pub socket_address: SocketAddress, -} - -#[derive( - Clone, - Default, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoWS { - pub socket_address: SocketAddress, - pub request: String, -} - -#[derive( - Clone, - Default, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct DialInfoWSS { - pub socket_address: SocketAddress, - pub request: String, -} - -// Keep member order appropriate for sorting < preference -// Must match ProtocolType order -#[derive( - Clone, - Debug, - PartialEq, - PartialOrd, - Ord, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -#[serde(tag = "kind")] -pub enum DialInfo { - UDP(DialInfoUDP), - TCP(DialInfoTCP), - WS(DialInfoWS), - WSS(DialInfoWSS), -} -impl Default for DialInfo { - fn default() -> Self { - DialInfo::UDP(DialInfoUDP::default()) - } -} - -impl fmt::Display for DialInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address), - DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address), - DialInfo::WS(di) => { - let url = format!("ws://{}", di.request); - let split_url = SplitUrl::from_str(&url).unwrap(); - match split_url.host { - SplitUrlHost::Hostname(_) => { - write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request) - } - SplitUrlHost::IpAddr(a) => { - if di.socket_address.to_ip_addr() == a { - write!(f, "ws|{}", di.request) - } else { - panic!("resolved address does not match url: {}", di.request); - } - } - } - } - DialInfo::WSS(di) => { - let url = format!("wss://{}", di.request); - let split_url = SplitUrl::from_str(&url).unwrap(); - match split_url.host { - SplitUrlHost::Hostname(_) => { - write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request) - } - SplitUrlHost::IpAddr(_) => { - panic!( - "secure websockets can not use ip address in request: {}", - di.request - ); - } - } - } - } - } -} - -impl FromStr for DialInfo { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let (proto, rest) = s.split_once('|').ok_or_else(|| { - VeilidAPIError::parse_error("DialInfo::from_str missing protocol '|' separator", s) - })?; - match proto { - "udp" => { - let socket_address = SocketAddress::from_str(rest)?; - Ok(DialInfo::udp(socket_address)) - } - "tcp" => { - let socket_address = SocketAddress::from_str(rest)?; - Ok(DialInfo::tcp(socket_address)) - } - "ws" => { - let url = format!("ws://{}", rest); - let split_url = SplitUrl::from_str(&url).map_err(|e| { - VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) - })?; - if split_url.scheme != "ws" || !url.starts_with("ws://") { - apibail_parse_error!("incorrect scheme for WS dialinfo", url); - } - let url_port = split_url.port.unwrap_or(80u16); - - match rest.split_once('|') { - Some((sa, rest)) => { - let address = Address::from_str(sa)?; - - DialInfo::try_ws( - SocketAddress::new(address, url_port), - format!("ws://{}", rest), - ) - } - None => { - let address = Address::from_str(&split_url.host.to_string())?; - DialInfo::try_ws( - SocketAddress::new(address, url_port), - format!("ws://{}", rest), - ) - } - } - } - "wss" => { - let url = format!("wss://{}", rest); - let split_url = SplitUrl::from_str(&url).map_err(|e| { - VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) - })?; - if split_url.scheme != "wss" || !url.starts_with("wss://") { - apibail_parse_error!("incorrect scheme for WSS dialinfo", url); - } - let url_port = split_url.port.unwrap_or(443u16); - - let (a, rest) = rest.split_once('|').ok_or_else(|| { - VeilidAPIError::parse_error( - "DialInfo::from_str missing socket address '|' separator", - s, - ) - })?; - - let address = Address::from_str(a)?; - DialInfo::try_wss( - SocketAddress::new(address, url_port), - format!("wss://{}", rest), - ) - } - _ => Err(VeilidAPIError::parse_error( - "DialInfo::from_str has invalid scheme", - s, - )), - } - } -} - -impl DialInfo { - pub fn udp_from_socketaddr(socket_addr: SocketAddr) -> Self { - Self::UDP(DialInfoUDP { - socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), - }) - } - pub fn tcp_from_socketaddr(socket_addr: SocketAddr) -> Self { - Self::TCP(DialInfoTCP { - socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), - }) - } - pub fn udp(socket_address: SocketAddress) -> Self { - Self::UDP(DialInfoUDP { - socket_address: socket_address.to_canonical(), - }) - } - pub fn tcp(socket_address: SocketAddress) -> Self { - Self::TCP(DialInfoTCP { - socket_address: socket_address.to_canonical(), - }) - } - pub fn try_ws(socket_address: SocketAddress, url: String) -> Result { - let split_url = SplitUrl::from_str(&url).map_err(|e| { - VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) - })?; - if split_url.scheme != "ws" || !url.starts_with("ws://") { - apibail_parse_error!("incorrect scheme for WS dialinfo", url); - } - let url_port = split_url.port.unwrap_or(80u16); - if url_port != socket_address.port() { - apibail_parse_error!("socket address port doesn't match url port", url); - } - if let SplitUrlHost::IpAddr(a) = split_url.host { - if socket_address.to_ip_addr() != a { - apibail_parse_error!( - format!("request address does not match socket address: {}", a), - socket_address - ); - } - } - Ok(Self::WS(DialInfoWS { - socket_address: socket_address.to_canonical(), - request: url[5..].to_string(), - })) - } - pub fn try_wss(socket_address: SocketAddress, url: String) -> Result { - let split_url = SplitUrl::from_str(&url).map_err(|e| { - VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) - })?; - if split_url.scheme != "wss" || !url.starts_with("wss://") { - apibail_parse_error!("incorrect scheme for WSS dialinfo", url); - } - let url_port = split_url.port.unwrap_or(443u16); - if url_port != socket_address.port() { - apibail_parse_error!("socket address port doesn't match url port", url); - } - if !matches!(split_url.host, SplitUrlHost::Hostname(_)) { - apibail_parse_error!( - "WSS url can not use address format, only hostname format", - url - ); - } - Ok(Self::WSS(DialInfoWSS { - socket_address: socket_address.to_canonical(), - request: url[6..].to_string(), - })) - } - pub fn protocol_type(&self) -> ProtocolType { - match self { - Self::UDP(_) => ProtocolType::UDP, - Self::TCP(_) => ProtocolType::TCP, - Self::WS(_) => ProtocolType::WS, - Self::WSS(_) => ProtocolType::WSS, - } - } - pub fn address_type(&self) -> AddressType { - self.socket_address().address_type() - } - pub fn address(&self) -> Address { - match self { - Self::UDP(di) => di.socket_address.address, - Self::TCP(di) => di.socket_address.address, - Self::WS(di) => di.socket_address.address, - Self::WSS(di) => di.socket_address.address, - } - } - pub fn socket_address(&self) -> SocketAddress { - match self { - Self::UDP(di) => di.socket_address, - Self::TCP(di) => di.socket_address, - Self::WS(di) => di.socket_address, - Self::WSS(di) => di.socket_address, - } - } - pub fn to_ip_addr(&self) -> IpAddr { - match self { - Self::UDP(di) => di.socket_address.to_ip_addr(), - Self::TCP(di) => di.socket_address.to_ip_addr(), - Self::WS(di) => di.socket_address.to_ip_addr(), - Self::WSS(di) => di.socket_address.to_ip_addr(), - } - } - pub fn port(&self) -> u16 { - match self { - Self::UDP(di) => di.socket_address.port, - Self::TCP(di) => di.socket_address.port, - Self::WS(di) => di.socket_address.port, - Self::WSS(di) => di.socket_address.port, - } - } - pub fn set_port(&mut self, port: u16) { - match self { - Self::UDP(di) => di.socket_address.port = port, - Self::TCP(di) => di.socket_address.port = port, - Self::WS(di) => di.socket_address.port = port, - Self::WSS(di) => di.socket_address.port = port, - } - } - pub fn to_socket_addr(&self) -> SocketAddr { - match self { - Self::UDP(di) => di.socket_address.to_socket_addr(), - Self::TCP(di) => di.socket_address.to_socket_addr(), - Self::WS(di) => di.socket_address.to_socket_addr(), - Self::WSS(di) => di.socket_address.to_socket_addr(), - } - } - pub fn to_peer_address(&self) -> PeerAddress { - match self { - Self::UDP(di) => PeerAddress::new(di.socket_address, ProtocolType::UDP), - Self::TCP(di) => PeerAddress::new(di.socket_address, ProtocolType::TCP), - Self::WS(di) => PeerAddress::new(di.socket_address, ProtocolType::WS), - Self::WSS(di) => PeerAddress::new(di.socket_address, ProtocolType::WSS), - } - } - pub fn request(&self) -> Option { - match self { - Self::UDP(_) => None, - Self::TCP(_) => None, - Self::WS(di) => Some(format!("ws://{}", di.request)), - Self::WSS(di) => Some(format!("wss://{}", di.request)), - } - } - pub fn is_valid(&self) -> bool { - let socket_address = self.socket_address(); - let address = socket_address.address(); - let port = socket_address.port(); - (address.is_global() || address.is_local()) && port > 0 - } - - pub fn make_filter(&self) -> DialInfoFilter { - DialInfoFilter { - protocol_type_set: ProtocolTypeSet::only(self.protocol_type()), - address_type_set: AddressTypeSet::only(self.address_type()), - } - } - - pub fn try_vec_from_short, H: AsRef>( - short: S, - hostname: H, - ) -> Result, VeilidAPIError> { - let short = short.as_ref(); - let hostname = hostname.as_ref(); - - if short.len() < 2 { - apibail_parse_error!("invalid short url length", short); - } - let url = match &short[0..1] { - "U" => { - format!("udp://{}:{}", hostname, &short[1..]) - } - "T" => { - format!("tcp://{}:{}", hostname, &short[1..]) - } - "W" => { - format!("ws://{}:{}", hostname, &short[1..]) - } - "S" => { - format!("wss://{}:{}", hostname, &short[1..]) - } - _ => { - apibail_parse_error!("invalid short url type", short); - } - }; - Self::try_vec_from_url(url) - } - - pub fn try_vec_from_url>(url: S) -> Result, VeilidAPIError> { - let url = url.as_ref(); - let split_url = SplitUrl::from_str(url) - .map_err(|e| VeilidAPIError::parse_error(format!("unable to split url: {}", e), url))?; - - let port = match split_url.scheme.as_str() { - "udp" | "tcp" => split_url - .port - .ok_or_else(|| VeilidAPIError::parse_error("Missing port in udp url", url))?, - "ws" => split_url.port.unwrap_or(80u16), - "wss" => split_url.port.unwrap_or(443u16), - _ => { - apibail_parse_error!("Invalid dial info url scheme", split_url.scheme); - } - }; - - let socket_addrs = { - // Resolve if possible, WASM doesn't support resolution and doesn't need it to connect to the dialinfo - // This will not be used on signed dialinfo, only for bootstrapping, so we don't need to worry about - // the '0.0.0.0' address being propagated across the routing table - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0,0,0,0)), port)] - } else { - match split_url.host { - SplitUrlHost::Hostname(_) => split_url - .host_port(port) - .to_socket_addrs() - .map_err(|_| VeilidAPIError::parse_error("couldn't resolve hostname in url", url))? - .collect(), - SplitUrlHost::IpAddr(a) => vec![SocketAddr::new(a, port)], - } - } - } - }; - - let mut out = Vec::new(); - for sa in socket_addrs { - out.push(match split_url.scheme.as_str() { - "udp" => Self::udp_from_socketaddr(sa), - "tcp" => Self::tcp_from_socketaddr(sa), - "ws" => Self::try_ws( - SocketAddress::from_socket_addr(sa).to_canonical(), - url.to_string(), - )?, - "wss" => Self::try_wss( - SocketAddress::from_socket_addr(sa).to_canonical(), - url.to_string(), - )?, - _ => { - unreachable!("Invalid dial info url scheme") - } - }); - } - Ok(out) - } - - pub async fn to_short(&self) -> (String, String) { - match self { - DialInfo::UDP(di) => ( - format!("U{}", di.socket_address.port()), - intf::ptr_lookup(di.socket_address.to_ip_addr()) - .await - .unwrap_or_else(|_| di.socket_address.to_string()), - ), - DialInfo::TCP(di) => ( - format!("T{}", di.socket_address.port()), - intf::ptr_lookup(di.socket_address.to_ip_addr()) - .await - .unwrap_or_else(|_| di.socket_address.to_string()), - ), - DialInfo::WS(di) => { - let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap(); - if let SplitUrlHost::IpAddr(a) = split_url.host { - if let Ok(host) = intf::ptr_lookup(a).await { - split_url.host = SplitUrlHost::Hostname(host); - } - } - ( - format!( - "W{}{}", - split_url.port.unwrap_or(80), - split_url - .path - .map(|p| format!("/{}", p)) - .unwrap_or_default() - ), - split_url.host.to_string(), - ) - } - DialInfo::WSS(di) => { - let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap(); - if let SplitUrlHost::IpAddr(a) = split_url.host { - if let Ok(host) = intf::ptr_lookup(a).await { - split_url.host = SplitUrlHost::Hostname(host); - } - } - ( - format!( - "S{}{}", - split_url.port.unwrap_or(443), - split_url - .path - .map(|p| format!("/{}", p)) - .unwrap_or_default() - ), - split_url.host.to_string(), - ) - } - } - } - pub async fn to_url(&self) -> String { - match self { - DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) - .await - .map(|h| format!("udp://{}:{}", h, di.socket_address.port())) - .unwrap_or_else(|_| format!("udp://{}", di.socket_address)), - DialInfo::TCP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) - .await - .map(|h| format!("tcp://{}:{}", h, di.socket_address.port())) - .unwrap_or_else(|_| format!("tcp://{}", di.socket_address)), - DialInfo::WS(di) => { - let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap(); - if let SplitUrlHost::IpAddr(a) = split_url.host { - if let Ok(host) = intf::ptr_lookup(a).await { - split_url.host = SplitUrlHost::Hostname(host); - } - } - split_url.to_string() - } - DialInfo::WSS(di) => { - let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap(); - if let SplitUrlHost::IpAddr(a) = split_url.host { - if let Ok(host) = intf::ptr_lookup(a).await { - split_url.host = SplitUrlHost::Hostname(host); - } - } - split_url.to_string() - } - } - } - - pub fn ordered_sequencing_sort(a: &DialInfo, b: &DialInfo) -> core::cmp::Ordering { - let ca = a.protocol_type().sort_order(Sequencing::EnsureOrdered); - let cb = b.protocol_type().sort_order(Sequencing::EnsureOrdered); - if ca < cb { - return core::cmp::Ordering::Less; - } - if ca > cb { - return core::cmp::Ordering::Greater; - } - match (a, b) { - (DialInfo::UDP(a), DialInfo::UDP(b)) => a.cmp(b), - (DialInfo::TCP(a), DialInfo::TCP(b)) => a.cmp(b), - (DialInfo::WS(a), DialInfo::WS(b)) => a.cmp(b), - (DialInfo::WSS(a), DialInfo::WSS(b)) => a.cmp(b), - _ => unreachable!(), - } - } -} - -impl MatchesDialInfoFilter for DialInfo { - fn matches_filter(&self, filter: &DialInfoFilter) -> bool { - if !filter.protocol_type_set.contains(self.protocol_type()) { - return false; - } - if !filter.address_type_set.contains(self.address_type()) { - return false; - } - true - } -} - -////////////////////////////////////////////////////////////////////////// - -// Signed NodeInfo that can be passed around amongst peers and verifiable -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct SignedDirectNodeInfo { - pub node_info: NodeInfo, - pub timestamp: u64, - pub signature: Option, -} - -impl SignedDirectNodeInfo { - pub fn new( - node_id: NodeId, - node_info: NodeInfo, - timestamp: u64, - signature: DHTSignature, - ) -> Result { - let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; - verify(&node_id.key, &node_info_bytes, &signature)?; - Ok(Self { - node_info, - timestamp, - signature: Some(signature), - }) - } - - pub fn with_secret( - node_id: NodeId, - node_info: NodeInfo, - secret: &DHTKeySecret, - ) -> Result { - let timestamp = intf::get_timestamp(); - let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; - let signature = sign(&node_id.key, secret, &node_info_bytes)?; - Ok(Self { - node_info, - timestamp, - signature: Some(signature), - }) - } - - fn make_signature_bytes( - node_info: &NodeInfo, - timestamp: u64, - ) -> Result, VeilidAPIError> { - let mut node_info_bytes = Vec::new(); - - // Add nodeinfo to signature - let mut ni_msg = ::capnp::message::Builder::new_default(); - let mut ni_builder = ni_msg.init_root::(); - encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?; - node_info_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?); - - // Add timestamp to signature - node_info_bytes.append(&mut timestamp.to_le_bytes().to_vec()); - - Ok(node_info_bytes) - } - - pub fn with_no_signature(node_info: NodeInfo) -> Self { - Self { - node_info, - signature: None, - timestamp: intf::get_timestamp(), - } - } - - pub fn has_valid_signature(&self) -> bool { - self.signature.is_some() - } -} - -/// Signed NodeInfo with a relay that can be passed around amongst peers and verifiable -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct SignedRelayedNodeInfo { - pub node_info: NodeInfo, - pub relay_id: NodeId, - pub relay_info: SignedDirectNodeInfo, - pub timestamp: u64, - pub signature: DHTSignature, -} - -impl SignedRelayedNodeInfo { - pub fn new( - node_id: NodeId, - node_info: NodeInfo, - relay_id: NodeId, - relay_info: SignedDirectNodeInfo, - timestamp: u64, - signature: DHTSignature, - ) -> Result { - let node_info_bytes = - Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; - verify(&node_id.key, &node_info_bytes, &signature)?; - Ok(Self { - node_info, - relay_id, - relay_info, - signature, - timestamp, - }) - } - - pub fn with_secret( - node_id: NodeId, - node_info: NodeInfo, - relay_id: NodeId, - relay_info: SignedDirectNodeInfo, - secret: &DHTKeySecret, - ) -> Result { - let timestamp = intf::get_timestamp(); - let node_info_bytes = - Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; - let signature = sign(&node_id.key, secret, &node_info_bytes)?; - Ok(Self { - node_info, - relay_id, - relay_info, - signature, - timestamp, - }) - } - - fn make_signature_bytes( - node_info: &NodeInfo, - relay_id: &NodeId, - relay_info: &SignedDirectNodeInfo, - timestamp: u64, - ) -> Result, VeilidAPIError> { - let mut sig_bytes = Vec::new(); - - // Add nodeinfo to signature - let mut ni_msg = ::capnp::message::Builder::new_default(); - let mut ni_builder = ni_msg.init_root::(); - encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?; - sig_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?); - - // Add relay id to signature - let mut rid_msg = ::capnp::message::Builder::new_default(); - let mut rid_builder = rid_msg.init_root::(); - encode_dht_key(&relay_id.key, &mut rid_builder).map_err(VeilidAPIError::internal)?; - sig_bytes.append(&mut builder_to_vec(rid_msg).map_err(VeilidAPIError::internal)?); - - // Add relay info to signature - let mut ri_msg = ::capnp::message::Builder::new_default(); - let mut ri_builder = ri_msg.init_root::(); - encode_signed_direct_node_info(relay_info, &mut ri_builder) - .map_err(VeilidAPIError::internal)?; - sig_bytes.append(&mut builder_to_vec(ri_msg).map_err(VeilidAPIError::internal)?); - - // Add timestamp to signature - sig_bytes.append(&mut timestamp.to_le_bytes().to_vec()); - - Ok(sig_bytes) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum SignedNodeInfo { - Direct(SignedDirectNodeInfo), - Relayed(SignedRelayedNodeInfo), -} - -impl SignedNodeInfo { - pub fn has_valid_signature(&self) -> bool { - match self { - SignedNodeInfo::Direct(d) => d.has_valid_signature(), - SignedNodeInfo::Relayed(_) => true, - } - } - - pub fn timestamp(&self) -> u64 { - match self { - SignedNodeInfo::Direct(d) => d.timestamp, - SignedNodeInfo::Relayed(r) => r.timestamp, - } - } - - pub fn node_info(&self) -> &NodeInfo { - match self { - SignedNodeInfo::Direct(d) => &d.node_info, - SignedNodeInfo::Relayed(r) => &r.node_info, - } - } - pub fn relay_id(&self) -> Option { - match self { - SignedNodeInfo::Direct(_) => None, - SignedNodeInfo::Relayed(r) => Some(r.relay_id.clone()), - } - } - pub fn relay_info(&self) -> Option<&NodeInfo> { - match self { - SignedNodeInfo::Direct(_) => None, - SignedNodeInfo::Relayed(r) => Some(&r.relay_info.node_info), - } - } - pub fn relay_peer_info(&self) -> Option { - match self { - SignedNodeInfo::Direct(_) => None, - SignedNodeInfo::Relayed(r) => Some(PeerInfo::new( - r.relay_id.clone(), - SignedNodeInfo::Direct(r.relay_info.clone()), - )), - } - } - pub fn has_any_dial_info(&self) -> bool { - self.node_info().has_dial_info() - || self - .relay_info() - .map(|relay_ni| relay_ni.has_dial_info()) - .unwrap_or_default() - } - - pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { - // Check our dial info - for did in &self.node_info().dial_info_detail_list { - match sequencing { - Sequencing::NoPreference | Sequencing::PreferOrdered => return true, - Sequencing::EnsureOrdered => { - if did.dial_info.protocol_type().is_connection_oriented() { - return true; - } - } - } - } - // Check our relay if we have one - return self - .relay_info() - .map(|relay_ni| { - for did in &relay_ni.dial_info_detail_list { - match sequencing { - Sequencing::NoPreference | Sequencing::PreferOrdered => return true, - Sequencing::EnsureOrdered => { - if did.dial_info.protocol_type().is_connection_oriented() { - return true; - } - } - } - } - false - }) - .unwrap_or_default(); - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PeerInfo { - pub node_id: NodeId, - pub signed_node_info: SignedNodeInfo, -} - -impl PeerInfo { - pub fn new(node_id: NodeId, signed_node_info: SignedNodeInfo) -> Self { - Self { - node_id, - signed_node_info, - } - } -} - -#[derive( - Copy, - Clone, - Debug, - PartialEq, - PartialOrd, - Eq, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PeerAddress { - protocol_type: ProtocolType, - #[serde(with = "json_as_string")] - socket_address: SocketAddress, -} - -impl PeerAddress { - pub fn new(socket_address: SocketAddress, protocol_type: ProtocolType) -> Self { - Self { - socket_address: socket_address.to_canonical(), - protocol_type, - } - } - - pub fn socket_address(&self) -> &SocketAddress { - &self.socket_address - } - - pub fn protocol_type(&self) -> ProtocolType { - self.protocol_type - } - - pub fn to_socket_addr(&self) -> SocketAddr { - self.socket_address.to_socket_addr() - } - - pub fn address_type(&self) -> AddressType { - self.socket_address.address_type() - } -} - -/// Represents the 5-tuple of an established connection -/// Not used to specify connections to create, that is reserved for DialInfo -/// -/// ConnectionDescriptors should never be from unspecified local addresses for connection oriented protocols -/// If the medium does not allow local addresses, None should have been used or 'new_no_local' -/// If we are specifying only a port, then the socket's 'local_address()' should have been used, since an -/// established connection is always from a real address to another real address. -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct ConnectionDescriptor { - remote: PeerAddress, - local: Option, -} - -impl ConnectionDescriptor { - pub fn new(remote: PeerAddress, local: SocketAddress) -> Self { - assert!( - !remote.protocol_type().is_connection_oriented() || !local.address().is_unspecified() - ); - - Self { - remote, - local: Some(local), - } - } - pub fn new_no_local(remote: PeerAddress) -> Self { - Self { - remote, - local: None, - } - } - pub fn remote(&self) -> PeerAddress { - self.remote - } - pub fn remote_address(&self) -> &SocketAddress { - self.remote.socket_address() - } - pub fn local(&self) -> Option { - self.local - } - pub fn protocol_type(&self) -> ProtocolType { - self.remote.protocol_type - } - pub fn address_type(&self) -> AddressType { - self.remote.address_type() - } - pub fn make_dial_info_filter(&self) -> DialInfoFilter { - DialInfoFilter::all() - .with_protocol_type(self.protocol_type()) - .with_address_type(self.address_type()) - } -} - -impl MatchesDialInfoFilter for ConnectionDescriptor { - fn matches_filter(&self, filter: &DialInfoFilter) -> bool { - if !filter.protocol_type_set.contains(self.protocol_type()) { - return false; - } - if !filter.address_type_set.contains(self.address_type()) { - return false; - } - true - } -} - -////////////////////////////////////////////////////////////////////////// - -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct LatencyStats { - #[serde(with = "json_as_string")] - pub fastest: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies - #[serde(with = "json_as_string")] - pub average: u64, // average latency over the ROLLING_LATENCIES_SIZE last latencies - #[serde(with = "json_as_string")] - pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies -} - -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct TransferStats { - #[serde(with = "json_as_string")] - pub total: u64, // total amount transferred ever - #[serde(with = "json_as_string")] - pub maximum: u64, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts - #[serde(with = "json_as_string")] - pub average: u64, // average rate over the ROLLING_TRANSFERS_SIZE last amounts - #[serde(with = "json_as_string")] - pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts -} - -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct TransferStatsDownUp { - pub down: TransferStats, - pub up: TransferStats, -} - -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct RPCStats { - pub messages_sent: u32, // number of rpcs that have been sent in the total_time range - pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range - pub questions_in_flight: u32, // number of questions issued that have yet to be answered - #[serde(with = "opt_json_as_string")] - pub last_question: Option, // when the peer was last questioned (either successfully or not) and we wanted an answer - #[serde(with = "opt_json_as_string")] - pub last_seen_ts: Option, // when the peer was last seen for any reason, including when we first attempted to reach out to it - #[serde(with = "opt_json_as_string")] - pub first_consecutive_seen_ts: Option, // the timestamp of the first consecutive proof-of-life for this node (an answer or received question) - pub recent_lost_answers: u32, // number of answers that have been lost since we lost reliability - pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one -} - -#[derive( - Clone, - Debug, - Default, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PeerStats { - #[serde(with = "json_as_string")] - pub time_added: u64, // when the peer was added to the routing table - pub rpc_stats: RPCStats, // information about RPCs - pub latency: Option, // latencies for communications with the peer - pub transfer: TransferStatsDownUp, // Stats for communications with the peer -} - -pub type ValueChangeCallback = - Arc) -> SendPinBoxFuture<()> + Send + Sync + 'static>; - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum SignalInfo { - HolePunch { - // UDP Hole Punch Request - receipt: Vec, // Receipt to be returned after the hole punch - peer_info: PeerInfo, // Sender's peer info - }, - ReverseConnect { - // Reverse Connection Request - receipt: Vec, // Receipt to be returned by the reverse connection - peer_info: PeerInfo, // Sender's peer info - }, - // XXX: WebRTC -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive( - Copy, - Clone, - Debug, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum TunnelMode { - Raw, - Turn, -} - -#[derive( - Copy, - Clone, - Debug, - PartialOrd, - PartialEq, - Eq, - Ord, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(u8), derive(CheckBytes))] -pub enum TunnelError { - BadId, // Tunnel ID was rejected - NoEndpoint, // Endpoint was unreachable - RejectedMode, // Endpoint couldn't provide mode - NoCapacity, // Endpoint is full -} - -pub type TunnelId = u64; - -#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct TunnelEndpoint { - pub mode: TunnelMode, - pub description: String, // XXX: TODO -} - -impl Default for TunnelEndpoint { - fn default() -> Self { - Self { - mode: TunnelMode::Raw, - description: "".to_string(), - } - } -} - -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct FullTunnel { - pub id: TunnelId, - pub timeout: u64, - pub local: TunnelEndpoint, - pub remote: TunnelEndpoint, -} - -#[derive( - Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes))] -pub struct PartialTunnel { - pub id: TunnelId, - pub timeout: u64, - pub local: TunnelEndpoint, -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// - -struct VeilidAPIInner { - context: Option, -} - -impl fmt::Debug for VeilidAPIInner { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "VeilidAPIInner") - } -} - -impl Drop for VeilidAPIInner { - fn drop(&mut self) { - if let Some(context) = self.context.take() { - intf::spawn_detached(api_shutdown(context)); - } - } -} - -#[derive(Clone, Debug)] -pub struct VeilidAPI { - inner: Arc>, -} - -impl VeilidAPI { - #[instrument(skip_all)] - pub(crate) fn new(context: VeilidCoreContext) -> Self { - Self { - inner: Arc::new(Mutex::new(VeilidAPIInner { - context: Some(context), - })), - } - } - - #[instrument(skip_all)] - pub async fn shutdown(self) { - let context = { self.inner.lock().context.take() }; - if let Some(context) = context { - api_shutdown(context).await; - } - } - - pub fn is_shutdown(&self) -> bool { - self.inner.lock().context.is_none() - } - - //////////////////////////////////////////////////////////////// - // Accessors - pub fn config(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.config.clone()); - } - Err(VeilidAPIError::NotInitialized) - } - pub fn crypto(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.crypto.clone()); - } - Err(VeilidAPIError::NotInitialized) - } - pub fn table_store(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.table_store.clone()); - } - Err(VeilidAPIError::not_initialized()) - } - pub fn block_store(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.block_store.clone()); - } - Err(VeilidAPIError::not_initialized()) - } - pub fn protected_store(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.protected_store.clone()); - } - Err(VeilidAPIError::not_initialized()) - } - pub fn attachment_manager(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.attachment_manager.clone()); - } - Err(VeilidAPIError::not_initialized()) - } - pub fn network_manager(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.attachment_manager.network_manager()); - } - Err(VeilidAPIError::not_initialized()) - } - pub fn rpc_processor(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.attachment_manager.network_manager().rpc_processor()); - } - Err(VeilidAPIError::NotInitialized) - } - pub fn routing_table(&self) -> Result { - let inner = self.inner.lock(); - if let Some(context) = &inner.context { - return Ok(context.attachment_manager.network_manager().routing_table()); - } - Err(VeilidAPIError::NotInitialized) - } - - //////////////////////////////////////////////////////////////// - // Attach/Detach - - // get a full copy of the current state - pub async fn get_state(&self) -> Result { - let attachment_manager = self.attachment_manager()?; - let network_manager = attachment_manager.network_manager(); - let config = self.config()?; - - let attachment = attachment_manager.get_veilid_state(); - let network = network_manager.get_veilid_state(); - let config = config.get_veilid_state(); - - Ok(VeilidState { - attachment, - network, - config, - }) - } - - // get network connectedness - - // connect to the network - #[instrument(level = "debug", err, skip_all)] - pub async fn attach(&self) -> Result<(), VeilidAPIError> { - let attachment_manager = self.attachment_manager()?; - attachment_manager - .request_attach() - .await - .map_err(|e| VeilidAPIError::internal(e)) - } - - // disconnect from the network - #[instrument(level = "debug", err, skip_all)] - pub async fn detach(&self) -> Result<(), VeilidAPIError> { - let attachment_manager = self.attachment_manager()?; - attachment_manager - .request_detach() - .await - .map_err(|e| VeilidAPIError::internal(e)) - } - - //////////////////////////////////////////////////////////////// - // Routing Context - - #[instrument(level = "debug", skip(self))] - pub fn routing_context(&self) -> RoutingContext { - RoutingContext::new(self.clone()) - } - - //////////////////////////////////////////////////////////////// - // Private route allocation - - #[instrument(level = "debug", skip(self))] - pub async fn new_private_route(&self) -> Result<(DHTKey, Vec), VeilidAPIError> { - self.new_custom_private_route(Stability::default(), Sequencing::default()) - .await - } - - #[instrument(level = "debug", skip(self))] - pub async fn new_custom_private_route( - &self, - stability: Stability, - sequencing: Sequencing, - ) -> Result<(DHTKey, Vec), VeilidAPIError> { - let default_route_hop_count: usize = { - let config = self.config()?; - let c = config.get(); - c.network.rpc.default_route_hop_count.into() - }; - - let rss = self.routing_table()?.route_spec_store(); - let r = rss - .allocate_route( - stability, - sequencing, - default_route_hop_count, - Direction::Inbound.into(), - &[], - ) - .map_err(VeilidAPIError::internal)?; - let Some(pr_pubkey) = r else { - apibail_generic!("unable to allocate route"); - }; - if !rss - .test_route(&pr_pubkey) - .await - .map_err(VeilidAPIError::no_connection)? - { - rss.release_route(&pr_pubkey); - apibail_generic!("allocated route failed to test"); - } - let private_route = rss - .assemble_private_route(&pr_pubkey, Some(true)) - .map_err(VeilidAPIError::generic)?; - let blob = match RouteSpecStore::private_route_to_blob(&private_route) { - Ok(v) => v, - Err(e) => { - rss.release_route(&pr_pubkey); - apibail_internal!(e); - } - }; - - rss.mark_route_published(&pr_pubkey, true) - .map_err(VeilidAPIError::internal)?; - - Ok((pr_pubkey, blob)) - } - - #[instrument(level = "debug", skip(self))] - pub fn import_remote_private_route(&self, blob: Vec) -> Result { - let rss = self.routing_table()?.route_spec_store(); - rss.import_remote_private_route(blob) - .map_err(|e| VeilidAPIError::invalid_argument(e, "blob", "private route blob")) - } - - #[instrument(level = "debug", skip(self))] - pub fn release_private_route(&self, key: &DHTKey) -> Result<(), VeilidAPIError> { - let rss = self.routing_table()?.route_spec_store(); - if rss.release_route(key) { - Ok(()) - } else { - Err(VeilidAPIError::invalid_argument( - "release_private_route", - "key", - key, - )) - } - } - - //////////////////////////////////////////////////////////////// - // App Calls - - #[instrument(level = "debug", skip(self))] - pub async fn app_call_reply(&self, id: u64, message: Vec) -> Result<(), VeilidAPIError> { - let rpc_processor = self.rpc_processor()?; - rpc_processor - .app_call_reply(id, message) - .await - .map_err(|e| e.into()) - } - - //////////////////////////////////////////////////////////////// - // Tunnel Building - - #[instrument(level = "debug", err, skip(self))] - pub async fn start_tunnel( - &self, - _endpoint_mode: TunnelMode, - _depth: u8, - ) -> Result { - panic!("unimplemented"); - } - - #[instrument(level = "debug", err, skip(self))] - pub async fn complete_tunnel( - &self, - _endpoint_mode: TunnelMode, - _depth: u8, - _partial_tunnel: PartialTunnel, - ) -> Result { - panic!("unimplemented"); - } - - #[instrument(level = "debug", err, skip(self))] - pub async fn cancel_tunnel(&self, _tunnel_id: TunnelId) -> Result { - panic!("unimplemented"); - } -} diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index 3af6eaae..4e712330 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -1,4 +1,5 @@ use super::*; + /////////////////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug)] @@ -106,7 +107,7 @@ impl RoutingContext { // Resolve node let mut nr = match rpc_processor.resolve_node(node_id.key).await { Ok(Some(nr)) => nr, - Ok(None) => return Err(VeilidAPIError::KeyNotFound { key: node_id.key }), + Ok(None) => apibail_key_not_found!(node_id.key), Err(e) => return Err(e.into()), }; // Apply sequencing to match safety selection @@ -123,7 +124,7 @@ impl RoutingContext { let Some(private_route) = rss .get_remote_private_route(&pr) else { - return Err(VeilidAPIError::KeyNotFound { key: pr }); + apibail_key_not_found!(pr); }; Ok(rpc_processor::Destination::PrivateRoute { @@ -151,15 +152,13 @@ impl RoutingContext { // Send app message let answer = match rpc_processor.rpc_call_app_call(dest, request).await { Ok(NetworkResult::Value(v)) => v, - Ok(NetworkResult::Timeout) => return Err(VeilidAPIError::Timeout), + Ok(NetworkResult::Timeout) => apibail_timeout!(), Ok(NetworkResult::NoConnection(e)) | Ok(NetworkResult::AlreadyExists(e)) => { - return Err(VeilidAPIError::NoConnection { - message: e.to_string(), - }) + apibail_no_connection!(e); } Ok(NetworkResult::InvalidMessage(message)) => { - return Err(VeilidAPIError::Generic { message }) + apibail_generic!(message); } Err(e) => return Err(e.into()), }; @@ -181,14 +180,12 @@ impl RoutingContext { // Send app message match rpc_processor.rpc_call_app_message(dest, message).await { Ok(NetworkResult::Value(())) => {} - Ok(NetworkResult::Timeout) => return Err(VeilidAPIError::Timeout), + Ok(NetworkResult::Timeout) => apibail_timeout!(), Ok(NetworkResult::NoConnection(e)) | Ok(NetworkResult::AlreadyExists(e)) => { - return Err(VeilidAPIError::NoConnection { - message: e.to_string(), - }) + apibail_no_connection!(e); } Ok(NetworkResult::InvalidMessage(message)) => { - return Err(VeilidAPIError::Generic { message }) + apibail_generic!(message); } Err(e) => return Err(e.into()), }; diff --git a/veilid-core/src/veilid_api/types.rs b/veilid-core/src/veilid_api/types.rs new file mode 100644 index 00000000..e8bb0e96 --- /dev/null +++ b/veilid-core/src/veilid_api/types.rs @@ -0,0 +1,2402 @@ +use super::*; + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Copy, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum VeilidLogLevel { + Error = 1, + Warn, + Info, + Debug, + Trace, +} + +impl VeilidLogLevel { + pub fn from_tracing_level(level: tracing::Level) -> VeilidLogLevel { + match level { + tracing::Level::ERROR => VeilidLogLevel::Error, + tracing::Level::WARN => VeilidLogLevel::Warn, + tracing::Level::INFO => VeilidLogLevel::Info, + tracing::Level::DEBUG => VeilidLogLevel::Debug, + tracing::Level::TRACE => VeilidLogLevel::Trace, + } + } + pub fn from_log_level(level: log::Level) -> VeilidLogLevel { + match level { + log::Level::Error => VeilidLogLevel::Error, + log::Level::Warn => VeilidLogLevel::Warn, + log::Level::Info => VeilidLogLevel::Info, + log::Level::Debug => VeilidLogLevel::Debug, + log::Level::Trace => VeilidLogLevel::Trace, + } + } + pub fn to_tracing_level(&self) -> tracing::Level { + match self { + Self::Error => tracing::Level::ERROR, + Self::Warn => tracing::Level::WARN, + Self::Info => tracing::Level::INFO, + Self::Debug => tracing::Level::DEBUG, + Self::Trace => tracing::Level::TRACE, + } + } + pub fn to_log_level(&self) -> log::Level { + match self { + Self::Error => log::Level::Error, + Self::Warn => log::Level::Warn, + Self::Info => log::Level::Info, + Self::Debug => log::Level::Debug, + Self::Trace => log::Level::Trace, + } + } +} + +impl fmt::Display for VeilidLogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let text = match self { + Self::Error => "ERROR", + Self::Warn => "WARN", + Self::Info => "INFO", + Self::Debug => "DEBUG", + Self::Trace => "TRACE", + }; + write!(f, "{}", text) + } +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidLog { + pub log_level: VeilidLogLevel, + pub message: String, + pub backtrace: Option, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidAppMessage { + /// Some(sender) if the message was sent directly, None if received via a private/safety route + #[serde(with = "opt_json_as_string")] + pub sender: Option, + /// The content of the message to deliver to the application + #[serde(with = "json_as_base64")] + pub message: Vec, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidAppCall { + /// Some(sender) if the request was sent directly, None if received via a private/safety route + #[serde(with = "opt_json_as_string")] + pub sender: Option, + /// The content of the request to deliver to the application + #[serde(with = "json_as_base64")] + pub message: Vec, + /// The id to reply to + #[serde(with = "json_as_string")] + pub id: u64, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidStateAttachment { + pub state: AttachmentState, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PeerTableData { + pub node_id: DHTKey, + pub peer_address: PeerAddress, + pub peer_stats: PeerStats, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidStateNetwork { + pub started: bool, + #[serde(with = "json_as_string")] + pub bps_down: u64, + #[serde(with = "json_as_string")] + pub bps_up: u64, + pub peers: Vec, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidStateRoute { + pub dead_routes: Vec, + pub dead_remote_routes: Vec, +} + +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidStateConfig { + pub config: VeilidConfigInner, +} + +#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] +#[serde(tag = "kind")] +pub enum VeilidUpdate { + Log(VeilidLog), + AppMessage(VeilidAppMessage), + AppCall(VeilidAppCall), + Attachment(VeilidStateAttachment), + Network(VeilidStateNetwork), + Config(VeilidStateConfig), + Route(VeilidStateRoute), + Shutdown, +} + +#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct VeilidState { + pub attachment: VeilidStateAttachment, + pub network: VeilidStateNetwork, + pub config: VeilidStateConfig, +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +/// +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct NodeId { + pub key: DHTKey, +} +impl NodeId { + pub fn new(key: DHTKey) -> Self { + Self { key } + } +} +impl fmt::Display for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.key.encode()) + } +} +impl FromStr for NodeId { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + Ok(Self { + key: DHTKey::try_decode(s)?, + }) + } +} + +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct ValueKey { + pub key: DHTKey, + pub subkey: Option, +} +impl ValueKey { + pub fn new(key: DHTKey) -> Self { + Self { key, subkey: None } + } + pub fn new_subkey(key: DHTKey, subkey: String) -> Self { + Self { + key, + subkey: if subkey.is_empty() { + None + } else { + Some(subkey) + }, + } + } +} + +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct ValueData { + pub data: Vec, + pub seq: u32, +} +impl ValueData { + pub fn new(data: Vec) -> Self { + Self { data, seq: 0 } + } + pub fn new_with_seq(data: Vec, seq: u32) -> Self { + Self { data, seq } + } + pub fn change(&mut self, data: Vec) { + self.data = data; + self.seq += 1; + } +} + +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct BlockId { + pub key: DHTKey, +} +impl BlockId { + pub fn new(key: DHTKey) -> Self { + Self { key } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +// Keep member order appropriate for sorting < preference +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum DialInfoClass { + Direct = 0, // D = Directly reachable with public IP and no firewall, with statically configured port + Mapped = 1, // M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port + FullConeNAT = 2, // F = Directly reachable device without portmap behind full-cone NAT + Blocked = 3, // B = Inbound blocked at firewall but may hole punch with public address + AddressRestrictedNAT = 4, // A = Device without portmap behind address-only restricted NAT + PortRestrictedNAT = 5, // P = Device without portmap behind address-and-port restricted NAT +} + +impl DialInfoClass { + // Is a signal required to do an inbound hole-punch? + pub fn requires_signal(&self) -> bool { + matches!( + self, + Self::Blocked | Self::AddressRestrictedNAT | Self::PortRestrictedNAT + ) + } + + // Does a relay node need to be allocated for this dial info? + // For full cone NAT, the relay itself may not be used but the keepalive sent to it + // is required to keep the NAT mapping valid in the router state table + pub fn requires_relay(&self) -> bool { + matches!( + self, + Self::FullConeNAT + | Self::Blocked + | Self::AddressRestrictedNAT + | Self::PortRestrictedNAT + ) + } +} + +// Ordering here matters, >= is used to check strength of sequencing requirement +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum Sequencing { + NoPreference, + PreferOrdered, + EnsureOrdered, +} + +impl Default for Sequencing { + fn default() -> Self { + Self::NoPreference + } +} + +// Ordering here matters, >= is used to check strength of stability requirement +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum Stability { + LowLatency, + Reliable, +} + +impl Default for Stability { + fn default() -> Self { + Self::LowLatency + } +} + +/// The choice of safety route to include in compiled routes +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum SafetySelection { + /// Don't use a safety route, only specify the sequencing preference + Unsafe(Sequencing), + /// Use a safety route and parameters specified by a SafetySpec + Safe(SafetySpec), +} + +/// Options for safety routes (sender privacy) +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct SafetySpec { + /// preferred safety route if it still exists + pub preferred_route: Option, + /// must be greater than 0 + pub hop_count: usize, + /// prefer reliability over speed + pub stability: Stability, + /// prefer connection-oriented sequenced protocols + pub sequencing: Sequencing, +} + +// Keep member order appropriate for sorting < preference +#[derive( + Debug, + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoDetail { + pub class: DialInfoClass, + pub dial_info: DialInfo, +} + +impl MatchesDialInfoFilter for DialInfoDetail { + fn matches_filter(&self, filter: &DialInfoFilter) -> bool { + self.dial_info.matches_filter(filter) + } +} + +impl DialInfoDetail { + pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering { + if a.class < b.class { + return core::cmp::Ordering::Less; + } + if a.class > b.class { + return core::cmp::Ordering::Greater; + } + DialInfo::ordered_sequencing_sort(&a.dial_info, &b.dial_info) + } + pub const NO_SORT: std::option::Option< + for<'r, 's> fn( + &'r veilid_api::DialInfoDetail, + &'s veilid_api::DialInfoDetail, + ) -> std::cmp::Ordering, + > = None:: core::cmp::Ordering>; +} + +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum NetworkClass { + InboundCapable = 0, // I = Inbound capable without relay, may require signal + OutboundOnly = 1, // O = Outbound only, inbound relay required except with reverse connect signal + WebApp = 2, // W = PWA, outbound relay is required in most cases + Invalid = 3, // X = Invalid network class, we don't know how to reach this node +} + +impl Default for NetworkClass { + fn default() -> Self { + Self::Invalid + } +} + +impl NetworkClass { + // Should an outbound relay be kept available? + pub fn outbound_wants_relay(&self) -> bool { + matches!(self, Self::WebApp) + } +} + +/// RoutingDomain-specific status for each node +/// is returned by the StatusA call + +/// PublicInternet RoutingDomain Status +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PublicInternetNodeStatus { + pub will_route: bool, + pub will_tunnel: bool, + pub will_signal: bool, + pub will_relay: bool, + pub will_validate_dial_info: bool, +} + +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct LocalNetworkNodeStatus { + pub will_relay: bool, + pub will_validate_dial_info: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum NodeStatus { + PublicInternet(PublicInternetNodeStatus), + LocalNetwork(LocalNetworkNodeStatus), +} + +impl NodeStatus { + pub fn will_route(&self) -> bool { + match self { + NodeStatus::PublicInternet(pi) => pi.will_route, + NodeStatus::LocalNetwork(_) => false, + } + } + pub fn will_tunnel(&self) -> bool { + match self { + NodeStatus::PublicInternet(pi) => pi.will_tunnel, + NodeStatus::LocalNetwork(_) => false, + } + } + pub fn will_signal(&self) -> bool { + match self { + NodeStatus::PublicInternet(pi) => pi.will_signal, + NodeStatus::LocalNetwork(_) => false, + } + } + pub fn will_relay(&self) -> bool { + match self { + NodeStatus::PublicInternet(pi) => pi.will_relay, + NodeStatus::LocalNetwork(ln) => ln.will_relay, + } + } + pub fn will_validate_dial_info(&self) -> bool { + match self { + NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info, + NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct NodeInfo { + pub network_class: NetworkClass, + #[with(RkyvEnumSet)] + pub outbound_protocols: ProtocolTypeSet, + #[with(RkyvEnumSet)] + pub address_types: AddressTypeSet, + pub min_version: u8, + pub max_version: u8, + pub dial_info_detail_list: Vec, +} + +impl NodeInfo { + pub fn first_filtered_dial_info_detail( + &self, + sort: Option, + filter: F, + ) -> Option + where + S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, + F: Fn(&DialInfoDetail) -> bool, + { + if let Some(sort) = sort { + let mut dids = self.dial_info_detail_list.clone(); + dids.sort_by(sort); + for did in dids { + if filter(&did) { + return Some(did); + } + } + } else { + for did in &self.dial_info_detail_list { + if filter(did) { + return Some(did.clone()); + } + } + }; + None + } + + pub fn all_filtered_dial_info_details( + &self, + sort: Option, + filter: F, + ) -> Vec + where + S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering, + F: Fn(&DialInfoDetail) -> bool, + { + let mut dial_info_detail_list = Vec::new(); + + if let Some(sort) = sort { + let mut dids = self.dial_info_detail_list.clone(); + dids.sort_by(sort); + for did in dids { + if filter(&did) { + dial_info_detail_list.push(did); + } + } + } else { + for did in &self.dial_info_detail_list { + if filter(did) { + dial_info_detail_list.push(did.clone()); + } + } + }; + dial_info_detail_list + } + + /// Does this node has some dial info + pub fn has_dial_info(&self) -> bool { + !self.dial_info_detail_list.is_empty() + } + + /// Is some relay required either for signal or inbound relay or outbound relay? + pub fn requires_relay(&self) -> bool { + match self.network_class { + NetworkClass::InboundCapable => { + for did in &self.dial_info_detail_list { + if did.class.requires_relay() { + return true; + } + } + } + NetworkClass::OutboundOnly => { + return true; + } + NetworkClass::WebApp => { + return true; + } + NetworkClass::Invalid => {} + } + false + } + + /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. + pub fn can_signal(&self) -> bool { + // Must be inbound capable + if !matches!(self.network_class, NetworkClass::InboundCapable) { + return false; + } + // Do any of our dial info require signalling? if so, we can't offer signalling + for did in &self.dial_info_detail_list { + if did.class.requires_signal() { + return false; + } + } + true + } + + /// Can this node relay be an inbound relay? + pub fn can_inbound_relay(&self) -> bool { + // For now this is the same + self.can_signal() + } + + /// Is this node capable of validating dial info + pub fn can_validate_dial_info(&self) -> bool { + // For now this is the same + self.can_signal() + } +} + +#[allow(clippy::derive_hash_xor_eq)] +#[derive( + Debug, + PartialOrd, + Ord, + Hash, + EnumSetType, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[enumset(repr = "u8")] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum Direction { + Inbound, + Outbound, +} +pub type DirectionSet = EnumSet; + +// Keep member order appropriate for sorting < preference +// Must match DialInfo order +#[allow(clippy::derive_hash_xor_eq)] +#[derive( + Debug, + PartialOrd, + Ord, + Hash, + EnumSetType, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[enumset(repr = "u8")] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum LowLevelProtocolType { + UDP, + TCP, +} + +impl LowLevelProtocolType { + pub fn is_connection_oriented(&self) -> bool { + matches!(self, LowLevelProtocolType::TCP) + } +} +pub type LowLevelProtocolTypeSet = EnumSet; + +// Keep member order appropriate for sorting < preference +// Must match DialInfo order +#[allow(clippy::derive_hash_xor_eq)] +#[derive( + Debug, + PartialOrd, + Ord, + Hash, + EnumSetType, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[enumset(repr = "u8")] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum ProtocolType { + UDP, + TCP, + WS, + WSS, +} + +impl ProtocolType { + pub fn is_connection_oriented(&self) -> bool { + matches!( + self, + ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS + ) + } + pub fn low_level_protocol_type(&self) -> LowLevelProtocolType { + match self { + ProtocolType::UDP => LowLevelProtocolType::UDP, + ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS => LowLevelProtocolType::TCP, + } + } + pub fn sort_order(&self, sequencing: Sequencing) -> usize { + match self { + ProtocolType::UDP => { + if sequencing != Sequencing::NoPreference { + 3 + } else { + 0 + } + } + ProtocolType::TCP => { + if sequencing != Sequencing::NoPreference { + 0 + } else { + 1 + } + } + ProtocolType::WS => { + if sequencing != Sequencing::NoPreference { + 1 + } else { + 2 + } + } + ProtocolType::WSS => { + if sequencing != Sequencing::NoPreference { + 2 + } else { + 3 + } + } + } + } + pub fn all_ordered_set() -> ProtocolTypeSet { + ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS + } +} + +pub type ProtocolTypeSet = EnumSet; + +#[allow(clippy::derive_hash_xor_eq)] +#[derive( + Debug, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, + EnumSetType, +)] +#[enumset(repr = "u8")] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum AddressType { + IPV4, + IPV6, +} +pub type AddressTypeSet = EnumSet; + +// Routing domain here is listed in order of preference, keep in order +#[allow(clippy::derive_hash_xor_eq)] +#[derive( + Debug, + Ord, + PartialOrd, + Hash, + EnumSetType, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[enumset(repr = "u8")] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum RoutingDomain { + LocalNetwork = 0, + PublicInternet = 1, +} +impl RoutingDomain { + pub const fn count() -> usize { + 2 + } + pub const fn all() -> [RoutingDomain; RoutingDomain::count()] { + // Routing domain here is listed in order of preference, keep in order + [RoutingDomain::LocalNetwork, RoutingDomain::PublicInternet] + } +} +pub type RoutingDomainSet = EnumSet; + +#[derive( + Copy, + Clone, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum Address { + IPV4(Ipv4Addr), + IPV6(Ipv6Addr), +} + +impl Default for Address { + fn default() -> Self { + Address::IPV4(Ipv4Addr::new(0, 0, 0, 0)) + } +} + +impl Address { + pub fn from_socket_addr(sa: SocketAddr) -> Address { + match sa { + SocketAddr::V4(v4) => Address::IPV4(*v4.ip()), + SocketAddr::V6(v6) => Address::IPV6(*v6.ip()), + } + } + pub fn from_ip_addr(addr: IpAddr) -> Address { + match addr { + IpAddr::V4(v4) => Address::IPV4(v4), + IpAddr::V6(v6) => Address::IPV6(v6), + } + } + pub fn address_type(&self) -> AddressType { + match self { + Address::IPV4(_) => AddressType::IPV4, + Address::IPV6(_) => AddressType::IPV6, + } + } + pub fn address_string(&self) -> String { + match self { + Address::IPV4(v4) => v4.to_string(), + Address::IPV6(v6) => v6.to_string(), + } + } + pub fn address_string_with_port(&self, port: u16) -> String { + match self { + Address::IPV4(v4) => format!("{}:{}", v4, port), + Address::IPV6(v6) => format!("[{}]:{}", v6, port), + } + } + pub fn is_unspecified(&self) -> bool { + match self { + Address::IPV4(v4) => ipv4addr_is_unspecified(v4), + Address::IPV6(v6) => ipv6addr_is_unspecified(v6), + } + } + pub fn is_global(&self) -> bool { + match self { + Address::IPV4(v4) => ipv4addr_is_global(v4) && !ipv4addr_is_multicast(v4), + Address::IPV6(v6) => ipv6addr_is_unicast_global(v6), + } + } + pub fn is_local(&self) -> bool { + match self { + Address::IPV4(v4) => { + ipv4addr_is_private(v4) + || ipv4addr_is_link_local(v4) + || ipv4addr_is_ietf_protocol_assignment(v4) + } + Address::IPV6(v6) => { + ipv6addr_is_unicast_site_local(v6) + || ipv6addr_is_unicast_link_local(v6) + || ipv6addr_is_unique_local(v6) + } + } + } + pub fn to_ip_addr(&self) -> IpAddr { + match self { + Self::IPV4(a) => IpAddr::V4(*a), + Self::IPV6(a) => IpAddr::V6(*a), + } + } + pub fn to_socket_addr(&self, port: u16) -> SocketAddr { + SocketAddr::new(self.to_ip_addr(), port) + } + pub fn to_canonical(&self) -> Address { + match self { + Address::IPV4(v4) => Address::IPV4(*v4), + Address::IPV6(v6) => match v6.to_ipv4() { + Some(v4) => Address::IPV4(v4), + None => Address::IPV6(*v6), + }, + } + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Address::IPV4(v4) => write!(f, "{}", v4), + Address::IPV6(v6) => write!(f, "{}", v6), + } + } +} + +impl FromStr for Address { + type Err = VeilidAPIError; + fn from_str(host: &str) -> Result { + if let Ok(addr) = Ipv4Addr::from_str(host) { + Ok(Address::IPV4(addr)) + } else if let Ok(addr) = Ipv6Addr::from_str(host) { + Ok(Address::IPV6(addr)) + } else { + Err(VeilidAPIError::parse_error( + "Address::from_str failed", + host, + )) + } + } +} + +#[derive( + Copy, + Default, + Clone, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct SocketAddress { + address: Address, + port: u16, +} + +impl SocketAddress { + pub fn new(address: Address, port: u16) -> Self { + Self { address, port } + } + pub fn from_socket_addr(sa: SocketAddr) -> SocketAddress { + Self { + address: Address::from_socket_addr(sa), + port: sa.port(), + } + } + pub fn address(&self) -> Address { + self.address + } + pub fn address_type(&self) -> AddressType { + self.address.address_type() + } + pub fn port(&self) -> u16 { + self.port + } + pub fn to_canonical(&self) -> SocketAddress { + SocketAddress { + address: self.address.to_canonical(), + port: self.port, + } + } + pub fn to_ip_addr(&self) -> IpAddr { + self.address.to_ip_addr() + } + pub fn to_socket_addr(&self) -> SocketAddr { + self.address.to_socket_addr(self.port) + } +} + +impl fmt::Display for SocketAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.to_socket_addr()) + } +} + +impl FromStr for SocketAddress { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let sa = SocketAddr::from_str(s) + .map_err(|e| VeilidAPIError::parse_error("Failed to parse SocketAddress", e))?; + Ok(SocketAddress::from_socket_addr(sa)) + } +} + +////////////////////////////////////////////////////////////////// + +#[derive( + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoFilter { + #[with(RkyvEnumSet)] + pub protocol_type_set: ProtocolTypeSet, + #[with(RkyvEnumSet)] + pub address_type_set: AddressTypeSet, +} + +impl Default for DialInfoFilter { + fn default() -> Self { + Self { + protocol_type_set: ProtocolTypeSet::all(), + address_type_set: AddressTypeSet::all(), + } + } +} + +impl DialInfoFilter { + pub fn all() -> Self { + Self { + protocol_type_set: ProtocolTypeSet::all(), + address_type_set: AddressTypeSet::all(), + } + } + pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self { + self.protocol_type_set = ProtocolTypeSet::only(protocol_type); + self + } + pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self { + self.protocol_type_set = protocol_set; + self + } + pub fn with_address_type(mut self, address_type: AddressType) -> Self { + self.address_type_set = AddressTypeSet::only(address_type); + self + } + pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self { + self.address_type_set = address_set; + self + } + pub fn filtered(mut self, other_dif: &DialInfoFilter) -> Self { + self.protocol_type_set &= other_dif.protocol_type_set; + self.address_type_set &= other_dif.address_type_set; + self + } + pub fn is_dead(&self) -> bool { + self.protocol_type_set.is_empty() || self.address_type_set.is_empty() + } +} + +impl fmt::Debug for DialInfoFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let mut out = String::new(); + if self.protocol_type_set != ProtocolTypeSet::all() { + out += &format!("+{:?}", self.protocol_type_set); + } else { + out += "*"; + } + if self.address_type_set != AddressTypeSet::all() { + out += &format!("+{:?}", self.address_type_set); + } else { + out += "*"; + } + write!(f, "[{}]", out) + } +} + +pub trait MatchesDialInfoFilter { + fn matches_filter(&self, filter: &DialInfoFilter) -> bool; +} + +#[derive( + Clone, + Default, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoUDP { + pub socket_address: SocketAddress, +} + +#[derive( + Clone, + Default, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoTCP { + pub socket_address: SocketAddress, +} + +#[derive( + Clone, + Default, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoWS { + pub socket_address: SocketAddress, + pub request: String, +} + +#[derive( + Clone, + Default, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct DialInfoWSS { + pub socket_address: SocketAddress, + pub request: String, +} + +// Keep member order appropriate for sorting < preference +// Must match ProtocolType order +#[derive( + Clone, + Debug, + PartialEq, + PartialOrd, + Ord, + Eq, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +#[serde(tag = "kind")] +pub enum DialInfo { + UDP(DialInfoUDP), + TCP(DialInfoTCP), + WS(DialInfoWS), + WSS(DialInfoWSS), +} +impl Default for DialInfo { + fn default() -> Self { + DialInfo::UDP(DialInfoUDP::default()) + } +} + +impl fmt::Display for DialInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address), + DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address), + DialInfo::WS(di) => { + let url = format!("ws://{}", di.request); + let split_url = SplitUrl::from_str(&url).unwrap(); + match split_url.host { + SplitUrlHost::Hostname(_) => { + write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request) + } + SplitUrlHost::IpAddr(a) => { + if di.socket_address.to_ip_addr() == a { + write!(f, "ws|{}", di.request) + } else { + panic!("resolved address does not match url: {}", di.request); + } + } + } + } + DialInfo::WSS(di) => { + let url = format!("wss://{}", di.request); + let split_url = SplitUrl::from_str(&url).unwrap(); + match split_url.host { + SplitUrlHost::Hostname(_) => { + write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request) + } + SplitUrlHost::IpAddr(_) => { + panic!( + "secure websockets can not use ip address in request: {}", + di.request + ); + } + } + } + } + } +} + +impl FromStr for DialInfo { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let (proto, rest) = s.split_once('|').ok_or_else(|| { + VeilidAPIError::parse_error("DialInfo::from_str missing protocol '|' separator", s) + })?; + match proto { + "udp" => { + let socket_address = SocketAddress::from_str(rest)?; + Ok(DialInfo::udp(socket_address)) + } + "tcp" => { + let socket_address = SocketAddress::from_str(rest)?; + Ok(DialInfo::tcp(socket_address)) + } + "ws" => { + let url = format!("ws://{}", rest); + let split_url = SplitUrl::from_str(&url).map_err(|e| { + VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) + })?; + if split_url.scheme != "ws" || !url.starts_with("ws://") { + apibail_parse_error!("incorrect scheme for WS dialinfo", url); + } + let url_port = split_url.port.unwrap_or(80u16); + + match rest.split_once('|') { + Some((sa, rest)) => { + let address = Address::from_str(sa)?; + + DialInfo::try_ws( + SocketAddress::new(address, url_port), + format!("ws://{}", rest), + ) + } + None => { + let address = Address::from_str(&split_url.host.to_string())?; + DialInfo::try_ws( + SocketAddress::new(address, url_port), + format!("ws://{}", rest), + ) + } + } + } + "wss" => { + let url = format!("wss://{}", rest); + let split_url = SplitUrl::from_str(&url).map_err(|e| { + VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) + })?; + if split_url.scheme != "wss" || !url.starts_with("wss://") { + apibail_parse_error!("incorrect scheme for WSS dialinfo", url); + } + let url_port = split_url.port.unwrap_or(443u16); + + let (a, rest) = rest.split_once('|').ok_or_else(|| { + VeilidAPIError::parse_error( + "DialInfo::from_str missing socket address '|' separator", + s, + ) + })?; + + let address = Address::from_str(a)?; + DialInfo::try_wss( + SocketAddress::new(address, url_port), + format!("wss://{}", rest), + ) + } + _ => Err(VeilidAPIError::parse_error( + "DialInfo::from_str has invalid scheme", + s, + )), + } + } +} + +impl DialInfo { + pub fn udp_from_socketaddr(socket_addr: SocketAddr) -> Self { + Self::UDP(DialInfoUDP { + socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), + }) + } + pub fn tcp_from_socketaddr(socket_addr: SocketAddr) -> Self { + Self::TCP(DialInfoTCP { + socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), + }) + } + pub fn udp(socket_address: SocketAddress) -> Self { + Self::UDP(DialInfoUDP { + socket_address: socket_address.to_canonical(), + }) + } + pub fn tcp(socket_address: SocketAddress) -> Self { + Self::TCP(DialInfoTCP { + socket_address: socket_address.to_canonical(), + }) + } + pub fn try_ws(socket_address: SocketAddress, url: String) -> Result { + let split_url = SplitUrl::from_str(&url).map_err(|e| { + VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) + })?; + if split_url.scheme != "ws" || !url.starts_with("ws://") { + apibail_parse_error!("incorrect scheme for WS dialinfo", url); + } + let url_port = split_url.port.unwrap_or(80u16); + if url_port != socket_address.port() { + apibail_parse_error!("socket address port doesn't match url port", url); + } + if let SplitUrlHost::IpAddr(a) = split_url.host { + if socket_address.to_ip_addr() != a { + apibail_parse_error!( + format!("request address does not match socket address: {}", a), + socket_address + ); + } + } + Ok(Self::WS(DialInfoWS { + socket_address: socket_address.to_canonical(), + request: url[5..].to_string(), + })) + } + pub fn try_wss(socket_address: SocketAddress, url: String) -> Result { + let split_url = SplitUrl::from_str(&url).map_err(|e| { + VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) + })?; + if split_url.scheme != "wss" || !url.starts_with("wss://") { + apibail_parse_error!("incorrect scheme for WSS dialinfo", url); + } + let url_port = split_url.port.unwrap_or(443u16); + if url_port != socket_address.port() { + apibail_parse_error!("socket address port doesn't match url port", url); + } + if !matches!(split_url.host, SplitUrlHost::Hostname(_)) { + apibail_parse_error!( + "WSS url can not use address format, only hostname format", + url + ); + } + Ok(Self::WSS(DialInfoWSS { + socket_address: socket_address.to_canonical(), + request: url[6..].to_string(), + })) + } + pub fn protocol_type(&self) -> ProtocolType { + match self { + Self::UDP(_) => ProtocolType::UDP, + Self::TCP(_) => ProtocolType::TCP, + Self::WS(_) => ProtocolType::WS, + Self::WSS(_) => ProtocolType::WSS, + } + } + pub fn address_type(&self) -> AddressType { + self.socket_address().address_type() + } + pub fn address(&self) -> Address { + match self { + Self::UDP(di) => di.socket_address.address, + Self::TCP(di) => di.socket_address.address, + Self::WS(di) => di.socket_address.address, + Self::WSS(di) => di.socket_address.address, + } + } + pub fn socket_address(&self) -> SocketAddress { + match self { + Self::UDP(di) => di.socket_address, + Self::TCP(di) => di.socket_address, + Self::WS(di) => di.socket_address, + Self::WSS(di) => di.socket_address, + } + } + pub fn to_ip_addr(&self) -> IpAddr { + match self { + Self::UDP(di) => di.socket_address.to_ip_addr(), + Self::TCP(di) => di.socket_address.to_ip_addr(), + Self::WS(di) => di.socket_address.to_ip_addr(), + Self::WSS(di) => di.socket_address.to_ip_addr(), + } + } + pub fn port(&self) -> u16 { + match self { + Self::UDP(di) => di.socket_address.port, + Self::TCP(di) => di.socket_address.port, + Self::WS(di) => di.socket_address.port, + Self::WSS(di) => di.socket_address.port, + } + } + pub fn set_port(&mut self, port: u16) { + match self { + Self::UDP(di) => di.socket_address.port = port, + Self::TCP(di) => di.socket_address.port = port, + Self::WS(di) => di.socket_address.port = port, + Self::WSS(di) => di.socket_address.port = port, + } + } + pub fn to_socket_addr(&self) -> SocketAddr { + match self { + Self::UDP(di) => di.socket_address.to_socket_addr(), + Self::TCP(di) => di.socket_address.to_socket_addr(), + Self::WS(di) => di.socket_address.to_socket_addr(), + Self::WSS(di) => di.socket_address.to_socket_addr(), + } + } + pub fn to_peer_address(&self) -> PeerAddress { + match self { + Self::UDP(di) => PeerAddress::new(di.socket_address, ProtocolType::UDP), + Self::TCP(di) => PeerAddress::new(di.socket_address, ProtocolType::TCP), + Self::WS(di) => PeerAddress::new(di.socket_address, ProtocolType::WS), + Self::WSS(di) => PeerAddress::new(di.socket_address, ProtocolType::WSS), + } + } + pub fn request(&self) -> Option { + match self { + Self::UDP(_) => None, + Self::TCP(_) => None, + Self::WS(di) => Some(format!("ws://{}", di.request)), + Self::WSS(di) => Some(format!("wss://{}", di.request)), + } + } + pub fn is_valid(&self) -> bool { + let socket_address = self.socket_address(); + let address = socket_address.address(); + let port = socket_address.port(); + (address.is_global() || address.is_local()) && port > 0 + } + + pub fn make_filter(&self) -> DialInfoFilter { + DialInfoFilter { + protocol_type_set: ProtocolTypeSet::only(self.protocol_type()), + address_type_set: AddressTypeSet::only(self.address_type()), + } + } + + pub fn try_vec_from_short, H: AsRef>( + short: S, + hostname: H, + ) -> Result, VeilidAPIError> { + let short = short.as_ref(); + let hostname = hostname.as_ref(); + + if short.len() < 2 { + apibail_parse_error!("invalid short url length", short); + } + let url = match &short[0..1] { + "U" => { + format!("udp://{}:{}", hostname, &short[1..]) + } + "T" => { + format!("tcp://{}:{}", hostname, &short[1..]) + } + "W" => { + format!("ws://{}:{}", hostname, &short[1..]) + } + "S" => { + format!("wss://{}:{}", hostname, &short[1..]) + } + _ => { + apibail_parse_error!("invalid short url type", short); + } + }; + Self::try_vec_from_url(url) + } + + pub fn try_vec_from_url>(url: S) -> Result, VeilidAPIError> { + let url = url.as_ref(); + let split_url = SplitUrl::from_str(url) + .map_err(|e| VeilidAPIError::parse_error(format!("unable to split url: {}", e), url))?; + + let port = match split_url.scheme.as_str() { + "udp" | "tcp" => split_url + .port + .ok_or_else(|| VeilidAPIError::parse_error("Missing port in udp url", url))?, + "ws" => split_url.port.unwrap_or(80u16), + "wss" => split_url.port.unwrap_or(443u16), + _ => { + apibail_parse_error!("Invalid dial info url scheme", split_url.scheme); + } + }; + + let socket_addrs = { + // Resolve if possible, WASM doesn't support resolution and doesn't need it to connect to the dialinfo + // This will not be used on signed dialinfo, only for bootstrapping, so we don't need to worry about + // the '0.0.0.0' address being propagated across the routing table + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0,0,0,0)), port)] + } else { + match split_url.host { + SplitUrlHost::Hostname(_) => split_url + .host_port(port) + .to_socket_addrs() + .map_err(|_| VeilidAPIError::parse_error("couldn't resolve hostname in url", url))? + .collect(), + SplitUrlHost::IpAddr(a) => vec![SocketAddr::new(a, port)], + } + } + } + }; + + let mut out = Vec::new(); + for sa in socket_addrs { + out.push(match split_url.scheme.as_str() { + "udp" => Self::udp_from_socketaddr(sa), + "tcp" => Self::tcp_from_socketaddr(sa), + "ws" => Self::try_ws( + SocketAddress::from_socket_addr(sa).to_canonical(), + url.to_string(), + )?, + "wss" => Self::try_wss( + SocketAddress::from_socket_addr(sa).to_canonical(), + url.to_string(), + )?, + _ => { + unreachable!("Invalid dial info url scheme") + } + }); + } + Ok(out) + } + + pub async fn to_short(&self) -> (String, String) { + match self { + DialInfo::UDP(di) => ( + format!("U{}", di.socket_address.port()), + intf::ptr_lookup(di.socket_address.to_ip_addr()) + .await + .unwrap_or_else(|_| di.socket_address.to_string()), + ), + DialInfo::TCP(di) => ( + format!("T{}", di.socket_address.port()), + intf::ptr_lookup(di.socket_address.to_ip_addr()) + .await + .unwrap_or_else(|_| di.socket_address.to_string()), + ), + DialInfo::WS(di) => { + let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap(); + if let SplitUrlHost::IpAddr(a) = split_url.host { + if let Ok(host) = intf::ptr_lookup(a).await { + split_url.host = SplitUrlHost::Hostname(host); + } + } + ( + format!( + "W{}{}", + split_url.port.unwrap_or(80), + split_url + .path + .map(|p| format!("/{}", p)) + .unwrap_or_default() + ), + split_url.host.to_string(), + ) + } + DialInfo::WSS(di) => { + let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap(); + if let SplitUrlHost::IpAddr(a) = split_url.host { + if let Ok(host) = intf::ptr_lookup(a).await { + split_url.host = SplitUrlHost::Hostname(host); + } + } + ( + format!( + "S{}{}", + split_url.port.unwrap_or(443), + split_url + .path + .map(|p| format!("/{}", p)) + .unwrap_or_default() + ), + split_url.host.to_string(), + ) + } + } + } + pub async fn to_url(&self) -> String { + match self { + DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) + .await + .map(|h| format!("udp://{}:{}", h, di.socket_address.port())) + .unwrap_or_else(|_| format!("udp://{}", di.socket_address)), + DialInfo::TCP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) + .await + .map(|h| format!("tcp://{}:{}", h, di.socket_address.port())) + .unwrap_or_else(|_| format!("tcp://{}", di.socket_address)), + DialInfo::WS(di) => { + let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap(); + if let SplitUrlHost::IpAddr(a) = split_url.host { + if let Ok(host) = intf::ptr_lookup(a).await { + split_url.host = SplitUrlHost::Hostname(host); + } + } + split_url.to_string() + } + DialInfo::WSS(di) => { + let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap(); + if let SplitUrlHost::IpAddr(a) = split_url.host { + if let Ok(host) = intf::ptr_lookup(a).await { + split_url.host = SplitUrlHost::Hostname(host); + } + } + split_url.to_string() + } + } + } + + pub fn ordered_sequencing_sort(a: &DialInfo, b: &DialInfo) -> core::cmp::Ordering { + let ca = a.protocol_type().sort_order(Sequencing::EnsureOrdered); + let cb = b.protocol_type().sort_order(Sequencing::EnsureOrdered); + if ca < cb { + return core::cmp::Ordering::Less; + } + if ca > cb { + return core::cmp::Ordering::Greater; + } + match (a, b) { + (DialInfo::UDP(a), DialInfo::UDP(b)) => a.cmp(b), + (DialInfo::TCP(a), DialInfo::TCP(b)) => a.cmp(b), + (DialInfo::WS(a), DialInfo::WS(b)) => a.cmp(b), + (DialInfo::WSS(a), DialInfo::WSS(b)) => a.cmp(b), + _ => unreachable!(), + } + } +} + +impl MatchesDialInfoFilter for DialInfo { + fn matches_filter(&self, filter: &DialInfoFilter) -> bool { + if !filter.protocol_type_set.contains(self.protocol_type()) { + return false; + } + if !filter.address_type_set.contains(self.address_type()) { + return false; + } + true + } +} + +////////////////////////////////////////////////////////////////////////// + +// Signed NodeInfo that can be passed around amongst peers and verifiable +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct SignedDirectNodeInfo { + pub node_info: NodeInfo, + pub timestamp: u64, + pub signature: Option, +} + +impl SignedDirectNodeInfo { + pub fn new( + node_id: NodeId, + node_info: NodeInfo, + timestamp: u64, + signature: DHTSignature, + ) -> Result { + let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; + verify(&node_id.key, &node_info_bytes, &signature)?; + Ok(Self { + node_info, + timestamp, + signature: Some(signature), + }) + } + + pub fn with_secret( + node_id: NodeId, + node_info: NodeInfo, + secret: &DHTKeySecret, + ) -> Result { + let timestamp = intf::get_timestamp(); + let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; + let signature = sign(&node_id.key, secret, &node_info_bytes)?; + Ok(Self { + node_info, + timestamp, + signature: Some(signature), + }) + } + + fn make_signature_bytes( + node_info: &NodeInfo, + timestamp: u64, + ) -> Result, VeilidAPIError> { + let mut node_info_bytes = Vec::new(); + + // Add nodeinfo to signature + let mut ni_msg = ::capnp::message::Builder::new_default(); + let mut ni_builder = ni_msg.init_root::(); + encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?; + node_info_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?); + + // Add timestamp to signature + node_info_bytes.append(&mut timestamp.to_le_bytes().to_vec()); + + Ok(node_info_bytes) + } + + pub fn with_no_signature(node_info: NodeInfo) -> Self { + Self { + node_info, + signature: None, + timestamp: intf::get_timestamp(), + } + } + + pub fn has_valid_signature(&self) -> bool { + self.signature.is_some() + } +} + +/// Signed NodeInfo with a relay that can be passed around amongst peers and verifiable +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct SignedRelayedNodeInfo { + pub node_info: NodeInfo, + pub relay_id: NodeId, + pub relay_info: SignedDirectNodeInfo, + pub timestamp: u64, + pub signature: DHTSignature, +} + +impl SignedRelayedNodeInfo { + pub fn new( + node_id: NodeId, + node_info: NodeInfo, + relay_id: NodeId, + relay_info: SignedDirectNodeInfo, + timestamp: u64, + signature: DHTSignature, + ) -> Result { + let node_info_bytes = + Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; + verify(&node_id.key, &node_info_bytes, &signature)?; + Ok(Self { + node_info, + relay_id, + relay_info, + signature, + timestamp, + }) + } + + pub fn with_secret( + node_id: NodeId, + node_info: NodeInfo, + relay_id: NodeId, + relay_info: SignedDirectNodeInfo, + secret: &DHTKeySecret, + ) -> Result { + let timestamp = intf::get_timestamp(); + let node_info_bytes = + Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; + let signature = sign(&node_id.key, secret, &node_info_bytes)?; + Ok(Self { + node_info, + relay_id, + relay_info, + signature, + timestamp, + }) + } + + fn make_signature_bytes( + node_info: &NodeInfo, + relay_id: &NodeId, + relay_info: &SignedDirectNodeInfo, + timestamp: u64, + ) -> Result, VeilidAPIError> { + let mut sig_bytes = Vec::new(); + + // Add nodeinfo to signature + let mut ni_msg = ::capnp::message::Builder::new_default(); + let mut ni_builder = ni_msg.init_root::(); + encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?; + sig_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?); + + // Add relay id to signature + let mut rid_msg = ::capnp::message::Builder::new_default(); + let mut rid_builder = rid_msg.init_root::(); + encode_dht_key(&relay_id.key, &mut rid_builder).map_err(VeilidAPIError::internal)?; + sig_bytes.append(&mut builder_to_vec(rid_msg).map_err(VeilidAPIError::internal)?); + + // Add relay info to signature + let mut ri_msg = ::capnp::message::Builder::new_default(); + let mut ri_builder = ri_msg.init_root::(); + encode_signed_direct_node_info(relay_info, &mut ri_builder) + .map_err(VeilidAPIError::internal)?; + sig_bytes.append(&mut builder_to_vec(ri_msg).map_err(VeilidAPIError::internal)?); + + // Add timestamp to signature + sig_bytes.append(&mut timestamp.to_le_bytes().to_vec()); + + Ok(sig_bytes) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum SignedNodeInfo { + Direct(SignedDirectNodeInfo), + Relayed(SignedRelayedNodeInfo), +} + +impl SignedNodeInfo { + pub fn has_valid_signature(&self) -> bool { + match self { + SignedNodeInfo::Direct(d) => d.has_valid_signature(), + SignedNodeInfo::Relayed(_) => true, + } + } + + pub fn timestamp(&self) -> u64 { + match self { + SignedNodeInfo::Direct(d) => d.timestamp, + SignedNodeInfo::Relayed(r) => r.timestamp, + } + } + + pub fn node_info(&self) -> &NodeInfo { + match self { + SignedNodeInfo::Direct(d) => &d.node_info, + SignedNodeInfo::Relayed(r) => &r.node_info, + } + } + pub fn relay_id(&self) -> Option { + match self { + SignedNodeInfo::Direct(_) => None, + SignedNodeInfo::Relayed(r) => Some(r.relay_id.clone()), + } + } + pub fn relay_info(&self) -> Option<&NodeInfo> { + match self { + SignedNodeInfo::Direct(_) => None, + SignedNodeInfo::Relayed(r) => Some(&r.relay_info.node_info), + } + } + pub fn relay_peer_info(&self) -> Option { + match self { + SignedNodeInfo::Direct(_) => None, + SignedNodeInfo::Relayed(r) => Some(PeerInfo::new( + r.relay_id.clone(), + SignedNodeInfo::Direct(r.relay_info.clone()), + )), + } + } + pub fn has_any_dial_info(&self) -> bool { + self.node_info().has_dial_info() + || self + .relay_info() + .map(|relay_ni| relay_ni.has_dial_info()) + .unwrap_or_default() + } + + pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { + // Check our dial info + for did in &self.node_info().dial_info_detail_list { + match sequencing { + Sequencing::NoPreference | Sequencing::PreferOrdered => return true, + Sequencing::EnsureOrdered => { + if did.dial_info.protocol_type().is_connection_oriented() { + return true; + } + } + } + } + // Check our relay if we have one + return self + .relay_info() + .map(|relay_ni| { + for did in &relay_ni.dial_info_detail_list { + match sequencing { + Sequencing::NoPreference | Sequencing::PreferOrdered => return true, + Sequencing::EnsureOrdered => { + if did.dial_info.protocol_type().is_connection_oriented() { + return true; + } + } + } + } + false + }) + .unwrap_or_default(); + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PeerInfo { + pub node_id: NodeId, + pub signed_node_info: SignedNodeInfo, +} + +impl PeerInfo { + pub fn new(node_id: NodeId, signed_node_info: SignedNodeInfo) -> Self { + Self { + node_id, + signed_node_info, + } + } +} + +#[derive( + Copy, + Clone, + Debug, + PartialEq, + PartialOrd, + Eq, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PeerAddress { + protocol_type: ProtocolType, + #[serde(with = "json_as_string")] + socket_address: SocketAddress, +} + +impl PeerAddress { + pub fn new(socket_address: SocketAddress, protocol_type: ProtocolType) -> Self { + Self { + socket_address: socket_address.to_canonical(), + protocol_type, + } + } + + pub fn socket_address(&self) -> &SocketAddress { + &self.socket_address + } + + pub fn protocol_type(&self) -> ProtocolType { + self.protocol_type + } + + pub fn to_socket_addr(&self) -> SocketAddr { + self.socket_address.to_socket_addr() + } + + pub fn address_type(&self) -> AddressType { + self.socket_address.address_type() + } +} + +/// Represents the 5-tuple of an established connection +/// Not used to specify connections to create, that is reserved for DialInfo +/// +/// ConnectionDescriptors should never be from unspecified local addresses for connection oriented protocols +/// If the medium does not allow local addresses, None should have been used or 'new_no_local' +/// If we are specifying only a port, then the socket's 'local_address()' should have been used, since an +/// established connection is always from a real address to another real address. +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct ConnectionDescriptor { + remote: PeerAddress, + local: Option, +} + +impl ConnectionDescriptor { + pub fn new(remote: PeerAddress, local: SocketAddress) -> Self { + assert!( + !remote.protocol_type().is_connection_oriented() || !local.address().is_unspecified() + ); + + Self { + remote, + local: Some(local), + } + } + pub fn new_no_local(remote: PeerAddress) -> Self { + Self { + remote, + local: None, + } + } + pub fn remote(&self) -> PeerAddress { + self.remote + } + pub fn remote_address(&self) -> &SocketAddress { + self.remote.socket_address() + } + pub fn local(&self) -> Option { + self.local + } + pub fn protocol_type(&self) -> ProtocolType { + self.remote.protocol_type + } + pub fn address_type(&self) -> AddressType { + self.remote.address_type() + } + pub fn make_dial_info_filter(&self) -> DialInfoFilter { + DialInfoFilter::all() + .with_protocol_type(self.protocol_type()) + .with_address_type(self.address_type()) + } +} + +impl MatchesDialInfoFilter for ConnectionDescriptor { + fn matches_filter(&self, filter: &DialInfoFilter) -> bool { + if !filter.protocol_type_set.contains(self.protocol_type()) { + return false; + } + if !filter.address_type_set.contains(self.address_type()) { + return false; + } + true + } +} + +////////////////////////////////////////////////////////////////////////// + +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct LatencyStats { + #[serde(with = "json_as_string")] + pub fastest: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies + #[serde(with = "json_as_string")] + pub average: u64, // average latency over the ROLLING_LATENCIES_SIZE last latencies + #[serde(with = "json_as_string")] + pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies +} + +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct TransferStats { + #[serde(with = "json_as_string")] + pub total: u64, // total amount transferred ever + #[serde(with = "json_as_string")] + pub maximum: u64, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts + #[serde(with = "json_as_string")] + pub average: u64, // average rate over the ROLLING_TRANSFERS_SIZE last amounts + #[serde(with = "json_as_string")] + pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts +} + +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct TransferStatsDownUp { + pub down: TransferStats, + pub up: TransferStats, +} + +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct RPCStats { + pub messages_sent: u32, // number of rpcs that have been sent in the total_time range + pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range + pub questions_in_flight: u32, // number of questions issued that have yet to be answered + #[serde(with = "opt_json_as_string")] + pub last_question: Option, // when the peer was last questioned (either successfully or not) and we wanted an answer + #[serde(with = "opt_json_as_string")] + pub last_seen_ts: Option, // when the peer was last seen for any reason, including when we first attempted to reach out to it + #[serde(with = "opt_json_as_string")] + pub first_consecutive_seen_ts: Option, // the timestamp of the first consecutive proof-of-life for this node (an answer or received question) + pub recent_lost_answers: u32, // number of answers that have been lost since we lost reliability + pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one +} + +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PeerStats { + #[serde(with = "json_as_string")] + pub time_added: u64, // when the peer was added to the routing table + pub rpc_stats: RPCStats, // information about RPCs + pub latency: Option, // latencies for communications with the peer + pub transfer: TransferStatsDownUp, // Stats for communications with the peer +} + +pub type ValueChangeCallback = + Arc) -> SendPinBoxFuture<()> + Send + Sync + 'static>; + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum SignalInfo { + HolePunch { + // UDP Hole Punch Request + receipt: Vec, // Receipt to be returned after the hole punch + peer_info: PeerInfo, // Sender's peer info + }, + ReverseConnect { + // Reverse Connection Request + receipt: Vec, // Receipt to be returned by the reverse connection + peer_info: PeerInfo, // Sender's peer info + }, + // XXX: WebRTC +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +#[derive( + Copy, + Clone, + Debug, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum TunnelMode { + Raw, + Turn, +} + +#[derive( + Copy, + Clone, + Debug, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] +pub enum TunnelError { + BadId, // Tunnel ID was rejected + NoEndpoint, // Endpoint was unreachable + RejectedMode, // Endpoint couldn't provide mode + NoCapacity, // Endpoint is full +} + +pub type TunnelId = u64; + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct TunnelEndpoint { + pub mode: TunnelMode, + pub description: String, // XXX: TODO +} + +impl Default for TunnelEndpoint { + fn default() -> Self { + Self { + mode: TunnelMode::Raw, + description: "".to_string(), + } + } +} + +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct FullTunnel { + pub id: TunnelId, + pub timeout: u64, + pub local: TunnelEndpoint, + pub remote: TunnelEndpoint, +} + +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct PartialTunnel { + pub id: TunnelId, + pub timeout: u64, + pub local: TunnelEndpoint, +}