Adopt mature Git config and DNS codecs

This commit is contained in:
ms2k 2026-07-19 00:40:26 +09:00
commit bda342890f
6 changed files with 634 additions and 122 deletions

405
Cargo.lock generated
View file

@ -178,6 +178,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.0" version = "2.13.0"
@ -219,6 +225,12 @@ version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.12.1" version = "1.12.1"
@ -424,6 +436,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.7" version = "0.1.7"
@ -511,6 +529,37 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "defmt"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.10" version = "0.7.10"
@ -662,6 +711,16 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "faster-hex"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73"
dependencies = [
"heapless",
"serde",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.4.1" version = "2.4.1"
@ -727,7 +786,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80a5eca878900c2e39e9e52fd797954b7fc39eeefc8558257114bfea6a698fcf" checksum = "80a5eca878900c2e39e9e52fd797954b7fc39eeefc8558257114bfea6a698fcf"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"libc", "libc",
"log", "log",
"memchr", "memchr",
@ -849,14 +908,95 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "gix-actor"
version = "0.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bc998b8f746dda8565450d08a63b792ced9165d8c27a1ed3f02799ec6a7820f"
dependencies = [
"bstr",
"gix-date",
"gix-error",
]
[[package]]
name = "gix-config"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a29bf266c4cdaf759e535c24ad4ce655b987aeb6911075643403cc7cc5ade583"
dependencies = [
"bstr",
"gix-config-value",
"gix-features",
"gix-glob",
"gix-path",
"gix-ref",
"gix-sec",
"smallvec",
"thiserror",
"unicode-bom",
]
[[package]]
name = "gix-config-value"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed42168329552f6c2e5df09665c104199d45d84bedb53683738a49b57fe1baab"
dependencies = [
"bitflags 2.13.0",
"bstr",
"gix-path",
"libc",
"thiserror",
]
[[package]]
name = "gix-date"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e47b9e8cdc688296609b706428de570f88b1e0eed7156dde7b4a89d26fa4567"
dependencies = [
"bstr",
"gix-error",
"itoa",
"jiff",
]
[[package]]
name = "gix-error"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9292309fd944e71b2a3c96d3c03a6feb8852db646febdde7cbb9f79cb5f329"
dependencies = [
"bstr",
]
[[package]] [[package]]
name = "gix-features" name = "gix-features"
version = "0.48.1" version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1849ae154d38bc403185be14fa871e38e3c93ee606875d94e207fdb9fba52dbc" checksum = "1849ae154d38bc403185be14fa871e38e3c93ee606875d94e207fdb9fba52dbc"
dependencies = [ dependencies = [
"gix-path",
"gix-trace", "gix-trace",
"gix-utils",
"libc", "libc",
"prodash",
"walkdir",
]
[[package]]
name = "gix-fs"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cdff46db8798e47e2f727d84b9379aac5add3dd3d9d0b07bb4d7d5d640771fe"
dependencies = [
"bstr",
"fastrand",
"gix-features",
"gix-path",
"gix-utils",
"thiserror",
] ]
[[package]] [[package]]
@ -865,12 +1005,36 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1fcb8ef5b16bcf874abe9b68d8abb3c0493c876d367ab824151f30a0f3f3756" checksum = "d1fcb8ef5b16bcf874abe9b68d8abb3c0493c876d367ab824151f30a0f3f3756"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"bstr", "bstr",
"gix-features", "gix-features",
"gix-path", "gix-path",
] ]
[[package]]
name = "gix-hash"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0926d3819c837750b4e03c7754901e73f68b8c9b690753a6372a1bed4eedce"
dependencies = [
"faster-hex",
"gix-features",
"sha1-checked",
"sha2 0.11.0",
"thiserror",
]
[[package]]
name = "gix-hashtable"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e261d54091f0d1c729bc83f54548c071bdec60a697de1e58e88bdfd7a99d24e"
dependencies = [
"gix-hash",
"hashbrown",
"parking_lot",
]
[[package]] [[package]]
name = "gix-ignore" name = "gix-ignore"
version = "0.21.1" version = "0.21.1"
@ -884,6 +1048,36 @@ dependencies = [
"unicode-bom", "unicode-bom",
] ]
[[package]]
name = "gix-lock"
version = "23.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65c9dedd9e90b0d47624d2ed241d394e09294118364e87b9b7e5f1fe755f3c2c"
dependencies = [
"gix-tempfile",
"gix-utils",
"thiserror",
]
[[package]]
name = "gix-object"
version = "0.62.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "019b38afc3eac1e41f9fe09a327664b313ba4a120fa5f40e3678795d0e42783e"
dependencies = [
"bstr",
"gix-actor",
"gix-date",
"gix-features",
"gix-hash",
"gix-hashtable",
"gix-utils",
"gix-validate",
"itoa",
"smallvec",
"thiserror",
]
[[package]] [[package]]
name = "gix-path" name = "gix-path"
version = "0.12.1" version = "0.12.1"
@ -896,12 +1090,67 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "gix-ref"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bbfbce1dfd7d7f8469ddef6d3518376aff664348f153cbe0fc3e58ef993d24e"
dependencies = [
"gix-actor",
"gix-features",
"gix-fs",
"gix-hash",
"gix-lock",
"gix-object",
"gix-path",
"gix-tempfile",
"gix-utils",
"gix-validate",
"memmap2",
"thiserror",
]
[[package]]
name = "gix-sec"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8519976e4c7e486270740a5400369f37940779b80bd1377d94cfa1125d01b3"
dependencies = [
"bitflags 2.13.0",
"gix-path",
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "gix-tempfile"
version = "23.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef60812443484e67bf84e444cc71b4c78ae62deb822221774a4fa0c57fdb17f"
dependencies = [
"gix-fs",
"libc",
"parking_lot",
"tempfile",
]
[[package]] [[package]]
name = "gix-trace" name = "gix-trace"
version = "0.1.20" version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44dc45eae785c0eb14173e0f152e6e224dcf4d45b6a6999a3aed22af541ad678" checksum = "44dc45eae785c0eb14173e0f152e6e224dcf4d45b6a6999a3aed22af541ad678"
[[package]]
name = "gix-utils"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d773a906e39472c2b00aaf1993cd120d40198c1ff6db07c0ee9a44d4431b66c1"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"unicode-normalization",
]
[[package]] [[package]]
name = "gix-validate" name = "gix-validate"
version = "0.11.2" version = "0.11.2"
@ -911,12 +1160,31 @@ dependencies = [
"bstr", "bstr",
] ]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.17.1" version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -929,6 +1197,25 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[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",
"ring",
"thiserror",
"tinyvec",
"tracing",
"url",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.2" version = "1.4.2"
@ -1205,6 +1492,48 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e"
dependencies = [
"defmt",
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-link",
]
[[package]]
name = "jiff-static"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jiff-tzdb"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e"
[[package]]
name = "jiff-tzdb-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
[[package]] [[package]]
name = "jni" name = "jni"
version = "0.22.4" version = "0.22.4"
@ -1383,6 +1712,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"bstr", "bstr",
"fuser", "fuser",
"gix-config",
"gix-ignore", "gix-ignore",
"libc", "libc",
"nix 0.31.3", "nix 0.31.3",
@ -1425,6 +1755,7 @@ dependencies = [
name = "lazaret-runtime-supervisor" name = "lazaret-runtime-supervisor"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"hickory-proto",
"lazaret-core", "lazaret-core",
"lazaret-ignored-workspace", "lazaret-ignored-workspace",
"lazaret-linux", "lazaret-linux",
@ -1510,6 +1841,15 @@ version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memmap2"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.9.1" version = "0.9.1"
@ -1552,7 +1892,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -1565,7 +1905,7 @@ version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -1723,6 +2063,10 @@ name = "once_cell"
version = "1.21.4" version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
dependencies = [
"critical-section",
"portable-atomic",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
@ -1797,6 +2141,21 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "portable-atomic"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "postcard" name = "postcard"
version = "1.1.3" version = "1.1.3"
@ -1842,6 +2201,15 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "prodash"
version = "31.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c"
dependencies = [
"parking_lot",
]
[[package]] [[package]]
name = "quinn" name = "quinn"
version = "0.11.11" version = "0.11.11"
@ -1977,7 +2345,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
] ]
[[package]] [[package]]
@ -2114,7 +2482,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -2238,7 +2606,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -2325,6 +2693,27 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sha1"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest 0.10.7",
]
[[package]]
name = "sha1-checked"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423"
dependencies = [
"digest 0.10.7",
"sha1",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.9" version = "0.10.9"
@ -2720,7 +3109,7 @@ version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"bytes", "bytes",
"futures-util", "futures-util",
"http", "http",

View file

@ -28,7 +28,9 @@ ed25519-dalek = { version = "=2.2.0", default-features = false, features = ["std
flate2 = "=1.1.9" flate2 = "=1.1.9"
fuser = { version = "=0.17.0", default-features = false, features = ["abi-7-31"] } fuser = { version = "=0.17.0", default-features = false, features = ["abi-7-31"] }
gix-ignore = "=0.21.1" gix-ignore = "=0.21.1"
gix-config = { version = "=0.58.0", features = ["sha1", "sha256"] }
getrandom = "=0.4.3" getrandom = "=0.4.3"
hickory-proto = { version = "=0.26.1", default-features = false, features = ["std"] }
oci-client = { version = "=0.17.0", default-features = false, features = ["rustls-tls"] } oci-client = { version = "=0.17.0", default-features = false, features = ["rustls-tls"] }
oci-spec = { version = "=0.10.0", default-features = false, features = ["runtime"] } oci-spec = { version = "=0.10.0", default-features = false, features = ["runtime"] }
postcard = { version = "=1.1.3", default-features = false, features = ["use-std"] } postcard = { version = "=1.1.3", default-features = false, features = ["use-std"] }

View file

@ -8,6 +8,7 @@ license.workspace = true
[dependencies] [dependencies]
bstr.workspace = true bstr.workspace = true
fuser.workspace = true fuser.workspace = true
gix-config.workspace = true
gix-ignore.workspace = true gix-ignore.workspace = true
libc.workspace = true libc.workspace = true
nix.workspace = true nix.workspace = true

View file

@ -330,8 +330,10 @@ impl RepositoryPolicy {
gix_ignore::search::Ignore::default(), gix_ignore::search::Ignore::default(),
); );
} }
let ignore_case = read_external_optional(&metadata.config)? let ignore_case = match read_external_optional(&metadata.config)? {
.is_some_and(|bytes| config_boolean(&bytes, b"core", b"ignorecase")); Some(bytes) => config_boolean(&bytes, "core", "ignoreCase")?,
None => false,
};
let case = if ignore_case { let case = if ignore_case {
gix_ignore::glob::pattern::Case::Fold gix_ignore::glob::pattern::Case::Fold
} else { } else {
@ -414,7 +416,7 @@ fn repository_metadata(
let value = bytes let value = bytes
.strip_prefix(b"gitdir:") .strip_prefix(b"gitdir:")
.ok_or_else(|| io::Error::other("worktree Git pointer has no gitdir prefix"))?; .ok_or_else(|| io::Error::other("worktree Git pointer has no gitdir prefix"))?;
let value = trim_ascii(value); let value = value.trim_ascii();
let path = PathBuf::from(OsString::from_vec(value.to_vec())); let path = PathBuf::from(OsString::from_vec(value.to_vec()));
if path.is_absolute() { if path.is_absolute() {
path path
@ -429,7 +431,7 @@ fn repository_metadata(
let git_root = BackingRoot::open(&git_dir)?; let git_root = BackingRoot::open(&git_dir)?;
let common_dir = match git_root.read_file(Path::new("commondir"), 64 * 1024) { let common_dir = match git_root.read_file(Path::new("commondir"), 64 * 1024) {
Ok(bytes) => { Ok(bytes) => {
let path = PathBuf::from(OsString::from_vec(trim_ascii(&bytes).to_vec())); let path = PathBuf::from(OsString::from_vec(bytes.trim_ascii().to_vec()));
if path.is_absolute() { if path.is_absolute() {
path path
} else { } else {
@ -691,38 +693,18 @@ fn index_overflow() -> io::Error {
io::Error::other("Git index offset is out of range") io::Error::other("Git index offset is out of range")
} }
fn config_boolean(bytes: &[u8], section: &[u8], key: &[u8]) -> bool { fn config_boolean(bytes: &[u8], section: &str, key: &str) -> io::Result<bool> {
let mut current = Vec::new(); let configuration = gix_config::File::from_bytes_no_includes(
for line in bytes.split(|byte| *byte == b'\n') { bytes,
let line = trim_ascii(line); gix_config::file::Metadata::api(),
if line.starts_with(b"[") && line.ends_with(b"]") { gix_config::file::init::Options::default(),
current = line[1..line.len() - 1] )
.split(u8::is_ascii_whitespace) .map_err(|error| io::Error::other(format!("Git configuration is malformed: {error}")))?;
.next() configuration
.unwrap_or_default() .boolean_by(section, None, key)
.to_ascii_lowercase(); .transpose()
} else if current == section { .map(|value| value.unwrap_or(false))
let mut fields = line.splitn(2, |byte| *byte == b'='); .map_err(|error| io::Error::other(format!("Git configuration boolean is invalid: {error}")))
let name = trim_ascii(fields.next().unwrap_or_default()).to_ascii_lowercase();
let value = trim_ascii(fields.next().unwrap_or(b"true"));
if name == key {
return value.eq_ignore_ascii_case(b"true")
|| value.eq_ignore_ascii_case(b"yes")
|| value == b"1";
}
}
}
false
}
fn trim_ascii(mut bytes: &[u8]) -> &[u8] {
while bytes.first().is_some_and(u8::is_ascii_whitespace) {
bytes = &bytes[1..];
}
while bytes.last().is_some_and(u8::is_ascii_whitespace) {
bytes = &bytes[..bytes.len() - 1];
}
bytes
} }
fn input_stamp(path: &Path) -> io::Result<Option<InputStamp>> { fn input_stamp(path: &Path) -> io::Result<Option<InputStamp>> {
@ -782,7 +764,7 @@ mod tests {
use std::fs; use std::fs;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use super::{Classification, GitClassifier, parse_index_paths}; use super::{Classification, GitClassifier, config_boolean, parse_index_paths};
static SEQUENCE: AtomicU64 = AtomicU64::new(0); static SEQUENCE: AtomicU64 = AtomicU64::new(0);
@ -802,6 +784,34 @@ mod tests {
assert!(parse_index_paths(&truncated).is_err()); assert!(parse_index_paths(&truncated).is_err());
} }
#[test]
fn git_configuration_boolean_uses_git_casing_and_last_value_semantics() {
assert!(config_boolean(b"[CoRe]\nIgNoReCaSe = on\n", "core", "ignoreCase").unwrap());
assert!(
!config_boolean(
b"[core]\nignoreCase = true\nignoreCase = false\n",
"core",
"ignoreCase"
)
.unwrap()
);
assert!(config_boolean(b"[core]\nignoreCase = perhaps\n", "core", "ignoreCase").is_err());
assert!(!config_boolean(b"[core]\n", "core", "ignoreCase").unwrap());
}
#[test]
fn repository_with_invalid_ignore_case_is_refused() {
let fixture = Fixture::new("invalid-ignore-case");
repository(&fixture.workspace, &[]);
fs::write(
fixture.workspace.join(".git/config"),
b"[core]\nignoreCase = perhaps\n",
)
.unwrap();
assert!(GitClassifier::open(&fixture.workspace, None).is_err());
}
#[test] #[test]
fn repository_without_an_index_has_no_tracked_paths() { fn repository_without_an_index_has_no_tracked_paths() {
let fixture = Fixture::new("absent-index"); let fixture = Fixture::new("absent-index");

View file

@ -10,6 +10,7 @@ name = "lazaret-runtime-supervisor"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
hickory-proto.workspace = true
lazaret-core = { version = "=0.0.0", path = "../lazaret-core" } lazaret-core = { version = "=0.0.0", path = "../lazaret-core" }
lazaret-ignored-workspace = { version = "=0.0.0", path = "../lazaret-ignored-workspace" } lazaret-ignored-workspace = { version = "=0.0.0", path = "../lazaret-ignored-workspace" }
lazaret-linux = { version = "=0.0.0", path = "../lazaret-linux" } lazaret-linux = { version = "=0.0.0", path = "../lazaret-linux" }

View file

@ -5,6 +5,10 @@ use std::os::fd::{AsFd, AsRawFd, OwnedFd};
use std::path::Path; use std::path::Path;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use hickory_proto::{
op::{Message, MessageType, OpCode, Query, ResponseCode},
rr::{DNSClass, Name, RData, RecordType},
};
use lazaret_core::codec::decode; use lazaret_core::codec::decode;
use lazaret_core::identity::{ use lazaret_core::identity::{
ConfigurationFingerprint, DerivedPolicyDigest, DomainId, GenerationId, NamespaceIdentity, ConfigurationFingerprint, DerivedPolicyDigest, DomainId, GenerationId, NamespaceIdentity,
@ -328,7 +332,7 @@ fn dns_probe(target: &DnsProbeTarget, transaction_id: u16) -> io::Result<DnsObse
"DNS probe response source mismatched resolver", "DNS probe response source mismatched resolver",
)); ));
} }
let resolved_addresses = dns_answers(&response[..length], transaction_id)?; let resolved_addresses = dns_answers(&response[..length], transaction_id, &target.query_name)?;
Ok(DnsObservation { Ok(DnsObservation {
resolver: target.resolver, resolver: target.resolver,
query_name: target.query_name.clone(), query_name: target.query_name.clone(),
@ -337,13 +341,25 @@ fn dns_probe(target: &DnsProbeTarget, transaction_id: u16) -> io::Result<DnsObse
} }
fn dns_query(transaction_id: u16, name: &str) -> io::Result<Vec<u8>> { fn dns_query(transaction_id: u16, name: &str) -> io::Result<Vec<u8>> {
let name = dns_name(name)?;
let mut query = Message::new(transaction_id, MessageType::Query, OpCode::Query);
query.metadata.recursion_desired = true;
query.add_query(Query::query(name, RecordType::A));
let bytes = query
.to_vec()
.map_err(|error| io::Error::other(format!("DNS query encoding failed: {error}")))?;
if bytes.len() > MAX_DNS_PACKET {
return Err(io::Error::other("DNS query exceeds packet bound"));
}
Ok(bytes)
}
fn dns_name(name: &str) -> io::Result<Name> {
if name.is_empty() || name.len() > 253 || !name.is_ascii() { if name.is_empty() || name.len() > 253 || !name.is_ascii() {
return Err(io::Error::other("DNS probe name is invalid")); return Err(io::Error::other("DNS probe name is invalid"));
} }
let mut query = Vec::with_capacity(name.len() + 18); let labels = name.trim_end_matches('.');
query.extend_from_slice(&transaction_id.to_be_bytes()); for label in labels.split('.') {
query.extend_from_slice(&[0x01, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]);
for label in name.trim_end_matches('.').split('.') {
if label.is_empty() if label.is_empty()
|| label.len() > 63 || label.len() > 63
|| !label || !label
@ -352,87 +368,62 @@ fn dns_query(transaction_id: u16, name: &str) -> io::Result<Vec<u8>> {
{ {
return Err(io::Error::other("DNS probe label is invalid")); return Err(io::Error::other("DNS probe label is invalid"));
} }
query.push(
u8::try_from(label.len()).map_err(|_| io::Error::other("DNS label is oversized"))?,
);
query.extend_from_slice(label.as_bytes());
} }
query.extend_from_slice(&[0, 0, 1, 0, 1]); let mut name = Name::from_ascii(labels)
Ok(query) .map_err(|error| io::Error::other(format!("DNS probe name is invalid: {error}")))?;
name.set_fqdn(true);
Ok(name)
} }
fn dns_answers(response: &[u8], transaction_id: u16) -> io::Result<Vec<IpAddress>> { fn dns_answers(
if response.len() < 12 response: &[u8],
|| response[..2] != transaction_id.to_be_bytes() transaction_id: u16,
|| response[2] & 0x80 == 0 expected_name: &str,
|| response[3] & 0x0f != 0 ) -> io::Result<Vec<IpAddress>> {
|| u16::from_be_bytes([response[4], response[5]]) != 1 if response.len() > MAX_DNS_PACKET {
{ return Err(io::Error::other("DNS response exceeds packet bound"));
return Err(io::Error::other("DNS probe response header is invalid"));
} }
let answers = u16::from_be_bytes([response[6], response[7]]); let expected_name = dns_name(expected_name)?;
let mut offset = skip_dns_name(response, 12)?; let message = Message::from_vec(response)
offset = offset .map_err(|error| io::Error::other(format!("DNS response decoding failed: {error}")))?;
.checked_add(4) if message.metadata.id != transaction_id
.filter(|offset| *offset <= response.len()) || message.metadata.message_type != MessageType::Response
.ok_or_else(|| io::Error::other("DNS probe question is truncated"))?; || message.metadata.op_code != OpCode::Query
let mut addresses = Vec::new(); || message.metadata.response_code != ResponseCode::NoError
for _ in 0..answers { || message.metadata.truncation
offset = skip_dns_name(response, offset)?; {
let header_end = offset return Err(io::Error::other(
.checked_add(10) "DNS response metadata does not match query",
.filter(|end| *end <= response.len())
.ok_or_else(|| io::Error::other("DNS probe answer is truncated"))?;
let record_type = u16::from_be_bytes([response[offset], response[offset + 1]]);
let class = u16::from_be_bytes([response[offset + 2], response[offset + 3]]);
let data_length = usize::from(u16::from_be_bytes([
response[offset + 8],
response[offset + 9],
]));
let data_end = header_end
.checked_add(data_length)
.filter(|end| *end <= response.len())
.ok_or_else(|| io::Error::other("DNS probe record data is truncated"))?;
if record_type == 1 && class == 1 && data_length == 4 {
addresses.push(IpAddress::V4(
response[header_end..data_end]
.try_into()
.map_err(|_| io::Error::other("DNS A record is malformed"))?,
)); ));
} }
offset = data_end; let [question] = message.queries.as_slice() else {
return Err(io::Error::other(
"DNS response does not contain exactly one question",
));
};
if question.name() != &expected_name
|| question.query_type() != RecordType::A
|| question.query_class() != DNSClass::IN
{
return Err(io::Error::other(
"DNS response question does not match query",
));
} }
let addresses = message
.answers
.iter()
.filter(|answer| answer.record_type() == RecordType::A && answer.dns_class == DNSClass::IN)
.filter_map(|answer| match &answer.data {
RData::A(address) => Some(IpAddress::V4(Ipv4Addr::from(*address).octets())),
_ => None,
})
.collect::<Vec<_>>();
if addresses.is_empty() { if addresses.is_empty() {
return Err(io::Error::other("DNS probe returned no IPv4 addresses")); return Err(io::Error::other("DNS probe returned no IPv4 addresses"));
} }
Ok(addresses) Ok(addresses)
} }
fn skip_dns_name(packet: &[u8], mut offset: usize) -> io::Result<usize> {
loop {
let length = *packet
.get(offset)
.ok_or_else(|| io::Error::other("DNS name is truncated"))?;
offset += 1;
if length == 0 {
return Ok(offset);
}
if length & 0xc0 == 0xc0 {
return offset
.checked_add(1)
.filter(|end| *end <= packet.len())
.ok_or_else(|| io::Error::other("DNS compression pointer is truncated"));
}
if length & 0xc0 != 0 || length > 63 {
return Err(io::Error::other("DNS name label is malformed"));
}
offset = offset
.checked_add(usize::from(length))
.filter(|end| *end <= packet.len())
.ok_or_else(|| io::Error::other("DNS name label is truncated"))?;
}
}
fn read_sealed_policy( fn read_sealed_policy(
policy_file: &File, policy_file: &File,
expected_digest: DerivedPolicyDigest, expected_digest: DerivedPolicyDigest,
@ -660,6 +651,12 @@ fn ipv4_cidr(address: [u8; 4], prefix_length: u8) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::net::Ipv4Addr;
use hickory_proto::rr::{
Record,
rdata::{A, CNAME},
};
use lazaret_core::network::derive_policy; use lazaret_core::network::derive_policy;
use lazaret_core::records::{ use lazaret_core::records::{
AddressFamily, AddressFamilyDisposition, AddressFamilyDispositionValue, DefaultAction, AddressFamily, AddressFamilyDisposition, AddressFamilyDispositionValue, DefaultAction,
@ -687,13 +684,110 @@ mod tests {
response[6..8].copy_from_slice(&1_u16.to_be_bytes()); response[6..8].copy_from_slice(&1_u16.to_be_bytes());
response.extend_from_slice(&[0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 0, 60, 0, 4, 93, 184, 216, 34]); response.extend_from_slice(&[0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 0, 60, 0, 4, 93, 184, 216, 34]);
assert_eq!( assert_eq!(
dns_answers(&response, transaction).unwrap(), dns_answers(&response, transaction, "example.com").unwrap(),
vec![IpAddress::V4([93, 184, 216, 34])] vec![IpAddress::V4([93, 184, 216, 34])]
); );
assert!(dns_answers(&response, transaction + 1).is_err()); assert!(dns_answers(&response, transaction + 1, "example.com").is_err());
assert!(dns_query(transaction, "bad..name").is_err()); assert!(dns_query(transaction, "bad..name").is_err());
} }
#[test]
fn dns_codec_refuses_changed_metadata_and_question() {
assert!(
dns_answers(
&dns_response(|message| message.metadata.message_type = MessageType::Query),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| message.metadata.op_code = OpCode::Update),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| {
message.metadata.response_code = ResponseCode::ServFail;
}),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| message.metadata.truncation = true),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| {
message.queries[0].set_name(Name::from_ascii("other.example").unwrap());
}),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| {
message.queries[0].set_query_type(RecordType::AAAA);
}),
17,
"example.com"
)
.is_err()
);
assert!(
dns_answers(
&dns_response(|message| {
message.queries[0].set_query_class(DNSClass::CH);
}),
17,
"example.com"
)
.is_err()
);
}
#[test]
fn dns_codec_accepts_terminal_a_after_cname_and_refuses_malformed_packets() {
let canonical = Name::from_ascii("canonical.example").unwrap();
let response = dns_response(|message| {
message.answers.clear();
message.add_answer(Record::from_rdata(
Name::from_ascii("example.com").unwrap(),
60,
RData::CNAME(CNAME(canonical.clone())),
));
message.add_answer(Record::from_rdata(
canonical,
60,
RData::A(A(Ipv4Addr::new(192, 0, 2, 2))),
));
});
assert_eq!(
dns_answers(&response, 17, "example.com").unwrap(),
vec![IpAddress::V4([192, 0, 2, 2])]
);
let mut invalid_pointer = dns_response(|_| {});
let answer_offset = dns_query(17, "example.com").unwrap().len();
invalid_pointer[answer_offset] = 0xff;
invalid_pointer[answer_offset + 1] = 0xff;
assert!(dns_answers(&invalid_pointer, 17, "example.com").is_err());
assert!(dns_answers(&vec![0; MAX_DNS_PACKET + 1], 17, "example.com").is_err());
}
#[test] #[test]
fn probe_refuses_a_changed_enforcement_namespace_before_network_io() { fn probe_refuses_a_changed_enforcement_namespace_before_network_io() {
let mut namespace = namespace_identity(Path::new("/proc/self/ns/net")).unwrap(); let mut namespace = namespace_identity(Path::new("/proc/self/ns/net")).unwrap();
@ -732,6 +826,21 @@ mod tests {
assert_eq!(u32::from_le_bytes(mask.to_be_bytes()), 0x0000_00ff); assert_eq!(u32::from_le_bytes(mask.to_be_bytes()), 0x0000_00ff);
} }
fn dns_response(change: impl FnOnce(&mut Message)) -> Vec<u8> {
let name = Name::from_ascii("example.com").unwrap();
let mut response = Message::new(17, MessageType::Response, OpCode::Query);
response.metadata.recursion_desired = true;
response.metadata.recursion_available = true;
response.add_query(Query::query(name.clone(), RecordType::A));
response.add_answer(Record::from_rdata(
name,
60,
RData::A(A(Ipv4Addr::new(93, 184, 216, 34))),
));
change(&mut response);
response.to_vec().unwrap()
}
fn policy() -> CanonicalDerivedNetworkPolicy { fn policy() -> CanonicalDerivedNetworkPolicy {
derive_policy(DerivedNetworkPolicyFields { derive_policy(DerivedNetworkPolicyFields {
address_families: vec![ address_families: vec![