Compare commits

..

118 Commits

Author SHA1 Message Date
gdm85
80e95cd028 docs: mention poor compatibility with dnscrypt-proxy 2022-09-03 10:32:29 +02:00
gdm85
1d59772fad docs: mention where to find logs 2022-09-03 10:31:14 +02:00
Star Brilliant
a375bea95d Merge pull request #131 from GreyXor/master
chore: upgrade deps
2022-08-17 16:07:35 +00:00
GreyXor
b98b01cc4e chore: upgrade deps 2022-08-17 10:32:42 +02:00
Star Brilliant
6276bed46f Pre-bump version to 2.3.2 2022-06-01 02:57:07 +00:00
Star Brilliant
19737361ad Release 2.3.1 2022-06-01 02:56:35 +00:00
Star Brilliant
791d2d43dd Merge pull request #128 from GreyXor/master 2022-05-31 11:31:46 +00:00
GreyXor
90753c3910 ci: update setup go version 2022-05-31 12:07:30 +02:00
GreyXor
221240a840 chore: update dependencies 2022-05-31 12:00:18 +02:00
StarBrilliant
b338c7ae52 Bump to version 2.3.1 2021-09-13 10:31:04 +00:00
StarBrilliant
9fd69439c4 Release 2.3.0 2021-09-13 10:30:38 +00:00
Star Brilliant
10eb8f5c87 Merge pull request #116 from leiless/gh-115-go-import-v2
GH#115: Fix Go module semver import
2021-09-13 05:27:45 +00:00
Fishbone
8cd4c4205d gh-115-go-import-v2: Suffix Go module path with /v2 2021-09-12 17:12:39 +08:00
Fishbone
63c6c1de91 gh-115-go-import-v2: Run go mod tidy 2021-09-12 17:09:53 +08:00
Star Brilliant
f25e9a706d Merge pull request #112 from 1574242600/patch-1
Fix not working example docker command
2021-07-15 19:04:41 -04:00
Nworm
a2e3b0cd4b readme: fix not working example docker command 2021-07-15 22:40:57 +08:00
Star Brilliant
f172a7b7fb Merge pull request #110 from gdm85/fix/simplify
Simplify doDNSQuery call
2021-05-15 12:16:35 +00:00
Star Brilliant
56a01679ad Merge pull request #108 from gdm85/fix/add-gh-action
Add GitHub actions to build master and each PR
2021-05-14 16:39:28 +00:00
Star Brilliant
05c3b1676d Merge pull request #107 from gdm85/fix/verbose-logging
Log for response patching only when verbose is enabled
2021-05-13 21:17:46 +00:00
gdm85
5af0d538ca Remove deps download, modern Go does it automatically 2021-05-13 19:20:46 +02:00
gdm85
0bbd26c1b5 Use Go 1.13 2021-05-13 19:19:44 +02:00
gdm85
8a13f085a6 Simplify doDNSQuery call 2021-05-13 19:15:54 +02:00
gdm85
849bc584cc Add GitHub actions to build master and each PR 2021-05-13 19:00:58 +02:00
gdm85
5f8371817b Log for response patching only when verbose is enabled 2021-05-13 18:54:09 +02:00
Star Brilliant
2e36b4ebcd New minimum Go version 2021-04-02 22:23:50 +00:00
Star Brilliant
02dbd9d954 Bump to version 2.2.6 2021-03-25 14:31:03 +00:00
Star Brilliant
0a76416f8e Release 2.2.5 2021-03-25 14:30:44 +00:00
Star Brilliant
82c50163c1 Merge pull request #99 from amincheloh/patch-1
Fix not working example docker command
2021-01-26 19:24:34 +08:00
Amin Cheloh
d5c1c592f6 Fix not working example docker command 2021-01-25 14:30:26 +07:00
Alex Chauvin
1cf98e87c9 add client certificate authentication (#98)
* add client certificate authentication
* fix #97 for ECS forward local addresses
2021-01-08 08:34:25 +00:00
Satish Gaikwad
e7461f2d85 Documentation update: Docker example update (#96)
* Set traefik container version to 2.3 in docker-compose example. This supports recent lets encrypt changes.
* Remove docker swarm related references. Docker swarm example is no more valid. Docker compose is the best example available atm.
2020-12-27 13:25:25 +00:00
Star Brilliant
608394e2d2 Bump to version 2.2.5 2020-12-06 22:53:33 +00:00
Star Brilliant
eb166ececa Release 2.2.4 2020-12-06 22:52:35 +00:00
Star Brilliant
f557e4aa29 Reformat the code 2020-11-24 12:38:16 +00:00
Alex Chauvin
967faec56c add options for ECS full subnet mask in server & TLS verification bypass in client (#92)
* add ECS full size & limit filtering

* add tls certification bypass in configuration

* flush log lines

* changes following pull request comments

* with fmt and reorg of libs in client.go
2020-11-24 12:35:23 +00:00
Star Brilliant
2aa7370aaf Bump to version 2.2.4 2020-11-22 13:27:30 +00:00
Star Brilliant
b63e86bab3 Release 2.2.3 2020-11-22 13:26:41 +00:00
Star Brilliant
7c96cd4436 Merge pull request #91 from dwoffinden/patch-1
Fix an inconsistency in the example doh-client.conf
2020-11-22 13:17:44 +00:00
Daniel Woffinden
f5f1a8f3f4 Fix an inconsistency in the example doh-client.conf
Above, it was said that 8.8.8.8 had good ECS, so don't contradict that further down.

This confused a reviewer of https://github.com/NixOS/nixpkgs/pull/104530 :)
2020-11-22 12:18:12 +00:00
Star Brilliant
4f46b89feb Resolve (some) linter warnings 2020-08-02 05:58:24 +08:00
Star Brilliant
2c7e70466e Rewrite globalip_test 2020-08-02 05:53:21 +08:00
Star Brilliant
88f9ef84d1 Merge pull request #83 from sanyo0714/globalip_use_iptree
Use ipTree to determine the global IP
2020-08-02 05:02:15 +08:00
Star Brilliant
63bceea638 Merge branch 'master' into globalip_use_iptree 2020-08-02 05:01:56 +08:00
Star Brilliant
16120fdc11 Bump to version 2.2.3 2020-08-02 04:44:31 +08:00
Star Brilliant
b2fcfb706c Release 2.2.2 2020-08-02 04:43:22 +08:00
Star Brilliant
64e9375e3b Merge pull request #85 from leiless/alidns-json-Question
json-dns/response.go: Fix variant question response in Response.Question
2020-08-02 02:46:52 +08:00
leixiang
34feec9f5d json-dns/response.go: Fix variant question response in Response.Question
Known affected DoH server:
    https://www.alidns.com/faqs/#dns-safe
2020-08-01 13:26:35 +08:00
Star Brilliant
6d30a12d5f Pass X-Real-IP to handlers.CombinedLoggingHandler
Note that X-Forwarded-For or guessed client IP are not used due to security concerns.
This should fix issue #71.
2020-07-30 20:44:18 +08:00
sanyo
0c878a6ad7 change git ignore 2020-07-29 10:50:38 +08:00
Star Brilliant
a8aed7e09a Use ExchangeClient for DNS request 2020-07-26 22:09:24 +08:00
sanyo
31ea7c520d Use ipTree to determine the global IP 2020-07-16 17:11:34 +08:00
Star Brilliant
6e99d8153a Merge pull request #78 from leiless/DNSSEC-OK
[JSON-DOH] Honor DNSSEC OK flag for incoming DNS requests
2020-04-19 14:49:55 +08:00
Star Brilliant
2d4495a0dd Merge pull request #79 from leiless/json-fix-empty-rr-names
[JSON-DOH] Fix DNS response empty []RR.Name
2020-04-19 14:49:07 +08:00
leixiang
b30056a590 doh-client/google.go: [JSON-DOH] Fix DNS response empty []RR.Name
Cloudflare JSON DOH may return empty RR names if r.Question[0].Name is "."
Which causes malformed DNS response
2020-04-19 11:28:28 +08:00
leixiang
b92da52539 .gitignore: Ignore make output binaries 2020-04-19 11:02:16 +08:00
leixiang
f43d2c69e0 doh-client/google.go: [JSON-DOH] Honor DNSSEC OK flag for incoming DNS requests 2020-04-19 10:57:03 +08:00
Star Brilliant
5f1f418664 Merge pull request #77 from Henrocker/patch-1
Upgrade Caddyfile config to v2 and drastically reduce size.
2020-04-10 23:30:01 +08:00
Henrik
9a316a56a8 Upgrade Caddyfile config to v2 and drastically reduce size.
Since Caddy v2 is in RC state, config should be updates to v2. Also config has been simplified a lot.

Running example with this config: https://dns.hnrk.io/dns

Cheers and happy easter 😊!
2020-04-10 17:05:37 +02:00
Star Brilliant
81b977ca11 Merge pull request #76 from satishweb/master
Docker compose based doh-server setup example
2020-04-05 20:42:36 +08:00
Satish Gaikwad
b7d252de7b Added arch types 2020-04-05 02:45:01 -07:00
Satish Gaikwad
e12b87b48d Enhanced documentation. Added Docker compose based doh-server deployment example. 2020-04-05 02:33:20 -07:00
Star Brilliant
09bdfe2b14 Merge pull request #75 from buckaroogeek/pihole
Added pi-hole as potential dnssec validator
2020-04-04 15:21:35 +08:00
Bradley G Smith
a84b65dd56 expose container to port 53/upd 2020-04-03 10:58:57 -07:00
Bradley G Smith
540f6e3043 Added pi-hole as potential dnssec validator 2020-04-03 08:07:34 -07:00
Star Brilliant
7db67db247 Merge pull request #74 from satishweb/master
Added example configuration for Docker Flow Proxy + Docker
2020-04-03 10:49:39 +08:00
Satish Gaikwad
026d89ac8d Added info on ipv6 support. Added simple docker run example in installation section 2020-04-02 10:42:33 -07:00
Satish Gaikwad
8228ea6299 Added example configuration for Docker Flow Proxy + Docker 2020-04-01 14:54:09 -07:00
Star Brilliant
59f79fb666 Merge pull request #67 from monperrus/patch-1
doc: document upstream_selector
2020-02-05 00:41:24 +08:00
Martin Monperrus
502caabd15 doc: document upstream_selector
fix #66
2020-02-04 15:39:29 +00:00
Star Brilliant
f151c90e9d Merge pull request #61 from m13253/feature/no_ecs_arg
Allow client opt-out of EDNS0 Client Subnet
2019-11-11 12:21:41 +08:00
James Swineson
d8e3969640 add no_ecs argument in query string support 2019-11-11 10:11:25 +08:00
Star Brilliant
475ef65f57 Bump version to 2.2.2 2019-10-29 06:07:30 +08:00
Star Brilliant
72165bffff Release 2.2.1 2019-10-29 06:07:09 +08:00
Star Brilliant
82317bd63e Remove weird logs, fix #59 2019-10-29 03:23:00 +08:00
Star Brilliant
acf3e3c328 Bump version to 2.2.1 2019-10-27 22:41:14 +08:00
Star Brilliant
b708ff47b9 Release 2.2.0 2019-10-27 22:40:48 +08:00
Star Brilliant
4f4966878f Merge pull request #58 from gdm85/master
Add support for DNS-over-TLS upstream resolvers
2019-10-16 19:18:26 +08:00
gdm85
a09dfbbbc1 Add support for type prefix for upstream addresses
Add support for DNS-over-TLS upstream addresses
Remove tcp_only configuration option
2019-10-16 13:14:03 +02:00
gdm85
cc60be718c Improve error logging/checking 2019-10-16 13:14:03 +02:00
gdm85
2067eb688f Fix Opcode never assigned in jsonDNS.PrepareReply 2019-10-16 13:14:03 +02:00
Star Brilliant
ba9b14045e Merge pull request #57 from NESC1US/patch-1
Update Readme.md
2019-10-03 03:11:34 +08:00
NESC1US
ebcc85c01a Update Readme.md
Typo
2019-10-02 20:13:53 +02:00
Star Brilliant
48618aa6e2 Merge pull request #55 from fuero/feature-rpm-package
RPM package + SELinux policy
2019-09-11 21:05:49 +08:00
Star Brilliant
b78329afbc Merge pull request #54 from fuero/feature-nginx-config
Example nginx config
2019-09-11 20:51:36 +08:00
fuero
b1c41e5818 adds example nginx config 2019-09-11 14:48:17 +02:00
fuero
637d50ad91 initial package 2019-09-11 14:13:08 +02:00
Star Brilliant
ce13a961db Fix build error 2019-09-11 02:08:02 +08:00
Star Brilliant
b74220718f Add an option no_user_agent 2019-09-11 00:23:20 +08:00
Star Brilliant
db522591a1 Add example Apache and Caddy configurations 2019-08-31 21:30:20 +08:00
Star Brilliant
1eda33aec3 Add example Apache and Caddy configurations
Solves issue #51
2019-08-31 21:27:32 +08:00
Star Brilliant
268e203540 Release 2.1.2 2019-08-30 01:27:06 +08:00
Star Brilliant
21264c78cf Merge pull request #52 from takumin/patch-1
fix typo
2019-08-30 01:22:44 +08:00
Takumi Takahashi
ae74f1efe5 fix typo 2019-08-29 18:02:24 +09:00
Star Brilliant
d02c31d3ee Merge pull request #50 from felixonmars/patch-1
Update address for google's resolver
2019-06-27 17:45:23 +08:00
Felix Yan
edc86f32e5 Update address for google's resolver
The new ietf endpoint is the only one in the documentation now:
https://developers.google.com/speed/public-dns/docs/doh/

Their blog post prefers the new address too:
https://security.googleblog.com/2019/06/google-public-dns-over-https-doh.html
2019-06-27 14:57:37 +08:00
Star Brilliant
1c321be49c Release 2.1.1 2019-06-24 10:19:03 +08:00
Star Brilliant
852d0f6767 Fix a typo 2019-06-14 17:47:10 +08:00
Star Brilliant
a2d65bc89a Include DNS.SB's resolver in example configuration 2019-05-27 15:17:03 +08:00
Star Brilliant
6d8efe0939 Merge pull request #47 from rwv/master
slightly optimize the order of instructions in Dockerfile
2019-05-20 13:02:13 +08:00
seedgou
7e35e18164 optimize the order of instructions in Dockerfile 2019-05-20 11:17:40 +08:00
Star Brilliant
f40a7160b8 Merge pull request #46 from jangrewe/master
Add Dockerfiles
2019-05-17 22:50:33 +08:00
Jan Grewe
c8c22873bb Build separate Docker image for doh-server and doh-client
Make doh-client also listen on both IPv4 and IPv6
2019-05-16 20:47:40 +02:00
Star Brilliant
cb64f6694b Update the sample configuration to teach users how to listen on both IPv4 and IPv6 2019-05-17 02:37:52 +08:00
Jan Grewe
5c27ae02c0 Update Dockerfile to make doh-server listen on IPv4 and IPv6 2019-05-16 20:31:03 +02:00
Jan Grewe
f5ba377d2a Add Dockerfile 2019-05-16 00:28:46 +02:00
Star Brilliant
1ec9548ff1 Release 2.1.0 2019-05-14 01:39:46 +08:00
Star Brilliant
81f1cfba5d Disable static linking to Swift standard libraries
According to Apple: Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by default starting with macOS Mojave 10.14.4. For macOS Mojave 10.14.3 and earlier, there's an optional Swift library package that can be downloaded from "More Downloads" for Apple Developers at https://developer.apple.com/download/more/
2019-05-14 01:36:10 +08:00
Star Brilliant
ebba9c8ef5 Explain why ECS is disabled by some servers 2019-05-14 01:13:06 +08:00
Star Brilliant
6a2f2cea22 Merge pull request #44 from modib/quad9-dns-config
Added Quad9 servers in config.
2019-05-14 01:01:07 +08:00
B. Modi
63f07d20af Updated Quad9 config with ECS, DNSSEC info. 2019-05-13 09:55:20 -07:00
Star Brilliant
f0dec57e1a Merge pull request #45 from wsquasher/master
Use TCP when appropriate for the given query type/response
2019-05-13 11:40:26 +08:00
Wesley Squasher
f6b52a653a Use TCP when appropriate for the given query type/response 2019-05-12 08:17:52 +00:00
B. Modi
9a07f5b856 Added Quad9 servers in config. Good for malware threat prevention. 2019-05-10 13:50:17 -07:00
Star Brilliant
8787921faf Merge pull request #43 from modib/macos-build-error-fix
Make Makefile compatible with swift5 and older swift versions
2019-05-11 02:13:12 +08:00
B. Modi
1642730af0 Make Makefile compatible with swift5 and older swift versions 2019-05-10 11:02:07 -07:00
Ming Aldrich-Gan
2332d9b7c1 Add local_addr configuration for doh-server (#39)
* Add local_addr configuration for doh-server

This commit adds a `local_addr` string value to `doh-server.conf`, specifying the IP address and port from which outgoing calls to upstream DNS resolvers should originate. This value is set as the `udpClient`'s and `tcpClient`'s `Dialer.LocalAddr` when initializing a `NewServer`. If the value is left empty in `doh-server.conf`, it defaults to the first `listen` address (which in turn defaults to `"127.0.0.1:8053"`).

One use case for this would be if `doh-server` is proxying requests to a local DNS resolver (e.g. `unbound` or Pi-hole). Up to version 2.0.0, all DNS queries from `doh-server` are sent from `127.0.0.1` (even if the `listen` address is set to a different loopback IP address), making it hard to distinguish them from all other local DNS queries from the same machine in the query logs.

* Revert defaulting of local_addr to listen address

This commit reverts to the existing behavior when `conf.LocalAddr == ""`, i.e. letting `dns.Client` instantiate its own `Dialer` with the default local address.

* Fixup comment in configuration file

* Log errors from Dialer instantiation (e.g. if LocalAddr port is missing)

* Fixup other comment in configuration file

* Return error and log fatal
2019-03-25 04:01:32 +08:00
Star Brilliant
7f5a23584f Release 2.0.1 2019-03-24 19:11:12 +08:00
Sherlock Holo
17e5848178 Fix random selector (#41)
* Fix a bug: when only have one upstream, random selector will panic

Signed-off-by: Sherlock Holo <sherlockya@gmail.com>
2019-03-24 09:13:14 +08:00
39 changed files with 1596 additions and 312 deletions

37
.github/workflows/go.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Go build for Linux
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: ubuntu-18.04
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18
id: go
- name: Check out repository
uses: actions/checkout@v2
- name: Linux build
run: |
make
- name: Upload Linux build
uses: actions/upload-artifact@v2
with:
name: linux-amd64
path: |
doh-client/doh-client
doh-server/doh-server
- name: Cache
uses: actions/cache@v2
with:
# A directory to store and save the cache
path: ~/go/pkg/mod
# An explicit key for restoring and saving the cache
key: ${{ runner.os }}-${{ hashFiles('**/go.sum') }}

4
.gitignore vendored
View File

@@ -3,6 +3,9 @@
*.dll
*.so
*.dylib
darwin-wrapper/doh-logger
doh-client/doh-client
doh-server/doh-server
# Test binary, build with `go test -c`
*.test
@@ -14,3 +17,4 @@
.glide/
.idea/
vendor/

View File

@@ -4,6 +4,75 @@ This Changelog records major changes between versions.
Not all changes are recorded. Please check git log for details.
## Version 2.3.1
- No new features in this release
- Bumped versions of Go toolchain and third-party dependencies, requested by #128
## Version 2.3.0
- The repository now conforms to the Go semvar standard (Fixed #115, thanks to @leiless)
## Version 2.2.5
- Add client certificate authentication
- Fixing documentation related to Docker
## Version 2.2.4
- Add options to configure ECS netmask length
- Add an option to disable TLS verification (Note: dangerous)
## Version 2.2.3
- Use the library ipTree to determine whether an IP is global routable, improving the performance
- Google's 8.8.8.8 resolver is now marked as "Good ECS" in the example configuration file
## Version 2.2.2
- Allow client to opt-out EDNS0 Client Support
- [JSON-DoH] Honor DNSSEC OK flag for incoming DNS requests
- [JSON-DoH] Add support for non-standard response formats
- `X-Real-IP` is now used in logging if set by frontend load balancer
- Fix documentation
## Version 2.2.1
- Fix messy log
## Version 2.2.0
- Breaking change: The configuration format of doh-server is changed
- Add support for type prefix for upstream addresses of doh-server
- Add support for DNS-over-TLS upstream addresses of doh-server
- Remove `tcp_only` configuration option in doh-server
- Add `no_user_agent` configuration option in doh-server
- Add an RPM package script with SELinux policy
- Fix Opcode never assigned in `jsonDNS.PrepareReply`
- Improve error logging / checking
- Updated Readme
## Version 2.1.2
- Update address for google's resolver
- Fix a typo
## Version 2.1.1
- Add a set of Dockerfile contributed by the community
- Include DNS.SB's resolver in example configuration
## Version 2.1.0
- Add `local_addr` configuration for doh-server (#39)
- Fix a problem when compiling on macOS 10.14.4 or newer
- Add Quad9 DoH server to the example `doh-client.conf`
- Use TCP when appropriate for the given query type/response (AXFR/IXFR)
## Version 2.0.1
- Fix a crash with the random load balancing algorithm.
## Version 2.0.0
**This is a breaking change!** Please update the configuration file after upgrading.

23
Dockerfile.client Normal file
View File

@@ -0,0 +1,23 @@
FROM golang:alpine AS build-env
RUN apk add --no-cache git make
WORKDIR /src
ADD . /src
RUN make doh-client/doh-client
FROM alpine:latest
COPY --from=build-env /src/doh-client/doh-client /doh-client
ADD doh-client/doh-client.conf /doh-client.conf
RUN sed -i '$!N;s/"127.0.0.1:53",.*"127.0.0.1:5380",/":53",/;P;D' /doh-client.conf
RUN sed -i '$!N;s/"\[::1\]:53",.*"\[::1\]:5380",/":5380",/;P;D' /doh-client.conf
EXPOSE 53/udp
EXPOSE 53/tcp
EXPOSE 5380
ENTRYPOINT ["/doh-client"]
CMD ["-conf", "/doh-client.conf"]

20
Dockerfile.server Normal file
View File

@@ -0,0 +1,20 @@
FROM golang:alpine AS build-env
RUN apk add --no-cache git make
WORKDIR /src
ADD . /src
RUN make doh-server/doh-server
FROM alpine:latest
COPY --from=build-env /src/doh-server/doh-server /doh-server
ADD doh-server/doh-server.conf /doh-server.conf
RUN sed -i '$!N;s/"127.0.0.1:8053",\s*"\[::1\]:8053",/":8053",/;P;D' /doh-server.conf
EXPOSE 8053
ENTRYPOINT ["/doh-server"]
CMD ["-conf", "/doh-server.conf"]

View File

@@ -1,15 +1,11 @@
.PHONY: all clean install uninstall deps
.PHONY: all clean install uninstall
PREFIX = /usr/local
ifeq ($(GOROOT),)
GOBUILD = go build
GOGET = go get -d -v
GOGET_UPDATE = go get -d -u -v
else
GOBUILD = $(GOROOT)/bin/go build
GOGET = $(GOROOT)/bin/go get -d -v
GOGET_UPDATE = $(GOROOT)/bin/go get -d -u -v
endif
ifeq ($(shell uname),Darwin)
@@ -57,13 +53,8 @@ uninstall:
$(MAKE) -C launchd uninstall "DESTDIR=$(DESTDIR)"; \
fi
deps:
@# I am not sure if it is the correct way to keep the common library updated
$(GOGET_UPDATE) github.com/m13253/dns-over-https/json-dns
$(GOGET) ./doh-client ./doh-server
doh-client/doh-client: deps doh-client/client.go doh-client/config/config.go doh-client/google.go doh-client/ietf.go doh-client/main.go doh-client/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
doh-client/doh-client: doh-client/client.go doh-client/config/config.go doh-client/google.go doh-client/ietf.go doh-client/main.go doh-client/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
cd doh-client && $(GOBUILD)
doh-server/doh-server: deps doh-server/config.go doh-server/google.go doh-server/ietf.go doh-server/main.go doh-server/server.go doh-server/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
doh-server/doh-server: doh-server/config.go doh-server/google.go doh-server/ietf.go doh-server/main.go doh-server/server.go doh-server/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
cd doh-server && $(GOBUILD)

280
Readme.md
View File

@@ -4,57 +4,72 @@ DNS-over-HTTPS
Client and server software to query DNS over HTTPS, using [Google DNS-over-HTTPS protocol](https://developers.google.com/speed/public-dns/docs/dns-over-https)
and [IETF DNS-over-HTTPS (RFC 8484)](https://www.rfc-editor.org/rfc/rfc8484.txt).
## Guide
## Guides
[Tutorial to setup your own DNS-over-HTTPS (DoH) server](https://www.aaflalo.me/2018/10/tutorial-setup-dns-over-https-server/). (Thanks to Antoine Aflalo)
- [Tutorial: Setup your own DNS-over-HTTPS (DoH) server](https://www.aaflalo.me/2018/10/tutorial-setup-dns-over-https-server/). (Thanks to Antoine Aflalo)
- [Tutorial: Setup your own Docker based DNS-over-HTTPS (DoH) server](https://github.com/satishweb/docker-doh/blob/master/README.md). (Thanks to Satish Gaikwad)
## Installing
### From Source
- Install [Go](https://golang.org), at least version 1.13. The newer the better.
> Note for Debian/Ubuntu users: You need to set `$GOROOT` if you could not get your new version of Go selected by the Makefile.)
Install [Go](https://golang.org), at least version 1.10.
(Note for Debian/Ubuntu users: You need to set `$GOROOT` if you could not get your new version of Go selected by the Makefile.)
First create an empty directory, used for `$GOPATH`:
mkdir ~/gopath
export GOPATH=~/gopath
To build the program, type:
make
To install DNS-over-HTTPS as Systemd services, type:
sudo make install
By default, [Google DNS over HTTPS](https://dns.google.com) is used. It should
- First create an empty directory, used for `$GOPATH`:
```bash
mkdir ~/gopath
export GOPATH=~/gopath
```
- To build the program, type:
```bash
make
```
- To install DNS-over-HTTPS as Systemd services, type:
```bash
sudo make install
```
- By default, [Google DNS over HTTPS](https://dns.google.com) is used. It should
work for most users (except for People's Republic of China). If you need to
modify the default settings, type:
```bash
sudoedit /etc/dns-over-https/doh-client.conf
```
- To automatically start DNS-over-HTTPS client as a system service, type:
```bash
sudo systemctl start doh-client.service
sudo systemctl enable doh-client.service
```
- Then, modify your DNS settings (usually with NetworkManager) to 127.0.0.1.
sudoedit /etc/dns-over-https/doh-client.conf
- To test your configuration, type:
```bash
dig www.google.com
Output:
;; SERVER: 127.0.0.1#53(127.0.0.1)
```
#### Uninstall
To automatically start DNS-over-HTTPS client as a system service, type:
- To uninstall, type:
```bash
sudo make uninstall
```
> Note: The configuration files are kept at `/etc/dns-over-https`. Remove them manually if you want.
sudo systemctl start doh-client.service
sudo systemctl enable doh-client.service
### Using docker image
```bash
docker run -d --name doh-server \
-p 8053:8053 \
-e UPSTREAM_DNS_SERVER="udp:8.8.8.8:53" \
-e DOH_HTTP_PREFIX="/dns-query" \
-e DOH_SERVER_LISTEN=":8053" \
-e DOH_SERVER_TIMEOUT="10" \
-e DOH_SERVER_TRIES="3" \
-e DOH_SERVER_VERBOSE="false" \
satishweb/doh-server
```
Then, modify your DNS settings (usually with NetworkManager) to 127.0.0.1.
## Logging
To test your configuration, type:
dig www.google.com
If it is OK, you will wee:
;; SERVER: 127.0.0.1#53(127.0.0.1)
### Uninstalling
To uninstall, type:
sudo make uninstall
The configuration files are kept at `/etc/dns-over-https`. Remove them manually if you want.
All log lines (by either doh-client or doh-server) are written into `stderr`; you can view them using your OS tool of choice (`journalctl` when using systemd).
## Server Configuration
@@ -79,12 +94,191 @@ you can host DNS-over-HTTPS along with other HTTPS services.
HTTP/2 with at least TLS v1.3 is recommended. OCSP stapling must be enabled,
otherwise DNS recursion may happen.
### Configuration file
The main configuration file is `doh-client.conf`.
**Server selectors.** If several upstream servers are set, one is selected according to `upstream_selector` for each request. With `upstream_selector = "random"`, a random upstream server will be chosen for each request.
```toml
# available selector: random (default) or weighted_round_robin or lvs_weighted_round_robin
upstream_selector = "random"
```
### Example configuration: Apache
```bash
SSLProtocol TLSv1.2
SSLHonorCipherOrder On
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+3DES:!aNULL:!MD5:!DSS:!eNULL:!EXP:!LOW:!MD5
SSLUseStapling on
SSLStaplingCache shmcb:/var/lib/apache2/stapling_cache(512000)
<VirtualHost *:443>
ServerName MY_SERVER_NAME
Protocols h2 http/1.1
ProxyPass /dns-query http://[::1]:8053/dns-query
ProxyPassReverse /dns-query http://[::1]:8053/dns-query
</VirtualHost>
```
(Credit: [Joan Moreau](https://github.com/m13253/dns-over-https/issues/51#issuecomment-526820884))
### Example configuration: Nginx
```bash
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name MY_SERVER_NAME;
server_tokens off;
ssl_protocols TLSv1.2 TLSv1.3; # TLS 1.3 requires nginx >= 1.13.0
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; # openssl dhparam -dsaparam -out /etc/nginx/dhparam.pem 4096
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
ssl_early_data off; # 0-RTT, enable if desired - Requires nginx >= 1.15.4
resolver 1.1.1.1 valid=300s; # Replace with your local resolver
resolver_timeout 5s;
# HTTP Security Headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=63072000";
ssl_certificate /path/to/your/server/certificates/fullchain.pem;
ssl_certificate_key /path/to/your/server/certificates/privkey.pem;
location /dns-query {
proxy_pass http://localhost:8053/dns-query;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
(Credit: [Cipherli.st](https://cipherli.st/))
### Example configuration: Caddy (v2)
```bash
my.server.name {
reverse_proxy * localhost:8053
tls your@email.address
try_files {path} {path}/index.php /index.php?{query}
}
```
### Example configuration: Docker Compose + Traefik + Unbound (Raspberry Pi/Linux/Mac) [linux/amd64,linux/arm64,linux/arm/v7]
```yaml
version: '2.2'
networks:
default:
services:
proxy:
# The official v2 Traefik docker image
image: traefik:v2.3
hostname: proxy
networks:
- default
environment:
TRAEFIK_ACCESSLOG: "true"
TRAEFIK_API: "true"
TRAEFIK_PROVIDERS_DOCKER: "true"
TRAEFIK_API_INSECURE: "true"
TRAEFIK_PROVIDERS_DOCKER_NETWORK: "${STACK}_default"
# DNS provider specific environment variables for DNS Challenge using route53 (AWS)
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
AWS_HOSTED_ZONE_ID: ${AWS_HOSTED_ZONE_ID}
ports:
# The HTTP port
- "80:80"
# The HTTPS port
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
command:
#- "--log.level=DEBUG"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
# Providers list:
# https://docs.traefik.io/https/acme/#providers
# https://go-acme.github.io/lego/dns/
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=route53"
# Enable below line to use staging letsencrypt server.
#- "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.letsencrypt.acme.email=${EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/certs/acme.json"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./data/proxy/certs:/certs
doh-server:
image: satishweb/doh-server:latest
hostname: doh-server
networks:
- default
environment:
# Enable below line to see more logs
# DEBUG: "1"
UPSTREAM_DNS_SERVER: "udp:unbound:53"
DOH_HTTP_PREFIX: "${DOH_HTTP_PREFIX}"
DOH_SERVER_LISTEN: ":${DOH_SERVER_LISTEN}"
DOH_SERVER_TIMEOUT: "10"
DOH_SERVER_TRIES: "3"
DOH_SERVER_VERBOSE: "false"
#volumes:
# - ./doh-server.conf:/server/doh-server.conf
# - ./app-config:/app-config
depends_on:
- unbound
labels:
- "traefik.enable=true"
- "traefik.http.routers.doh-server.rule=Host(`${SUBDOMAIN}.${DOMAIN}`) && Path(`${DOH_HTTP_PREFIX}`)"
- "traefik.http.services.doh-server.loadbalancer.server.port=${DOH_SERVER_LISTEN}"
- "traefik.http.middlewares.mw-doh-compression.compress=true"
- "traefik.http.routers.doh-server.tls=true"
- "traefik.http.middlewares.mw-doh-tls.headers.sslredirect=true"
- "traefik.http.middlewares.mw-doh-tls.headers.sslforcehost=true"
- "traefik.http.routers.doh-server.tls.certresolver=letsencrypt"
- "traefik.http.routers.doh-server.tls.domains[0].main=${DOMAIN}"
- "traefik.http.routers.doh-server.tls.domains[0].sans=${SUBDOMAIN}.${DOMAIN}"
# Protection from requests flood
- "traefik.http.middlewares.mw-doh-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.mw-doh-ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.mw-doh-ratelimit.ratelimit.period=10s"
unbound:
image: satishweb/unbound:latest
hostname: unbound
networks:
- default
ports:
# Disable these ports if DOH server is the only client
- 53:53/tcp
- 53:53/udp
volumes:
- ./unbound.sample.conf:/templates/unbound.sample.conf
- ./data/unbound/custom:/etc/unbound/custom
# Keep your custom.hosts file inside custom folder
#environment:
# DEBUG: "1"
````
> Complete Guide available at: https://github.com/satishweb/docker-doh
> IPV6 Support for Docker Compose based configuration TBA
## DNSSEC
DNS-over-HTTPS is compatible with DNSSEC, and requests DNSSEC signatures by
default. However signature validation is not built-in. It is highly recommended
that you install `unbound` or `bind` and pass results for them to validate DNS
records.
records. An instance of [Pi Hole](https://pi-hole.net) could also be used to validate DNS signatures as well as provide other capabilities.
## EDNS0-Client-Subnet (GeoDNS)
@@ -125,6 +319,10 @@ Currently supported features are:
- [X] EDNS0 large UDP packet (4 KiB by default)
- [X] EDNS0-Client-Subnet (/24 for IPv4, /56 for IPv6 by default)
## Known issues
* it does not work well with [dnscrypt-proxy](https://github.com/DNSCrypt/dnscrypt-proxy), you might want to use either (or fix the compatibility bugs by submitting PRs)
## The name of the project
This project is named "DNS-over-HTTPS" because it was written before the IETF DoH project. Although this project is compatible with IETF DoH, the project is not affiliated with IETF.

View File

@@ -0,0 +1,36 @@
diff -Naur dns-over-https-2.1.2.org/systemd/doh-client.service dns-over-https-2.1.2/systemd/doh-client.service
--- dns-over-https-2.1.2.org/systemd/doh-client.service 2019-09-10 12:08:35.177574074 +0200
+++ dns-over-https-2.1.2/systemd/doh-client.service 2019-09-10 12:10:05.473700374 +0200
@@ -7,12 +7,12 @@
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
-ExecStart=/usr/local/bin/doh-client -conf /etc/dns-over-https/doh-client.conf
+ExecStart=/usr/bin/doh-client -conf /etc/dns-over-https/doh-client.conf
LimitNOFILE=1048576
Restart=always
RestartSec=3
Type=simple
-User=nobody
+User=doh-client
[Install]
WantedBy=multi-user.target
diff -Naur dns-over-https-2.1.2.org/systemd/doh-server.service dns-over-https-2.1.2/systemd/doh-server.service
--- dns-over-https-2.1.2.org/systemd/doh-server.service 2019-09-10 12:08:35.177574074 +0200
+++ dns-over-https-2.1.2/systemd/doh-server.service 2019-09-10 12:10:20.980273992 +0200
@@ -5,12 +5,12 @@
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
-ExecStart=/usr/local/bin/doh-server -conf /etc/dns-over-https/doh-server.conf
+ExecStart=/usr/bin/doh-server -conf /etc/dns-over-https/doh-server.conf
LimitNOFILE=1048576
Restart=always
RestartSec=3
Type=simple
-User=nobody
+User=doh-server
[Install]
WantedBy=multi-user.target

240
contrib/rpm/doh.spec Normal file
View File

@@ -0,0 +1,240 @@
# vim: tabstop=4 shiftwidth=4 expandtab
%global _hardened_build 1
# Debug package is empty anyway
%define debug_package %{nil}
%global _release 1
%global provider github
%global provider_tld com
%global project m13253
%global repo dns-over-https
%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo}
%global import_path %{provider_prefix}
#define commit 984df34ca7b45897ecb5871791e398cc160a4b93
%if 0%{?commit:1}
%define shortcommit %(c=%{commit}; echo ${c:0:7})
%define _date %(date +'%%Y%%m%%dT%%H%%M%%S')
%endif
%define rand_id %(head -c20 /dev/urandom|od -An -tx1|tr -d '[[:space:]]')
%if ! 0%{?gobuild:1}
%define gobuild(o:) go build -ldflags "${LDFLAGS:-} -B 0x%{rand_id}" -a -v -x %{?**};
%endif
%if ! 0%{?gotest:1}
%define gotest() go test -ldflags "${LDFLAGS:-}" %{?**}
%endif
Name: %{repo}
Version: 2.1.2
%if 0%{?commit:1}
Release: %{_release}.git%{shortcommit}.%{_date}%{?dist}
Source0: https://%{import_path}/archive/%{commit}.tar.gz
%else
Release: %{_release}%{?dist}
Source0: https://%{import_path}/archive/v%{version}.tar.gz
%endif
Patch0: %{name}-%{version}-systemd.patch
Summary: High performance DNS over HTTPS client & server
License: MIT
URL: https://github.com/m13253/dns-over-https
# e.g. el6 has ppc64 arch without gcc-go, so EA tag is required
# If go_compiler is not set to 1, there is no virtual provide. Use golang instead.
#BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} >= 1.10
BuildRequires: golang >= 1.10
BuildRequires: systemd
BuildRequires: upx
%description
%{summary}
%package common
BuildArch: noarch
Summary: %{summary} - common files
%description common
%{summary}
%package server
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
Summary: %{summary} - Server
Requires(pre): shadow-utils
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
%description server
%{summary}
%package client
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
Summary: %{summary} - Client
Requires(pre): shadow-utils
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
%description client
%{summary}
%package selinux
BuildArch: noarch
Source3: doh_server.fc
Source4: doh_server.if
Source5: doh_server.te
Source6: doh_client.fc
Source7: doh_client.if
Source8: doh_client.te
BuildRequires: selinux-policy
BuildRequires: selinux-policy-devel
Requires: %{name}
Requires(post): policycoreutils
Requires(post): policycoreutils-python
Requires(postun): policycoreutils
Summary: SELinux policy for %{name}
%description selinux
%summary
%prep
%if 0%{?commit:1}
%autosetup -n %{name}-%{commit} -p1
%else
%autosetup -n %{name}-%{version} -p1
%endif
mkdir -p selinux
cp %{SOURCE3} %{SOURCE4} %{SOURCE5} %{SOURCE6} %{SOURCE7} %{SOURCE8} selinux
%build
cd selinux
make -f /usr/share/selinux/devel/Makefile doh_server.pp doh_client.pp || exit
cd -
%set_build_flags
%make_build \
PREFIX=%{_prefix} \
GOBUILD="go build -ldflags \"-s -w -B 0x%{rand_id}\" -a -v -x"
%install
%make_install \
PREFIX=%{_prefix}
install -Dpm 0600 selinux/doh_server.pp %{buildroot}%{_datadir}/selinux/packages/doh_server.pp
install -Dpm 0644 selinux/doh_server.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/doh_server.if
install -Dpm 0600 selinux/doh_client.pp %{buildroot}%{_datadir}/selinux/packages/doh_client.pp
install -Dpm 0644 selinux/doh_client.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/doh_client.if
mkdir -p %{buildroot}%{_docdir}/%{name}
mv %{buildroot}%{_sysconfdir}/%{name}/*.example %{buildroot}%{_docdir}/%{name}
mkdir -p %{buildroot}%{_libdir}
mv %{buildroot}%{_sysconfdir}/NetworkManager %{buildroot}%{_libdir}/
for i in $(find %{_buildroot}%{_bindir} -type f)
do
upx $i
done
%files common
%license LICENSE
%doc Changelog.md Readme.md
%files server
%{_libdir}/NetworkManager/dispatcher.d/doh-server
%{_docdir}/%{name}/doh-server.conf.example
%config(noreplace) %{_sysconfdir}/%{name}/doh-server.conf
%{_bindir}/doh-server
%{_unitdir}/doh-server.service
%files client
%{_libdir}/NetworkManager/dispatcher.d/doh-client
%{_docdir}/%{name}/doh-client.conf.example
%config(noreplace) %{_sysconfdir}/%{name}/doh-client.conf
%{_bindir}/doh-client
%{_unitdir}/doh-client.service
%pre server
test -d %{_sharedstatedir}/home || mkdir -p %{_sharedstatedir}/home
getent group doh-server > /dev/null || groupadd -r doh-server
getent passwd doh-server > /dev/null || \
useradd -r -d %{_sharedstatedir}/home/doh-server -g doh-server \
-s /sbin/nologin -c "%{name} - server" doh-server
exit 0
%pre client
test -d %{_sharedstatedir}/home || mkdir -p %{_sharedstatedir}/home
getent group doh-client > /dev/null || groupadd -r doh-client
getent passwd doh-client > /dev/null || \
useradd -r -d %{_sharedstatedir}/home/doh-client -g doh-client \
-s /sbin/nologin -c "%{name} - client" doh-client
exit 0
%post server
%systemd_post doh-server.service
%preun server
%systemd_preun doh-server.service
%postun server
%systemd_postun_with_restart doh-server.service
%post client
%systemd_post doh-client.service
%preun client
%systemd_preun doh-client.service
%postun client
%systemd_postun_with_restart doh-client.service
%files selinux
%{_datadir}/selinux/packages/doh_server.pp
%{_datadir}/selinux/devel/include/contrib/doh_server.if
%{_datadir}/selinux/packages/doh_client.pp
%{_datadir}/selinux/devel/include/contrib/doh_client.if
%post selinux
semodule -n -i %{_datadir}/selinux/packages/doh_server.pp
semodule -n -i %{_datadir}/selinux/packages/doh_client.pp
if /usr/sbin/selinuxenabled ; then
/usr/sbin/load_policy
/usr/sbin/fixfiles -R %{name}-server restore
/usr/sbin/fixfiles -R %{name}-client restore
fi;
semanage -i - << __eof
port -a -t doh_server_port_t -p tcp "8053"
port -a -t doh_client_port_t -p udp "5380"
__eof
exit 0
%postun selinux
if [ $1 -eq 0 ]; then
semanage -i - << __eof
port -d -t doh_server_port_t -p tcp "8053"
port -d -t doh_client_port_t -p udp "5380"
__eof
semodule -n -r doh_server
semodule -n -r doh_client
if /usr/sbin/selinuxenabled ; then
/usr/sbin/load_policy
/usr/sbin/fixfiles -R %{name}-server restore
/usr/sbin/fixfiles -R %{name}-client restore
fi;
fi;
exit 0
%changelog
* Tue Sep 10 2019 fuero <fuerob@gmail.com> 2.1.2-1
- initial package

View File

@@ -0,0 +1,2 @@
/usr/bin/doh-client -- gen_context(system_u:object_r:doh_client_exec_t,s0)
/usr/lib/systemd/system/doh-client.service -- gen_context(system_u:object_r:doh_client_unit_file_t,s0)

103
contrib/rpm/doh_client.if Normal file
View File

@@ -0,0 +1,103 @@
## <summary>policy for doh_client</summary>
########################################
## <summary>
## Execute doh_client_exec_t in the doh_client domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed to transition.
## </summary>
## </param>
#
interface(`doh_client_domtrans',`
gen_require(`
type doh_client_t, doh_client_exec_t;
')
corecmd_search_bin($1)
domtrans_pattern($1, doh_client_exec_t, doh_client_t)
')
######################################
## <summary>
## Execute doh_client in the caller domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
#
interface(`doh_client_exec',`
gen_require(`
type doh_client_exec_t;
')
corecmd_search_bin($1)
can_exec($1, doh_client_exec_t)
')
########################################
## <summary>
## Execute doh_client server in the doh_client domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed to transition.
## </summary>
## </param>
#
interface(`doh_client_systemctl',`
gen_require(`
type doh_client_t;
type doh_client_unit_file_t;
')
systemd_exec_systemctl($1)
systemd_read_fifo_file_passwd_run($1)
allow $1 doh_client_unit_file_t:file read_file_perms;
allow $1 doh_client_unit_file_t:service manage_service_perms;
ps_process_pattern($1, doh_client_t)
')
########################################
## <summary>
## All of the rules required to administrate
## an doh_client environment
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
## <param name="role">
## <summary>
## Role allowed access.
## </summary>
## </param>
## <rolecap/>
#
interface(`doh_client_admin',`
gen_require(`
type doh_client_t;
type doh_client_unit_file_t;
')
allow $1 doh_client_t:process { signal_perms };
ps_process_pattern($1, doh_client_t)
tunable_policy(`deny_ptrace',`',`
allow $1 doh_client_t:process ptrace;
')
doh_client_systemctl($1)
admin_pattern($1, doh_client_unit_file_t)
allow $1 doh_client_unit_file_t:service all_service_perms;
optional_policy(`
systemd_passwd_agent_exec($1)
systemd_read_fifo_file_passwd_run($1)
')
')

49
contrib/rpm/doh_client.te Normal file
View File

@@ -0,0 +1,49 @@
policy_module(doh_client, 1.0.0)
########################################
#
# Declarations
#
type doh_client_t;
type doh_client_exec_t;
init_daemon_domain(doh_client_t, doh_client_exec_t)
type doh_client_port_t;
corenet_port(doh_client_port_t)
type doh_client_unit_file_t;
systemd_unit_file(doh_client_unit_file_t)
########################################
#
# doh_client local policy
#
allow doh_client_t self:fifo_file rw_fifo_file_perms;
allow doh_client_t self:unix_stream_socket create_stream_socket_perms;
allow doh_client_t self:capability net_bind_service;
allow doh_client_t self:process execmem;
allow doh_client_t self:tcp_socket { accept bind connect create getattr getopt listen read setopt write };
allow doh_client_t self:udp_socket { bind connect create getattr read setopt write };
allow doh_client_t doh_client_exec_t:file execmod;
allow doh_client_t doh_client_port_t:tcp_socket name_bind;
corenet_tcp_bind_dns_port(doh_client_t)
corenet_tcp_bind_generic_node(doh_client_t)
corenet_tcp_connect_http_port(doh_client_t)
corenet_udp_bind_dns_port(doh_client_t)
corenet_udp_bind_generic_node(doh_client_t)
corenet_udp_bind_generic_port(doh_client_t)
kernel_read_net_sysctls(doh_client_t)
kernel_search_network_sysctl(doh_client_t)
miscfiles_read_certs(doh_client_t)
sysnet_read_config(doh_client_t)
domain_use_interactive_fds(doh_client_t)
files_read_etc_files(doh_client_t)
miscfiles_read_localization(doh_client_t)

View File

@@ -0,0 +1,2 @@
/usr/bin/doh-server -- gen_context(system_u:object_r:doh_server_exec_t,s0)
/usr/lib/systemd/system/doh-server.service -- gen_context(system_u:object_r:doh_server_unit_file_t,s0)

122
contrib/rpm/doh_server.if Normal file
View File

@@ -0,0 +1,122 @@
## <summary>policy for doh_server</summary>
########################################
## <summary>
## Execute doh_server_exec_t in the doh_server domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed to transition.
## </summary>
## </param>
#
interface(`doh_server_domtrans',`
gen_require(`
type doh_server_t, doh_server_exec_t;
')
corecmd_search_bin($1)
domtrans_pattern($1, doh_server_exec_t, doh_server_t)
')
######################################
## <summary>
## Execute doh_server in the caller domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
#
interface(`doh_server_exec',`
gen_require(`
type doh_server_exec_t;
')
corecmd_search_bin($1)
can_exec($1, doh_server_exec_t)
')
########################################
## <summary>
## Execute doh_server server in the doh_server domain.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed to transition.
## </summary>
## </param>
#
interface(`doh_server_systemctl',`
gen_require(`
type doh_server_t;
type doh_server_unit_file_t;
')
systemd_exec_systemctl($1)
systemd_read_fifo_file_passwd_run($1)
allow $1 doh_server_unit_file_t:file read_file_perms;
allow $1 doh_server_unit_file_t:service manage_service_perms;
ps_process_pattern($1, doh_server_t)
')
########################################
## <summary>
## All of the rules required to administrate
## an doh_server environment
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
## <param name="role">
## <summary>
## Role allowed access.
## </summary>
## </param>
## <rolecap/>
#
interface(`doh_server_admin',`
gen_require(`
type doh_server_t;
type doh_server_unit_file_t;
')
allow $1 doh_server_t:process { signal_perms };
ps_process_pattern($1, doh_server_t)
tunable_policy(`deny_ptrace',`',`
allow $1 doh_server_t:process ptrace;
')
doh_server_systemctl($1)
admin_pattern($1, doh_server_unit_file_t)
allow $1 doh_server_unit_file_t:service all_service_perms;
optional_policy(`
systemd_passwd_agent_exec($1)
systemd_read_fifo_file_passwd_run($1)
')
')
########################################
## <summary>
## Make a TCP connection to the vault_ocsp_responder port.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
#
interface(`doh_server_connect',`
gen_require(`
type doh_server_port_t;
type $1;
')
allow $1 doh_server_port_t:tcp_socket name_connect;
')

42
contrib/rpm/doh_server.te Normal file
View File

@@ -0,0 +1,42 @@
policy_module(doh_server, 1.0.0)
require {
class process execmem;
class tcp_socket { accept bind create read write getattr listen setopt connect getopt };
class udp_socket { connect create getattr setopt read write };
class file execmod;
}
type doh_server_t;
type doh_server_exec_t;
init_daemon_domain(doh_server_t, doh_server_exec_t)
type doh_server_port_t;
corenet_port(doh_server_port_t)
type doh_server_unit_file_t;
systemd_unit_file(doh_server_unit_file_t)
allow doh_server_t self:fifo_file rw_fifo_file_perms;
allow doh_server_t self:unix_stream_socket create_stream_socket_perms;
allow doh_server_t self:process execmem;
allow doh_server_t self:tcp_socket { accept read write bind create getattr listen setopt connect getopt};
allow doh_server_t self:udp_socket { connect create getattr setopt read write };
allow doh_server_t doh_server_exec_t:file execmod;
allow doh_server_t doh_server_port_t:tcp_socket name_bind;
domain_use_interactive_fds(doh_server_t)
files_read_etc_files(doh_server_t)
corenet_tcp_bind_generic_node(doh_server_t)
corenet_tcp_connect_dns_port(doh_server_t)
doh_server_connect(httpd_t)
kernel_read_net_sysctls(doh_server_t)
kernel_search_network_sysctl(doh_server_t)
miscfiles_read_localization(doh_server_t)

View File

@@ -6,7 +6,7 @@ PREFIX = /usr/local
all: doh-logger
doh-logger: doh-logger.swift
$(SWIFTC) -o $@ -O -static-stdlib $<
$(SWIFTC) -o $@ -O $<
clean:
rm -f doh-logger

View File

@@ -25,6 +25,7 @@ package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"math/rand"
@@ -37,9 +38,9 @@ import (
"sync"
"time"
"github.com/m13253/dns-over-https/doh-client/config"
"github.com/m13253/dns-over-https/doh-client/selector"
"github.com/m13253/dns-over-https/json-dns"
"github.com/m13253/dns-over-https/v2/doh-client/config"
"github.com/m13253/dns-over-https/v2/doh-client/selector"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
"golang.org/x/net/http2"
"golang.org/x/net/idna"
@@ -155,7 +156,7 @@ func NewClient(conf *config.Config) (c *Client, err error) {
switch c.conf.Upstream.UpstreamSelector {
case config.NginxWRR:
if c.conf.Other.Verbose {
log.Println(config.NginxWRR, " mode start")
log.Println(config.NginxWRR, "mode start")
}
s := selector.NewNginxWRRSelector(time.Duration(c.conf.Other.Timeout) * time.Second)
@@ -175,7 +176,7 @@ func NewClient(conf *config.Config) (c *Client, err error) {
case config.LVSWRR:
if c.conf.Other.Verbose {
log.Println(config.LVSWRR, " mode start")
log.Println(config.LVSWRR, "mode start")
}
s := selector.NewLVSWRRSelector(time.Duration(c.conf.Other.Timeout) * time.Second)
@@ -195,7 +196,7 @@ func NewClient(conf *config.Config) (c *Client, err error) {
default:
if c.conf.Other.Verbose {
log.Println(config.Random, " mode start")
log.Println(config.Random, "mode start")
}
// if selector is invalid or random, use random selector, or should we stop program and let user knows he is wrong?
@@ -247,6 +248,7 @@ func (c *Client) newHTTPClient() error {
MaxIdleConnsPerHost: 10,
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: time.Duration(c.conf.Other.Timeout) * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.conf.Other.TLSInsecureSkipVerify},
}
if c.conf.Other.NoIPv6 {
c.httpTransport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
@@ -305,7 +307,7 @@ func (c *Client) handlerFunc(w dns.ResponseWriter, r *dns.Msg, isTCP bool) {
if len(r.Question) != 1 {
log.Println("Number of questions is not 1")
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeFormatError
w.WriteMsg(reply)
return
@@ -356,7 +358,7 @@ func (c *Client) handlerFunc(w dns.ResponseWriter, r *dns.Msg, isTCP bool) {
return
}
log.Println(err)
reply = jsonDNS.PrepareReply(r)
reply = jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return
@@ -471,7 +473,7 @@ func (c *Client) findClientIP(w dns.ResponseWriter, r *dns.Msg) (ednsClientAddre
if err != nil {
return
}
if ip := remoteAddr.IP; jsonDNS.IsGlobalIP(ip) {
if ip := remoteAddr.IP; jsondns.IsGlobalIP(ip) {
if ipv4 := ip.To4(); ipv4 != nil {
ednsClientAddress = ipv4.Mask(ipv4Mask24)
ednsClientNetmask = 24

View File

@@ -47,14 +47,16 @@ type upstream struct {
}
type others struct {
Bootstrap []string `toml:"bootstrap"`
Passthrough []string `toml:"passthrough"`
Timeout uint `toml:"timeout"`
NoCookies bool `toml:"no_cookies"`
NoECS bool `toml:"no_ecs"`
NoIPv6 bool `toml:"no_ipv6"`
Verbose bool `toml:"verbose"`
DebugHTTPHeaders []string `toml:"debug_http_headers"`
Bootstrap []string `toml:"bootstrap"`
Passthrough []string `toml:"passthrough"`
Timeout uint `toml:"timeout"`
NoCookies bool `toml:"no_cookies"`
NoECS bool `toml:"no_ecs"`
NoIPv6 bool `toml:"no_ipv6"`
NoUserAgent bool `toml:"no_user_agent"`
Verbose bool `toml:"verbose"`
DebugHTTPHeaders []string `toml:"debug_http_headers"`
TLSInsecureSkipVerify bool `toml:"insecure_tls_skip_verify"`
}
type Config struct {

View File

@@ -4,6 +4,9 @@ listen = [
"127.0.0.1:5380",
"[::1]:53",
"[::1]:5380",
## To listen on both 0.0.0.0:53 and [::]:53, use the following line
# ":53",
]
# HTTP path for upstream resolver
@@ -15,34 +18,34 @@ upstream_selector = "random"
# weight should in (0, 100], if upstream_selector is random, weight will be ignored
## Google's productive resolver, good ECS, bad DNSSEC
#[[upstream.upstream_google]]
# url = "https://dns.google.com/resolve"
## Google's resolver, good ECS, good DNSSEC
#[[upstream.upstream_ietf]]
# url = "https://dns.google/dns-query"
# weight = 50
## CloudFlare's resolver, bad ECS, good DNSSEC
#[[upstream.upstream_google]]
# url = "https://cloudflare-dns.com/dns-query"
# weight = 50
## CloudFlare's resolver, bad ECS, good DNSSEC
#[[upstream.upstream_google]]
# url = "https://1.1.1.1/dns-query"
# weight = 50
# CloudFlare's resolver, bad ECS, good DNSSEC
## ECS is disabled for privacy by design: https://developers.cloudflare.com/1.1.1.1/nitty-gritty-details/#edns-client-subnet
[[upstream.upstream_ietf]]
url = "https://cloudflare-dns.com/dns-query"
weight = 50
## CloudFlare's resolver, bad ECS, good DNSSEC
## ECS is disabled for privacy by design: https://developers.cloudflare.com/1.1.1.1/nitty-gritty-details/#edns-client-subnet
## Note that some ISPs have problems connecting to 1.1.1.1, try 1.0.0.1 if problems happen.
#[[upstream.upstream_ietf]]
# url = "https://1.1.1.1/dns-query"
# weight = 50
## Google's experimental resolver, good ECS, good DNSSEC
## DNS.SB's resolver, good ECS, good DNSSEC
## The provider claims no logging: https://dns.sb/doh/
#[[upstream.upstream_ietf]]
# url = "https://dns.google.com/experimental"
# url = "https://doh.dns.sb/dns-query"
# weight = 50
## Quad9's resolver, bad ECS, good DNSSEC
## ECS is disabled for privacy by design: https://www.quad9.net/faq/#What_is_EDNS_Client-Subnet
#[[upstream.upstream_ietf]]
# url = "https://9.9.9.9/dns-query"
# weight = 50
## CloudFlare's resolver for Tor, available only with Tor
@@ -61,7 +64,7 @@ upstream_selector = "random"
# bootstrap server, please make this list empty.
bootstrap = [
# Google's resolver, bad ECS, good DNSSEC
# Google's resolver, good ECS, good DNSSEC
"8.8.8.8:53",
"8.8.4.4:53",
@@ -116,5 +119,22 @@ no_ecs = false
# Note that DNS listening and bootstrapping is not controlled by this option.
no_ipv6 = false
# Disable submitting User-Agent
#
# It is generally not recommended to disable submitting User-Agent because it
# is still possible to probe client version according to behavior differences,
# such as TLS handshaking, handling of malformed packets, and specific bugs.
# Additionally, User-Agent is an important way for the server to distinguish
# buggy, old, or insecure clients, and to workaround specific bugs.
# (e.g. doh-server can detect and workaround certain issues of DNSCrypt-Proxy
# and older Firefox.)
no_user_agent = false
# Enable logging
verbose = false
# insecure_tls_skip_verification will disable necessary TLS security verification.
# This option is designed for testing or development purposes,
# turning on this option on public Internet may cause your connection
# vulnerable to MITM attack.
insecure_tls_skip_verify = false

View File

@@ -34,8 +34,8 @@ import (
"strconv"
"strings"
"github.com/m13253/dns-over-https/doh-client/selector"
"github.com/m13253/dns-over-https/json-dns"
"github.com/m13253/dns-over-https/v2/doh-client/selector"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
)
@@ -44,7 +44,7 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
questionName := question.Name
questionClass := question.Qclass
if questionClass != dns.ClassINET {
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeRefused
w.WriteMsg(reply)
return &DNSRequest{
@@ -67,6 +67,9 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
udpSize := uint16(512)
if opt := r.IsEdns0(); opt != nil {
udpSize = opt.UDPSize()
if opt.Do() {
requestURL += "&do=1"
}
}
ednsClientAddress, ednsClientNetmask := c.findClientIP(w, r)
@@ -77,7 +80,7 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
req, err := http.NewRequest(http.MethodGet, requestURL, nil)
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{
@@ -86,7 +89,11 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
}
req.Header.Set("Accept", "application/json, application/dns-message, application/dns-udpwireformat")
req.Header.Set("User-Agent", USER_AGENT)
if !c.conf.Other.NoUserAgent {
req.Header.Set("User-Agent", USER_AGENT)
} else {
req.Header.Set("User-Agent", "")
}
req = req.WithContext(ctx)
c.httpClientMux.RLock()
@@ -104,7 +111,7 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{
@@ -114,7 +121,7 @@ func (c *Client) generateRequestGoogle(ctx context.Context, w dns.ResponseWriter
return &DNSRequest{
response: resp,
reply: jsonDNS.PrepareReply(r),
reply: jsondns.PrepareReply(r),
udpSize: udpSize,
ednsClientAddress: ednsClientAddress,
ednsClientNetmask: ednsClientNetmask,
@@ -141,7 +148,7 @@ func (c *Client) parseResponseGoogle(ctx context.Context, w dns.ResponseWriter,
return
}
var respJSON jsonDNS.Response
var respJSON jsondns.Response
err = json.Unmarshal(body, &respJSON)
if err != nil {
log.Println(err)
@@ -153,8 +160,9 @@ func (c *Client) parseResponseGoogle(ctx context.Context, w dns.ResponseWriter,
if respJSON.Status != dns.RcodeSuccess && respJSON.Comment != "" {
log.Printf("DNS error: %s\n", respJSON.Comment)
}
fixEmptyNames(&respJSON)
fullReply := jsonDNS.Unmarshal(req.reply, &respJSON, req.udpSize, req.ednsClientNetmask)
fullReply := jsondns.Unmarshal(req.reply, &respJSON, req.udpSize, req.ednsClientNetmask)
buf, err := fullReply.Pack()
if err != nil {
log.Println(err)
@@ -173,3 +181,19 @@ func (c *Client) parseResponseGoogle(ctx context.Context, w dns.ResponseWriter,
}
w.Write(buf)
}
// Fix DNS response empty []RR.Name
// Additional section won't be rectified
// see: https://stackoverflow.com/questions/52136176/what-is-additional-section-in-dns-and-how-it-works
func fixEmptyNames(respJSON *jsondns.Response) {
for i := range respJSON.Answer {
if respJSON.Answer[i].Name == "" {
respJSON.Answer[i].Name = "."
}
}
for i := range respJSON.Authority {
if respJSON.Authority[i].Name == "" {
respJSON.Authority[i].Name = "."
}
}
}

View File

@@ -35,8 +35,8 @@ import (
"strings"
"time"
"github.com/m13253/dns-over-https/doh-client/selector"
"github.com/m13253/dns-over-https/json-dns"
"github.com/m13253/dns-over-https/v2/doh-client/selector"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
)
@@ -90,7 +90,7 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
requestBinary, err := r.Pack()
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeFormatError
w.WriteMsg(reply)
return &DNSRequest{
@@ -107,7 +107,7 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
req, err = http.NewRequest(http.MethodGet, requestURL, nil)
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{
@@ -118,7 +118,7 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
req, err = http.NewRequest(http.MethodPost, upstream.URL, bytes.NewReader(requestBinary))
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{
@@ -128,7 +128,11 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
req.Header.Set("Content-Type", "application/dns-message")
}
req.Header.Set("Accept", "application/dns-message, application/dns-udpwireformat, application/json")
req.Header.Set("User-Agent", USER_AGENT)
if !c.conf.Other.NoUserAgent {
req.Header.Set("User-Agent", USER_AGENT)
} else {
req.Header.Set("User-Agent", "")
}
req = req.WithContext(ctx)
c.httpClientMux.RLock()
resp, err := c.httpClient.Do(req)
@@ -145,7 +149,7 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
if err != nil {
log.Println(err)
reply := jsonDNS.PrepareReply(r)
reply := jsondns.PrepareReply(r)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{
@@ -155,7 +159,7 @@ func (c *Client) generateRequestIETF(ctx context.Context, w dns.ResponseWriter,
return &DNSRequest{
response: resp,
reply: jsonDNS.PrepareReply(r),
reply: jsondns.PrepareReply(r),
udpSize: udpSize,
ednsClientAddress: ednsClientAddress,
ednsClientNetmask: ednsClientNetmask,
@@ -176,7 +180,7 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
body, err := ioutil.ReadAll(req.response.Body)
if err != nil {
log.Println(err)
log.Printf("read error from upstream %s: %v\n", req.currentUpstream, err)
req.reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(req.reply)
return
@@ -187,7 +191,7 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
if nowDate, err := time.Parse(http.TimeFormat, headerNow); err == nil {
now = nowDate
} else {
log.Println(err)
log.Printf("Date header parse error from upstream %s: %v\n", req.currentUpstream, err)
}
}
headerLastModified := req.response.Header.Get("Last-Modified")
@@ -196,7 +200,7 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
if lastModifiedDate, err := time.Parse(http.TimeFormat, headerLastModified); err == nil {
lastModified = lastModifiedDate
} else {
log.Println(err)
log.Printf("Last-Modified header parse error from upstream %s: %v\n", req.currentUpstream, err)
}
}
timeDelta := now.Sub(lastModified)
@@ -207,7 +211,7 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
fullReply := new(dns.Msg)
err = fullReply.Unpack(body)
if err != nil {
log.Println(err)
log.Printf("unpacking error from upstream %s: %v\n", req.currentUpstream, err)
req.reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(req.reply)
return
@@ -229,7 +233,7 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
buf, err := fullReply.Pack()
if err != nil {
log.Println(err)
log.Printf("packing error with upstream %s: %v\n", req.currentUpstream, err)
req.reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(req.reply)
return
@@ -238,12 +242,15 @@ func (c *Client) parseResponseIETF(ctx context.Context, w dns.ResponseWriter, r
fullReply.Truncated = true
buf, err = fullReply.Pack()
if err != nil {
log.Println(err)
log.Printf("re-packing error with upstream %s: %v\n", req.currentUpstream, err)
return
}
buf = buf[:req.udpSize]
}
w.Write(buf)
_, err = w.Write(buf)
if err != nil {
log.Printf("failed to write to client: %v\n", err)
}
}
func fixRecordTTL(rr dns.RR, delta time.Duration) dns.RR {

View File

@@ -33,7 +33,7 @@ import (
"runtime"
"strconv"
"github.com/m13253/dns-over-https/doh-client/config"
"github.com/m13253/dns-over-https/v2/doh-client/config"
)
func checkPIDFile(pidFile string) (bool, error) {
@@ -89,7 +89,7 @@ func main() {
flag.Parse()
if *showVersion {
fmt.Printf("doh-server %s\nHomepage: https://github.com/m13253/dns-over-https\n", VERSION)
fmt.Printf("doh-client %s\nHomepage: https://github.com/m13253/dns-over-https\n", VERSION)
return
}

View File

@@ -42,7 +42,7 @@ func (rs *RandomSelector) Add(url string, upstreamType UpstreamType) (err error)
}
func (rs *RandomSelector) Get() *Upstream {
return rs.upstreams[rand.Intn(len(rs.upstreams)-1)]
return rs.upstreams[rand.Intn(len(rs.upstreams))]
}
func (rs *RandomSelector) StartEvaluate() {}

View File

@@ -24,6 +24,6 @@
package main
const (
VERSION = "2.0.0"
VERSION = "2.3.2"
USER_AGENT = "DNS-over-HTTPS/" + VERSION + " (+https://github.com/m13253/dns-over-https)"
)

View File

@@ -25,22 +25,27 @@ package main
import (
"fmt"
"regexp"
"github.com/BurntSushi/toml"
)
type config struct {
Listen []string `toml:"listen"`
Cert string `toml:"cert"`
Key string `toml:"key"`
Path string `toml:"path"`
Upstream []string `toml:"upstream"`
Timeout uint `toml:"timeout"`
Tries uint `toml:"tries"`
TCPOnly bool `toml:"tcp_only"`
Verbose bool `toml:"verbose"`
DebugHTTPHeaders []string `toml:"debug_http_headers"`
LogGuessedIP bool `toml:"log_guessed_client_ip"`
Listen []string `toml:"listen"`
LocalAddr string `toml:"local_addr"`
Cert string `toml:"cert"`
Key string `toml:"key"`
Path string `toml:"path"`
Upstream []string `toml:"upstream"`
Timeout uint `toml:"timeout"`
Tries uint `toml:"tries"`
Verbose bool `toml:"verbose"`
DebugHTTPHeaders []string `toml:"debug_http_headers"`
LogGuessedIP bool `toml:"log_guessed_client_ip"`
ECSAllowNonGlobalIP bool `toml:"ecs_allow_non_global_ip"`
ECSUsePreciseIP bool `toml:"ecs_use_precise_ip"`
TLSClientAuth bool `toml:"tls_client_auth"`
TLSClientAuthCA string `toml:"tls_client_auth_ca"`
}
func loadConfig(path string) (*config, error) {
@@ -61,7 +66,7 @@ func loadConfig(path string) (*config, error) {
conf.Path = "/dns-query"
}
if len(conf.Upstream) == 0 {
conf.Upstream = []string{"8.8.8.8:53", "8.8.4.4:53"}
conf.Upstream = []string{"udp:8.8.8.8:53", "udp:8.8.4.4:53"}
}
if conf.Timeout == 0 {
conf.Timeout = 10
@@ -74,9 +79,35 @@ func loadConfig(path string) (*config, error) {
return nil, &configError{"You must specify both -cert and -key to enable TLS"}
}
// validate all upstreams
for _, us := range conf.Upstream {
address, t := addressAndType(us)
if address == "" {
return nil, &configError{"One of the upstreams has not a (udp|tcp|tcp-tls) prefix e.g. udp:1.1.1.1:53"}
}
switch t {
case "tcp", "udp", "tcp-tls":
// OK
default:
return nil, &configError{"Invalid upstream prefix specified, choose one of: udp tcp tcp-tls"}
}
}
return conf, nil
}
var rxUpstreamWithTypePrefix = regexp.MustCompile("^[a-z-]+(:)")
func addressAndType(us string) (string, string) {
p := rxUpstreamWithTypePrefix.FindStringSubmatchIndex(us)
if len(p) != 4 {
return "", ""
}
return us[p[2]+1:], us[:p[2]]
}
type configError struct {
err string
}

View File

@@ -2,8 +2,15 @@
listen = [
"127.0.0.1:8053",
"[::1]:8053",
## To listen on both 0.0.0.0:8053 and [::]:8053, use the following line
# ":8053",
]
# Local address and port for upstream DNS
# If left empty, a local address is automatically chosen.
local_addr = ""
# TLS certification file
# If left empty, plain-text HTTP will be used.
# You are recommended to leave empty and to use a server load balancer (e.g.
@@ -20,11 +27,16 @@ path = "/dns-query"
# Upstream DNS resolver
# If multiple servers are specified, a random one will be chosen each time.
# You can use "udp", "tcp" or "tcp-tls" for the type prefix.
# For "udp", UDP will first be used, and switch to TCP when the server asks to
# or the response is too large.
# For "tcp", only TCP will be used.
# For "tcp-tls", DNS-over-TLS (RFC 7858) will be used to secure the upstream connection.
upstream = [
"1.1.1.1:53",
"1.0.0.1:53",
"8.8.8.8:53",
"8.8.4.4:53",
"udp:1.1.1.1:53",
"udp:1.0.0.1:53",
"udp:8.8.8.8:53",
"udp:8.8.4.4:53",
]
# Upstream timeout
@@ -33,12 +45,34 @@ timeout = 10
# Number of tries if upstream DNS fails
tries = 3
# Only use TCP for DNS query
tcp_only = false
# Enable logging
verbose = false
# Enable log IP from HTTPS-reverse proxy header: X-Forwarded-For or X-Real-IP
# Note: http uri/useragent log cannot be controlled by this config
log_guessed_client_ip = false
# By default, non global IP addresses are never forwarded to upstream servers.
# This is to prevent two things from happening:
# 1. the upstream server knowing your private LAN addresses;
# 2. the upstream server unable to provide geographically near results,
# or even fail to provide any result.
# However, if you are deploying a split tunnel corporation network
# environment, or for any other reason you want to inhibit this
# behavior and allow local (eg RFC1918) address to be forwarded,
# change the following option to "true".
ecs_allow_non_global_ip = false
# If ECS is added to the request, let the full IP address or
# cap it to 24 or 128 mask. This option is to be used only on private
# networks where knwoledge of the terminal endpoint may be required for
# security purposes (eg. DNS Firewalling). Not a good option on the
# internet where IP address may be used to identify the user and
# not only the approximate location.
ecs_use_precise_ip = false
# If DOH is used for a controlled network, it is possible to enable
# the client TLS certificate validation with a specific certificate
# authority used to sign any client one. Disabled by default.
# tls_client_auth = true
# tls_client_auth_ca = "root-ca-public.crt"

View File

@@ -34,7 +34,7 @@ import (
"strings"
"time"
"github.com/m13253/dns-over-https/json-dns"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
"golang.org/x/net/idna"
)
@@ -170,11 +170,11 @@ func (s *Server) parseRequestGoogle(ctx context.Context, w http.ResponseWriter,
}
func (s *Server) generateResponseGoogle(ctx context.Context, w http.ResponseWriter, r *http.Request, req *DNSRequest) {
respJSON := jsonDNS.Marshal(req.response)
respJSON := jsondns.Marshal(req.response)
respStr, err := json.Marshal(respJSON)
if err != nil {
log.Println(err)
jsonDNS.FormatError(w, fmt.Sprintf("DNS packet parse failure (%s)", err.Error()), 500)
jsondns.FormatError(w, fmt.Sprintf("DNS packet parse failure (%s)", err.Error()), 500)
return
}

View File

@@ -36,7 +36,7 @@ import (
"strings"
"time"
"github.com/m13253/dns-over-https/json-dns"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
)
@@ -125,6 +125,7 @@ func (s *Server) parseRequestIETF(ctx context.Context, w http.ResponseWriter, r
}
}
isTailored := edns0Subnet == nil
if edns0Subnet == nil {
ednsClientFamily := uint16(0)
ednsClientAddress := s.findClientIP(r)
@@ -133,10 +134,20 @@ func (s *Server) parseRequestIETF(ctx context.Context, w http.ResponseWriter, r
if ipv4 := ednsClientAddress.To4(); ipv4 != nil {
ednsClientFamily = 1
ednsClientAddress = ipv4
ednsClientNetmask = 24
if s.conf.ECSUsePreciseIP {
ednsClientNetmask = 32
} else {
ednsClientNetmask = 24
ednsClientAddress = ednsClientAddress.Mask(net.CIDRMask(24, 32))
}
} else {
ednsClientFamily = 2
ednsClientNetmask = 56
if s.conf.ECSUsePreciseIP {
ednsClientNetmask = 128
} else {
ednsClientNetmask = 56
ednsClientAddress = ednsClientAddress.Mask(net.CIDRMask(56, 128))
}
}
edns0Subnet = new(dns.EDNS0_SUBNET)
edns0Subnet.Code = dns.EDNS0SUBNET
@@ -156,12 +167,12 @@ func (s *Server) parseRequestIETF(ctx context.Context, w http.ResponseWriter, r
}
func (s *Server) generateResponseIETF(ctx context.Context, w http.ResponseWriter, r *http.Request, req *DNSRequest) {
respJSON := jsonDNS.Marshal(req.response)
respJSON := jsondns.Marshal(req.response)
req.response.Id = req.transactionID
respBytes, err := req.response.Pack()
if err != nil {
log.Println(err)
jsonDNS.FormatError(w, fmt.Sprintf("DNS packet construct failure (%s)", err.Error()), 500)
log.Printf("DNS packet construct failure with upstream %s: %v\n", req.currentUpstream, err)
jsondns.FormatError(w, fmt.Sprintf("DNS packet construct failure (%s)", err.Error()), 500)
return
}
@@ -183,15 +194,21 @@ func (s *Server) generateResponseIETF(ctx context.Context, w http.ResponseWriter
}
if respJSON.Status == dns.RcodeServerFailure {
log.Printf("received server failure from upstream %s: %v\n", req.currentUpstream, req.response)
w.WriteHeader(503)
}
w.Write(respBytes)
_, err = w.Write(respBytes)
if err != nil {
log.Printf("failed to write to client: %v\n", err)
}
}
// Workaround a bug causing DNSCrypt-Proxy to expect a response with TransactionID = 0xcafe
func (s *Server) patchDNSCryptProxyReqID(w http.ResponseWriter, r *http.Request, requestBinary []byte) bool {
if strings.Contains(r.UserAgent(), "dnscrypt-proxy") && bytes.Equal(requestBinary, []byte("\xca\xfe\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x02\x00\x01\x00\x00\x29\x10\x00\x00\x00\x80\x00\x00\x00")) {
log.Println("DNSCrypt-Proxy detected. Patching response.")
if s.conf.Verbose {
log.Println("DNSCrypt-Proxy detected. Patching response.")
}
w.Header().Set("Content-Type", "application/dns-message")
w.Header().Set("Vary", "Accept, User-Agent")
now := time.Now().UTC().Format(http.TimeFormat)
@@ -205,7 +222,9 @@ func (s *Server) patchDNSCryptProxyReqID(w http.ResponseWriter, r *http.Request,
// Workaround a bug causing Firefox 61-62 to reject responses with Content-Type = application/dns-message
func (s *Server) patchFirefoxContentType(w http.ResponseWriter, r *http.Request, req *DNSRequest) bool {
if strings.Contains(r.UserAgent(), "Firefox") && strings.Contains(r.Header.Get("Accept"), "application/dns-udpwireformat") && !strings.Contains(r.Header.Get("Accept"), "application/dns-message") {
log.Println("Firefox 61-62 detected. Patching response.")
if s.conf.Verbose {
log.Println("Firefox 61-62 detected. Patching response.")
}
w.Header().Set("Content-Type", "application/dns-udpwireformat")
w.Header().Set("Vary", "Accept, User-Agent")
req.isTailored = true

View File

@@ -110,6 +110,9 @@ func main() {
conf.Verbose = true
}
server := NewServer(conf)
server, err := NewServer(conf)
if err != nil {
log.Fatalln(err)
}
_ = server.Start()
}

View File

@@ -25,7 +25,10 @@ package main
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net"
@@ -35,15 +38,16 @@ import (
"time"
"github.com/gorilla/handlers"
"github.com/m13253/dns-over-https/json-dns"
jsondns "github.com/m13253/dns-over-https/v2/json-dns"
"github.com/miekg/dns"
)
type Server struct {
conf *config
udpClient *dns.Client
tcpClient *dns.Client
servemux *http.ServeMux
conf *config
udpClient *dns.Client
tcpClient *dns.Client
tcpClientTLS *dns.Client
servemux *http.ServeMux
}
type DNSRequest struct {
@@ -56,22 +60,49 @@ type DNSRequest struct {
errtext string
}
func NewServer(conf *config) (s *Server) {
s = &Server{
func NewServer(conf *config) (*Server, error) {
timeout := time.Duration(conf.Timeout) * time.Second
s := &Server{
conf: conf,
udpClient: &dns.Client{
Net: "udp",
UDPSize: dns.DefaultMsgSize,
Timeout: time.Duration(conf.Timeout) * time.Second,
Timeout: timeout,
},
tcpClient: &dns.Client{
Net: "tcp",
Timeout: time.Duration(conf.Timeout) * time.Second,
Timeout: timeout,
},
tcpClientTLS: &dns.Client{
Net: "tcp-tls",
Timeout: timeout,
},
servemux: http.NewServeMux(),
}
if conf.LocalAddr != "" {
udpLocalAddr, err := net.ResolveUDPAddr("udp", conf.LocalAddr)
if err != nil {
return nil, err
}
tcpLocalAddr, err := net.ResolveTCPAddr("tcp", conf.LocalAddr)
if err != nil {
return nil, err
}
s.udpClient.Dialer = &net.Dialer{
Timeout: timeout,
LocalAddr: udpLocalAddr,
}
s.tcpClient.Dialer = &net.Dialer{
Timeout: timeout,
LocalAddr: tcpLocalAddr,
}
s.tcpClientTLS.Dialer = &net.Dialer{
Timeout: timeout,
LocalAddr: tcpLocalAddr,
}
}
s.servemux.HandleFunc(conf.Path, s.handlerFunc)
return
return s, nil
}
func (s *Server) Start() error {
@@ -79,12 +110,48 @@ func (s *Server) Start() error {
if s.conf.Verbose {
servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
}
var clientCAPool *x509.CertPool
if s.conf.TLSClientAuth {
if s.conf.TLSClientAuthCA != "" {
clientCA, err := ioutil.ReadFile(s.conf.TLSClientAuthCA)
if err != nil {
log.Fatalf("Reading certificate for client authentication has failed: %v", err)
}
clientCAPool = x509.NewCertPool()
clientCAPool.AppendCertsFromPEM(clientCA)
log.Println("Certificate loaded for client TLS authentication")
} else {
log.Fatalln("TLS client authentication requires both tls_client_auth and tls_client_auth_ca, exiting.")
}
}
results := make(chan error, len(s.conf.Listen))
for _, addr := range s.conf.Listen {
go func(addr string) {
var err error
if s.conf.Cert != "" || s.conf.Key != "" {
err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
if clientCAPool != nil {
srvtls := &http.Server{
Handler: servemux,
Addr: addr,
TLSConfig: &tls.Config{
ClientCAs: clientCAPool,
ClientAuth: tls.RequireAndVerifyClientCert,
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
c, err := tls.LoadX509KeyPair(s.conf.Cert, s.conf.Key)
if err != nil {
fmt.Printf("Error loading server certificate key pair: %v\n", err)
return nil, err
}
return &c, nil
},
},
}
err = srvtls.ListenAndServeTLS("", "")
} else {
err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
}
} else {
err = http.ListenAndServe(addr, servemux)
}
@@ -108,6 +175,18 @@ func (s *Server) Start() error {
func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
if strings.ContainsRune(realIP, ':') {
r.RemoteAddr = "[" + realIP + "]:0"
} else {
r.RemoteAddr = realIP + ":0"
}
_, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
r.RemoteAddr = realIP
}
}
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST")
w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -176,23 +255,22 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
} else if contentType == "application/dns-udpwireformat" {
req = s.parseRequestIETF(ctx, w, r)
} else {
jsonDNS.FormatError(w, fmt.Sprintf("Invalid argument value: \"ct\" = %q", contentType), 415)
jsondns.FormatError(w, fmt.Sprintf("Invalid argument value: \"ct\" = %q", contentType), 415)
return
}
if req.errcode == 444 {
return
}
if req.errcode != 0 {
jsonDNS.FormatError(w, req.errtext, req.errcode)
jsondns.FormatError(w, req.errtext, req.errcode)
return
}
req = s.patchRootRD(req)
var err error
req, err = s.doDNSQuery(ctx, req)
err := s.doDNSQuery(ctx, req)
if err != nil {
jsonDNS.FormatError(w, fmt.Sprintf("DNS query failure (%s)", err.Error()), 503)
jsondns.FormatError(w, fmt.Sprintf("DNS query failure (%s)", err.Error()), 503)
return
}
@@ -206,12 +284,17 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) findClientIP(r *http.Request) net.IP {
noEcs := r.URL.Query().Get("no_ecs")
if strings.ToLower(noEcs) == "true" {
return nil
}
XForwardedFor := r.Header.Get("X-Forwarded-For")
if XForwardedFor != "" {
for _, addr := range strings.Split(XForwardedFor, ",") {
addr = strings.TrimSpace(addr)
ip := net.ParseIP(addr)
if jsonDNS.IsGlobalIP(ip) {
if jsondns.IsGlobalIP(ip) {
return ip
}
}
@@ -220,15 +303,17 @@ func (s *Server) findClientIP(r *http.Request) net.IP {
if XRealIP != "" {
addr := strings.TrimSpace(XRealIP)
ip := net.ParseIP(addr)
if jsonDNS.IsGlobalIP(ip) {
if s.conf.ECSAllowNonGlobalIP || jsondns.IsGlobalIP(ip) {
return ip
}
}
remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
if err != nil {
return nil
}
if ip := remoteAddr.IP; jsonDNS.IsGlobalIP(ip) {
ip := remoteAddr.IP
if s.conf.ECSAllowNonGlobalIP || jsondns.IsGlobalIP(ip) {
return ip
}
return nil
@@ -244,24 +329,54 @@ func (s *Server) patchRootRD(req *DNSRequest) *DNSRequest {
return req
}
func (s *Server) doDNSQuery(ctx context.Context, req *DNSRequest) (resp *DNSRequest, err error) {
// TODO(m13253): Make ctx work. Waiting for a patch for ExchangeContext from miekg/dns.
// Return the position index for the question of qtype from a DNS msg, otherwise return -1
func (s *Server) indexQuestionType(msg *dns.Msg, qtype uint16) int {
for i, question := range msg.Question {
if question.Qtype == qtype {
return i
}
}
return -1
}
func (s *Server) doDNSQuery(ctx context.Context, req *DNSRequest) (err error) {
numServers := len(s.conf.Upstream)
for i := uint(0); i < s.conf.Tries; i++ {
req.currentUpstream = s.conf.Upstream[rand.Intn(numServers)]
if !s.conf.TCPOnly {
req.response, _, err = s.udpClient.Exchange(req.request, req.currentUpstream)
if err == nil && req.response != nil && req.response.Truncated {
log.Println(err)
req.response, _, err = s.tcpClient.Exchange(req.request, req.currentUpstream)
upstream, t := addressAndType(req.currentUpstream)
switch t {
default:
log.Printf("invalid DNS type %q in upstream %q", t, upstream)
return &configError{"invalid DNS type"}
// Use DNS-over-TLS (DoT) if configured to do so
case "tcp-tls":
req.response, _, err = s.tcpClientTLS.ExchangeContext(ctx, req.request, upstream)
case "tcp", "udp":
// Use TCP if always configured to or if the Query type dictates it (AXFR)
if t == "tcp" || (s.indexQuestionType(req.request, dns.TypeAXFR) > -1) {
req.response, _, err = s.tcpClient.ExchangeContext(ctx, req.request, upstream)
} else {
req.response, _, err = s.udpClient.ExchangeContext(ctx, req.request, upstream)
if err == nil && req.response != nil && req.response.Truncated {
log.Println(err)
req.response, _, err = s.tcpClient.ExchangeContext(ctx, req.request, upstream)
}
// Retry with TCP if this was an IXFR request and we only received an SOA
if (s.indexQuestionType(req.request, dns.TypeIXFR) > -1) &&
(len(req.response.Answer) == 1) &&
(req.response.Answer[0].Header().Rrtype == dns.TypeSOA) {
req.response, _, err = s.tcpClient.ExchangeContext(ctx, req.request, upstream)
}
}
} else {
req.response, _, err = s.tcpClient.Exchange(req.request, req.currentUpstream)
}
if err == nil {
return req, nil
return nil
}
log.Printf("DNS error from upstream %s: %s\n", req.currentUpstream, err.Error())
}
return req, err
return err
}

View File

@@ -24,6 +24,6 @@
package main
const (
VERSION = "2.0.0"
VERSION = "2.3.2"
USER_AGENT = "DNS-over-HTTPS/" + VERSION + " (+https://github.com/m13253/dns-over-https)"
)

25
go.mod
View File

@@ -1,13 +1,20 @@
module github.com/m13253/dns-over-https
module github.com/m13253/dns-over-https/v2
go 1.12
go 1.19
require (
github.com/BurntSushi/toml v0.3.1
github.com/gorilla/handlers v1.4.0
github.com/miekg/dns v1.1.6
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect
golang.org/x/sys v0.0.0-20190312061237-fead79001313 // indirect
github.com/BurntSushi/toml v1.2.0
github.com/gorilla/handlers v1.5.1
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9
github.com/miekg/dns v1.1.50
golang.org/x/net v0.0.0-20220812174116-3211cb980234
)
require (
github.com/felixge/httpsnoop v1.0.1 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

70
go.sum
View File

@@ -1,25 +1,51 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA=
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0=
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.6 h1:jVwb4GDwD65q/gtItR/lIZHjNH93QfeGxZUkzJcW9mc=
github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA=
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU=
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190308023053-584f3b12f43e h1:K7CV15oJ823+HLXQ+M7MSMrUg8LjfqY7O3naO+8Pp/I=
golang.org/x/sys v0.0.0-20190308023053-584f3b12f43e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -21,7 +21,7 @@
DEALINGS IN THE SOFTWARE.
*/
package jsonDNS
package jsondns
import (
"encoding/json"
@@ -38,11 +38,11 @@ type dnsError struct {
func FormatError(w http.ResponseWriter, comment string, errcode int) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
errJson := dnsError{
errJSON := dnsError{
Status: dns.RcodeServerFailure,
Comment: comment,
}
errStr, err := json.Marshal(errJson)
errStr, err := json.Marshal(errJSON)
if err != nil {
log.Fatalln(err)
}

View File

@@ -21,109 +21,111 @@
DEALINGS IN THE SOFTWARE.
*/
package jsonDNS
package jsondns
import (
"net"
"github.com/infobloxopen/go-trees/iptree"
)
// RFC6890
var localIPv4Nets = []net.IPNet{
// This host on this network
net.IPNet{
net.IP{0, 0, 0, 0},
net.IPMask{255, 0, 0, 0},
},
// Private-Use Networks
net.IPNet{
net.IP{10, 0, 0, 0},
net.IPMask{255, 0, 0, 0},
},
// Shared Address Space
net.IPNet{
net.IP{100, 64, 0, 0},
net.IPMask{255, 192, 0, 0},
},
// Loopback
net.IPNet{
net.IP{127, 0, 0, 0},
net.IPMask{255, 0, 0, 0},
},
// Link Local
net.IPNet{
net.IP{169, 254, 0, 0},
net.IPMask{255, 255, 0, 0},
},
// Private-Use Networks
net.IPNet{
net.IP{172, 16, 0, 0},
net.IPMask{255, 240, 0, 0},
},
// DS-Lite
net.IPNet{
net.IP{192, 0, 0, 0},
net.IPMask{255, 255, 255, 248},
},
// 6to4 Relay Anycast
net.IPNet{
net.IP{192, 88, 99, 0},
net.IPMask{255, 255, 255, 0},
},
// Private-Use Networks
net.IPNet{
net.IP{192, 168, 0, 0},
net.IPMask{255, 255, 0, 0},
},
// Reserved for Future Use & Limited Broadcast
net.IPNet{
net.IP{240, 0, 0, 0},
net.IPMask{240, 0, 0, 0},
},
}
var defaultFilter *iptree.Tree
// RFC6890
var localIPv6Nets = []net.IPNet{
func init() {
defaultFilter = iptree.NewTree()
// RFC6890
// This host on this network
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{0, 0, 0, 0},
Mask: net.IPMask{255, 0, 0, 0},
}, struct{}{})
// Private-Use Networks
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{10, 0, 0, 0},
Mask: net.IPMask{255, 0, 0, 0},
}, struct{}{})
// Shared Address Space
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{100, 64, 0, 0},
Mask: net.IPMask{255, 192, 0, 0},
}, struct{}{})
// Loopback
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{127, 0, 0, 0},
Mask: net.IPMask{255, 0, 0, 0},
}, struct{}{})
// Link Local
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{169, 254, 0, 0},
Mask: net.IPMask{255, 255, 0, 0},
}, struct{}{})
// Private-Use Networks
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{172, 16, 0, 0},
Mask: net.IPMask{255, 240, 0, 0},
}, struct{}{})
// DS-Lite
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{192, 0, 0, 0},
Mask: net.IPMask{255, 255, 255, 248},
}, struct{}{})
// 6to4 Relay Anycast
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{192, 88, 99, 0},
Mask: net.IPMask{255, 255, 255, 0},
}, struct{}{})
// Private-Use Networks
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{192, 168, 0, 0},
Mask: net.IPMask{255, 255, 0, 0},
}, struct{}{})
// Reserved for Future Use & Limited Broadcast
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{240, 0, 0, 0},
Mask: net.IPMask{240, 0, 0, 0},
}, struct{}{})
// RFC6890
// Unspecified & Loopback Address
net.IPNet{
net.IP{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe},
},
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe},
}, struct{}{})
// Discard-Only Prefix
net.IPNet{
net.IP{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}, struct{}{})
// Unique-Local
net.IPNet{
net.IP{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
net.IPMask{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Mask: net.IPMask{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}, struct{}{})
// Linked-Scoped Unicast
net.IPNet{
net.IP{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
net.IPMask{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
defaultFilter.InplaceInsertNet(&net.IPNet{
IP: net.IP{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Mask: net.IPMask{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}, struct{}{})
}
func IsGlobalIP(ip net.IP) bool {
if ip == nil {
return false
}
if ipv4 := ip.To4(); len(ipv4) == net.IPv4len {
for _, ipnet := range localIPv4Nets {
if ipnet.Contains(ip) {
return false
}
}
return true
}
if len(ip) == net.IPv6len {
for _, ipnet := range localIPv6Nets {
if ipnet.Contains(ip) {
return false
}
}
return true
}
return true
_, contained := defaultFilter.GetByIP(ip)
return !contained
}

34
json-dns/globalip_test.go Normal file
View File

@@ -0,0 +1,34 @@
package jsondns
import (
"fmt"
"net"
)
func ExampleIsGlobalIP() {
fmt.Println(IsGlobalIP(net.ParseIP("127.0.0.1")))
fmt.Println(IsGlobalIP(net.IP{192, 168, 1, 1}))
fmt.Println(IsGlobalIP(net.ParseIP("8.8.8.8")))
fmt.Println(IsGlobalIP(net.IP{8, 8, 4, 4}))
fmt.Println(IsGlobalIP(net.ParseIP("::1")))
fmt.Println(IsGlobalIP(net.IP{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
fmt.Println(IsGlobalIP(net.ParseIP("2001:4860:4860::8888")))
fmt.Println(IsGlobalIP(net.IP{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x44}))
fmt.Println(IsGlobalIP(net.ParseIP("::ffff:127.0.0.1")))
fmt.Println(IsGlobalIP(net.IP{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 192, 168, 1, 1}))
fmt.Println(IsGlobalIP(net.ParseIP("::ffff:808:808")))
fmt.Println(IsGlobalIP(net.IP{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 8, 8, 4, 4}))
// Output:
// false
// false
// true
// true
// false
// false
// true
// true
// false
// false
// true
// true
}

View File

@@ -21,7 +21,7 @@
DEALINGS IN THE SOFTWARE.
*/
package jsonDNS
package jsondns
import (
"net"

View File

@@ -21,12 +21,32 @@
DEALINGS IN THE SOFTWARE.
*/
package jsonDNS
package jsondns
import (
"encoding/json"
"time"
)
type QuestionList []Question
func (ql *QuestionList) UnmarshalJSON(b []byte) error {
// Fix variant question response in Response.Question
//
// Solution taken from:
// https://engineering.bitnami.com/articles/dealing-with-json-with-non-homogeneous-types-in-go.html
// https://archive.is/NU4zR
if len(b) > 0 && b[0] == '[' {
return json.Unmarshal(b, (*[]Question)(ql))
}
var q Question
if err := json.Unmarshal(b, &q); err != nil {
return err
}
*ql = []Question{q}
return nil
}
type Response struct {
// Standard DNS response code (32 bit integer)
Status uint32 `json:"Status"`
@@ -40,13 +60,13 @@ type Response struct {
// FIXME: We don't have DNSSEC yet! This bit is not reliable!
AD bool `json:"AD"`
// Whether the client asked to disable DNSSEC
CD bool `json:"CD"`
Question []Question `json:"Question"`
Answer []RR `json:"Answer,omitempty"`
Authority []RR `json:"Authority,omitempty"`
Additional []RR `json:"Additional,omitempty"`
Comment string `json:"Comment,omitempty"`
EdnsClientSubnet string `json:"edns_client_subnet,omitempty"`
CD bool `json:"CD"`
Question QuestionList `json:"Question"`
Answer []RR `json:"Answer,omitempty"`
Authority []RR `json:"Authority,omitempty"`
Additional []RR `json:"Additional,omitempty"`
Comment string `json:"Comment,omitempty"`
EdnsClientSubnet string `json:"edns_client_subnet,omitempty"`
// Least time-to-live
HaveTTL bool `json:"-"`
LeastTTL uint32 `json:"-"`

View File

@@ -21,7 +21,7 @@
DEALINGS IN THE SOFTWARE.
*/
package jsonDNS
package jsondns
import (
"fmt"
@@ -38,7 +38,7 @@ func PrepareReply(req *dns.Msg) *dns.Msg {
reply := new(dns.Msg)
reply.Id = req.Id
reply.Response = true
reply.Opcode = reply.Opcode
reply.Opcode = req.Opcode
reply.RecursionDesired = req.RecursionDesired
reply.RecursionAvailable = req.RecursionDesired
reply.CheckingDisabled = req.CheckingDisabled