Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20bf14e91c | ||
|
|
e860731c01 | ||
|
|
f556b60ce4 | ||
|
|
422726f1c8 | ||
|
|
dd021d8642 | ||
|
|
f20c72a829 | ||
|
|
44cd17cf84 | ||
|
|
fb0a21e5e6 | ||
|
|
66b937f710 | ||
|
|
524aed7fa1 | ||
|
|
11e3fdeae6 | ||
|
|
636c45b3d7 | ||
|
|
f602687d93 | ||
|
|
b8b0fda1e0 | ||
|
|
9a3fae9a0c | ||
|
|
a31ac36957 | ||
|
|
9001b14fed | ||
|
|
63ac69a222 |
34
.github/dependabot.yml
vendored
Normal file
34
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "chore(deps)"
|
||||||
|
groups:
|
||||||
|
minor-and-patch:
|
||||||
|
patterns: ["*"]
|
||||||
|
update-types: ["minor", "patch"]
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "chore(deps)"
|
||||||
|
groups:
|
||||||
|
minor-and-patch:
|
||||||
|
patterns: ["*"]
|
||||||
|
update-types: ["minor", "patch"]
|
||||||
|
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "chore(deps)"
|
||||||
|
groups:
|
||||||
|
minor-and-patch:
|
||||||
|
patterns: ["*"]
|
||||||
|
update-types: ["minor", "patch"]
|
||||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
check-macos:
|
check-macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: clippy
|
- name: clippy
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
check-windows:
|
check-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: build
|
- name: build
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
- name: test
|
- name: test
|
||||||
run: cargo test
|
run: cargo test
|
||||||
- name: Upload binary
|
- name: Upload binary
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: numa-windows-x86_64
|
name: numa-windows-x86_64
|
||||||
path: target/debug/numa.exe
|
path: target/debug/numa.exe
|
||||||
|
|||||||
19
.github/workflows/homebrew-bump.yml
vendored
19
.github/workflows/homebrew-bump.yml
vendored
@@ -1,8 +1,12 @@
|
|||||||
name: Bump Homebrew Tap
|
name: Bump Homebrew Tap
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
workflow_call:
|
||||||
types: [published]
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to bump (e.g. 0.10.0 or v0.10.0)'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
@@ -16,17 +20,14 @@ jobs:
|
|||||||
bump:
|
bump:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Determine version
|
- name: Determine version
|
||||||
id: ver
|
id: ver
|
||||||
|
env:
|
||||||
|
INPUT_VERSION: ${{ inputs.version }}
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ github.event_name }}" = "release" ]; then
|
V="${INPUT_VERSION#v}"
|
||||||
V="${{ github.event.release.tag_name }}"
|
|
||||||
else
|
|
||||||
V="${{ github.event.inputs.version }}"
|
|
||||||
fi
|
|
||||||
V="${V#v}"
|
|
||||||
echo "version=$V" >> "$GITHUB_OUTPUT"
|
echo "version=$V" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Fetch sha256 checksums from release assets
|
- name: Fetch sha256 checksums from release assets
|
||||||
|
|||||||
23
.github/workflows/release.yml
vendored
23
.github/workflows/release.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
(Get-FileHash "${{ matrix.name }}.zip" -Algorithm SHA256).Hash.ToLower() + " ${{ matrix.name }}.zip" | Out-File "${{ matrix.name }}.zip.sha256" -Encoding ascii
|
(Get-FileHash "${{ matrix.name }}.zip" -Algorithm SHA256).Hash.ToLower() + " ${{ matrix.name }}.zip" | Out-File "${{ matrix.name }}.zip.sha256" -Encoding ascii
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -96,23 +96,22 @@ jobs:
|
|||||||
needs: [build, publish]
|
needs: [build, publish]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
# Use a PAT (not the default GITHUB_TOKEN) so the resulting
|
|
||||||
# `release: published` event propagates to downstream workflows
|
|
||||||
# like homebrew-bump.yml. Events triggered by GITHUB_TOKEN are
|
|
||||||
# deliberately not propagated by GitHub Actions to prevent
|
|
||||||
# infinite loops; PAT-authored events are the documented escape
|
|
||||||
# hatch. Reusing HOMEBREW_TAP_GITHUB_TOKEN (already a PAT used
|
|
||||||
# by homebrew-bump.yml itself) keeps the secret surface flat.
|
|
||||||
token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
|
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
files: |
|
files: |
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zip
|
*.zip
|
||||||
*.sha256
|
*.sha256
|
||||||
|
|
||||||
|
bump-homebrew:
|
||||||
|
needs: release
|
||||||
|
uses: ./.github/workflows/homebrew-bump.yml
|
||||||
|
with:
|
||||||
|
version: ${{ github.ref_name }}
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
8
.github/workflows/static.yml
vendored
8
.github/workflows/static.yml
vendored
@@ -30,18 +30,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Install pandoc
|
- name: Install pandoc
|
||||||
run: sudo apt-get install -y pandoc
|
run: sudo apt-get install -y pandoc
|
||||||
- name: Generate blog HTML
|
- name: Generate blog HTML
|
||||||
run: make blog
|
run: make blog
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v6
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v4
|
||||||
with:
|
with:
|
||||||
# Upload entire repository
|
# Upload entire repository
|
||||||
path: './site'
|
path: './site'
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v5
|
||||||
|
|||||||
241
Cargo.lock
generated
241
Cargo.lock
generated
@@ -17,6 +17,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloca"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anes"
|
name = "anes"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -75,18 +84,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.9.0"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6"
|
checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asn1-rs"
|
name = "asn1-rs"
|
||||||
version = "0.6.2"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
|
checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asn1-rs-derive",
|
"asn1-rs-derive",
|
||||||
"asn1-rs-impl",
|
"asn1-rs-impl",
|
||||||
@@ -94,15 +103,15 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror 1.0.69",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asn1-rs-derive"
|
name = "asn1-rs-derive"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
|
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -368,25 +377,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "criterion"
|
name = "criterion"
|
||||||
version = "0.5.1"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"alloca",
|
||||||
"anes",
|
"anes",
|
||||||
"cast",
|
"cast",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
"clap",
|
"clap",
|
||||||
"criterion-plot",
|
"criterion-plot",
|
||||||
"is-terminal",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
|
||||||
"oorandom",
|
"oorandom",
|
||||||
|
"page_size",
|
||||||
"plotters",
|
"plotters",
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tinytemplate",
|
"tinytemplate",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
@@ -394,9 +402,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "criterion-plot"
|
name = "criterion-plot"
|
||||||
version = "0.5.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cast",
|
"cast",
|
||||||
"itertools",
|
"itertools",
|
||||||
@@ -441,9 +449,9 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der-parser"
|
name = "der-parser"
|
||||||
version = "9.0.0"
|
version = "10.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
|
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asn1-rs",
|
"asn1-rs",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
@@ -702,12 +710,6 @@ version = "0.16.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -755,9 +757,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.8.1"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
|
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -770,7 +772,6 @@ dependencies = [
|
|||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tokio",
|
"tokio",
|
||||||
"want",
|
"want",
|
||||||
@@ -810,7 +811,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.6.3",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -944,17 +945,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is-terminal"
|
|
||||||
version = "0.4.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.2"
|
version = "1.70.2"
|
||||||
@@ -963,9 +953,9 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
@@ -1143,7 +1133,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numa"
|
name = "numa"
|
||||||
version = "0.10.2"
|
version = "0.10.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1162,7 +1152,7 @@ dependencies = [
|
|||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"socket2 0.5.10",
|
"socket2",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
@@ -1172,9 +1162,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oid-registry"
|
name = "oid-registry"
|
||||||
version = "0.7.1"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9"
|
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asn1-rs",
|
"asn1-rs",
|
||||||
]
|
]
|
||||||
@@ -1197,6 +1187,16 @@ version = "11.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem"
|
name = "pem"
|
||||||
version = "3.0.6"
|
version = "3.0.6"
|
||||||
@@ -1219,12 +1219,6 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@@ -1314,8 +1308,8 @@ dependencies = [
|
|||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2 0.6.3",
|
"socket2",
|
||||||
"thiserror 2.0.18",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -1336,7 +1330,7 @@ dependencies = [
|
|||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror 2.0.18",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -1351,7 +1345,7 @@ dependencies = [
|
|||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2 0.6.3",
|
"socket2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
@@ -1422,9 +1416,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rcgen"
|
name = "rcgen"
|
||||||
version = "0.13.2"
|
version = "0.14.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
|
checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pem",
|
"pem",
|
||||||
"ring",
|
"ring",
|
||||||
@@ -1655,11 +1649,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.9"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1698,16 +1692,6 @@ version = "1.15.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "socket2"
|
|
||||||
version = "0.5.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@@ -1761,33 +1745,13 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl 1.0.69",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.18",
|
"thiserror-impl",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1869,24 +1833,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.50.0"
|
version = "1.51.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.6.3",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.6.1"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
|
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1918,44 +1882,42 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.23"
|
version = "1.1.2+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_edit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_datetime"
|
|
||||||
version = "0.6.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_edit"
|
|
||||||
version = "0.22.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde_core",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_write",
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_write"
|
name = "toml_datetime"
|
||||||
version = "0.1.2"
|
version = "1.1.1+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.1.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
@@ -2188,6 +2150,22 @@ dependencies = [
|
|||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
@@ -2197,6 +2175,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -2361,12 +2345,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.15"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
@@ -2382,9 +2363,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x509-parser"
|
name = "x509-parser"
|
||||||
version = "0.16.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69"
|
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asn1-rs",
|
"asn1-rs",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
@@ -2394,7 +2375,7 @@ dependencies = [
|
|||||||
"oid-registry",
|
"oid-registry",
|
||||||
"ring",
|
"ring",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror 1.0.69",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "numa"
|
name = "numa"
|
||||||
version = "0.10.2"
|
version = "0.10.3"
|
||||||
authors = ["razvandimescu <razvan@dimescu.com>"]
|
authors = ["razvandimescu <razvan@dimescu.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Portable DNS resolver in Rust — .numa local domains, ad blocking, developer overrides, DNS-over-HTTPS"
|
description = "Portable DNS resolver in Rust — .numa local domains, ad blocking, developer overrides, DNS-over-HTTPS"
|
||||||
@@ -14,7 +14,7 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "time",
|
|||||||
axum = "0.8"
|
axum = "0.8"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
toml = "0.8"
|
toml = "1.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
reqwest = { version = "0.12", features = ["rustls-tls", "gzip", "http2"], default-features = false }
|
reqwest = { version = "0.12", features = ["rustls-tls", "gzip", "http2"], default-features = false }
|
||||||
@@ -22,8 +22,8 @@ hyper = { version = "1", features = ["client", "http1", "server"] }
|
|||||||
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "tokio"] }
|
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "tokio"] }
|
||||||
http-body-util = "0.1"
|
http-body-util = "0.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
socket2 = { version = "0.5", features = ["all"] }
|
socket2 = { version = "0.6", features = ["all"] }
|
||||||
rcgen = { version = "0.13", features = ["pem", "x509-parser"] }
|
rcgen = { version = "0.14", features = ["pem", "x509-parser"] }
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
rustls = "0.23"
|
rustls = "0.23"
|
||||||
tokio-rustls = "0.26"
|
tokio-rustls = "0.26"
|
||||||
@@ -32,7 +32,7 @@ ring = "0.17"
|
|||||||
rustls-pemfile = "2.2.0"
|
rustls-pemfile = "2.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.8", features = ["html_reports"] }
|
||||||
tower = { version = "0.5", features = ["util"] }
|
tower = { version = "0.5", features = ["util"] }
|
||||||
http = "1"
|
http = "1"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.88-alpine AS builder
|
FROM rust:1.94-alpine AS builder
|
||||||
RUN apk add --no-cache musl-dev cmake make perl
|
RUN apk add --no-cache musl-dev cmake make perl
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
@@ -11,7 +11,7 @@ COPY numa.toml com.numa.dns.plist numa.service ./
|
|||||||
RUN touch src/main.rs src/lib.rs
|
RUN touch src/main.rs src/lib.rs
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
FROM alpine:3.20
|
FROM alpine:3.23
|
||||||
COPY --from=builder /app/target/release/numa /usr/local/bin/numa
|
COPY --from=builder /app/target/release/numa /usr/local/bin/numa
|
||||||
EXPOSE 53/udp 80/tcp 443/tcp 853/tcp 5380/tcp
|
EXPOSE 53/udp 80/tcp 443/tcp 853/tcp 5380/tcp
|
||||||
ENTRYPOINT ["numa"]
|
ENTRYPOINT ["numa"]
|
||||||
|
|||||||
239
src/buffer.rs
239
src/buffer.rs
@@ -84,6 +84,11 @@ impl BytePacketBuffer {
|
|||||||
|
|
||||||
/// Read a qname, handling label compression (pointer jumps).
|
/// Read a qname, handling label compression (pointer jumps).
|
||||||
/// Converts wire format like [3]www[6]google[3]com[0] into "www.google.com".
|
/// Converts wire format like [3]www[6]google[3]com[0] into "www.google.com".
|
||||||
|
///
|
||||||
|
/// Label bytes are escaped per RFC 1035 §5.1:
|
||||||
|
/// - literal `.` within a label → `\.`
|
||||||
|
/// - literal `\` → `\\`
|
||||||
|
/// - bytes outside `0x21..=0x7E` (excluding `.` and `\`) → `\DDD` (3-digit decimal)
|
||||||
pub fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
|
pub fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
|
||||||
let mut pos = self.pos();
|
let mut pos = self.pos();
|
||||||
let mut jumped = false;
|
let mut jumped = false;
|
||||||
@@ -121,7 +126,18 @@ impl BytePacketBuffer {
|
|||||||
|
|
||||||
let str_buffer = self.get_range(pos, len as usize)?;
|
let str_buffer = self.get_range(pos, len as usize)?;
|
||||||
for &b in str_buffer {
|
for &b in str_buffer {
|
||||||
outstr.push(b.to_ascii_lowercase() as char);
|
let c = b.to_ascii_lowercase();
|
||||||
|
match c {
|
||||||
|
b'.' => outstr.push_str("\\."),
|
||||||
|
b'\\' => outstr.push_str("\\\\"),
|
||||||
|
0x21..=0x7E => outstr.push(c as char),
|
||||||
|
_ => {
|
||||||
|
outstr.push('\\');
|
||||||
|
outstr.push((b'0' + c / 100) as char);
|
||||||
|
outstr.push((b'0' + (c / 10) % 10) as char);
|
||||||
|
outstr.push((b'0' + c % 10) as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delim = ".";
|
delim = ".";
|
||||||
@@ -163,24 +179,68 @@ impl BytePacketBuffer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a qname in wire format, parsing RFC 1035 §5.1 text escapes.
|
||||||
|
/// See `read_qname` for the escape grammar.
|
||||||
pub fn write_qname(&mut self, qname: &str) -> Result<()> {
|
pub fn write_qname(&mut self, qname: &str) -> Result<()> {
|
||||||
if qname.is_empty() || qname == "." {
|
if qname.is_empty() || qname == "." {
|
||||||
self.write_u8(0)?;
|
self.write_u8(0)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
for label in qname.split('.') {
|
let bytes = qname.as_bytes();
|
||||||
let len = label.len();
|
let mut i = 0;
|
||||||
if len == 0 {
|
while i < bytes.len() {
|
||||||
continue; // skip empty labels from trailing dot
|
let len_pos = self.pos;
|
||||||
}
|
self.write_u8(0)?; // placeholder length byte, backpatched below
|
||||||
if len > 0x3f {
|
let body_start = self.pos;
|
||||||
return Err("Single label exceeds 63 characters of length".into());
|
|
||||||
|
while i < bytes.len() && bytes[i] != b'.' {
|
||||||
|
let b = bytes[i];
|
||||||
|
if b == b'\\' {
|
||||||
|
i += 1;
|
||||||
|
let c1 = *bytes.get(i).ok_or("trailing backslash in qname")?;
|
||||||
|
if c1.is_ascii_digit() {
|
||||||
|
let c2 = *bytes
|
||||||
|
.get(i + 1)
|
||||||
|
.ok_or("invalid \\DDD escape: expected 3 digits")?;
|
||||||
|
let c3 = *bytes
|
||||||
|
.get(i + 2)
|
||||||
|
.ok_or("invalid \\DDD escape: expected 3 digits")?;
|
||||||
|
if !c2.is_ascii_digit() || !c3.is_ascii_digit() {
|
||||||
|
return Err("invalid \\DDD escape: expected 3 digits".into());
|
||||||
|
}
|
||||||
|
let val =
|
||||||
|
(c1 - b'0') as u16 * 100 + (c2 - b'0') as u16 * 10 + (c3 - b'0') as u16;
|
||||||
|
if val > 255 {
|
||||||
|
return Err(format!("\\DDD escape out of range: {}", val).into());
|
||||||
|
}
|
||||||
|
self.write_u8(val as u8)?;
|
||||||
|
i += 3;
|
||||||
|
} else {
|
||||||
|
// \. \\ and any other \X → literal next byte
|
||||||
|
self.write_u8(c1)?;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.write_u8(b)?;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.pos - body_start > 0x3f {
|
||||||
|
return Err("Single label exceeds 63 characters of length".into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_u8(len as u8)?;
|
let label_len = self.pos - body_start;
|
||||||
for b in label.as_bytes() {
|
if label_len == 0 && i < bytes.len() {
|
||||||
self.write_u8(*b)?;
|
// Empty label from leading/consecutive dots — roll back the placeholder.
|
||||||
|
self.pos = len_pos;
|
||||||
|
} else {
|
||||||
|
self.set(len_pos, label_len as u8)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < bytes.len() && bytes[i] == b'.' {
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,3 +272,160 @@ impl BytePacketBuffer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn roundtrip(wire: &[u8]) -> String {
|
||||||
|
let mut buf = BytePacketBuffer::from_bytes(wire);
|
||||||
|
let mut out = String::new();
|
||||||
|
buf.read_qname(&mut out).unwrap();
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_then_read(text: &str) -> String {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname(text).unwrap();
|
||||||
|
let wire_end = buf.pos();
|
||||||
|
buf.seek(0).unwrap();
|
||||||
|
let mut out = String::new();
|
||||||
|
buf.read_qname(&mut out).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
buf.pos(),
|
||||||
|
wire_end,
|
||||||
|
"reader should consume exactly what writer wrote"
|
||||||
|
);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_plain_domain() {
|
||||||
|
// [3]www[6]google[3]com[0]
|
||||||
|
let wire = b"\x03www\x06google\x03com\x00";
|
||||||
|
assert_eq!(roundtrip(wire), "www.google.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_label_with_literal_dot_is_escaped() {
|
||||||
|
// fanf2's example: [8]exa.mple[3]com[0] — two labels, first contains 0x2E
|
||||||
|
let wire = b"\x08exa.mple\x03com\x00";
|
||||||
|
assert_eq!(roundtrip(wire), "exa\\.mple.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_label_with_backslash_is_escaped() {
|
||||||
|
// [4]a\bc[3]com[0]
|
||||||
|
let wire = b"\x04a\\bc\x03com\x00";
|
||||||
|
assert_eq!(roundtrip(wire), "a\\\\bc.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_label_with_nonprintable_byte_uses_decimal_escape() {
|
||||||
|
// [4]\x00foo[3]com[0] — null byte at label start
|
||||||
|
let wire = b"\x04\x00foo\x03com\x00";
|
||||||
|
assert_eq!(roundtrip(wire), "\\000foo.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_label_with_space_uses_decimal_escape() {
|
||||||
|
// Space (0x20) is outside 0x21..=0x7E, so it must be decimal-escaped.
|
||||||
|
let wire = b"\x05a b c\x00";
|
||||||
|
assert_eq!(roundtrip(wire), "a\\032b\\032c");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_plain_domain() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname("www.google.com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x03www\x06google\x03com\x00");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_escaped_dot_does_not_split_label() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname("exa\\.mple.com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x08exa.mple\x03com\x00");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_escaped_backslash() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname("a\\\\bc.com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x04a\\bc\x03com\x00");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_decimal_escape_yields_raw_byte() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname("\\000foo.com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x04\x00foo\x03com\x00");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_skips_empty_labels() {
|
||||||
|
// Leading dot — first (empty) label is rolled back.
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname(".foo.com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x03foo\x03com\x00");
|
||||||
|
|
||||||
|
// Consecutive dots — middle empty label is rolled back.
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
buf.write_qname("foo..com").unwrap();
|
||||||
|
assert_eq!(&buf.buf[..buf.pos], b"\x03foo\x03com\x00");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_rejects_out_of_range_decimal_escape() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
assert!(buf.write_qname("\\999foo.com").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_rejects_trailing_backslash() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
assert!(buf.write_qname("foo\\").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_rejects_short_decimal_escape() {
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
assert!(buf.write_qname("\\1").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_rejects_label_over_63_bytes() {
|
||||||
|
// 64 bytes exceeds the wire-format label cap.
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
assert!(buf.write_qname(&"a".repeat(64)).is_err());
|
||||||
|
|
||||||
|
// 63 bytes is the maximum permitted label length.
|
||||||
|
let mut buf = BytePacketBuffer::new();
|
||||||
|
assert!(buf.write_qname(&"a".repeat(63)).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_preserves_dot_in_label() {
|
||||||
|
assert_eq!(write_then_read("exa\\.mple.com"), "exa\\.mple.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_preserves_backslash_in_label() {
|
||||||
|
assert_eq!(write_then_read("a\\\\b.com"), "a\\\\b.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_preserves_nonprintable_byte() {
|
||||||
|
assert_eq!(write_then_read("\\000foo.com"), "\\000foo.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn root_name_empty_and_dot_both_produce_single_zero() {
|
||||||
|
let mut a = BytePacketBuffer::new();
|
||||||
|
a.write_qname("").unwrap();
|
||||||
|
let mut b = BytePacketBuffer::new();
|
||||||
|
b.write_qname(".").unwrap();
|
||||||
|
assert_eq!(&a.buf[..a.pos], b"\x00");
|
||||||
|
assert_eq!(&b.buf[..b.pos], b"\x00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use log::{debug, trace};
|
|||||||
use ring::digest;
|
use ring::digest;
|
||||||
use ring::signature;
|
use ring::signature;
|
||||||
|
|
||||||
|
use crate::buffer::BytePacketBuffer;
|
||||||
use crate::cache::{DnsCache, DnssecStatus};
|
use crate::cache::{DnsCache, DnssecStatus};
|
||||||
use crate::packet::DnsPacket;
|
use crate::packet::DnsPacket;
|
||||||
use crate::question::QueryType;
|
use crate::question::QueryType;
|
||||||
@@ -720,22 +721,29 @@ pub fn verify_ds(ds: &DnsRecord, dnskey: &DnsRecord, owner: &str) -> bool {
|
|||||||
|
|
||||||
// -- Canonical wire format --
|
// -- Canonical wire format --
|
||||||
|
|
||||||
|
/// Encode a DNS name in canonical wire form per RFC 4034 §6.2:
|
||||||
|
/// uncompressed, with ASCII letters lowercased.
|
||||||
|
///
|
||||||
|
/// Lowercasing happens *after* escape resolution because `\065` yields
|
||||||
|
/// `'A'`, which canonical form must convert to `'a'`.
|
||||||
pub fn name_to_wire(name: &str) -> Vec<u8> {
|
pub fn name_to_wire(name: &str) -> Vec<u8> {
|
||||||
let mut wire = Vec::with_capacity(name.len() + 2);
|
let mut buf = BytePacketBuffer::new();
|
||||||
if name == "." || name.is_empty() {
|
buf.write_qname(name)
|
||||||
wire.push(0);
|
.expect("name_to_wire: input must parse as a valid DNS name");
|
||||||
return wire;
|
let mut wire = buf.filled().to_vec();
|
||||||
}
|
|
||||||
for label in name.split('.') {
|
let mut i = 0;
|
||||||
if label.is_empty() {
|
while i < wire.len() {
|
||||||
continue;
|
let label_len = wire[i] as usize;
|
||||||
}
|
if label_len == 0 {
|
||||||
wire.push(label.len() as u8);
|
break;
|
||||||
for &b in label.as_bytes() {
|
|
||||||
wire.push(b.to_ascii_lowercase());
|
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
|
let end = i + label_len;
|
||||||
|
wire[i..end].make_ascii_lowercase();
|
||||||
|
i = end;
|
||||||
}
|
}
|
||||||
wire.push(0);
|
|
||||||
wire
|
wire
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1475,6 +1483,23 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn name_to_wire_escaped_dot_in_label_is_not_a_separator() {
|
||||||
|
// `exa\.mple.com` is two labels: `exa.mple` (8 bytes including the 0x2E) and `com`.
|
||||||
|
let wire = name_to_wire("exa\\.mple.com");
|
||||||
|
assert_eq!(
|
||||||
|
wire,
|
||||||
|
vec![8, b'e', b'x', b'a', b'.', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn name_to_wire_decimal_escape_is_lowercased() {
|
||||||
|
// \065 = 'A', must become 'a' in canonical form.
|
||||||
|
let wire = name_to_wire("\\065bc.com");
|
||||||
|
assert_eq!(wire, vec![3, b'a', b'b', b'c', 3, b'c', b'o', b'm', 0]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parent_zone_cases() {
|
fn parent_zone_cases() {
|
||||||
assert_eq!(parent_zone("example.com"), "com");
|
assert_eq!(parent_zone("example.com"), "com");
|
||||||
|
|||||||
@@ -2,6 +2,17 @@ use std::net::SocketAddr;
|
|||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
fn print_recursive_hint() {
|
||||||
|
let is_recursive = crate::config::load_config("numa.toml")
|
||||||
|
.map(|c| c.config.upstream.mode == crate::config::UpstreamMode::Recursive)
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !is_recursive {
|
||||||
|
eprintln!(" Want full DNS sovereignty? Add to numa.toml:");
|
||||||
|
eprintln!(" [upstream]");
|
||||||
|
eprintln!(" mode = \"recursive\"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_loopback_or_stub(addr: &str) -> bool {
|
fn is_loopback_or_stub(addr: &str) -> bool {
|
||||||
matches!(addr, "127.0.0.1" | "127.0.0.53" | "0.0.0.0" | "::1" | "")
|
matches!(addr, "127.0.0.1" | "127.0.0.53" | "0.0.0.0" | "::1" | "")
|
||||||
}
|
}
|
||||||
@@ -688,9 +699,7 @@ fn install_windows() -> Result<(), String> {
|
|||||||
} else {
|
} else {
|
||||||
eprintln!(" Numa will start automatically on next boot.\n");
|
eprintln!(" Numa will start automatically on next boot.\n");
|
||||||
}
|
}
|
||||||
eprintln!(" Want full DNS sovereignty? Add to numa.toml:");
|
print_recursive_hint();
|
||||||
eprintln!(" [upstream]");
|
|
||||||
eprintln!(" mode = \"recursive\"\n");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1181,9 +1190,7 @@ fn install_service_macos() -> Result<(), String> {
|
|||||||
eprintln!(" Numa will auto-start on boot and restart if killed.");
|
eprintln!(" Numa will auto-start on boot and restart if killed.");
|
||||||
eprintln!(" Logs: /usr/local/var/log/numa.log");
|
eprintln!(" Logs: /usr/local/var/log/numa.log");
|
||||||
eprintln!(" Run 'sudo numa uninstall' to restore original DNS.\n");
|
eprintln!(" Run 'sudo numa uninstall' to restore original DNS.\n");
|
||||||
eprintln!(" Want full DNS sovereignty? Add to numa.toml:");
|
print_recursive_hint();
|
||||||
eprintln!(" [upstream]");
|
|
||||||
eprintln!(" mode = \"recursive\"\n");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1388,9 +1395,7 @@ fn install_service_linux() -> Result<(), String> {
|
|||||||
eprintln!(" Numa will auto-start on boot and restart if killed.");
|
eprintln!(" Numa will auto-start on boot and restart if killed.");
|
||||||
eprintln!(" Logs: journalctl -u numa -f");
|
eprintln!(" Logs: journalctl -u numa -f");
|
||||||
eprintln!(" Run 'sudo numa uninstall' to restore original DNS.\n");
|
eprintln!(" Run 'sudo numa uninstall' to restore original DNS.\n");
|
||||||
eprintln!(" Want full DNS sovereignty? Add to numa.toml:");
|
print_recursive_hint();
|
||||||
eprintln!(" [upstream]");
|
|
||||||
eprintln!(" mode = \"recursive\"\n");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
src/tls.rs
34
src/tls.rs
@@ -5,7 +5,9 @@ use std::sync::Arc;
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
use crate::ctx::ServerCtx;
|
use crate::ctx::ServerCtx;
|
||||||
use rcgen::{BasicConstraints, CertificateParams, DnType, IsCa, KeyPair, KeyUsagePurpose, SanType};
|
use rcgen::{
|
||||||
|
BasicConstraints, CertificateParams, DnType, IsCa, Issuer, KeyPair, KeyUsagePurpose, SanType,
|
||||||
|
};
|
||||||
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||||
use rustls::ServerConfig;
|
use rustls::ServerConfig;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
@@ -87,8 +89,8 @@ pub fn build_tls_config(
|
|||||||
alpn: Vec<Vec<u8>>,
|
alpn: Vec<Vec<u8>>,
|
||||||
data_dir: &Path,
|
data_dir: &Path,
|
||||||
) -> crate::Result<Arc<ServerConfig>> {
|
) -> crate::Result<Arc<ServerConfig>> {
|
||||||
let (ca_cert, ca_key) = ensure_ca(data_dir)?;
|
let (ca_der, issuer) = ensure_ca(data_dir)?;
|
||||||
let (cert_chain, key) = generate_service_cert(&ca_cert, &ca_key, tld, service_names)?;
|
let (cert_chain, key) = generate_service_cert(&ca_der, &issuer, tld, service_names)?;
|
||||||
|
|
||||||
// Ensure a crypto provider is installed (rustls needs one)
|
// Ensure a crypto provider is installed (rustls needs one)
|
||||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
@@ -106,7 +108,7 @@ pub fn build_tls_config(
|
|||||||
Ok(Arc::new(config))
|
Ok(Arc::new(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_ca(dir: &Path) -> crate::Result<(rcgen::Certificate, KeyPair)> {
|
fn ensure_ca(dir: &Path) -> crate::Result<(CertificateDer<'static>, Issuer<'static, KeyPair>)> {
|
||||||
let ca_key_path = dir.join("ca.key");
|
let ca_key_path = dir.join("ca.key");
|
||||||
let ca_cert_path = dir.join(CA_FILE_NAME);
|
let ca_cert_path = dir.join(CA_FILE_NAME);
|
||||||
|
|
||||||
@@ -114,10 +116,12 @@ fn ensure_ca(dir: &Path) -> crate::Result<(rcgen::Certificate, KeyPair)> {
|
|||||||
let key_pem = std::fs::read_to_string(&ca_key_path)?;
|
let key_pem = std::fs::read_to_string(&ca_key_path)?;
|
||||||
let cert_pem = std::fs::read_to_string(&ca_cert_path)?;
|
let cert_pem = std::fs::read_to_string(&ca_cert_path)?;
|
||||||
let key_pair = KeyPair::from_pem(&key_pem)?;
|
let key_pair = KeyPair::from_pem(&key_pem)?;
|
||||||
let params = CertificateParams::from_ca_cert_pem(&cert_pem)?;
|
let ca_der = rustls_pemfile::certs(&mut cert_pem.as_bytes())
|
||||||
let cert = params.self_signed(&key_pair)?;
|
.next()
|
||||||
|
.ok_or("empty CA PEM file")??;
|
||||||
|
let issuer = Issuer::from_ca_cert_der(&ca_der, key_pair)?;
|
||||||
info!("loaded CA from {:?}", ca_cert_path);
|
info!("loaded CA from {:?}", ca_cert_path);
|
||||||
return Ok((cert, key_pair));
|
return Ok((ca_der, issuer));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new CA
|
// Generate new CA
|
||||||
@@ -145,14 +149,16 @@ fn ensure_ca(dir: &Path) -> crate::Result<(rcgen::Certificate, KeyPair)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("generated CA at {:?}", ca_cert_path);
|
info!("generated CA at {:?}", ca_cert_path);
|
||||||
Ok((cert, key_pair))
|
let ca_der = cert.der().clone();
|
||||||
|
let issuer = Issuer::new(params, key_pair);
|
||||||
|
Ok((ca_der, issuer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a cert with explicit SANs for each service name.
|
/// Generate a cert with explicit SANs for each service name.
|
||||||
/// Always regenerated at startup (~5ms) — no disk caching needed.
|
/// Always regenerated at startup (~5ms) — no disk caching needed.
|
||||||
fn generate_service_cert(
|
fn generate_service_cert(
|
||||||
ca_cert: &rcgen::Certificate,
|
ca_der: &CertificateDer<'static>,
|
||||||
ca_key: &KeyPair,
|
issuer: &Issuer<'_, KeyPair>,
|
||||||
tld: &str,
|
tld: &str,
|
||||||
service_names: &[String],
|
service_names: &[String],
|
||||||
) -> crate::Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
|
) -> crate::Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
|
||||||
@@ -187,7 +193,7 @@ fn generate_service_cert(
|
|||||||
params.not_before = OffsetDateTime::now_utc();
|
params.not_before = OffsetDateTime::now_utc();
|
||||||
params.not_after = OffsetDateTime::now_utc() + Duration::days(CERT_VALIDITY_DAYS);
|
params.not_after = OffsetDateTime::now_utc() + Duration::days(CERT_VALIDITY_DAYS);
|
||||||
|
|
||||||
let cert = params.signed_by(&key_pair, ca_cert, ca_key)?;
|
let cert = params.signed_by(&key_pair, issuer)?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"generated TLS cert for: {}",
|
"generated TLS cert for: {}",
|
||||||
@@ -198,11 +204,11 @@ fn generate_service_cert(
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
);
|
);
|
||||||
|
|
||||||
let cert_der = CertificateDer::from(cert.der().to_vec());
|
let cert_der = cert.der().clone();
|
||||||
let ca_der = CertificateDer::from(ca_cert.der().to_vec());
|
let ca_cert_der = ca_der.clone();
|
||||||
let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
|
let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
|
||||||
|
|
||||||
Ok((vec![cert_der, ca_der], key_der))
|
Ok((vec![cert_der, ca_cert_der], key_der))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user