commit de029dd6e763e55b0f2b828ba626b0adecc61e0f Author: asxalex Date: Thu Jul 4 16:50:28 2024 +0800 sdlan diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54b02da --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*.so diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b330340 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2416 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "etherparse" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21696e6dfe1057a166a042c6d27b89a46aad2ee1003e6e1e03c49d54fd3270d7" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.2", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.68", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.68", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rolling-file" +version = "0.1.0" +source = "git+https://git.asxalex.pw/rust/rolling-file#cc5f7e57bc84d09a1c69f3f3e302d8024299b1f2" +dependencies = [ + "anyhow", + "chrono", + "time", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdlan-rs" +version = "0.1.0" +dependencies = [ + "dashmap 6.0.1", + "etherparse", + "futures-util", + "num_enum", + "once_cell", + "prost", + "prost-build", + "rsa", + "sdlan-sn-rs", + "structopt", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "sdlan-sn-rs" +version = "0.1.0" +source = "git+ssh://git@git.asxalex.pw/sdlan-v2/sdlan-rs.git#f320526f2dc1f6c3154b9ade81c556af0d068378" +dependencies = [ + "aes", + "byteorder", + "cbc", + "dashmap 5.5.3", + "futures", + "once_cell", + "prost", + "rand", + "rolling-file", + "rsa", + "serde", + "serde_json", + "serde_repr", + "sqlx", + "tokio", + "tracing", + "tracing-appender", + "uuid", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +dependencies = [ + "atoi", + "base64", + "bitflags 2.6.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +dependencies = [ + "atoi", + "base64", + "bitflags 2.6.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "time", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.68", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ef2d909 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sdlan-rs" +version = "0.1.0" +edition = "2021" + +[dependencies] +dashmap = "6.0.1" +etherparse = "0.15.0" +futures-util = "0.3.30" +num_enum = "0.7.2" +once_cell = "1.19.0" +prost = "0.12.6" +prost-build = "0.12.6" +rsa = "0.9.6" +sdlan-sn-rs = { git = "ssh://git@git.asxalex.pw/sdlan-v2/sdlan-rs.git" } +structopt = "0.3.26" +tokio = { version = "1.38.0", futures = ["full"] } +tokio-util = "0.7.11" +tracing = "0.1.40" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e8562c --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +pb: + cargo run --bin build_pb + mv src/pb/_.rs src/pb/message.rs + +libtun-so: + cd src/network && gcc -fPIC -shared -o libtuntap.so tuntap.c && cd - \ No newline at end of file diff --git a/message.proto b/message.proto new file mode 100644 index 0000000..7d2b9bb --- /dev/null +++ b/message.proto @@ -0,0 +1,156 @@ +syntax = "proto3"; + +// 基础公共类型定义 + +message SDLV4Info { + uint32 port = 1; + bytes v4 = 2; + uint32 nat_type = 3; +} + +message SDLV6Info { + uint32 port = 1; + bytes v6 = 2; +} + +// 设备网络地址信息 +message SDLDevAddr { + uint32 network_id = 1; + uint32 net_addr = 2; + uint32 net_bit_len = 3; +} + +// tcp通讯消息 +message SDLEmpty { + +} + +message SDLRegisterSuper { + uint32 version = 1; + string installed_channel = 2; + string client_id = 3; + SDLDevAddr dev_addr = 4; + string pub_key = 5; + string token = 6; +} + +message SDLRegisterSuperAck { + SDLDevAddr dev_addr = 1; + bytes aes_key = 2; + bytes known_ips = 3; + uint32 upgrade_type = 4; + optional string upgrade_prompt = 5; + optional string upgrade_address = 6; +} + +message SDLRegisterSuperNak { + uint32 error_code = 1; + string error_message = 2; +} + +// 网络地址查询 + +message SDLQueryInfo { + uint32 dst_ip = 1; +} + +message SDLPeerInfo { + uint32 dst_ip = 1; + SDLV4Info v4_info = 2; + optional SDLV6Info v6_info = 3; +} + +// 事件定义 + +message SDLKnownIpEvent { + uint32 ip = 1; +} + +message SDLDropIpEvent { + uint32 ip = 1; +} + +message SDLNatChangedEvent { + uint32 ip = 1; +} + +message SDLSendRegisterEvent { + uint32 dst_ip = 1; + uint32 nat_ip = 2; + uint32 nat_port = 3; +} + +message SDLNetworkShutdownEvent { + string message = 1; +} + +// 命令定义 + +message SDLChangeNetworkCommand { + SDLDevAddr dev_addr = 1; + bytes aes_key = 2; + bytes known_ips = 3; +} + +message SDLCommandAck { + // status = true, 表示成功;status = false 表示失败,message是失败原因描述 + bool status = 1; + optional string message = 2; +} + +message SDLFlows { + // 服务器转发流量 + uint32 forward_num = 1; + // p2p直接流量 + uint32 p2p_num = 2; + // 接收的流量 + uint32 inbound_num = 3; +} + +// UDP通讯消息 + +message SDLStunRequest { + uint32 cookie = 1; + string client_id = 2; + uint32 network_id = 3; + uint32 ip = 4; + uint32 nat_type = 5; +} + +message SDLStunReply { + uint32 cookie = 1; +} + +message SDLData { + uint32 network_id = 1; + uint32 src_ip = 2; + uint32 dst_ip = 3; + bool is_p2p = 4; + uint32 ttl = 5; + bytes data = 6; +} + +message SDLRegister { + uint32 network_id = 1; + uint32 src_ip = 2; + uint32 dst_ip = 3; +} + +message SDLRegisterAck { + uint32 network_id = 1; + uint32 src_ip = 2; + uint32 dst_ip = 3; +} + +// 网络类型探测 + +message SDLStunProbe { + uint32 cookie = 1; + uint32 attr = 2; +} + +message SDLStunProbeReply { + uint32 cookie = 1; + uint32 port = 2; + uint32 ip = 3; +} \ No newline at end of file diff --git a/src/bin/build_pb/main.rs b/src/bin/build_pb/main.rs new file mode 100644 index 0000000..af26abd --- /dev/null +++ b/src/bin/build_pb/main.rs @@ -0,0 +1,8 @@ +fn main() { + prost_build::Config::new() + .out_dir("src/pb") + // .out_dir("../tcp_mock/pb") + .protoc_arg("--experimental_allow_proto3_optional") + .compile_protos(&["message.proto"], &["../sdlan/"]) + .unwrap(); +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..8e90d01 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,9 @@ +pub const REGISTER_INTERVAL: u8 = 20; +pub const REGISTER_SUPER_INTERVAL: u16 = 20; + +pub const MULITCAST_V4: [u8; 4] = [224, 0, 0, 69]; +pub const MULTICAST_PORT: u16 = 1969; + +// pub const SUPER_ATTEMPTS_DEFAULT: u8 = 3; + +pub const TCP_PING_TIME: u64 = 7; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7e1948c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +mod config; +mod network; +mod pb; +mod tcp; +mod utils; diff --git a/src/network/async_main.rs b/src/network/async_main.rs new file mode 100644 index 0000000..e33a19a --- /dev/null +++ b/src/network/async_main.rs @@ -0,0 +1,495 @@ +use std::net::SocketAddr; +use std::sync::atomic::Ordering; +use std::time::Duration; + +use crate::config::TCP_PING_TIME; +use crate::network::{get_edge, ping_to_sn, read_and_parse_packet}; +use crate::pb::{ + encode_to_tcp_message, encode_to_udp_message, SdlData, SdlDevAddr, SdlRegisterSuper, + SdlRegisterSuperAck, SdlRegisterSuperNak, SdlSendRegisterEvent, SdlStunRequest, +}; +use crate::tcp::{init_tcp_conn, EventType, PacketType, SdlanTcp}; +use crate::utils::{send_to_sock, CommandLine}; +use etherparse::IpHeaders; +use sdlan_sn_rs::config::{AF_INET, SDLAN_DEFAULT_TTL}; +use sdlan_sn_rs::peer::SdlanSock; +use sdlan_sn_rs::utils::{ + self, aes_encrypt, create_or_load_uuid, get_current_timestamp, ip_to_string, + is_multi_broadcast, rsa_decrypt, +}; +use sdlan_sn_rs::utils::{Result, SDLanError}; +use tokio::io::AsyncWriteExt; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio_util::sync::CancellationToken; + +use super::{check_peer_registration_needed, init_edge, packet, Node, NodeConfig}; +use crate::utils::Socket; + +use prost::Message; +use tracing::{debug, error, info}; + +async fn handle_tcp_message(msg: SdlanTcp) { + let edge = get_edge(); + + // let now = get_current_timestamp(); + // edge.tcp_pong.store(now, Ordering::Relaxed); + + debug!("got tcp message: {:?}", msg.packet_type); + match msg.packet_type { + PacketType::RegisterSuperACK => { + let Ok(ack) = SdlRegisterSuperAck::decode(&msg.current_packet[..]) else { + error!("failed to decode REGISTER_SUPER_ACK"); + return; + }; + debug!("got register super ack: {:?}", ack); + let Ok(aes) = rsa_decrypt(&edge.rsa_private, &ack.aes_key) else { + error!("failed to rsa decrypt aes key"); + return; + }; + let Some(dev) = ack.dev_addr else { + error!("no dev_addr is specified"); + return; + }; + + let ip = ip_to_string(&dev.net_addr); + debug!("aes key is {:?}, ip is {}/{}", aes, ip, dev.net_bit_len,); + edge.device_config + .ip + .net_addr + .store(dev.net_addr, Ordering::Relaxed); + edge.device_config + .ip + .net_bit_len + .store(dev.net_bit_len as u8, Ordering::Relaxed); + edge.device.reload_config(&edge.device_config); + edge.network_id.store(dev.network_id, Ordering::Relaxed); + + edge.set_authorized(true, aes); + } + PacketType::RegisterSuperNAK => { + let Ok(_nak) = SdlRegisterSuperNak::decode(&msg.current_packet[..]) else { + error!("failed to decode REGISTER_SUPER_NAK"); + return; + }; + edge.set_authorized(false, Vec::new()); + // std::process::exit(0); + } + PacketType::Command => { + if msg.current_packet.len() < 1 { + error!("malformed COMMAND received"); + return; + } + handle_tcp_command(edge, msg.current_packet[0], &msg.current_packet[1..]).await; + } + PacketType::Event => { + if msg.current_packet.len() < 1 { + error!("malformed EVENT received"); + return; + } + let Ok(event) = msg.current_packet[0].try_into() else { + error!("failed to parse event type"); + return; + }; + handle_tcp_event(edge, event, &msg.current_packet[1..]).await; + } + PacketType::PeerInfo => { + let _ = packet::handle_packet_peer_info(edge, &msg.current_packet[..]).await; + } + PacketType::Pong => { + debug!("tcp pong received"); + let now = get_current_timestamp(); + edge.tcp_pong.store(now, Ordering::Relaxed); + } + other => { + debug!("tcp not handling {:?}", other); + } + } +} + +async fn handle_tcp_command(_edge: &Node, _cmdtype: u8, _cmdprotobuf: &[u8]) {} + +async fn handle_tcp_event(edge: &Node, eventtype: EventType, eventprotobuf: &[u8]) { + match eventtype { + EventType::SendRegister => { + let Ok(reg) = SdlSendRegisterEvent::decode(eventprotobuf) else { + error!("failed to decode SendRegister Event"); + return; + }; + let v4 = reg.nat_ip.to_be_bytes(); + check_peer_registration_needed( + edge, + true, + reg.dst_ip, + &None, + &SdlanSock { + family: AF_INET, + port: reg.nat_port as u16, + v4, + v6: [0; 16], + }, + ) + .await; + } + other => { + debug!("unhandled event {:?}", other); + } + } +} + +pub async fn async_main( + install_channel: String, + args: CommandLine, + start_stop_chan: Receiver, + cancel: CancellationToken, +) -> Result<()> { + // let _ = PidRecorder::new(".pid"); + + // // gen public key + // gen_rsa_keys(".client"); + // let mut pubkey = String::new(); + // File::open(".client/id_rsa.pub")?.read_to_string(&mut pubkey)?; + // let privatekey = load_private_key_file(".client/id_rsa")?; + + // // init sock + // if args.token.len() == 0 { + // println!("failed to load token"); + // return Ok(()); + // } + // let sock_v4 = Socket::build(0, true, true, args.tos).await?; + + // // allow multicast + + // // TODO: set the sn's tcp socket + // // let tcpsock = TCPSocket::build("121.4.79.234:1234").await?; + // let tcp_pong = Arc::new(AtomicU64::new(0)); + // let edge = Node::new( + // pubkey, + // node_conf, + // sock_v4, + // &args.token, + // privatekey, + // tcp_pong.clone(), + // ); + + let edge = get_edge(); + + // let token = args.token.clone(); + init_tcp_conn( + &args.tcp, + move |stream| { + let token = args.token.clone(); + let installed_channel = install_channel.to_owned(); + Box::pin(async { + // let edge = get_edge(); + // let edge = get_edge(); + // let token = args.token.clone(); + let register_super = SdlRegisterSuper { + version: 1, + installed_channel, + client_id: edge.config.node_uuid.clone(), + dev_addr: Some(SdlDevAddr { + net_addr: 0, + network_id: 0, + net_bit_len: 0, + }), + pub_key: edge.rsa_pubkey.clone(), + token, + }; + let packet_id = edge.get_next_packet_id(); + let data = encode_to_tcp_message( + Some(register_super), + packet_id, + PacketType::RegisterSuper as u8, + ) + .unwrap(); + if let Err(e) = stream.write(&data).await { + error!("failed to write to tcp: {}", e.to_string()); + } + }) + }, + |msg| handle_tcp_message(msg), + edge.tcp_pong.clone(), + // tcp_pong, + start_stop_chan, + ); + + // tcp_conn.send("hello".as_bytes()).await; + // tokio::spawn(handle_tcp_message(tcp_conn.data_from_tcp)); + + // tcp_conn.send("".as_bytes()); + + debug!("waiting for authorization..."); + + loop { + // let _ = edge.send_register_super().await; + // let _ = read_and_parse_packet(edge, &edge.udp_sock_v4, Some(Duration::from_secs(3))).await; + println!("checking for authorized"); + if edge.is_authorized() { + break; + } + tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(3)) => { + continue; + } + _ = cancel.cancelled() => { + return Ok(()); + } + } + } + + { + let cancel = cancel.clone(); + tokio::spawn(async move { + run_edge_loop(edge, cancel).await; + }); + } + + { + let cancel = cancel.clone(); + tokio::spawn(async move { + loop { + tokio::select! { + _ = cancel.cancelled() => { + if let Err(e) = edge.send_unregister_super().await { + error!("failed to send unregister super: {}", e.as_str()); + } + break; + } + _ = tokio::time::sleep(Duration::from_secs(TCP_PING_TIME)) => { + ping_to_sn().await; + } + } + } + debug!("loop update_supernode_reg exited"); + }); + } + + cancel.cancelled().await; + /* + match tokio::signal::ctrl_c().await { + Ok(()) => { + debug!("shutdown received"); + cancel.cancel(); + debug!("exiting async_main"); + tokio::time::sleep(Duration::from_millis(500)).await; + debug!("exiting async_main2"); + } + Err(err) => { + eprintln!("failed to listen for shutdown signal: {}", err); + } + } + */ + + // std::process::exit(0); + Ok(()) +} + +async fn run_edge_loop(eee: &'static Node, cancel: CancellationToken) { + ping_to_sn().await; + { + let cancel = cancel.clone(); + tokio::spawn(async move { + loop_socket_v4(eee, &eee.udp_sock_v4, cancel).await; + }); + } + + { + tokio::spawn(async move { + loop_tap(eee, cancel).await; + }); + } +} + +async fn loop_socket_v4(eee: &Node, socket: &Socket, cancel: CancellationToken) { + debug!("loop sock v4"); + loop { + tokio::select! { + _ = cancel.cancelled() => { + break; + } + _ = read_and_parse_packet(eee, socket,Some(Duration::from_secs(10))) => { } + _ = tokio::time::sleep(Duration::from_secs(10)) => { + let req = SdlStunRequest { + cookie: 0, + client_id: eee.config.node_uuid.clone(), + network_id: eee.network_id.load(Ordering::Relaxed), + ip: eee.device_config.get_ip(), + nat_type: 0, + }; + let msg = encode_to_udp_message(Some(req), PacketType::StunRequest as u8).unwrap(); + if let Err(e) = send_to_sock(eee, &msg, &eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize]).await { + error!("failed to send to sock: {:?}", e); + } + } + } + } + debug!("loop_socket_v4 exited"); +} + +async fn loop_tap(eee: &'static Node, cancel: CancellationToken) { + debug!("loop tap"); + let (tx, mut rx) = channel(10); + tokio::spawn(async { + get_tun_flow(eee, tx).await; + }); + + loop { + tokio::select! { + _ = cancel.cancelled() => { + drop(rx); + break; + } + buf = rx.recv() => { + if buf.is_none() { + break; + } + read_and_parse_tun_packet(eee, buf.unwrap()).await; + } + } + } + debug!("loop_tap exited"); +} + +async fn get_tun_flow(eee: &'static Node, tx: Sender>) { + loop { + let buf = tokio::task::spawn_blocking(|| { + let mut buf = vec![0; 1800]; + let Ok(size) = eee.device.recv(&mut buf) else { + return vec![]; + }; + buf.truncate(size); + buf + }) + .await + .unwrap(); + + if buf.len() == 0 { + return; + } + if let Err(e) = tx.send(buf).await { + error!("failed to send buf: {}", e); + return; + } + } +} + +async fn read_and_parse_tun_packet(eee: &'static Node, buf: Vec) { + if !eee.is_authorized() { + debug!("drop packet before authorized"); + return; + } + /* + if eee.stats.last_sup.load(Ordering::Relaxed) == 0 { + debug!("drop packet before first registration"); + return; + } + */ + // buf.truncate(size); + + edge_send_packet_to_net(eee, &buf).await; +} + +async fn edge_send_packet_to_net(eee: &Node, data: &[u8]) { + debug!("edge send packet to net({} bytes): {:?}", data.len(), data); + match IpHeaders::from_slice(&data) { + Ok((iphdr, _payload)) => { + let Some(ipv4hdr) = iphdr.ipv4() else { + debug!("ipv6 packet ignored"); + return; + }; + let dstip = u32::from_be_bytes(ipv4hdr.0.destination); + debug!("packet dst ip: {:?}", ipv4hdr.0.destination); + let src = u32::from_be_bytes(ipv4hdr.0.source); + debug!("packet src ip: {:?}", ipv4hdr.0.source); + // packet should be sent to dev + debug!("got {} bytes from tun", data.len()); + if (!eee.config.allow_routing) && (src != eee.device_config.get_ip()) { + info!("dropping routed packet"); + return; + } + if !eee.is_authorized() { + debug!("drop tun packet due to not authed"); + return; + } + let encrypt_key = eee.get_encrypt_key(); + if encrypt_key.len() == 0 { + error!("drop tun packet due to encrypt key len is 0"); + return; + } + let Ok(encrypted_flow) = aes_encrypt(encrypt_key.as_slice(), data) else { + error!("failed to encrypt flow"); + return; + }; + + let message = SdlData { + // TODO: network id should be stored in + network_id: eee.network_id.load(Ordering::Relaxed), + src_ip: eee.device_config.get_ip(), + dst_ip: dstip, + is_p2p: true, + ttl: SDLAN_DEFAULT_TTL as u32, + data: encrypted_flow, + }; + debug!("sending SdlData: {:?}", message); + let Ok(flow) = encode_to_udp_message(Some(message), PacketType::Data as u8) else { + error!("failed to encode to udp message"); + return; + }; + send_packet_to_net(eee, dstip, &flow).await; + } + Err(e) => { + error!("failed to parse ip packet: {}", e.to_string()); + } + } +} + +async fn send_packet_to_net(eee: &Node, dst_ip: u32, pkt: &[u8]) { + let (dest_sock, is_p2p) = find_peer_destination(eee, dst_ip).await; + if is_p2p { + eee.stats.tx_p2p.fetch_add(1, Ordering::Relaxed); + } else { + eee.stats.tx_sup.fetch_add(1, Ordering::Relaxed); + if is_multi_broadcast(dst_ip) { + eee.stats.tx_broadcast.fetch_add(1, Ordering::Relaxed); + } + } + debug!("send packet PACKET to {}", dest_sock.to_string()); + if let Err(e) = send_to_sock(eee, pkt, &dest_sock).await { + error!("failed to send packet to net: {}", e.as_str()); + } +} + +async fn find_peer_destination(eee: &Node, dst_ip: u32) -> (SdlanSock, bool) { + if is_multi_broadcast(dst_ip) { + return ( + eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize] + .deepcopy(), + false, + ); + } + let mut is_p2p = false; + let result: SdlanSock; + if let Some(dst) = eee.known_peers.get_peer(&dst_ip) { + let now = get_current_timestamp(); + if now - dst.last_seen.load(Ordering::Relaxed) >= ((dst.timeout / 2) as u64) { + // too much time elapsed since we saw the peer, need to register again + eee.known_peers.delete_peer_with_ip(&dst_ip); + result = eee.config.super_nodes + [eee.config.super_node_index.load(Ordering::Relaxed) as usize] + .deepcopy(); + } else { + // dst.last_seen.store(now, Ordering::Relaxed); + is_p2p = true; + result = dst.sock.read().unwrap().deepcopy(); + } + } else { + result = eee.config.super_nodes + [eee.config.super_node_index.load(Ordering::Relaxed) as usize] + .deepcopy(); + } + if !is_p2p { + debug!("check_query_peer_info"); + super::packet::check_query_peer_info(eee, dst_ip).await; + } + return (result, is_p2p); +} diff --git a/src/network/device.rs b/src/network/device.rs new file mode 100644 index 0000000..2b95b2a --- /dev/null +++ b/src/network/device.rs @@ -0,0 +1,56 @@ +use sdlan_sn_rs::peer::IpSubnet; + +pub struct DeviceConfig { + pub ip: IpSubnet, +} + +impl DeviceConfig { + pub fn new() -> Self { + DeviceConfig { + ip: IpSubnet::new(0, 0), + } + } + + /* + pub fn set_ip(&self, net_addr: u32, net_bit_len: u8) { + if net_bit_len <= 8 || net_bit_len > 32 { + error!("configured net bit length error: {}", net_bit_len); + return; + } + self.ip.net_addr.store(net_addr, Ordering::Relaxed); + self.ip.net_bit_len.store(net_bit_len, Ordering::Relaxed); + } + */ + + pub fn get_ip(&self) -> u32 { + self.ip.net_addr() + } + pub fn get_net_bit(&self) -> u8 { + self.ip.net_bit_len() + } +} + +/// The mode in which open the virtual network adapter. +#[allow(unused)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum Mode { + /// TUN mode + /// + /// The packets returned are on the IP layer (layer 3), prefixed with 4-byte header (2 bytes + /// are flags, 2 bytes are the protocol inside, eg one of + /// . + Tun = 1, + /// TAP mode + /// + /// The packets are on the transport layer (layer 2), and start with ethernet frame header. + Tap = 2, +} + +/* +pub trait TunDevice: ReadWriter { + fn name(&self) -> &str; + fn mode(&self) -> &Mode; + fn get_ip(&self) -> u32; + fn get_net_bit(&self) -> u8; +} +*/ diff --git a/src/network/mod.rs b/src/network/mod.rs new file mode 100644 index 0000000..0e1f62b --- /dev/null +++ b/src/network/mod.rs @@ -0,0 +1,21 @@ +mod node; +pub use node::*; + +mod async_main; +pub use async_main::*; + +mod packet; +pub use packet::*; + +#[cfg_attr(target_os = "linux", path = "tun_linux.rs")] +#[cfg_attr(target_os = "windows", path = "tun_win.rs")] +mod tun; + +mod device; + +/* +pub trait ReadWriter { + fn send(&self, content: &[u8]) -> std::io::Result; + fn recv(&self, buf: &mut [u8]) -> std::io::Result; +} +*/ diff --git a/src/network/node.rs b/src/network/node.rs new file mode 100644 index 0000000..dec278a --- /dev/null +++ b/src/network/node.rs @@ -0,0 +1,480 @@ +use dashmap::DashMap; +use rsa::RsaPrivateKey; +use sdlan_sn_rs::config::{AF_INET, AF_INET6}; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering}; +use std::sync::{Arc, RwLock}; +use tokio::io::AsyncReadExt; + +use crate::pb::{encode_to_tcp_message, SdlEmpty}; +use crate::tcp::{get_tcp_conn, PacketType}; +use crate::utils::{PidRecorder, Socket}; + +use sdlan_sn_rs::peer::{is_sdlan_sock_equal, IpSubnet, V6Info}; + +use once_cell::sync::OnceCell; + +use super::device::{DeviceConfig, Mode}; +use super::tun::{new_iface, Iface}; +use tokio::fs::File; + +use sdlan_sn_rs::utils::{create_or_load_uuid, gen_rsa_keys, load_private_key_file}; +use sdlan_sn_rs::utils::{Result, SDLanError}; + +static EDGE: OnceCell = OnceCell::new(); + +pub async fn init_edge(token: &str, node_conf: NodeConfig, tos: u32) -> Result<()> { + if token.len() == 0 { + println!("failed to load token"); + return Err(SDLanError::NormalError("no token is specified")); + } + + let _ = PidRecorder::new(".pid"); + + // gen public key + gen_rsa_keys(".client"); + let mut pubkey = String::new(); + + File::open(".client/id_rsa.pub") + .await? + .read_to_string(&mut pubkey) + .await?; + let privatekey = load_private_key_file(".client/id_rsa")?; + + // init sock + // let edge_uuid = create_or_load_uuid("")?; + //let node_conf = parse_config(edge_uuid, &args).await?; + + let sock_v4 = Socket::build(0, true, true, tos).await?; + + // allow multicast + + // TODO: set the sn's tcp socket + // let tcpsock = TCPSocket::build("121.4.79.234:1234").await?; + let tcp_pong = Arc::new(AtomicU64::new(0)); + let edge = Node::new( + pubkey, + node_conf, + sock_v4, + token, + privatekey, + tcp_pong.clone(), + ); + do_init_edge(edge)?; + + Ok(()) +} + +fn do_init_edge(edge: Node) -> Result<()> { + if let Err(_) = EDGE.set(edge) { + return Err(SDLanError::NormalError("initialize sn error")); + } + Ok(()) +} + +pub fn get_edge() -> &'static Node { + let edge = EDGE.get(); + if edge.is_none() { + panic!("should init_edge first"); + } + edge.unwrap() +} + +pub struct Node { + packet_id: AtomicU32, + + pub network_id: AtomicU32, + + pub tcp_pong: Arc, + + // user token info + pub _token: String, + + pub device_config: DeviceConfig, + pub device: Iface, + + // authorize related + pub authorized: AtomicBool, + // pub header_key: RwLock>>, + pub encrypt_key: RwLock>>, + + pub rsa_pubkey: String, + pub rsa_private: RsaPrivateKey, + + pub config: NodeConfig, + // pub super_node: Vec, + // pub super_attempts: AtomicU8, + + // store pending, and known peers + pub pending_peers: PeerMap, + pub known_peers: PeerMap, + + // pub tcp_sock_v4: TCPSocket, + pub udp_sock_v4: Socket, + pub udp_sock_v6: RwLock>>, + + pub multicast_sock: SdlanSock, + + pub _local_v6: RwLock>, + + pub stats: NodeStats, + + // last register super time, in unix + pub _last_register_req: AtomicU64, +} + +unsafe impl Sync for Node {} + +impl Node { + pub fn new( + pubkey: String, + config: NodeConfig, + sock: Socket, + // tcpsock: TCPSocket, + token: &str, + private: RsaPrivateKey, + tcp_pong: Arc, + ) -> Self { + Self { + packet_id: AtomicU32::new(1), + network_id: AtomicU32::new(0), + _token: token.to_owned(), + + tcp_pong, + + device_config: DeviceConfig::new(), + device: new_iface("dev", Mode::Tun), + + authorized: AtomicBool::new(false), + encrypt_key: RwLock::new(Arc::new(Vec::new())), + // rsa_pubkey: + rsa_pubkey: pubkey, + rsa_private: private, + config, + // super_node: Vec::new(), + // super_attempts: AtomicU8::new(0), + pending_peers: PeerMap::new(), + known_peers: PeerMap::new(), + + // tcp_sock_v4: tcpsock, + udp_sock_v4: sock, + udp_sock_v6: RwLock::new(Arc::new(None)), + multicast_sock: SdlanSock { + family: AF_INET, + port: config::MULTICAST_PORT, + v4: config::MULITCAST_V4, + v6: [0; 16], + }, + _local_v6: RwLock::new(None), + stats: NodeStats::new(), + _last_register_req: AtomicU64::new(0), + } + } + + pub fn get_next_packet_id(&self) -> u32 { + self.packet_id.fetch_add(1, Ordering::Relaxed) + } + + pub fn is_authorized(&self) -> bool { + // self.header_key + self.authorized.load(Ordering::Relaxed) + } + + pub fn set_authorized(&self, authorized: bool, encrypt_key: Vec) { + self.authorized.store(authorized, Ordering::Relaxed); + *(self.encrypt_key.write().unwrap()) = Arc::new(encrypt_key); + } + + /* + pub fn get_header_key(&self) -> Arc> { + // self.header_key.read().unwrap().clone() + } + */ + + pub fn get_encrypt_key(&self) -> Arc> { + self.encrypt_key.read().unwrap().clone() + } + + /* + pub fn sn_is_known(&self, sock: &SdlanSock) -> bool { + for sn in self.config.super_nodes.iter() { + if sn.family != sock.family || sn.port != sock.port { + continue; + } + if sn.family == AF_INET && sn.v4 == sock.v4 { + return true; + } + if sn.family == AF_INET6 && sn.v6 == sock.v6 { + return true; + } + } + return false; + } + */ + + pub fn _remove_v6(&self) { + *(self.udp_sock_v6.write().unwrap()) = Arc::new(None); + } + + /* + pub async fn send_to_v4(&self, info: &[u8], target: A) -> Result { + match self.udp_sock_v4.send_to(info, target).await { + Ok(n) => Ok(n), + Err(e) => { + println!("failed to send"); + Err(SDLanError::NormalError("failed to send")) + } + } + } + */ + + /* + pub async fn send_to_v6(&self, info: &[u8], target: A) -> Result { + let m = self.udp_sock_v6.read().unwrap().clone(); + if let Some(ref l) = m.as_ref() { + match l.send_to(info, target).await { + Err(e) => { + return Err(SDLanError::NormalError("send error")); + } + Ok(n) => return Ok(n), + } + } + + Err(SDLanError::NormalError("no udp6 conn is bined")) + } + */ + + pub async fn send_unregister_super(&self) -> Result<()> { + let content = + encode_to_tcp_message::(None, 0, PacketType::UnRegisterSuper as u8).unwrap(); + + let conn = get_tcp_conn(); + let _ = conn.send(&content).await; + + Ok(()) + } + + /* + pub async fn send_register_super(&self) -> Result<()> { + let packet_id = self.packet_id.fetch_add(1, Ordering::Relaxed); + let cmn = Common { + packet_id, + version: SDLAN_VERSION, + id: &self.config.node_uuid, + token: self.token, + ttl: SDLAN_DEFAULT_TTL, + pc: PacketType::PKTRegisterSuper, + flags: 0, + }; + let rs = RegisterSuper { + pass: "encrypt!", + cookie: 0, + sock: None, + v6_info: None, + dev_addr: IpSubnetNonAtomic::new( + self.device_config.get_ip(), + self.device_config.get_net_bit(), + ), + pub_key: self.rsa_pubkey.clone(), + }; + let content = packet::encode_packet(&cmn, &rs)?; + + // self.udp_sock_v4.send_to(&content, self.config.super_nodes) + send_to_sock( + &self, + &content, + &self.config.super_nodes[self.config.super_node_index.load(Ordering::Relaxed) as usize], + ) + .await?; + // write_to_addr(&sock, "127.0.0.1:7655", &content).await?; + // println!("sent!"); + Ok(()) + } + */ +} + +pub struct PeerMap { + pub peers: DashMap>, +} + +#[allow(unused)] +impl PeerMap { + pub fn new() -> PeerMap { + Self { + peers: DashMap::new(), + } + } + + pub fn get_peer(&self, ip: &u32) -> Option> { + if let Some(v) = self.peers.get(ip) { + Some(v.clone()) + } else { + None + } + } + + pub fn clear(&self) { + self.peers.clear(); + } + + pub fn get_peer_by_sock(&self, sock: &SdlanSock) -> Option> { + for s in self.peers.iter() { + let m = s.sock.read().unwrap(); + if is_sdlan_sock_equal(&m, sock) { + return Some(s.value().clone()); + } + } + None + } + + pub fn delete_peer_with_ip(&self, ip: &u32) { + self.peers.remove(ip); + } + + pub fn insert_peer(&self, p: Arc) { + let net_addr = p.dev_addr.net_addr(); + if net_addr != 0 { + self.peers.insert(net_addr, p); + } + } +} + +pub struct NodeStats { + pub tx_p2p: AtomicU64, + pub rx_p2p: AtomicU64, + pub tx_sup: AtomicU64, + pub rx_sup: AtomicU64, + pub tx_broadcast: AtomicU64, + pub rx_broadcast: AtomicU64, + + pub last_sup: AtomicU64, + pub last_p2p: AtomicU64, +} + +impl NodeStats { + pub fn new() -> Self { + Self { + tx_p2p: AtomicU64::new(0), + rx_p2p: AtomicU64::new(0), + tx_sup: AtomicU64::new(0), + rx_sup: AtomicU64::new(0), + tx_broadcast: AtomicU64::new(0), + rx_broadcast: AtomicU64::new(0), + + last_p2p: AtomicU64::new(0), + last_sup: AtomicU64::new(0), + } + } +} + +use sdlan_sn_rs::peer::SdlanSock; + +use crate::config::{self, REGISTER_INTERVAL}; +pub struct NodeConfig { + // node name + pub name: String, + // 允许路由 + pub allow_routing: bool, + + // 丢弃多播,广播消息 + pub _drop_multicast: bool, + + // 允许p2p打洞 + pub allow_p2p: bool, + + // mtu of the tun + pub mtu: u32, + + // udp消息的服务类型 + pub _tos: u8, + + // 打洞时候,时间间隔 + pub _register_super_interval: u16, + + // 打洞时候,register消息的ttl + pub register_ttl: u8, + + // 本地打开的udp端口 + pub _local_port: u16, + + pub node_uuid: String, + + // pub super_attempts: AtomicU8, + pub super_nodes: Vec, + pub super_node_index: AtomicU8, +} + +impl NodeConfig { + pub fn new() -> Self { + Self { + name: String::new(), + allow_routing: true, + allow_p2p: true, + _drop_multicast: false, + _tos: 0, + _register_super_interval: config::REGISTER_SUPER_INTERVAL, + register_ttl: 1, + // any port, + _local_port: 0, + + node_uuid: String::new(), + + super_nodes: Vec::new(), + // super_attempts: AtomicU8::new(config::SUPER_ATTEMPTS_DEFAULT), + super_node_index: AtomicU8::new(0), + + mtu: 1290, + } + } +} + +#[derive(Debug)] +pub struct EdgePeer { + pub dev_addr: IpSubnet, + + // 对端对外开放的ip和端口信息 + pub sock: RwLock, + // peer's ipv6 info + pub _v6_info: RwLock>, + + pub timeout: u8, + + // 最近一次遇见 + pub last_seen: AtomicU64, + // 最近一次p2p消息 + pub last_p2p: AtomicU64, + // 最近一次合法时间 + pub _last_valid_timestamp: AtomicU64, + + // 最近一次发送query + pub last_sent_query: AtomicU64, +} + +impl EdgePeer { + pub fn new( + net_addr: u32, + net_bit_len: u8, + sock: &SdlanSock, + v6info: &Option, + now: u64, + ) -> Self { + let mut v6_info = None; + if let Some(v6info) = v6info { + v6_info = Some(SdlanSock { + family: AF_INET6, + port: v6info.port, + v4: [0; 4], + v6: v6info.v6, + }) + } + Self { + dev_addr: IpSubnet::new(net_addr, net_bit_len), + sock: RwLock::new(sock.deepcopy()), + _v6_info: RwLock::new(v6_info), + timeout: REGISTER_INTERVAL, + last_p2p: AtomicU64::new(0), + last_seen: AtomicU64::new(0), + _last_valid_timestamp: AtomicU64::new(now), + last_sent_query: AtomicU64::new(0), + } + } +} diff --git a/src/network/packet.rs b/src/network/packet.rs new file mode 100644 index 0000000..565554a --- /dev/null +++ b/src/network/packet.rs @@ -0,0 +1,853 @@ +use std::{net::SocketAddr, sync::atomic::Ordering, time::Duration}; + +use crate::{ + config::REGISTER_INTERVAL, + pb::{ + encode_to_tcp_message, encode_to_udp_message, SdlData, SdlEmpty, SdlPeerInfo, SdlQueryInfo, + SdlRegister, SdlRegisterAck, + }, + tcp::{get_tcp_conn, PacketType}, + utils::{send_to_sock, Socket}, +}; +use etherparse::IpHeaders; +use prost::Message; +use sdlan_sn_rs::{ + config::AF_INET, + peer::{is_sdlan_sock_equal, SdlanSock, V6Info}, + utils::{ + aes_decrypt, get_current_timestamp, get_sdlan_sock_from_socketaddr, ip_to_string, + is_multi_broadcast, Result, SDLanError, + }, +}; +use std::sync::Arc; + +use tracing::{debug, error, info}; + +use super::{EdgePeer, Node}; + +pub async fn read_and_parse_packet( + eee: &Node, + sock: &Socket, + timeout: Option, + // cancel: CancellationToken, +) -> Result<()> { + let mut buf = vec![0; 3000]; + let res; + if timeout.is_some() { + tokio::select! { + _ = tokio::time::sleep(timeout.unwrap()) => { + return Err(SDLanError::NormalError("timeouted")); + } + r = sock.recv_from(&mut buf) => { + res=r; + } + } + } else { + res = sock.recv_from(&mut buf).await; + } + debug!("read_and_parse packet, got packet"); + // let res = sock.recv_from(&mut buf).await; + match res { + Ok((0, _)) => { + // received 0 + error!("received zero bytes"); + // return Ok(()) + } + Err(e) => { + // error occured + error!("receive error occured: {:?}", e); + } + Ok((size, from)) => { + // size > 0 + buf.truncate(size); + match handle_packet(eee, from, &buf).await { + Ok(_) => {} + Err(e) => { + error!("failed to handle_packet: {:?}", e); + } + } + } + } + + Ok(()) +} + +pub async fn handle_packet(eee: &Node, addr: SocketAddr, buf: &[u8]) -> Result<()> { + if buf.len() < 1 { + return Err(SDLanError::NormalError("buf length error")); + } + let Ok(pkt_type) = PacketType::try_from(buf[0]) else { + return Err(SDLanError::NormalError("invalid packet type")); + }; + match pkt_type { + PacketType::StunReply => { + // stun reply, like pong + } + PacketType::Register => { + if !eee.is_authorized() { + error!("dropping REGISTER received before authorized"); + return Ok(()); + } + let from_sock = get_sdlan_sock_from_socketaddr(addr).unwrap(); + let _ = handle_packet_register(eee, &buf[1..], false, &from_sock).await; + } + PacketType::RegisterACK => { + if !eee.is_authorized() { + error!("dropping REGISTER received before authorized"); + return Ok(()); + } + let from_sock = get_sdlan_sock_from_socketaddr(addr).unwrap(); + let _ = handle_packet_register_ack(eee, &buf[1..], &from_sock).await; + } + PacketType::Data => { + if !eee.is_authorized() { + error!("dropping PACKET received before authorized"); + return Ok(()); + } + + let Ok(data) = SdlData::decode(&buf[1..]) else { + error!("failed to decode to SDLData"); + return Err(SDLanError::NormalError("failed to decode to SDLData")); + }; + let from_sock = get_sdlan_sock_from_socketaddr(addr).unwrap(); + if data.is_p2p { + debug!("[P2P] Rx data from {}", from_sock.to_string()); + } else { + debug!( + "[PsP] Rx data from {} via {}", + ip_to_string(&data.src_ip), + from_sock.to_string() + ); + } + if data.is_p2p { + check_peer_registration_needed(eee, !data.is_p2p, data.src_ip, &None, &from_sock) + .await; + } + handle_tun_packet(eee, !data.is_p2p, data).await; + } + other => { + error!("udp not processing {:?}", other); + } + } + /* + let pkt_type = buf[0].into(); + debug!("got packet {} bytes", buf.len()); + let (cmn, slice) = packet::decode_common(&buf)?; + println!("got packet: {:?}", cmn.pc); + + if !eee.is_authorized() { + error!("unauthorized, returning"); + return Ok(()); + } + + let from_sn = (cmn.flags & config::SDLAN_FLAGS_FROM_SN) != 0; + let from_sock = get_sdlan_sock_from_socketaddr(addr).unwrap(); + if from_sn { + if !eee.sn_is_known(&from_sock) { + error!("drop incoming data from unknown supernode"); + return Ok(()); + } + } + + let res = match cmn.pc { + PacketType::PKTPacket => { + // handle packet + handle_packet_packet(eee, cmn, slice, from_sn, &from_sock).await + } + PacketType::PKTRegister => { + // handle register from other peer + handle_packet_register(eee, slice, from_sn, &from_sock).await + } + PacketType::PKTRegisterACK => { + // handle register ack from other peer + handle_packet_register_ack(eee, slice, &from_sock).await + } + PacketType::PKTRegisterSuperAcknowledge => { + // handle register super acknowledge + handle_packet_register_super_acknowledge(eee) + } + PacketType::PKTRegisterSuperACK => { + // handle register super ack + handle_packet_register_super_ack(eee, cmn, slice) + } + PacketType::PKTRegisterSuperNAK => { + // handle register super nak + handle_packet_register_super_nak(eee, cmn, slice) + } + PacketType::PKTPeerInfo => { + // handle peer info from sn + handle_packet_peer_info(eee, slice).await + } + PacketType::PKTCommand => { + // handle command + Ok(()) + } + other => { + error!("unknown packet {:?}", other); + Ok(()) + } + }; + + if let Err(e) = res { + error!("handle packet error occured: {}", e.as_str()); + } + + */ + Ok(()) +} + +pub async fn handle_packet_peer_info( + eee: &Node, + // cmn: Common<'_>, + body: &[u8], + //sender_sock: &SdlanSock, +) -> Result<()> { + let Ok(pi) = SdlPeerInfo::decode(body) else { + error!("failed to decode PEER_INFO"); + return Ok(()); + }; + + debug!("got peer info: {:?}", pi); + + if pi.v4_info.is_none() { + error!("PEER's v4_info is none"); + return Ok(()); + } + + let v4 = pi.v4_info.unwrap(); + let Ok(v4_u32) = v4.v4.try_into() else { + error!("failed to convert v4"); + return Ok(()); + }; + // let src_ip = u32::from_be_bytes(v4_u32); + + if pi.dst_ip == 0 { + // pong from sn + } else { + match eee.pending_peers.get_peer(&pi.dst_ip) { + Some(edgeinfo) => { + let sock = SdlanSock { + family: AF_INET, + port: v4.port as u16, + v4: v4_u32, + v6: [0; 16], + }; + *(edgeinfo.sock.write().unwrap()) = sock.deepcopy(); + info!( + "Rx PEERINFO for {}: is at {}", + ip_to_string(&pi.dst_ip), + sock.to_string() + ); + send_register(eee, &sock, &None).await; + } + None => { + debug!("Rx PEERINFO unknown peer: {}", ip_to_string(&pi.dst_ip)); + } + } + } + Ok(()) +} + +/* +fn handle_packet_register_super_nak(eee: &Node, _cmn: Common<'_>, slice: &[u8]) -> Result<()> { + let nak: RegisterSuperNAK = serde_json::from_slice(slice)?; + if nak.src_ip == eee.device_config.get_ip() { + eee.set_authorized(false, Vec::new()); + error!("unauthorized"); + } else { + eee.known_peers.delete_peer_with_ip(&nak.src_ip); + eee.pending_peers.delete_peer_with_ip(&nak.src_ip); + } + Ok(()) +} +*/ + +/* +fn handle_packet_register_super_ack(eee: &Node, _cmn: Common<'_>, slice: &[u8]) -> Result<()> { + debug!("handling REGISTER_SUPER_ACK"); + let ack: RegisterSuperACK = serde_json::from_slice(slice)?; + + if ack.dev_addr.net_addr != 0 && ack.dev_addr.net_bit_len != 0 { + // i'm authorized or moved; + // eee.device + eee.device_config + .set_ip(ack.dev_addr.net_addr, ack.dev_addr.net_bit_len); + debug!( + "ip addr assigned: {}/{}", + ip_to_string(&ack.dev_addr.net_addr), + ack.dev_addr.net_bit_len + ) + } + + let Ok(private_key) = load_private_key_file(".client/id_rsa") else { + error!("failed to load private key"); + return Err(SDLanError::NormalError("failed to load private key")); + }; + + let encrypt_key = rsa_decrypt(&private_key, &ack.encrypted_key)?; + let header_key = rsa_decrypt(&private_key, &ack.header_key)?; + + eee.config + .super_attempts + .store(SUPER_ATTEMPTS_DEFAULT, Ordering::Relaxed); + eee.stats + .last_sup + .store(get_current_timestamp(), Ordering::Relaxed); + debug!("changed to Authorized"); + eee.set_authorized(true, encrypt_key); + + eee.device.reload_config(&eee.device_config); + eee.known_peers.clear(); + eee.pending_peers.clear(); + Ok(()) +} +*/ + +/* +fn handle_packet_register_super_acknowledge( + eee: &Node, + // cmn: Common<'_>, + // slice: &[u8], +) -> Result<()> { + debug!("handling REGISTER_SUPER_ACKNOWLEDGE"); + // TODO: should check the common and the slice content. + eee.config + .super_attempts + .store(SUPER_ATTEMPTS_DEFAULT, Ordering::Relaxed); + eee.stats + .last_sup + .store(get_current_timestamp(), Ordering::Relaxed); + Ok(()) +} +*/ + +async fn handle_packet_register_ack( + eee: &Node, + // cmn: Common<'_>, + body: &[u8], + sender_sock: &SdlanSock, +) -> Result<()> { + let Ok(ack) = SdlRegisterAck::decode(body) else { + println!("failed to decode REGISTER_ACK"); + return Ok(()); + }; + + let origin_sender = sender_sock; + + debug!( + "Rx REGISTER ACK from {} [{}] to {} via {}", + ip_to_string(&ack.src_ip), + origin_sender.to_string(), + ip_to_string(&ack.dst_ip), + sender_sock.to_string(), + ); + peer_set_p2p_confirmed(eee, ack.src_ip, sender_sock); + + Ok(()) +} + +async fn handle_packet_register( + eee: &Node, + // cmn: Common<'_>, + body: &[u8], + from_sn: bool, + sender_sock: &SdlanSock, +) -> Result<()> { + if !eee.is_authorized() { + error!("drop register due to not authed"); + return Ok(()); + } + + let Ok(reg) = SdlRegister::decode(body) else { + error!("failed to decode REGISTER"); + return Ok(()); + }; + + let origin_sender = sender_sock; + + let via_multicast = is_multi_broadcast(reg.dst_ip); + if via_multicast && reg.src_ip == eee.device_config.get_ip() { + debug!("skipping register from self"); + return Ok(()); + } + + if !from_sn { + info!("[P2P] Rx REGISTER from {}", sender_sock.to_string()); + eee.pending_peers.delete_peer_with_ip(®.src_ip); + send_register_ack(eee, origin_sender, ®).await; + } else { + info!( + "[PsP] Rx REGISTER from {} [{}] to {} via {}", + ip_to_string(®.src_ip), + ip_to_string(®.dst_ip), + sender_sock.to_string(), + origin_sender.to_string(), + ); + } + check_peer_registration_needed(eee, from_sn, reg.src_ip, &None, origin_sender).await; + + Ok(()) +} + +/* +async fn handle_packet_packet( + eee: &Node, + cmn: Common<'_>, + body: &[u8], + from_sn: bool, + sender_sock: &SdlanSock, +) -> Result<()> { + if eee.stats.last_sup.load(Ordering::Relaxed) == 0 { + error!("dropping PACKET received before first registration with sn"); + return Ok(()); + } + let has_sock = cmn.flags & config::SDLAN_FLAGS_SOCKET != 0; + let has_v6 = cmn.flags & config::SDLAN_FLAGS_HAS_V6 != 0; + let pkt = packet::Packet::unmarshal(body, has_sock, has_v6)?; + + // let mut orig_sender: &SdlanSock = sender_sock; + + // here, the origin sender ref should be checked + // if let Some(ref sk) = pkt.sock { + // orig_sender = sk; + // } + // println!("orig_sender: {:?}", orig_sender); + + let mut origin_sender = sender_sock; + if let Some(ref k) = pkt.sock { + origin_sender = k; + } + println!("orig_sender: {:?}", origin_sender); + if !from_sn { + // data from other peer + debug!("[P2P] Rx data from {}", sender_sock.to_string()); + eee.pending_peers.peers.remove(&pkt.src_ip); + } else { + // from sn, sock should not be None + debug!( + "[PsP] Rx data from {} (via {})", + origin_sender.to_string(), + sender_sock.to_string() + ); + } + + check_peer_registration_needed(eee, from_sn, pkt.src_ip, &pkt.v6_info, origin_sender).await; + // handle_tun_packet(eee, from_sn, pkt).await; + + Ok(()) +} +*/ + +pub async fn check_peer_registration_needed( + eee: &Node, + from_sn: bool, + src_ip: u32, + v6_info: &Option, + peer_sock: &SdlanSock, +) { + let mut p = eee.known_peers.get_peer(&src_ip); + if let None = p { + p = eee.known_peers.get_peer_by_sock(peer_sock); + if let Some(ref k) = p { + eee.known_peers.insert_peer(k.clone()); + } + } + match p { + None => { + let _ = register_with_new_peer(eee, from_sn, src_ip, v6_info, peer_sock).await; + // unimplemented!(); + } + Some(k) => { + let now = get_current_timestamp(); + if !from_sn { + k.last_p2p.store(now, Ordering::Relaxed); + } + let last_seen = k.last_seen.load(Ordering::Relaxed); + // more than 3 seconds + if now - last_seen > 3 { + check_known_peer_sock_change(eee, from_sn, src_ip, v6_info, peer_sock).await; + } + } + } +} + +async fn check_known_peer_sock_change( + eee: &Node, + from_sn: bool, + ip: u32, + v6_info: &Option, + // dev_addr: &IpSubnet, + peersock: &SdlanSock, +) { + if is_multi_broadcast(ip) { + return; + } + match eee.known_peers.get_peer(&ip) { + Some(p) => { + if !is_sdlan_sock_equal(&p.sock.read().unwrap(), peersock) { + if !from_sn { + info!( + "peer changed: {}: {} -> {}", + ip_to_string(&ip), + &p.sock.read().unwrap().to_string(), + peersock.to_string() + ); + eee.known_peers.delete_peer_with_ip(&ip); + register_with_new_peer(eee, from_sn, ip, v6_info, peersock).await; + } + } else { + // from sn, sn could see a different sock with us, just ignore it + } + } + None => return, + } +} + +async fn register_with_new_peer( + eee: &Node, + from_sn: bool, + ip: u32, + v6_info: &Option, + // dev_addr: &IpSubnet, + peersock: &SdlanSock, +) { + let now = get_current_timestamp(); + let mut scan = eee.pending_peers.get_peer(&ip); + if let None = scan { + // such ip not found in pending + let temp = Arc::new(EdgePeer::new( + ip, + eee.device_config.get_net_bit(), + peersock, + v6_info, + now, + )); + debug!( + "===> new pending {} => {}", + ip_to_string(&ip), + peersock.to_string(), + ); + eee.pending_peers.insert_peer(temp.clone()); + scan = Some(temp); + debug!("Pending size: {}", eee.pending_peers.peers.len()); + + if from_sn { + // should send register to peer + if eee.config.register_ttl == 1 { + /* We are DMZ host or port is directly accessible. Just let peer to send back the ack */ + } else if eee.config.register_ttl > 1 { + let mut alter = 16; + if let Ok(ttl) = eee.udp_sock_v4.ttl() { + let mut temp = peersock.deepcopy(); + send_register(eee, &temp, v6_info).await; + + let _ = eee.udp_sock_v4.set_ttl(eee.config.register_ttl as u32); + while alter > 0 { + temp.port += 1; + send_register(eee, &temp, &None).await; + alter -= 1; + } + let _ = eee.udp_sock_v4.set_ttl(ttl); + } + } else { + // Normal STUN + send_register(eee, peersock, v6_info).await; + } + // 发送给sn + send_register( + eee, + &eee.config.super_nodes + [eee.config.super_node_index.load(Ordering::Relaxed) as usize], + &None, + ) + .await; + } else { + // P2P register, send directly + send_register(eee, peersock, v6_info).await; + } + register_with_local_peers(eee).await; + } else { + if let Some(ref s) = scan { + *(s.sock.write().unwrap()) = peersock.deepcopy(); + } + } + if let Some(s) = scan { + s.last_seen + .store(get_current_timestamp(), Ordering::Relaxed); + } +} + +async fn register_with_local_peers(eee: &Node) { + if eee.config.allow_p2p { + send_register(eee, &eee.multicast_sock, &None).await; + } +} + +async fn send_register(eee: &Node, sock: &SdlanSock, _v6_info: &Option) { + if !eee.config.allow_p2p { + debug!("skipping register as p2p is disabled"); + return; + } + let network_id = eee.network_id.load(Ordering::Relaxed); + if network_id == 0 { + error!("not authed"); + return; + } + + let register = SdlRegister { + network_id: network_id, + src_ip: eee.device_config.get_ip(), + dst_ip: u32::from_be_bytes(sock.v4), + }; + + let msg = encode_to_udp_message(Some(register), PacketType::Register as u8).unwrap(); + + let _ = send_to_sock(eee, &msg, sock).await; + /* + let key = eee.get_header_key(); + if key.len() > 0 { + if let Ok(cnt) = encode_packet_encrypted(&cmn, &r, key.as_slice()) { + let _ = send_to_sock_v4_and_v6(eee, &cnt, sock, v6_info).await; + } + } else { + error!("not authed"); + } + */ +} + +async fn handle_tun_packet( + eee: &Node, + from_sn: bool, + pkt: SdlData, //orig_sender: &SdlanSock +) { + let now = get_current_timestamp(); + if from_sn { + if is_multi_broadcast(pkt.dst_ip) { + eee.stats.rx_broadcast.fetch_add(1, Ordering::Relaxed); + } + eee.stats.rx_sup.fetch_add(1, Ordering::Relaxed); + eee.stats.last_sup.store(now, Ordering::Relaxed); + } else { + eee.stats.rx_p2p.fetch_add(1, Ordering::Relaxed); + eee.stats.last_p2p.store(now, Ordering::Relaxed); + } + + let payload = pkt.data; + let key = eee.get_encrypt_key(); + if key.len() == 0 { + // check the encrypt key + error!("packet encrypt key not provided"); + return; + } + + let origin = aes_decrypt(key.as_slice(), &payload); + if let Err(_e) = origin { + error!("failed to decrypt original data"); + return; + } + let data = origin.unwrap(); + + debug!("got packet from sock, will send to tun"); + match IpHeaders::from_slice(&data) { + Ok((iphdr, _)) => { + if let Some(ipv4hdr) = iphdr.ipv4() { + let dstip = u32::from_be_bytes(ipv4hdr.0.destination); + if !is_multi_broadcast(dstip) && dstip != eee.device_config.get_ip() { + // should not routed to me + error!("should not routed to me"); + return; + } + // packet should be sent to dev + debug!("writing {} bytes to tun", data.len()); + if let Err(e) = eee.device.send(&data) { + error!("failed to write to tun: {}", e.to_string()); + } + } + } + Err(e) => { + error!("failed to parse ip packet: {}", e.to_string()); + } + } +} + +async fn send_register_ack(eee: &Node, orig_sender: &SdlanSock, reg: &SdlRegister) { + if !eee.config.allow_p2p { + debug!("Skipping REGISTER ACK as P2P is disallowed"); + return; + } + let network_id = eee.network_id.load(Ordering::Relaxed); + if network_id == 0 { + error!("not authed"); + return; + } + let ack = SdlRegisterAck { + network_id, + src_ip: eee.device_config.get_ip(), + dst_ip: reg.src_ip, + }; + let Ok(ack) = encode_to_udp_message(Some(ack), PacketType::RegisterACK as u8) else { + error!("failed to encode to udp message"); + return; + }; + let _ = send_to_sock(eee, &ack, orig_sender).await; +} + +fn peer_set_p2p_confirmed(eee: &Node, src_ip: u32, sender_sock: &SdlanSock) { + let mut scan = eee.pending_peers.get_peer(&src_ip); + if let None = scan { + scan = eee.pending_peers.get_peer_by_sock(sender_sock); + } + if let None = scan { + error!( + "failed to find sender in pending peer: {}", + sender_sock.to_string() + ); + return; + } + + let mut scan = scan.unwrap(); + eee.pending_peers.delete_peer_with_ip(&src_ip); + match eee.known_peers.get_peer(&src_ip) { + Some(scantmp) => { + eee.known_peers.delete_peer_with_ip(&src_ip); + scan = scantmp; + scan.dev_addr.net_addr.store(src_ip, Ordering::Relaxed); + scan.dev_addr + .net_bit_len + .store(eee.device_config.get_net_bit(), Ordering::Relaxed); + } + None => { + *(scan.sock.write().unwrap()) = sender_sock.deepcopy(); + } + } + let now = get_current_timestamp(); + scan.last_p2p.store(now, Ordering::Relaxed); + scan.last_seen.store(now, Ordering::Relaxed); + + let ip_string = ip_to_string(&src_ip); + let sock_string = sender_sock.to_string(); + info!( + "P2P connection established: {} [{}]", + &ip_string, &sock_string, + ); + debug!("==> new peer: {} -> {}", &ip_string, &sock_string,); + eee.known_peers.insert_peer(scan); +} + +pub async fn check_query_peer_info(eee: &Node, dst_ip: u32) { + let scan: Arc; + let now = get_current_timestamp(); + match eee.pending_peers.get_peer(&dst_ip) { + None => { + let sock = SdlanSock { + family: AF_INET, + port: 0, + v4: [0; 4], + v6: [0; 16], + }; + let peer = Arc::new(EdgePeer::new( + dst_ip, + eee.device_config.get_net_bit(), + &sock, + &None, + now, + )); + debug!("insert peer {} to pending", ip_to_string(&dst_ip)); + eee.pending_peers.insert_peer(peer.clone()); + scan = peer; + } + Some(s) => { + scan = s; + } + } + debug!( + "now={}, last_sent_query={}, REGISTER_INTERVAL={}, scan={:?}", + now, + scan.last_sent_query.load(Ordering::Relaxed), + REGISTER_INTERVAL, + scan, + ); + if now - scan.last_sent_query.load(Ordering::Relaxed) > (REGISTER_INTERVAL as u64) { + /* + send_register( + eee, + &eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize], + &None, + ) + .await; + */ + debug!("sending query for {}", ip_to_string(&dst_ip)); + if let Ok(()) = send_query_peer(eee, dst_ip).await { + scan.last_sent_query.store(now, Ordering::Relaxed); + } + } +} + +async fn send_query_peer(eee: &Node, dst_ip: u32) -> Result<()> { + let network_id = eee.network_id.load(Ordering::Relaxed); + + if network_id == 0 { + error!("not authed"); + return Err(SDLanError::NormalError("not connected")); + } + let query = SdlQueryInfo { dst_ip }; + + let Ok(content) = encode_to_tcp_message( + Some(query), + eee.get_next_packet_id(), + PacketType::QueryInfo as u8, + ) else { + error!("failed to encode query"); + return Err(SDLanError::NormalError("encode query error")); + }; + let tcp_conn = get_tcp_conn(); + tcp_conn.send(&content).await +} + +pub async fn ping_to_sn() { + let Ok(msg) = encode_to_tcp_message::(None, 0, PacketType::Ping as u8) else { + error!("failed to encode ping"); + return; + }; + debug!("ping to sn"); + let tcp_conn = get_tcp_conn(); + if let Err(e) = tcp_conn.send(&msg).await { + error!("failed to ping to sn: {:?}", e); + } +} + +/* +pub async fn update_supernode_reg(eee: &Node) { + let now = get_current_timestamp(); + let authed = eee.is_authorized(); + let last_reg = eee.last_register_req.load(Ordering::Relaxed); + if !authed { + if now > (last_reg + (REGISTER_INTERVAL as u64) / 10) { + debug!("update supernode reg, fast retry"); + } else { + return; + } + } else if now < (last_reg + REGISTER_INTERVAL as u64) { + return; + } + + if eee.config.super_attempts.load(Ordering::Relaxed) == 0 { + eee.config + .super_attempts + .store(SUPER_ATTEMPTS_DEFAULT, Ordering::Relaxed); + error!("sup attempts = 0"); + // next time, the supernode will use the new one + let node_index = eee.config.super_node_index.fetch_add(1, Ordering::Relaxed); + if node_index >= (eee.config.super_nodes.len() - 1) as u8 { + eee.config.super_node_index.store(0, Ordering::Relaxed); + } + } else { + eee.config.super_attempts.fetch_sub(1, Ordering::Relaxed); + } + if let Err(e) = eee.send_register_super().await { + error!("failed to send register_super: {}", e.as_str()); + } + eee.last_register_req.store(now, Ordering::Relaxed); + register_with_local_peers(eee).await; +} +*/ diff --git a/src/network/tun_linux.rs b/src/network/tun_linux.rs new file mode 100644 index 0000000..7c275b4 --- /dev/null +++ b/src/network/tun_linux.rs @@ -0,0 +1,126 @@ +use sdlan_sn_rs::utils::{ip_to_string, net_bit_len_to_mask, SDLanError}; +use std::ffi::CStr; +use std::ffi::{c_char, c_int}; +use std::fs::OpenOptions; +use std::ptr::null_mut; + +use sdlan_sn_rs::utils::Result; +use std::io::{Read, Write}; +use std::os::fd::AsRawFd; +use std::process::Command; + +use tracing::{debug, error}; + +use super::device::{DeviceConfig, Mode}; + +#[link(name = "tuntap")] +extern "C" { + fn tuntap_setup(fd: c_int, name: *mut u8, mode: c_int, packet_info: c_int) -> c_int; +} + +#[allow(unused)] +pub struct Iface { + fd: std::fs::File, + mode: Mode, + name: String, +} + +pub fn new_iface(tunname: &str, mode: Mode) -> Iface { + match Iface::without_packet_info(tunname, mode) { + Err(e) => { + panic!("failed to create tun: {}", e.as_str()); + } + Ok(iface) => iface, + } +} + +impl Iface { + #[allow(unused)] + pub fn with_packet_info(ifname: &str, mode: Mode) -> Result { + Iface::open_tun(ifname, mode, true) + } + + pub fn without_packet_info(ifname: &str, mode: Mode) -> Result { + Iface::open_tun(ifname, mode, false) + } + + fn open_tun(ifname: &str, mode: Mode, need_packet_info: bool) -> Result { + let fs = match OpenOptions::new() + .read(true) + .write(true) + .open("/dev/net/tun") + { + Ok(fs) => fs, + Err(e) => panic!("failed to open tun: {}", e), + }; + let mut name_ptr: *mut u8 = null_mut(); + let mut success = false; + let mut _name = Vec::new(); + for i in 0..16 { + _name = Vec::new(); + _name.extend_from_slice(ifname.as_bytes()); + _name.extend_from_slice(i.to_string().as_bytes()); + _name.extend_from_slice(&[0; 33]); + + name_ptr = _name.as_mut_ptr(); + + let result = unsafe { + tuntap_setup( + fs.as_raw_fd(), + name_ptr, + mode as c_int, + if need_packet_info { 1 } else { 0 }, + ) + }; + if result >= 0 { + success = true; + break; + } + } + if success { + let name = unsafe { + CStr::from_ptr(name_ptr as *const c_char) + .to_string_lossy() + .into_owned() + }; + Ok(Iface { fd: fs, mode, name }) + } else { + Err(SDLanError::NormalError("failed to setup tun")) + } + } + + pub fn reload_config(&self, device_config: &DeviceConfig) { + let netbit = device_config.get_net_bit(); + let ip = device_config.get_ip(); + if netbit == 0 || ip == 0 { + error!("reload config's ip is 0"); + return; + } + let ip = ip_to_string(&ip); + let netbit = ip_to_string(&net_bit_len_to_mask(netbit)); + + let res = Command::new("ifconfig") + .arg(&self.name) + .arg(ip) + .arg("netmask") + .arg(&netbit) + .arg("up") + .output(); + match res { + Ok(_) => { + debug!("ifconfig ok"); + } + Err(e) => { + error!("failed to run ifconfig: {}", e.to_string()); + } + } + } + + pub fn recv(&self, buf: &mut [u8]) -> std::io::Result { + (&self.fd).read(buf) + } + + pub fn send(&self, content: &[u8]) -> std::io::Result { + (&self.fd).write(content) + } +} diff --git a/src/network/tun_win.rs b/src/network/tun_win.rs new file mode 100644 index 0000000..4f36cf6 --- /dev/null +++ b/src/network/tun_win.rs @@ -0,0 +1,54 @@ +use crate::network::ReadWriter; +use sdlan_sn_rs::utils::Result; +use std::io::{Error, ErrorKind}; +use std::sync::Arc; +use wintun; + +pub struct WinTun { + adapter: Arc, + session: Arc, +} + +impl ReadWriter for WinTun { + fn recv(&self, buf: &mut [u8]) -> std::io::Result { + let Ok(pkt) = self.session.receive_blocking() else { + return Err(Error::new(ErrorKind::Other, "failed to receive")); + }; + let content = pkt.bytes(); + let length = content.len(); + if content.len() > buf.len() { + return Err(Error::new(ErrorKind::Other, "length not enough")); + } + for i in 0..content.len() { + buf[i] = content[i]; + } + Ok(length) + } + + fn send(&self, content: &[u8]) -> std::io::Result { + let mut pkt = self + .session + .allocate_send_packet(content.len() as u16) + .unwrap(); + let buf: &mut [u8] = pkt.bytes_mut(); + buf.copy_from_slice(content); + self.session.send_packet(pkt); + Ok(content.len()) + } +} + +fn create_wintun(path: &str) -> WinTun { + let wt = unsafe { wintun::load_from_path(path) }.expect("failed to load wintun"); + + let adapter = match wintun::Adapter::open(&wt, "Demo") { + Ok(a) => a, + Err(_) => wintun::Adapter::create(&wt, "Demo", "Example", None) + .expect("failed to create wintun adapter"), + }; + let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap()); + WinTun { adapter, session } +} + +pub fn create_tun() -> Result> { + Ok(Box::new(create_wintun("/path/to/file"))) +} diff --git a/src/network/tuntap.c b/src/network/tuntap.c new file mode 100644 index 0000000..01f8866 --- /dev/null +++ b/src/network/tuntap.c @@ -0,0 +1,64 @@ +/* + * Since the rust ioctl bindings don't have all the structures and constants, + * it's easier to just write the thing in C and link it in. + */ + +#include +#include +#include + +#include +#ifdef __linux__ +#include +#include +#else +#include +#define IFF_TUN 0x0001 +#define IFF_TAP 0x0002 +#define IFF_NO_PI 0x1000 +#define TUNSETIFF _IOW('T', 202, int) +#endif +#include + +/** + * fd ‒ the fd to turn into TUN or TAP. + * name ‒ the name to use. If empty, kernel will assign something by itself. + * Must be buffer with capacity at least 33. + * mode ‒ 1 = TUN, 2 = TAP. + * packet_info ‒ if packet info should be provided, if the given value is 0 it will not prepend packet info. + */ +int tuntap_setup(int fd, unsigned char *name, int mode, int packet_info) +{ + struct ifreq ifr; + memset(&ifr, 0, sizeof ifr); + switch (mode) + { + case 1: + ifr.ifr_flags = IFF_TUN; + break; + case 2: + ifr.ifr_flags = IFF_TAP; + break; + default: + assert(0); + } + + // If no packet info needs to be provided add corresponding flag + if (!packet_info) + { + ifr.ifr_flags |= IFF_NO_PI; + } + + // Leave one for terminating '\0'. No idea if it is needed, didn't find + // it in the docs, but assuming the worst. + strncpy(ifr.ifr_name, (char *)name, IFNAMSIZ - 1); + + int ioresult = ioctl(fd, TUNSETIFF, &ifr); + if (ioresult < 0) + { + return ioresult; + } + strncpy((char *)name, ifr.ifr_name, IFNAMSIZ < 32 ? IFNAMSIZ : 32); + name[32] = '\0'; + return 0; +} diff --git a/src/pb/message.rs b/src/pb/message.rs new file mode 100644 index 0000000..3b51b6d --- /dev/null +++ b/src/pb/message.rs @@ -0,0 +1,230 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Sdlv4Info { + #[prost(uint32, tag = "1")] + pub port: u32, + #[prost(bytes = "vec", tag = "2")] + pub v4: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub nat_type: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Sdlv6Info { + #[prost(uint32, tag = "1")] + pub port: u32, + #[prost(bytes = "vec", tag = "2")] + pub v6: ::prost::alloc::vec::Vec, +} +/// 设备网络地址信息 +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlDevAddr { + #[prost(uint32, tag = "1")] + pub network_id: u32, + #[prost(uint32, tag = "2")] + pub net_addr: u32, + #[prost(uint32, tag = "3")] + pub net_bit_len: u32, +} +/// tcp通讯消息 +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlEmpty {} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlRegisterSuper { + #[prost(uint32, tag = "1")] + pub version: u32, + #[prost(string, tag = "2")] + pub installed_channel: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub client_id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "4")] + pub dev_addr: ::core::option::Option, + #[prost(string, tag = "5")] + pub pub_key: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub token: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlRegisterSuperAck { + #[prost(message, optional, tag = "1")] + pub dev_addr: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub aes_key: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub known_ips: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "4")] + pub upgrade_type: u32, + #[prost(string, optional, tag = "5")] + pub upgrade_prompt: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "6")] + pub upgrade_address: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlRegisterSuperNak { + #[prost(uint32, tag = "1")] + pub error_code: u32, + #[prost(string, tag = "2")] + pub error_message: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlQueryInfo { + #[prost(uint32, tag = "1")] + pub dst_ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlPeerInfo { + #[prost(uint32, tag = "1")] + pub dst_ip: u32, + #[prost(message, optional, tag = "2")] + pub v4_info: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub v6_info: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlKnownIpEvent { + #[prost(uint32, tag = "1")] + pub ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlDropIpEvent { + #[prost(uint32, tag = "1")] + pub ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlNatChangedEvent { + #[prost(uint32, tag = "1")] + pub ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlSendRegisterEvent { + #[prost(uint32, tag = "1")] + pub dst_ip: u32, + #[prost(uint32, tag = "2")] + pub nat_ip: u32, + #[prost(uint32, tag = "3")] + pub nat_port: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlNetworkShutdownEvent { + #[prost(string, tag = "1")] + pub message: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlChangeNetworkCommand { + #[prost(message, optional, tag = "1")] + pub dev_addr: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub aes_key: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub known_ips: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlCommandAck { + /// status = true, 表示成功;status = false 表示失败,message是失败原因描述 + #[prost(bool, tag = "1")] + pub status: bool, + #[prost(string, optional, tag = "2")] + pub message: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlFlows { + /// 服务器转发流量 + #[prost(uint32, tag = "1")] + pub forward_num: u32, + /// p2p直接流量 + #[prost(uint32, tag = "2")] + pub p2p_num: u32, + /// 接收的流量 + #[prost(uint32, tag = "3")] + pub inbound_num: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlStunRequest { + #[prost(uint32, tag = "1")] + pub cookie: u32, + #[prost(string, tag = "2")] + pub client_id: ::prost::alloc::string::String, + #[prost(uint32, tag = "3")] + pub network_id: u32, + #[prost(uint32, tag = "4")] + pub ip: u32, + #[prost(uint32, tag = "5")] + pub nat_type: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlStunReply { + #[prost(uint32, tag = "1")] + pub cookie: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlData { + #[prost(uint32, tag = "1")] + pub network_id: u32, + #[prost(uint32, tag = "2")] + pub src_ip: u32, + #[prost(uint32, tag = "3")] + pub dst_ip: u32, + #[prost(bool, tag = "4")] + pub is_p2p: bool, + #[prost(uint32, tag = "5")] + pub ttl: u32, + #[prost(bytes = "vec", tag = "6")] + pub data: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlRegister { + #[prost(uint32, tag = "1")] + pub network_id: u32, + #[prost(uint32, tag = "2")] + pub src_ip: u32, + #[prost(uint32, tag = "3")] + pub dst_ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlRegisterAck { + #[prost(uint32, tag = "1")] + pub network_id: u32, + #[prost(uint32, tag = "2")] + pub src_ip: u32, + #[prost(uint32, tag = "3")] + pub dst_ip: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlStunProbe { + #[prost(uint32, tag = "1")] + pub cookie: u32, + #[prost(uint32, tag = "2")] + pub attr: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SdlStunProbeReply { + #[prost(uint32, tag = "1")] + pub cookie: u32, + #[prost(uint32, tag = "2")] + pub port: u32, + #[prost(uint32, tag = "3")] + pub ip: u32, +} diff --git a/src/pb/mod.rs b/src/pb/mod.rs new file mode 100644 index 0000000..7a25496 --- /dev/null +++ b/src/pb/mod.rs @@ -0,0 +1,40 @@ +mod message; + +pub use message::*; +use prost::Message; +use sdlan_sn_rs::utils::Result; + +// tcp message has two-byte of size at header +pub fn encode_to_tcp_message( + msg: Option, + packet_id: u32, + packet_type: u8, +) -> Result> { + let mut raw_data = Vec::new(); + + if let Some(msg) = msg { + msg.encode(&mut raw_data)?; + } + + let mut result = Vec::with_capacity(raw_data.len() + 7); + let size = u16::to_be_bytes(raw_data.len() as u16 + 5); + result.extend_from_slice(&size); + result.extend_from_slice(&u32::to_be_bytes(packet_id)); + result.push(packet_type); + result.extend_from_slice(&raw_data); + Ok(result) +} + +// udp message has no two-byte of size at header +pub fn encode_to_udp_message(msg: Option, packet_type: u8) -> Result> { + let mut raw_data = Vec::new(); + + if let Some(msg) = msg { + msg.encode(&mut raw_data)?; + } + + let mut result = Vec::with_capacity(raw_data.len() + 1); + result.push(packet_type); + result.extend_from_slice(&raw_data); + Ok(result) +} diff --git a/src/tcp/mod.rs b/src/tcp/mod.rs new file mode 100644 index 0000000..c1ad114 --- /dev/null +++ b/src/tcp/mod.rs @@ -0,0 +1,5 @@ +mod tcp_codec; +mod tcp_conn; + +pub use tcp_codec::*; +pub use tcp_conn::*; diff --git a/src/tcp/tcp_codec.rs b/src/tcp/tcp_codec.rs new file mode 100644 index 0000000..097a739 --- /dev/null +++ b/src/tcp/tcp_codec.rs @@ -0,0 +1,203 @@ +use tokio::{ + io::{AsyncReadExt, BufReader}, + net::tcp::OwnedReadHalf, +}; + +use num_enum::TryFromPrimitive; +use tracing::debug; + +#[derive(Debug)] +pub struct SdlanTcp { + pub _packet_id: u32, + pub packet_type: PacketType, + pub current_packet: Vec, +} + +#[derive(Debug, Copy, Clone, TryFromPrimitive)] +#[repr(u8)] +pub enum EventType { + KnownIP = 0x01, + + DropIP = 0x02, + + NatChanged = 0x03, + + SendRegister = 0x04, + + NetworkShutdown = 0xFF, +} + +#[derive(Debug, Copy, Clone, TryFromPrimitive)] +#[repr(u8)] +pub enum PacketType { + Empty = 0x00, + RegisterSuper = 0x01, + RegisterSuperACK = 0x02, + RegisterSuperNAK = 0x04, + + UnRegisterSuper = 0x05, + + QueryInfo = 0x06, + PeerInfo = 0x07, + + Ping = 0x08, + Pong = 0x09, + + Event = 0x10, + + Command = 0x11, + CommandACK = 0x12, + + FlowTracer = 0x15, + + Register = 0x20, + RegisterACK = 0x21, + + StunRequest = 0x30, + StunReply = 0x31, + + StunProbe = 0x32, + StunProbeReply = 0x33, + + Data = 0xff, +} + +pub async fn read_a_packet( + reader: &mut BufReader, +) -> Result { + debug!("read a packet"); + let size = reader.read_u16().await?; + debug!("1"); + let packet_id = reader.read_u32().await?; + debug!("2"); + let packet_type = reader.read_u8().await?; + debug!("3"); + + if size < 5 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "size less than five", + )); + } + + let bufsize = (size - 5) as usize; + let mut binary = vec![0; bufsize]; + + let mut to_read = bufsize; + loop { + if to_read == 0 { + break; + } + let size_got = reader.read(&mut binary[(bufsize - to_read)..]).await?; + + if size_got == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "read got zero bytes", + )); + } + to_read -= size_got; + } + let Ok(packet_type) = packet_type.try_into() else { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "packet type error", + )); + }; + let result = SdlanTcp { + _packet_id: packet_id, + packet_type, + current_packet: binary, + }; + Ok(result) +} + +/* +pub async fn read_a_packet2(reader: &mut OwnedReadHalf) -> Result { + debug!("read a packet"); + let size = reader.read_u16().await?; + debug!("1"); + let packet_id = reader.read_u32().await?; + debug!("2"); + let packet_type = reader.read_u8().await?; + debug!("3"); + + if size < 5 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "size less than five", + )); + } + + let mut binary = vec![0; (size - 5) as usize]; + + let mut bytes_read = 0; + loop { + let size_got = reader.read(&mut binary[bytes_read..]).await?; + if size_got == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "read got zero bytes", + )); + } + bytes_read += size_got; + if bytes_read == (size - 5) as usize { + break; + } + } + let Ok(packet_type) = packet_type.try_into() else { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "packet type error", + )); + }; + let result = SdlanTcp { + _packet_id: packet_id, + packet_type, + current_packet: binary, + }; + Ok(result) +} + +impl SdlanTcpCodec { + pub fn new() -> Self { + Self + } +} + +#[derive(Debug)] +pub enum Item { + Data(Vec), +} + +impl Decoder for SdlanTcpCodec { + type Item = SdlanTcp; + type Error = std::io::Error; + + fn decode( + &mut self, + src: &mut tokio_util::bytes::BytesMut, + ) -> Result, Self::Error> { + if src.is_empty() { + return Ok(None); + } + let size = src.get_u16(); + let packet_id = src.get_u32(); + let packet_type = src.get_u8(); + let mut binary = Vec::with_capacity((size - 5).into()); + for i in 0..(size - 5) { + let data = src.get_u8(); + binary.push(data); + } + let Ok(packet_type) = packet_type.try_into() else { + return Ok(None); + }; + let result = SdlanTcp { + _packet_id: packet_id, + packet_type, + current_packet: binary, + }; + Ok(Some(result)) + } +} +*/ diff --git a/src/tcp/tcp_conn.rs b/src/tcp/tcp_conn.rs new file mode 100644 index 0000000..d627a1d --- /dev/null +++ b/src/tcp/tcp_conn.rs @@ -0,0 +1,251 @@ +use once_cell::sync::OnceCell; +use sdlan_sn_rs::utils::{get_current_timestamp, Result, SDLanError}; +use std::future::Future; +use std::sync::atomic::AtomicU64; +use std::sync::Arc; +use std::{ + sync::atomic::{AtomicBool, Ordering}, + time::Duration, +}; +use tokio::io::BufReader; +use tokio_util::sync::CancellationToken; +use tracing::debug; + +use futures_util::{future::BoxFuture, pin_mut}; +use tokio::{ + io::AsyncWriteExt, + net::TcpStream, + sync::mpsc::{channel, Receiver, Sender}, +}; +use tracing::error; + +use crate::config::TCP_PING_TIME; +use crate::tcp::read_a_packet; + +use super::tcp_codec::SdlanTcp; + +static GLOBAL_TCP_HANDLE: OnceCell = OnceCell::new(); + +pub struct ReadWriteActor { + // actor接收的发送给tcp的接收端,由handle存放发送端 + // to_tcp: Receiver>, + remote: String, + connected: Arc, + pong_time: Arc, + // actor收到数据之后,发送给上层的发送端口,接收端由handle保存 + from_tcp: Sender, +} + +impl ReadWriteActor { + pub fn new( + remote: &str, + from_tcp: Sender, + connected: Arc, + pong_time: Arc, + ) -> Self { + Self { + // to_tcp, + pong_time, + connected, + remote: remote.to_owned(), + from_tcp, + } + } + + pub async fn run<'a, T>( + &self, + keep_reconnect: bool, + mut to_tcp: Receiver>, + on_connected: T, + mut start_stop_chan: Receiver, + // cancel: CancellationToken, + ) where + T: for<'b> Fn(&'b mut TcpStream) -> BoxFuture<'b, ()>, + { + // let (tx, rx) = channel(20); + let mut started = false; + loop { + self.connected.store(false, Ordering::Relaxed); + if !started { + println!("waiting for start"); + while let Some(m) = start_stop_chan.recv().await { + if m { + println!("true received"); + started = true; + break; + } else { + println!("false received"); + } + } + } + println!("try connecting..."); + let Ok(mut stream) = TcpStream::connect(&self.remote).await else { + self.connected.store(false, Ordering::Relaxed); + if keep_reconnect { + tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(3)) => { + continue; + } + } + // tokio::time::sleep(Duration::from_secs(3)).await; + // continue; + } + return; + }; + self.connected.store(true, Ordering::Relaxed); + on_connected(&mut stream).await; + // stream.write("hello".as_bytes()).await; + let (reader, mut write) = stream.into_split(); + + let read_from_tcp = async move { + let mut buffed_reader = BufReader::new(reader); + loop { + match read_a_packet(&mut buffed_reader).await { + Ok(packet) => { + debug!("got packet: {:?}", packet); + if let Err(_e) = self.from_tcp.send(packet).await { + error!("failed to receive a packet: {:?}", _e); + } + } + Err(e) => { + error!("failed to read a packet: {}, reconnecting...", e); + return; + } + } + } + }; + + let write_to_tcp = async { + while let Some(data) = to_tcp.recv().await { + match write.write(&data).await { + Ok(size) => { + debug!("{} bytes sent to tcp", size); + } + Err(e) => { + error!("failed to write to tcp: {}", e.to_string()); + return; + } + } + } + println!("to_tcp recv None"); + }; + + let check_pong = async { + loop { + tokio::time::sleep(Duration::from_secs(10)).await; + + let connected = self.connected.load(Ordering::Relaxed); + let now = get_current_timestamp(); + if connected && now - self.pong_time.load(Ordering::Relaxed) > TCP_PING_TIME * 2 + { + // pong time expire, need to re-connect + error!("pong check expired"); + return; + } + } + }; + + pin_mut!(read_from_tcp, write_to_tcp); + + tokio::select! { + _ = read_from_tcp => {}, + _ = write_to_tcp => {}, + _ = check_pong => {}, + Some(false) = start_stop_chan.recv() => { + started = false; + } + } + + println!("connect retrying"); + // future::select(read_from_tcp, write_to_tcp).await; + } + } +} + +#[derive(Debug)] +pub struct ReadWriterHandle { + connected: Arc, + send_to_tcp: Sender>, + // pub data_from_tcp: Receiver, +} + +impl ReadWriterHandle { + pub async fn send(&self, data: &[u8]) -> Result<()> { + if self.connected.load(Ordering::Relaxed) { + // connected, send to it + if let Err(e) = self.send_to_tcp.send(Vec::from(data)).await { + println!("failed to send to send_to_tcp: {}", e.to_string()); + return Err(SDLanError::NormalError("failed to send")); + }; + debug!("tcp info sent"); + } else { + error!("tcp not connected, so not sending data"); + return Err(SDLanError::NormalError("not connected, so not sending")); + } + Ok(()) + } + + fn new<'a, T, T2, F>( + addr: &str, + on_connected: T, + on_message: T2, + pong_time: Arc, + start_stop_chan: Receiver, + // cancel: CancellationToken, + ) -> Self + where + T: for<'b> Fn(&'b mut TcpStream) -> BoxFuture<'b, ()> + Send + 'static, + T2: Fn(SdlanTcp) -> F + Send + 'static, + F: Future + Send, + { + let (send_to_tcp, to_tcp) = channel(20); + let (from_tcp, mut data_from_tcp) = channel(20); + + let connected = Arc::new(AtomicBool::new(false)); + let actor = ReadWriteActor::new(addr, from_tcp, connected.clone(), pong_time); + tokio::spawn(async move { actor.run(true, to_tcp, on_connected, start_stop_chan).await }); + tokio::spawn(async move { + loop { + if let Some(msg) = data_from_tcp.recv().await { + on_message(msg).await; + } else { + println!("data from tcp exited"); + return; + } + } + }); + + ReadWriterHandle { + connected, + send_to_tcp, + // data_from_tcp, + } + } +} + +pub fn init_tcp_conn<'a, T, T2, F>( + addr: &str, + on_connected: T, + on_message: T2, + pong_time: Arc, + // cancel: CancellationToken, + start_stop_chan: Receiver, +) where + T: for<'b> Fn(&'b mut TcpStream) -> BoxFuture<'b, ()> + Send + 'static, + T2: Fn(SdlanTcp) -> F + Send + 'static, + F: Future + Send, +{ + let tcp_handle = + ReadWriterHandle::new(addr, on_connected, on_message, pong_time, start_stop_chan); + + GLOBAL_TCP_HANDLE + .set(tcp_handle) + .expect("failed to set global tcp handle"); +} + +pub fn get_tcp_conn() -> &'static ReadWriterHandle { + match GLOBAL_TCP_HANDLE.get() { + Some(v) => v, + None => panic!("should call init_tcp_conn first"), + } +} diff --git a/src/utils/command.rs b/src/utils/command.rs new file mode 100644 index 0000000..1fdc85c --- /dev/null +++ b/src/utils/command.rs @@ -0,0 +1,47 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +pub struct CommandLine { + #[structopt(short = "s", long = "sn", default_value = "127.0.0.1:7655")] + pub sn: String, + + #[structopt(short = "t", long = "tcp", default_value = "127.0.0.1:7656")] + pub tcp: String, + + #[structopt(short = "r")] + pub _allow_routing: bool, + + #[structopt( + help = "ttl of the register udp4 packet", + short = "L", + default_value = "1" + )] + pub register_ttl: u8, + + #[structopt(help = "mtu of the tun", short = "m", default_value = "1290")] + pub mtu: u32, + + #[structopt(short = "n", long = "name", default_value = "test-name")] + pub name: String, + + #[structopt(long = "tos", default_value = "0")] + pub tos: u32, + + #[structopt(long = "token", default_value = "0")] + pub token: String, +} + +impl Clone for CommandLine { + fn clone(&self) -> Self { + Self { + sn: self.sn.clone(), + tcp: self.tcp.clone(), + _allow_routing: self._allow_routing, + register_ttl: self.register_ttl, + mtu: self.mtu, + name: self.name.clone(), + tos: self.tos, + token: self.token.clone(), + } + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..b1e5a83 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,8 @@ +mod command; +pub use command::*; + +mod socks; +pub use socks::*; + +mod pid_recorder; +pub use pid_recorder::PidRecorder; diff --git a/src/utils/pid_recorder.rs b/src/utils/pid_recorder.rs new file mode 100644 index 0000000..3b1714c --- /dev/null +++ b/src/utils/pid_recorder.rs @@ -0,0 +1,36 @@ +use std::{ + fs::{self, OpenOptions}, + io::Write, +}; + +pub struct PidRecorder(String); + +impl PidRecorder { + pub fn new(pidfile: &str) -> Self { + let pid = std::process::id(); + match OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(pidfile) + { + Ok(mut fp) => { + fp.write(format!("{}", pid).as_bytes()) + .expect("failed to write"); + } + Err(e) => { + println!("failed to open pid file: {}", e); + } + } + + Self(pidfile.to_owned()) + } +} + +impl Drop for PidRecorder { + fn drop(&mut self) { + if let Err(e) = fs::remove_file(&self.0) { + println!("failed to remove pid file: {}", e); + } + } +} diff --git a/src/utils/socks.rs b/src/utils/socks.rs new file mode 100644 index 0000000..3f7f67d --- /dev/null +++ b/src/utils/socks.rs @@ -0,0 +1,145 @@ +use sdlan_sn_rs::{ + config::{AF_INET, AF_INET6}, + utils::{Result, SDLanError}, +}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use tokio::net::ToSocketAddrs; +use tracing::error; + +use sdlan_sn_rs::peer::SdlanSock; +use tokio::net::UdpSocket; + +use crate::network::Node; + +pub struct Socket { + udp: UdpSocket, +} + +impl Socket { + pub async fn send_to(&self, buf: &[u8], target: A) -> Result { + let m = self.udp.send_to(buf, target).await?; + Ok(m) + } + + pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { + let m = self.udp.recv_from(buf).await?; + Ok(m) + } + + pub fn ttl(&self) -> Result { + if let Ok(v) = self.udp.ttl() { + Ok(v) + } else { + Err(SDLanError::NormalError("no ttl found")) + } + } + + pub fn set_ttl(&self, ttl: u32) -> Result<()> { + if let Ok(_) = self.udp.set_ttl(ttl) { + Ok(()) + } else { + Err(SDLanError::NormalError("failed to set ttl")) + } + } + + pub async fn build(port: u16, bind_any: bool, join_multicast: bool, tos: u32) -> Result { + let addr = match bind_any { + true => "0.0.0.0", + false => "127.0.0.1", + }; + let udp = UdpSocket::bind(format!("{}:{}", addr, port)).await?; + if join_multicast { + if let Err(e) = + udp.join_multicast_v4(Ipv4Addr::new(224, 0, 0, 69), Ipv4Addr::new(0, 0, 0, 0)) + { + error!("failed to join multicast: {}", e.to_string()); + } + } + if tos != 0 { + if let Err(e) = udp.set_tos(tos) { + error!("failed to set tos: {}", e.to_string()); + } + } + Ok(Self { udp }) + } +} + +/* +pub async fn send_to_sock_v4_and_v6( + // sk: &Socket, + eee: &Node, + content: &[u8], + sock: &SdlanSock, + v6: &Option, +) -> Result<()> { + let _ = send_to_sock(&eee, content, sock).await; + if let Some(v6) = v6 { + // let sk6 = eee.udp_sock_v6.read().unwrap().clone(); + let sock = SdlanSock { + family: AF_INET6, + port: v6.port, + v4: [0; 4], + v6: v6.v6, + }; + let _ = send_to_sock(eee, content, &sock).await; + } + Ok(()) +} +*/ + +pub async fn send_to_sock( + // sk: &Socket, + eee: &Node, + content: &[u8], + sock: &SdlanSock, + // v6: &Option, +) -> Result<()> { + match sock.family { + AF_INET => { + // sockv4 + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::from(sock.v4)), sock.port); + eee.udp_sock_v4.send_to(content, addr).await?; + return Ok(()); + } + AF_INET6 => { + // sock v6 + let sk = eee.udp_sock_v6.read().unwrap().clone(); + match sk.as_ref() { + None => { + error!("ipv6 not opened, not responding"); + return Ok(()); + } + Some(sk) => { + let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::from(sock.v6)), sock.port); + sk.send_to(content, addr).await?; + return Ok(()); + } + } + } + other => { + error!("unknown family {}, aborting", other); + return Err(SDLanError::NormalError("unknown family")); + } + } +} + +/* +pub async fn send_to_sock(sk: &Socket, content: &[u8], sock: &SdlanSock) -> Result { + match sock.family { + AF_INET => { + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::from(sock.v4)), sock.port); + let n = sk.send_to(content, addr).await?; + return Ok(n); + } + AF_INET6 => { + let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::from(sock.v6)), sock.port); + let n = sk.send_to(content, addr).await?; + return Ok(n); + } + other => { + error!("AF family {} not implemented", other); + return Ok(0); + } + } +} +*/