From 536f02368b064702b54bd852c2a35b2a4f6dcfb9 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:28:10 +0200 Subject: [PATCH 01/18] =?UTF-8?q?Projekt=20von=20rust-template=20zu=20mac2?= =?UTF-8?q?ip=20umbenennen,=20Abh=C3=A4ngigkeiten=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo.toml/Cargo.lock: Paketname, Version, Beschreibung und Paketierungs- Metadaten (deb/rpm/arch) auf mac2ip angepasst; neue Laufzeit-Abhängigkeiten (tokio, turso, clap, serde, serde_json, thiserror) für die kommende Implementierung ergänzt. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0118Wbg95ADSDynYfci2qUjK --- Cargo.lock | 3430 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 67 +- 2 files changed, 3472 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad94a61..08a3d0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5 +3,3431 @@ version = 4 [[package]] -name = "rust-template" -version = "0.0.0" +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aegis" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58541132f980da31e9aa99f7bdee69bc84bf1e168b9b91ef2dbe8abb7b4ce5dd" +dependencies = [ + "cc", + "softaes", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc 0.2.189", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "antithesis_sdk" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08410fcac93669a476c006cd6c4512ac1e2b30fd117231a5d55d8a2c76599b82" +dependencies = [ + "libc 0.2.189", + "libloading", + "linkme", + "once_cell", + "rand 0.8.8", + "rustc_version_runtime", + "serde", + "serde_json", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "aristo" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdeefa800110050103e2459a2f8dbdbd560ad046e30f1f87e24ce19c2cb8bde" +dependencies = [ + "aristo-macros", +] + +[[package]] +name = "aristo-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64a66d21a80182b35b1741997a6d2456911f54b7eb1918aa4e2382fc205268d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "assoc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdc70193dadb9d7287fa4b633f15f90c876915b31f6af17da307fc59c9859a8" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bigdecimal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease 0.2.37", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex 1.3.0", + "syn 2.0.119", + "which", +] + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "bitpacking" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a7139abd3d9cebf8cd6f920a389cf3dc9576172e32f4563f188cae3c3eb019" +dependencies = [ + "crunchy", +] + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bon" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60eafe0d77c3a2fc292c1d1346c3041b33c0a108085a2afabf672b70f69dbbc9" +dependencies = [ + "bon-macros", +] + +[[package]] +name = "bon-macros" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0f9631d8aaaee112c41985d675ef269e02acbd4f33122836af4f0c5f699ff6" +dependencies = [ + "darling", + "ident_case", + "prettyplease 0.3.0", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "branches" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7436b21fd6195415058eb41c9090e156b556bc7e0377bb57c5c026a421521525" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc 0.2.189", + "shlex 2.0.1", +] + +[[package]] +name = "census" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "cfg_block" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18758054972164c3264f7c8386f5fc6da6114cb46b619fd365d4e3b2dc3ae487" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc 0.2.189", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config-ctdra" +version = "1.0.6" +source = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" +checksum = "83eb23d473fce79e8234ad66baf210289b5c9e4c4a587273b4df7deceb73f416" +dependencies = [ + "confy", + "program-ctdra", + "serde", + "sudo-ctdra", +] + +[[package]] +name = "confy" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8807c397789cbe02bbdb1a27ea5f345584132808697b2a3f957c829829ee4814" +dependencies = [ + "etcetera", + "lazy_static", + "serde", + "thiserror", + "toml", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc 0.2.189", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc 0.2.189", +] + +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.5", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "datasketches" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c286de4e81ea2590afc24d754e0f83810c566f50a1388fa75ebd57928c0d9745" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "env_filter" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" +dependencies = [ + "env_filter", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc 0.2.189", + "windows-sys 0.61.2", +] + +[[package]] +name = "etcetera" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.59.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fastbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" +dependencies = [ + "getrandom 0.3.4", + "libm", + "siphasher", +] + +[[package]] +name = "fastdivide" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "fs4" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" +dependencies = [ + "rustix 1.1.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "generator" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +dependencies = [ + "cc", + "cfg-if", + "libc 0.2.189", + "log", + "rustversion", + "windows-link", + "windows-result", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc 0.2.189", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc 0.2.189", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc 0.2.189", + "r-efi 6.0.0", + "rand_core 0.10.1", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2 0.2.21", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "htmlescape" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collator" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08984ed58ac439ebf3e13d2cf26b0c46a60afcd21721c1d14087c0b240344dda" +dependencies = [ + "icu_collator_data", + "icu_collections", + "icu_locale_core", + "icu_locale_fallback", + "icu_normalizer", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_collator_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d7e54efdddeb1208c08dd5d32b53a879ac00d2d3d051b2255fd26821d16368" + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "785f595c61ef57169a467eeed7b4b6936a66f6cacbb116a96ee86da983251bf4" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_locale_data", + "icu_locale_fallback", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "serde", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locale_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37d91460e362a5cf58907cd7ce871411775ee6294e49a5cc8e6cec16dfa75ea" + +[[package]] +name = "icu_locale_fallback" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251af8e57c9400e3eb58242fe5b8b1152b2a64fdf4cf632f923c38ccee6f2fa9" +dependencies = [ + "icu_locale_core", + "icu_locale_fallback_data", + "icu_provider", + "potential_utf", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locale_fallback_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf2a22ec8fa68f1a0c1129a3f8583f8f8bc24e8b9ccbe98ead99f62a4dc3a8" + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset", +] + +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "io-uring" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3bd0ecfbb87805f538bb7b32e5239ca0763890c623e349860ecba69469f2bb" +dependencies = [ + "bitflags", + "cfg-if", + "libc 0.2.189", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc 0.2.189", +] + +[[package]] +name = "js-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "levenshtein_automata" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libc" +version = "1.0.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0f24f33af482526a4e3f9b47f0abb2c6377a1713c8aa4a8106994689a4cfa5" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libmimalloc-sys" +version = "0.1.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" +dependencies = [ + "cc", +] + +[[package]] +name = "linkme" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3045e122bd98aef8ec3ad58ce84f0791f64e70163d1a02710af4aa11a4d54cc5" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77060ebe535362c3da75682cd17b0431017b6e7c5661e714fc69a7ad017d1301" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "logger-ctdra" +version = "1.0.5" +source = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" +checksum = "ce612b7d943b77060fa36eab0c85782e650c4cf023e9d560bef791951ed92cad" +dependencies = [ + "program-ctdra", + "time", +] + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "lz4_flex" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e" + +[[package]] +name = "mac2ip" +version = "0.1.0" +dependencies = [ + "clap", + "config-ctdra", + "logger-ctdra", + "program-ctdra", + "serde", + "serde_json", + "sudo-ctdra", + "thiserror", + "tokio", + "turso", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "measure_time" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c55d61e72fc3ab704396c5fa16f4c184db37978ae4e94ca8959693a235fc0e" +dependencies = [ + "log", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memmap2" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +dependencies = [ + "libc 0.2.189", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "miette-derive", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "mimalloc" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" +dependencies = [ + "libmimalloc-sys", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc 0.2.189", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "murmurhash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc 0.2.189", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oneshot" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269bca4c2591a28585d6bf10d9ed0332b7d76900a1b02bec41bdc3a2cdcda107" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "ordered-float" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ownedbytes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fbd56f7631767e61784dc43f8580f403f4475bd4aaa4da003e6295e1bab4a7e" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pack1" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b7bb0ecf2e447b1f20ee94ee79ef6eed1e9d4b3c36ce1903b9dea3bf205523" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc 0.2.189", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "serde_core", + "writeable", + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.119", +] + +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn 3.0.5", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "program-ctdra" +version = "1.0.1" +source = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" +checksum = "528d5771916f74bdffb77ad60ea00c0ae4bd85f9a84e920cd65730a082133d67" + +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc 0.2.189", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rapidhash" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "roaring" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bd8a37d17a58532776dcdf6041ce64929adca78e8489d5cacbafe99229d3e1" +dependencies = [ + "bytemuck", + "byteorder", +] + +[[package]] +name = "rust-stemmers" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd18cd2bae1820af0b6ad5e54f4a51d0f3fcc53b05f845675074efcc7af071d" +dependencies = [ + "rustc_version", + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc 0.2.189", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc 0.2.189", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "shuttle" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab17edba38d63047f46780cf7360acf7467fec2c048928689a5c1dd1c2b4e31" +dependencies = [ + "assoc", + "bitvec", + "cfg-if", + "generator", + "hex", + "owo-colors", + "rand 0.8.8", + "rand_core 0.6.4", + "rand_pcg", + "scoped-tls", + "smallvec", + "tracing", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc 0.2.189", +] + +[[package]] +name = "simsimd" +version = "6.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fb3bc3cdce07a7d7d4caa4c54f8aa967f6be41690482b54b24100a2253fa70" +dependencies = [ + "cc", +] + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "sketches-ddsketch" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513c3f5f732bfd6fbb187619c2dfe9d2f25f1a2976f01d575f0fd329d565df56" +dependencies = [ + "serde", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" + +[[package]] +name = "softaes" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e14297decde697ddf377c25752aead0927d5cfc89c2684d2af96901a4ceeea" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.119", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sudo-ctdra" +version = "1.0.1" +source = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" +checksum = "20e576be60eb2050d475d0fbe46f0d09462ba9985ab55c7833e0c2e6d116d341" +dependencies = [ + "libc 1.0.0-alpha.4", +] + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tantivy" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "861facfabd71044968f364837f9a083b56464ba5a59079f88706ee5c451ca069" +dependencies = [ + "aho-corasick", + "arc-swap", + "base64", + "bitpacking", + "bon", + "byteorder", + "census", + "crc32fast", + "crossbeam-channel", + "datasketches", + "downcast-rs", + "fastdivide", + "fnv", + "fs4", + "htmlescape", + "itertools 0.14.0", + "levenshtein_automata", + "log", + "lru", + "lz4_flex", + "measure_time", + "memmap2", + "once_cell", + "oneshot", + "rayon", + "regex", + "rust-stemmers", + "rustc-hash 2.1.3", + "serde", + "serde_json", + "sketches-ddsketch", + "smallvec", + "tantivy-bitpacker", + "tantivy-columnar", + "tantivy-common", + "tantivy-fst", + "tantivy-query-grammar", + "tantivy-stacker", + "tantivy-tokenizer-api", + "tempfile", + "thiserror", + "time", + "typetag", + "uuid", + "winapi", +] + +[[package]] +name = "tantivy-bitpacker" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fed3d674429bcd2de5d0a6d1aa5495fed8afd9c5ecce993019caf7615f53fa4" +dependencies = [ + "bitpacking", +] + +[[package]] +name = "tantivy-columnar" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57166f5bcfd478f370ab8445afb4678dce44801fa5ce5c451aaf8595583c5dc" +dependencies = [ + "downcast-rs", + "fastdivide", + "itertools 0.14.0", + "serde", + "tantivy-bitpacker", + "tantivy-common", + "tantivy-sstable", + "tantivy-stacker", +] + +[[package]] +name = "tantivy-common" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf10915aa75da3c3b0d58b58853d2e889efbaf32d4982a4c3715dde6bba23e5" +dependencies = [ + "async-trait", + "byteorder", + "ownedbytes", + "serde", + "time", +] + +[[package]] +name = "tantivy-fst" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" +dependencies = [ + "byteorder", + "regex-syntax", + "utf8-ranges", +] + +[[package]] +name = "tantivy-query-grammar" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfadb8526b6da90704feb293b0701a6aae62ea14983143344be2dc5ce30f1d82" +dependencies = [ + "fnv", + "nom", + "ordered-float", + "serde", + "serde_json", +] + +[[package]] +name = "tantivy-sstable" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2cfc3ac5164cbadc28965ffb145a8f47582a60ae5897859ad8d4316596c606" +dependencies = [ + "futures-util", + "itertools 0.14.0", + "tantivy-bitpacker", + "tantivy-common", + "tantivy-fst", + "zstd", +] + +[[package]] +name = "tantivy-stacker" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbb051742da9d53ca9e8fff43a9b10e319338b24e2c0e15d0372df19ffeb951" +dependencies = [ + "murmurhash32", + "tantivy-common", +] + +[[package]] +name = "tantivy-tokenizer-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac258c2c6390673f2685813afeeafcb8c4e0ee7de8dd3fc46838dcc37263f98" +dependencies = [ + "serde", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "libc 0.2.189", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc 0.2.189", + "mio", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" +dependencies = [ + "crossbeam-channel", + "symlink", + "thiserror", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +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.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "turso" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9491d7a80312c5abe66a4409e4dce02065503a235453c94b9e4133877e39ffc" +dependencies = [ + "mimalloc", + "thiserror", + "tracing", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sync_sdk_kit", +] + +[[package]] +name = "turso_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a833cc3bf8d4e6c101c504fa470f8ab4270c2202ff2591b61b2e373b4f20d9b" +dependencies = [ + "aegis", + "aes", + "aes-gcm", + "allocator-api2 0.4.0", + "antithesis_sdk", + "arc-swap", + "aristo", + "bigdecimal", + "bitflags", + "branches", + "bumpalo", + "bytemuck", + "cfg_aliases", + "cfg_block", + "chrono", + "crc32c", + "crossbeam-epoch", + "crossbeam-utils", + "either", + "fallible-iterator", + "fastbloom", + "hex", + "icu_collator", + "icu_locale", + "intrusive-collections", + "io-uring", + "libc 0.2.189", + "libloading", + "libm", + "loom", + "miette", + "num-bigint", + "num-traits", + "pack1", + "parking_lot", + "pastey", + "polling", + "rand 0.9.5", + "rapidhash", + "regex", + "regex-syntax", + "roaring", + "rustc-hash 2.1.3", + "rustix 1.1.4", + "ryu", + "serde_json", + "shuttle", + "simsimd", + "smallvec", + "strum", + "strum_macros", + "tantivy", + "tempfile", + "thiserror", + "tracing", + "tracing-subscriber", + "turso_ext", + "turso_macros", + "turso_parser", + "twox-hash", + "uncased", + "uuid", + "windows-sys 0.61.2", +] + +[[package]] +name = "turso_ext" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7452fe676450b6840e9eb80e512d14293265221154eba66aba3845dfc0d659f" +dependencies = [ + "chrono", + "getrandom 0.4.3", + "turso_macros", +] + +[[package]] +name = "turso_macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2825eaa6b7b893693636947f988e252c1196393fa54d4b85637c70d616d92fa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "turso_parser" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e58d029f02e442df4be0b5036ce520b49c553505d9d52ef5a98fc4056e95b7" +dependencies = [ + "bitflags", + "memchr", + "miette", + "strum", + "strum_macros", + "thiserror", + "turso_macros", +] + +[[package]] +name = "turso_sdk_kit" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c1dc1c0304348c39b97bc6b27cdcb1d7292454ebd0de0f30b5ee3a4c61f9bb" +dependencies = [ + "bindgen", + "env_logger", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_ext", + "turso_sdk_kit_macros", +] + +[[package]] +name = "turso_sdk_kit_macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4708b5fe678b8730c85964e3d378f75e18c23c4409971360b2209f4e53e4956" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "turso_sync_engine" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8f03e430240d590d209ac874db52773b7382c627cd604a685affde77622b09" +dependencies = [ + "base64", + "bytes", + "crc32c", + "genawaiter", + "http", + "libc 0.2.189", + "prost", + "roaring", + "serde", + "serde_json", + "thiserror", + "tracing", + "turso_core", + "turso_parser", + "uuid", +] + +[[package]] +name = "turso_sync_sdk_kit" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cb47d056c3ec567745761fa6f832358ca30ef9eb0435fdcfb77c558e70089" +dependencies = [ + "bindgen", + "env_logger", + "genawaiter", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sdk_kit_macros", + "turso_sync_engine", +] + +[[package]] +name = "twox-hash" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5283634e518fe9e82c7b20520bb4bc209009fd16c82077c802f8111ecbb0117a" +dependencies = [ + "rand 0.10.2", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "typetag" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c90e86058a30d42a1a928dfb4b49bb33c98c3a2b4909492e6b0881cd94798ec2" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f153acc4e99a5f2a5aefa09fb078be54e26271b2813f6041200b224c098d8328" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8-ranges" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 0cb0754..43efe5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,24 @@ [package] -name = "rust-template" # TODO: Projektnamen setzen -version = "0.0.0" # TODO: Initiale Version setzen (z. B. 0.1.0) +name = "mac2ip" +version = "0.1.0" edition = "2024" -authors = ['DragonSlayer_14'] # TODO: Autor(en) anpassen +authors = ['DragonSlayer_14'] readme = "README.md" -license = "GPL-3.0-or-later" # TODO: Lizenz prüfen / anpassen -repository = "https://gitea.creative-dragonslayer.de/Linuxapps/rust-template" # TODO: Repository-URL anpassen -description = "" # TODO: Kurze Projektbeschreibung setzen +license = "GPL-3.0-or-later" +repository = "https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip" +description = "Findet zuverlässig die aktuelle IP-Adresse zu einer MAC-Adresse im lokalen Netzwerk (Cache, ip neigh, nmap)" [dependencies] -# TODO: Projektabhängigkeiten hier hinzufügen +config-ctdra = { version = "1.0.6", registry = "gitea" } +logger-ctdra = { version = "1.0.5", registry = "gitea" } +program-ctdra = { version = "1.0.1", registry = "gitea" } +sudo-ctdra = { version = "1.0.1", registry = "gitea" } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "process", "time"] } +turso = "0.7" +clap = { version = "4", features = ["derive", "env"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" [profile.release] debug = "none" @@ -17,28 +26,40 @@ debug = "none" # --- Paketierungs-Metadaten für Linux-Distributionen --- [package.metadata.deb] -name = "" # TODO: Optional (Standard: package.name) -maintainer = "" # TODO: Maintainer setzen (z. B. "Max Mustermann ") -copyright = "" # TODO: Copyright-Hinweis setzen (z. B. "2026 Max Mustermann") -section = "utils" # TODO: Debian-Sektion anpassen (z. B. utils, net, admin) -priority = "optional" # TODO: Debian-Priorität anpassen (z. B. optional) -depends = "$auto" # TODO: Abhängigkeiten definieren ($auto ermittelt via ldd automatisch) +name = "mac2ip" +maintainer = "DragonSlayer_14" +copyright = "2026 DragonSlayer_14" +section = "net" +priority = "optional" +depends = "$auto, sudo, iproute2, iputils-ping, nmap" +maintainer-scripts = "packaging/deb" extended-description = """\ -""" # TODO: Ausführliche Paketbeschreibung setzen +mac2ip ermittelt zuverlässig die aktuelle IP-Adresse zu einer gegebenen MAC-Adresse +im lokalen Netzwerk. Dazu wird zunächst ein globaler, systemweiter Cache geprüft, +danach die 'ip neigh'-Nachbartabelle (moderner Ersatz für 'arp'), und als letzter +Schritt ein nmap-Ping-/ARP-Scan der lokal angeschlossenen Subnetze durchgeführt.\ +""" assets = [ - # TODO: Bei Bedarf zusätzliche Dateien / Assets definieren, z. B.: - # ["target/release/rust-template", "usr/bin/", "755"], + ["target/release/mac2ip", "usr/bin/mac2ip", "755"], + ["README.md", "usr/share/doc/mac2ip/README.md", "644"], + ["LICENSE", "usr/share/doc/mac2ip/copyright", "644"], ] [package.metadata.generate-rpm] assets = [ - # TODO: Bei Bedarf zusätzliche Dateien / Assets definieren, z. B.: - # { source = "target/release/rust-template", dest = "/usr/bin/rust-template", mode = "755" }, + { source = "target/release/mac2ip", dest = "/usr/bin/mac2ip", mode = "755" }, + { source = "README.md", dest = "/usr/share/doc/mac2ip/README.md", mode = "644", doc = true }, + { source = "LICENSE", dest = "/usr/share/licenses/mac2ip/LICENSE", mode = "644", license = true }, ] -requires = { } # TODO: RPM-Abhängigkeiten definieren, z. B. { "glibc" = "*" } +requires = { "sudo" = "*", "iproute" = "*", "iputils" = "*", "nmap" = "*" } +post_install_script = """ +mkdir -p /var/lib/mac2ip +chmod 0777 /var/lib/mac2ip +""" [package.metadata.arch] -pkgrel = "1" # TODO: Standard Release-Nummer (wird in CI durch get-build-number.py dynamisch überschrieben) -arch = "x86_64" # TODO: Zielarchitektur (z. B. x86_64, aarch64, i686) -depends = ["gcc-libs", "glibc"] # TODO: Arch Linux Paketabhängigkeiten definieren -optdepends = [] # TODO: Optionale Abhängigkeiten definieren (z. B. ["curl: für Web-Downloads"]) +pkgrel = "1" +arch = "x86_64" +depends = ["gcc-libs", "glibc", "sudo", "iproute2", "iputils", "nmap"] +optdepends = [] +install_script = "packaging/arch/mac2ip.install" From e40cba3ecb0837871d8f0d04f42fc42d36423775 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:28:23 +0200 Subject: [PATCH 02/18] =?UTF-8?q?Paketierungs-Postinstall-Skripte=20f?= =?UTF-8?q?=C3=BCr=20/var/lib/mac2ip=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deb/rpm/arch legen beim Paket-Install /var/lib/mac2ip mit 0777 an, da der globale Cache für alle Nutzer beschreibbar sein muss, /var/lib selbst aber nur root beschreiben kann. package-arch.py unterstützt dafür neu ein optionales, metadatengetriebenes Install-Skriptlet ([package.metadata.arch].install_script), das pacman-konform als .install eingebettet wird. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0118Wbg95ADSDynYfci2qUjK --- packaging/arch/mac2ip.install | 8 ++++++++ packaging/deb/postinst | 7 +++++++ scripts/package-arch.py | 17 ++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 packaging/arch/mac2ip.install create mode 100755 packaging/deb/postinst diff --git a/packaging/arch/mac2ip.install b/packaging/arch/mac2ip.install new file mode 100644 index 0000000..4a31851 --- /dev/null +++ b/packaging/arch/mac2ip.install @@ -0,0 +1,8 @@ +post_install() { + mkdir -p /var/lib/mac2ip + chmod 0777 /var/lib/mac2ip +} + +post_upgrade() { + post_install +} diff --git a/packaging/deb/postinst b/packaging/deb/postinst new file mode 100755 index 0000000..4d8f64f --- /dev/null +++ b/packaging/deb/postinst @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +mkdir -p /var/lib/mac2ip +chmod 0777 /var/lib/mac2ip + +exit 0 diff --git a/scripts/package-arch.py b/scripts/package-arch.py index d03aeee..2dc3387 100755 --- a/scripts/package-arch.py +++ b/scripts/package-arch.py @@ -87,6 +87,7 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): depends = arch_meta.get("depends", ["gcc-libs", "glibc"]) optdepends = arch_meta.get("optdepends", []) + install_rel_path = arch_meta.get("install_script") with tempfile.TemporaryDirectory() as build_dir: bin_dir = os.path.join(build_dir, "usr/bin") @@ -102,6 +103,14 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): if os.path.exists("README.md"): subprocess.run(["install", "-m", "644", "README.md", f"{doc_dir}/README.md"], check=True) + install_scriptlet_name = None + if install_rel_path and os.path.exists(install_rel_path): + install_scriptlet_name = f"{name}.install" + subprocess.run( + ["install", "-m", "644", install_rel_path, os.path.join(build_dir, install_scriptlet_name)], + check=True, + ) + installed_size = subprocess.check_output(["du", "-sb", build_dir]).decode().split()[0] builddate = str(int(time.time())) @@ -117,6 +126,8 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): f"arch = {arch}", f"license = {license_name}", ] + if install_scriptlet_name: + pkginfo_lines.append(f"install = {install_scriptlet_name}") for dep in depends: pkginfo_lines.append(f"depend = {dep}") for optdep in optdepends: @@ -128,7 +139,11 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): os.makedirs("target/arch", exist_ok=True) output_file = os.path.abspath(f"target/arch/{name}-{version}-{pkgrel}-{arch}.pkg.tar.zst") - subprocess.run(["tar", "--zstd", "-cf", output_file, ".PKGINFO", "usr"], cwd=build_dir, check=True) + tar_members = [".PKGINFO"] + if install_scriptlet_name: + tar_members.append(install_scriptlet_name) + tar_members.append("usr") + subprocess.run(["tar", "--zstd", "-cf", output_file, *tar_members], cwd=build_dir, check=True) print(f"Arch-Paket erfolgreich erstellt: {output_file}") From 1ec2b92c875ecc2f47ac07ce6eacb5f80ff1b68a Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:28:39 +0200 Subject: [PATCH 03/18] mac2ip-Kernlogik implementieren: Cache -> ip neigh -> nmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementiert den 3-stufigen Auflösungsalgorithmus für MAC-zu-IP-Lookups: - mac.rs: MacAddress-Typ (Parsing/Kanonisierung, direkt als clap-Typ nutzbar) - cache.rs: globaler, systemweiter Cache via turso (lokale Datei), TTL-Logik, best-effort Fehlerbehandlung (Cache-I/O darf den Lookup nie scheitern lassen) - network.rs: ip neigh / ping / nmap, getrennt in dünne Exec-Funktionen und reine, testbare Parse-Funktionen - resolver.rs: Orchestrierung der drei Stufen inkl. sudo/sudo -n-Entscheidung für den nmap-Schritt - cli.rs/config.rs: clap-CLI + config-ctdra-Integration mit vollständiger CLI > ENV > Datei > Default-Overlay-Kette - log.rs: JSON-Modus-bewusster logger-ctdra-Wrapper (unterdrückt jegliche Log-Ausgabe im --json-Modus an einer einzigen Stelle) - output.rs: Human- und JSON-Ausgabe des Ergebnisses - lib.rs: Modul-Wurzel, damit tests/*.rs als Integrationstests zugreifen können; main.rs wird dünner Einstiegspunkt Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0118Wbg95ADSDynYfci2qUjK --- src/cache.rs | 175 +++++++++++++++++++++++++++++++++++++++++++++++ src/cli.rs | 78 +++++++++++++++++++++ src/config.rs | 93 +++++++++++++++++++++++++ src/error.rs | 13 ++++ src/lib.rs | 12 ++++ src/log.rs | 43 ++++++++++++ src/mac.rs | 77 +++++++++++++++++++++ src/main.rs | 43 +++++++++--- src/network.rs | 178 ++++++++++++++++++++++++++++++++++++++++++++++++ src/output.rs | 68 ++++++++++++++++++ src/resolver.rs | 140 +++++++++++++++++++++++++++++++++++++ 11 files changed, 910 insertions(+), 10 deletions(-) create mode 100644 src/cache.rs create mode 100644 src/cli.rs create mode 100644 src/config.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/log.rs create mode 100644 src/mac.rs create mode 100644 src/network.rs create mode 100644 src/output.rs create mode 100644 src/resolver.rs diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 0000000..833626b --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,175 @@ +//! Globaler, systemweiter Cache (MAC -> IP) auf Basis der `turso`-Crate (lokale Datei). +//! +//! Der Cache liegt absichtlich an einem einzigen, für alle Nutzer gemeinsamen Pfad +//! (Standard: `/var/lib/mac2ip/cache.db`). Ist dieser Pfad nicht anlegbar/beschreibbar +//! (z. B. bei einem Entwicklungslauf ohne vorherige Paketinstallation), wird der Cache +//! für den aktuellen Lauf einfach deaktiviert (Warnung, best-effort) - es gibt bewusst +//! KEINEN Fallback auf einen Pro-User-Pfad, da der Cache explizit global sein soll. + +use std::net::IpAddr; +use std::path::{Path, PathBuf}; + +use crate::mac::MacAddress; + +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + +pub struct CacheEntry { + pub ip: IpAddr, + #[allow(dead_code)] + pub source: String, + pub updated_at: i64, +} + +pub struct Cache { + conn: turso::Connection, +} + +impl Cache { + async fn init_schema(&self) -> turso::Result<()> { + self.conn + .execute( + "CREATE TABLE IF NOT EXISTS mac_ip_cache (\ + mac TEXT PRIMARY KEY, \ + ip TEXT NOT NULL, \ + source TEXT NOT NULL, \ + updated_at INTEGER NOT NULL\ + )", + (), + ) + .await?; + Ok(()) + } + + /// Best-effort: gibt bei jedem Fehler `None` zurück (nur eine Warnung wird geloggt). + pub async fn get(&self, mac: &MacAddress) -> Option { + let key = mac.to_lower_colon(); + let mut rows = match self + .conn + .query( + "SELECT ip, source, updated_at FROM mac_ip_cache WHERE mac = ?1", + (key,), + ) + .await + { + Ok(rows) => rows, + Err(e) => { + crate::log::warn("cache", &format!("Cache-Lesefehler: {e}")); + return None; + } + }; + + match rows.next().await { + Ok(Some(row)) => { + let ip_str: String = row.get(0).ok()?; + let source: String = row.get(1).ok()?; + let updated_at: i64 = row.get(2).ok()?; + ip_str.parse().ok().map(|ip| CacheEntry { + ip, + source, + updated_at, + }) + } + Ok(None) => None, + Err(e) => { + crate::log::warn("cache", &format!("Cache-Lesefehler: {e}")); + None + } + } + } + + /// Best-effort Upsert: Fehler werden nur geloggt, niemals propagiert - ein + /// Cache-Schreibfehler darf den 3-Stufen-Lookup nie zum Scheitern bringen. + pub async fn upsert(&self, mac: &MacAddress, ip: IpAddr, source: &str, now: i64) { + let key = mac.to_lower_colon(); + if let Err(e) = self + .conn + .execute( + "INSERT INTO mac_ip_cache (mac, ip, source, updated_at) VALUES (?1, ?2, ?3, ?4) \ + ON CONFLICT(mac) DO UPDATE SET ip = excluded.ip, source = excluded.source, updated_at = excluded.updated_at", + (key, ip.to_string(), source.to_string(), now), + ) + .await + { + crate::log::warn("cache", &format!("Cache-Schreibfehler (ignoriert): {e}")); + } + } +} + +/// Reine, injectable-Zeit-Funktion - unit-testbar ohne echte Systemzeit. +pub fn is_expired(updated_at: i64, now: i64, ttl_seconds: u64) -> bool { + now.saturating_sub(updated_at) as u64 > ttl_seconds +} + +fn ensure_cache_dir(dir: &Path) -> Result<(), String> { + if !dir.exists() { + std::fs::create_dir_all(dir).map_err(|e| { + format!( + "Verzeichnis '{}' konnte nicht angelegt werden: {e}", + dir.display() + ) + })?; + } + // create_dir_all() unterliegt dem Prozess-Umask; explizites chmod ist nötig, um + // wirklich 0777 zu erreichen (Verzeichnis-Rechte steuern nicht die Umask neuer Dateien). + #[cfg(unix)] + std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o777)) + .map_err(|e| format!("Verzeichnis-Rechte konnten nicht gesetzt werden: {e}"))?; + Ok(()) +} + +fn chmod_cache_sidecars(db_path: &Path) { + #[cfg(unix)] + for suffix in ["", "-wal", "-shm", "-journal"] { + let p = PathBuf::from(format!("{}{suffix}", db_path.display())); + if p.exists() { + let _ = std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o666)); + } + } +} + +/// Best-effort Cache-Öffnung: gibt `None` zurück (statt Fehler), wenn der Cache aus +/// irgendeinem Grund nicht verfügbar ist - der Aufrufer führt den 3-Stufen-Algorithmus +/// dann einfach ohne Cache aus. +pub async fn try_open_cache(db_path: &Path) -> Option { + if let Some(dir) = db_path.parent() + && let Err(reason) = ensure_cache_dir(dir) + { + crate::log::warn("cache", &format!("Cache deaktiviert: {reason}")); + return None; + } + + let db = match turso::Builder::new_local(db_path.to_string_lossy().as_ref()) + .build() + .await + { + Ok(db) => db, + Err(e) => { + crate::log::warn( + "cache", + &format!("Cache-Datenbank konnte nicht geöffnet werden: {e}"), + ); + return None; + } + }; + + let conn = match db.connect() { + Ok(c) => c, + Err(e) => { + crate::log::warn("cache", &format!("Cache-Verbindung fehlgeschlagen: {e}")); + return None; + } + }; + + let cache = Cache { conn }; + if let Err(e) = cache.init_schema().await { + crate::log::warn( + "cache", + &format!("Cache-Schema konnte nicht initialisiert werden: {e}"), + ); + return None; + } + + chmod_cache_sidecars(db_path); + Some(cache) +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..949755b --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,78 @@ +//! Kommandozeilen-Interface. + +use std::path::PathBuf; + +use clap::{Parser, ValueEnum}; + +use crate::mac::MacAddress; + +#[derive(Parser, Debug)] +#[command( + name = "mac2ip", + author, + version, + about = "Findet zuverlässig die aktuelle IP-Adresse zu einer MAC-Adresse im lokalen Netzwerk", + long_about = None +)] +pub struct Cli { + /// MAC-Adresse, deren aktuelle IP-Adresse ermittelt werden soll (z. B. aa:bb:cc:dd:ee:ff) + pub mac: MacAddress, + + /// Gibt das Ergebnis als einzeiliges JSON-Objekt aus; unterdrückt alle sonstigen Log-Ausgaben + #[arg(long)] + pub json: bool, + + /// Benutzerdefinierter Pfad zur Konfigurationsdatei + #[arg(long)] + pub config: Option, + + /// Logging-Level (error, warn, info, debug) + #[arg(long, value_enum, env = "MAC2IP_LOG_LEVEL")] + pub log_level: Option, + + /// Cache-TTL in Sekunden (Standard: 1800) + #[arg(long, env = "MAC2IP_CACHE_TTL_SECONDS")] + pub cache_ttl_seconds: Option, + + /// Pfad zur globalen Cache-Datenbankdatei (Standard: /var/lib/mac2ip/cache.db) + #[arg(long, env = "MAC2IP_CACHE_DB_PATH")] + pub cache_db_path: Option, + + /// Timeout in Sekunden für einen einzelnen nmap-Subnetz-Scan (Standard: 120) + #[arg(long, env = "MAC2IP_NMAP_TIMEOUT_SECONDS")] + pub nmap_timeout_seconds: Option, + + /// Kommagetrennte Liste von CIDR-Subnetzen für den nmap-Scan (überschreibt Auto-Erkennung) + #[arg(long, env = "MAC2IP_NETWORKS", value_delimiter = ',')] + pub networks: Option>, +} + +#[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq)] +pub enum LogLevelArg { + Error, + Warn, + Info, + Debug, +} + +impl LogLevelArg { + pub fn as_str(&self) -> &'static str { + match self { + Self::Error => "error", + Self::Warn => "warn", + Self::Info => "info", + Self::Debug => "debug", + } + } +} + +impl From for logger_ctdra::LogLevel { + fn from(value: LogLevelArg) -> Self { + match value { + LogLevelArg::Error => logger_ctdra::LogLevel::Error, + LogLevelArg::Warn => logger_ctdra::LogLevel::Warn, + LogLevelArg::Info => logger_ctdra::LogLevel::Info, + LogLevelArg::Debug => logger_ctdra::LogLevel::Debug, + } + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4952700 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,93 @@ +//! Anwendungskonfiguration: persistente Datei (via `config-ctdra`) + CLI-Overlay. + +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; + +pub const DEFAULT_CACHE_TTL_SECONDS: u64 = 1800; // 30 Min: Ping-Check ist die primäre +// Absicherung gegen veraltete Einträge, die TTL ist nur eine zusätzliche Absicherung +// gegen den Fall, dass eine alte IP inzwischen an ein anderes, ebenfalls +// erreichbares Gerät vergeben wurde. +pub const DEFAULT_CACHE_DB_PATH: &str = "/var/lib/mac2ip/cache.db"; +pub const DEFAULT_LOG_LEVEL: &str = "info"; +pub const DEFAULT_NMAP_TIMEOUT_SECONDS: u64 = 120; + +fn default_cache_ttl_seconds() -> u64 { + DEFAULT_CACHE_TTL_SECONDS +} + +fn default_cache_db_path() -> PathBuf { + PathBuf::from(DEFAULT_CACHE_DB_PATH) +} + +fn default_log_level() -> String { + DEFAULT_LOG_LEVEL.to_string() +} + +fn default_nmap_timeout_seconds() -> u64 { + DEFAULT_NMAP_TIMEOUT_SECONDS +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct AppConfig { + #[serde(default = "default_cache_ttl_seconds")] + pub cache_ttl_seconds: u64, + #[serde(default = "default_cache_db_path")] + pub cache_db_path: PathBuf, + #[serde(default = "default_log_level")] + pub log_level: String, + #[serde(default = "default_nmap_timeout_seconds")] + pub nmap_timeout_seconds: u64, + #[serde(default)] + pub networks: Vec, +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + cache_ttl_seconds: DEFAULT_CACHE_TTL_SECONDS, + cache_db_path: PathBuf::from(DEFAULT_CACHE_DB_PATH), + log_level: DEFAULT_LOG_LEVEL.to_string(), + nmap_timeout_seconds: DEFAULT_NMAP_TIMEOUT_SECONDS, + networks: Vec::new(), + } + } +} + +/// Initialisiert den Konfigurationspfad bei `config-ctdra` (Standardname "config", +/// optionaler expliziter Pfad via `--config`). +pub fn init_config_path(custom_path: Option<&Path>) { + config_ctdra::set_config_name("config"); + if let Some(path) = custom_path { + config_ctdra::set_custom_path(path.to_path_buf()); + } +} + +pub fn get_config_file_path() -> PathBuf { + config_ctdra::get_config_path() +} + +pub fn load_config() -> AppConfig { + config_ctdra::load_config::() +} + +/// Reine, testbare Overlay-Funktion: überschreibt Konfigurationsfelder mit +/// expliziten CLI-Werten (clap hat CLI>ENV bereits selbst aufgelöst; hier wird +/// nur noch "vorhanden > Datei/Default" angewendet). +pub fn apply_cli_overrides(config: &mut AppConfig, cli: &crate::cli::Cli) { + if let Some(level) = &cli.log_level { + config.log_level = level.as_str().to_string(); + } + if let Some(ttl) = cli.cache_ttl_seconds { + config.cache_ttl_seconds = ttl; + } + if let Some(path) = &cli.cache_db_path { + config.cache_db_path = path.clone(); + } + if let Some(timeout) = cli.nmap_timeout_seconds { + config.nmap_timeout_seconds = timeout; + } + if let Some(networks) = &cli.networks { + config.networks = networks.clone(); + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..e4dbe24 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,13 @@ +//! Fehlertypen für mac2ip. + +use crate::mac::MacAddressError; + +#[derive(Debug, thiserror::Error)] +pub enum Mac2IpError { + #[error("keine IP-Adresse für MAC '{mac}' gefunden")] + NotFound { mac: String }, + #[error("MAC-Adresse ungültig: {0}")] + InvalidMac(#[from] MacAddressError), + #[error("E/A-Fehler: {0}")] + Io(#[from] std::io::Error), +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0f87c66 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +//! mac2ip - findet zuverlässig die aktuelle IP-Adresse zu einer MAC-Adresse +//! im lokalen Netzwerk (Cache -> `ip neigh` -> `nmap`). + +pub mod cache; +pub mod cli; +pub mod config; +pub mod error; +pub mod log; +pub mod mac; +pub mod network; +pub mod output; +pub mod resolver; diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..129b2b7 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,43 @@ +//! JSON-Modus-bewusster Wrapper um `logger-ctdra`. +//! +//! Im `--json`-Modus dürfen keinerlei Log-Ausgaben (auch keine Fehler/Warnungen) +//! auf stdout/stderr erscheinen, da nur ein einzelnes JSON-Objekt erwartet wird. +//! Alle Module rufen ausschließlich diese Funktionen auf, niemals `logger_ctdra` +//! direkt, damit die Unterdrückung an genau einer Stelle greift. + +use std::sync::atomic::{AtomicBool, Ordering}; + +static JSON_MODE: AtomicBool = AtomicBool::new(false); + +/// Muss als allererste Logger-bezogene Aktion in main() aufgerufen werden. +pub fn set_json_mode(enabled: bool) { + JSON_MODE.store(enabled, Ordering::Relaxed); +} + +pub fn is_json_mode() -> bool { + JSON_MODE.load(Ordering::Relaxed) +} + +pub fn error(tag: &str, message: &str) { + if !is_json_mode() { + logger_ctdra::error(tag, message); + } +} + +pub fn warn(tag: &str, message: &str) { + if !is_json_mode() { + logger_ctdra::warn(tag, message); + } +} + +pub fn info(tag: &str, message: &str) { + if !is_json_mode() { + logger_ctdra::info(tag, message); + } +} + +pub fn debug(tag: &str, message: &str) { + if !is_json_mode() { + logger_ctdra::debug(tag, message); + } +} diff --git a/src/mac.rs b/src/mac.rs new file mode 100644 index 0000000..7e81c30 --- /dev/null +++ b/src/mac.rs @@ -0,0 +1,77 @@ +//! Repräsentation und Parsing von MAC-Adressen. + +use std::fmt; +use std::str::FromStr; + +/// Kanonische Darstellung einer MAC-Adresse (6 Bytes). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct MacAddress([u8; 6]); + +#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] +pub enum MacAddressError { + #[error("ungültige MAC-Adresse: '{0}'")] + InvalidFormat(String), +} + +impl MacAddress { + /// Parst eine MAC-Adresse aus ':' oder '-' getrennter Hex-Notation (case-insensitive). + pub fn parse(input: &str) -> Result { + let err = || MacAddressError::InvalidFormat(input.to_string()); + + let normalized = input.trim(); + let separator = if normalized.contains(':') { + ':' + } else if normalized.contains('-') { + '-' + } else { + return Err(err()); + }; + + let parts: Vec<&str> = normalized.split(separator).collect(); + if parts.len() != 6 { + return Err(err()); + } + + let mut bytes = [0u8; 6]; + for (i, part) in parts.iter().enumerate() { + if part.len() != 2 { + return Err(err()); + } + bytes[i] = u8::from_str_radix(part, 16).map_err(|_| err())?; + } + + Ok(Self(bytes)) + } + + /// Kleinschreibung, Doppelpunkt-getrennt (Vergleichsform, wie `ip neigh` sie ausgibt). + pub fn to_lower_colon(&self) -> String { + self.0 + .iter() + .map(|b| format!("{b:02x}")) + .collect::>() + .join(":") + } + + /// Großschreibung, Doppelpunkt-getrennt (wie nmap sie ausgibt). + pub fn to_upper_colon(&self) -> String { + self.0 + .iter() + .map(|b| format!("{b:02X}")) + .collect::>() + .join(":") + } +} + +impl FromStr for MacAddress { + type Err = MacAddressError; + + fn from_str(s: &str) -> Result { + Self::parse(s) + } +} + +impl fmt::Display for MacAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_lower_colon()) + } +} diff --git a/src/main.rs b/src/main.rs index 79004d9..adf8d6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,36 @@ -fn main() { - // TODO: Hauptlogik der Anwendung implementieren - println!("Hello, World!"); -} +use std::process::ExitCode; -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - // TODO: Unit-Tests für die Anwendung schreiben - assert_eq!(2 + 2, 4); +use clap::Parser; + +use mac2ip::{cache, cli::Cli, config, log, output, resolver}; + +#[tokio::main] +async fn main() -> ExitCode { + let cli = Cli::parse(); + + // Muss vor jedem weiteren Logger-Aufruf geschehen, damit im --json-Modus + // wirklich keine einzige Log-Zeile ausgegeben wird. + log::set_json_mode(cli.json); + + config::init_config_path(cli.config.as_deref()); + let mut app_config = config::load_config(); + config::apply_cli_overrides(&mut app_config, &cli); + + match logger_ctdra::LogLevel::try_from(app_config.log_level.as_str()) { + Ok(level) => logger_ctdra::set_log_level(level), + Err(fallback) => logger_ctdra::set_log_level(fallback), + } + + let cache = cache::try_open_cache(&app_config.cache_db_path).await; + + match resolver::resolve(&cli.mac, &app_config, cache.as_ref(), cli.json).await { + Ok(result) => { + output::print_success(&result, cli.json); + ExitCode::SUCCESS + } + Err(err) => { + output::print_failure(&cli.mac, &err, cli.json); + ExitCode::FAILURE + } } } diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..920bcbf --- /dev/null +++ b/src/network.rs @@ -0,0 +1,178 @@ +//! Netzwerk-Interaktion: Auto-Erkennung lokaler Subnetze, `ip neigh`, Ping-Erreichbarkeit +//! und der nmap-Scan (Schritt 3). Bewusst getrennt in dünne, das System aufrufende +//! Exec-Funktionen und reine, unit-testbare Parse-Funktionen. + +use std::net::IpAddr; +use std::time::Duration; + +use crate::mac::MacAddress; + +/// Schnelle Liveness-Probe, absichtlich kein Konfigurationsfeld (kein Nutzer-Tuning nötig). +const PING_TIMEOUT_SECS: u32 = 1; + +// --------------------------------------------------------------------------- +// Exec-Funktionen (führen echte Subprozesse aus) +// --------------------------------------------------------------------------- + +pub async fn run_ip_neigh_show() -> std::io::Result { + let out = tokio::process::Command::new("ip") + .args(["neigh", "show"]) + .output() + .await?; + Ok(String::from_utf8_lossy(&out.stdout).into_owned()) +} + +pub async fn run_ip_route_show_scope_link() -> std::io::Result { + let out = tokio::process::Command::new("ip") + .args(["-4", "route", "show", "scope", "link"]) + .output() + .await?; + Ok(String::from_utf8_lossy(&out.stdout).into_owned()) +} + +pub async fn ping_check(ip: IpAddr) -> bool { + tokio::process::Command::new("ping") + .args([ + "-c", + "1", + "-W", + &PING_TIMEOUT_SECS.to_string(), + &ip.to_string(), + ]) + // stdout/stderr von `ping` sind für uns irrelevant (nur der Exit-Code zählt) und + // dürfen insbesondere im --json-Modus nicht in unseren eigenen Output durchsickern. + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status() + .await + .map(|s| s.success()) + .unwrap_or(false) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SudoMode { + /// Bereits root - nmap wird ohne sudo-Prefix aufgerufen. + None, + /// sudo darf interaktiv nach einem Passwort fragen. + Interactive, + /// sudo darf NICHT interaktiv nach einem Passwort fragen (`sudo -n`). + NonInteractive, +} + +#[derive(Debug, thiserror::Error)] +pub enum NmapRunError { + #[error("sudo-Zugangsdaten nicht verfügbar (nicht-interaktiver Modus)")] + SudoUnavailable, + #[error("nmap-Scan hat das Zeitlimit überschritten")] + Timeout, + #[error("E/A-Fehler: {0}")] + Io(#[from] std::io::Error), +} + +pub async fn run_nmap_scan( + cidr: &str, + timeout_secs: u64, + mode: SudoMode, +) -> Result { + let mut cmd = match mode { + SudoMode::None => { + let mut c = tokio::process::Command::new("nmap"); + c.arg("-sn").arg("-n").arg(cidr); + c + } + SudoMode::Interactive => { + let mut c = tokio::process::Command::new("sudo"); + c.args(["nmap", "-sn", "-n", cidr]); + c + } + SudoMode::NonInteractive => { + let mut c = tokio::process::Command::new("sudo"); + c.args(["-n", "nmap", "-sn", "-n", cidr]); + c + } + }; + // Erzwingt englische Fehlermeldungen, damit die "password"-Erkennung unten + // unabhängig von der System-Locale funktioniert (z. B. "Passwort ist notwendig" + // auf einem deutschen System würde sonst nicht erkannt werden). + cmd.env("LC_ALL", "C").env("LANG", "C"); + + let out = tokio::time::timeout(Duration::from_secs(timeout_secs), cmd.output()) + .await + .map_err(|_| NmapRunError::Timeout)??; + + if !out.status.success() { + let stderr = String::from_utf8_lossy(&out.stderr); + if stderr.to_lowercase().contains("password") { + return Err(NmapRunError::SudoUnavailable); + } + return Err(NmapRunError::Io(std::io::Error::other(stderr.into_owned()))); + } + + Ok(String::from_utf8_lossy(&out.stdout).into_owned()) +} + +// --------------------------------------------------------------------------- +// Reine Parse-Funktionen (kein Subprozess, vollständig unit-testbar) +// --------------------------------------------------------------------------- + +/// Parst `ip neigh show`-Ausgabe, z. B. Zeilen wie +/// "192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE". +/// Gibt die IP zurück, sobald eine Zeile mit passender `lladdr`-MAC gefunden wird. +pub fn parse_ip_neigh_output(output: &str, mac: &MacAddress) -> Option { + let target = mac.to_lower_colon(); + for line in output.lines() { + let tokens: Vec<&str> = line.split_whitespace().collect(); + if tokens.is_empty() { + continue; + } + if let Some(pos) = tokens.iter().position(|&t| t == "lladdr") + && let Some(&found_mac) = tokens.get(pos + 1) + && found_mac.eq_ignore_ascii_case(&target) + && let Ok(ip) = tokens[0].parse() + { + return Some(ip); + } + } + None +} + +/// Extrahiert lokale CIDR-Subnetze aus `ip -4 route show scope link`. +/// Die CIDR ist bereits das erste whitespace-getrennte Token pro passender Zeile. +/// Überspringt `default`-Zeilen und das Loopback-Interface. +pub fn parse_local_subnets(ip_route_output: &str) -> Vec { + ip_route_output + .lines() + .filter(|l| !l.trim_start().starts_with("default")) + .filter(|l| !l.split_whitespace().any(|t| t == "lo")) + .filter_map(|l| l.split_whitespace().next()) + .filter(|t| t.contains('/')) + .map(|s| s.to_string()) + .collect() +} + +/// Parst `nmap -sn`-Ausgabe: verknüpft "Nmap scan report for IP" mit der +/// nächstfolgenden "MAC Address: XX:XX:XX:XX:XX:XX (Vendor)"-Zeile, bevor ein neuer +/// Host-Block beginnt. +pub fn parse_nmap_output(output: &str, mac: &MacAddress) -> Option { + let target = mac.to_lower_colon(); + let mut current_ip: Option = None; + + for line in output.lines() { + if let Some(rest) = line.strip_prefix("Nmap scan report for ") { + // rest ist entweder "192.168.1.5" oder "hostname (192.168.1.5)" + let rest = rest.trim(); + current_ip = if let Some(open) = rest.rfind('(') { + rest[open + 1..].trim_end_matches(')').trim().parse().ok() + } else { + rest.parse().ok() + }; + } else if let Some(rest) = line.trim_start().strip_prefix("MAC Address: ") + && let Some(mac_token) = rest.split_whitespace().next() + && mac_token.eq_ignore_ascii_case(&target) + && let Some(ip) = current_ip + { + return Some(ip); + } + } + None +} diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..522d960 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,68 @@ +//! Human- und JSON-Ausgabe des Lookup-Ergebnisses. + +use serde::Serialize; + +use crate::error::Mac2IpError; +use crate::mac::MacAddress; +use crate::resolver::ResolveResult; + +#[derive(Serialize, Debug, PartialEq)] +pub struct SuccessJson { + pub status: &'static str, + pub mac: String, + pub ip: String, + pub source: &'static str, +} + +#[derive(Serialize, Debug, PartialEq)] +pub struct FailureJson { + pub status: &'static str, + pub mac: String, + pub error: String, +} + +pub fn success_json(result: &ResolveResult) -> SuccessJson { + SuccessJson { + status: "ok", + mac: result.mac.to_lower_colon(), + ip: result.ip.to_string(), + source: result.source.as_str(), + } +} + +pub fn failure_json(mac: &MacAddress, err: &Mac2IpError) -> FailureJson { + FailureJson { + status: "error", + mac: mac.to_lower_colon(), + error: err.to_string(), + } +} + +pub fn print_success(result: &ResolveResult, json: bool) { + if json { + println!( + "{}", + serde_json::to_string(&success_json(result)) + .expect("JSON-Serialisierung schlägt nicht fehl") + ); + } else { + println!( + "{} -> {} (Quelle: {})", + result.mac, + result.ip, + result.source.as_str() + ); + } +} + +pub fn print_failure(mac: &MacAddress, err: &Mac2IpError, json: bool) { + if json { + println!( + "{}", + serde_json::to_string(&failure_json(mac, err)) + .expect("JSON-Serialisierung schlägt nicht fehl") + ); + } else { + crate::log::error("mac2ip", &err.to_string()); + } +} diff --git a/src/resolver.rs b/src/resolver.rs new file mode 100644 index 0000000..2648753 --- /dev/null +++ b/src/resolver.rs @@ -0,0 +1,140 @@ +//! Orchestrierung des 3-Stufen-Algorithmus: Cache -> `ip neigh` -> `nmap`. + +use std::net::IpAddr; +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::cache::{self, Cache}; +use crate::config::AppConfig; +use crate::error::Mac2IpError; +use crate::mac::MacAddress; +use crate::network::{self, NmapRunError, SudoMode}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MatchSource { + Cache, + Arp, + Nmap, +} + +impl MatchSource { + pub fn as_str(&self) -> &'static str { + match self { + Self::Cache => "cache", + Self::Arp => "arp", + Self::Nmap => "nmap", + } + } +} + +pub struct ResolveResult { + pub mac: MacAddress, + pub ip: IpAddr, + pub source: MatchSource, +} + +fn now_unix() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs() as i64) + .unwrap_or(0) +} + +pub async fn resolve( + mac: &MacAddress, + config: &AppConfig, + cache: Option<&Cache>, + json_mode: bool, +) -> Result { + let now = now_unix(); + + // Schritt 1: Cache + if let Some(cache) = cache + && let Some(entry) = cache.get(mac).await + && !cache::is_expired(entry.updated_at, now, config.cache_ttl_seconds) + && network::ping_check(entry.ip).await + { + crate::log::info( + "resolver", + &format!("Treffer im Cache für {mac}: {}", entry.ip), + ); + return Ok(ResolveResult { + mac: *mac, + ip: entry.ip, + source: MatchSource::Cache, + }); + } + + // Schritt 2: ip neigh + crate::log::debug( + "resolver", + "Cache-Treffer nicht verfügbar, prüfe 'ip neigh show'", + ); + if let Ok(output) = network::run_ip_neigh_show().await + && let Some(ip) = network::parse_ip_neigh_output(&output, mac) + && network::ping_check(ip).await + { + crate::log::info("resolver", &format!("Treffer via ip neigh für {mac}: {ip}")); + if let Some(cache) = cache { + cache.upsert(mac, ip, MatchSource::Arp.as_str(), now).await; + } + return Ok(ResolveResult { + mac: *mac, + ip, + source: MatchSource::Arp, + }); + } + + // Schritt 3: nmap + crate::log::debug("resolver", "Kein Treffer via ip neigh, starte nmap-Scan"); + let subnets = if !config.networks.is_empty() { + config.networks.clone() + } else { + network::run_ip_route_show_scope_link() + .await + .map(|o| network::parse_local_subnets(&o)) + .unwrap_or_default() + }; + + let sudo_mode = if sudo_ctdra::is_run_as_root() { + SudoMode::None + } else if json_mode { + SudoMode::NonInteractive + } else { + SudoMode::Interactive + }; + + for cidr in subnets { + match network::run_nmap_scan(&cidr, config.nmap_timeout_seconds, sudo_mode).await { + Ok(output) => { + if let Some(ip) = network::parse_nmap_output(&output, mac) + && network::ping_check(ip).await + { + crate::log::info("resolver", &format!("Treffer via nmap für {mac}: {ip}")); + if let Some(cache) = cache { + cache.upsert(mac, ip, MatchSource::Nmap.as_str(), now).await; + } + return Ok(ResolveResult { + mac: *mac, + ip, + source: MatchSource::Nmap, + }); + } + } + Err(NmapRunError::SudoUnavailable) => { + crate::log::warn( + "nmap", + "sudo -n fehlgeschlagen (keine Zugangsdaten) - Schritt 3 wird abgebrochen", + ); + break; // sudo-Session-Status ist global, ein Retry pro Subnetz bringt nichts + } + Err(e) => { + crate::log::warn("nmap", &format!("nmap-Scan für {cidr} fehlgeschlagen: {e}")); + continue; // Fehler pro Subnetz (z. B. Timeout) - nächstes Subnetz versuchen + } + } + } + + Err(Mac2IpError::NotFound { + mac: mac.to_string(), + }) +} From 8f6570e385ccf7b251a0aeed5097e3c03af53020 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:28:51 +0200 Subject: [PATCH 04/18] =?UTF-8?q?Unit-/Integrationstests=20f=C3=BCr=20Pars?= =?UTF-8?q?ing-=20und=20Konfigurationslogik=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deckt MAC-Adress-Parsing, ip-neigh-/nmap-Output-Parsing, Subnetz-Extraktion, Cache-TTL-Grenzfälle, CLI-Overlay-Verhalten und das JSON-Ausgabeschema ab. Alle Tests laufen auf reinen Funktionen ohne Netzwerk-, root- oder nmap-Abhängigkeit, damit die unprivilegierte CI (unit-tests.yaml) grün bleibt. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0118Wbg95ADSDynYfci2qUjK --- tests/cache_ttl.rs | 28 ++++++++++++ tests/config_overrides.rs | 80 ++++++++++++++++++++++++++++++++++ tests/ip_neigh_parsing.rs | 52 ++++++++++++++++++++++ tests/json_output.rs | 90 +++++++++++++++++++++++++++++++++++++++ tests/mac_address.rs | 65 ++++++++++++++++++++++++++++ tests/nmap_parsing.rs | 74 ++++++++++++++++++++++++++++++++ tests/subnet_parsing.rs | 40 +++++++++++++++++ 7 files changed, 429 insertions(+) create mode 100644 tests/cache_ttl.rs create mode 100644 tests/config_overrides.rs create mode 100644 tests/ip_neigh_parsing.rs create mode 100644 tests/json_output.rs create mode 100644 tests/mac_address.rs create mode 100644 tests/nmap_parsing.rs create mode 100644 tests/subnet_parsing.rs diff --git a/tests/cache_ttl.rs b/tests/cache_ttl.rs new file mode 100644 index 0000000..c13424d --- /dev/null +++ b/tests/cache_ttl.rs @@ -0,0 +1,28 @@ +use mac2ip::cache::is_expired; + +#[test] +fn fresh_entry_is_not_expired() { + assert!(!is_expired(1000, 1000, 1800)); +} + +#[test] +fn entry_exactly_at_ttl_boundary_is_not_expired() { + // now - updated_at == ttl_seconds -> nicht abgelaufen (strikt größer nötig) + assert!(!is_expired(1000, 1000 + 1800, 1800)); +} + +#[test] +fn entry_one_second_past_ttl_is_expired() { + assert!(is_expired(1000, 1000 + 1801, 1800)); +} + +#[test] +fn zero_ttl_expires_immediately_after_any_elapsed_time() { + assert!(is_expired(1000, 1001, 0)); + assert!(!is_expired(1000, 1000, 0)); +} + +#[test] +fn large_gap_is_expired() { + assert!(is_expired(0, 1_000_000, 1800)); +} diff --git a/tests/config_overrides.rs b/tests/config_overrides.rs new file mode 100644 index 0000000..34b13c3 --- /dev/null +++ b/tests/config_overrides.rs @@ -0,0 +1,80 @@ +use std::path::PathBuf; + +use mac2ip::cli::Cli; +use mac2ip::config::{AppConfig, apply_cli_overrides}; +use mac2ip::mac::MacAddress; + +fn base_cli() -> Cli { + Cli { + mac: MacAddress::parse("aa:bb:cc:dd:ee:ff").unwrap(), + json: false, + config: None, + log_level: None, + cache_ttl_seconds: None, + cache_db_path: None, + nmap_timeout_seconds: None, + networks: None, + } +} + +#[test] +fn no_overrides_leaves_config_untouched() { + let default_config = AppConfig::default(); + let mut config = default_config.clone(); + apply_cli_overrides(&mut config, &base_cli()); + assert_eq!(config, default_config); +} + +#[test] +fn overrides_cache_ttl_seconds() { + let mut config = AppConfig::default(); + let mut cli = base_cli(); + cli.cache_ttl_seconds = Some(60); + apply_cli_overrides(&mut config, &cli); + assert_eq!(config.cache_ttl_seconds, 60); +} + +#[test] +fn overrides_cache_db_path() { + let mut config = AppConfig::default(); + let mut cli = base_cli(); + cli.cache_db_path = Some(PathBuf::from("/tmp/custom-cache.db")); + apply_cli_overrides(&mut config, &cli); + assert_eq!(config.cache_db_path, PathBuf::from("/tmp/custom-cache.db")); +} + +#[test] +fn overrides_nmap_timeout_seconds() { + let mut config = AppConfig::default(); + let mut cli = base_cli(); + cli.nmap_timeout_seconds = Some(30); + apply_cli_overrides(&mut config, &cli); + assert_eq!(config.nmap_timeout_seconds, 30); +} + +#[test] +fn overrides_networks() { + let mut config = AppConfig::default(); + let mut cli = base_cli(); + cli.networks = Some(vec!["10.0.0.0/24".to_string()]); + apply_cli_overrides(&mut config, &cli); + assert_eq!(config.networks, vec!["10.0.0.0/24".to_string()]); +} + +#[test] +fn partial_overrides_only_touch_provided_fields() { + let default_config = AppConfig::default(); + let mut config = default_config.clone(); + let mut cli = base_cli(); + cli.cache_ttl_seconds = Some(999); + apply_cli_overrides(&mut config, &cli); + + assert_eq!(config.cache_ttl_seconds, 999); + assert_eq!(config.cache_db_path, default_config.cache_db_path); + assert_eq!(config.log_level, default_config.log_level); + assert_eq!( + config.nmap_timeout_seconds, + default_config.nmap_timeout_seconds + ); + assert_eq!(config.networks, default_config.networks); +} diff --git a/tests/ip_neigh_parsing.rs b/tests/ip_neigh_parsing.rs new file mode 100644 index 0000000..55d50ae --- /dev/null +++ b/tests/ip_neigh_parsing.rs @@ -0,0 +1,52 @@ +use mac2ip::mac::MacAddress; +use mac2ip::network::parse_ip_neigh_output; + +fn mac(s: &str) -> MacAddress { + MacAddress::parse(s).unwrap() +} + +#[test] +fn finds_matching_entry() { + let output = "192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE\n"; + let ip = parse_ip_neigh_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn picks_correct_entry_among_multiple() { + let output = "\ +192.168.1.4 dev eth0 lladdr 11:22:33:44:55:66 STALE +192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE +192.168.1.6 dev eth0 lladdr 77:88:99:aa:bb:cc STALE +"; + let ip = parse_ip_neigh_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn skips_entries_without_lladdr() { + let output = "\ +192.168.1.7 dev eth0 FAILED +192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE +"; + let ip = parse_ip_neigh_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn returns_none_when_no_match() { + let output = "192.168.1.4 dev eth0 lladdr 11:22:33:44:55:66 STALE\n"; + assert!(parse_ip_neigh_output(output, &mac("aa:bb:cc:dd:ee:ff")).is_none()); +} + +#[test] +fn tolerates_blank_lines_and_extra_whitespace() { + let output = "\n \n192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE\n\n"; + let ip = parse_ip_neigh_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn empty_output_returns_none() { + assert!(parse_ip_neigh_output("", &mac("aa:bb:cc:dd:ee:ff")).is_none()); +} diff --git a/tests/json_output.rs b/tests/json_output.rs new file mode 100644 index 0000000..d12441b --- /dev/null +++ b/tests/json_output.rs @@ -0,0 +1,90 @@ +use std::net::IpAddr; +use std::str::FromStr; + +use mac2ip::error::Mac2IpError; +use mac2ip::mac::MacAddress; +use mac2ip::output::{SuccessJson, failure_json, success_json}; +use mac2ip::resolver::{MatchSource, ResolveResult}; + +fn mac() -> MacAddress { + MacAddress::parse("aa:bb:cc:dd:ee:ff").unwrap() +} + +fn ip() -> IpAddr { + IpAddr::from_str("192.168.1.5").unwrap() +} + +#[test] +fn success_json_has_expected_shape_for_cache_source() { + let result = ResolveResult { + mac: mac(), + ip: ip(), + source: MatchSource::Cache, + }; + let json = success_json(&result); + assert_eq!( + json, + SuccessJson { + status: "ok", + mac: "aa:bb:cc:dd:ee:ff".to_string(), + ip: "192.168.1.5".to_string(), + source: "cache", + } + ); +} + +#[test] +fn success_json_has_expected_shape_for_arp_source() { + let result = ResolveResult { + mac: mac(), + ip: ip(), + source: MatchSource::Arp, + }; + assert_eq!(success_json(&result).source, "arp"); +} + +#[test] +fn success_json_has_expected_shape_for_nmap_source() { + let result = ResolveResult { + mac: mac(), + ip: ip(), + source: MatchSource::Nmap, + }; + assert_eq!(success_json(&result).source, "nmap"); +} + +#[test] +fn failure_json_has_expected_shape() { + let err = Mac2IpError::NotFound { + mac: mac().to_string(), + }; + let json = failure_json(&mac(), &err); + assert_eq!(json.status, "error"); + assert_eq!(json.mac, "aa:bb:cc:dd:ee:ff"); + assert_eq!(json.error, err.to_string()); +} + +#[test] +fn success_json_serializes_with_stable_field_names() { + let result = ResolveResult { + mac: mac(), + ip: ip(), + source: MatchSource::Arp, + }; + let value = serde_json::to_value(success_json(&result)).unwrap(); + assert_eq!(value["status"], "ok"); + assert_eq!(value["mac"], "aa:bb:cc:dd:ee:ff"); + assert_eq!(value["ip"], "192.168.1.5"); + assert_eq!(value["source"], "arp"); +} + +#[test] +fn failure_json_serializes_with_stable_field_names() { + let err = Mac2IpError::NotFound { + mac: mac().to_string(), + }; + let value = serde_json::to_value(failure_json(&mac(), &err)).unwrap(); + assert_eq!(value["status"], "error"); + assert_eq!(value["mac"], "aa:bb:cc:dd:ee:ff"); + assert!(value["error"].is_string()); +} diff --git a/tests/mac_address.rs b/tests/mac_address.rs new file mode 100644 index 0000000..f26c534 --- /dev/null +++ b/tests/mac_address.rs @@ -0,0 +1,65 @@ +use mac2ip::mac::MacAddress; + +#[test] +fn parses_lowercase_colon_separated() { + let mac = MacAddress::parse("aa:bb:cc:dd:ee:ff").unwrap(); + assert_eq!(mac.to_lower_colon(), "aa:bb:cc:dd:ee:ff"); +} + +#[test] +fn parses_uppercase_colon_separated() { + let mac = MacAddress::parse("AA:BB:CC:DD:EE:FF").unwrap(); + assert_eq!(mac.to_lower_colon(), "aa:bb:cc:dd:ee:ff"); + assert_eq!(mac.to_upper_colon(), "AA:BB:CC:DD:EE:FF"); +} + +#[test] +fn parses_hyphen_separated() { + let mac = MacAddress::parse("aa-bb-cc-dd-ee-ff").unwrap(); + assert_eq!(mac.to_lower_colon(), "aa:bb:cc:dd:ee:ff"); +} + +#[test] +fn parses_mixed_case() { + let mac = MacAddress::parse("Aa:bB:Cc:dD:eE:fF").unwrap(); + assert_eq!(mac.to_lower_colon(), "aa:bb:cc:dd:ee:ff"); +} + +#[test] +fn different_notations_are_equal_after_canonicalization() { + let a = MacAddress::parse("AA:BB:CC:DD:EE:FF").unwrap(); + let b = MacAddress::parse("aa-bb-cc-dd-ee-ff").unwrap(); + assert_eq!(a, b); +} + +#[test] +fn from_str_matches_parse() { + use std::str::FromStr; + let mac: MacAddress = "aa:bb:cc:dd:ee:ff".parse().unwrap(); + assert_eq!(mac, MacAddress::from_str("aa:bb:cc:dd:ee:ff").unwrap()); +} + +#[test] +fn rejects_too_short() { + assert!(MacAddress::parse("aa:bb:cc:dd:ee").is_err()); +} + +#[test] +fn rejects_too_long() { + assert!(MacAddress::parse("aa:bb:cc:dd:ee:ff:00").is_err()); +} + +#[test] +fn rejects_non_hex_characters() { + assert!(MacAddress::parse("gg:bb:cc:dd:ee:ff").is_err()); +} + +#[test] +fn rejects_wrong_separator_count() { + assert!(MacAddress::parse("aabbccddeeff").is_err()); +} + +#[test] +fn rejects_empty_string() { + assert!(MacAddress::parse("").is_err()); +} diff --git a/tests/nmap_parsing.rs b/tests/nmap_parsing.rs new file mode 100644 index 0000000..4f5ec73 --- /dev/null +++ b/tests/nmap_parsing.rs @@ -0,0 +1,74 @@ +use mac2ip::mac::MacAddress; +use mac2ip::network::parse_nmap_output; + +fn mac(s: &str) -> MacAddress { + MacAddress::parse(s).unwrap() +} + +#[test] +fn finds_matching_host_among_multiple_blocks() { + let output = "\ +Nmap scan report for 192.168.1.4 +Host is up (0.0010s latency). +MAC Address: 11:22:33:44:55:66 (Some Vendor) +Nmap scan report for 192.168.1.5 +Host is up (0.0020s latency). +MAC Address: AA:BB:CC:DD:EE:FF (Other Vendor) +Nmap scan report for 192.168.1.6 +Host is up (0.0030s latency). +MAC Address: 77:88:99:AA:BB:CC (Third Vendor) +"; + let ip = parse_nmap_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn host_without_mac_line_does_not_leak_into_next_block() { + let output = "\ +Nmap scan report for 192.168.1.4 +Host is up (0.0010s latency). +Nmap scan report for 192.168.1.5 +Host is up (0.0020s latency). +MAC Address: AA:BB:CC:DD:EE:FF (Vendor) +"; + // Even if we searched for a MAC that never appears, the missing MAC line for + // 192.168.1.4 must not accidentally get associated with 192.168.1.5's MAC. + assert!(parse_nmap_output(output, &mac("11:22:33:44:55:66")).is_none()); + let ip = parse_nmap_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn parses_hostname_plus_ip_form() { + let output = "\ +Nmap scan report for myhost.lan (192.168.1.5) +Host is up (0.0020s latency). +MAC Address: AA:BB:CC:DD:EE:FF (Vendor) +"; + let ip = parse_nmap_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn is_case_insensitive_against_uppercase_nmap_output() { + let output = "\ +Nmap scan report for 192.168.1.5 +MAC Address: AA:BB:CC:DD:EE:FF (Vendor) +"; + let ip = parse_nmap_output(output, &mac("aa:bb:cc:dd:ee:ff")).unwrap(); + assert_eq!(ip.to_string(), "192.168.1.5"); +} + +#[test] +fn returns_none_when_no_match() { + let output = "\ +Nmap scan report for 192.168.1.4 +MAC Address: 11:22:33:44:55:66 (Vendor) +"; + assert!(parse_nmap_output(output, &mac("aa:bb:cc:dd:ee:ff")).is_none()); +} + +#[test] +fn empty_output_returns_none() { + assert!(parse_nmap_output("", &mac("aa:bb:cc:dd:ee:ff")).is_none()); +} diff --git a/tests/subnet_parsing.rs b/tests/subnet_parsing.rs new file mode 100644 index 0000000..eeaea2a --- /dev/null +++ b/tests/subnet_parsing.rs @@ -0,0 +1,40 @@ +use mac2ip::network::parse_local_subnets; + +#[test] +fn extracts_cidrs_from_multiple_interfaces() { + let output = "\ +192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.23 +10.0.0.0/8 dev wlan0 proto kernel scope link src 10.0.0.5 +"; + let subnets = parse_local_subnets(output); + assert_eq!( + subnets, + vec!["192.168.1.0/24".to_string(), "10.0.0.0/8".to_string()] + ); +} + +#[test] +fn excludes_default_route() { + let output = "\ +default via 192.168.1.1 dev eth0 +192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.23 +"; + let subnets = parse_local_subnets(output); + assert_eq!(subnets, vec!["192.168.1.0/24".to_string()]); +} + +#[test] +fn excludes_loopback_interface() { + let output = "\ +127.0.0.0/8 dev lo proto kernel scope link src 127.0.0.1 +192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.23 +"; + let subnets = parse_local_subnets(output); + assert_eq!(subnets, vec!["192.168.1.0/24".to_string()]); +} + +#[test] +fn empty_input_returns_empty_vec() { + let subnets = parse_local_subnets(""); + assert!(subnets.is_empty()); +} From b45afb921b89b7e1ec176e4e4a5277c19ddd111c Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:29:04 +0200 Subject: [PATCH 05/18] README.md und AGENTS.md auf mac2ip aktualisieren MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README.md: CLI-Nutzung, Konfigurationsdatei, Caching/TTL und Voraussetzungen (ip/ping/nmap/sudo) dokumentiert; Template-Checkliste entfernt, generische Paketierungs-/CI-CD-Abschnitte beibehalten. AGENTS.md: Projektübersicht auf den 3-Stufen-Algorithmus und die neue Modulstruktur umgeschrieben; veralteten Docker/TARGET_BIN-Hinweis entfernt (keiner der Workflows baut tatsächlich ein Docker-Image); Paketierungs- und Skript-Abschnitte um die neuen Laufzeit-Abhängigkeiten und das Arch-Install-Skriptlet ergänzt. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0118Wbg95ADSDynYfci2qUjK --- AGENTS.md | 45 ++++++++++---- README.md | 171 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 155 insertions(+), 61 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3b7ff31..686989a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,9 +6,19 @@ Dieses Dokument dient als technischer Leitfaden und Kontextdokument für KI-Codi ## 1. Projektübersicht & Philosophie -Dieses Repository ist ein **Rust-Projekt-Template** für Linux-Anwendungen und CLI-Tools mit Fokus auf: +**mac2ip** ist ein CLI-Tool, das zuverlässig die aktuelle IP-Adresse zu einer gegebenen MAC-Adresse im lokalen Netzwerk findet, über einen 3-stufigen Algorithmus: + +1. **Cache** (`src/cache.rs`): globaler, systemweiter Cache auf Basis der `turso`-Crate (lokale Datei, Standard `/var/lib/mac2ip/cache.db`). Ein Treffer wird nur verwendet, wenn er nicht abgelaufen ist (TTL, `src/config.rs`) **und** die IP per Ping erreichbar ist. +2. **`ip neigh`** (`src/network.rs`): moderner Ersatz für `arp`. Treffer nur bei Ping-Erreichbarkeit. +3. **`nmap -sn`** (`src/network.rs`): ARP-/Ping-Scan der lokal angeschlossenen Subnetze. MAC-Adressen erscheinen in der nmap-Ausgabe nur mit Root-Rechten (Raw-Socket/libpcap), daher läuft dieser Schritt über `sudo nmap` (bzw. `nmap` direkt, falls schon root). Kein Treffer → Fehler ("nicht gefunden"). + +Die Orchestrierung dieser drei Schritte liegt in `src/resolver.rs`; `src/main.rs` ist nur ein dünner Entry-Point (CLI-Parsing via `src/cli.rs`, Konfiguration via `src/config.rs` + `config-ctdra`, Logging via `src/log.rs` + `logger-ctdra`). + +Zur Laufzeit werden folgende System-Tools benötigt: `ip` (iproute2), `ping` (iputils), `nmap`, `sudo` (nur für Schritt 3, falls nicht schon root). Diese sind in den Paketierungs-Metadaten (`Cargo.toml`, siehe §3.2) als Abhängigkeiten hinterlegt. + +Das Projekt basiert auf einem generischen **Rust-Projekt-Template** für Linux-Anwendungen/CLI-Tools mit Fokus auf: - Automatisierte Multi-Architektur-Kompilierung (`x86_64`, `aarch64`, `i686`). -- Native Paketierung für Debian (`.deb`), Fedora/RHEL (`.rpm`) und Arch Linux (`.pkg.tar.zst`) sowie Docker-Container-Images. +- Native Paketierung für Debian (`.deb`), Fedora/RHEL (`.rpm`) und Arch Linux (`.pkg.tar.zst`). - Vollständig automatisierte CI/CD-Pipelines via Gitea Actions (kompatibel mit Forgejo / GitHub Actions). - Automatisierte Sicherheits-Scans (Schwachstellen, Secrets) und Dependency-Updates. @@ -16,7 +26,6 @@ Dieses Repository ist ein **Rust-Projekt-Template** für Linux-Anwendungen und C - **Sprache**: Rust (Edition 2024), Python 3 (für Hilfsskripte in `scripts/`). - **Rust Toolchain**: Stable. - **Zielplattform**: Linux (GLIBC-basiert, Cross-Kompilierung für `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `i686-unknown-linux-gnu`). -- **Container**: Docker-Images werden zusätzlich zu den nativen Paketen gebaut und in die Gitea Container Registry veröffentlicht. - **Sicherheits-Tooling**: Trivy, OSV-Scanner, TruffleHog (Secret-Scanning), Renovate (Dependency-Updates), Qodana (statische Codeanalyse). - **Lizenz**: GPL-3.0-or-later (sofern nicht im abgeleiteten Projekt anders definiert). @@ -29,27 +38,40 @@ Dieses Repository ist ein **Rust-Projekt-Template** für Linux-Anwendungen und C │ └── config.toml # Linker für Cross-Target-Kompilierung & Registry-Konfiguration ├── .gitea/ │ └── workflows/ -│ ├── main.yaml # CI/CD: Stabile Builds, Multi-Arch-Paketierung, Docker-Image, Release & Upload -│ ├── testing.yaml # CI/CD: Preview-Builds, Docker-Image & Testing-Pakete +│ ├── main.yaml # CI/CD: Stabile Builds, Multi-Arch-Paketierung, Release & Upload +│ ├── testing.yaml # CI/CD: Preview-Builds & Testing-Pakete │ ├── unit-tests.yaml # CI: Unit-Tests für Pull Requests gegen 'testing' │ ├── security-scan.yaml # CI: Trivy & OSV-Scanner (Schwachstellen/Misconfig/Secrets) │ ├── trufflehog-scan.yaml # CI: TruffleHog Secret-Scan (inkl. Git-Historie) │ └── renovate.yaml # CI: Wöchentlicher Renovate-Lauf für Dependency-Updates +├── packaging/ +│ ├── deb/postinst # Debian-Postinstall: legt /var/lib/mac2ip an +│ └── arch/mac2ip.install # Arch-Install-Skriptlet: legt /var/lib/mac2ip an ├── scripts/ │ ├── get-build-number.py # Ermittelt automatisch die nächste Revisions-/Build-Nummer -│ ├── package-arch.py # Erzeugt native Arch Linux .pkg.tar.zst Pakete +│ ├── package-arch.py # Erzeugt native Arch Linux .pkg.tar.zst Pakete (inkl. optionalem Install-Skriptlet) │ └── report-security-issue.py # Meldet Scan-Ergebnisse (Trivy/OSV/TruffleHog) als Gitea-Issue ├── src/ -│ └── main.rs # Einstiegspunkt der Anwendung +│ ├── main.rs # Einstiegspunkt (CLI-Parsing, Wiring) +│ ├── lib.rs # Modul-Wurzel (für Integrationstests) +│ ├── cli.rs # clap-Kommandozeilen-Definition +│ ├── config.rs # AppConfig + config-ctdra-Integration + CLI-Overlay +│ ├── log.rs # JSON-Modus-bewusster logger-ctdra-Wrapper +│ ├── mac.rs # MacAddress-Typ (Parsing/Kanonisierung) +│ ├── cache.rs # Globaler Turso-Cache (MAC -> IP) +│ ├── network.rs # ip neigh / ping / nmap: Exec- und Parse-Funktionen +│ ├── resolver.rs # 3-Stufen-Algorithmus (Orchestrierung) +│ └── output.rs # Human- und JSON-Ausgabe +├── tests/ # Integrationstests (pure Parsing-/Logik-Funktionen, kein Netzwerk/root/nmap nötig) ├── Cargo.toml # Projekt-Manifest & Metadaten für deb, rpm und arch ├── qodana.yaml # Konfiguration für JetBrains Qodana (statische Analyse) ├── renovate.json # Renovate-Konfiguration (Gruppierung, Versions-Pins in Workflows) ├── LICENSE # Lizenztext -├── README.md # Benutzerdokumentation & Setup-Checkliste +├── README.md # Benutzerdokumentation └── AGENTS.md # Dieses Agenten-Handbuch ``` -> **Hinweis:** `main.yaml`/`testing.yaml` bauen zusätzlich ein Docker-Image (`docker build .` mit `--build-arg TARGET_BIN=...`). Ein `Dockerfile` ist im Template noch **nicht** enthalten und muss von abgeleiteten Projekten ergänzt werden; der aktuell hartkodierte `TARGET_BIN`-Pfad (`.../release/mirror-package`) ist ein Platzhalter aus einem Referenzprojekt und muss beim Ableiten des Templates auf den tatsächlichen Binärnamen (`Cargo.toml` → `[package] name`) angepasst werden. +> **Hinweis:** Dieses Projekt baut aktuell **kein** Docker-Image — keiner der Workflows unter `.gitea/workflows/` enthält einen Docker-Build-Schritt. Sollte das zukünftig ergänzt werden, muss ein `Dockerfile` hinzugefügt und der Binärname konsistent mit `[package] name` in `Cargo.toml` gehalten werden. --- @@ -68,12 +90,13 @@ Bei Änderungen an Binärnamen, Abhängigkeiten oder Beschreibungen müssen die 2. `[package.metadata.generate-rpm]` (für `cargo-generate-rpm`): - `requires`, `assets`. 3. `[package.metadata.arch]` (für `scripts/package-arch.py`): - - `pkgrel`, `arch`, `depends`, `optdepends`. + - `pkgrel`, `arch`, `depends`, `optdepends`, optional `install_script` (Pfad zu einem pacman-Install-Skriptlet, siehe §3.3). -Ändert sich der Binärname (`[package] name`), muss auch der `TARGET_BIN`-Build-Arg im Docker-Build-Step von `main.yaml`/`testing.yaml` sowie das (abzuleitende) `Dockerfile` angepasst werden. +Alle drei Blöcke listen bei mac2ip zusätzlich `sudo`, `iproute2`/`iproute`, `iputils`/`iputils-ping` und `nmap` als Laufzeit-Abhängigkeiten (benötigt für den 3-Stufen-Algorithmus, siehe §1). `[package.metadata.deb].maintainer-scripts` sowie `[package.metadata.generate-rpm].post_install_script` legen beim Paket-Install `/var/lib/mac2ip` mit den nötigen Rechten an (siehe §1, "Caching & TTL" in README.md). ### 3.3 Skripte in `scripts/` - **Generizität**: Die Skripte dürfen keine hardcodierten Anwendungsnamen, spezifischen Abhängigkeiten oder projektspezifischen URLs enthalten. Alle Werte müssen dynamisch aus `Cargo.toml` (via `cargo metadata` oder Dateiparsing) oder Umgebungsvariablen (`BUILD_NUMBER`, `GITEA_URL`, `REPO`, `TOKEN`) ermittelt werden. +- **`package-arch.py`-Install-Skriptlet**: Liest optional `[package.metadata.arch].install_script` aus `cargo metadata` und bettet die referenzierte Datei pacman-konform als `.install` (mit `install = .install` in `.PKGINFO`) in das erzeugte `.pkg.tar.zst` ein — weiterhin vollständig metadatengetrieben, kein hartkodierter Anwendungsname im Skript selbst. - **Python-Kompatibilität**: Verwende Standard-Python 3 ohne externe PyPI-Abhängigkeiten (nur Standardbibliothek: `json`, `subprocess`, `urllib`, `argparse`, `os`, `re`, `tempfile`, `tarfile` etc.). - **`get-build-number.py`**: Ermittelt die nächste Build-/Revisions-Nummer nicht mehr rein lokal, sondern dynamisch über: 1. Gitea Releases API (Tag-/Asset-Namen), diff --git a/README.md b/README.md index 56e1fe8..346b84a 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,110 @@ -# rust-template +# mac2ip -Ein modernes Template-Projekt für Rust-basierte Linux-Anwendungen und Kommandozeilen-Tools (CLI). +Ein Kommandozeilen-Tool (CLI), das zuverlässig die aktuelle IP-Adresse zu einer gegebenen MAC-Adresse im lokalen Netzwerk findet. -Dieses Template bietet eine vorkonfigurierte Umgebung für modernes Rust (Edition 2024), automatisierte Multi-Architektur-Kompilierung und native Paketierung für die gängigsten Linux-Distributionen (Debian/Ubuntu, Fedora/RHEL, Arch Linux) sowie vollständige CI/CD-Pipelines für Gitea Actions (kompatibel mit Forgejo und GitHub Actions). +Dazu wird ein 3-stufiger Algorithmus verwendet: + +1. **Cache**: Ein globaler, systemweiter Cache (für alle Nutzer dieses Rechners) wird geprüft. Ein Treffer wird nur verwendet, wenn er noch nicht abgelaufen ist (TTL) **und** die IP per Ping erreichbar ist. +2. **`ip neigh`**: Die Linux-Nachbartabelle (`ip neigh show`, der moderne Ersatz für den veralteten `arp`-Befehl) wird nach der MAC-Adresse durchsucht. Ein Treffer wird nur verwendet, wenn die IP per Ping erreichbar ist. +3. **`nmap`**: Als letzter Schritt wird ein Ping-/ARP-Scan (`nmap -sn`) der lokal angeschlossenen Subnetze durchgeführt. Da MAC-Adressen in der nmap-Ausgabe nur mit Root-Rechten sichtbar sind (ARP-Scans benötigen Raw-Socket-/libpcap-Zugriff), läuft dieser Schritt über `sudo nmap` (bzw. direkt `nmap`, falls das Programm bereits als root läuft). Wird auch hier keine erreichbare IP gefunden, bricht das Programm mit einem Fehler ("nicht gefunden") ab. --- ## Features -- **Rust Edition 2024**: Moderner Rust-Sprachstandard. -- **Multi-Architektur-Kompilierung**: - - `x86_64-unknown-linux-gnu` (64-Bit x86) - - `aarch64-unknown-linux-gnu` (64-Bit ARM / ARM64) - - `i686-unknown-linux-gnu` (32-Bit x86) -- **Linux-Paketierung out-of-the-box**: - - **Debian / Ubuntu** (`.deb` via `cargo-deb`) - - **Fedora / RHEL / openSUSE** (`.rpm` via `cargo-generate-rpm`) - - **Arch Linux** (`.pkg.tar.zst` via mitgeliefertem `scripts/package-arch.py`) -- **Automatisierte CI/CD-Pipelines**: - - `main`-Branch: Erstellt stabile Builds, ermittelt dynamisch Build-Nummern, paketiert für alle Architekturen, lädt Pakete in die Gitea Package Registry und erstellt Gitea Releases mit Dateianhängen. - - `testing`-Branch: Erstellt Preview-Builds und Pre-Releases mit Testing-Paketen. -- **Automatisierte Versions- & Build-Nummern**: - - `scripts/get-build-number.py` fragt Gitea Releases, die Package Registry sowie lokale Artefakte ab, um Revisions-/Release-Nummern (z. B. `1.0.0-1`, `1.0.0-2`) automatisch zu erhöhen. -- **Vorkonfigurierte Cargo-Einstellungen**: - - `.cargo/config.toml` mit vorkonfigurierten Cross-Compilation-Linkern und Unterstützung für private/öffentliche Cargo Registries. +- **Zuverlässige MAC → IP-Auflösung** über Cache, `ip neigh` und `nmap`, jeweils mit Erreichbarkeitsprüfung per Ping. +- **Globaler, systemweiter Cache** (via [`turso`](https://turso.tech), lokal-dateibasiert) mit konfigurierbarer TTL. +- **Maschinenlesbare Ausgabe** über `--json` (unattended-Modus) — unterdrückt dabei alle sonstigen Log-Ausgaben. +- **Vollständig über die Kommandozeile konfigurierbar**, mit Overlay-Kette CLI > Umgebungsvariable > Konfigurationsdatei > Standardwert. +- **Funktioniert mit und ohne `sudo`** — Root-Rechte werden nur für den nmap-Schritt benötigt. +- **Rust Edition 2024**, Multi-Architektur-Kompilierung (`x86_64`, `aarch64`, `i686`), native Linux-Paketierung (`.deb`, `.rpm`, `.pkg.tar.zst`) und automatisierte CI/CD-Pipelines via Gitea Actions. + +--- + +## CLI-Nutzung + +```bash +mac2ip [OPTIONEN] +``` + +Beispiel: + +```bash +$ mac2ip aa:bb:cc:dd:ee:ff +aa:bb:cc:dd:ee:ff -> 192.168.1.42 (Quelle: cache) +``` + +Maschinenlesbare Ausgabe (unattended-Modus): + +```bash +$ mac2ip aa:bb:cc:dd:ee:ff --json +{"status":"ok","mac":"aa:bb:cc:dd:ee:ff","ip":"192.168.1.42","source":"cache"} +``` + +Bei Fehlern (`status: "error"`) ist `source` nicht enthalten, dafür ein `error`-Feld mit einer Beschreibung; der Exit-Code ist in beiden Fällen ungleich 0 (`1`) bei Fehlschlag. + +### Optionen + +| Option | Umgebungsvariable | Standard | Beschreibung | +| :--- | :--- | :--- | :--- | +| `--json` | - | aus | Gibt das Ergebnis als einzeiliges JSON-Objekt aus; unterdrückt alle sonstigen Log-Ausgaben. | +| `--config ` | - | siehe unten | Benutzerdefinierter Pfad zur Konfigurationsdatei. | +| `--log-level ` | `MAC2IP_LOG_LEVEL` | `info` | Logging-Level: `error`, `warn`, `info`, `debug`. | +| `--cache-ttl-seconds ` | `MAC2IP_CACHE_TTL_SECONDS` | `1800` | Wie lange ein Cache-Eintrag als gültig angesehen wird (zusätzlich zur Ping-Prüfung). | +| `--cache-db-path ` | `MAC2IP_CACHE_DB_PATH` | `/var/lib/mac2ip/cache.db` | Pfad zur globalen Cache-Datenbankdatei. | +| `--nmap-timeout-seconds ` | `MAC2IP_NMAP_TIMEOUT_SECONDS` | `120` | Timeout für einen einzelnen nmap-Subnetz-Scan. | +| `--networks ` | `MAC2IP_NETWORKS` | Auto-Erkennung | Kommagetrennte Liste von Subnetzen für den nmap-Scan; überschreibt die automatische Erkennung über `ip route`. | + +Die Überschreibungs-Reihenfolge ist immer: **CLI-Flag > Umgebungsvariable > Konfigurationsdatei > Standardwert.** + +--- + +## Konfigurationsdatei + +Die Konfiguration wird über [`config-ctdra`](https://gitea.creative-dragonslayer.de/Rust-Crates) verwaltet: + +- Als **root** (bzw. beim nmap-Schritt via `sudo`): `/etc/mac2ip/config.toml` +- Als **normaler Nutzer**: `~/.config/mac2ip/config.toml` +- Oder explizit über `--config ` + +Beispiel: + +```toml +cache_ttl_seconds = 1800 +cache_db_path = "/var/lib/mac2ip/cache.db" +log_level = "info" +nmap_timeout_seconds = 120 +networks = [] +``` + +Alle Felder sind optional (fehlende Felder verwenden den Standardwert) und können, wie oben beschrieben, zusätzlich per CLI-Flag oder Umgebungsvariable überschrieben werden. + +--- + +## Caching & TTL + +Der Cache liegt standardmäßig unter `/var/lib/mac2ip/cache.db` — **global für alle Nutzer des Rechners**, nicht pro Benutzerkonto. Verzeichnis und Datei sind bewusst world-writable (`0777`/`0666`), damit auch unprivilegierte Nutzer den Cache lesen und schreiben können, ohne dass mac2ip dafür Root-Rechte bräuchte. + +Ein Cache-Treffer wird nur verwendet, wenn: +1. der Eintrag noch nicht älter als `cache_ttl_seconds` ist (Standard: 30 Minuten), **und** +2. die gespeicherte IP-Adresse aktuell per Ping erreichbar ist. + +Die Ping-Prüfung ist die primäre Absicherung gegen veraltete Zuordnungen; die TTL ist eine zusätzliche Absicherung für den Fall, dass eine alte IP-Adresse inzwischen an ein anderes, ebenfalls erreichbares Gerät vergeben wurde (z. B. nach einem DHCP-Lease-Wechsel). + +Kann das Cache-Verzeichnis beim Programmstart nicht angelegt/beschrieben werden (z. B. bei einem Entwicklungslauf ohne vorherige Paketinstallation), wird der Cache für diesen Lauf einfach deaktiviert (eine Warnung wird geloggt, im `--json`-Modus unterdrückt) — mac2ip führt den Lookup dann ohne Cache über `ip neigh`/`nmap` durch. Bei einer Installation über `.deb`/`.rpm`/`.pkg.tar.zst` wird das Verzeichnis automatisch mit den richtigen Rechten angelegt (siehe Paketierungs-Postinstall-Skripte unter `packaging/`). + +--- + +## Voraussetzungen + +Neben Rust/Cargo zur Laufzeit benötigt werden folgende System-Tools: + +- `ip` (Paket `iproute2`) — für Schritt 2 (`ip neigh`) und die automatische Subnetz-Erkennung. +- `ping` (Paket `iputils`/`iputils-ping`) — für die Erreichbarkeitsprüfung. +- `nmap` — für Schritt 3. +- `sudo` — nur nötig, wenn Schritt 3 erreicht wird und das Programm nicht bereits als root läuft. + +Im `--json`-Modus wird für Schritt 3 ausschließlich `sudo -n` (nicht-interaktiv) verwendet, damit das Programm niemals interaktiv nach einem Passwort fragt und dadurch ein Skript blockiert. Ist keine gültige sudo-Sitzung/NOPASSWD-Regel vorhanden, wird Schritt 3 abgebrochen und das Ergebnis als "nicht gefunden" zurückgegeben. Im interaktiven (Nicht-JSON-)Modus darf `sudo` regulär nach einem Passwort fragen. --- @@ -36,49 +117,39 @@ Dieses Template bietet eine vorkonfigurierte Umgebung für modernes Rust (Editio │ └── workflows/ │ ├── main.yaml # CI/CD-Workflow für stabile Releases (main-Branch) │ └── testing.yaml # CI/CD-Workflow für Pre-Releases (testing-Branch) +├── packaging/ +│ ├── deb/postinst # Debian-Postinstall: legt /var/lib/mac2ip an +│ └── arch/mac2ip.install # Arch-Install-Skriptlet: legt /var/lib/mac2ip an ├── scripts/ │ ├── get-build-number.py # Dynamische Ermittlung der nächsten Paket-Revisionsnummer │ └── package-arch.py # Erstellung von Arch Linux .pkg.tar.zst Paketen ├── src/ -│ └── main.rs # Quellcode & Einstiegspunkt der Anwendung -├── Cargo.toml # Cargo Manifest & Paketierungsmetadaten (deb, rpm, arch) -├── LICENSE # Lizenzdatei (Standard: GPL-3.0-or-later) -├── AGENTS.md # Richtlinien und Leitfaden für KI-Coding-Agenten -└── README.md # Projektdokumentation +│ ├── main.rs # Einstiegspunkt (CLI-Parsing, Wiring) +│ ├── lib.rs # Modul-Wurzel (für Integrationstests) +│ ├── cli.rs # clap-Kommandozeilen-Definition +│ ├── config.rs # AppConfig + config-ctdra-Integration + CLI-Overlay +│ ├── log.rs # JSON-Modus-bewusster logger-ctdra-Wrapper +│ ├── mac.rs # MacAddress-Typ (Parsing/Kanonisierung) +│ ├── cache.rs # Globaler Turso-Cache (MAC -> IP) +│ ├── network.rs # ip neigh / ping / nmap: Exec- und Parse-Funktionen +│ ├── resolver.rs # 3-Stufen-Algorithmus (Orchestrierung) +│ └── output.rs # Human- und JSON-Ausgabe +├── tests/ # Integrationstests (pure Parsing-/Logik-Funktionen) +├── Cargo.toml # Cargo Manifest & Paketierungsmetadaten (deb, rpm, arch) +├── LICENSE # Lizenzdatei (Standard: GPL-3.0-or-later) +├── AGENTS.md # Richtlinien und Leitfaden für KI-Coding-Agenten +└── README.md # Projektdokumentation ``` --- -## Checkliste zur Verwendung als Template - -Wenn du ein neues Projekt aus diesem Template erstellst, gehe folgende Schritte durch: - -1. **`Cargo.toml` anpassen**: - - `name`: Den Namen deiner Anwendung setzen. - - `version`: Initiale Version festlegen (z. B. `0.1.0`). - - `authors`, `repository`, `description`, `license`: Projektdaten eintragen. - - Paketierungsabschnitte prüfen: - - `[package.metadata.deb]`: `maintainer`, `copyright`, `section`, `extended-description` setzen. - - `[package.metadata.generate-rpm]`: `requires` anpassen. - - `[package.metadata.arch]`: `depends` und `optdepends` anpassen. -2. **`src/` implementieren**: - - Eigene Anwendungslogik in `src/main.rs` (bzw. Modulen / `src/lib.rs`) implementieren. - - Tests in `src/` oder `tests/` ergänzen. -3. **`.cargo/config.toml` prüfen**: - - Falls crates.io statt einer privaten Registry genutzt werden soll, den Standard-Registry-Eintrag anpassen oder auskommentieren. -4. **CI/CD Secrets konfigurieren**: - - In den Repository-Einstellungen von Gitea/Forgejo ein Secret `PACKAGE_TOKEN` (bzw. `GITEA_TOKEN`) mit Rechten für Pakete und Releases hinterlegen. -5. **Dokumentation aktualisieren**: - - `README.md` an die konkrete Funktionsweise deiner Anwendung anpassen. - ---- - ## Lokale Entwicklung ### Voraussetzungen - **Rust & Cargo** (aktuelle Stable-Version, Edition 2024 unterstützt) - **Python 3** (für Hilfsskripte in `scripts/`) +- Zur Laufzeit: `ip`, `ping`, `nmap`, `sudo` (siehe "Voraussetzungen" oben) - Für Cross-Compilation (optional): - `rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu i686-unknown-linux-gnu` - Cross-Toolchains: `gcc-aarch64-linux-gnu`, `gcc-i686-linux-gnu` @@ -126,6 +197,7 @@ Erstellt native Arch Linux-Pakete (`.pkg.tar.zst`), ohne dass `makepkg` oder ein - Liest Konfiguration aus `[package.metadata.arch]` in `Cargo.toml`. - Installiert die Binary nach `/usr/bin/`, sowie `LICENSE` und `README.md` nach `/usr/share/`. - Erzeugt eine standardkonforme `.PKGINFO`-Datei und komprimiert das Paket mit `zstandard`. +- Unterstützt optional ein Install-Skriptlet (`[package.metadata.arch].install_script`), das pacman-konform als `.install` mit `post_install()`/`post_upgrade()` eingebettet wird (bei mac2ip: legt `/var/lib/mac2ip` an). - Parameter: - `--target`: Rust Target-Triple (z. B. `x86_64-unknown-linux-gnu`) - `--arch`: Zielarchitektur (z. B. `x86_64`, `aarch64`, `i686`) @@ -144,5 +216,4 @@ Erstellt native Arch Linux-Pakete (`.pkg.tar.zst`), ohne dass `makepkg` oder ein ## Lizenz -Dieses Template steht standardmäßig unter der [GPL-3.0-or-later](LICENSE)-Lizenz. Die Lizenz kann bei Bedarf in `LICENSE` und `Cargo.toml` angepasst werden. - +mac2ip steht unter der [GPL-3.0-or-later](LICENSE)-Lizenz. From 464262265c2d09474a7d5a93ebab78f898e6fa8c Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 18:33:43 +0200 Subject: [PATCH 06/18] =?UTF-8?q?Feat:=20F=C3=BCgt=20`tests/`=20als=20Test?= =?UTF-8?q?-Ordner=20f=C3=BCr=20RustRover=20hinzu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/rust-template.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/rust-template.iml b/.idea/rust-template.iml index cf84ae4..bbe0a70 100644 --- a/.idea/rust-template.iml +++ b/.idea/rust-template.iml @@ -3,6 +3,7 @@ + From 8d47f957b6673d0329ecda4b5129de8726ac9e81 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 19:21:11 +0200 Subject: [PATCH 07/18] =?UTF-8?q?Fix:=20chmod=200777=20auf=20/var/lib/mac2?= =?UTF-8?q?ip=20nur=20beim=20Neuanlegen=20ausf=C3=BChren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zuvor wurde das explizite chmod bei jedem Aufruf ausgeführt, auch wenn das Verzeichnis bereits existierte. Bei einem bereits root-eigenen, durch die Paketierungs-Skripte angelegten Verzeichnis scheitert das chmod für unprivilegierte Nutzer mit EPERM, wodurch der Cache bei jedem Lauf stillschweigend deaktiviert wurde. Das chmod läuft jetzt nur direkt nach create_dir_all(), also ausschließlich wenn der Prozess das Verzeichnis selbst neu anlegt. Ergänzt Unit-Tests für beide Fälle (bestehendes Systemverzeichnis, Neuanlage). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- src/cache.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index 833626b..ee7f7f0 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -109,12 +109,14 @@ fn ensure_cache_dir(dir: &Path) -> Result<(), String> { dir.display() ) })?; + // create_dir_all() unterliegt dem Prozess-Umask; explizites chmod ist nötig, um + // wirklich 0777 zu erreichen (Verzeichnis-Rechte steuern nicht die Umask neuer Dateien). + // Wird nur beim Neuanlegen ausgeführt; bei existierenden Verzeichnissen würde ein + // unprivilegierter Aufruf auf root-eigenen Pfaden mit EPERM fehlschlagen. + #[cfg(unix)] + std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o777)) + .map_err(|e| format!("Verzeichnis-Rechte konnten nicht gesetzt werden: {e}"))?; } - // create_dir_all() unterliegt dem Prozess-Umask; explizites chmod ist nötig, um - // wirklich 0777 zu erreichen (Verzeichnis-Rechte steuern nicht die Umask neuer Dateien). - #[cfg(unix)] - std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o777)) - .map_err(|e| format!("Verzeichnis-Rechte konnten nicht gesetzt werden: {e}"))?; Ok(()) } @@ -173,3 +175,24 @@ pub async fn try_open_cache(db_path: &Path) -> Option { chmod_cache_sidecars(db_path); Some(cache) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ensure_cache_dir_existing_directory_does_not_fail() { + // Bestehende Systemverzeichnisse (die einem unprivilegierten Nutzer nicht gehören) + // dürfen beim Überprüfen keinen EPERM-Fehler werfen. + assert!(ensure_cache_dir(Path::new("/tmp")).is_ok()); + } + + #[test] + fn test_ensure_cache_dir_creates_new_directory() { + let temp_dir = std::env::temp_dir().join(format!("mac2ip_test_{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos())); + assert!(!temp_dir.exists()); + assert!(ensure_cache_dir(&temp_dir).is_ok()); + assert!(temp_dir.exists()); + let _ = std::fs::remove_dir(&temp_dir); + } +} From 7a033fa19d9d23fcf8c6bd7d02aa7b922f94387e Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 19:21:28 +0200 Subject: [PATCH 08/18] Fix: nmap-Timeout beendet auch den von sudo geforkten Enkelprozess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd.kill_on_drop(true) signalisiert bei einem Timeout nur den direkten Kind-Prozess. Im Sudo-Fall ist das `sudo` selbst, nicht das davon geforkte, als root laufende `nmap` - dieses lief als root-Waise nach Timeout unbegrenzt weiter, statt vom Timeout begrenzt zu werden. Der nmap/sudo-Prozess läuft jetzt in einer eigenen Prozessgruppe (process_group(0)). Bei einem Timeout wird per `kill -KILL -` die gesamte Gruppe (sudo + nmap) beendet statt nur des direkten Kindes. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- src/network.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/network.rs b/src/network.rs index 920bcbf..da42b20 100644 --- a/src/network.rs +++ b/src/network.rs @@ -95,10 +95,33 @@ pub async fn run_nmap_scan( // unabhängig von der System-Locale funktioniert (z. B. "Passwort ist notwendig" // auf einem deutschen System würde sonst nicht erkannt werden). cmd.env("LC_ALL", "C").env("LANG", "C"); + cmd.kill_on_drop(true); + // Eigene Prozessgruppe: `kill_on_drop`/Child::kill() signalisiert bei einem + // Timeout nur den direkten Kind-Prozess. Im Sudo-Fall ist das `sudo` selbst, + // nicht das von `sudo` geforkte (als root laufende) `nmap`. Ohne eigene + // Prozessgruppe würde `sudo` beim SIGKILL sterben, während `nmap` als + // root-Waise weiterläuft und den Scan fortsetzt. + cmd.process_group(0); - let out = tokio::time::timeout(Duration::from_secs(timeout_secs), cmd.output()) + let child = cmd.spawn()?; + let pgid = child.id(); + + let out = match tokio::time::timeout(Duration::from_secs(timeout_secs), child.wait_with_output()) .await - .map_err(|_| NmapRunError::Timeout)??; + { + Ok(out) => out?, + Err(_) => { + if let Some(pgid) = pgid { + // Negative PID = Signal an die gesamte Prozessgruppe (sudo + nmap). + let _ = tokio::process::Command::new("kill") + .arg("-KILL") + .arg(format!("-{pgid}")) + .output() + .await; + } + return Err(NmapRunError::Timeout); + } + }; if !out.status.success() { let stderr = String::from_utf8_lossy(&out.stderr); From 4ebbbc93f7943fa754990a6c1637ad3c771e3926 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 19:21:34 +0200 Subject: [PATCH 09/18] Fix: Arch-Install-Skriptlet als .INSTALL statt .install einbetten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pacman/libalpm erwartet im Paket-Archiv ein Member mit dem literalen Namen .INSTALL, nicht einen per install=-Feld in .PKGINFO referenzierten Dateinamen. package-arch.py bettete das Skriptlet bisher unter .install ein und trug zusätzlich eine install=-Zeile in .PKGINFO ein - beides entspricht nicht dem tatsächlich von pacman genutzten Format. AGENTS.md entsprechend aktualisiert. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- AGENTS.md | 2 +- scripts/package-arch.py | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 686989a..cfe1368 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,7 +96,7 @@ Alle drei Blöcke listen bei mac2ip zusätzlich `sudo`, `iproute2`/`iproute`, `i ### 3.3 Skripte in `scripts/` - **Generizität**: Die Skripte dürfen keine hardcodierten Anwendungsnamen, spezifischen Abhängigkeiten oder projektspezifischen URLs enthalten. Alle Werte müssen dynamisch aus `Cargo.toml` (via `cargo metadata` oder Dateiparsing) oder Umgebungsvariablen (`BUILD_NUMBER`, `GITEA_URL`, `REPO`, `TOKEN`) ermittelt werden. -- **`package-arch.py`-Install-Skriptlet**: Liest optional `[package.metadata.arch].install_script` aus `cargo metadata` und bettet die referenzierte Datei pacman-konform als `.install` (mit `install = .install` in `.PKGINFO`) in das erzeugte `.pkg.tar.zst` ein — weiterhin vollständig metadatengetrieben, kein hartkodierter Anwendungsname im Skript selbst. +- **`package-arch.py`-Install-Skriptlet**: Liest optional `[package.metadata.arch].install_script` aus `cargo metadata` und bettet die referenzierte Datei pacman-konform als `.INSTALL` in das erzeugte `.pkg.tar.zst` ein — weiterhin vollständig metadatengetrieben, kein hartkodierter Anwendungsname im Skript selbst. - **Python-Kompatibilität**: Verwende Standard-Python 3 ohne externe PyPI-Abhängigkeiten (nur Standardbibliothek: `json`, `subprocess`, `urllib`, `argparse`, `os`, `re`, `tempfile`, `tarfile` etc.). - **`get-build-number.py`**: Ermittelt die nächste Build-/Revisions-Nummer nicht mehr rein lokal, sondern dynamisch über: 1. Gitea Releases API (Tag-/Asset-Namen), diff --git a/scripts/package-arch.py b/scripts/package-arch.py index 2dc3387..13cf639 100755 --- a/scripts/package-arch.py +++ b/scripts/package-arch.py @@ -103,11 +103,11 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): if os.path.exists("README.md"): subprocess.run(["install", "-m", "644", "README.md", f"{doc_dir}/README.md"], check=True) - install_scriptlet_name = None + has_install_scriptlet = False if install_rel_path and os.path.exists(install_rel_path): - install_scriptlet_name = f"{name}.install" + has_install_scriptlet = True subprocess.run( - ["install", "-m", "644", install_rel_path, os.path.join(build_dir, install_scriptlet_name)], + ["install", "-m", "644", install_rel_path, os.path.join(build_dir, ".INSTALL")], check=True, ) @@ -126,8 +126,6 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): f"arch = {arch}", f"license = {license_name}", ] - if install_scriptlet_name: - pkginfo_lines.append(f"install = {install_scriptlet_name}") for dep in depends: pkginfo_lines.append(f"depend = {dep}") for optdep in optdepends: @@ -140,8 +138,8 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None): os.makedirs("target/arch", exist_ok=True) output_file = os.path.abspath(f"target/arch/{name}-{version}-{pkgrel}-{arch}.pkg.tar.zst") tar_members = [".PKGINFO"] - if install_scriptlet_name: - tar_members.append(install_scriptlet_name) + if has_install_scriptlet: + tar_members.append(".INSTALL") tar_members.append("usr") subprocess.run(["tar", "--zstd", "-cf", output_file, *tar_members], cwd=build_dir, check=True) print(f"Arch-Paket erfolgreich erstellt: {output_file}") From 1141d18f663a3ac91ba1956b7261edceeedc6d6d Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:10:51 +0200 Subject: [PATCH 10/18] =?UTF-8?q?Feat:=20Vertrauenspr=C3=BCfung=20f=C3=BCr?= =?UTF-8?q?=20Netzwerke=20vor=20nmap-Scan=20(Schritt=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein ARP-/Ping-Scan ganzer Subnetze ist auf Netzwerken, die man nicht selbst administriert (Firmen-/Gast-WLAN etc.), potenziell heikel (IDS-Alarm, Verstoß gegen Nutzungsordnungen). nmap wird deshalb künftig nur noch in explizit vertrauten Netzwerken ausgeführt. Das aktuelle Netzwerk wird über die MAC-Adresse seines Default-Gateways identifiziert (stabil über IP-/Subnetzwechsel hinweg, unabhängig von SSID). Neues Modul src/trust.rs prüft vor Schritt 3: - statisch konfigurierte Gateway-MACs (AppConfig::trusted_networks, via --trusted-networks/MAC2IP_TRUSTED_NETWORKS oder Config-Datei), - zuvor per Rückfrage bestätigte Netzwerke (persistiert in einer neuen trusted_networks-Tabelle im bestehenden globalen Cache), - automatische Bestätigung via --auto-trust-networks (merkt sich das Netzwerk danach ebenfalls im Cache). Ist nichts davon der Fall, wird im interaktiven Modus auf stderr nachgefragt; im --json-Modus ist keine Rückfrage möglich, daher wird der Scan sicherheitshalber abgelehnt (neuer Fehler Mac2IpError::UntrustedNetwork). src/network.rs: neue Exec-/Parse-Funktionen zur Gateway-MAC-Ermittlung (run_ip_route_show_default, parse_default_gateway_ip, parse_ip_neigh_mac_for_ip, detect_default_gateway_mac). Ergänzt Unit-/Integrationstests (tests/gateway_trust.rs, tests/config_overrides.rs). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- src/cache.rs | 62 ++++++++++++++++++++- src/cli.rs | 11 ++++ src/config.rs | 23 ++++++++ src/error.rs | 5 ++ src/lib.rs | 1 + src/main.rs | 10 +++- src/network.rs | 84 +++++++++++++++++++++++------ src/resolver.rs | 16 ++++++ src/trust.rs | 110 ++++++++++++++++++++++++++++++++++++++ tests/config_overrides.rs | 15 ++++++ tests/gateway_trust.rs | 94 ++++++++++++++++++++++++++++++++ 11 files changed, 414 insertions(+), 17 deletions(-) create mode 100644 src/trust.rs create mode 100644 tests/gateway_trust.rs diff --git a/src/cache.rs b/src/cache.rs index ee7f7f0..6907551 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -38,9 +38,63 @@ impl Cache { (), ) .await?; + self.conn + .execute( + "CREATE TABLE IF NOT EXISTS trusted_networks (\ + gateway_mac TEXT PRIMARY KEY, \ + trusted_at INTEGER NOT NULL\ + )", + (), + ) + .await?; Ok(()) } + /// Best-effort: gibt bei jedem Fehler `false` zurück (nur eine Warnung wird geloggt) - + /// ein Cache-Lesefehler darf ein zuvor bereits bestätigtes Netzwerk nicht plötzlich + /// als unbekannt erscheinen lassen, aber sicherheitshalber auch nicht automatisch + /// als vertrauenswürdig gelten lassen; der Aufrufer fragt in diesem Fall erneut nach. + pub async fn is_trusted_network(&self, gateway_mac: &MacAddress) -> bool { + let key = gateway_mac.to_lower_colon(); + match self + .conn + .query( + "SELECT 1 FROM trusted_networks WHERE gateway_mac = ?1", + (key,), + ) + .await + { + Ok(mut rows) => matches!(rows.next().await, Ok(Some(_))), + Err(e) => { + crate::log::warn( + "cache", + &format!("Cache-Lesefehler (trusted_networks): {e}"), + ); + false + } + } + } + + /// Best-effort Upsert: merkt sich ein Netzwerk (identifiziert über die Gateway-MAC) + /// dauerhaft als vertrauenswürdig für zukünftige nmap-Scans. + pub async fn trust_network(&self, gateway_mac: &MacAddress, now: i64) { + let key = gateway_mac.to_lower_colon(); + if let Err(e) = self + .conn + .execute( + "INSERT INTO trusted_networks (gateway_mac, trusted_at) VALUES (?1, ?2) \ + ON CONFLICT(gateway_mac) DO UPDATE SET trusted_at = excluded.trusted_at", + (key, now), + ) + .await + { + crate::log::warn( + "cache", + &format!("Cache-Schreibfehler (trusted_networks, ignoriert): {e}"), + ); + } + } + /// Best-effort: gibt bei jedem Fehler `None` zurück (nur eine Warnung wird geloggt). pub async fn get(&self, mac: &MacAddress) -> Option { let key = mac.to_lower_colon(); @@ -189,7 +243,13 @@ mod tests { #[test] fn test_ensure_cache_dir_creates_new_directory() { - let temp_dir = std::env::temp_dir().join(format!("mac2ip_test_{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos())); + let temp_dir = std::env::temp_dir().join(format!( + "mac2ip_test_{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); assert!(!temp_dir.exists()); assert!(ensure_cache_dir(&temp_dir).is_ok()); assert!(temp_dir.exists()); diff --git a/src/cli.rs b/src/cli.rs index 949755b..024e104 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -45,6 +45,17 @@ pub struct Cli { /// Kommagetrennte Liste von CIDR-Subnetzen für den nmap-Scan (überschreibt Auto-Erkennung) #[arg(long, env = "MAC2IP_NETWORKS", value_delimiter = ',')] pub networks: Option>, + + /// Kommagetrennte Liste von Gateway-MAC-Adressen, deren Netzwerke ohne Rückfrage für + /// nmap-Scans (Schritt 3) vertraut werden (überschreibt die Konfigurationsdatei vollständig) + #[arg(long, env = "MAC2IP_TRUSTED_NETWORKS", value_delimiter = ',')] + pub trusted_networks: Option>, + + /// Beantwortet die "nmap-Scan in diesem Netzwerk erlauben?"-Rückfrage vor Schritt 3 + /// automatisch mit Ja (und merkt sich das Netzwerk dauerhaft im Cache), statt + /// interaktiv nachzufragen bzw. im --json-Modus den Scan abzulehnen + #[arg(long)] + pub auto_trust_networks: bool, } #[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/config.rs b/src/config.rs index 4952700..abbc921 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,6 +4,8 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; +use crate::mac::MacAddress; + pub const DEFAULT_CACHE_TTL_SECONDS: u64 = 1800; // 30 Min: Ping-Check ist die primäre // Absicherung gegen veraltete Einträge, die TTL ist nur eine zusätzliche Absicherung // gegen den Fall, dass eine alte IP inzwischen an ein anderes, ebenfalls @@ -40,6 +42,11 @@ pub struct AppConfig { pub nmap_timeout_seconds: u64, #[serde(default)] pub networks: Vec, + /// Gateway-MAC-Adressen von Netzwerken, in denen ein nmap-Scan (Schritt 3) ohne + /// interaktive Rückfrage erlaubt ist. Wird um per Rückfrage/`--auto-trust-networks` + /// bestätigte Netzwerke ergänzt, die im Cache gespeichert werden (siehe `src/trust.rs`). + #[serde(default)] + pub trusted_networks: Vec, } impl Default for AppConfig { @@ -50,10 +57,23 @@ impl Default for AppConfig { log_level: DEFAULT_LOG_LEVEL.to_string(), nmap_timeout_seconds: DEFAULT_NMAP_TIMEOUT_SECONDS, networks: Vec::new(), + trusted_networks: Vec::new(), } } } +impl AppConfig { + /// Reine, testbare Funktion: validiert die konfigurierten vertrauten Netzwerke. + /// Nicht als MAC-Adresse parsbare Einträge werden übersprungen (kein harter Fehler, + /// da eine fehlerhafte Config-Zeile den Lookup nicht insgesamt scheitern lassen soll). + pub fn trusted_network_macs(&self) -> Vec { + self.trusted_networks + .iter() + .filter_map(|s| MacAddress::parse(s).ok()) + .collect() + } +} + /// Initialisiert den Konfigurationspfad bei `config-ctdra` (Standardname "config", /// optionaler expliziter Pfad via `--config`). pub fn init_config_path(custom_path: Option<&Path>) { @@ -90,4 +110,7 @@ pub fn apply_cli_overrides(config: &mut AppConfig, cli: &crate::cli::Cli) { if let Some(networks) = &cli.networks { config.networks = networks.clone(); } + if let Some(trusted) = &cli.trusted_networks { + config.trusted_networks = trusted.iter().map(MacAddress::to_lower_colon).collect(); + } } diff --git a/src/error.rs b/src/error.rs index e4dbe24..ed8c7c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,11 @@ use crate::mac::MacAddressError; pub enum Mac2IpError { #[error("keine IP-Adresse für MAC '{mac}' gefunden")] NotFound { mac: String }, + #[error( + "nmap-Scan abgelehnt: Netzwerk nicht vertrauenswürdig{}", + .gateway_mac.as_ref().map(|m| format!(" (Gateway-MAC {m})")).unwrap_or_default() + )] + UntrustedNetwork { gateway_mac: Option }, #[error("MAC-Adresse ungültig: {0}")] InvalidMac(#[from] MacAddressError), #[error("E/A-Fehler: {0}")] diff --git a/src/lib.rs b/src/lib.rs index 0f87c66..4e4142a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ pub mod mac; pub mod network; pub mod output; pub mod resolver; +pub mod trust; diff --git a/src/main.rs b/src/main.rs index adf8d6a..72d5ecf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,15 @@ async fn main() -> ExitCode { let cache = cache::try_open_cache(&app_config.cache_db_path).await; - match resolver::resolve(&cli.mac, &app_config, cache.as_ref(), cli.json).await { + match resolver::resolve( + &cli.mac, + &app_config, + cache.as_ref(), + cli.json, + cli.auto_trust_networks, + ) + .await + { Ok(result) => { output::print_success(&result, cli.json); ExitCode::SUCCESS diff --git a/src/network.rs b/src/network.rs index da42b20..2b06331 100644 --- a/src/network.rs +++ b/src/network.rs @@ -30,6 +30,31 @@ pub async fn run_ip_route_show_scope_link() -> std::io::Result { Ok(String::from_utf8_lossy(&out.stdout).into_owned()) } +pub async fn run_ip_route_show_default() -> std::io::Result { + let out = tokio::process::Command::new("ip") + .args(["-4", "route", "show", "default"]) + .output() + .await?; + Ok(String::from_utf8_lossy(&out.stdout).into_owned()) +} + +/// Ermittelt die MAC-Adresse des aktuellen Default-Gateways - dient als stabiler +/// Identifikator für "dieses Netzwerk" (bleibt bei DHCP-Subnetzwechsel gleich, +/// solange derselbe Router im Einsatz ist), unabhängig von SSID/CIDR. +/// Gibt `None` zurück, wenn kein Default-Gateway existiert oder dessen MAC nicht +/// ermittelt werden kann (z. B. kein Nachbar-Eintrag trotz Ping). +pub async fn detect_default_gateway_mac() -> Option { + let route_output = run_ip_route_show_default().await.ok()?; + let gateway_ip = parse_default_gateway_ip(&route_output)?; + + // Erzwingt best-effort einen frischen Nachbar-Tabelleneintrag; Ergebnis wird + // ignoriert, da `ip neigh` auch von einem veralteten/STALE-Eintrag profitiert. + let _ = ping_check(gateway_ip).await; + + let neigh_output = run_ip_neigh_show().await.ok()?; + parse_ip_neigh_mac_for_ip(&neigh_output, gateway_ip) +} + pub async fn ping_check(ip: IpAddr) -> bool { tokio::process::Command::new("ping") .args([ @@ -106,22 +131,23 @@ pub async fn run_nmap_scan( let child = cmd.spawn()?; let pgid = child.id(); - let out = match tokio::time::timeout(Duration::from_secs(timeout_secs), child.wait_with_output()) - .await - { - Ok(out) => out?, - Err(_) => { - if let Some(pgid) = pgid { - // Negative PID = Signal an die gesamte Prozessgruppe (sudo + nmap). - let _ = tokio::process::Command::new("kill") - .arg("-KILL") - .arg(format!("-{pgid}")) - .output() - .await; + let out = + match tokio::time::timeout(Duration::from_secs(timeout_secs), child.wait_with_output()) + .await + { + Ok(out) => out?, + Err(_) => { + if let Some(pgid) = pgid { + // Negative PID = Signal an die gesamte Prozessgruppe (sudo + nmap). + let _ = tokio::process::Command::new("kill") + .arg("-KILL") + .arg(format!("-{pgid}")) + .output() + .await; + } + return Err(NmapRunError::Timeout); } - return Err(NmapRunError::Timeout); - } - }; + }; if !out.status.success() { let stderr = String::from_utf8_lossy(&out.stderr); @@ -159,6 +185,34 @@ pub fn parse_ip_neigh_output(output: &str, mac: &MacAddress) -> Option { None } +/// Parst `ip -4 route show default`, z. B. "default via 192.168.1.1 dev eth0 proto dhcp +/// metric 100". Gibt die Gateway-IP zurück (Token direkt nach "via"). +pub fn parse_default_gateway_ip(output: &str) -> Option { + output.lines().find_map(|line| { + let tokens: Vec<&str> = line.split_whitespace().collect(); + let pos = tokens.iter().position(|&t| t == "via")?; + tokens.get(pos + 1)?.parse().ok() + }) +} + +/// Parst `ip neigh show`-Ausgabe und gibt die `lladdr`-MAC des Eintrags zurück, dessen +/// erste Spalte (IP) mit `target_ip` übereinstimmt. Kehrbild zu [`parse_ip_neigh_output`]. +pub fn parse_ip_neigh_mac_for_ip(output: &str, target_ip: IpAddr) -> Option { + for line in output.lines() { + let tokens: Vec<&str> = line.split_whitespace().collect(); + if tokens.first().and_then(|t| t.parse::().ok()) != Some(target_ip) { + continue; + } + if let Some(pos) = tokens.iter().position(|&t| t == "lladdr") + && let Some(&mac_str) = tokens.get(pos + 1) + && let Ok(mac) = mac_str.parse() + { + return Some(mac); + } + } + None +} + /// Extrahiert lokale CIDR-Subnetze aus `ip -4 route show scope link`. /// Die CIDR ist bereits das erste whitespace-getrennte Token pro passender Zeile. /// Überspringt `default`-Zeilen und das Loopback-Interface. diff --git a/src/resolver.rs b/src/resolver.rs index 2648753..1995611 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -8,6 +8,7 @@ use crate::config::AppConfig; use crate::error::Mac2IpError; use crate::mac::MacAddress; use crate::network::{self, NmapRunError, SudoMode}; +use crate::trust::{self, TrustDecision}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MatchSource { @@ -44,6 +45,7 @@ pub async fn resolve( config: &AppConfig, cache: Option<&Cache>, json_mode: bool, + auto_trust_networks: bool, ) -> Result { let now = now_unix(); @@ -86,6 +88,20 @@ pub async fn resolve( // Schritt 3: nmap crate::log::debug("resolver", "Kein Treffer via ip neigh, starte nmap-Scan"); + + match trust::ensure_network_trusted(config, cache, auto_trust_networks, json_mode, now).await { + TrustDecision::Allowed => {} + TrustDecision::Denied { gateway_mac } => { + crate::log::warn( + "resolver", + "nmap-Scan übersprungen: Netzwerk nicht als vertrauenswürdig bestätigt", + ); + return Err(Mac2IpError::UntrustedNetwork { + gateway_mac: gateway_mac.map(|m| m.to_string()), + }); + } + } + let subnets = if !config.networks.is_empty() { config.networks.clone() } else { diff --git a/src/trust.rs b/src/trust.rs new file mode 100644 index 0000000..7dd7075 --- /dev/null +++ b/src/trust.rs @@ -0,0 +1,110 @@ +//! Netzwerk-Vertrauensprüfung vor dem nmap-Scan (Schritt 3). +//! +//! Ein ARP-/Ping-Scan ganzer Subnetze ist auf Netzwerken, die man nicht selbst +//! administriert (Firmen-/Gast-WLAN etc.), potenziell heikel (IDS-Alarm, Verstoß gegen +//! Nutzungsordnungen). Das aktuelle Netzwerk wird daher über die MAC-Adresse seines +//! Default-Gateways identifiziert (stabil über IP-/Subnetzwechsel hinweg) und nmap darf +//! nur laufen, wenn dieses Netzwerk explizit vertraut ist: +//! - statisch über `AppConfig::trusted_networks` (Config-Datei/CLI/ENV), oder +//! - dynamisch über eine frühere Bestätigung, die im Cache gespeichert wurde, oder +//! - automatisch, wenn `--auto-trust-networks` gesetzt ist (merkt sich das Netzwerk +//! danach ebenfalls im Cache). +//! +//! Ist keins davon der Fall, wird im interaktiven Modus nachgefragt; im --json-Modus +//! ist keine Rückfrage möglich, daher wird sicherheitshalber abgelehnt. + +use std::io::Write; + +use crate::cache::Cache; +use crate::config::AppConfig; +use crate::mac::MacAddress; +use crate::network; + +pub enum TrustDecision { + Allowed, + Denied { gateway_mac: Option }, +} + +pub async fn ensure_network_trusted( + config: &AppConfig, + cache: Option<&Cache>, + auto_trust: bool, + json_mode: bool, + now: i64, +) -> TrustDecision { + let Some(gateway_mac) = network::detect_default_gateway_mac().await else { + crate::log::warn( + "trust", + "Gateway-MAC konnte nicht ermittelt werden, Netzwerk kann nicht identifiziert werden", + ); + if auto_trust { + crate::log::warn( + "trust", + "--auto-trust-networks gesetzt: nmap-Scan wird trotz unbekannter Gateway-MAC ausgeführt", + ); + return TrustDecision::Allowed; + } + return TrustDecision::Denied { gateway_mac: None }; + }; + + if config.trusted_network_macs().contains(&gateway_mac) { + return TrustDecision::Allowed; + } + + if let Some(cache) = cache + && cache.is_trusted_network(&gateway_mac).await + { + return TrustDecision::Allowed; + } + + if auto_trust { + crate::log::info( + "trust", + &format!( + "Netzwerk (Gateway-MAC {gateway_mac}) automatisch als vertrauenswürdig markiert" + ), + ); + if let Some(cache) = cache { + cache.trust_network(&gateway_mac, now).await; + } + return TrustDecision::Allowed; + } + + if json_mode { + // Keine Rückfrage im --json-Modus möglich (stdin ggf. nicht an ein Terminal + // gebunden, Ausgabe darf nicht durch einen Prompt verunreinigt werden). + return TrustDecision::Denied { + gateway_mac: Some(gateway_mac), + }; + } + + if prompt_trust_confirmation(gateway_mac).await { + if let Some(cache) = cache { + cache.trust_network(&gateway_mac, now).await; + } + TrustDecision::Allowed + } else { + TrustDecision::Denied { + gateway_mac: Some(gateway_mac), + } + } +} + +/// Blockierende stdin-Abfrage, daher in `spawn_blocking` ausgelagert, damit der +/// Tokio-Runtime-Thread währenddessen nicht blockiert. +async fn prompt_trust_confirmation(gateway_mac: MacAddress) -> bool { + tokio::task::spawn_blocking(move || { + eprint!( + "Unbekanntes Netzwerk (Gateway-MAC {gateway_mac}). nmap-Scan in diesem Netzwerk erlauben und dauerhaft merken? [y/N]: " + ); + let _ = std::io::stderr().flush(); + + let mut input = String::new(); + if std::io::stdin().read_line(&mut input).is_err() { + return false; + } + matches!(input.trim().to_lowercase().as_str(), "y" | "yes" | "j" | "ja") + }) + .await + .unwrap_or(false) +} diff --git a/tests/config_overrides.rs b/tests/config_overrides.rs index 34b13c3..25a83ee 100644 --- a/tests/config_overrides.rs +++ b/tests/config_overrides.rs @@ -14,6 +14,8 @@ fn base_cli() -> Cli { cache_db_path: None, nmap_timeout_seconds: None, networks: None, + trusted_networks: None, + auto_trust_networks: false, } } @@ -61,6 +63,18 @@ fn overrides_networks() { assert_eq!(config.networks, vec!["10.0.0.0/24".to_string()]); } +#[test] +fn overrides_trusted_networks() { + let mut config = AppConfig::default(); + let mut cli = base_cli(); + cli.trusted_networks = Some(vec![MacAddress::parse("aa:bb:cc:dd:ee:ff").unwrap()]); + apply_cli_overrides(&mut config, &cli); + assert_eq!( + config.trusted_networks, + vec!["aa:bb:cc:dd:ee:ff".to_string()] + ); +} + #[test] fn partial_overrides_only_touch_provided_fields() { let default_config = AppConfig::default(); @@ -77,4 +91,5 @@ fn partial_overrides_only_touch_provided_fields() { default_config.nmap_timeout_seconds ); assert_eq!(config.networks, default_config.networks); + assert_eq!(config.trusted_networks, default_config.trusted_networks); } diff --git a/tests/gateway_trust.rs b/tests/gateway_trust.rs new file mode 100644 index 0000000..b7ac6e0 --- /dev/null +++ b/tests/gateway_trust.rs @@ -0,0 +1,94 @@ +use std::net::IpAddr; + +use mac2ip::config::AppConfig; +use mac2ip::mac::MacAddress; +use mac2ip::network::{parse_default_gateway_ip, parse_ip_neigh_mac_for_ip}; + +fn mac(s: &str) -> MacAddress { + MacAddress::parse(s).unwrap() +} + +fn ip(s: &str) -> IpAddr { + s.parse().unwrap() +} + +// --- parse_default_gateway_ip --- + +#[test] +fn parses_gateway_ip_from_default_route() { + let output = "default via 192.168.1.1 dev eth0 proto dhcp metric 100\n"; + assert_eq!(parse_default_gateway_ip(output), Some(ip("192.168.1.1"))); +} + +#[test] +fn parses_gateway_ip_ignoring_extra_trailing_fields() { + let output = "default via 10.0.0.254 dev wlan0 proto static metric 600 onlink\n"; + assert_eq!(parse_default_gateway_ip(output), Some(ip("10.0.0.254"))); +} + +#[test] +fn no_default_route_returns_none() { + assert!(parse_default_gateway_ip("").is_none()); +} + +#[test] +fn missing_via_token_returns_none() { + let output = "default dev eth0 proto dhcp metric 100\n"; + assert!(parse_default_gateway_ip(output).is_none()); +} + +// --- parse_ip_neigh_mac_for_ip --- + +#[test] +fn finds_mac_for_matching_ip() { + let output = "192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE\n"; + let result = parse_ip_neigh_mac_for_ip(output, ip("192.168.1.1")); + assert_eq!(result, Some(mac("aa:bb:cc:dd:ee:ff"))); +} + +#[test] +fn ignores_entries_for_other_ips() { + let output = "\ +192.168.1.2 dev eth0 lladdr 11:22:33:44:55:66 STALE +192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE +"; + let result = parse_ip_neigh_mac_for_ip(output, ip("192.168.1.1")); + assert_eq!(result, Some(mac("aa:bb:cc:dd:ee:ff"))); +} + +#[test] +fn returns_none_when_ip_not_present() { + let output = "192.168.1.2 dev eth0 lladdr 11:22:33:44:55:66 STALE\n"; + assert!(parse_ip_neigh_mac_for_ip(output, ip("192.168.1.1")).is_none()); +} + +// --- AppConfig::trusted_network_macs --- + +#[test] +fn trusted_network_macs_parses_valid_entries() { + let config = AppConfig { + trusted_networks: vec!["aa:bb:cc:dd:ee:ff".to_string()], + ..Default::default() + }; + assert_eq!( + config.trusted_network_macs(), + vec![mac("aa:bb:cc:dd:ee:ff")] + ); +} + +#[test] +fn trusted_network_macs_skips_invalid_entries() { + let config = AppConfig { + trusted_networks: vec!["not-a-mac".to_string(), "aa:bb:cc:dd:ee:ff".to_string()], + ..Default::default() + }; + assert_eq!( + config.trusted_network_macs(), + vec![mac("aa:bb:cc:dd:ee:ff")] + ); +} + +#[test] +fn trusted_network_macs_empty_by_default() { + assert!(AppConfig::default().trusted_network_macs().is_empty()); +} From 8660b10e8f9aa36dc9e2de9e4f36139405e31533 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:10:58 +0200 Subject: [PATCH 11/18] =?UTF-8?q?README.md=20und=20AGENTS.md=20um=20Netzwe?= =?UTF-8?q?rk-Vertrauen-Feature=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dokumentiert die neue Vertrauensprüfung vor dem nmap-Schritt: Konzept (Gateway-MAC als Netzwerk-Identifikator), die neuen CLI-Optionen --trusted-networks/--auto-trust-networks samt Config-Datei-Feld trusted_networks, sowie das Verhalten im interaktiven vs. --json-Modus. AGENTS.md um src/trust.rs in der Modulstruktur ergänzt. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- AGENTS.md | 3 +++ README.md | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index cfe1368..0c3d83e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,8 @@ Dieses Dokument dient als technischer Leitfaden und Kontextdokument für KI-Codi 2. **`ip neigh`** (`src/network.rs`): moderner Ersatz für `arp`. Treffer nur bei Ping-Erreichbarkeit. 3. **`nmap -sn`** (`src/network.rs`): ARP-/Ping-Scan der lokal angeschlossenen Subnetze. MAC-Adressen erscheinen in der nmap-Ausgabe nur mit Root-Rechten (Raw-Socket/libpcap), daher läuft dieser Schritt über `sudo nmap` (bzw. `nmap` direkt, falls schon root). Kein Treffer → Fehler ("nicht gefunden"). +Vor Schritt 3 prüft `src/trust.rs`, ob das aktuelle Netzwerk (identifiziert über die MAC-Adresse seines Default-Gateways) für einen nmap-Scan vertrauenswürdig ist (statische Liste in `AppConfig::trusted_networks`, zuvor per interaktiver Rückfrage/`--auto-trust-networks` im Cache bestätigtes Netzwerk). Ist das Netzwerk unbekannt, wird im interaktiven Modus nachgefragt; im `--json`-Modus (keine Rückfrage möglich) wird der Scan abgelehnt. + Die Orchestrierung dieser drei Schritte liegt in `src/resolver.rs`; `src/main.rs` ist nur ein dünner Entry-Point (CLI-Parsing via `src/cli.rs`, Konfiguration via `src/config.rs` + `config-ctdra`, Logging via `src/log.rs` + `logger-ctdra`). Zur Laufzeit werden folgende System-Tools benötigt: `ip` (iproute2), `ping` (iputils), `nmap`, `sudo` (nur für Schritt 3, falls nicht schon root). Diese sind in den Paketierungs-Metadaten (`Cargo.toml`, siehe §3.2) als Abhängigkeiten hinterlegt. @@ -60,6 +62,7 @@ Das Projekt basiert auf einem generischen **Rust-Projekt-Template** für Linux-A │ ├── mac.rs # MacAddress-Typ (Parsing/Kanonisierung) │ ├── cache.rs # Globaler Turso-Cache (MAC -> IP) │ ├── network.rs # ip neigh / ping / nmap: Exec- und Parse-Funktionen +│ ├── trust.rs # Netzwerk-Vertrauensprüfung vor Schritt 3 (Gateway-MAC, Rückfrage, Cache) │ ├── resolver.rs # 3-Stufen-Algorithmus (Orchestrierung) │ └── output.rs # Human- und JSON-Ausgabe ├── tests/ # Integrationstests (pure Parsing-/Logik-Funktionen, kein Netzwerk/root/nmap nötig) diff --git a/README.md b/README.md index 346b84a..3e9cda2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Dazu wird ein 3-stufiger Algorithmus verwendet: 1. **Cache**: Ein globaler, systemweiter Cache (für alle Nutzer dieses Rechners) wird geprüft. Ein Treffer wird nur verwendet, wenn er noch nicht abgelaufen ist (TTL) **und** die IP per Ping erreichbar ist. 2. **`ip neigh`**: Die Linux-Nachbartabelle (`ip neigh show`, der moderne Ersatz für den veralteten `arp`-Befehl) wird nach der MAC-Adresse durchsucht. Ein Treffer wird nur verwendet, wenn die IP per Ping erreichbar ist. -3. **`nmap`**: Als letzter Schritt wird ein Ping-/ARP-Scan (`nmap -sn`) der lokal angeschlossenen Subnetze durchgeführt. Da MAC-Adressen in der nmap-Ausgabe nur mit Root-Rechten sichtbar sind (ARP-Scans benötigen Raw-Socket-/libpcap-Zugriff), läuft dieser Schritt über `sudo nmap` (bzw. direkt `nmap`, falls das Programm bereits als root läuft). Wird auch hier keine erreichbare IP gefunden, bricht das Programm mit einem Fehler ("nicht gefunden") ab. +3. **`nmap`**: Als letzter Schritt wird ein Ping-/ARP-Scan (`nmap -sn`) der lokal angeschlossenen Subnetze durchgeführt. Da MAC-Adressen in der nmap-Ausgabe nur mit Root-Rechten sichtbar sind (ARP-Scans benötigen Raw-Socket-/libpcap-Zugriff), läuft dieser Schritt über `sudo nmap` (bzw. direkt `nmap`, falls das Programm bereits als root läuft). Da ein Subnetz-Scan auf fremden Netzwerken heikel sein kann, wird dieser Schritt nur in einem als vertrauenswürdig eingestuften Netzwerk ausgeführt (siehe [Netzwerk-Vertrauen](#netzwerk-vertrauen)). Wird auch hier keine erreichbare IP gefunden, bricht das Programm mit einem Fehler ("nicht gefunden") ab. --- @@ -54,6 +54,8 @@ Bei Fehlern (`status: "error"`) ist `source` nicht enthalten, dafür ein `error` | `--cache-db-path ` | `MAC2IP_CACHE_DB_PATH` | `/var/lib/mac2ip/cache.db` | Pfad zur globalen Cache-Datenbankdatei. | | `--nmap-timeout-seconds ` | `MAC2IP_NMAP_TIMEOUT_SECONDS` | `120` | Timeout für einen einzelnen nmap-Subnetz-Scan. | | `--networks ` | `MAC2IP_NETWORKS` | Auto-Erkennung | Kommagetrennte Liste von Subnetzen für den nmap-Scan; überschreibt die automatische Erkennung über `ip route`. | +| `--trusted-networks ` | `MAC2IP_TRUSTED_NETWORKS` | leer | Kommagetrennte Liste von Gateway-MAC-Adressen, deren Netzwerke ohne Rückfrage für nmap-Scans (Schritt 3) vertraut werden; überschreibt die Konfigurationsdatei vollständig. | +| `--auto-trust-networks` | - | aus | Beantwortet die "Netzwerk vertrauen?"-Rückfrage vor Schritt 3 automatisch mit Ja (und merkt sich das Netzwerk dauerhaft im Cache), statt interaktiv nachzufragen bzw. im `--json`-Modus abzulehnen. | Die Überschreibungs-Reihenfolge ist immer: **CLI-Flag > Umgebungsvariable > Konfigurationsdatei > Standardwert.** @@ -75,6 +77,7 @@ cache_db_path = "/var/lib/mac2ip/cache.db" log_level = "info" nmap_timeout_seconds = 120 networks = [] +trusted_networks = [] ``` Alle Felder sind optional (fehlende Felder verwenden den Standardwert) und können, wie oben beschrieben, zusätzlich per CLI-Flag oder Umgebungsvariable überschrieben werden. @@ -95,6 +98,24 @@ Kann das Cache-Verzeichnis beim Programmstart nicht angelegt/beschrieben werden --- +## Netzwerk-Vertrauen + +Ein `nmap`-Subnetz-Scan (Schritt 3) ist auf Netzwerken, die man nicht selbst administriert (Firmen-/Gast-WLAN, Kundenstandort etc.), potenziell heikel — er kann IDS-Alarme auslösen oder gegen eine Nutzungsordnung verstoßen. mac2ip führt Schritt 3 deshalb nur in einem als vertrauenswürdig eingestuften Netzwerk aus. + +Das aktuelle Netzwerk wird über die **MAC-Adresse seines Default-Gateways** identifiziert — sie bleibt stabil, solange derselbe Router im Einsatz ist, unabhängig von SSID oder wechselndem DHCP-Subnetz. Ein Netzwerk gilt als vertrauenswürdig, wenn eine der folgenden Bedingungen zutrifft: + +1. Die Gateway-MAC steht in `trusted_networks` (Konfigurationsdatei oder `--trusted-networks`). +2. Das Netzwerk wurde bereits einmal per Rückfrage bestätigt — das Ergebnis wird dauerhaft im globalen Cache gespeichert, sodass beim nächsten Besuch (auch nach Neustart) keine erneute Rückfrage nötig ist. +3. `--auto-trust-networks` ist gesetzt: Die Rückfrage wird automatisch mit Ja beantwortet und das Ergebnis ebenfalls im Cache gemerkt. + +Ist keine der Bedingungen erfüllt: +- Im **interaktiven Modus** fragt mac2ip auf stderr nach, ob der Scan in diesem Netzwerk erlaubt werden soll. +- Im **`--json`-Modus** ist keine Rückfrage möglich (die Ausgabe darf nicht durch einen Prompt verunreinigt werden) — der Scan wird sicherheitshalber abgelehnt und ein Fehler zurückgegeben. Für unbeaufsichtigte Läufe in bekannten Netzwerken also entweder `trusted_networks` vorkonfigurieren oder einmalig interaktiv (ohne `--json`) bestätigen. + +Kann die Gateway-MAC nicht ermittelt werden (z. B. kein Default-Gateway vorhanden), wird der Scan ebenfalls abgelehnt, außer `--auto-trust-networks` ist gesetzt. + +--- + ## Voraussetzungen Neben Rust/Cargo zur Laufzeit benötigt werden folgende System-Tools: From 5aa4bdfd280c5c80db7483fb5e021dd774af3bbe Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:18:32 +0200 Subject: [PATCH 12/18] =?UTF-8?q?Docs:=20Badges=20f=C3=BCr=20CI/CD-Workflo?= =?UTF-8?q?ws=20in=20README.md=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3e9cda2..d3e9f0a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # mac2ip +[![Main Release & Publish](https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip/actions/workflows/main.yaml/badge.svg?branch=main)](https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip/actions?workflow=main.yaml) +[![Testing Build, Publish & Preview Release](https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip/actions/workflows/testing.yaml/badge.svg?branch=testing)](https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip/actions?workflow=testing.yaml) + Ein Kommandozeilen-Tool (CLI), das zuverlässig die aktuelle IP-Adresse zu einer gegebenen MAC-Adresse im lokalen Netzwerk findet. Dazu wird ein 3-stufiger Algorithmus verwendet: From a741b6e37ba0108c6bd1d26a3b3c0b9d481239c2 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:19:07 +0200 Subject: [PATCH 13/18] Feat: JSON-Schemas um neue Workflow-Dateien aktualisiert: testing-to-main-pr.yaml, version-bump.yaml --- .idea/jsonSchemas.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index c3ebb13..35e53b8 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -32,6 +32,12 @@ + + + + From 2a96e031ef4d20ae65096575e98e1335d0f0e695 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:25:31 +0200 Subject: [PATCH 14/18] Fix: RUSTSEC-2026-0253 (lru < 0.18.2) durch Deaktivierung von turso/fts beheben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die transitive Abhängigkeit lru 0.16.4 (über turso -> turso_core -> tantivy) ist von RUSTSEC-2026-0253 betroffen: LruCache::pop() ruft bei einem panickenden Drop des Keys self.detach() nicht auf, wodurch dangling Pointer in der internen verketteten Liste zurückbleiben - eine nachfolgende Eviction kann dadurch freigegebenen Speicher beschreiben (Use-after-free/Double-free, CWE-416/415). Fix erst ab lru 0.18.2, aber tantivy 0.26.2 (aktuell einzige stabile turso-Version) pinnt `lru = "^0.16.3"`, ein direktes Hochziehen ist daher nicht möglich. mac2ip nutzt turso ausschließlich als einfachen Key-Value-Cache (CREATE TABLE/INSERT/SELECT) und benötigt keine Volltextsuche. Das `fts`-Feature (zieht tantivy und damit lru überhaupt erst ein) wird daher deaktiviert, `mimalloc` bleibt als einziges Default-Feature erhalten. Damit verschwindet die verwundbare Abhängigkeit komplett aus dem Dependency-Baum (-591 Zeilen Cargo.lock), statt nur auf einen Fix von tantivy/turso zu warten. Verifiziert: Build/Clippy/Tests weiterhin grün, Cache-Datei wird nach wie vor erstellt und funktioniert (manueller Smoke-Test). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- Cargo.lock | 591 +---------------------------------------------------- Cargo.toml | 3 +- 2 files changed, 4 insertions(+), 590 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08a3d0f..68d5700 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,12 +56,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "allocator-api2" version = "0.4.0" @@ -184,17 +178,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdc70193dadb9d7287fa4b633f15f90c876915b31f6af17da307fc59c9859a8" -[[package]] -name = "async-trait" -version = "0.1.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.5", -] - [[package]] name = "autocfg" version = "1.5.1" @@ -233,7 +216,7 @@ dependencies = [ "lazy_static", "lazycell", "log", - "prettyplease 0.2.37", + "prettyplease", "proc-macro2", "quote", "regex", @@ -249,15 +232,6 @@ version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" -[[package]] -name = "bitpacking" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a7139abd3d9cebf8cd6f920a389cf3dc9576172e32f4563f188cae3c3eb019" -dependencies = [ - "crunchy", -] - [[package]] name = "bitvec" version = "1.1.1" @@ -270,29 +244,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "bon" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60eafe0d77c3a2fc292c1d1346c3041b33c0a108085a2afabf672b70f69dbbc9" -dependencies = [ - "bon-macros", -] - -[[package]] -name = "bon-macros" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0f9631d8aaaee112c41985d675ef269e02acbd4f33122836af4f0c5f699ff6" -dependencies = [ - "darling", - "ident_case", - "prettyplease 0.3.0", - "proc-macro2", - "quote", - "syn 3.0.5", -] - [[package]] name = "branches" version = "0.4.6" @@ -347,17 +298,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4" dependencies = [ "find-msvc-tools", - "jobserver", - "libc 0.2.189", "shlex 2.0.1", ] -[[package]] -name = "census" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" - [[package]] name = "cexpr" version = "0.6.0" @@ -543,15 +486,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "crc32fast" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-channel" version = "0.5.17" @@ -561,16 +495,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - [[package]] name = "crossbeam-epoch" version = "0.9.21" @@ -586,12 +510,6 @@ version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-common" version = "0.1.7" @@ -612,54 +530,11 @@ dependencies = [ "cipher", ] -[[package]] -name = "darling" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 3.0.5", -] - -[[package]] -name = "darling_macro" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" -dependencies = [ - "darling_core", - "quote", - "syn 3.0.5", -] - -[[package]] -name = "datasketches" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c286de4e81ea2590afc24d754e0f83810c566f50a1388fa75ebd57928c0d9745" - [[package]] name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "serde_core", -] [[package]] name = "displaydoc" @@ -672,12 +547,6 @@ dependencies = [ "syn 3.0.5", ] -[[package]] -name = "downcast-rs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" - [[package]] name = "either" version = "1.18.0" @@ -710,17 +579,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - [[package]] name = "errno" version = "0.3.14" @@ -759,12 +617,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "fastdivide" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" - [[package]] name = "fastrand" version = "2.5.0" @@ -777,28 +629,6 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "fs4" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" -dependencies = [ - "rustix 1.1.4", - "windows-sys 0.59.0", -] - [[package]] name = "funty" version = "2.0.0" @@ -811,17 +641,6 @@ version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" -[[package]] -name = "futures-macro" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.5", -] - [[package]] name = "futures-task" version = "0.3.34" @@ -835,7 +654,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", - "futures-macro", "futures-task", "pin-project-lite", "slab", @@ -932,17 +750,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2 0.2.21", - "equivalent", - "foldhash", -] - [[package]] name = "hashbrown" version = "0.17.1" @@ -976,12 +783,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "htmlescape" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" - [[package]] name = "http" version = "1.5.0" @@ -1171,12 +972,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "indexmap" version = "2.14.2" @@ -1184,7 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", - "hashbrown 0.17.1", + "hashbrown", ] [[package]] @@ -1205,15 +1000,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "inventory" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" -dependencies = [ - "rustversion", -] - [[package]] name = "io-uring" version = "0.7.15" @@ -1255,16 +1041,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.3", - "libc 0.2.189", -] - [[package]] name = "js-sys" version = "0.3.105" @@ -1288,12 +1064,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "levenshtein_automata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" - [[package]] name = "libc" version = "0.2.189" @@ -1407,21 +1177,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" -dependencies = [ - "hashbrown 0.16.1", -] - -[[package]] -name = "lz4_flex" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e" - [[package]] name = "mac2ip" version = "0.1.0" @@ -1429,7 +1184,6 @@ dependencies = [ "clap", "config-ctdra", "logger-ctdra", - "program-ctdra", "serde", "serde_json", "sudo-ctdra", @@ -1447,30 +1201,12 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "measure_time" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c55d61e72fc3ab704396c5fa16f4c184db37978ae4e94ca8959693a235fc0e" -dependencies = [ - "log", -] - [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" -[[package]] -name = "memmap2" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" -dependencies = [ - "libc 0.2.189", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1528,12 +1264,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "murmurhash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" - [[package]] name = "nom" version = "7.1.3" @@ -1608,36 +1338,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "oneshot" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269bca4c2591a28585d6bf10d9ed0332b7d76900a1b02bec41bdc3a2cdcda107" - [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "ordered-float" -version = "5.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ownedbytes" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbd56f7631767e61784dc43f8580f403f4475bd4aaa4da003e6295e1bab4a7e" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "owo-colors" version = "3.5.0" @@ -1688,12 +1394,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkg-config" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" - [[package]] name = "polling" version = "3.11.0" @@ -1756,16 +1456,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "prettyplease" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" -dependencies = [ - "proc-macro2", - "syn 3.0.5", -] - [[package]] name = "proc-macro2" version = "1.0.107" @@ -1925,26 +1615,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -1993,16 +1663,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "rust-stemmers" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" -dependencies = [ - "serde", - "serde_derive", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -2214,15 +1874,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" -[[package]] -name = "sketches-ddsketch" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513c3f5f732bfd6fbb187619c2dfe9d2f25f1a2976f01d575f0fd329d565df56" -dependencies = [ - "serde", -] - [[package]] name = "slab" version = "0.4.12" @@ -2329,154 +1980,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "tantivy" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "861facfabd71044968f364837f9a083b56464ba5a59079f88706ee5c451ca069" -dependencies = [ - "aho-corasick", - "arc-swap", - "base64", - "bitpacking", - "bon", - "byteorder", - "census", - "crc32fast", - "crossbeam-channel", - "datasketches", - "downcast-rs", - "fastdivide", - "fnv", - "fs4", - "htmlescape", - "itertools 0.14.0", - "levenshtein_automata", - "log", - "lru", - "lz4_flex", - "measure_time", - "memmap2", - "once_cell", - "oneshot", - "rayon", - "regex", - "rust-stemmers", - "rustc-hash 2.1.3", - "serde", - "serde_json", - "sketches-ddsketch", - "smallvec", - "tantivy-bitpacker", - "tantivy-columnar", - "tantivy-common", - "tantivy-fst", - "tantivy-query-grammar", - "tantivy-stacker", - "tantivy-tokenizer-api", - "tempfile", - "thiserror", - "time", - "typetag", - "uuid", - "winapi", -] - -[[package]] -name = "tantivy-bitpacker" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fed3d674429bcd2de5d0a6d1aa5495fed8afd9c5ecce993019caf7615f53fa4" -dependencies = [ - "bitpacking", -] - -[[package]] -name = "tantivy-columnar" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57166f5bcfd478f370ab8445afb4678dce44801fa5ce5c451aaf8595583c5dc" -dependencies = [ - "downcast-rs", - "fastdivide", - "itertools 0.14.0", - "serde", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-sstable", - "tantivy-stacker", -] - -[[package]] -name = "tantivy-common" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf10915aa75da3c3b0d58b58853d2e889efbaf32d4982a4c3715dde6bba23e5" -dependencies = [ - "async-trait", - "byteorder", - "ownedbytes", - "serde", - "time", -] - -[[package]] -name = "tantivy-fst" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" -dependencies = [ - "byteorder", - "regex-syntax", - "utf8-ranges", -] - -[[package]] -name = "tantivy-query-grammar" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfadb8526b6da90704feb293b0701a6aae62ea14983143344be2dc5ce30f1d82" -dependencies = [ - "fnv", - "nom", - "ordered-float", - "serde", - "serde_json", -] - -[[package]] -name = "tantivy-sstable" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2cfc3ac5164cbadc28965ffb145a8f47582a60ae5897859ad8d4316596c606" -dependencies = [ - "futures-util", - "itertools 0.14.0", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-fst", - "zstd", -] - -[[package]] -name = "tantivy-stacker" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbb051742da9d53ca9e8fff43a9b10e319338b24e2c0e15d0372df19ffeb951" -dependencies = [ - "murmurhash32", - "tantivy-common", -] - -[[package]] -name = "tantivy-tokenizer-api" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac258c2c6390673f2685813afeeafcb8c4e0ee7de8dd3fc46838dcc37263f98" -dependencies = [ - "serde", -] - [[package]] name = "tap" version = "1.0.1" @@ -2731,7 +2234,7 @@ dependencies = [ "aegis", "aes", "aes-gcm", - "allocator-api2 0.4.0", + "allocator-api2", "antithesis_sdk", "arc-swap", "aristo", @@ -2779,7 +2282,6 @@ dependencies = [ "smallvec", "strum", "strum_macros", - "tantivy", "tempfile", "thiserror", "tracing", @@ -2909,42 +2411,12 @@ dependencies = [ "rand 0.10.2", ] -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" -[[package]] -name = "typetag" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90e86058a30d42a1a928dfb4b49bb33c98c3a2b4909492e6b0881cd94798ec2" -dependencies = [ - "erased-serde", - "inventory", - "once_cell", - "serde", - "typetag-impl", -] - -[[package]] -name = "typetag-impl" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f153acc4e99a5f2a5aefa09fb078be54e26271b2813f6041200b224c098d8328" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.5", -] - [[package]] name = "uncased" version = "0.9.10" @@ -2982,12 +2454,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -3008,7 +2474,6 @@ checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" dependencies = [ "getrandom 0.4.3", "js-sys", - "serde_core", "sha1_smol", "wasm-bindgen", ] @@ -3097,28 +2562,6 @@ dependencies = [ "rustix 0.38.44", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.62.2" @@ -3403,31 +2846,3 @@ name = "zmij" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.1.0+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 43efe5a..13cd826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,9 @@ description = "Findet zuverlässig die aktuelle IP-Adresse zu einer MAC-Adresse [dependencies] config-ctdra = { version = "1.0.6", registry = "gitea" } logger-ctdra = { version = "1.0.5", registry = "gitea" } -program-ctdra = { version = "1.0.1", registry = "gitea" } sudo-ctdra = { version = "1.0.1", registry = "gitea" } tokio = { version = "1", features = ["macros", "rt-multi-thread", "process", "time"] } -turso = "0.7" +turso = { version = "0.7", default-features = false, features = ["mimalloc"] } clap = { version = "4", features = ["derive", "env"] } serde = { version = "1", features = ["derive"] } serde_json = "1" From e482216b1bf680516e30892e01ec80a8350c3986 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:28:06 +0200 Subject: [PATCH 15/18] =?UTF-8?q?Docs:=20Revert-Bedingung=20f=C3=BCr=20RUS?= =?UTF-8?q?TSEC-2026-0253-Workaround=20dokumentieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Damit das Deaktivieren von turso/fts (Commit 2a96e03) nicht dauerhaft vergessen wird, sobald der eigentliche Fix upstream verfügbar ist: - Cargo.toml: Kommentar direkt über der turso-Zeile mit Grund und konkreten Schritten zum Zurücksetzen. - AGENTS.md: neue Tabelle "Bekannte Sicherheits-Workarounds" (§5.2.1), damit auch bei einem Renovate-Update/Dependency-Review ohne Blick in Cargo.toml auffällt, dass hier eine befristete Ausnahme besteht. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- AGENTS.md | 11 +++++++++++ Cargo.toml | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 0c3d83e..5c09c77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -152,6 +152,17 @@ Alle Workflows liegen unter `.gitea/workflows/` und nutzen gecachte Abhängigkei - **`qodana.yaml`**: Konfiguration für JetBrains Qodana (statische Codeanalyse, Profil `qodana.starter`); wird über eine externe/IDE-seitige Qodana-CI-Integration ausgeführt, nicht über einen eigenen Gitea-Workflow in diesem Template. - **`renovate.yaml`** (Trigger: wöchentlich montags 04:00 UTC, `workflow_dispatch`): Führt Renovate (Container-Image, gepinnte Version) gegen die Gitea-Plattform aus und erstellt Pull Requests für veraltete Abhängigkeiten gemäß `renovate.json`. +### 5.2.1 Bekannte Sicherheits-Workarounds (temporär, mit Revert-Bedingung) + +Workarounds für Schwachstellen in transitiven Abhängigkeiten, die nicht direkt +per Versions-Bump behoben werden können (z. B. weil ein Zwischenpaket eine zu +enge Versionsanforderung pinnt). Vor jedem Renovate-Merge/Dependency-Update +prüfen, ob die Revert-Bedingung inzwischen erfüllt ist. + +| Advisory | Betroffenes Paket | Workaround | Wo dokumentiert | Revert-Bedingung | +| :--- | :--- | :--- | :--- | :--- | +| [RUSTSEC-2026-0253](https://rustsec.org/advisories/RUSTSEC-2026-0253.html) (Use-after-free in `LruCache::pop()`) | `lru` < 0.18.2, transitiv über `turso -> turso_core -> tantivy` | `fts`-Default-Feature von `turso` deaktiviert (`Cargo.toml`) — mac2ip nutzt keine Volltextsuche, dadurch entfällt `tantivy`/`lru` komplett aus dem Dependency-Baum | Kommentar direkt über der `turso`-Zeile in `Cargo.toml` | Sobald `cargo update -p lru --precise 0.18.2` erfolgreich ist (d. h. eine stabile `turso`-Version eine `tantivy`-Version mit `lru >= 0.18.2` zulässt): `turso`-Zeile zurück auf `turso = "0.7"` (oder aktueller) ändern, `cargo update -p turso`, und `cargo tree -i lru` zur Kontrolle. | + ### 5.3 Renovate-Konfiguration (`renovate.json`) - Basis-Branch für PRs: `dev`. - Gruppiert Updates nach `Gitea Actions`, `Cargo Dependencies` und `Docker-Images`. diff --git a/Cargo.toml b/Cargo.toml index 13cd826..b014ed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,19 @@ config-ctdra = { version = "1.0.6", registry = "gitea" } logger-ctdra = { version = "1.0.5", registry = "gitea" } sudo-ctdra = { version = "1.0.1", registry = "gitea" } tokio = { version = "1", features = ["macros", "rt-multi-thread", "process", "time"] } +# WORKAROUND für RUSTSEC-2026-0253 (Use-after-free in lru < 0.18.2): das +# Default-Feature "fts" zieht tantivy 0.26.2 -> lru "^0.16.3" (verwundbar) ein, +# obwohl mac2ip keine Volltextsuche nutzt. "fts" bleibt deaktiviert, bis eine +# stabile turso-Version tantivy>=eine lru>=0.18.2 zulassende Version pinnt. +# +# Revert, sobald behoben (z. B. Renovate-PR/manuelle Prüfung): +# 1. `cargo update -p lru --precise 0.18.2` probieren - schlägt fehl, solange +# das o. g. Problem besteht (siehe Fehlermeldung mit dem Pfad turso -> +# turso_core -> tantivy -> lru "^0.16.3"). +# 2. Sobald das gelingt (oder eine neue stabile turso-Version das Problem +# upstream behebt): diese Zeile zurück zu `turso = "0.7"` (oder aktueller) +# ändern und `cargo update -p turso` laufen lassen. +# 3. `cargo tree -i lru` zur Kontrolle - keine Version < 0.18.2 mehr vorhanden. turso = { version = "0.7", default-features = false, features = ["mimalloc"] } clap = { version = "4", features = ["derive", "env"] } serde = { version = "1", features = ["derive"] } From 4a4a1afe9d584a5749d3903a2c940aae802c97c4 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 20:31:09 +0200 Subject: [PATCH 16/18] Feat: Hebt Version auf 1.0.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68d5700..d403a21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1179,7 +1179,7 @@ dependencies = [ [[package]] name = "mac2ip" -version = "0.1.0" +version = "1.0.0" dependencies = [ "clap", "config-ctdra", diff --git a/Cargo.toml b/Cargo.toml index b014ed4..620b025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mac2ip" -version = "0.1.0" +version = "1.0.0" edition = "2024" authors = ['DragonSlayer_14'] readme = "README.md" From 15121eba3cf7129fb64d7952c9e32ebf462968e0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 13 Sep 2026 18:40:05 +0000 Subject: [PATCH 17/18] chore(deps): update ghcr.io/renovatebot/renovate docker tag to v44.83.0 --- .gitea/workflows/renovate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml index f9f6456..ccbdcb2 100644 --- a/.gitea/workflows/renovate.yaml +++ b/.gitea/workflows/renovate.yaml @@ -9,7 +9,7 @@ jobs: renovate: name: Dependency-Updates prüfen & Pull Requests erstellen runs-on: ubuntu-latest - container: ghcr.io/renovatebot/renovate:44.82.0 + container: ghcr.io/renovatebot/renovate:44.83.0 steps: - name: Renovate ausführen run: renovate From 01dbbba22745ccca932e12b95e90406e9836cd8f Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Sun, 13 Sep 2026 21:50:19 +0200 Subject: [PATCH 18/18] =?UTF-8?q?Fix:=20i686-Build-Target=20entfernen=20(i?= =?UTF-8?q?o-uring=20unterst=C3=BCtzt=20keine=2032-Bit-x86-Bindings)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testing.yaml scheiterte beim i686-Cross-Build mit "The prebuilt sys.rs may not be compatible with your target" aus der io-uring-Crate. Ursache: turso_sdk_kit (Abhängigkeit von turso) fordert von turso_core unconditional das Feature "io_uring" an - anders als "fts" ist das nicht abschaltbar. io-uring 0.7.15 (aktuellste Version) liefert vorgefertigte FFI-Bindings nur für x86_64/aarch64/riscv64/powerpc64/ loongarch64, nicht für i686. --cfg=io_uring_skip_arch_check wurde bewusst NICHT verwendet: auf i686 würde das stillschweigend die x86_64-Bindings nutzen, deren Struct-Layouts (u. a. Pointer-Breite) nicht zum 32-Bit-ABI passen - echtes Risiko für Speicherkorruption bei den rohen io_uring-Syscalls, kein reiner Lint. i686 daher komplett aus den Build-Targets entfernt: Linker-Eintrag (.cargo/config.toml), Toolchain-Install/rustup-Target/Build-/Paketier- Schritte (main.yaml, testing.yaml), sowie die Zielarchitektur-Angaben in README.md/AGENTS.md. Revert-Bedingung in AGENTS.md §5.2.2 dokumentiert (sobald io-uring i686-Bindings mitbringt oder turso io_uring optional macht). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SnWfGGqHJGh2AhZ6uhotaD --- .cargo/config.toml | 3 --- .gitea/workflows/main.yaml | 9 ++------- .gitea/workflows/testing.yaml | 9 ++------- AGENTS.md | 10 ++++++++-- README.md | 6 +++--- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c7356c5..bb28fe0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -10,6 +10,3 @@ git-fetch-with-cli = true [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" - -[target.i686-unknown-linux-gnu] -linker = "i686-linux-gnu-gcc" diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index f201502..b9fac9e 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -64,7 +64,7 @@ jobs: - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu gcc-i686-linux-gnu + sudo apt-get install -y gcc-aarch64-linux-gnu - name: Ermittle Rust-Version für Rustup-Target-Cache-Key run: echo "RUST_VERSION=$(rustc --version | awk '{print $2}')" >> "$GITHUB_ENV" @@ -75,12 +75,11 @@ jobs: with: path: | ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu - ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu key: rustup-targets-${{ runner.os }}-${{ env.RUST_VERSION }} - name: Add Rust Cross-Compilation Targets if: steps.cache-rustup-targets.outputs.cache-hit != 'true' - run: rustup target add aarch64-unknown-linux-gnu i686-unknown-linux-gnu + run: rustup target add aarch64-unknown-linux-gnu - name: PATH um Cargo-bin-Verzeichnis ergänzen run: | @@ -111,7 +110,6 @@ jobs: run: | cargo build --release --target x86_64-unknown-linux-gnu cargo build --release --target aarch64-unknown-linux-gnu - cargo build --release --target i686-unknown-linux-gnu - name: Determine Build Number id: build_num @@ -130,20 +128,17 @@ jobs: run: | cargo deb --target x86_64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build cargo deb --target aarch64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - cargo deb --target i686-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - name: Build Fedora / RPM Packages (.rpm) run: | mkdir -p target/generate-rpm cargo generate-rpm --target x86_64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm cargo generate-rpm --target aarch64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - cargo generate-rpm --target i686-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - name: Build Arch Linux Packages (.pkg.tar.zst) run: | python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - python3 scripts/package-arch.py --target i686-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - name: Publish Packages to Gitea Package Registry env: diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml index 6bf6d29..42cf728 100644 --- a/.gitea/workflows/testing.yaml +++ b/.gitea/workflows/testing.yaml @@ -64,7 +64,7 @@ jobs: - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu gcc-i686-linux-gnu + sudo apt-get install -y gcc-aarch64-linux-gnu - name: Ermittle Rust-Version für Rustup-Target-Cache-Key run: echo "RUST_VERSION=$(rustc --version | awk '{print $2}')" >> "$GITHUB_ENV" @@ -75,12 +75,11 @@ jobs: with: path: | ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu - ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu key: rustup-targets-${{ runner.os }}-${{ env.RUST_VERSION }} - name: Add Rust Cross-Compilation Targets if: steps.cache-rustup-targets.outputs.cache-hit != 'true' - run: rustup target add aarch64-unknown-linux-gnu i686-unknown-linux-gnu + run: rustup target add aarch64-unknown-linux-gnu - name: PATH um Cargo-bin-Verzeichnis ergänzen run: | @@ -111,7 +110,6 @@ jobs: run: | cargo build --release --target x86_64-unknown-linux-gnu cargo build --release --target aarch64-unknown-linux-gnu - cargo build --release --target i686-unknown-linux-gnu - name: Determine Build Number id: build_num @@ -130,20 +128,17 @@ jobs: run: | cargo deb --target x86_64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build cargo deb --target aarch64-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - cargo deb --target i686-unknown-linux-gnu --deb-revision "${BUILD_NUMBER}" --no-build - name: Build Fedora / RPM Packages (.rpm) run: | mkdir -p target/generate-rpm cargo generate-rpm --target x86_64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm cargo generate-rpm --target aarch64-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - cargo generate-rpm --target i686-unknown-linux-gnu -s "release=\"${BUILD_NUMBER}\"" -o target/generate-rpm - name: Build Arch Linux Packages (.pkg.tar.zst) run: | python3 scripts/package-arch.py --target x86_64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" python3 scripts/package-arch.py --target aarch64-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - python3 scripts/package-arch.py --target i686-unknown-linux-gnu --pkgrel "${BUILD_NUMBER}" - name: Publish Packages to Gitea Package Registry env: diff --git a/AGENTS.md b/AGENTS.md index 5c09c77..111b50e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ Die Orchestrierung dieser drei Schritte liegt in `src/resolver.rs`; `src/main.rs Zur Laufzeit werden folgende System-Tools benötigt: `ip` (iproute2), `ping` (iputils), `nmap`, `sudo` (nur für Schritt 3, falls nicht schon root). Diese sind in den Paketierungs-Metadaten (`Cargo.toml`, siehe §3.2) als Abhängigkeiten hinterlegt. Das Projekt basiert auf einem generischen **Rust-Projekt-Template** für Linux-Anwendungen/CLI-Tools mit Fokus auf: -- Automatisierte Multi-Architektur-Kompilierung (`x86_64`, `aarch64`, `i686`). +- Automatisierte Multi-Architektur-Kompilierung (`x86_64`, `aarch64`; `i686` derzeit ausgesetzt, siehe §5.2.1). - Native Paketierung für Debian (`.deb`), Fedora/RHEL (`.rpm`) und Arch Linux (`.pkg.tar.zst`). - Vollständig automatisierte CI/CD-Pipelines via Gitea Actions (kompatibel mit Forgejo / GitHub Actions). - Automatisierte Sicherheits-Scans (Schwachstellen, Secrets) und Dependency-Updates. @@ -27,7 +27,7 @@ Das Projekt basiert auf einem generischen **Rust-Projekt-Template** für Linux-A ### Standards & Tech-Stack - **Sprache**: Rust (Edition 2024), Python 3 (für Hilfsskripte in `scripts/`). - **Rust Toolchain**: Stable. -- **Zielplattform**: Linux (GLIBC-basiert, Cross-Kompilierung für `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `i686-unknown-linux-gnu`). +- **Zielplattform**: Linux (GLIBC-basiert, Cross-Kompilierung für `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`; `i686-unknown-linux-gnu` derzeit ausgesetzt, siehe §5.2.1). - **Sicherheits-Tooling**: Trivy, OSV-Scanner, TruffleHog (Secret-Scanning), Renovate (Dependency-Updates), Qodana (statische Codeanalyse). - **Lizenz**: GPL-3.0-or-later (sofern nicht im abgeleiteten Projekt anders definiert). @@ -163,6 +163,12 @@ prüfen, ob die Revert-Bedingung inzwischen erfüllt ist. | :--- | :--- | :--- | :--- | :--- | | [RUSTSEC-2026-0253](https://rustsec.org/advisories/RUSTSEC-2026-0253.html) (Use-after-free in `LruCache::pop()`) | `lru` < 0.18.2, transitiv über `turso -> turso_core -> tantivy` | `fts`-Default-Feature von `turso` deaktiviert (`Cargo.toml`) — mac2ip nutzt keine Volltextsuche, dadurch entfällt `tantivy`/`lru` komplett aus dem Dependency-Baum | Kommentar direkt über der `turso`-Zeile in `Cargo.toml` | Sobald `cargo update -p lru --precise 0.18.2` erfolgreich ist (d. h. eine stabile `turso`-Version eine `tantivy`-Version mit `lru >= 0.18.2` zulässt): `turso`-Zeile zurück auf `turso = "0.7"` (oder aktueller) ändern, `cargo update -p turso`, und `cargo tree -i lru` zur Kontrolle. | +### 5.2.2 Bekannte Build-Einschränkungen (temporär, mit Revert-Bedingung) + +| Einschränkung | Ursache | Workaround | Wo dokumentiert | Revert-Bedingung | +| :--- | :--- | :--- | :--- | :--- | +| Kein `i686-unknown-linux-gnu`-Build/-Paket mehr | `turso_sdk_kit` (Abhängigkeit von `turso`) fordert von `turso_core` unconditional das Feature `io_uring` an (nicht abschaltbar wie `fts`). Die `io-uring`-Crate (aktuell 0.7.15, neueste Version) liefert vorgefertigte FFI-Bindings nur für `x86_64`, `aarch64`, `riscv64`, `powerpc64`, `loongarch64` - **nicht** für `i686`. `--cfg=io_uring_skip_arch_check` würde auf i686 stillschweigend die x86_64-Bindings verwenden (falsche Struct-Layouts/Pointer-Breite -> potenzielle Speicherkorruption bei io_uring-Syscalls) und wurde deshalb bewusst NICHT verwendet. | `i686-unknown-linux-gnu` komplett aus den Build-Targets entfernt: `.cargo/config.toml` (Linker-Eintrag), `.gitea/workflows/main.yaml` + `testing.yaml` (Toolchain-Install, `rustup target add`, `cargo build`/`cargo deb`/`cargo generate-rpm`/`package-arch.py`-Aufrufe), `README.md`/`AGENTS.md` (Zielarchitektur-Angaben) | Diese Tabelle; `git log` nach "i686" für die genauen Commits | Sobald eine neue `io-uring`-Version vorgefertigte `i686`-Bindings mitbringt (`cargo info io-uring` prüfen) ODER eine neue `turso`-Version `io_uring` optional/abschaltbar macht: i686 wieder in allen oben genannten Dateien ergänzen und mit einem echten i686-Cross-Build verifizieren, bevor gemergt wird. | + ### 5.3 Renovate-Konfiguration (`renovate.json`) - Basis-Branch für PRs: `dev`. - Gruppiert Updates nach `Gitea Actions`, `Cargo Dependencies` und `Docker-Images`. diff --git a/README.md b/README.md index d3e9f0a..1c263f4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Dazu wird ein 3-stufiger Algorithmus verwendet: - **Maschinenlesbare Ausgabe** über `--json` (unattended-Modus) — unterdrückt dabei alle sonstigen Log-Ausgaben. - **Vollständig über die Kommandozeile konfigurierbar**, mit Overlay-Kette CLI > Umgebungsvariable > Konfigurationsdatei > Standardwert. - **Funktioniert mit und ohne `sudo`** — Root-Rechte werden nur für den nmap-Schritt benötigt. -- **Rust Edition 2024**, Multi-Architektur-Kompilierung (`x86_64`, `aarch64`, `i686`), native Linux-Paketierung (`.deb`, `.rpm`, `.pkg.tar.zst`) und automatisierte CI/CD-Pipelines via Gitea Actions. +- **Rust Edition 2024**, Multi-Architektur-Kompilierung (`x86_64`, `aarch64`), native Linux-Paketierung (`.deb`, `.rpm`, `.pkg.tar.zst`) und automatisierte CI/CD-Pipelines via Gitea Actions. --- @@ -175,8 +175,8 @@ Im `--json`-Modus wird für Schritt 3 ausschließlich `sudo -n` (nicht-interakti - **Python 3** (für Hilfsskripte in `scripts/`) - Zur Laufzeit: `ip`, `ping`, `nmap`, `sudo` (siehe "Voraussetzungen" oben) - Für Cross-Compilation (optional): - - `rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu i686-unknown-linux-gnu` - - Cross-Toolchains: `gcc-aarch64-linux-gnu`, `gcc-i686-linux-gnu` + - `rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu` + - Cross-Toolchains: `gcc-aarch64-linux-gnu` - Für lokales Paketieren (optional): - `cargo install cargo-deb cargo-generate-rpm`