Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f3a4f36c | ||
|
|
14b035387b | ||
|
|
d457ffc296 | ||
|
|
8ab50844c2 | ||
|
|
e04afe5b70 | ||
|
|
44113492f0 | ||
|
|
ec41f32d4e | ||
|
|
a35b0ea23c | ||
|
|
fbdb0a245f | ||
|
|
285778b646 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -24,3 +24,5 @@ jobs:
|
|||||||
run: cargo clippy -- -D warnings
|
run: cargo clippy -- -D warnings
|
||||||
- name: test
|
- name: test
|
||||||
run: cargo test
|
run: cargo test
|
||||||
|
- name: audit
|
||||||
|
run: cargo install cargo-audit && cargo audit
|
||||||
|
|||||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
|||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
name: numa-macos-aarch64
|
name: numa-macos-aarch64
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
name: numa-linux-x86_64
|
name: numa-linux-x86_64
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-musl
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
name: numa-linux-aarch64
|
name: numa-linux-aarch64
|
||||||
|
|
||||||
@@ -35,23 +35,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Install cross-compilation tools
|
- name: Install musl tools (x86_64)
|
||||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||||
run: |
|
run: sudo apt-get update && sudo apt-get install -y musl-tools
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y gcc-aarch64-linux-gnu
|
|
||||||
|
|
||||||
- name: Build
|
- name: Install cross (aarch64)
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-musl'
|
||||||
|
run: cargo install cross
|
||||||
|
|
||||||
|
- name: Build (native)
|
||||||
|
if: matrix.target != 'aarch64-unknown-linux-musl'
|
||||||
run: cargo build --release --target ${{ matrix.target }}
|
run: cargo build --release --target ${{ matrix.target }}
|
||||||
env:
|
|
||||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
- name: Build (cross)
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-musl'
|
||||||
|
run: cross build --release --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
cd target/${{ matrix.target }}/release
|
cd target/${{ matrix.target }}/release
|
||||||
tar czf ../../../${{ matrix.name }}.tar.gz numa
|
tar czf ../../../${{ matrix.name }}.tar.gz numa
|
||||||
cd ../../..
|
cd ../../..
|
||||||
sha256sum ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256
|
sha256sum ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256 || shasum -a 256 ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -944,7 +944,6 @@ dependencies = [
|
|||||||
"rcgen",
|
"rcgen",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pemfile",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time",
|
"time",
|
||||||
@@ -1275,15 +1274,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-pemfile"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
|
||||||
dependencies = [
|
|
||||||
"rustls-pki-types",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.14.0"
|
version = "1.14.0"
|
||||||
@@ -1296,9 +1286,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.9"
|
version = "0.103.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"ring",
|
"ring",
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ rcgen = { version = "0.13", features = ["pem", "x509-parser"] }
|
|||||||
time = "0.3"
|
time = "0.3"
|
||||||
rustls = "0.23"
|
rustls = "0.23"
|
||||||
tokio-rustls = "0.26"
|
tokio-rustls = "0.26"
|
||||||
rustls-pemfile = "2"
|
|
||||||
|
|||||||
7
Makefile
7
Makefile
@@ -1,11 +1,11 @@
|
|||||||
.PHONY: all build lint fmt check test clean deploy
|
.PHONY: all build lint fmt check audit test clean deploy
|
||||||
|
|
||||||
all: lint build
|
all: lint build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cargo build
|
cargo build
|
||||||
|
|
||||||
lint: fmt check
|
lint: fmt check audit
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
cargo fmt --check
|
cargo fmt --check
|
||||||
@@ -13,6 +13,9 @@ fmt:
|
|||||||
check:
|
check:
|
||||||
cargo clippy -- -D warnings
|
cargo clippy -- -D warnings
|
||||||
|
|
||||||
|
audit:
|
||||||
|
cargo audit
|
||||||
|
|
||||||
test:
|
test:
|
||||||
cargo test
|
cargo test
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
# Numa
|
# Numa
|
||||||
|
|
||||||
|
[](https://github.com/razvandimescu/numa/actions)
|
||||||
|
[](https://crates.io/crates/numa)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
**DNS you own. Everywhere you go.**
|
**DNS you own. Everywhere you go.**
|
||||||
|
|
||||||
A portable DNS resolver in a single binary. Block ads on any network, name your local services (`frontend.numa`), and override any hostname with auto-revert — all from your laptop, no cloud account or Raspberry Pi required.
|
A portable DNS resolver in a single binary. Block ads on any network, name your local services (`frontend.numa`), and override any hostname with auto-revert — all from your laptop, no cloud account or Raspberry Pi required.
|
||||||
|
|
||||||
Built from scratch in Rust. Zero DNS libraries. RFC 1035 wire protocol parsed by hand.
|
Built from scratch in Rust. Zero DNS libraries. RFC 1035 wire protocol parsed by hand. One ~8MB binary, no PHP, no web server, no database — everything is embedded.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -22,7 +26,7 @@ dig @127.0.0.1 google.com # ✓ resolves normally
|
|||||||
dig @127.0.0.1 ads.google.com # ✗ blocked → 0.0.0.0
|
dig @127.0.0.1 ads.google.com # ✗ blocked → 0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Open the dashboard: **http://localhost:5380**
|
Open the dashboard: **http://numa.numa** (or `http://localhost:5380`)
|
||||||
|
|
||||||
Or build from source:
|
Or build from source:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 775 KiB |
115
src/proxy.rs
115
src/proxy.rs
@@ -141,9 +141,120 @@ async fn proxy_handler(State(state): State<ProxyState>, req: Request) -> axum::r
|
|||||||
Some(entry) => entry.target_port,
|
Some(entry) => entry.target_port,
|
||||||
None => {
|
None => {
|
||||||
return (
|
return (
|
||||||
StatusCode::BAD_GATEWAY,
|
StatusCode::NOT_FOUND,
|
||||||
|
[(hyper::header::CONTENT_TYPE, "text/html; charset=utf-8")],
|
||||||
format!(
|
format!(
|
||||||
"unknown service: {}{}",
|
r##"<!DOCTYPE html>
|
||||||
|
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>404 — {0}{1}</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Sans:opsz,wght@9..40,400;9..40,500&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*,*::before,*::after {{ margin:0;padding:0;box-sizing:border-box }}
|
||||||
|
body {{
|
||||||
|
font-family: 'DM Sans', system-ui, sans-serif;
|
||||||
|
background: #f5f0e8;
|
||||||
|
color: #2c2418;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}}
|
||||||
|
body::before {{
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='120' height='60' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1' y='1' width='56' height='27' rx='1' fill='none' stroke='%23a39888' stroke-width='0.5' opacity='0.12'/%3E%3Crect x='61' y='1' width='56' height='27' rx='1' fill='none' stroke='%23a39888' stroke-width='0.5' opacity='0.12'/%3E%3Crect x='31' y='31' width='56' height='27' rx='1' fill='none' stroke='%23a39888' stroke-width='0.5' opacity='0.12'/%3E%3C/svg%3E");
|
||||||
|
background-size: 120px 60px;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
-webkit-mask-image: radial-gradient(ellipse at center, transparent 20%, rgba(0,0,0,0.4) 70%);
|
||||||
|
mask-image: radial-gradient(ellipse at center, transparent 20%, rgba(0,0,0,0.4) 70%);
|
||||||
|
}}
|
||||||
|
.container {{
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 480px;
|
||||||
|
padding: 2rem;
|
||||||
|
animation: rise 0.6s cubic-bezier(0.22,1,0.36,1);
|
||||||
|
}}
|
||||||
|
@keyframes rise {{
|
||||||
|
from {{ opacity:0; transform:translateY(20px) }}
|
||||||
|
to {{ opacity:1; transform:translateY(0) }}
|
||||||
|
}}
|
||||||
|
.code {{
|
||||||
|
font-family: 'Instrument Serif', Georgia, serif;
|
||||||
|
font-size: 6rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: #c0623a;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
opacity: 0.85;
|
||||||
|
}}
|
||||||
|
.domain {{
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #2c2418;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
background: rgba(192,98,58,0.08);
|
||||||
|
border: 1px solid rgba(192,98,58,0.15);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
}}
|
||||||
|
.message {{
|
||||||
|
color: #6b5e4f;
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}}
|
||||||
|
.message a {{
|
||||||
|
color: #c0623a;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid rgba(192,98,58,0.3);
|
||||||
|
}}
|
||||||
|
.message a:hover {{ border-bottom-color: #c0623a }}
|
||||||
|
pre {{
|
||||||
|
text-align: left;
|
||||||
|
background: #1a1814;
|
||||||
|
color: #e8e0d4;
|
||||||
|
padding: 1rem 1.2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}}
|
||||||
|
pre .prompt {{ color: #8baa6e }}
|
||||||
|
pre .flag {{ color: #8b9fbb }}
|
||||||
|
pre .str {{ color: #d48a5a }}
|
||||||
|
.lyrics {{
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
font-family: 'Instrument Serif', Georgia, serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #a39888;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
opacity: 0;
|
||||||
|
animation: fade 0.8s 1.5s forwards;
|
||||||
|
}}
|
||||||
|
@keyframes fade {{ to {{ opacity: 1 }} }}
|
||||||
|
</style></head><body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="code">404</div>
|
||||||
|
<div class="domain">{0}{1}</div>
|
||||||
|
<p class="message">This service isn't registered yet.<br>Add it from the <a href="http://numa.numa">dashboard</a> or:</p>
|
||||||
|
<pre><span class="prompt">$</span> <span class="str">curl</span> <span class="flag">-X POST</span> numa.numa:5380/services \
|
||||||
|
<span class="flag">-H</span> 'Content-Type: application/json' \
|
||||||
|
<span class="flag">-d</span> '<span class="str">{{"name":"{0}","target_port":3000}}</span>'</pre>
|
||||||
|
<div class="lyrics">ma-ia hii, ma-ia huu, ma-ia haa, ma-ia ha-ha</div>
|
||||||
|
</div>
|
||||||
|
</body></html>"##,
|
||||||
service_name, state.ctx.proxy_tld_suffix
|
service_name, state.ctx.proxy_tld_suffix
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user