diff --git a/.gitignore b/.gitignore index 4fffb2f..0095668 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -/Cargo.lock +*.a diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..04731e1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1022 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "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 = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[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 = "cbindgen" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" +dependencies = [ + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[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 = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[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 = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[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 = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "libflate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +dependencies = [ + "core2", + "hashbrown 0.14.5", + "rle-decode-fast", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybenot" +version = "1.1.1" +dependencies = [ + "byteorder", + "hex", + "libflate", + "rand", + "rand_distr", + "ring", + "serde", + "simple-error", +] + +[[package]] +name = "maybenot-ffi" +version = "1.0.0" +dependencies = [ + "cbindgen", + "maybenot", +] + +[[package]] +name = "maybenot-simulator" +version = "1.1.1" +dependencies = [ + "env_logger 0.10.2", + "fastrand", + "log", + "maybenot", + "priority-queue", + "rand", + "serde", + "serde_json", + "test-log", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[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-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[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 = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "priority-queue" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bda9164fe05bc9225752d54aae413343c36f684380005398a6a8fde95fe785" +dependencies = [ + "autocfg", + "indexmap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[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 = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[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 = "simple-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8542b68b8800c3cda649d2c72d688b6907b30f1580043135d61669d4aad1c175" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[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.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +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", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger 0.11.3", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[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 = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[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 = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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 = "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.63", + "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.63", + "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 = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[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-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] diff --git a/Cargo.toml b/Cargo.toml index 454c3a6..e936dcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ resolver = "2" members = [ # lib for the Maybenot framework "crates/maybenot", + # ffi lib for the Maybenot framework + "crates/maybenot-ffi", # simulator for the Maybenot framework "crates/maybenot-simulator", ] diff --git a/README.md b/README.md index 6552c81..19b55c1 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ framework for creating defenses that hide such patterns. ## Workspace structure The Maybenot workspace consists of the following crates: - [maybenot](crates/maybenot): The core framework for creating defenses. +- [maybenot-ffi](crates/maybenot-ffi): A wrapper library around maybenot with a C FFI. - [maybenot-simulator](crates/maybenot-simulator): A simulator for testing defenses. diff --git a/crates/maybenot-ffi/Cargo.toml b/crates/maybenot-ffi/Cargo.toml new file mode 100644 index 0000000..f8452cf --- /dev/null +++ b/crates/maybenot-ffi/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "maybenot-ffi" +description = "An FFI wrapper around Maybenot" +version = "1.0.0" +edition.workspace = true +license.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true +repository.workspace = true + +[lib] +crate-type = ["lib", "staticlib", "cdylib"] + +[dependencies] +maybenot = { version = "1.1.1", path = "../maybenot" } + +[build-dependencies] +cbindgen = { version = "0.26.0", default-features = false } diff --git a/crates/maybenot-ffi/Makefile b/crates/maybenot-ffi/Makefile new file mode 100644 index 0000000..7381c3b --- /dev/null +++ b/crates/maybenot-ffi/Makefile @@ -0,0 +1,31 @@ +CARGO ?= cargo +TARGET ?= +PROFILE ?= release +DESTINATION ?= . +CARGO_TARGET_DIR ?= ../../target + +CARGO_OUTPUT_DIR := $(CARGO_TARGET_DIR)/$(TARGET)/$(PROFILE) +CARGOFLAGS += --target-dir $(CARGO_TARGET_DIR) + +ifeq ($(PROFILE), release) + CARGOFLAGS += --release +endif + +ifneq ($(TARGET),) + CARGOFLAGS += --target + CARGOFLAGS += $(TARGET) +endif + +.PHONY: clean + +# copy the library to the final destination, and strip the _ffi part +$(DESTINATION)/libmaybenot.a: $(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a + cp $^ $@ + +# build the static library +$(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a: src/*.rs Cargo.toml build.rs cbindgen.toml + RUSTFLAGS="-C metadata=maybenot-ffi" ${CARGO} build $(CARGOFLAGS) + +clean: + rm -f $(DESTINATION)/libmaybenot.a + ${CARGO} clean $(CARGOFLAGS) diff --git a/crates/maybenot-ffi/README.md b/crates/maybenot-ffi/README.md new file mode 100644 index 0000000..3436cff --- /dev/null +++ b/crates/maybenot-ffi/README.md @@ -0,0 +1,27 @@ +# Maybenot FFI + +This crate contains C FFI bindings for Maybenot, which let's you use Maybenot as a static library +for languages other than Rust. Headers are found at `maybenot-ffi/maybenot.h` and are +auto-generated when compiling. + +## Building +You need to have [rust](https://rustup.rs/) installed. +Then just run `make` to build a static library at `maybenot-ffi/libmaybenot.a`. + +Arguments to `make`: +- `CARGO` override the `cargo` command +- `TARGET` override target architecture; cross-compile. +- `PROFILE` override the cargo profile, valid options are `release` and `debug`. +- `DESTINATION` change the directory where the output artifacts will be places. + +Example: +``` +make TARGET=x86_64-unknown-linux-gnu PROFILE=debug +``` + +In order to link the resulting library to your program, you'll need to explicitly link some +additional dependencies in addition to `-lmaybenot`. +Run the following command to get an up-to-date list of the required flags for your platform: +``` +RUSTFLAGS="--print native-static-libs" cargo build +``` diff --git a/crates/maybenot-ffi/build.rs b/crates/maybenot-ffi/build.rs new file mode 100644 index 0000000..72b6b3d --- /dev/null +++ b/crates/maybenot-ffi/build.rs @@ -0,0 +1,13 @@ +use std::env; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + generate_c_header(&crate_dir); +} + +fn generate_c_header(crate_dir: &str) { + cbindgen::generate(&crate_dir) + .expect("Unable to generate bindings") + .write_to_file(format!("{crate_dir}/maybenot.h")); +} diff --git a/crates/maybenot-ffi/cbindgen.toml b/crates/maybenot-ffi/cbindgen.toml new file mode 100644 index 0000000..7ff27bf --- /dev/null +++ b/crates/maybenot-ffi/cbindgen.toml @@ -0,0 +1,9 @@ +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml + +language = "C" + +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +include_version = true + +[enum] +prefix_with_name = true diff --git a/crates/maybenot-ffi/maybenot.h b/crates/maybenot-ffi/maybenot.h new file mode 100644 index 0000000..b802ac2 --- /dev/null +++ b/crates/maybenot-ffi/maybenot.h @@ -0,0 +1,204 @@ +/* Generated with cbindgen:0.26.0 */ + +/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ + +#include +#include +#include +#include + +enum MaybenotEventType { + /** + * We sent a normal packet. + */ + MaybenotEventType_NonpaddingSent = 0, + /** + * We received a normal packet. + */ + MaybenotEventType_NonpaddingReceived = 1, + /** + * We send a padding packet. + */ + MaybenotEventType_PaddingSent = 2, + /** + * We received a padding packet. + */ + MaybenotEventType_PaddingReceived = 3, +}; +typedef uint32_t MaybenotEventType; + +/** + * An FFI friendly result error code type. + */ +enum MaybenotResult { + /** + * Operation completed successfully + */ + MaybenotResult_Ok = 0, + /** + * The machine string wasn't valid UTF-8 + */ + MaybenotResult_MachineStringNotUtf8 = 1, + /** + * Failed to parse machine string + */ + MaybenotResult_InvalidMachineString = 2, + /** + * Failed to start framework + */ + MaybenotResult_StartFramework = 3, + /** + * A null pointer was encountered + */ + MaybenotResult_NullPointer = 4, +}; +typedef uint32_t MaybenotResult; + +/** + * A running Maybenot instance. + * + * - Create it: [maybenot_start]. + * - Feed it actions: [maybenot_on_event]. + * - Stop it: [maybenot_stop]. + */ +typedef struct MaybenotFramework MaybenotFramework; + +typedef struct MaybenotEvent { + MaybenotEventType event_type; + /** + * The number of bytes that was sent or received. + */ + uint16_t xmit_bytes; + /** + * The ID of the machine that triggered the event, if any. + */ + uintptr_t machine; +} MaybenotEvent; + +typedef struct MaybenotDuration { + /** + * Number of whole seconds + */ + uint64_t secs; + /** + * A nanosecond fraction of a second. + */ + uint32_t nanos; +} MaybenotDuration; + +enum MaybenotAction_Tag { + MaybenotAction_Cancel = 0, + /** + * Send a padding packet. + */ + MaybenotAction_InjectPadding = 1, + MaybenotAction_BlockOutgoing = 2, +}; +typedef uint32_t MaybenotAction_Tag; + +typedef struct MaybenotAction_Cancel_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; +} MaybenotAction_Cancel_Body; + +typedef struct MaybenotAction_InjectPadding_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; + /** + * The time to wait before injecting a padding packet. + */ + struct MaybenotDuration timeout; + bool replace; + bool bypass; + /** + * The size of the padding packet. + */ + uint16_t size; +} MaybenotAction_InjectPadding_Body; + +typedef struct MaybenotAction_BlockOutgoing_Body { + /** + * The machine that generated the action. + */ + uintptr_t machine; + /** + * The time to wait before blocking. + */ + struct MaybenotDuration timeout; + bool replace; + bool bypass; + /** + * How long to block. + */ + struct MaybenotDuration duration; +} MaybenotAction_BlockOutgoing_Body; + +typedef struct MaybenotAction { + MaybenotAction_Tag tag; + union { + MaybenotAction_Cancel_Body cancel; + MaybenotAction_InjectPadding_Body inject_padding; + MaybenotAction_BlockOutgoing_Body block_outgoing; + }; +} MaybenotAction; + +/** + * Get the version of maybenot-ffi as a null terminated UTF-8-string. + * + * Example: `maybenot-ffi/1.0.1` + */ +const char *maybenot_version(void); + +/** + * Start a new [`MaybenotFramework`] instance. + * + * # Safety + * - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. + * - `out` must be a valid pointer to some valid and aligned pointer-sized memory. + * - The pointer written to `out` is NOT safe to be used concurrently. + */ +MaybenotResult maybenot_start(const char *machines_str, + double max_padding_bytes, + double max_blocking_bytes, + uint16_t mtu, + struct MaybenotFramework **out); + +/** + * Get the number of machines running in the [`MaybenotFramework`] instance. + * + * # Safety + * - `this` must have been created by [`maybenot_start`]. + */ +uintptr_t maybenot_num_machines(struct MaybenotFramework *this_); + +/** + * Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. + * + * # Safety + * - `this` MUST have been created by [`maybenot_start`]. + * - `this` MUST NOT be used after it has been passed to [`maybenot_stop`]. + */ +void maybenot_stop(struct MaybenotFramework *this_); + +/** + * Feed events to the [`MaybenotFramework`] instance. + * + * This may generate [super::MaybenotAction]s that will be written to `actions_out`. + * The number of actions will be written to `num_actions_out`. + * + * # Safety + * - `this` MUST have been created by [`maybenot_start`]. + * - `events` MUST be a valid pointer to an array of size `num_events`. + * - `actions_out` MUST have capacity for [`maybenot_num_machines`] items of size + * `sizeof(MaybenotAction)` bytes. + * - `num_actions_out` MUST be a valid pointer where a 64bit int can be written. + */ +MaybenotResult maybenot_on_events(struct MaybenotFramework *this_, + const struct MaybenotEvent *events, + uintptr_t num_events, + struct MaybenotAction *actions_out, + uintptr_t *num_actions_out); diff --git a/crates/maybenot-ffi/src/error.rs b/crates/maybenot-ffi/src/error.rs new file mode 100644 index 0000000..1f0deea --- /dev/null +++ b/crates/maybenot-ffi/src/error.rs @@ -0,0 +1,25 @@ +/// An FFI friendly result error code type. +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +pub enum MaybenotResult { + /// Operation completed successfully + Ok = 0, + + /// The machine string wasn't valid UTF-8 + MachineStringNotUtf8 = 1, + + /// Failed to parse machine string + InvalidMachineString = 2, + + /// Failed to start framework + StartFramework = 3, + + /// A null pointer was encountered + NullPointer = 4, +} + +impl From> for MaybenotResult { + fn from(result: Result) -> Self { + result.map(|_| MaybenotResult::Ok).unwrap_or_else(|err| err) + } +} diff --git a/crates/maybenot-ffi/src/ffi.rs b/crates/maybenot-ffi/src/ffi.rs new file mode 100644 index 0000000..5c0fcc0 --- /dev/null +++ b/crates/maybenot-ffi/src/ffi.rs @@ -0,0 +1,125 @@ +use crate::{error::MaybenotResult, MaybenotAction, MaybenotEvent, MaybenotFramework}; +use core::{ + ffi::{c_char, CStr}, + mem::MaybeUninit, + slice::from_raw_parts_mut, +}; +use std::slice::from_raw_parts; + +// NOTE: must be null-terminated. +static VERSION: &str = concat!("maybenot-ffi/", env!("CARGO_PKG_VERSION"), "\0"); + +/// Get the version of maybenot-ffi as a null terminated UTF-8-string. +/// +/// Example: `maybenot-ffi/1.0.1` +#[no_mangle] +pub extern "C" fn maybenot_version() -> *const c_char { + debug_assert_eq!( + VERSION.find('\0'), + Some(VERSION.len() - 1), + "VERSION must be null terminated" + ); + + VERSION.as_ptr().cast() +} + +/// Start a new [`MaybenotFramework`] instance. +/// +/// # Safety +/// - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines. +/// - `out` must be a valid pointer to some valid and aligned pointer-sized memory. +/// - The pointer written to `out` is NOT safe to be used concurrently. +#[no_mangle] +pub unsafe extern "C" fn maybenot_start( + machines_str: *const c_char, + max_padding_bytes: f64, + max_blocking_bytes: f64, + mtu: u16, + out: *mut MaybeUninit<*mut MaybenotFramework>, +) -> MaybenotResult { + // SAFETY: see function docs + let Some(out) = (unsafe { out.as_mut() }) else { + return MaybenotResult::NullPointer; + }; + + // SAFETY: see function docs + let machines_str = unsafe { CStr::from_ptr(machines_str) }; + let Ok(machines_str) = machines_str.to_str() else { + return MaybenotResult::MachineStringNotUtf8; + }; + + MaybenotFramework::start(machines_str, max_padding_bytes, max_blocking_bytes, mtu) + .map(|maybenot| { + let box_pointer = Box::into_raw(Box::new(maybenot)); + out.write(box_pointer); + }) + .into() +} + +/// Get the number of machines running in the [`MaybenotFramework`] instance. +/// +/// # Safety +/// - `this` must have been created by [`maybenot_start`]. +#[no_mangle] +pub unsafe extern "C" fn maybenot_num_machines(this: *mut MaybenotFramework) -> usize { + let Some(this) = (unsafe { this.as_mut() }) else { + return 0; + }; + + this.framework.num_machines() +} + +/// Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer. +/// +/// # Safety +/// - `this` MUST have been created by [`maybenot_start`]. +/// - `this` MUST NOT be used after it has been passed to [`maybenot_stop`]. +#[no_mangle] +pub unsafe extern "C" fn maybenot_stop(this: *mut MaybenotFramework) { + // Reconstruct the Box and drop it. + // SAFETY: caller pinky promises that this pointer was created by `maybenot_start` + let _this = unsafe { Box::from_raw(this) }; +} + +/// Feed events to the [`MaybenotFramework`] instance. +/// +/// This may generate [super::MaybenotAction]s that will be written to `actions_out`. +/// The number of actions will be written to `num_actions_out`. +/// +/// # Safety +/// - `this` MUST have been created by [`maybenot_start`]. +/// - `events` MUST be a valid pointer to an array of size `num_events`. +/// - `actions_out` MUST have capacity for [`maybenot_num_machines`] items of size +/// `sizeof(MaybenotAction)` bytes. +/// - `num_actions_out` MUST be a valid pointer where a 64bit int can be written. +#[no_mangle] +pub unsafe extern "C" fn maybenot_on_events( + this: *mut MaybenotFramework, + events: *const MaybenotEvent, + num_events: usize, + actions_out: *mut MaybeUninit, + num_actions_out: *mut usize, +) -> MaybenotResult { + let Some(this) = (unsafe { this.as_mut() }) else { + return MaybenotResult::NullPointer; + }; + + if events.is_null() || actions_out.is_null() || num_actions_out.is_null() { + return MaybenotResult::NullPointer; + } + + // SAFETY: called promises that `events` points to valid array containing `num_events` events. + // Rust arrays have the same layout as C arrays. + let events: &[MaybenotEvent] = unsafe { from_raw_parts(events, num_events) }; + + // SAFETY: called promises that `actions_out` points to valid memory with the capacity to + // hold at least a `num_machines` amount of `MaybenotAction`. Rust arrays have the same + // layout as C arrays. Since we use `MaybeUninit`, rust won't assume that the slice + // elements have been initialized. + let actions: &mut [MaybeUninit] = + unsafe { from_raw_parts_mut(actions_out, this.framework.num_machines()) }; + + let num_actions = this.on_events(events, actions); + unsafe { num_actions_out.write(num_actions) }; + MaybenotResult::Ok +} diff --git a/crates/maybenot-ffi/src/lib.rs b/crates/maybenot-ffi/src/lib.rs new file mode 100644 index 0000000..648e102 --- /dev/null +++ b/crates/maybenot-ffi/src/lib.rs @@ -0,0 +1,223 @@ +use core::{mem::MaybeUninit, str::FromStr, time::Duration}; +use std::time::Instant; + +use maybenot::{ + framework::{Framework, MachineId, TriggerEvent}, + machine::Machine, +}; + +mod error; +pub use error::MaybenotResult; + +mod ffi; +pub use ffi::*; + +/// A running Maybenot instance. +/// +/// - Create it: [maybenot_start]. +/// - Feed it actions: [maybenot_on_event]. +/// - Stop it: [maybenot_stop]. +pub struct MaybenotFramework { + framework: Framework>, + + /// A buffer used internally for converting from [MaybenotEvent]s. + events_buf: Vec, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct MaybenotEvent { + pub event_type: MaybenotEventType, + + /// The number of bytes that was sent or received. + pub xmit_bytes: u16, + + /// The ID of the machine that triggered the event, if any. + pub machine: usize, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct MaybenotDuration { + /// Number of whole seconds + pub secs: u64, + + /// A nanosecond fraction of a second. + pub nanos: u32, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub enum MaybenotEventType { + /// We sent a normal packet. + NonpaddingSent = 0, + + /// We received a normal packet. + NonpaddingReceived = 1, + + /// We send a padding packet. + PaddingSent = 2, + + /// We received a padding packet. + PaddingReceived = 3, +} + +#[repr(C, u32)] +#[derive(Debug, Clone, Copy)] +pub enum MaybenotAction { + Cancel { + /// The machine that generated the action. + machine: usize, + } = 0, + + /// Send a padding packet. + InjectPadding { + /// The machine that generated the action. + machine: usize, + + /// The time to wait before injecting a padding packet. + timeout: MaybenotDuration, + + replace: bool, + bypass: bool, + + /// The size of the padding packet. + size: u16, + } = 1, + + BlockOutgoing { + /// The machine that generated the action. + machine: usize, + + /// The time to wait before blocking. + timeout: MaybenotDuration, + + replace: bool, + bypass: bool, + + /// How long to block. + duration: MaybenotDuration, + } = 2, +} + +impl MaybenotFramework { + fn start( + machines_str: &str, + max_padding_bytes: f64, + max_blocking_bytes: f64, + mtu: u16, + ) -> Result { + let machines: Vec<_> = machines_str + .lines() + .map(Machine::from_str) + .collect::>() + .map_err(|_e| MaybenotResult::InvalidMachineString)?; + + let machines_count = machines.len(); + + let framework = Framework::new( + machines, + max_padding_bytes, + max_blocking_bytes, + mtu, + Instant::now(), + ) + .map_err(|_e| MaybenotResult::StartFramework)?; + + Ok(MaybenotFramework { + framework, + events_buf: Vec::with_capacity(machines_count), + }) + } + + fn on_events( + &mut self, + events: &[MaybenotEvent], + actions: &mut [MaybeUninit], + ) -> usize { + let now = Instant::now(); + + // convert from the repr(C) events and store them temporarily in our buffer + self.events_buf.clear(); + for &event in events { + self.events_buf.push(convert_event(event)); + } + + let num_actions = self + .framework + .trigger_events(&self.events_buf, now) + // convert maybenot actions to repr(C) equivalents + .map(convert_action) + // write the actions to the out buffer + // NOTE: trigger_events will not emit more than one action per machine. + .zip(actions.iter_mut()) + .map(|(action, out)| out.write(action)) + .count(); + + num_actions + } +} + +/// Convert an action from [maybenot] to our own `repr(C)` action type. +fn convert_action(action: &maybenot::framework::Action) -> MaybenotAction { + match *action { + maybenot::framework::Action::Cancel { machine } => MaybenotAction::Cancel { + machine: machine.into_raw(), + }, + maybenot::framework::Action::InjectPadding { + timeout, + size, + bypass, + replace, + machine, + } => MaybenotAction::InjectPadding { + timeout: timeout.into(), + size, + replace, + bypass, + machine: machine.into_raw(), + }, + maybenot::framework::Action::BlockOutgoing { + timeout, + duration, + bypass, + replace, + machine, + } => MaybenotAction::BlockOutgoing { + timeout: timeout.into(), + duration: duration.into(), + replace, + bypass, + machine: machine.into_raw(), + }, + } +} + +fn convert_event(event: MaybenotEvent) -> TriggerEvent { + match event.event_type { + MaybenotEventType::NonpaddingSent => TriggerEvent::NonPaddingSent { + bytes_sent: event.xmit_bytes, + }, + MaybenotEventType::NonpaddingReceived => TriggerEvent::NonPaddingRecv { + bytes_recv: event.xmit_bytes, + }, + MaybenotEventType::PaddingSent => TriggerEvent::PaddingSent { + bytes_sent: event.xmit_bytes, + machine: MachineId::from_raw(event.machine), + }, + MaybenotEventType::PaddingReceived => TriggerEvent::PaddingRecv { + bytes_recv: event.xmit_bytes, + }, + } +} + +impl From for MaybenotDuration { + #[inline] + fn from(duration: Duration) -> Self { + MaybenotDuration { + secs: duration.as_secs(), + nanos: duration.subsec_nanos(), + } + } +}