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
|
||||
- name: 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
|
||||
os: macos-latest
|
||||
name: numa-macos-aarch64
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
- target: x86_64-unknown-linux-musl
|
||||
os: ubuntu-latest
|
||||
name: numa-linux-x86_64
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
- target: aarch64-unknown-linux-musl
|
||||
os: ubuntu-latest
|
||||
name: numa-linux-aarch64
|
||||
|
||||
@@ -35,23 +35,28 @@ jobs:
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Install cross-compilation tools
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
- name: Install musl tools (x86_64)
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
run: sudo apt-get update && sudo apt-get install -y musl-tools
|
||||
|
||||
- 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 }}
|
||||
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
|
||||
run: |
|
||||
cd target/${{ matrix.target }}/release
|
||||
tar czf ../../../${{ matrix.name }}.tar.gz numa
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -944,7 +944,6 @@ dependencies = [
|
||||
"rcgen",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"time",
|
||||
@@ -1275,15 +1274,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.14.0"
|
||||
@@ -1296,9 +1286,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.9"
|
||||
version = "0.103.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
|
||||
@@ -26,4 +26,3 @@ rcgen = { version = "0.13", features = ["pem", "x509-parser"] }
|
||||
time = "0.3"
|
||||
rustls = "0.23"
|
||||
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
|
||||
|
||||
build:
|
||||
cargo build
|
||||
|
||||
lint: fmt check
|
||||
lint: fmt check audit
|
||||
|
||||
fmt:
|
||||
cargo fmt --check
|
||||
@@ -13,6 +13,9 @@ fmt:
|
||||
check:
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
audit:
|
||||
cargo audit
|
||||
|
||||
test:
|
||||
cargo test
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# Numa
|
||||
|
||||
[](https://github.com/razvandimescu/numa/actions)
|
||||
[](https://crates.io/crates/numa)
|
||||
[](LICENSE)
|
||||
|
||||
**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.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Open the dashboard: **http://localhost:5380**
|
||||
Open the dashboard: **http://numa.numa** (or `http://localhost:5380`)
|
||||
|
||||
Or build from source:
|
||||
```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,
|
||||
None => {
|
||||
return (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
StatusCode::NOT_FOUND,
|
||||
[(hyper::header::CONTENT_TYPE, "text/html; charset=utf-8")],
|
||||
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
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user