diff --git a/.cargo/config.toml b/.cargo/config.toml index 2280655..5798c45 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,6 +4,8 @@ rustflags = ["-C", "target-feature=+crt-static"] [alias] test_pc = "test --target=x86_64-unknown-linux-gnu" +build_pc = "build --target=x86_64-unknown-linux-gnu" + [target.armv7-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d0ba238 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build_pc --verbose + - name: Run tests + run: cargo test_pc --verbose diff --git a/Cargo.lock b/Cargo.lock index da54cce..de1bbff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -26,6 +38,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -60,9 +78,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09dbe0e490df5da9d69b36dca48a76635288a82f92eca90024883a56202026d" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" dependencies = [ "async-trait", "axum-core", @@ -94,9 +112,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c8503f93e6d144ee5690907ba22db7ba79ab001a932ab99034f0fe836b3df" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", @@ -136,9 +154,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bitvec" @@ -196,16 +214,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -301,9 +320,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -343,6 +362,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -364,6 +389,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[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.48", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -383,9 +419,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -396,9 +434,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" dependencies = [ "bytes", "fnv", @@ -418,12 +456,16 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "http" @@ -498,12 +540,11 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", - "futures-channel", "futures-util", "http", "http-body", @@ -511,7 +552,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tracing", ] [[package]] @@ -564,9 +604,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown", @@ -591,18 +631,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libusb1-sys" @@ -618,9 +658,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -720,21 +760,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "orca" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "crc", - "deku", - "env_logger", - "libc", - "log", - "pcap-file", - "thiserror", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -777,18 +802,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -809,9 +834,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "proc-macro-crate" @@ -825,9 +850,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -847,6 +872,79 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rayhunter" +version = "0.1.0" +dependencies = [ + "bytes", + "chrono", + "crc", + "deku", + "env_logger", + "libc", + "log", + "pcap-file", + "thiserror", +] + +[[package]] +name = "rayhunter-daemon" +version = "0.1.0" +dependencies = [ + "axum", + "chrono", + "env_logger", + "futures-core", + "futures-macro", + "include_dir", + "log", + "mime_guess", + "rayhunter", + "serde", + "tempdir", + "thiserror", + "tokio", + "tokio-util", + "toml", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -858,9 +956,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -870,9 +968,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -885,6 +983,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rootshell" version = "0.1.0" @@ -907,11 +1014,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -938,18 +1045,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -958,9 +1065,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", @@ -1025,9 +1132,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -1080,10 +1187,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "termcolor" -version = "1.4.0" +name = "tempdir" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand", + "remove_dir_all", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -1147,6 +1264,8 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", + "hashbrown", "pin-project-lite", "tokio", "tracing", @@ -1154,14 +1273,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -1186,9 +1305,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "serde", @@ -1280,9 +1399,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1290,9 +1409,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -1305,9 +1424,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1315,9 +1434,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -1328,27 +1447,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" - -[[package]] -name = "wavehunter" -version = "0.1.0" -dependencies = [ - "axum", - "env_logger", - "futures-core", - "include_dir", - "log", - "mime_guess", - "orca", - "serde", - "thiserror", - "tokio", - "tokio-util", - "toml", -] +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "winapi" @@ -1524,9 +1625,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.32" +version = "0.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "818ce546a11a9986bc24f93d0cdf38a8a1a400f1473ea8c82e59f6e0ffab9249" dependencies = [ "memchr", ] @@ -1539,3 +1640,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/Cargo.toml b/Cargo.toml index 37d1adf..64bdc8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] members = [ - "orca", + "lib", + "bin", "serial", "rootshell", - "wavehunter", ] resolver = "2" diff --git a/README.md b/README.md index d10ff20..c0cf67a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# Wave Hunter +# Rayhunter ``` -@@@ @@@ @@@ @@@@@@ @@@ @@@ @@@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@@@@@ @@@@@@@@ @@@@@@@ -@@! @@! @@! @@! @@@ @@! @@@ @@! @@! @@@ @@! @@@ @@!@!@@@ @!! @@! @@! @@@ -@!! !!@ @!@ @!@!@!@! @!@ !@! @!!!:! @!@!@!@! @!@ !@! @!@@!!@! @!! @!!!:! @!@!!@! - !: !!: !! !!: !!! !: .:! !!: !!: !!! !!: !!! !!: !!! !!: !!: !!: :!! - ::.: ::: : : : :: : :: :: : : : :.:: : :: : : : :: :: : : : - + @@@@@@@ @@@@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@@@@@ @@@@@@@@ @@@@@@@ + @@! @@@ @@! @@@ @@! !@@ @@! @@@ @@! @@@ @@!@!@@@ @@! @@! @@! @@@ + @!@!!@! @!@!@!@! !@!@! @!@!@!@! @!@ !@! @!@@!!@! @!! @!!!:! @!@!!@! + !!: :!! !!: !!! !!: !!: !!! !!: !!! !!: !!! !!: !!: !!: :!! + : : : : : : .: : : : :.:: : :: : : : :: ::: : : : + + _ _ _ _ _ _ _ _ )`'-.,_)`'-.,_)`'-.,_)`'-.,_)`'-.,_)`'-.,_)`'-.,_)`'-.,_ @@ -24,4 +25,35 @@ _ _ _ _ _ _ _ _ \__; ``` -diag helper binary for the Orbic mobile hotspot. Based on code from [QCSuper](https://github.com/P1sec/QCSuper) +Rayhunter is an IMSI Catcher Catcher for the Orbic mobile hotspot. Based on code from [QCSuper](https://github.com/P1sec/QCSuper) + +**THIS CODE IS PROOF OF CONCEPT AND SHOULD NOT BE RELIED UPON IN HIGH RISK SITUATIONS** + +Code is built and tested for the Orbic RC400L mobile hotspot, it may work on other orbics and other +linux/qualcom devices but this is the only one we have tested on. Buy the orbic [using bezos bucks](https://www.amazon.com/gp/product/B09CLS6Z7X/) + +Root your device on windows using the instructions here: https://xdaforums.com/t/resetting-verizon-orbic-speed-rc400l-firmware-flash-kajeet.4334899/#post-87855183 +(script to root on linux coming soon) + + + +## Setup +on your linux laptop install rust the usual way and then install cross compiling dependences. +run `sudo apt install build-essential libc6-armhf-cross libc6-dev-armhf-cross gcc-arm-linux-gnueabihf` + +set up cross compliing for rust: +``` +rustup target add x86_64-unknown-linux-gnu +rustup target add armv7-unknown-linux-gnueabihf +``` + +Build for arm using `cargo build` + +Run tests using `cargo test_pc` + + +Push the scripts in `scripts/` to /etc/init.d on device and make a directory called /data/rayhunter using `adb shell` (and sshell for your root shell if you followed the steps above) + +you also need to copy `config.toml.example` to /data/rayhunter/config.toml + +Then run ./make.sh this will build the binary and push it over adb. Restart your device or run `/etc/init.d/rayhunter_daemon start` on the device and you are good to go. diff --git a/wavehunter/Cargo.toml b/bin/Cargo.toml similarity index 53% rename from wavehunter/Cargo.toml rename to bin/Cargo.toml index 1d94e20..63f96d0 100644 --- a/wavehunter/Cargo.toml +++ b/bin/Cargo.toml @@ -1,20 +1,25 @@ [package] -name = "wavehunter" +name = "rayhunter-daemon" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "rayhunter" +path = "src/main.rs" [dependencies] -orca = { path = "../orca" } +rayhunter = { path = "../lib" } toml = "0.8.8" serde = { version = "1.0.193", features = ["derive"] } tokio = { version = "1.35.1", features = ["full"] } -axum = "0.7.2" +axum = "0.7.3" futures-core = "0.3.30" thiserror = "1.0.52" log = "0.4.20" env_logger = "0.10.1" -tokio-util = { version = "0.7.10", features = ["io"] } +tokio-util = { version = "0.7.10", features = ["rt"] } +futures-macro = "0.3.30" include_dir = "0.7.3" mime_guess = "2.0.4" +tempdir = "0.3.7" +chrono = { version = "0.4.31", features = ["serde"] } diff --git a/bin/src/config.rs b/bin/src/config.rs new file mode 100644 index 0000000..ef910ac --- /dev/null +++ b/bin/src/config.rs @@ -0,0 +1,54 @@ +use crate::error::RayhunterError; + +use serde::Deserialize; + +#[derive(Deserialize)] +struct ConfigFile { + qmdl_store_path: Option, + port: Option, + readonly_mode: Option, +} + +#[derive(Debug)] +pub struct Config { + pub qmdl_store_path: String, + pub port: u16, + pub readonly_mode: bool, +} + +impl Default for Config { + fn default() -> Self { + Config { + qmdl_store_path: "/data/rayhunter/qmdl".to_string(), + port: 8080, + readonly_mode: false, + } + } +} + +pub fn parse_config

(path: P) -> Result where P: AsRef { + let mut config = Config::default(); + if let Ok(config_file) = std::fs::read_to_string(&path) { + let parsed_config: ConfigFile = toml::from_str(&config_file) + .map_err(RayhunterError::ConfigFileParsingError)?; + if let Some(path) = parsed_config.qmdl_store_path { config.qmdl_store_path = path } + if let Some(port) = parsed_config.port { config.port = port } + if let Some(readonly_mode) = parsed_config.readonly_mode { config.readonly_mode = readonly_mode } + } + Ok(config) +} + +pub struct Args { + pub config_path: String, +} + +pub fn parse_args() -> Args { + let args: Vec = std::env::args().collect(); + if args.len() != 2 { + println!("Usage: {} /path/to/config/file", args[0]); + std::process::exit(1); + } + Args { + config_path: args[1].clone(), + } +} diff --git a/bin/src/diag.rs b/bin/src/diag.rs new file mode 100644 index 0000000..1086ec2 --- /dev/null +++ b/bin/src/diag.rs @@ -0,0 +1,110 @@ +use std::sync::Arc; + +use axum::extract::State; +use axum::http::StatusCode; +use rayhunter::diag_device::DiagDevice; +use rayhunter::diag_reader::DiagReader; +use tokio::sync::RwLock; +use tokio::sync::mpsc::{Receiver, self}; +use rayhunter::qmdl::QmdlWriter; +use log::{debug, info}; +use tokio::sync::mpsc::error::TryRecvError; +use tokio::task::JoinHandle; +use tokio_util::task::TaskTracker; + +use crate::error::RayhunterError; +use crate::qmdl_store::QmdlStore; +use crate::server::ServerState; + +pub enum DiagDeviceCtrlMessage { + StopRecording, + StartRecording(QmdlWriter), + Exit, +} + +pub fn run_diag_read_thread(task_tracker: &TaskTracker, mut dev: DiagDevice, mut qmdl_file_rx: Receiver, qmdl_store_lock: Arc>) -> JoinHandle> { + // mpsc channel for updating QmdlStore entry filesizes. First usize is the + // index, second is the size in bytes + let (tx, mut rx) = mpsc::channel::<(usize, usize)>(1); + + // Spawn a thread to monitor the (usize, usize) channel for updates, + // triggering QmdlStore updates + let qmdl_store_lock_clone = qmdl_store_lock.clone(); + task_tracker.spawn(async move { + while let Some((entry_idx, new_size)) = rx.recv().await { + let mut qmdl_store = qmdl_store_lock_clone.write().await; + qmdl_store.update_entry_size(entry_idx, new_size).await + .expect("failed to update qmdl file size"); + } + info!("QMDL store size updater thread exiting..."); + }); + + // Spawn a thread to drive the DiagDevice reading loop. Since DiagDevice + // works via synchronous I/O, we have to spawn a "blocking" thread to avoid + // gumming up tokio's event loop. + task_tracker.spawn_blocking(move || { + loop { + // First check if we've gotten any control meesages + match qmdl_file_rx.try_recv() { + Ok(DiagDeviceCtrlMessage::StartRecording(qmdl_writer)) => { + dev.qmdl_writer = Some(qmdl_writer); + }, + Ok(DiagDeviceCtrlMessage::StopRecording) => dev.qmdl_writer = None, + // Disconnected means all the Senders have been dropped, so it's + // time to go + Ok(DiagDeviceCtrlMessage::Exit) | Err(TryRecvError::Disconnected) => { + info!("Diag reader thread exiting..."); + return Ok(()) + }, + // empty just means there's no message for us, so continue as normal + Err(TryRecvError::Empty) => {}, + } + + // remember the QmdlStore current entry index so we can update its size later + let qmdl_store_index = qmdl_store_lock.blocking_read().current_entry; + + // TODO: once we're actually doing analysis, we'll wanna use the messages + // returned here. Until then, the DiagDevice has already written those messages + // to the QMDL file, so we can just ignore them. + debug!("reading response from diag device..."); + let _messages = dev.read_response().map_err(RayhunterError::DiagReadError)?; + debug!("got diag response ({} messages)", _messages.len()); + + // keep track of how many bytes were written to the QMDL file so we can read + // a valid block of data from it in the HTTP server + if let Some(qmdl_writer) = dev.qmdl_writer.as_ref() { + debug!("total QMDL bytes written: {}, sending update...", qmdl_writer.total_written); + let index = qmdl_store_index.expect("DiagDevice had qmdl_writer, but QmdlStore didn't have current entry???"); + tx.blocking_send((index, qmdl_writer.total_written)).unwrap(); + debug!("done!"); + } else { + debug!("no qmdl_writer set, continuing..."); + } + } + }) +} + +pub async fn start_recording(State(state): State>) -> Result<(StatusCode, String), (StatusCode, String)> { + if state.readonly_mode { + return Err((StatusCode::FORBIDDEN, format!("server is in readonly mode"))); + } + let mut qmdl_store = state.qmdl_store_lock.write().await; + let qmdl_file = qmdl_store.new_entry().await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't create new qmdl entry: {}", e)))?; + let qmdl_writer = QmdlWriter::new(qmdl_file.into_std().await); + state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StartRecording(qmdl_writer)).await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; + Ok((StatusCode::ACCEPTED, format!("ok"))) +} + +pub async fn stop_recording(State(state): State>) -> Result<(StatusCode, String), (StatusCode, String)> { + if state.readonly_mode { + return Err((StatusCode::FORBIDDEN, format!("server is in readonly mode"))); + } + let mut qmdl_store = state.qmdl_store_lock.write().await; + qmdl_store.close_current_entry().await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't close current qmdl entry: {}", e)))?; + state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; + Ok((StatusCode::ACCEPTED, format!("ok"))) +} diff --git a/wavehunter/src/error.rs b/bin/src/error.rs similarity index 54% rename from wavehunter/src/error.rs rename to bin/src/error.rs index 72200a9..78d15a8 100644 --- a/wavehunter/src/error.rs +++ b/bin/src/error.rs @@ -1,10 +1,10 @@ use thiserror::Error; -use orca::diag_device::DiagDeviceError; +use rayhunter::diag_device::DiagDeviceError; + +use crate::qmdl_store::QmdlStoreError; #[derive(Error, Debug)] -pub enum WavehunterError { - #[error("Missing config file: {0}")] - MissingConfigFile(String), +pub enum RayhunterError{ #[error("Config file parsing error: {0}")] ConfigFileParsingError(#[from] toml::de::Error), #[error("Diag intialization error: {0}")] @@ -13,4 +13,8 @@ pub enum WavehunterError { DiagReadError(DiagDeviceError), #[error("Tokio error: {0}")] TokioError(#[from] tokio::io::Error), + #[error("QmdlStore error: {0}")] + QmdlStoreError(#[from] QmdlStoreError), + #[error("No QMDL store found at path {0}, but can't create a new one due to readonly mode")] + NoStoreReadonlyMode(String), } diff --git a/bin/src/main.rs b/bin/src/main.rs new file mode 100644 index 0000000..55fab69 --- /dev/null +++ b/bin/src/main.rs @@ -0,0 +1,148 @@ +mod config; +mod error; +mod pcap; +mod server; +mod stats; +mod qmdl_store; +mod diag; + +use crate::config::{parse_config, parse_args}; +use crate::diag::run_diag_read_thread; +use crate::qmdl_store::QmdlStore; +use crate::server::{ServerState, get_qmdl, serve_static}; +use crate::pcap::get_pcap; +use crate::stats::get_system_stats; +use crate::error::RayhunterError; + +use axum::response::Redirect; +use diag::{DiagDeviceCtrlMessage, start_recording, stop_recording}; +use log::{info, error}; +use rayhunter::diag_device::DiagDevice; +use axum::routing::{get, post}; +use axum::Router; +use rayhunter::qmdl::QmdlWriter; +use stats::get_qmdl_manifest; +use tokio::sync::mpsc::{self, Sender}; +use tokio::task::JoinHandle; +use tokio_util::task::TaskTracker; +use std::net::SocketAddr; +use tokio::net::TcpListener; +use tokio::sync::{RwLock, oneshot}; +use std::sync::Arc; + +// Runs the axum server, taking all the elements needed to build up our +// ServerState and a oneshot Receiver that'll fire when it's time to shutdown +// (i.e. user hit ctrl+c) +async fn run_server( + task_tracker: &TaskTracker, + config: &config::Config, + qmdl_store_lock: Arc>, + server_shutdown_rx: oneshot::Receiver<()>, + diag_device_sender: Sender +) -> JoinHandle<()> { + let state = Arc::new(ServerState { + qmdl_store_lock, + diag_device_ctrl_sender: diag_device_sender, + readonly_mode: config.readonly_mode, + }); + + let app = Router::new() + .route("/api/pcap/*name", get(get_pcap)) + .route("/api/qmdl/*name", get(get_qmdl)) + .route("/api/system-stats", get(get_system_stats)) + .route("/api/qmdl-manifest", get(get_qmdl_manifest)) + .route("/api/start-recording", post(start_recording)) + .route("/api/stop-recording", post(stop_recording)) + .route("/", get(|| async { Redirect::permanent("/index.html") })) + .route("/*path", get(serve_static)) + .with_state(state); + let addr = SocketAddr::from(([127, 0, 0, 1], config.port)); + let listener = TcpListener::bind(&addr).await.unwrap(); + task_tracker.spawn(async move { + info!("The orca is hunting for stingrays..."); + axum::serve(listener, app) + .with_graceful_shutdown(server_shutdown_signal(server_shutdown_rx)) + .await.unwrap(); + }) +} + +async fn server_shutdown_signal(server_shutdown_rx: oneshot::Receiver<()>) { + server_shutdown_rx.await.unwrap(); + info!("Server received shutdown signal, exiting..."); +} + +// Loads a QmdlStore if one exists, and if not, only create one if we're not in +// readonly mode. +async fn init_qmdl_store(config: &config::Config) -> Result { + match (QmdlStore::exists(&config.qmdl_store_path).await?, config.readonly_mode) { + (true, _) => Ok(QmdlStore::load(&config.qmdl_store_path).await?), + (false, false) => Ok(QmdlStore::create(&config.qmdl_store_path).await?), + (false, true) => Err(RayhunterError::NoStoreReadonlyMode(config.qmdl_store_path.clone())), + } +} + +// Start a thread that'll track when user hits ctrl+c. When that happens, +// trigger various cleanup tasks, including sending signals to other threads to +// shutdown +fn run_ctrl_c_thread( + task_tracker: &TaskTracker, + diag_device_sender: Sender, + server_shutdown_tx: oneshot::Sender<()>, + qmdl_store_lock: Arc> +) -> JoinHandle> { + task_tracker.spawn(async move { + match tokio::signal::ctrl_c().await { + Ok(()) => { + let mut qmdl_store = qmdl_store_lock.write().await; + if qmdl_store.current_entry.is_some() { + info!("Closing current QMDL entry..."); + qmdl_store.close_current_entry().await?; + info!("Done!"); + } + + server_shutdown_tx.send(()) + .expect("couldn't send server shutdown signal"); + diag_device_sender.send(DiagDeviceCtrlMessage::Exit).await + .expect("couldn't send Exit message to diag thread"); + }, + Err(err) => { + error!("Unable to listen for shutdown signal: {}", err); + } + } + Ok(()) + }) +} + +#[tokio::main] +async fn main() -> Result<(), RayhunterError> { + env_logger::init(); + + let args = parse_args(); + let config = parse_config(&args.config_path)?; + + // TaskTrackers give us an interface to spawn tokio threads, and then + // eventually await all of them ending + let task_tracker = TaskTracker::new(); + + let qmdl_store_lock = Arc::new(RwLock::new(init_qmdl_store(&config).await?)); + let (tx, rx) = mpsc::channel::(1); + if !config.readonly_mode { + let qmdl_file = qmdl_store_lock.write().await.new_entry().await?; + let qmdl_writer = QmdlWriter::new(qmdl_file.into_std().await); + let mut dev = DiagDevice::new(Some(qmdl_writer)) + .map_err(RayhunterError::DiagInitError)?; + dev.config_logs() + .map_err(RayhunterError::DiagInitError)?; + + run_diag_read_thread(&task_tracker, dev, rx, qmdl_store_lock.clone()); + } + + let (server_shutdown_tx, server_shutdown_rx) = oneshot::channel::<()>(); + run_ctrl_c_thread(&task_tracker, tx.clone(), server_shutdown_tx, qmdl_store_lock.clone()); + run_server(&task_tracker, &config, qmdl_store_lock.clone(), server_shutdown_rx, tx).await; + + task_tracker.close(); + task_tracker.wait().await; + + Ok(()) +} diff --git a/wavehunter/src/pcap.rs b/bin/src/pcap.rs similarity index 80% rename from wavehunter/src/pcap.rs rename to bin/src/pcap.rs index 290c60d..7694c39 100644 --- a/wavehunter/src/pcap.rs +++ b/bin/src/pcap.rs @@ -1,15 +1,14 @@ use crate::ServerState; -use orca::gsmtap_parser::GsmtapParser; -use orca::pcap::GsmtapPcapWriter; -use orca::qmdl::{QmdlReader, QmdlReaderError}; -use orca::diag_reader::DiagReader; +use rayhunter::gsmtap_parser::GsmtapParser; +use rayhunter::pcap::GsmtapPcapWriter; +use rayhunter::qmdl::{QmdlReader, QmdlReaderError}; +use rayhunter::diag_reader::DiagReader; use axum::body::Body; use axum::http::header::CONTENT_TYPE; -use axum::extract::State; +use axum::extract::{State, Path}; use axum::http::StatusCode; use axum::response::{Response, IntoResponse}; -use std::fs::File; use std::io::Write; use std::pin::Pin; use std::sync::Arc; @@ -22,14 +21,19 @@ use tokio::sync::mpsc; // written so far. This is done by spawning a blocking thread (a tokio thread // capable of handling blocking operations) which streams chunks of pcap data to // a channel that's piped to the client. -pub async fn get_pcap(State(state): State>) -> Result { - let qmdl_bytes_written = *state.qmdl_bytes_written.read().await; - if qmdl_bytes_written == 0 { +pub async fn get_pcap(State(state): State>, Path(qmdl_name): Path) -> Result { + let qmdl_store = state.qmdl_store_lock.read().await; + let entry = qmdl_store.entry_for_name(&qmdl_name) + .ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_name)))?; + if entry.size_bytes == 0 { return Err(( StatusCode::SERVICE_UNAVAILABLE, "QMDL file is empty, try again in a bit!".to_string() )); } + let qmdl_file = qmdl_store.open_entry(&entry).await + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)))? + .into_std().await; let (tx, rx) = mpsc::channel(1); let channel_reader = ChannelReader { rx }; @@ -37,8 +41,7 @@ pub async fn get_pcap(State(state): State>) -> Result, // index into manifest +} + +#[derive(Deserialize, Serialize, Clone, PartialEq, Debug)] +pub struct Manifest { + pub entries: Vec, +} + +#[derive(Deserialize, Serialize, Clone, PartialEq, Debug)] +pub struct ManifestEntry { + pub name: String, + pub start_time: DateTime, + pub end_time: Option>, + pub size_bytes: usize, +} + +impl ManifestEntry { + fn new() -> Self { + let now = Local::now(); + ManifestEntry { + name: format!("{}", now.timestamp()), + start_time: now, + end_time: None, + size_bytes: 0, + } + } +} + +impl QmdlStore { + // Returns whether a directory with a "manifest.toml" exists at the given + // path (though doesn't check if that manifest is valid) + pub async fn exists

(path: P) -> Result where P: AsRef { + let manifest_path = path.as_ref().join("manifest.toml"); + let dir_exists = try_exists(path).await.map_err(QmdlStoreError::OpenDirError)?; + let manifest_exists = try_exists(manifest_path).await.map_err(QmdlStoreError::ReadManifestError)?; + Ok(dir_exists && manifest_exists) + } + + // Loads an existing QmdlStore at the given path. Errors if no store exists, + // or if it's malformed. + pub async fn load

(path: P) -> Result where P: AsRef { + let path: PathBuf = path.as_ref().to_path_buf(); + let manifest = QmdlStore::read_manifest(&path).await?; + Ok(QmdlStore { + path, + manifest, + current_entry: None, + }) + } + + // Creates a new QmdlStore at the given path. This involves creating a dir + // and writing an empty manifest. + pub async fn create

(path: P) -> Result where P: AsRef { + let manifest_path = path.as_ref().join("manifest.toml"); + fs::create_dir_all(&path).await + .map_err(QmdlStoreError::OpenDirError)?; + let mut manifest_file = File::create(&manifest_path).await + .map_err(QmdlStoreError::WriteManifestError)?; + let empty_manifest = Manifest { entries: Vec::new() }; + let empty_manifest_contents = toml::to_string_pretty(&empty_manifest) + .expect("failed to serialize manifest"); + manifest_file.write_all(empty_manifest_contents.as_bytes()).await + .map_err(QmdlStoreError::WriteManifestError)?; + QmdlStore::load(path).await + } + + async fn read_manifest

(path: P) -> Result where P: AsRef { + let manifest_path = path.as_ref().join("manifest.toml"); + let file_contents = fs::read_to_string(&manifest_path).await + .map_err(QmdlStoreError::ReadManifestError)?; + toml::from_str(&file_contents) + .map_err(QmdlStoreError::ParseManifestError) + } + + // Closes the current entry (if needed), creates a new entry based on the + // current time, and updates the manifest + pub async fn new_entry(&mut self) -> Result { + // if we've already got an entry open, close it + if self.current_entry.is_some() { + self.close_current_entry().await?; + } + let new_entry = ManifestEntry::new(); + let mut file_path = self.path.join(&new_entry.name); + file_path.set_extension("qmdl"); + let file = File::options() + .create(true) + .write(true) + .open(&file_path).await + .map_err(QmdlStoreError::CreateFileError)?; + self.manifest.entries.push(new_entry); + self.current_entry = Some(self.manifest.entries.len() - 1); + self.write_manifest().await?; + Ok(file) + } + + // Returns the corresponding QMDL file for a given entry + pub async fn open_entry(&self, entry: &ManifestEntry) -> Result { + let mut file_path = self.path.join(&entry.name); + file_path.set_extension("qmdl"); + File::open(file_path).await + .map_err(QmdlStoreError::ReadFileError) + } + + // Sets the current entry's end_time, updates the manifest, and unsets the + // current entry + pub async fn close_current_entry(&mut self) -> Result<(), QmdlStoreError> { + let entry_index = self.current_entry.take() + .ok_or(QmdlStoreError::NoCurrentEntry)?; + self.manifest.entries[entry_index].end_time = Some(Local::now()); + self.write_manifest().await + } + + // Sets the given entry's size, updating the manifest + pub async fn update_entry_size(&mut self, entry_index: usize, size_bytes: usize) -> Result<(), QmdlStoreError> { + self.manifest.entries[entry_index].size_bytes = size_bytes; + self.write_manifest().await + } + + async fn write_manifest(&mut self) -> Result<(), QmdlStoreError> { + let mut manifest_file = File::options() + .write(true) + .open(self.path.join("manifest.toml")).await + .map_err(QmdlStoreError::WriteManifestError)?; + let manifest_contents = toml::to_string_pretty(&self.manifest) + .expect("failed to serialize manifest"); + manifest_file.write_all(manifest_contents.as_bytes()).await + .map_err(QmdlStoreError::WriteManifestError)?; + Ok(()) + } + + // Finds an entry by filename + pub fn entry_for_name(&self, name: &str) -> Option { + self.manifest.entries.iter() + .find(|entry| entry.name == name) + .cloned() + } +} + +#[cfg(test)] +mod tests { + use tempdir::TempDir; + use super::*; + + #[tokio::test] + async fn test_load_from_empty_dir() { + let dir = TempDir::new("qmdl_store_test").unwrap(); + assert!(!QmdlStore::exists(dir.path()).await.unwrap()); + let _created_store = QmdlStore::create(dir.path()).await.unwrap(); + assert!(QmdlStore::exists(dir.path()).await.unwrap()); + let loaded_store = QmdlStore::load(dir.path()).await.unwrap(); + assert_eq!(loaded_store.manifest.entries.len(), 0); + } + + #[tokio::test] + async fn test_creating_updating_and_closing_entries() { + let dir = TempDir::new("qmdl_store_test").unwrap(); + let mut store = QmdlStore::create(dir.path()).await.unwrap(); + let _ = store.new_entry().await.unwrap(); + let entry_index = store.current_entry.unwrap(); + assert_eq!(QmdlStore::read_manifest(dir.path()).await.unwrap(), store.manifest); + + store.update_entry_size(entry_index, 1000).await.unwrap(); + assert_eq!(store.manifest.entries[entry_index].size_bytes, 1000); + assert_eq!(QmdlStore::read_manifest(dir.path()).await.unwrap(), store.manifest); + + assert!(store.manifest.entries[entry_index].end_time.is_none()); + store.close_current_entry().await.unwrap(); + let entry = store.entry_for_name(&store.manifest.entries[entry_index].name).unwrap(); + assert!(entry.end_time.is_some()); + assert_eq!(QmdlStore::read_manifest(dir.path()).await.unwrap(), store.manifest); + + assert!(matches!(store.close_current_entry().await, Err(QmdlStoreError::NoCurrentEntry))); + } + + #[tokio::test] + async fn test_repeated_new_entries() { + let dir = TempDir::new("qmdl_store_test").unwrap(); + let mut store = QmdlStore::create(dir.path()).await.unwrap(); + let _ = store.new_entry().await.unwrap(); + let entry_index = store.current_entry.unwrap(); + let _ = store.new_entry().await.unwrap(); + let new_entry_index = store.current_entry.unwrap(); + assert_ne!(entry_index, new_entry_index); + assert_eq!(store.manifest.entries.len(), 2); + } +} diff --git a/wavehunter/src/server.rs b/bin/src/server.rs similarity index 68% rename from wavehunter/src/server.rs rename to bin/src/server.rs index affa28d..783130d 100644 --- a/wavehunter/src/server.rs +++ b/bin/src/server.rs @@ -5,22 +5,28 @@ use axum::http::{StatusCode, HeaderValue}; use axum::response::{Response, IntoResponse}; use axum::extract::Path; use tokio::io::AsyncReadExt; +use tokio::sync::mpsc::Sender; use std::sync::Arc; use tokio::sync::RwLock; -use tokio::fs::File as AsyncFile; use tokio_util::io::ReaderStream; use include_dir::{include_dir, Dir}; +use crate::DiagDeviceCtrlMessage; +use crate::qmdl_store::QmdlStore; + pub struct ServerState { - pub qmdl_bytes_written: Arc>, - pub qmdl_path: String, + pub qmdl_store_lock: Arc>, + pub diag_device_ctrl_sender: Sender, + pub readonly_mode: bool, } -pub async fn get_qmdl(State(state): State>) -> Result { - let qmdl_file = AsyncFile::open(&state.qmdl_path).await +pub async fn get_qmdl(State(state): State>, Path(qmdl_name): Path) -> Result { + let qmdl_store = state.qmdl_store_lock.read().await; + let entry = qmdl_store.entry_for_name(&qmdl_name) + .ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_name)))?; + let qmdl_file = qmdl_store.open_entry(&entry).await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("error opening QMDL file: {}", e)))?; - let qmdl_bytes_written = *state.qmdl_bytes_written.read().await; - let limited_qmdl_file = qmdl_file.take(qmdl_bytes_written as u64); + let limited_qmdl_file = qmdl_file.take(entry.size_bytes as u64); let qmdl_stream = ReaderStream::new(limited_qmdl_file); let headers = [(CONTENT_TYPE, "application/octet-stream")]; diff --git a/wavehunter/src/stats.rs b/bin/src/stats.rs similarity index 83% rename from wavehunter/src/stats.rs rename to bin/src/stats.rs index 9ccc0fd..3ddd088 100644 --- a/wavehunter/src/stats.rs +++ b/bin/src/stats.rs @@ -1,10 +1,11 @@ +use std::sync::Arc; + +use crate::qmdl_store::ManifestEntry; use crate::server::ServerState; use axum::Json; use axum::extract::State; use axum::http::StatusCode; -use axum::response::IntoResponse; -use std::sync::Arc; use log::error; use serde::Serialize; use tokio::process::Command; @@ -97,7 +98,8 @@ fn humanize_kb(kb: usize) -> String { } pub async fn get_system_stats(State(state): State>) -> Result, (StatusCode, String)> { - match SystemStats::new(&state.qmdl_path).await { + let qmdl_store = state.qmdl_store_lock.read().await; + match SystemStats::new(qmdl_store.path.to_str().unwrap()).await { Ok(stats) => Ok(Json(stats)), Err(err) => { error!("error getting system stats: {}", err); @@ -109,13 +111,18 @@ pub async fn get_system_stats(State(state): State>) -> Result, + pub current_entry: Option, } -pub async fn get_diag_stats(State(state): State>) -> impl IntoResponse { - Json(DiagStats { - bytes_written: *state.qmdl_bytes_written.read().await, - }) +pub async fn get_qmdl_manifest(State(state): State>) -> Result, (StatusCode, String)> { + let qmdl_store = state.qmdl_store_lock.read().await; + let mut entries = qmdl_store.manifest.entries.clone(); + let current_entry = qmdl_store.current_entry.map(|index| entries.remove(index)); + Ok(Json(ManifestStats { + entries, + current_entry, + })) } diff --git a/bin/static/css/style.css b/bin/static/css/style.css new file mode 100644 index 0000000..de89e27 --- /dev/null +++ b/bin/static/css/style.css @@ -0,0 +1,40 @@ +td, +th { + border: 1px solid rgb(190, 190, 190); + padding: 10px; +} + +td { + text-align: center; +} + +tr:nth-child(even) { + background-color: #eee; +} + +th[scope='col'] { + background-color: #696969; + color: #fff; +} + +th[scope='row'] { + background-color: #d7d9f2; +} + +tr.current { + background-color: #fe537b; + font-weight: bold; +} + +caption { + padding: 10px; + caption-side: bottom; +} + +table { + border-collapse: collapse; + border: 2px solid rgb(200, 200, 200); + letter-spacing: 1px; + font-family: sans-serif; + font-size: 0.8rem; +} diff --git a/bin/static/index.html b/bin/static/index.html new file mode 100644 index 0000000..3ba9b67 --- /dev/null +++ b/bin/static/index.html @@ -0,0 +1,38 @@ + + + rayhunter + + + + + +

+ + +
+ + + + + + + + + + + +
NameDate StartedDate StoppedSize (bytes)PCAPQMDL
+
+

System stats

+
Loading...
+
+ + diff --git a/bin/static/js/main.js b/bin/static/js/main.js new file mode 100644 index 0000000..b8885dd --- /dev/null +++ b/bin/static/js/main.js @@ -0,0 +1,90 @@ +async function populateDivs() { + const systemStats = await getSystemStats(); + const systemStatsDiv = document.getElementById('system-stats'); + systemStatsDiv.innerHTML = JSON.stringify(systemStats, null, 2); + + const qmdlManifest = await getQmdlManifest(); + updateQmdlManifestTable(qmdlManifest); +} + +function updateQmdlManifestTable(manifest) { + const table = document.getElementById('qmdl-manifest-table'); + const numRows = table.rows.length; + for (let i=1; i= 200 && response.status < 300) { + return body; + } else { + throw new Error(body); + } +} diff --git a/config.toml.example b/config.toml.example new file mode 100644 index 0000000..cd911e4 --- /dev/null +++ b/config.toml.example @@ -0,0 +1,4 @@ +# cat config.toml +qmdl_store_path = "/data/rayhunter/qmdl" +port = 8080 +readonly_mode = false diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..05dbd4d --- /dev/null +++ b/install.sh @@ -0,0 +1,20 @@ +cargo build --release +# Force a switch into the debug mode to enable ADB +target/x86_64-unknown-linux-gnu/release/serial AT +echo -n "device rooted, waiting for reboot" +until adb shell true +do + echo -n . + sleep 1 +done +echo +echo "it's alive!" +adb push target/armv7-unknown-linux-gnueabihf/release/rootshell /tmp/ +target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=mv /tmp/rootshell /bin/rootshell" +sleep 1 +target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=chown root /bin/rootshell" +sleep 1 +target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=chmod 4755 /bin/rootshell" +echo "we have root!" +adb shell id +adb push target/armv7-unknown-linux-gnueabihf/release/rayhunter /data/rayhunter/rayhunter diff --git a/orca/Cargo.toml b/lib/Cargo.toml similarity index 70% rename from orca/Cargo.toml rename to lib/Cargo.toml index 085ee12..a3228a1 100644 --- a/orca/Cargo.toml +++ b/lib/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "orca" +name = "rayhunter" version = "0.1.0" edition = "2021" -description = "Orbic Realtime Cellular Analysis" +description = "Realtime cellular data decoding and analysis for IMSI catcher detection" [dependencies] bytes = "1.5.0" diff --git a/orca/src/diag.rs b/lib/src/diag.rs similarity index 100% rename from orca/src/diag.rs rename to lib/src/diag.rs diff --git a/orca/src/diag_device.rs b/lib/src/diag_device.rs similarity index 90% rename from orca/src/diag_device.rs rename to lib/src/diag_device.rs index 1b1d471..4e456f6 100644 --- a/orca/src/diag_device.rs +++ b/lib/src/diag_device.rs @@ -71,7 +71,7 @@ const DIAG_IOCTL_SWITCH_LOGGING: u64 = 7; pub struct DiagDevice { file: File, - pub qmdl_writer: QmdlWriter, + pub qmdl_writer: Option>, fully_initialized: bool, read_buf: Vec, use_mdm: i32, @@ -92,16 +92,18 @@ impl DiagReader for DiagDevice { warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len()); } - if self.fully_initialized { - self.qmdl_writer.write_container(&container) - .map_err(DiagDeviceError::QmdlFileWriteError)?; + if let Some(qmdl_writer) = self.qmdl_writer.as_mut() { + if self.fully_initialized { + qmdl_writer.write_container(&container) + .map_err(DiagDeviceError::QmdlFileWriteError)?; + } } Ok(container) } } impl DiagDevice { - pub fn new

(qmdl_path: P) -> DiagResult where P: AsRef { + pub fn new(qmdl_writer: Option>) -> DiagResult { let diag_file = std::fs::File::options() .read(true) .write(true) @@ -109,21 +111,6 @@ impl DiagDevice { .map_err(DiagDeviceError::OpenDiagDeviceError)?; let fd = diag_file.as_raw_fd(); - let qmdl_file = File::options() - .create(true) - .append(true) - .open(&qmdl_path) - .map_err(DiagDeviceError::OpenQmdlFileError)?; - let qmdl_metadata = qmdl_file.metadata().map_err(DiagDeviceError::OpenQmdlFileError)?; - if qmdl_metadata.len() != 0 { - info!( - "QMDL file {} already contains data ({} bytes), appending to it", - qmdl_path.as_ref().display(), - qmdl_metadata.len() - ); - } - let qmdl_writer = QmdlWriter::new_with_existing_size(qmdl_file, qmdl_metadata.len() as usize); - enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?; let use_mdm = determine_use_mdm(fd)?; diff --git a/orca/src/diag_reader.rs b/lib/src/diag_reader.rs similarity index 100% rename from orca/src/diag_reader.rs rename to lib/src/diag_reader.rs diff --git a/orca/src/gsmtap.rs b/lib/src/gsmtap.rs similarity index 100% rename from orca/src/gsmtap.rs rename to lib/src/gsmtap.rs diff --git a/orca/src/gsmtap_parser.rs b/lib/src/gsmtap_parser.rs similarity index 100% rename from orca/src/gsmtap_parser.rs rename to lib/src/gsmtap_parser.rs diff --git a/orca/src/hdlc.rs b/lib/src/hdlc.rs similarity index 100% rename from orca/src/hdlc.rs rename to lib/src/hdlc.rs diff --git a/orca/src/lib.rs b/lib/src/lib.rs similarity index 100% rename from orca/src/lib.rs rename to lib/src/lib.rs diff --git a/orca/src/log_codes.rs b/lib/src/log_codes.rs similarity index 100% rename from orca/src/log_codes.rs rename to lib/src/log_codes.rs diff --git a/orca/src/pcap.rs b/lib/src/pcap.rs similarity index 100% rename from orca/src/pcap.rs rename to lib/src/pcap.rs diff --git a/orca/src/qmdl.rs b/lib/src/qmdl.rs similarity index 100% rename from orca/src/qmdl.rs rename to lib/src/qmdl.rs diff --git a/orca/tests/test_lte_parsing.rs b/lib/tests/test_lte_parsing.rs similarity index 99% rename from orca/tests/test_lte_parsing.rs rename to lib/tests/test_lte_parsing.rs index 48a219c..ed449b8 100644 --- a/orca/tests/test_lte_parsing.rs +++ b/lib/tests/test_lte_parsing.rs @@ -1,10 +1,10 @@ -use orca::diag::{ +use rayhunter::diag::{ Message, LogBody, LteRrcOtaPacket, Timestamp, }; -use orca::gsmtap_parser::GsmtapParser; +use rayhunter::gsmtap_parser::GsmtapParser; use deku::prelude::*; // Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py diff --git a/make.sh b/make.sh index 1f46e64..e343dde 100755 --- a/make.sh +++ b/make.sh @@ -1,10 +1,2 @@ cargo build --release -# Force a switch into the debug mode to enable ADB -target/x86_64-unknown-linux-gnu/release/serial AT -adb push target/armv7-unknown-linux-gnueabihf/release/rootshell /tmp/ -target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=mv /tmp/rootshell /bin/rootshell" -sleep 1 -target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=chown root /bin/rootshell" -sleep 1 -target/x86_64-unknown-linux-gnu/release/serial "AT+SYSCMD=chmod 4755 /bin/rootshell" -adb push target/armv7-unknown-linux-gnueabihf/release/wavehunter /data/wavehunter/wavehunter +adb push target/armv7-unknown-linux-gnueabihf/release/rayhunter /data/rayhunter/rayhunter diff --git a/scripts/misc-daemon b/scripts/misc-daemon index 4181f5a..f5970b9 100644 --- a/scripts/misc-daemon +++ b/scripts/misc-daemon @@ -41,9 +41,9 @@ case "$1" in /etc/init.d/start_atfwd_daemon start fi - if [ -f /etc/init.d/wavehunter_daemon ] + if [ -f /etc/init.d/rayhunter_daemon ] then - /etc/init.d/wavehunter_daemon start + /etc/init.d/rayhunter_daemon start fi if [ -f /etc/init.d/start_stop_qti_ppp_le ] @@ -72,9 +72,9 @@ case "$1" in /etc/init.d/start_loc_launcher stop fi - if [ -f /etc/init.d/wavehunter_daemon ] + if [ -f /etc/init.d/rayhunter_daemon ] then - /etc/init.d/wavehunter_daemon stop + /etc/init.d/rayhunter_daemon stop fi if [ -f /etc/init.d/init_qcom_audio ] diff --git a/scripts/rayhunter_daemon b/scripts/rayhunter_daemon new file mode 100644 index 0000000..d190b73 --- /dev/null +++ b/scripts/rayhunter_daemon @@ -0,0 +1,27 @@ +#! /bin/sshell + +set -e + +case "$1" in +start) + echo -n "Starting rayhunter: " + start-stop-daemon -S -b --make-pidfile --pidfile /tmp/rayhunter.pid \ + --startas /bin/bash -- -c "exec /data/rayhunter/rayhunter /data/rayhunter/config.toml > /data/rayhunter/rayhunter.log 2>&1" + echo "done" + ;; + stop) + echo -n "Stopping rayhunter: " + start-stop-daemon -K -p /tmp/rayhunter.pid + echo "done" + ;; + restart) + $0 stop + $0 start + ;; + *) + echo "Usage rayhunter_daemon { start | stop | restart }" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/scripts/wavehunter_daemon b/scripts/wavehunter_daemon deleted file mode 100644 index da469c4..0000000 --- a/scripts/wavehunter_daemon +++ /dev/null @@ -1,27 +0,0 @@ -#! /bin/sshell - -set -e - -case "$1" in -start) - echo -n "Starting wavehunter: " - start-stop-daemon -S -b --make-pidfile --pidfile /tmp/wavehunter.pid \ - --startas /bin/bash -- -c "exec /data/wavehunter/wavehunter > /data/wavehunter/wavehunter.log 2>&1" - echo "done" - ;; - stop) - echo -n "Stopping wavehunter: " - start-stop-daemon -K -p /tmp/wavehunter.pid - echo "done" - ;; - restart) - $0 stop - $0 start - ;; - *) - echo "Usage atfwd_daemon{ start | stop | restart }" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/wavehunter/src/config.rs b/wavehunter/src/config.rs deleted file mode 100644 index d8280f8..0000000 --- a/wavehunter/src/config.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::error::WavehunterError; - -use serde::Deserialize; - -#[derive(Deserialize)] -struct ConfigFile { - qmdl_path: Option, - port: Option, - debug_mode: Option, -} - -#[derive(Debug)] -pub struct Config { - pub qmdl_path: String, - pub port: u16, - pub debug_mode: bool, -} - -impl Default for Config { - fn default() -> Self { - Config { - qmdl_path: "./wavehunter.qmdl".to_string(), - port: 8080, - debug_mode: false, - } - } -} - -pub fn parse_config

(path: P) -> Result where P: AsRef { - let config_file = std::fs::read_to_string(&path) - .map_err(|_| WavehunterError::MissingConfigFile(format!("{:?}", path.as_ref())))?; - let parsed_config: ConfigFile = toml::from_str(&config_file) - .map_err(WavehunterError::ConfigFileParsingError)?; - let mut config = Config::default(); - if let Some(path) = parsed_config.qmdl_path { config.qmdl_path = path } - if let Some(port) = parsed_config.port { config.port = port } - if let Some(debug_mode) = parsed_config.debug_mode { config.debug_mode = debug_mode } - Ok(config) -} - -pub struct Args { - pub config_path: String, -} - -pub fn parse_args() -> Args { - let args: Vec = std::env::args().collect(); - if args.len() != 2 { - println!("Usage: {} /path/to/config/file", args[0]); - std::process::exit(1); - } - Args { - config_path: args[1].clone(), - } -} diff --git a/wavehunter/src/main.rs b/wavehunter/src/main.rs deleted file mode 100644 index 404d465..0000000 --- a/wavehunter/src/main.rs +++ /dev/null @@ -1,92 +0,0 @@ -mod config; -mod error; -mod pcap; -mod server; -mod stats; - -use crate::config::{parse_config, parse_args}; -use crate::server::{ServerState, get_qmdl, serve_static}; -use crate::pcap::get_pcap; -use crate::stats::{get_system_stats, get_diag_stats}; -use crate::error::WavehunterError; - -use axum::response::Redirect; -use orca::diag_device::DiagDevice; -use orca::diag_reader::DiagReader; -use axum::routing::get; -use axum::Router; -use tokio::fs::File; -use log::debug; -use std::net::SocketAddr; -use tokio::net::TcpListener; -use tokio::sync::RwLock; -use tokio::task::JoinHandle; -use std::sync::Arc; - -fn run_diag_read_thread(mut dev: DiagDevice, bytes_read_lock: Arc>) -> JoinHandle> { - tokio::task::spawn_blocking(move || { - loop { - // TODO: once we're actually doing analysis, we'll wanna use the messages - // returned here. Until then, the DiagDevice has already written those messages - // to the QMDL file, so we can just ignore them. - debug!("reading response from diag device..."); - let _messages = dev.read_response().map_err(WavehunterError::DiagReadError)?; - debug!("got diag response ({} messages)", _messages.len()); - - // keep track of how many bytes were written to the QMDL file so we can read - // a valid block of data from it in the HTTP server - debug!("total QMDL bytes written: {}, updating state...", dev.qmdl_writer.total_written); - let mut bytes_read = bytes_read_lock.blocking_write(); - *bytes_read = dev.qmdl_writer.total_written; - debug!("done!"); - } - }) -} - -async fn run_server(config: &config::Config, qmdl_bytes_written: Arc>) -> Result<(), WavehunterError> { - let state = Arc::new(ServerState { - qmdl_bytes_written, - qmdl_path: config.qmdl_path.clone(), - }); - - let app = Router::new() - .route("/api/pcap/latest.pcap", get(get_pcap)) - .route("/api/qmdl/latest.qmdl", get(get_qmdl)) - .route("/api/system-stats", get(get_system_stats)) - .route("/api/diag-stats", get(get_diag_stats)) - .route("/", get(|| async { Redirect::permanent("/index.html") })) - .route("/*path", get(serve_static)) - .with_state(state); - let addr = SocketAddr::from(([127, 0, 0, 1], config.port)); - let listener = TcpListener::bind(&addr).await.unwrap(); - axum::serve(listener, app).await.unwrap(); - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<(), WavehunterError> { - env_logger::init(); - - let args = parse_args(); - let config = parse_config(&args.config_path)?; - - let qmdl_bytes_lock: Arc>; - if !config.debug_mode { - let mut dev = DiagDevice::new(&config.qmdl_path) - .map_err(WavehunterError::DiagInitError)?; - dev.config_logs() - .map_err(WavehunterError::DiagInitError)?; - qmdl_bytes_lock = Arc::new(RwLock::new(dev.qmdl_writer.total_written)); - - // TODO: handle exiting gracefully - let _read_thread_handle = run_diag_read_thread(dev, qmdl_bytes_lock.clone()); - } else { - let qmdl_file = File::open(&config.qmdl_path).await.expect("couldn't open QMDL file"); - let qmdl_file_size = qmdl_file.metadata().await.expect("couldn't get QMDL file metadata") - .len() as usize; - qmdl_bytes_lock = Arc::new(RwLock::new(qmdl_file_size)); - } - - println!("The orca is hunting for stingrays..."); - run_server(&config, qmdl_bytes_lock).await -} diff --git a/wavehunter/static/css/style.css b/wavehunter/static/css/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/wavehunter/static/index.html b/wavehunter/static/index.html deleted file mode 100644 index bc2d433..0000000 --- a/wavehunter/static/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - ORCA - - - - - -

-
Loading...
-
Loading...
- - diff --git a/wavehunter/static/js/main.js b/wavehunter/static/js/main.js deleted file mode 100644 index de2f9ee..0000000 --- a/wavehunter/static/js/main.js +++ /dev/null @@ -1,24 +0,0 @@ -async function populateDivs() { - const systemStats = await getSystemStats(); - const diagStats = await getDiagStats(); - - const systemStatsDiv = document.getElementById('system-stats'); - const diagStatsDiv = document.getElementById('diag-stats'); - - systemStatsDiv.innerHTML = JSON.stringify(systemStats, null, 2); - diagStatsDiv.innerHTML = JSON.stringify(diagStats, null, 2); -} - -async function getSystemStats() { - return await getJson('/api/system-stats'); -} - -async function getDiagStats() { - return await getJson('/api/diag-stats'); -} - -async function getJson(url) { - const response = await fetch(url); - const data = await response.json(); - return data; -}