Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
*.x86_64
*.hex
dohd
ns2dohd/ns2dohd
test/dohd_base64_test

# Debug files
Expand Down
25 changes: 24 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
export VERSION := 0.8
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/sbin
MANDIR ?= $(PREFIX)/share/man

build:
make -C src
make -C ns2dohd

debug:
make -C src debug
make -C ns2dohd debug

dmalloc:
make -C src dmalloc

asan:
make -C src asan
make -C ns2dohd asan

clean:
make -C src clean
make -C ns2dohd clean
make -C test clean

docker-build:
Expand Down Expand Up @@ -72,5 +80,20 @@ check-flame:
site:
npx docsify-cli serve ./docs

install: build
install -d $(DESTDIR)$(BINDIR)
install -m 0755 src/dohd $(DESTDIR)$(BINDIR)/dohd
install -m 0755 ns2dohd/ns2dohd $(DESTDIR)$(BINDIR)/ns2dohd
install -d $(DESTDIR)$(MANDIR)/man8
install -m 0644 man/dohd.8 $(DESTDIR)$(MANDIR)/man8/dohd.8
install -m 0644 man/ns2dohd.8 $(DESTDIR)$(MANDIR)/man8/ns2dohd.8

uninstall:
rm -f $(DESTDIR)$(BINDIR)/dohd
rm -f $(DESTDIR)$(BINDIR)/ns2dohd
rm -f $(DESTDIR)$(MANDIR)/man8/dohd.8
rm -f $(DESTDIR)$(MANDIR)/man8/ns2dohd.8

.PHONY: build debug dmalloc asan clean docker-build docker-build-alpine docker-run \
check check-asan check-integration check-valgrind check-flame site
check check-asan check-integration check-valgrind check-flame site \
install uninstall
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<span> • </span>
<a href="#-configuration">🔧 Configuration</a>
<span> • </span>
<a href="#-ns2dohd">🌐 ns2dohd</a>
<span> • </span>
<a href="#-acknowledgements">😍 Acknowledgements</a>
<span> • </span>
<a href="#-license">💼 License</a>
Expand All @@ -40,7 +42,14 @@ option: `--enable-tls13` or simply `--enable-all`).

2. Type `make`.

3. Executable will be found in `./src/dohd`
3. Executable will be found in `./src/dohd`.
`ns2dohd` executable will be found in `./ns2dohd/ns2dohd`.

4. Optional install step for binaries and manpages:

```
sudo make install
```

***
# 🎮 Quick start
Expand Down Expand Up @@ -101,6 +110,16 @@ Usage: dohd -c cert -k key [-p port] [-d dnsserver] [-F] [-u user] [-V] [-v] [-h
- '-u *user*' - drop root privileges after binding to the TCP port by switching user (mandatory when running as root)
- '-F' - runs dohd in foreground (instead of creating a daemon)

***
# 🌐 ns2dohd

`ns2dohd` is a companion daemon that accepts plain DNS requests on localhost and forwards them to a DoH endpoint.

- Project README: [`ns2dohd/README.md`](ns2dohd/README.md)
- Manpage: `man ns2dohd`
- To route system DNS through `ns2dohd`, set `nameserver 127.0.0.1` in `/etc/resolv.conf` or set `127.0.0.1` as primary DNS in NetworkManager.
- Run `ns2dohd` as root in daemon mode and drop privileges with `-u`.

***
# 😍 Acknowledgements

Expand Down Expand Up @@ -132,4 +151,3 @@ You should have received a copy of the GNU Affero General Public
License along with dohd. If not, see <http://www.gnu.org/licenses/>.
Dohd is licensed under the terms of GNU Affero General Public License
(GNU AGPL). See COPYING for details.

75 changes: 75 additions & 0 deletions man/dohd.8
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.TH DOHD 8 "February 2026" "dohd 0.8" "System Manager's Manual"
.SH NAME
\fBdohd\fR \- minimalist DNS-over-HTTPS daemon
.SH SYNOPSIS
.B dohd
\-c
.I cert
\-k
.I key
[
.B \-p
.I port
] [
.B \-d
.I dnsserver
] [
.B \-u
.I user
] [
.B \-F
] [
.B \-v
] [
.B \-V
] [
.B \-h
]
.SH DESCRIPTION
.B dohd
listens for incoming DoH requests over TLS and forwards DNS payloads to one or more classic DNS resolvers over UDP.
.SH OPTIONS
.TP
.BI \-c " cert"
TLS certificate chain file (PEM). Mandatory.
.TP
.BI \-k " key"
TLS private key file (PEM). Mandatory.
.TP
.BI \-p " port"
Listen port for DoH service. Default: 8053.
.TP
.BI \-d " dnsserver"
Upstream DNS resolver IP address. Can be repeated to add multiple resolvers.
.TP
.BI \-u " user"
Drop privileges to this user after binding sockets (when started as root).
.TP
.B \-F
Run in foreground (do not daemonize).
.TP
.B \-v
Verbose logging.
.TP
.B \-V
Print version and exit.
.TP
.B \-h
Print help and exit.
.SH EXAMPLES
Run in foreground on port 8053 and forward to 8.8.8.8:
.PP
.nf
dohd -c /etc/test.crt -k /etc/test.key -p 8053 -d 8.8.8.8 -F
.fi
.SH FILES
.TP
.I /etc/test.crt
Example TLS certificate.
.TP
.I /etc/test.key
Example TLS private key.
.SH SEE ALSO
.BR ns2dohd (8)
.SH LICENSE
GNU AGPLv3.
79 changes: 79 additions & 0 deletions man/ns2dohd.8
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.TH NS2DOHD 8 "February 2026" "dohd 0.8" "System Manager's Manual"
.SH NAME
\fBns2dohd\fR \- local DNS to DNS-over-HTTPS forwarding daemon
.SH SYNOPSIS
.B ns2dohd
\-d
.I doh_url
[
.B \-p
.I port
] [
.B \-u
.I user
] [
.B \-r
.I resolver_ip
] [
.B \-A
.I cafile
] [
.B \-F
] [
.B \-v
] [
.B \-V
] [
.B \-h
]
.SH DESCRIPTION
.B ns2dohd
listens on local UDP DNS (default 127.0.0.1:53), receives classic DNS requests from the host, and forwards them to a DoH endpoint over HTTP/2 using wolfSSL and nghttp2.
.PP
To avoid resolver recursion when the local host itself uses 127.0.0.1 as nameserver,
.B ns2dohd
uses a separate bootstrap DNS resolver (default 1.1.1.1) to resolve the DoH endpoint hostname.
.SH OPTIONS
.TP
.BI \-d " doh_url"
DoH endpoint URL (https://...). Mandatory.
.TP
.BI \-p " port"
Local UDP listen port. Default: 53.
.TP
.BI \-u " user"
Drop privileges to this user after binding sockets (when started as root).
.TP
.BI \-r " resolver_ip"
Bootstrap DNS resolver IP used for resolving the DoH endpoint host. Default: 1.1.1.1.
.TP
.BI \-A " cafile"
CA bundle file in PEM format.
.TP
.B \-F
Run in foreground (do not daemonize).
.TP
.B \-v
Verbose logging.
.TP
.B \-V
Print version and exit.
.TP
.B \-h
Print help and exit.
.SH EXAMPLES
Run as local DNS daemon and drop privileges to user \fB_dohd\fR:
.PP
.nf
ns2dohd -d https://dns.dyne.org/dns-query -u _dohd
.fi
.PP
Use a custom bootstrap resolver:
.PP
.nf
ns2dohd -d https://dns.dyne.org/dns-query -r 9.9.9.9 -u _dohd
.fi
.SH SEE ALSO
.BR dohd (8)
.SH LICENSE
GNU AGPLv3.
20 changes: 20 additions & 0 deletions ns2dohd/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CC=gcc

CFLAGS := -Wall -Wextra -Wno-sign-compare -DVERSION=\"${VERSION}\" -fPIE
LDFLAGS := -lwolfssl -lrt -lm -lnghttp2

all: CFLAGS += -O3
all: ns2dohd

debug: CFLAGS += -ggdb -O0
debug: ns2dohd

asan: CFLAGS += -fsanitize=address
asan: LDFLAGS += -fsanitize=address
asan: ns2dohd

ns2dohd: ns2dohd.o
$(CC) -o $@ $^ $(LDFLAGS)

clean:
rm -f *.o ns2dohd
102 changes: 102 additions & 0 deletions ns2dohd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
![dohd logo](https://raw.githubusercontent.com/dyne/dohd/master/docs/dohd.png)

<p align="center">
<a href="https://dyne.org">
<img src="https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%9D%A4%20by-Dyne.org-blue.svg" alt="Dyne.org" />
</a>
</p>

<h4 align="center">
<a href="#-install">💾 Install</a>
<span> • </span>
<a href="#-quick-start">🎮 Quick start</a>
<span> • </span>
<a href="#-configuration">🔧 Configuration</a>
<span> • </span>
<a href="#-license">💼 License</a>
</h4>

`ns2dohd` is a local DNS-to-DoH gateway daemon.
It listens on `127.0.0.1:53` (by default), accepts classic DNS requests, and forwards them to a trusted DoH endpoint using wolfSSL + nghttp2.

***
# 💾 Install

Build requirements: `gcc` or `clang`, `make`, `wolfSSL`, `nghttp2`.

1. Build binaries:

```bash
make build
```

2. Install binaries and manpages (default prefix `/usr/local`):

```bash
sudo make install
```

You can override installation paths, for example:

```bash
sudo make install PREFIX=/usr
```

***
# 🎮 Quick start

Run as root to bind port `53`, then drop privileges using `-u`:

```bash
ns2dohd -d https://dns.dyne.org/dns-query -u nobody
```

`ns2dohd` daemonizes by default. Use `-F` to stay in foreground.

Common options:

- `-d <https://...>` DoH endpoint URL (mandatory)
- `-p <port>` local UDP port (default: `53`)
- `-u <user>` drop privileges after bind
- `-r <resolver_ip>` bootstrap resolver used to resolve the DoH endpoint host (default: `1.1.1.1`)
- `-A <cafile>` custom CA bundle
- `-F` foreground mode
- `-v` verbose logs

See full options with:

```bash
ns2dohd -h
man ns2dohd
```

***
# 🔧 Configuration

To use `ns2dohd` as your host DNS resolver, configure your system DNS to point to localhost.

## /etc/resolv.conf

Set:

```conf
nameserver 127.0.0.1
```

## NetworkManager

Set `127.0.0.1` as the primary DNS server in your active network profile, then reconnect.

After either configuration, keep `ns2dohd` running as a background daemon as root (with `-u` recommended).

Notes:

- `ns2dohd` uses a separate bootstrap resolver (default `1.1.1.1`) for resolving the DoH endpoint hostname, avoiding resolver recursion.
- Change bootstrap resolver with `-r`, for example `-r 9.9.9.9`.

***
# 💼 License

This is free software distributed under the GNU Affero General Public License (AGPLv3).

Author: Dyne.org Foundation `<info@dyne.org>`
Loading