From f4ca255f06f436038d089c5f22d8f9619b9efaa2 Mon Sep 17 00:00:00 2001 From: Alain Zscheile Date: Sat, 24 Jun 2023 21:14:10 +0200 Subject: [PATCH] initial commit --- .gitignore | 6 + Cargo.lock | 744 ++++++++++++++++++++++++++++++ Cargo.toml | 8 + crates/floof-core/Cargo.toml | 20 + crates/floof-core/src/lib.rs | 249 ++++++++++ crates/floof-core/src/mangler.rs | 125 +++++ crates/floof-core/src/pktspool.rs | 119 +++++ crates/floof-core/src/sessmgr.rs | 190 ++++++++ 8 files changed, 1461 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/floof-core/Cargo.toml create mode 100644 crates/floof-core/src/lib.rs create mode 100644 crates/floof-core/src/mangler.rs create mode 100644 crates/floof-core/src/pktspool.rs create mode 100644 crates/floof-core/src/sessmgr.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1ca14a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.#* +/target +result +result-* +perf.data* +flamegraph.svg diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..747e2c8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,744 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "allocator-api2" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[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.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" + +[[package]] +name = "blake3" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest", +] + +[[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 = "camino" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" + +[[package]] +name = "cap-primitives" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b6df5b295dca8d56f35560be8c391d59f0420f72e546997154e24e765e6451" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "windows-sys", + "winx", +] + +[[package]] +name = "cap-std" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3373a62accd150b4fcba056d4c5f3b552127f0ec86d3c8c102d60b978174a012" +dependencies = [ + "camino", + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[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 = "curve25519-dalek" +version = "4.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "zeroize", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "floof-core" +version = "0.1.0" +dependencies = [ + "blake3", + "cap-std", + "ed25519", + "ed25519-dalek", + "event-listener", + "lru", + "rusqlite", + "thiserror", + "tracing", + "yzb64", +] + +[[package]] +name = "fs-set-times" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7833d0f115a013d51c55950a3b09d30e4b057be9961b709acb9b5b17a1108861" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[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 = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +dependencies = [ + "hashbrown 0.14.0", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "io-extras" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde93d48f0d9277f977a333eca8313695ddd5301dc96f7e02aeddcb0dd99096f" +dependencies = [ + "io-lifetimes", + "windows-sys", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lru" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.3.2", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "windows-sys", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winx" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c52a121f0fbf9320d5f2a9a5d82f6cb7557eda5e8b47fc3e7f359ec866ae960" +dependencies = [ + "bitflags 1.3.2", + "io-lifetimes", + "windows-sys", +] + +[[package]] +name = "yzb64" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e8cedcc4503bd570a10e8161e669fa7038c893311dfe6b3c21d80b30f7be38" +dependencies = [ + "base64", + "once_cell", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1d32103 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = ["crates/*"] +resolver = "2" + +[profile.release] +codegen-units = 2 +debug = 1 +lto = "thin" diff --git a/crates/floof-core/Cargo.toml b/crates/floof-core/Cargo.toml new file mode 100644 index 0000000..8a0c901 --- /dev/null +++ b/crates/floof-core/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "floof-core" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[dependencies] +blake3 = "1.4.0" +ed25519 = "2.2" +ed25519-dalek = "2.0.0-rc.3" +event-listener = "2.5" +lru = "0.10" +rusqlite = "0.29" +thiserror = "1.0" +tracing = "0.1" +yzb64 = "0.1" + +[dependencies.cap-std] +version = "1.0" +features = ["fs_utf8"] diff --git a/crates/floof-core/src/lib.rs b/crates/floof-core/src/lib.rs new file mode 100644 index 0000000..ff265a6 --- /dev/null +++ b/crates/floof-core/src/lib.rs @@ -0,0 +1,249 @@ +use ed25519_dalek::{Signature, SigningKey, VerifyingKey}; +use std::io::Read; +use std::io::{Error as IoError, ErrorKind}; + +pub type KVFilters = Vec<(Vec, Vec)>; +pub type KVs = std::collections::BTreeMap, Vec>; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("I/O error: {0:?}")] + Io(#[from] std::io::Error), + + #[error("database error: {0:?}")] + Database(#[from] rusqlite::Error), +} + +#[derive(Clone)] +pub struct Packet { + pub ttl: u8, + pub markers: KVs, + pub pubkey: VerifyingKey, + pub signature: Signature, + pub inner: Vec, +} + +pub use blake3::Hash as PHash; +pub use event_listener::{Event, EventListener}; + +mod mangler; +pub use mangler::{FConfig, Mangler, WrappedPacket}; + +mod sessmgr; +pub use sessmgr::{Key as SessionKey, SessionManager}; + +mod pktspool; +pub use pktspool::PacketSpool; + +pub use ed25519_dalek; +pub use rusqlite::Connection as DbConnection; + +/// the signed part of a packet +#[derive(Clone)] +pub struct InnerPacket { + pub attrs: KVs, + pub data: Vec, +} + +fn decode_u8(rdr: &mut R) -> std::io::Result { + let mut buf = [0u8; 1]; + rdr.read_exact(&mut buf)?; + Ok(buf[0]) +} + +fn decode_u16(rdr: &mut R) -> std::io::Result { + let mut buf = [0u8; 2]; + rdr.read_exact(&mut buf)?; + Ok(u16::from_be_bytes(buf)) +} + +fn decode_kvs(rdr: &mut R) -> std::io::Result { + let mut kvs = KVs::new(); + let kvpcnt = decode_u16(rdr)?; + for _ in 0..kvpcnt { + let keylen = decode_u8(rdr)?; + let vallen = decode_u16(rdr)?; + let (mut key, mut val): (Vec, Vec) = + (vec![0; keylen.into()], vec![0; vallen.into()]); + rdr.read_exact(&mut key[..])?; + rdr.read_exact(&mut val[..])?; + if kvs.insert(key, val).is_some() { + return Err(IoError::new( + ErrorKind::InvalidData, + "key repeated in KV map", + )); + } + } + Ok(kvs) +} + +fn encode_kvs(kvs: &KVs, wtr: &mut W) -> std::io::Result<()> { + let kvpcnt: u16 = kvs + .len() + .try_into() + .map_err(|e| IoError::new(ErrorKind::InvalidData, e))?; + wtr.write_all(&kvpcnt.to_be_bytes())?; + + for (key, val) in kvs { + let keylen: u8 = key + .len() + .try_into() + .map_err(|e| IoError::new(ErrorKind::InvalidData, e))?; + let vallen: u16 = val + .len() + .try_into() + .map_err(|e| IoError::new(ErrorKind::InvalidData, e))?; + let [vall0, vall1] = vallen.to_be_bytes(); + // NOTE: if write_all_vectored becomes stable, use that instead + wtr.write_all(&[keylen, vall0, vall1])?; + wtr.write_all(&key[..])?; + wtr.write_all(&val[..])?; + } + + Ok(()) +} + +impl Packet { + pub fn hash(&self) -> std::io::Result { + let mut hasher = blake3::Hasher::new(); + self.encode_to_wtr(&mut hasher)?; + Ok(hasher.finalize()) + } + + pub fn verify(&self) -> bool { + self.pubkey + .verify_strict(&self.inner[..], &self.signature) + .is_ok() + } + + pub fn sign(&mut self, sk: &SigningKey) { + use ed25519_dalek::Signer; + self.signature = sk.sign(&self.inner[..]); + self.pubkey = sk.into(); + } + + pub fn decode_from_rdr(mut rdr: R) -> std::io::Result { + let ver = decode_u8(&mut rdr)?; + if ver != 0 { + return Err(IoError::new( + ErrorKind::InvalidData, + "unknown packet version", + )); + } + let ttl = decode_u8(&mut rdr)?; + let markers = decode_kvs(&mut rdr)?; + + let mut pubkey_tmp = [0u8; ed25519_dalek::PUBLIC_KEY_LENGTH]; + rdr.read_exact(&mut pubkey_tmp)?; + let pubkey = VerifyingKey::from_bytes(&pubkey_tmp) + .map_err(|e| IoError::new(ErrorKind::InvalidData, e))?; + + let mut sig_tmp = [0u8; ed25519_dalek::SIGNATURE_LENGTH]; + rdr.read_exact(&mut sig_tmp)?; + let signature = Signature::from_bytes(&sig_tmp); + + let mut inner = Vec::new(); + rdr.read_to_end(&mut inner)?; + Ok(Self { + ttl, + markers, + pubkey, + signature, + inner, + }) + } + + pub fn encode_to_wtr(&self, mut wtr: W) -> std::io::Result<()> { + wtr.write_all(&[0, self.ttl])?; + encode_kvs(&self.markers, &mut wtr)?; + wtr.write_all(self.pubkey.as_bytes())?; + wtr.write_all(&self.signature.to_bytes())?; + wtr.write_all(&self.inner[..])?; + wtr.flush()?; + Ok(()) + } + + pub fn decode_inner(&self) -> Option { + let mut ret = InnerPacket { + attrs: Default::default(), + data: Vec::new(), + }; + let mut cur = std::io::Cursor::new(&self.inner[..]); + ret.attrs = decode_kvs(&mut cur).ok()?; + cur.read_to_end(&mut ret.data).ok()?; + Some(ret) + } +} + +impl InnerPacket { + pub fn encode(&self) -> Option> { + let mut ret = Vec::new(); + encode_kvs(&self.attrs, &mut std::io::Cursor::new(&mut ret)).ok()?; + ret.extend_from_slice(&self.data[..]); + Some(ret) + } +} + +pub fn collect_garbage( + sessmgr: &mut SessionManager, + pktspool: &PacketSpool, +) -> Result, Error> { + let still_valid = sessmgr.collect_garbage()?; + pktspool.collect_garbage(&still_valid)?; + Ok(still_valid) +} + +/// the main coordination structure +pub struct Constellation { + pub mangler: Mangler, + pub pktspool: PacketSpool, + pub sessmgr: SessionManager, + pub notifs: Event, +} + +impl Constellation { + /// convenience wrapper around [`crate::collect_garbage`] + #[inline] + pub fn collect_garbage(&mut self) -> Result, Error> { + collect_garbage(&mut self.sessmgr, &self.pktspool) + } + + #[inline] + pub fn listen(&self) -> EventListener { + self.notifs.listen() + } + + pub fn process_packet bool>( + &mut self, + pkt: WrappedPacket, + cb: F, + ) -> Result, Error> { + let mut pkt = match self.mangler.process(pkt) { + Some(x) => x, + None => return Ok(None), + }; + // allow the user to manipulate the packet if it passes the normal filters + if !cb(&mut pkt) { + return Ok(None); + } + if pkt.inner.ttl > 1 { + if self.sessmgr.set_for_all(pkt.hash.as_bytes())? { + // only store packets if we have any sessions + self.pktspool.store(false, Some(&pkt).into_iter())?; + // only send notifications after we stored the message + self.notifs.notify(usize::MAX); + } + } + // in the original implementation, every conn, after getting :fwdxfer + // which was only emitted in Distributor, called + // * SessionManager.set_soft_multi + // * PacketSpool.store_new + // we can safely omit that because we control the ordering. + // and thus no conn should have to "upgrade" messages to a session + + // now, downstreams need to get the packet + // * non-session-based: get :fwdxfer, store packet, :requestpull + // * session-based: get :SessionPushed, peek queue, :requestpull + Ok(Some(pkt)) + } +} diff --git a/crates/floof-core/src/mangler.rs b/crates/floof-core/src/mangler.rs new file mode 100644 index 0000000..0f871e6 --- /dev/null +++ b/crates/floof-core/src/mangler.rs @@ -0,0 +1,125 @@ +use crate::{KVFilters, KVs, PHash}; +use core::num::NonZeroUsize; + +#[derive(Clone)] +pub struct WrappedPacket { + pub hash: PHash, + pub inner: crate::Packet, +} + +impl AsRef for WrappedPacket { + #[inline(always)] + fn as_ref(&self) -> &Self { + self + } +} + +impl WrappedPacket { + #[inline] + pub fn wrap(inner: crate::Packet) -> std::io::Result { + inner.hash().map(|hash| Self { hash, inner }) + } + + #[inline] + pub fn hash2str(&self) -> String { + crate::pktspool::enc_hash(&self.hash) + } +} + +pub struct FConfig { + pub pos_markers: KVs, + pub neg_markers: KVFilters, + pub set_markers: KVs, + pub pos_attrs: KVs, + pub neg_attrs: KVFilters, +} + +impl FConfig { + fn check_filters(&self, pkt: &WrappedPacket) -> bool { + let pktx = match pkt.inner.decode_inner() { + Some(x) => x, + None => return false, + }; + let markers = &pkt.inner.markers; + let attrs = &pktx.attrs; + if self + .neg_markers + .iter() + .filter_map(|(k, v)| markers.get(k).map(|xv| v == xv)) + .any(core::convert::identity) + { + return false; + } + if !self + .pos_markers + .iter() + .filter_map(|(k, v)| markers.get(k).map(|xv| v == xv)) + .all(core::convert::identity) + { + return false; + } + if self + .neg_attrs + .iter() + .filter_map(|(k, v)| attrs.get(k).map(|xv| v == xv)) + .any(core::convert::identity) + { + return false; + } + if !self + .pos_attrs + .iter() + .filter_map(|(k, v)| attrs.get(k).map(|xv| v == xv)) + .all(core::convert::identity) + { + return false; + } + true + } +} + +/// message filtering and processing +pub struct Mangler { + pub fconf: FConfig, + seen: lru::LruCache, +} + +impl Mangler { + pub fn new(fconf: FConfig) -> Self { + Self { + fconf, + seen: lru::LruCache::new(NonZeroUsize::new(4096).unwrap()), + } + } + + /// packet should only be forwarded further if it gets passed through (with modifications) + pub fn process(&mut self, mut pkt: WrappedPacket) -> Option { + debug_assert_eq!(pkt.inner.hash().ok(), Some(pkt.hash)); + if !self.seen.get(&pkt.hash).is_some() { + tracing::debug!("{}: packet discarded, already seen", pkt.hash2str()); + return None; + } + if pkt.inner.ttl == 0 { + tracing::debug!( + "{}: packet expired in processing (out of hops)", + pkt.hash2str() + ); + return None; + } + if !pkt.inner.verify() { + tracing::error!("{}: packet signature invalid", pkt.hash2str()); + return None; + } + if !self.fconf.check_filters(&pkt) { + tracing::debug!("{}: packet discarded according to filters", pkt.hash2str()); + return None; + } + + self.seen.put(pkt.hash, ()); + pkt.inner.ttl -= 1; + pkt.inner + .markers + .append(&mut self.fconf.set_markers.clone()); + Some(pkt) + } +} diff --git a/crates/floof-core/src/pktspool.rs b/crates/floof-core/src/pktspool.rs new file mode 100644 index 0000000..96b1ce9 --- /dev/null +++ b/crates/floof-core/src/pktspool.rs @@ -0,0 +1,119 @@ +use crate::{mangler::WrappedPacket, PHash}; + +use cap_std::fs_utf8::{Dir, OpenOptions}; +use std::collections::HashSet; +use std::io::{ErrorKind, Result}; +use yzb64::{base64::Engine, B64_ENGINE}; + +pub struct PacketSpool(pub Dir); + +#[inline] +pub(crate) fn enc_hash(h: &PHash) -> String { + B64_ENGINE.encode(h.as_bytes()) +} + +impl PacketSpool { + pub fn drop(&self, h: &PHash) -> Result<()> { + let henc = enc_hash(h); + if let Err(e) = self.0.remove_file(&henc) { + tracing::error!("unable to remove {}: {:?}", &henc, e); + Err(e) + } else { + Ok(()) + } + } + + pub fn fetch(&self, h: &PHash) -> Result { + let henc = enc_hash(h); + let fh = self.0.open(&henc)?; + let pkt = crate::Packet::decode_from_rdr(fh)?; + Ok(pkt) + } + + pub fn store, I: Iterator>( + &self, + overwrite: bool, + wps: I, + ) -> Result<()> { + let opts = { + let mut opts = OpenOptions::new(); + opts.write(true); + if overwrite { + opts.create(true).truncate(true); + } else { + opts.create_new(true); + }; + opts + }; + for w in wps { + let w = w.as_ref(); + let henc = enc_hash(&w.hash); + let mut fh = match self.0.open_with(&henc, &opts) { + Ok(fh) => fh, + Err(e) if e.kind() == ErrorKind::AlreadyExists => continue, + Err(e) => return Err(e), + }; + // if this fails, bail, bc filesystem is probably full... + if let Err(e) = w.inner.encode_to_wtr(&mut fh) { + core::mem::drop(fh); + // make sure to invalidate the incomplete entry + let _ = self.0.remove_file(&henc); + return Err(e); + } + } + Ok(()) + } + + pub(crate) fn collect_garbage(&self, still_valid: &HashSet) -> Result<()> { + let (mut cnt_rem, cnt_keep, mut cnt_present) = (0, still_valid.len(), 0); + + for i in self.0.entries()? { + // decode entry metadata + let i = i?; + let ft = i.file_type()?; + if !ft.is_file() { + continue; + } + cnt_present += 1; + let name = match i.file_name() { + Ok(x) => x, + Err(e) => { + tracing::warn!("file without UTF-8 encodable filename in spool: {:?}", e); + continue; + } + }; + let h = { + let mut x = [0u8; 33]; + if let Err(e) = B64_ENGINE.decode_slice(&name, &mut x[..]) { + tracing::warn!( + "{:?}: file without base64 [32b] decodable filename in spool: {:?}", + name, + e + ); + continue; + } + if x[32] != 0 { + continue; + } + PHash::from_bytes(x[..32].try_into().unwrap()) + }; + + // handle entry + if !still_valid.contains(&h) { + cnt_rem += 1; + if let Err(e) = i.remove_file() { + tracing::error!("{:?}: unable to remove file: {:?}", name, e); + } + } + } + + tracing::debug!( + "removed {} / kept {} / available {}", + cnt_rem, + cnt_keep, + cnt_present + ); + + Ok(()) + } +} diff --git a/crates/floof-core/src/sessmgr.rs b/crates/floof-core/src/sessmgr.rs new file mode 100644 index 0000000..742e147 --- /dev/null +++ b/crates/floof-core/src/sessmgr.rs @@ -0,0 +1,190 @@ +use crate::PHash; + +use rusqlite::{ + params, + types::{ToSqlOutput, ValueRef}, + Connection, Result as SResult, +}; +use std::collections::{BTreeMap, HashSet}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Key(i64); + +pub struct SessionManager(Connection); + +fn inl_blob2sql(blb: &[u8]) -> ToSqlOutput<'_> { + ToSqlOutput::Borrowed(ValueRef::Blob(blb)) +} + +impl SessionManager { + pub fn new(conn: Connection) -> SResult { + conn.execute( + "CREATE TABLE IF NOT EXISTS keys ( + kid integer primary key, + name blob not null unique, + pubkey blob not null +)", + params![], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS ents ( + eid integer primary key, + name blob not null unique +)", + params![], + )?; + conn.execute( + "CREATE TABLE IF NOT EXISTS sess ( + id integer primary key, + kid integer not null references keys(kid), + eid integer not null references ents(eid) +)", + params![], + )?; + Ok(Self(conn)) + } + + pub fn resolve_key(&self, key: &[u8]) -> SResult)>> { + let mut stmt = self.0 + .prepare_cached("SELECT kid, pubkey FROM keys WHERE name = ?1 LIMIT 1")?; + let x = match stmt + .query_map([inl_blob2sql(key)], |row| { + Ok((Key(row.get(0)?), row.get(1)?)) + })? + .next() + { + Some(Err(e)) => return Err(e), + Some(Ok(x)) => Some(x), + None => None, + }; + Ok(x) + } + + pub fn drop_key(&mut self, key: Key) -> SResult<()> { + let txn = self.0.transaction()?; + txn.execute("DELETE FROM sess WHERE kid = ?1", [key.0])?; + txn.execute("DELETE FROM keys WHERE kid = ?1", [key.0])?; + txn.commit()?; + Ok(()) + } + + fn resolve_entry_on(conn: &Connection, subkey: &[u8]) -> SResult> { + let x = match conn + .prepare_cached("SELECT eid FROM ents WHERE name = ?1 LIMIT 1")? + .query_map([inl_blob2sql(subkey)], |row| row.get(0))? + .next() + { + Some(Err(e)) => Err(e), + Some(Ok(x)) => Ok(Some(x)), + None => Ok(None), + }; + x + } + + pub fn drop_entry(&mut self, subkey: &[u8]) -> SResult<()> { + let txn = self.0.transaction()?; + let eid = Self::resolve_entry_on(&*txn, subkey)?; + txn.execute("DELETE FROM sess WHERE eid = ?1", [eid])?; + txn.execute("DELETE FROM ents WHERE eid = ?1", [eid])?; + txn.commit() + } + + pub fn register_key(&self, key: &[u8], pubkey: &[u8]) -> SResult<()> { + self.0.execute( + "INSERT INTO keys (name, pubkey) VALUES (?1, ?2)", + [inl_blob2sql(key), inl_blob2sql(pubkey)], + )?; + Ok(()) + } + + pub fn peek(&self, key: Key) -> SResult> { + let mut stmt = self.0.prepare_cached( + "SELECT ents.name FROM ents INNER JOIN sess ON ents.eid = sess.eid WHERE sess.kid = ?1", + )?; + let ret = stmt + .query_map([key.0], |row| row.get(0).map(PHash::from_bytes))? + .collect::>>()?; + Ok(ret) + } + + pub fn set_for_all(&mut self, subkey: &[u8]) -> SResult { + let txn = self.0.transaction()?; + let eid = Self::resolve_entry_on(&*txn, subkey)?; + let chg = txn + .prepare_cached("INSERT INTO sess (kid, eid) SELECT keys.kid, ?1 FROM keys")? + .execute([eid])?; + txn.commit()?; + Ok(if chg > 0 { + true + } else { + false + }) + } + + pub fn set_soft_multi<'sk, SKI: Iterator>( + &mut self, + key: Key, + subkeys: SKI, + ) -> SResult<()> { + let txn = self.0.transaction()?; + { + let mut stmi_sess = + txn.prepare_cached("INSERT INTO sess (kid, eid) SELECT ?1, ents.eid FROM ents WHERE ents.name = ?2 LIMIT 1")?; + for subkey in subkeys { + stmi_sess.insert(params![key.0, inl_blob2sql(subkey)])?; + } + } + txn.commit() + } + + pub fn unset_multi<'sk, SKI: Iterator>( + &mut self, + key: Key, + subkeys: SKI, + ) -> SResult<()> { + let txn = self.0.transaction()?; + { + let mut stmd_sess = + txn.prepare_cached("DELETE FROM sess WHERE sess.kid = ?1, sess.eid IN (SELECT ents.eid FROM ents WHERE ents.name = ?2 LIMIT 1)")?; + for subkey in subkeys { + stmd_sess.execute(params![key.0, inl_blob2sql(subkey)])?; + } + } + txn.commit() + } + + /// runs garbage collection, returns set of still-valid entries + pub(crate) fn collect_garbage(&mut self) -> SResult> { + let txn = self.0.transaction()?; + let still_valid: BTreeMap = { + let mut stmt = txn.prepare_cached( + "SELECT ents.eid, ents.name FROM ents INNER JOIN sess ON ents.eid = sess.eid", + )?; + // such stuff is a work..around stmt-drop problems... + let x = stmt + .query_map([], |row| Ok((row.get(0)?, PHash::from_bytes(row.get(1)?))))? + .collect::>()?; + x + }; + + { + let mut stmt_all = txn.prepare_cached("SELECT eid FROM ents")?; + let mut stmt_del = txn.prepare_cached("DELETE FROM ents WHERE eid = ?1")?; + + // I hate this, but it is relatively clean, and we already have all the data already, + // so no need to allocate temporary tables or use some slow table-except to find all + // the entries to delete. + for eid in stmt_all.query_map([], |row| row.get(0))? { + let eid = eid?; + if !still_valid.contains_key(&eid) { + // we don't need to delete anything from sess because we already checked + // that it isn't there. + stmt_del.execute([eid])?; + } + } + } + + txn.commit()?; + Ok(still_valid.into_iter().map(|(_, v)| v).collect()) + } +}