diff --git a/Cargo.lock b/Cargo.lock index cb29359..084d87f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -37,7 +37,7 @@ checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -162,7 +162,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -174,7 +174,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -195,7 +195,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -284,7 +284,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -313,13 +313,13 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt" -version = "0.15.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7" +checksum = "24ae5479c93d3720e4c1dbd6b945b97457c50cb672781104768190371df1a905" dependencies = [ "base64", - "blowfish", - "getrandom 0.2.17", + "blowfish 0.10.0", + "getrandom 0.4.2", "subtle", "zeroize", ] @@ -330,7 +330,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ - "blowfish", + "blowfish 0.9.1", "pbkdf2", "sha2 0.10.9", ] @@ -511,7 +511,7 @@ dependencies = [ "aes", "bergshamra-core", "cbc", - "cipher", + "cipher 0.4.4", "des", "hmac 0.12.1", "pbkdf2", @@ -553,9 +553,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] @@ -603,7 +603,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "blowfish" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ce3946557b35e71d1bbe07ec385073ce9eda05043f95de134eb578fcf1a298" +dependencies = [ + "byteorder", + "cipher 0.5.2", ] [[package]] @@ -618,9 +628,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -652,7 +662,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -684,7 +694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -701,9 +711,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -719,7 +729,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", - "inout", + "inout 0.1.4", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "crypto-common 0.2.2", + "inout 0.2.2", ] [[package]] @@ -750,10 +770,10 @@ version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -792,6 +812,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console" version = "0.16.3" @@ -859,6 +889,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -926,7 +962,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -962,7 +998,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -981,9 +1017,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "deadpool" @@ -1011,7 +1047,7 @@ checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1059,7 +1095,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1078,29 +1114,32 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] name = "dhcproto" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6794294f2c4665aae452e950c2803a1e487c5672dc8448f0bfa3f52ff67e270" +checksum = "c278d2f17dbcb7332f3b31788be67f76017096c5eedc293e1259f2d48b0f891f" dependencies = [ "dhcproto-macros", - "hex", + "hickory-proto", "ipnet", - "rand 0.8.6", - "thiserror 1.0.69", - "trust-dns-proto", - "url", + "rand 0.10.1", + "thiserror 2.0.18", ] [[package]] name = "dhcproto-macros" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7993efb860416547839c115490d4951c6d0f8ec04a3594d9dd99d50ed7ec170" +checksum = "3944d18b1889ce23702b0ea0230e87033cf5b4659e233419b5c00da3983a387b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "digest" @@ -1128,13 +1167,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1244,18 +1283,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-as-inner" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "enum_dispatch" version = "0.3.13" @@ -1265,7 +1292,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1430,7 +1457,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1475,9 +1502,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb130435a959a8d525e6bca66ff6c40981a300ee96d70e3ef56f046556d614a3" +checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351" dependencies = [ "generic-array 0.14.7", "rustversion", @@ -1583,9 +1610,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -1617,9 +1644,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hax-lib" @@ -1642,7 +1669,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1658,12 +1685,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1688,6 +1709,24 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hickory-proto" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bab31817bfb44672a252e97fe81cd0c18d1b2cf892108922f6818820df8c643" +dependencies = [ + "data-encoding", + "idna", + "ipnet", + "jni", + "once_cell", + "rand 0.10.1", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "url", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -1737,9 +1776,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1797,9 +1836,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -1850,7 +1889,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2", "tokio", "tower-service", "tracing", @@ -1968,17 +2007,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.1.0" @@ -1992,9 +2020,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -2035,7 +2063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -2056,6 +2084,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "insta" version = "1.47.2" @@ -2101,16 +2138,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2124,14 +2151,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "js-sys" -version = "0.3.99" +name = "jni" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -2223,13 +2298,13 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 1.1.0", + "idna", "mime", "nom 8.0.0", "percent-encoding", "quoted_printable", "rustls", - "socket2 0.6.3", + "socket2", "tokio", "tokio-rustls", "url", @@ -2347,9 +2422,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "lru-slab" @@ -2366,12 +2441,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.3" @@ -2396,9 +2465,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "mime" @@ -2434,9 +2503,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -2514,7 +2583,7 @@ checksum = "06d8fb377e6efeb91911a8ed962a69615535e167e675025a2be8fa109751426a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2532,7 +2601,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -2596,9 +2665,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -2654,6 +2723,10 @@ name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" @@ -2669,7 +2742,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openpxe" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "axum", @@ -2691,7 +2764,7 @@ dependencies = [ [[package]] name = "openpxe-core" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "base64", @@ -2718,14 +2791,14 @@ dependencies = [ [[package]] name = "openpxe-dhcp-proxy" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "bytes", "dhcproto", "openpxe-core", "parking_lot", - "socket2 0.5.10", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -2733,7 +2806,7 @@ dependencies = [ [[package]] name = "openpxe-http-api" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "axum", @@ -2769,7 +2842,7 @@ dependencies = [ [[package]] name = "openpxe-ipxe-assets" -version = "0.6.1" +version = "0.6.2" dependencies = [ "openpxe-core", "rust-embed", @@ -2779,7 +2852,7 @@ dependencies = [ [[package]] name = "openpxe-iso-store" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "bcrypt", @@ -2808,13 +2881,13 @@ dependencies = [ [[package]] name = "openpxe-tftp" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "bytes", "openpxe-core", "openpxe-ipxe-assets", - "socket2 0.5.10", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -2822,7 +2895,7 @@ dependencies = [ [[package]] name = "openpxe-webui" -version = "0.6.1" +version = "0.6.2" [[package]] name = "p256" @@ -2958,7 +3031,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3048,7 +3121,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -3078,6 +3151,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "potential_utf" version = "0.1.5" @@ -3109,7 +3188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn", ] [[package]] @@ -3140,7 +3219,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3160,7 +3239,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "version_check", "yansi", ] @@ -3199,7 +3278,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -3236,9 +3315,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -3363,14 +3442,14 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -3391,9 +3470,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" @@ -3506,7 +3585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b4d036bb45d7bbe99dbfef4ec60eaeb614708d22ff107124272f8ef6b54548" dependencies = [ "aes", - "bitflags 2.11.1", + "bitflags 2.13.0", "block-padding", "byteorder", "bytes", @@ -3522,12 +3601,12 @@ dependencies = [ "elliptic-curve", "enum_dispatch", "futures", - "generic-array 1.4.2", + "generic-array 1.4.3", "getrandom 0.2.17", "hex-literal", "hmac 0.12.1", "home", - "inout", + "inout 0.1.4", "internal-russh-forked-ssh-key", "libcrux-ml-kem", "log", @@ -3577,7 +3656,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed8949eca4163c18a8f59ff96d32cf61e9c13b9735e21ef32b3907f4aafa1a9" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "bytes", "chrono", "dashmap", @@ -3623,7 +3702,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.117", + "syn", "walkdir", ] @@ -3668,7 +3747,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", @@ -3729,7 +3808,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -3824,14 +3903,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3976,6 +4055,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -4015,19 +4110,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -4069,7 +4154,7 @@ dependencies = [ "aes-gcm", "cbc", "chacha20 0.9.1", - "cipher", + "cipher 0.4.4", "ctr", "poly1305", "ssh-encoding", @@ -4106,17 +4191,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -4145,7 +4219,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4187,7 +4261,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4198,7 +4272,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4284,14 +4358,14 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -4299,7 +4373,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -4312,7 +4386,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4410,11 +4484,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "bytes", "futures-core", "futures-util", @@ -4423,7 +4497,6 @@ dependencies = [ "http-body-util", "http-range-header", "httpdate", - "iri-string", "mime", "mime_guess", "percent-encoding", @@ -4434,6 +4507,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", + "url", ] [[package]] @@ -4468,7 +4542,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4523,30 +4597,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "trust-dns-proto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.2.3", - "ipnet", - "lazy_static", - "rand 0.8.6", - "smallvec", - "thiserror 1.0.69", - "tinyvec", - "tracing", - "url", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -4586,9 +4636,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uncased" @@ -4605,27 +4655,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -4661,7 +4696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", - "idna 1.1.0", + "idna", "percent-encoding", "serde", ] @@ -4680,9 +4715,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -4747,9 +4782,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" dependencies = [ "cfg-if", "once_cell", @@ -4760,9 +4795,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "54568702fabf5d4849ce2b90fadfa64168a097eaf4b351ce9df8b687a0086aaf" dependencies = [ "js-sys", "wasm-bindgen", @@ -4770,9 +4805,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4780,22 +4815,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" dependencies = [ "unicode-ident", ] @@ -4841,7 +4876,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "hashbrown 0.15.5", "indexmap", "semver", @@ -4849,9 +4884,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" dependencies = [ "js-sys", "wasm-bindgen", @@ -4966,7 +5001,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4977,7 +5012,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5020,7 +5055,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -5038,14 +5082,31 @@ 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", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -5063,48 +5124,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.15" @@ -5159,7 +5268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "wit-parser", ] @@ -5170,10 +5279,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "indexmap", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5189,7 +5298,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5201,7 +5310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.13.0", "indexmap", "log", "serde", @@ -5295,9 +5404,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -5312,35 +5421,35 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -5353,7 +5462,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -5374,7 +5483,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5407,7 +5516,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 64f2e99..42b6d19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ ] [workspace.package] -version = "0.6.1" +version = "0.6.2" edition = "2021" rust-version = "1.95" license = "MIT OR Apache-2.0" @@ -20,21 +20,23 @@ repository = "https://gitea.milesward.dev/mward4/OpenPXE" authors = ["OpenPXE contributors"] [workspace.dependencies] -tokio = { version = "1.40", features = ["full"] } +tokio = { version = "1.52", features = ["full"] } tokio-util = { version = "0.7", features = ["io"] } tokio-stream = { version = "0.1", features = ["sync"] } futures = "0.3" async-trait = "0.1" -dhcproto = "0.12" -socket2 = { version = "0.5", features = ["all"] } +# v0.6.2: dhcproto 0.15 drops the deprecated trust-dns-proto dependency +# (replaced by hickory-proto) and carries three releases of DHCP option +# coverage accumulated upstream — both directly relevant to the proxy core. +dhcproto = "0.15" +socket2 = { version = "0.6", features = ["all"] } bytes = "1.7" -nom = "7.1" axum = { version = "0.7", features = ["macros", "multipart", "http2"] } tower = "0.5" tower-http = { version = "0.6", features = ["fs", "trace", "cors", "limit"] } -hyper = "1.4" +hyper = "1.9" reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "stream", "json"] } serde = { version = "1.0", features = ["derive"] } @@ -52,9 +54,11 @@ thiserror = "2.0" clap = { version = "4.5", features = ["derive", "env"] } uuid = { version = "1.10", features = ["v4", "serde"] } time = { version = "0.3", features = ["serde", "serde-human-readable", "formatting", "macros"] } +# sha2 stays 0.10 deliberately: bergshamra-crypto requires ^0.10, and +# bumping to 0.11 would split the RustCrypto digest stack in the tree. sha2 = "0.10" hex = "0.4" -bcrypt = "0.15" +bcrypt = "0.19" parking_lot = "0.12" rust-embed = { version = "8.5", features = ["include-exclude"] } diff --git a/crates/core/src/auth.rs b/crates/core/src/auth.rs index f8255a5..b022d0a 100644 --- a/crates/core/src/auth.rs +++ b/crates/core/src/auth.rs @@ -125,9 +125,7 @@ impl AdminStore { { let mut g = self.inner.write(); if g.admin.is_some() { - return Err(Error::Invalid( - "admin account already configured".into(), - )); + return Err(Error::Invalid("admin account already configured".into())); } g.admin = Some(admin.clone()); } @@ -346,7 +344,7 @@ mod tests { assert!(s.bootstrap("", "hunter2hunter2").is_err()); assert!(s.bootstrap("ad:min", "hunter2hunter2").is_err()); // ':' reserved assert!(s.bootstrap("admin", "short").is_err()); // <8 chars - // 65-char username is too long. + // 65-char username is too long. let long = "a".repeat(65); assert!(s.bootstrap(&long, "hunter2hunter2").is_err()); } diff --git a/crates/core/src/client.rs b/crates/core/src/client.rs index 14c119f..b463877 100644 --- a/crates/core/src/client.rs +++ b/crates/core/src/client.rs @@ -12,11 +12,9 @@ use time::OffsetDateTime; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ClientEvent { DhcpDiscover, - DhcpRequest, PxeBootServerRequest, TftpRead { file: String }, HttpScriptFetch { target: String }, - HttpIsoAsset { file: String }, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -32,8 +30,6 @@ pub struct ClientSnapshot { // Events are left with default serialization (9-tuple) — they're // diagnostic only and not consumed by the UI today. pub events: Vec<(OffsetDateTime, ClientEvent)>, - /// The boot target (ISO id) last selected via the iPXE menu, if any. - pub selected_target: Option, } #[derive(Debug, Default)] @@ -66,7 +62,6 @@ impl ClientRegistry { first_seen: now, last_seen: now, events: Vec::new(), - selected_target: None, }); entry.last_seen = now; if ip.is_some() { @@ -84,13 +79,6 @@ impl ClientRegistry { } } - pub fn set_selected_target(&self, mac: &str, target: Option) { - let mut guard = self.inner.write(); - if let Some(c) = guard.get_mut(mac) { - c.selected_target = target; - } - } - #[must_use] pub fn list(&self) -> Vec { let guard = self.inner.read(); @@ -99,9 +87,4 @@ impl ClientRegistry { v.sort_by_key(|c| std::cmp::Reverse(c.last_seen)); v } - - #[must_use] - pub fn get(&self, mac: &str) -> Option { - self.inner.read().get(mac).cloned() - } } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index d202dd4..47682ed 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -40,10 +40,6 @@ pub struct NetworkConfig { pub dhcp_port: u16, /// UDP port for PXE Boot Server discovery. Standard is 4011. pub pxe_port: u16, - /// Optional allowlist of client MAC prefixes (OUI). Empty = serve everyone. - pub mac_allowlist: Vec, - /// Optional allowlist of subnets (CIDR). Empty = serve everyone. - pub subnet_allowlist: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] @@ -105,8 +101,6 @@ impl Default for NetworkConfig { dhcp_bind: IpAddr::V4(Ipv4Addr::UNSPECIFIED), dhcp_port: 67, pxe_port: 4011, - mac_allowlist: Vec::new(), - subnet_allowlist: Vec::new(), } } } diff --git a/crates/core/src/notify.rs b/crates/core/src/notify.rs index 6d7c2f0..37f577f 100644 --- a/crates/core/src/notify.rs +++ b/crates/core/src/notify.rs @@ -303,7 +303,10 @@ mod tests { smtp_host: "smtp.example.com".into(), ..Default::default() }); - assert!(matches!(r, Err(Error::Invalid(_))), "missing recipient should reject"); + assert!( + matches!(r, Err(Error::Invalid(_))), + "missing recipient should reject" + ); s.replace(NotifyConfig { enabled: true, kind: NotifyKind::Smtp, diff --git a/crates/core/src/wol.rs b/crates/core/src/wol.rs index d4b19dd..889ce45 100644 --- a/crates/core/src/wol.rs +++ b/crates/core/src/wol.rs @@ -181,10 +181,7 @@ mod tests { Ipv4Addr::new(192, 168, 1, 255) ); assert_eq!( - subnet_broadcast( - Ipv4Addr::new(10, 5, 3, 7), - Ipv4Addr::new(255, 255, 0, 0) - ), + subnet_broadcast(Ipv4Addr::new(10, 5, 3, 7), Ipv4Addr::new(255, 255, 0, 0)), Ipv4Addr::new(10, 5, 255, 255) ); } @@ -196,7 +193,8 @@ mod tests { // and confirm send_magic transmits the exact 102-byte packet. let rx = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)).unwrap(); let port = rx.local_addr().unwrap().port(); - rx.set_read_timeout(Some(std::time::Duration::from_secs(2))).unwrap(); + rx.set_read_timeout(Some(std::time::Duration::from_secs(2))) + .unwrap(); let packet = magic_packet([0x0a, 0x1b, 0x2c, 0x3d, 0x4e, 0x5f]); let sent = send_magic(&packet, &[Ipv4Addr::LOCALHOST], port).unwrap(); diff --git a/crates/dhcp-proxy/src/escalation.rs b/crates/dhcp-proxy/src/escalation.rs index bcf0f0a..d531da2 100644 --- a/crates/dhcp-proxy/src/escalation.rs +++ b/crates/dhcp-proxy/src/escalation.rs @@ -35,6 +35,13 @@ const ENTRY_TTL: Duration = Duration::from_mins(30); /// entry — escalation is best-effort, never a memory-growth vector. const MAX_ENTRIES: usize = 4096; +/// How often (at most) the whole map is swept for expired entries. +/// Correctness doesn't depend on the sweep — a stale entry is also +/// detected inline when its MAC next appears — so the sweep only bounds +/// memory for MACs that never return, and amortizing it keeps the +/// per-packet path O(1) instead of O(map). +const PRUNE_INTERVAL: Duration = Duration::from_mins(1); + #[derive(Debug, Clone, Copy)] struct Entry { mode: DriverMode, @@ -45,10 +52,26 @@ struct Entry { last_seen: Instant, } +#[derive(Debug)] +struct Inner { + map: HashMap, + /// When the last full TTL sweep ran — see [`PRUNE_INTERVAL`]. + last_prune: Instant, +} + +impl Default for Inner { + fn default() -> Self { + Self { + map: HashMap::new(), + last_prune: Instant::now(), + } + } +} + /// Tracks per-MAC driver-mode escalation. Cheap to share via `Arc`. #[derive(Debug, Default)] pub struct DriverEscalation { - inner: Mutex>, + inner: Mutex, } impl DriverEscalation { @@ -75,11 +98,23 @@ impl DriverEscalation { fn decide_at(&self, mac: &str, primary: bool, now: Instant) -> DriverMode { let mut g = self.inner.lock(); - g.retain(|_, e| now.duration_since(e.last_seen) < ENTRY_TTL); + if now.duration_since(g.last_prune) >= PRUNE_INTERVAL { + g.map + .retain(|_, e| now.duration_since(e.last_seen) < ENTRY_TTL); + g.last_prune = now; + } + // Inline staleness check: a MAC whose entry outlived the TTL starts + // fresh even when the amortized sweep above hasn't caught it yet. + if g.map + .get(mac) + .is_some_and(|e| now.duration_since(e.last_seen) >= ENTRY_TTL) + { + g.map.remove(mac); + } - match g.get_mut(mac) { + match g.map.get_mut(mac) { None => { - g.insert( + g.map.insert( mac.to_owned(), Entry { mode: DriverMode::Firmware, @@ -88,8 +123,8 @@ impl DriverEscalation { last_seen: now, }, ); - if g.len() > MAX_ENTRIES { - evict_oldest(&mut g); + if g.map.len() > MAX_ENTRIES { + evict_oldest(&mut g.map); } DriverMode::Firmware } @@ -114,7 +149,7 @@ impl DriverEscalation { fn confirm_at(&self, mac: &str, now: Instant) { let mut g = self.inner.lock(); - if let Some(e) = g.get_mut(mac) { + if let Some(e) = g.map.get_mut(mac) { e.awaiting_confirm = false; e.last_seen = now; } diff --git a/crates/dhcp-proxy/src/server.rs b/crates/dhcp-proxy/src/server.rs index 7b65cec..f339848 100644 --- a/crates/dhcp-proxy/src/server.rs +++ b/crates/dhcp-proxy/src/server.rs @@ -237,11 +237,16 @@ fn bind_udp(bind: IpAddr, port: u16, broadcast: bool) -> anyhow::Result String { - let take = chaddr.iter().take(6).copied().collect::>(); - take.iter() - .map(|b| format!("{b:02x}")) - .collect::>() - .join(":") + use std::fmt::Write; + // One allocation — this runs for every PXE datagram we answer. + let mut s = String::with_capacity(17); + for (i, b) in chaddr.iter().take(6).enumerate() { + if i > 0 { + s.push(':'); + } + let _ = write!(s, "{b:02x}"); + } + s } /// Walk raw DHCP options looking for option 93 (Client System Architecture) diff --git a/crates/http-api/src/app.rs b/crates/http-api/src/app.rs index 0f54274..f091712 100644 --- a/crates/http-api/src/app.rs +++ b/crates/http-api/src/app.rs @@ -35,7 +35,7 @@ use openpxe_core::{ encoding::pct_encode, ext_for_mime, wol, BootEvent, ClientEvent, DeployProfile, Error, LogoSlot, NotifyConfig, Settings, SsoConfig, ALLOWED_LOGO_MIMES, MAX_LOGO_BYTES, }; -use openpxe_ipxe_assets::asset_bytes; +use openpxe_ipxe_assets::asset_slice; use openpxe_iso_store::{ render_template, IsoCategory, IsoMeta, IsoSource, NfsAddRequest, SftpAddRequest, SmbAddRequest, SmbState, UnattendedKind, UnattendedMeta, @@ -406,6 +406,22 @@ fn bundled_logo_response() -> Response { /// brand mark falls back to the *default* background for the PXE screen /// (the WebUI still renders the SVG natively in the top-left). async fn ui_pxe_logo(State(state): State) -> Response { + // The composite is a pure function of the uploaded logo, so the + // encoded PNG is cached keyed on the branding revision — an upload + // or clear bumps the rev and invalidates it. The response headers + // stay `no-cache` (clients must refetch); only the server-side + // ~50-200 ms decode/compose/encode is skipped per boot. + let rev = state.branding.logo_rev(); + let cached = state + .pxe_bg_cache + .lock() + .as_ref() + .filter(|(r, _)| *r == rev) + .map(|(_, png)| png.clone()); + if let Some(png) = cached { + return pxe_png_response(png); + } + // Resolve the operator's raster upload, if any and if it's a format // iPXE/our compositor can consume. SVG (or a missing/unreadable // file) yields `None`, which composes the default background. @@ -450,6 +466,12 @@ async fn ui_pxe_logo(State(state): State) -> Response { .into_response(); } }; + let png = bytes::Bytes::from(composed); + *state.pxe_bg_cache.lock() = Some((rev, png.clone())); + pxe_png_response(png) +} + +fn pxe_png_response(png: bytes::Bytes) -> Response { ( [ (header::CONTENT_TYPE, HeaderValue::from_static("image/png")), @@ -460,7 +482,7 @@ async fn ui_pxe_logo(State(state): State) -> Response { HeaderValue::from_static("no-cache, max-age=0"), ), ], - composed, + png, ) .into_response() } @@ -638,7 +660,23 @@ async fn boot_sub( )); } Some(token) => { - match state.iso_store.verify_password(&iso.id, token) { + // bcrypt verify costs ~100-200 ms of pure + // CPU and this path is unauthenticated — + // run it on the blocking pool so password + // probes can't stall the workers that are + // streaming ISO bytes to imaging machines. + let store = state.iso_store.clone(); + let iso_id = iso.id.clone(); + let tok = token.to_string(); + let verdict = match tokio::task::spawn_blocking(move || { + store.verify_password(&iso_id, &tok) + }) + .await + { + Ok(v) => v, + Err(e) => Err(openpxe_core::Error::Other(e.into())), + }; + match verdict { Ok(true) => { /* fall through to render the entry */ } Ok(false) => { // Don't log the candidate — just the @@ -735,9 +773,15 @@ async fn ipxe_binary(AxumPath(name): AxumPath) -> Response { if name.contains('/') || name.contains('\\') { return (StatusCode::BAD_REQUEST, "invalid name").into_response(); } - let Some(bytes) = asset_bytes(&name) else { + let Some(data) = asset_slice(&name) else { return (StatusCode::NOT_FOUND, "no such ipxe asset").into_response(); }; + // Release builds embed the asset in rodata — serve it without the + // ~1 MiB per-request heap copy `into_owned` would cost. + let bytes = match data { + std::borrow::Cow::Borrowed(b) => bytes::Bytes::from_static(b), + std::borrow::Cow::Owned(v) => bytes::Bytes::from(v), + }; ( [ ( @@ -768,7 +812,10 @@ async fn iso_raw( }; match &meta.source { IsoSource::Local => { - let Some(path) = state.iso_store.iso_path_for(id) else { + // `local_path(&meta)` reuses the meta we already cloned — + // `iso_path_for(id)` would re-lock and deep-clone it again, + // hundreds of times per sanboot install. + let Some(path) = state.iso_store.local_path(&meta) else { return (StatusCode::NOT_FOUND, "no such iso").into_response(); }; match stream_file_range(&path, headers.get(header::RANGE)).await { @@ -1027,15 +1074,25 @@ fn parse_range(h: Option<&HeaderValue>, total: u64) -> Option<(u64, u64, bool)> return Some((total.saturating_sub(n), total.saturating_sub(1), true)); } } - let mut parts = spec.splitn(2, '-'); - let start = parts - .next() - .and_then(|s| s.parse::().ok()) - .unwrap_or(0); - let end = parts - .next() - .and_then(|s| s.parse::().ok()) - .unwrap_or(total.saturating_sub(1)); + // RFC 7233 §3.1: a Range header we can't parse is *ignored* (200 + + // full body), never coerced into a bogus 206 claiming the whole + // file. Only `first-pos[-last-pos]` with numeric positions reaches + // the partial path; `None` is reserved for syntactically valid but + // unsatisfiable ranges (→ 416). + let full = Some((0, total.saturating_sub(1), false)); + let Some((start_s, end_s)) = spec.split_once('-') else { + return full; + }; + let Ok(start) = start_s.trim().parse::() else { + return full; + }; + let end = if end_s.trim().is_empty() { + total.saturating_sub(1) + } else if let Ok(e) = end_s.trim().parse::() { + e + } else { + return full; + }; if start >= total { return None; } diff --git a/crates/http-api/src/auth.rs b/crates/http-api/src/auth.rs index 85aa3dd..f008f1e 100644 --- a/crates/http-api/src/auth.rs +++ b/crates/http-api/src/auth.rs @@ -154,10 +154,15 @@ pub fn session_cookie(session: &str) -> String { fn parse_cookie(headers: &axum::http::HeaderMap) -> Option { // `Cookie: a=b; c=d` parsing — small enough not to drag in a crate. + // Two-step strip (name, then '=') keeps this allocation-free per + // candidate and can't match a longer cookie name sharing the prefix. let raw = headers.get(header::COOKIE)?.to_str().ok()?; for part in raw.split(';') { let part = part.trim(); - if let Some(v) = part.strip_prefix(&format!("{SESSION_COOKIE}=")) { + if let Some(v) = part + .strip_prefix(SESSION_COOKIE) + .and_then(|rest| rest.strip_prefix('=')) + { return Some(v.to_string()); } } @@ -246,7 +251,14 @@ pub async fn api_setup(State(state): State, Json(body): Json { let session = state.sessions.create(&pub_.username); login_response(StatusCode::CREATED, &pub_, &session) @@ -271,8 +283,13 @@ pub struct LoginBody { pub async fn api_login(State(state): State, Json(body): Json) -> Response { // Brief, deliberately vague — "invalid credentials" rather than // "no such user" / "wrong password". Same anti-enumeration posture - // as Sonarr/Radarr. - let pub_ = match state.admin.verify(&body.username, &body.password) { + // as Sonarr/Radarr. The bcrypt verify is ~100-200 ms of pure CPU on + // an unauthenticated endpoint, so it runs on the blocking pool. + let admin = state.admin.clone(); + let verdict = tokio::task::spawn_blocking(move || admin.verify(&body.username, &body.password)) + .await + .unwrap_or_else(|e| Err(openpxe_core::Error::Other(e.into()))); + let pub_ = match verdict { Ok(Some(u)) => u, Ok(None) => { return ( @@ -394,11 +411,18 @@ pub async fn api_update_credentials( ) .into_response(); } - let result = state.admin.update_credentials( - &body.current_password, - body.new_username.as_deref(), - body.new_password.as_deref(), - ); + // Two bcrypt operations (verify current + hash new) plus a sync disk + // persist — run the lot on the blocking pool. + let admin = state.admin.clone(); + let result = tokio::task::spawn_blocking(move || { + admin.update_credentials( + &body.current_password, + body.new_username.as_deref(), + body.new_password.as_deref(), + ) + }) + .await + .unwrap_or_else(|e| Err(openpxe_core::Error::Other(e.into()))); match result { Ok(pub_) => { state.sessions.revoke_all(); diff --git a/crates/http-api/src/notify.rs b/crates/http-api/src/notify.rs index 973c1e7..7315720 100644 --- a/crates/http-api/src/notify.rs +++ b/crates/http-api/src/notify.rs @@ -105,7 +105,11 @@ async fn send_email(cfg: &NotifyConfig, subject: &str, body: &str) -> Result<(), .trim() .parse() .map_err(|e| format!("invalid To address '{}': {e}", cfg.smtp_to))?) - .subject(if subject.is_empty() { "OpenPXE" } else { subject }) + .subject(if subject.is_empty() { + "OpenPXE" + } else { + subject + }) .body(body.to_string()) .map_err(|e| format!("could not build email: {e}"))?; diff --git a/crates/http-api/src/state.rs b/crates/http-api/src/state.rs index 6784c97..da78df3 100644 --- a/crates/http-api/src/state.rs +++ b/crates/http-api/src/state.rs @@ -11,6 +11,10 @@ use openpxe_iso_store::{ use std::sync::Arc; use time::OffsetDateTime; +/// Cached composited PXE boot-menu background: `(logo_rev, encoded PNG)`. +/// See `AppState::pxe_bg_cache`. +pub type PxeBgCache = Arc>>; + #[derive(Clone)] pub struct AppState { pub iso_store: IsoStore, @@ -29,6 +33,12 @@ pub struct AppState { /// operator hasn't uploaded anything, the WebUI serves the bundled /// rainbow-horizon mark. pub branding: BrandingStore, + /// v0.6.2: cache of the composited PXE boot-menu background PNG, + /// keyed on the branding logo revision. Composing costs ~50-200 ms + /// of image decode/encode and **every** booting client fetches it + /// for `console --picture` — caching makes that one compose per + /// logo change instead of one per boot. + pub pxe_bg_cache: PxeBgCache, /// Forms-auth admin record + first-run bootstrap state. When /// `admin.is_configured() == false`, the auth middleware passes /// every request through and `/api/me` reports `setup_required`. diff --git a/crates/http-api/tests/full_flow.rs b/crates/http-api/tests/full_flow.rs index df67616..4f3f0d2 100644 --- a/crates/http-api/tests/full_flow.rs +++ b/crates/http-api/tests/full_flow.rs @@ -116,6 +116,7 @@ async fn build_state() -> (AppState, tempfile::TempDir) { hosts, boot_log, branding, + pxe_bg_cache: openpxe_http_api::state::PxeBgCache::default(), admin, sessions, sso, diff --git a/crates/ipxe-assets/src/lib.rs b/crates/ipxe-assets/src/lib.rs index 0080147..9670990 100644 --- a/crates/ipxe-assets/src/lib.rs +++ b/crates/ipxe-assets/src/lib.rs @@ -35,23 +35,11 @@ use rust_embed::Embed; #[include = "wimboot"] pub struct IpxeAssets; -/// Return the embedded iPXE binary for `arch`, or `None` if we didn't bundle -/// one for that architecture. -#[must_use] -pub fn bootfile_bytes(arch: ClientArch) -> Option> { - let name = arch.ipxe_bootfile()?; - IpxeAssets::get(name).map(|f| f.data.into_owned()) -} - -/// Return a named asset directly (e.g. `wimboot`, or a fallback `ipxe.efi`). -#[must_use] -pub fn asset_bytes(name: &str) -> Option> { - IpxeAssets::get(name).map(|f| f.data.into_owned()) -} - -/// Same as [`asset_bytes`] but returns the embedded slice directly, -/// avoiding the heap copy when the caller only needs to read the -/// payload. Falls back to None for unknown names. +/// Return a named embedded asset (e.g. `snponly.efi`, `wimboot`) as a +/// `Cow` over the embedded bytes. In release builds the data is borrowed +/// straight from the binary's rodata — **zero copy** — which matters +/// because the TFTP and HTTP serving paths hit this for every boot +/// (`ipxe.efi` is ~1 MiB). Debug builds read from disk and return Owned. #[must_use] pub fn asset_slice(name: &str) -> Option> { IpxeAssets::get(name).map(|f| f.data) diff --git a/crates/iso-store/src/introspect.rs b/crates/iso-store/src/introspect.rs index ebe39b8..b91abca 100644 --- a/crates/iso-store/src/introspect.rs +++ b/crates/iso-store/src/introspect.rs @@ -103,7 +103,13 @@ pub fn introspect(path: &Path) -> IntrospectionReport { let scan_bytes = 64 * 1024 * 1024; let mut buf = vec![0u8; 1024 * 1024]; let mut read_total = 0usize; - let mut haystack = Vec::with_capacity(scan_bytes.min(32 * 1024 * 1024)); + // Size the haystack to what will actually be read — the scan cap or + // the file itself, whichever is smaller — so the fill never reallocs + // and a small ISO doesn't reserve the full 64 MiB. + let file_len = f.metadata().map_or(usize::MAX, |m| { + usize::try_from(m.len()).unwrap_or(usize::MAX) + }); + let mut haystack = Vec::with_capacity(scan_bytes.min(file_len)); while read_total < scan_bytes { let n = f.read(&mut buf).unwrap_or(0); if n == 0 { diff --git a/crates/iso-store/src/nfs_share.rs b/crates/iso-store/src/nfs_share.rs index 2652e6c..db83c58 100644 --- a/crates/iso-store/src/nfs_share.rs +++ b/crates/iso-store/src/nfs_share.rs @@ -63,8 +63,8 @@ use bytes::Bytes; use nfs3_client::tokio::TokioConnector; use nfs3_client::Nfs3ConnectionBuilder; use nfs3_types::nfs3::{ - self as nfs3, diropargs3, entry3, filename3, nfs_fh3, GETATTR3args, LOOKUP3args, - Nfs3Result, READ3args, READDIR3args, + self as nfs3, diropargs3, entry3, filename3, nfs_fh3, GETATTR3args, LOOKUP3args, Nfs3Result, + READ3args, READDIR3args, }; use nfs3_types::rpc::{auth_unix, opaque_auth}; use nfs3_types::xdr_codec::Opaque; @@ -220,10 +220,7 @@ impl NfsShareManager { /// Register an NFS share. Validates, probes connectivity by /// performing a real MOUNT3 + READDIR3, and registers the /// resulting ISOs with the store. - pub async fn add( - &self, - req: NfsAddRequest, - ) -> std::result::Result { + pub async fn add(&self, req: NfsAddRequest) -> std::result::Result { let server = normalize_server(&req.server); let export = req.export.trim().to_string(); if server.is_empty() { @@ -258,7 +255,9 @@ impl NfsShareManager { if let Err(e) = self.rescan_inner(&id).await { let m = self.get(&id); return Err(NfsShareError { - error: m.as_ref().and_then(|m| m.last_error.clone()) + error: m + .as_ref() + .and_then(|m| m.last_error.clone()) .unwrap_or_else(|| e.to_string()), stderr: String::new(), hint: m.and_then(|m| m.last_hint), @@ -333,8 +332,7 @@ impl NfsShareManager { return Err(Error::Invalid(format!("invalid filename '{filename}'"))); } - let (tx, rx) = - tokio::sync::mpsc::channel::>(STREAM_BUFFER_DEPTH); + let (tx, rx) = tokio::sync::mpsc::channel::>(STREAM_BUFFER_DEPTH); let server = share.server.clone(); let export = share.export.clone(); let port = share.port; @@ -358,16 +356,11 @@ impl NfsShareManager { if let Err(e) = result { // Best-effort signal of the error to the consumer. // If the receiver has already dropped we just exit. - let _ = tx - .send(Err(std::io::Error::other(e.to_string()))) - .await; + let _ = tx.send(Err(std::io::Error::other(e.to_string()))).await; } }); - Ok(NfsStream { - rx, - _task: task, - }) + Ok(NfsStream { rx, _task: task }) } // ── internals ───────────────────────────────────────────────────── @@ -982,8 +975,14 @@ mod tests { // the allow-list and the secure/insecure angle. let h = hint_for("connect failed: MNT3ERR_ACCES").unwrap(); let lc = h.to_lowercase(); - assert!(lc.contains("insecure") || lc.contains("privileged"), "got: {h}"); - assert!(lc.contains("allow") || lc.contains("permission"), "got: {h}"); + assert!( + lc.contains("insecure") || lc.contains("privileged"), + "got: {h}" + ); + assert!( + lc.contains("allow") || lc.contains("permission"), + "got: {h}" + ); } #[test] diff --git a/crates/iso-store/src/smb_share.rs b/crates/iso-store/src/smb_share.rs index 4ff307f..10905a3 100644 --- a/crates/iso-store/src/smb_share.rs +++ b/crates/iso-store/src/smb_share.rs @@ -231,10 +231,7 @@ impl SmbShareManager { /// Add or refresh a share. Validates the input, writes a creds /// file, probes connectivity, and scans for ISOs. - pub async fn add( - &self, - req: SmbAddRequest, - ) -> std::result::Result { + pub async fn add(&self, req: SmbAddRequest) -> std::result::Result { let server = normalize_server(&req.server); let share = req.share.trim().trim_start_matches('/').to_string(); if server.is_empty() { @@ -309,7 +306,9 @@ impl SmbShareManager { if let Err(e) = self.rescan_inner(&id).await { let m = self.get(&id); return Err(SmbShareError { - error: m.as_ref().and_then(|m| m.last_error.clone()) + error: m + .as_ref() + .and_then(|m| m.last_error.clone()) .unwrap_or_else(|| e.to_string()), stderr: String::new(), hint: m.and_then(|m| m.last_hint), @@ -365,11 +364,7 @@ impl SmbShareManager { /// throttling concurrent smbclients) would need to await without /// changing the call sites. #[allow(clippy::unused_async)] - pub async fn stream_iso( - &self, - share_id: &str, - filename: &str, - ) -> Result { + pub async fn stream_iso(&self, share_id: &str, filename: &str) -> Result { let share = self .get(share_id) .ok_or_else(|| Error::Invalid(format!("no such SMB share '{share_id}'")))?; @@ -377,9 +372,7 @@ impl SmbShareManager { // share root. smbclient itself accepts only filenames at the // share root in our `get` form, but belt-and-suspenders. if filename.contains('/') || filename.contains('\\') || filename.contains("..") { - return Err(Error::Invalid(format!( - "invalid filename '{filename}'" - ))); + return Err(Error::Invalid(format!("invalid filename '{filename}'"))); } let creds = share .creds_path @@ -537,10 +530,7 @@ impl SmbShareManager { } else { String::new() }; - return Err(( - format!("could not exec smbclient: {e}"), - stderr, - )); + return Err((format!("could not exec smbclient: {e}"), stderr)); } }; if !output.status.success() { @@ -751,10 +741,7 @@ fn parse_ls_iso(out: &str) -> Vec { /// Pre-flight TCP probe to `server:port`. Format matches v0.4.64 NFS /// probe so the UI banner reads consistently. -async fn tcp_probe( - server: &str, - port: u16, -) -> std::result::Result<(), (String, String)> { +async fn tcp_probe(server: &str, port: u16) -> std::result::Result<(), (String, String)> { use tokio::net::TcpStream; let addr = format!("{server}:{port}"); match tokio::time::timeout(PROBE_TIMEOUT, TcpStream::connect(&addr)).await { @@ -931,8 +918,8 @@ mod tests { // exec error in `error` plus an empty `stderr`. The // SmbShareError constructor's hint_for fallback checks error // too, so this pattern needs to translate as well. - let h2 = hint_for("could not exec smbclient: No such file or directory (os error 2)") - .unwrap(); + let h2 = + hint_for("could not exec smbclient: No such file or directory (os error 2)").unwrap(); assert!(h2.contains("smbclient")); } diff --git a/crates/iso-store/src/store.rs b/crates/iso-store/src/store.rs index 7afe7cd..1b6fcfe 100644 --- a/crates/iso-store/src/store.rs +++ b/crates/iso-store/src/store.rs @@ -342,9 +342,18 @@ impl IsoStore { /// SMB sources or when the file is missing. pub fn iso_path_for(&self, id: &str) -> Option { let meta = self.get(id)?; + self.local_path(&meta) + } + + /// Same resolution as [`Self::iso_path_for`], but for a meta the + /// caller already holds — skips the second registry lock + deep + /// clone, which matters on the per-range-request ISO serving path + /// (a sanboot install issues hundreds of those). + #[must_use] + pub fn local_path(&self, meta: &IsoMeta) -> Option { match &meta.source { IsoSource::Local => { - let path = self.iso_path(id); + let path = self.iso_path(&meta.id); if path.exists() { Some(path) } else { diff --git a/crates/openpxe/src/main.rs b/crates/openpxe/src/main.rs index d835d2c..e92c784 100644 --- a/crates/openpxe/src/main.rs +++ b/crates/openpxe/src/main.rs @@ -192,6 +192,7 @@ async fn main() -> anyhow::Result<()> { hosts: hosts.clone(), boot_log: boot_log.clone(), branding: branding.clone(), + pxe_bg_cache: openpxe_http_api::state::PxeBgCache::default(), admin: admin.clone(), sessions: sessions.clone(), sso: sso.clone(), diff --git a/crates/tftp/src/server.rs b/crates/tftp/src/server.rs index bacbcbd..1deb9e9 100644 --- a/crates/tftp/src/server.rs +++ b/crates/tftp/src/server.rs @@ -7,12 +7,12 @@ //! `tftpd`/`in.tftpd` works and is why TFTP is awkward behind stateful NAT: //! the ephemeral ports must be reachable from the client. //! -//! We only serve files from `openpxe_ipxe_assets::asset_bytes` — that is, +//! We only serve files from `openpxe_ipxe_assets::asset_slice` — that is, //! the bundled iPXE binaries and wimboot. No filesystem is ever opened, so //! `../` path traversal attempts simply return ENOENT. use openpxe_core::{ClientEvent, ClientRegistry}; -use openpxe_ipxe_assets::asset_bytes; +use openpxe_ipxe_assets::asset_slice; use socket2::{Domain, Protocol, Socket, Type}; use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; @@ -21,6 +21,7 @@ use tokio::net::UdpSocket; // TFTP opcodes. const OP_RRQ: u16 = 1; +const OP_WRQ: u16 = 2; const OP_DATA: u16 = 3; const OP_ACK: u16 = 4; const OP_ERROR: u16 = 5; @@ -89,16 +90,34 @@ async fn handle_rrq( metrics: openpxe_core::Metrics, ) -> anyhow::Result<()> { let Some(req) = parse_rrq(&packet) else { + // Not a well-formed RRQ. A WRQ deserves an explicit refusal — + // legacy clients retry a silently-dropped write until they time + // out; an ERROR packet fails them fast with a readable reason. + if packet.len() >= 2 && u16::from_be_bytes([packet[0], packet[1]]) == OP_WRQ { + let sock = bind_udp(bind_ip, 0)?; + let _ = send_error(&sock, peer, ERR_ILLEGAL_OP, "writes not supported").await; + } return Ok(()); }; let Request { - filename, options, .. + filename, + mode, + options, } = req; // Per-transfer ephemeral socket. let sock = bind_udp(bind_ip, 0)?; - let Some(file_bytes) = asset_bytes(&filename) else { + // We serve binary boot artifacts; netascii line-ending translation + // would corrupt them. Refuse loudly instead of timing out silently — + // matters for legacy clients that default to netascii. + if !mode.eq_ignore_ascii_case("octet") { + let _ = send_error(&sock, peer, ERR_NOT_DEFINED, "only octet mode is supported").await; + tracing::info!(target: "openpxe::tftp", peer=%peer, %mode, "rejected non-octet transfer"); + return Ok(()); + } + + let Some(file_bytes) = asset_slice(&filename) else { let _ = send_error(&sock, peer, ERR_FILE_NOT_FOUND, "no such file").await; tracing::info!(target: "openpxe::tftp", peer=%peer, file=%filename, "404"); clients.record( @@ -262,7 +281,6 @@ async fn handle_rrq( #[derive(Debug)] struct Request { filename: String, - #[allow(dead_code)] mode: String, options: Vec<(String, String)>, } @@ -393,17 +411,13 @@ fn bind_udp(bind: IpAddr, port: u16) -> anyhow::Result { Ok(UdpSocket::from_std(std_sock)?) } -#[allow(dead_code)] -const _UNUSED: (u16, u16) = (ERR_NOT_DEFINED, ERR_ILLEGAL_OP); - -/// Pure-logic helper used by the unit tests below and (in a refactor) by -/// `handle_rrq`. Given a position in the file and the window, return the -/// (block_no, chunk_len) list this window will emit. Useful as a sanity -/// check that our windowing math matches the wire behavior the spec -/// requires — tested against edge cases (exact-blksize tail, short tail, -/// single-block window). -#[must_use] -pub fn plan_window( +/// Pure-logic mirror of `handle_rrq`'s windowing math, exercised by the +/// unit tests below. Given a position in the file and the window, return +/// the (block_no, chunk_len) list this window will emit — tested against +/// edge cases (exact-blksize tail, short tail, single-block window, +/// block-number wraparound). +#[cfg(test)] +fn plan_window( total: usize, offset: usize, blksize: usize,