Skip to content

Commit a19f086

Browse files
committed
Added 'ns2dohd' localhost daemon, + manpages
1 parent 652cf4f commit a19f086

8 files changed

Lines changed: 1521 additions & 3 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
*.x86_64
3737
*.hex
3838
dohd
39+
ns2dohd/ns2dohd
3940
test/dohd_base64_test
4041

4142
# Debug files

Makefile

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
export VERSION := 0.8
2+
PREFIX ?= /usr/local
3+
BINDIR ?= $(PREFIX)/sbin
4+
MANDIR ?= $(PREFIX)/share/man
5+
26
build:
37
make -C src
8+
make -C ns2dohd
49

510
debug:
611
make -C src debug
12+
make -C ns2dohd debug
713

814
dmalloc:
915
make -C src dmalloc
1016

1117
asan:
1218
make -C src asan
19+
make -C ns2dohd asan
1320

1421
clean:
1522
make -C src clean
23+
make -C ns2dohd clean
1624
make -C test clean
1725

1826
docker-build:
@@ -72,5 +80,20 @@ check-flame:
7280
site:
7381
npx docsify-cli serve ./docs
7482

83+
install: build
84+
install -d $(DESTDIR)$(BINDIR)
85+
install -m 0755 src/dohd $(DESTDIR)$(BINDIR)/dohd
86+
install -m 0755 ns2dohd/ns2dohd $(DESTDIR)$(BINDIR)/ns2dohd
87+
install -d $(DESTDIR)$(MANDIR)/man8
88+
install -m 0644 man/dohd.8 $(DESTDIR)$(MANDIR)/man8/dohd.8
89+
install -m 0644 man/ns2dohd.8 $(DESTDIR)$(MANDIR)/man8/ns2dohd.8
90+
91+
uninstall:
92+
rm -f $(DESTDIR)$(BINDIR)/dohd
93+
rm -f $(DESTDIR)$(BINDIR)/ns2dohd
94+
rm -f $(DESTDIR)$(MANDIR)/man8/dohd.8
95+
rm -f $(DESTDIR)$(MANDIR)/man8/ns2dohd.8
96+
7597
.PHONY: build debug dmalloc asan clean docker-build docker-build-alpine docker-run \
76-
check check-asan check-integration check-valgrind check-flame site
98+
check check-asan check-integration check-valgrind check-flame site \
99+
install uninstall

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
<span> • </span>
1515
<a href="#-configuration">🔧 Configuration</a>
1616
<span> • </span>
17+
<a href="#-ns2dohd">🌐 ns2dohd</a>
18+
<span> • </span>
1719
<a href="#-acknowledgements">😍 Acknowledgements</a>
1820
<span> • </span>
1921
<a href="#-license">💼 License</a>
@@ -40,7 +42,14 @@ option: `--enable-tls13` or simply `--enable-all`).
4042

4143
2. Type `make`.
4244

43-
3. Executable will be found in `./src/dohd`
45+
3. Executable will be found in `./src/dohd`.
46+
`ns2dohd` executable will be found in `./ns2dohd/ns2dohd`.
47+
48+
4. Optional install step for binaries and manpages:
49+
50+
```
51+
sudo make install
52+
```
4453

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

113+
***
114+
# 🌐 ns2dohd
115+
116+
`ns2dohd` is a companion daemon that accepts plain DNS requests on localhost and forwards them to a DoH endpoint.
117+
118+
- Project README: [`ns2dohd/README.md`](ns2dohd/README.md)
119+
- Manpage: `man ns2dohd`
120+
- 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.
121+
- Run `ns2dohd` as root in daemon mode and drop privileges with `-u`.
122+
104123
***
105124
# 😍 Acknowledgements
106125

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

man/dohd.8

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.TH DOHD 8 "February 2026" "dohd 0.8" "System Manager's Manual"
2+
.SH NAME
3+
\fBdohd\fR \- minimalist DNS-over-HTTPS daemon
4+
.SH SYNOPSIS
5+
.B dohd
6+
\-c
7+
.I cert
8+
\-k
9+
.I key
10+
[
11+
.B \-p
12+
.I port
13+
] [
14+
.B \-d
15+
.I dnsserver
16+
] [
17+
.B \-u
18+
.I user
19+
] [
20+
.B \-F
21+
] [
22+
.B \-v
23+
] [
24+
.B \-V
25+
] [
26+
.B \-h
27+
]
28+
.SH DESCRIPTION
29+
.B dohd
30+
listens for incoming DoH requests over TLS and forwards DNS payloads to one or more classic DNS resolvers over UDP.
31+
.SH OPTIONS
32+
.TP
33+
.BI \-c " cert"
34+
TLS certificate chain file (PEM). Mandatory.
35+
.TP
36+
.BI \-k " key"
37+
TLS private key file (PEM). Mandatory.
38+
.TP
39+
.BI \-p " port"
40+
Listen port for DoH service. Default: 8053.
41+
.TP
42+
.BI \-d " dnsserver"
43+
Upstream DNS resolver IP address. Can be repeated to add multiple resolvers.
44+
.TP
45+
.BI \-u " user"
46+
Drop privileges to this user after binding sockets (when started as root).
47+
.TP
48+
.B \-F
49+
Run in foreground (do not daemonize).
50+
.TP
51+
.B \-v
52+
Verbose logging.
53+
.TP
54+
.B \-V
55+
Print version and exit.
56+
.TP
57+
.B \-h
58+
Print help and exit.
59+
.SH EXAMPLES
60+
Run in foreground on port 8053 and forward to 8.8.8.8:
61+
.PP
62+
.nf
63+
dohd -c /etc/test.crt -k /etc/test.key -p 8053 -d 8.8.8.8 -F
64+
.fi
65+
.SH FILES
66+
.TP
67+
.I /etc/test.crt
68+
Example TLS certificate.
69+
.TP
70+
.I /etc/test.key
71+
Example TLS private key.
72+
.SH SEE ALSO
73+
.BR ns2dohd (8)
74+
.SH LICENSE
75+
GNU AGPLv3.

man/ns2dohd.8

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
.TH NS2DOHD 8 "February 2026" "dohd 0.8" "System Manager's Manual"
2+
.SH NAME
3+
\fBns2dohd\fR \- local DNS to DNS-over-HTTPS forwarding daemon
4+
.SH SYNOPSIS
5+
.B ns2dohd
6+
\-d
7+
.I doh_url
8+
[
9+
.B \-p
10+
.I port
11+
] [
12+
.B \-u
13+
.I user
14+
] [
15+
.B \-r
16+
.I resolver_ip
17+
] [
18+
.B \-A
19+
.I cafile
20+
] [
21+
.B \-F
22+
] [
23+
.B \-v
24+
] [
25+
.B \-V
26+
] [
27+
.B \-h
28+
]
29+
.SH DESCRIPTION
30+
.B ns2dohd
31+
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.
32+
.PP
33+
To avoid resolver recursion when the local host itself uses 127.0.0.1 as nameserver,
34+
.B ns2dohd
35+
uses a separate bootstrap DNS resolver (default 1.1.1.1) to resolve the DoH endpoint hostname.
36+
.SH OPTIONS
37+
.TP
38+
.BI \-d " doh_url"
39+
DoH endpoint URL (https://...). Mandatory.
40+
.TP
41+
.BI \-p " port"
42+
Local UDP listen port. Default: 53.
43+
.TP
44+
.BI \-u " user"
45+
Drop privileges to this user after binding sockets (when started as root).
46+
.TP
47+
.BI \-r " resolver_ip"
48+
Bootstrap DNS resolver IP used for resolving the DoH endpoint host. Default: 1.1.1.1.
49+
.TP
50+
.BI \-A " cafile"
51+
CA bundle file in PEM format.
52+
.TP
53+
.B \-F
54+
Run in foreground (do not daemonize).
55+
.TP
56+
.B \-v
57+
Verbose logging.
58+
.TP
59+
.B \-V
60+
Print version and exit.
61+
.TP
62+
.B \-h
63+
Print help and exit.
64+
.SH EXAMPLES
65+
Run as local DNS daemon and drop privileges to user \fB_dohd\fR:
66+
.PP
67+
.nf
68+
ns2dohd -d https://dns.dyne.org/dns-query -u _dohd
69+
.fi
70+
.PP
71+
Use a custom bootstrap resolver:
72+
.PP
73+
.nf
74+
ns2dohd -d https://dns.dyne.org/dns-query -r 9.9.9.9 -u _dohd
75+
.fi
76+
.SH SEE ALSO
77+
.BR dohd (8)
78+
.SH LICENSE
79+
GNU AGPLv3.

ns2dohd/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CC=gcc
2+
3+
CFLAGS := -Wall -Wextra -Wno-sign-compare -DVERSION=\"${VERSION}\" -fPIE
4+
LDFLAGS := -lwolfssl -lrt -lm -lnghttp2
5+
6+
all: CFLAGS += -O3
7+
all: ns2dohd
8+
9+
debug: CFLAGS += -ggdb -O0
10+
debug: ns2dohd
11+
12+
asan: CFLAGS += -fsanitize=address
13+
asan: LDFLAGS += -fsanitize=address
14+
asan: ns2dohd
15+
16+
ns2dohd: ns2dohd.o
17+
$(CC) -o $@ $^ $(LDFLAGS)
18+
19+
clean:
20+
rm -f *.o ns2dohd

ns2dohd/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
![dohd logo](https://raw.githubusercontent.com/dyne/dohd/master/docs/dohd.png)
2+
3+
<p align="center">
4+
<a href="https://dyne.org">
5+
<img src="https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%9D%A4%20by-Dyne.org-blue.svg" alt="Dyne.org" />
6+
</a>
7+
</p>
8+
9+
<h4 align="center">
10+
<a href="#-install">💾 Install</a>
11+
<span> • </span>
12+
<a href="#-quick-start">🎮 Quick start</a>
13+
<span> • </span>
14+
<a href="#-configuration">🔧 Configuration</a>
15+
<span> • </span>
16+
<a href="#-license">💼 License</a>
17+
</h4>
18+
19+
`ns2dohd` is a local DNS-to-DoH gateway daemon.
20+
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.
21+
22+
***
23+
# 💾 Install
24+
25+
Build requirements: `gcc` or `clang`, `make`, `wolfSSL`, `nghttp2`.
26+
27+
1. Build binaries:
28+
29+
```bash
30+
make build
31+
```
32+
33+
2. Install binaries and manpages (default prefix `/usr/local`):
34+
35+
```bash
36+
sudo make install
37+
```
38+
39+
You can override installation paths, for example:
40+
41+
```bash
42+
sudo make install PREFIX=/usr
43+
```
44+
45+
***
46+
# 🎮 Quick start
47+
48+
Run as root to bind port `53`, then drop privileges using `-u`:
49+
50+
```bash
51+
ns2dohd -d https://dns.dyne.org/dns-query -u nobody
52+
```
53+
54+
`ns2dohd` daemonizes by default. Use `-F` to stay in foreground.
55+
56+
Common options:
57+
58+
- `-d <https://...>` DoH endpoint URL (mandatory)
59+
- `-p <port>` local UDP port (default: `53`)
60+
- `-u <user>` drop privileges after bind
61+
- `-r <resolver_ip>` bootstrap resolver used to resolve the DoH endpoint host (default: `1.1.1.1`)
62+
- `-A <cafile>` custom CA bundle
63+
- `-F` foreground mode
64+
- `-v` verbose logs
65+
66+
See full options with:
67+
68+
```bash
69+
ns2dohd -h
70+
man ns2dohd
71+
```
72+
73+
***
74+
# 🔧 Configuration
75+
76+
To use `ns2dohd` as your host DNS resolver, configure your system DNS to point to localhost.
77+
78+
## /etc/resolv.conf
79+
80+
Set:
81+
82+
```conf
83+
nameserver 127.0.0.1
84+
```
85+
86+
## NetworkManager
87+
88+
Set `127.0.0.1` as the primary DNS server in your active network profile, then reconnect.
89+
90+
After either configuration, keep `ns2dohd` running as a background daemon as root (with `-u` recommended).
91+
92+
Notes:
93+
94+
- `ns2dohd` uses a separate bootstrap resolver (default `1.1.1.1`) for resolving the DoH endpoint hostname, avoiding resolver recursion.
95+
- Change bootstrap resolver with `-r`, for example `-r 9.9.9.9`.
96+
97+
***
98+
# 💼 License
99+
100+
This is free software distributed under the GNU Affero General Public License (AGPLv3).
101+
102+
Author: Dyne.org Foundation `<info@dyne.org>`

0 commit comments

Comments
 (0)