Compare commits

...

984 Commits

Author SHA1 Message Date
Frank Denis 0059194a9e Update deps 2024-06-03 08:40:06 +02:00
Frank Denis 35d7aa0603 Print error when the lying resolver test fails 2024-05-19 18:17:05 +02:00
Frank Denis 8dadd61730
Merge pull request #2630 from DNSCrypt/dependabot/github_actions/softprops/action-gh-release-2.0.5
Bump softprops/action-gh-release from 2.0.2 to 2.0.5
2024-05-08 10:31:44 +02:00
dependabot[bot] f7da81cf29
Bump softprops/action-gh-release from 2.0.2 to 2.0.5
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.2 to 2.0.5.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](d99959edae...69320dbe05)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-08 03:55:52 +00:00
Frank Denis 0efce55895 Try another ODoH relay 2024-05-07 22:44:33 +02:00
Frank Denis 5a1d94d506 Update the test ODoH relay 2024-05-07 22:35:09 +02:00
Frank Denis 271943c158 Update quic-go 2024-05-07 17:39:56 +02:00
Frank Denis 34a1f2ebf5 Update quic-go 2024-04-27 22:33:56 +02:00
Frank Denis f8ce22d9b9 Update deps 2024-04-25 12:45:52 +02:00
Frank Denis 249dba391d Support gzip compression to fetch source files 2024-04-25 12:43:29 +02:00
Frank Denis 987ae216e3 Add fritz.box to the set of undelegated zones 2024-04-21 20:14:15 +02:00
Frank Denis 7fba32651b Make it more visible that DNS64 has been enabled 2024-04-19 18:27:39 +02:00
Frank Denis 6ae388e646 DNS64 plugin: don't return SYNTH data, alter the response directly
Fixes #2619

However, cached responses now appear with the "PASS" status rather
than "CLOAK".
2024-04-19 18:19:16 +02:00
Frank Denis 0af88bc875 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  chore: fix some typos in comments
2024-04-15 12:36:30 +02:00
Frank Denis d36edeb612 Update deps 2024-04-15 12:36:21 +02:00
Frank Denis 041a6c7d7f
Merge pull request #2615 from cuibuwei/master
chore: fix some typos in comments
2024-04-13 20:02:49 +02:00
cuibuwei 2c6416d5ae chore: fix some typos in comments
Signed-off-by: cuibuwei <cuibuwei@gmail.com>
2024-04-13 19:56:31 +08:00
Frank Denis 4d1cd67d4d Nits 2024-04-03 16:49:37 +02:00
Frank Denis 363d44919f Properly check for the sticky bit 2024-04-03 16:47:13 +02:00
Frank Denis a88076d06f
Merge pull request #2605 from edmonds/forward-root-subdomain-matches
Forwarding plugin: Support forwarding subdomains of the root domain
2024-03-26 21:32:06 +01:00
Frank Denis 119bc0b660 Update deps 2024-03-26 19:56:06 +01:00
Robert Edmonds 49000cd4f4 Forwarding plugin: Support forwarding subdomains of the root domain
This commit updates the forwarding plugin to support matching subdomains
of the root domain ("."). It looks like the forwarding plugin already
performs subdomain matches against the domains specified in the
forwarding rules files, but matches against the root domain weren't
working because of the way matches are performed by comparing the
normalized presentation format QNAME (which omits the trailing dot for
all QNAMEs except the root domain name).

Without this commit, only queries where the QNAME is exactly "."
would match a forwarding rule for the "." domain, like this (with
`offline_mode = true` and a single forwarding rule for the "." domain):

```
[2024-03-25 21:13:31]	100.100.100.100	.	NS	FORWARD	0ms	127.0.0.1:53
[2024-03-25 21:13:36]	100.100.100.100	com	NS	NOT_READY	0ms	-
```

With this commit I get the expected result:

```
[2024-03-25 21:40:07]	100.100.100.100	.	NS	FORWARD	0ms	127.0.0.1:53
[2024-03-25 21:40:09]	100.100.100.100	com	NS	FORWARD	0ms	127.0.0.1:53
```
2024-03-25 21:30:09 -04:00
Frank Denis ec46e09360
Merge pull request #2597 from DNSCrypt/dependabot/github_actions/softprops/action-gh-release-d99959edae48b5ffffd7b00da66dcdb0a33a52ee
Bump softprops/action-gh-release from 975c1b265e11dd76618af1c374e7981f9a6ff44a to d99959edae48b5ffffd7b00da66dcdb0a33a52ee
2024-03-11 09:23:02 +01:00
dependabot[bot] ea5808e024
Bump softprops/action-gh-release
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 975c1b265e11dd76618af1c374e7981f9a6ff44a to d99959edae48b5ffffd7b00da66dcdb0a33a52ee.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](975c1b265e...d99959edae)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:24:39 +00:00
Frank Denis 79a1aa8325
Merge pull request #2591 from alisonatwork/fix-build 2024-02-28 10:11:48 +01:00
Alison Winters 4c442f5dbb use go mod version for codeql 2024-02-28 10:07:03 +08:00
Alison Winters f7e13502c0 fix go version format 2024-02-28 09:47:34 +08:00
YX Hao 8d43ce9b56 make expression be more self-explanatory 2024-02-27 22:05:40 +08:00
YX Hao ac5087315c Listen `0.0.0.0` only on IPv4 2024-02-27 19:04:09 +08:00
Frank Denis ad80d81d43
Merge pull request #2587 from DNSCrypt/dependabot/github_actions/softprops/action-gh-release-975c1b265e11dd76618af1c374e7981f9a6ff44a
Bump softprops/action-gh-release from 4634c16e79c963813287e889244c50009e7f0981 to 975c1b265e11dd76618af1c374e7981f9a6ff44a
2024-02-26 08:47:39 +01:00
dependabot[bot] a7fb13ba4e
Bump softprops/action-gh-release
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 4634c16e79c963813287e889244c50009e7f0981 to 975c1b265e11dd76618af1c374e7981f9a6ff44a.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](4634c16e79...975c1b265e)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 03:37:41 +00:00
Frank Denis 006619837f Update deps 2024-02-20 08:01:38 +01:00
Frank Denis 093936f7ab Check for dumb file permissions on startup
There's nothing special about "-service install".

On any system, executables shouldn't be modifiable by other system
users, no matter what the executable is and how it's run.
2024-02-20 02:39:39 +01:00
Frank Denis 7462961980 Warn if the executable of the service being installed could be overwritten by other system users
Fixes #2579

Until this is handled by `kardianos/service`
2024-02-20 02:23:49 +01:00
Frank Denis 0b559bb54f Warn if the main config file could be written by other system users 2024-02-20 02:11:03 +01:00
Frank Denis 658835b4ff
Merge pull request #2573 from DNSCrypt/dependabot/github_actions/softprops/action-gh-release-4634c16e79c963813287e889244c50009e7f0981
Bump softprops/action-gh-release from c9b46fe7aad9f02afd89b12450b780f52dacfb2d to 4634c16e79c963813287e889244c50009e7f0981
2024-02-06 13:23:03 +01:00
Frank Denis 90c3017793
Merge pull request #2544 from DNSCrypt/dependabot/github_actions/actions/setup-go-5
Bump actions/setup-go from 4 to 5
2024-02-06 13:22:24 +01:00
dependabot[bot] e371138b86
Bump softprops/action-gh-release
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from c9b46fe7aad9f02afd89b12450b780f52dacfb2d to 4634c16e79c963813287e889244c50009e7f0981.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](c9b46fe7aa...4634c16e79)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-06 03:22:12 +00:00
Frank Denis bcbf2db4ff
Merge pull request #2568 from lifenjoiner/ci 2024-01-19 14:30:34 +01:00
YX Hao 64fa90839c CI tests the unregistered domain for solid NS answer
And formats.
2024-01-19 19:11:03 +08:00
Frank Denis f2484f5bd5 Cache plugin: replace ARC cache with SIEVE 2024-01-19 00:05:33 +01:00
Frank Denis 63f8d9b30d Update deps 2024-01-18 23:47:00 +01:00
Xiaotong Liu 49e3570c2c
Support server refresh concurrency (#2537)
* simultaneously refresh all servers

* Add `cert_refresh_concurrency`

---------

Co-authored-by: YX Hao <lifenjoiner@163.com>
2023-12-18 19:25:54 +08:00
lifenjoiner 3be53642fe
Merge pull request #2549 from lifenjoiner/wg
Optimize CaptivePortalHandler for clean code
2023-12-17 18:59:05 +08:00
YX Hao 13e7077200 Optimize CaptivePortalHandler for clean code 2023-12-14 19:23:42 +08:00
Frank Denis f5912d7ca9
Merge pull request #2548 from DNSCrypt/dependabot/github_actions/github/codeql-action-3
Bump github/codeql-action from 2 to 3
2023-12-14 07:47:05 +01:00
dependabot[bot] 0196d7d2ab
Bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-14 03:22:09 +00:00
Frank Denis 898ded9c52 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  add timeout for udp and tcp dialer
  Use blocking channel instead of looping sleep for less CPU usage
2023-12-13 08:35:55 +01:00
Frank Denis e782207911 Update deps 2023-12-13 08:35:41 +01:00
Frank Denis 0f1f635ec1
Merge pull request #2545 from keatonLiu/dial-timeout
add timeout for udp and tcp dialer
2023-12-11 14:45:20 +01:00
keatonLiu 956a14ee21 add timeout for udp and tcp dialer 2023-12-10 23:56:13 +08:00
dependabot[bot] 22731786a2
Bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-07 03:49:26 +00:00
Frank Denis 42b6ae9052
Merge pull request #2541 from lifenjoiner/chan
Use blocking channel instead of looping sleep for less CPU usage
2023-12-03 17:24:10 +01:00
YX Hao 0d5e52bb16 Use blocking channel instead of looping sleep for less CPU usage 2023-12-03 23:00:10 +08:00
Frank Denis 0ba728b6ce Update deps 2023-11-15 15:51:48 -08:00
Frank Denis cb80bf33e8 shellcheck 2023-11-09 22:47:41 +01:00
Frank Denis 88207560a7 shellcheck fixes 2023-11-09 22:45:11 +01:00
Jeffrey Damick 4a361dbb05 Added support to package dnscrypt for windows into an msi 2023-11-09 13:14:29 -08:00
Frank Denis b37a5c991a Update deps 2023-10-12 15:53:53 +02:00
Frank Denis 0232870870 -list: only copy nofilter flag for ODoH relays 2023-09-23 22:52:43 +02:00
Frank Denis 1a9bf8a286 Omit DNSSEC flag for relays 2023-09-23 18:46:11 +02:00
Frank Denis 7fb58720fb Add -include-relays option to include relays in -list and -list-all 2023-09-23 18:37:52 +02:00
Frank Denis f85b3e81ec Update deps 2023-09-19 20:59:57 +02:00
Frank Denis 79779cf744 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Bump actions/checkout from 3 to 4
2023-09-05 22:37:54 +02:00
Frank Denis 8bea679e7b Unofficially support DoH/ODoH over HTTP 2023-09-05 22:37:11 +02:00
Frank Denis 96f21f1bff
Merge pull request #2479 from DNSCrypt/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-09-05 08:13:08 +02:00
dependabot[bot] 21097686c1
Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-05 03:29:06 +00:00
Frank Denis 87571d4a7f Add an IPv6 forwarding example
Fixes #2474
2023-08-30 21:32:22 +02:00
Frank Denis f531c8fffb plugin_forward: parse more conventions for IPv6 addresses 2023-08-30 21:29:09 +02:00
Frank Denis 5ae83c1592 Remove minor go version for codeql 2023-08-26 18:28:55 +02:00
Frank Denis c86e9a90cc Update quic-go 2023-08-25 16:15:45 +02:00
Frank Denis d48c811ea9 Don't use absolute paths in the example file 2023-08-17 14:44:47 +02:00
Frank Denis f2b1edcec2 Add dnscry.pt servers 2023-08-17 14:43:33 +02:00
Frank Denis 1b65fe62b0 Bump 2023-08-11 18:56:31 +02:00
Frank Denis 194752e829 Update ChangeLog 2023-08-11 18:47:44 +02:00
Frank Denis 808f2dfa0e Update deps 2023-08-11 17:06:18 +02:00
Frank Denis 7dd79d5f96 Add a little bit more delay when spinning
But we really shouldn't do it that way, if only because there's a race
between the last write to the channel and the close() call
2023-08-11 15:24:14 +02:00
Frank Denis 5088d8fae1 Use the latest version of Go in CI 2023-08-11 14:59:34 +02:00
Frank Denis aff09648bb Add support for extended error codes 2023-08-11 14:59:10 +02:00
Frank Denis 7bca9a6c0a
Merge pull request #2457 from Neo2308/feature/master/check-latest-go
Configured Github CI to always use latest go version
2023-08-11 14:07:59 +02:00
Frank Denis 98d0938815 Make RefreshDelay match the documentation 2023-08-11 14:06:03 +02:00
Frank Denis 50780421a8 Remove ipv6.download.dnscrypt.info
IPv6 address has been added to download.dnscrypt.info
2023-08-11 14:04:20 +02:00
RadhaKrishna be7d5d1277 Configured Github CI to always use latest go
version

* Check if there is a newer version of go before using the cached version.
2023-08-11 17:15:09 +05:30
Frank Denis c3dd761b81 Make error more explicit 2023-08-11 12:07:13 +02:00
Frank Denis d8aec47a72 Revert "Make RefreshDelay match the documentation"
This reverts commit cfd6ced134.
2023-08-11 11:48:30 +02:00
Frank Denis cfd6ced134 Make RefreshDelay match the documentation 2023-08-11 11:42:12 +02:00
Frank Denis bdf27330c9 Make fetchWithCache() more readable 2023-08-11 11:24:54 +02:00
Frank Denis a108d048d8 A useless Chtimes() call is still required for the tests :/ 2023-08-11 11:16:44 +02:00
Frank Denis afcfd566c9 Make updateCache() more readable 2023-08-11 11:11:16 +02:00
Frank Denis ce55d1c5bb Get rid of named return parameters 2023-08-11 11:01:55 +02:00
Frank Denis 2481fbebd7 Revert b898e07066 2023-08-11 01:39:15 +02:00
Frank Denis 32aad7bb34 Format fix 2023-08-11 01:15:34 +02:00
Frank Denis 7033f242c0 Restore the cache update code from version 2.1.4 for now 2023-08-11 00:51:34 +02:00
Frank Denis 2675d73b13 Port changes from #2334
I'm not sure I follow, but I trust @lifenjoiner

Fixes #2334
2023-08-11 00:17:46 +02:00
Frank Denis 5085a22903 Update quic-go again 2023-08-10 23:50:43 +02:00
Frank Denis 7cc5a051c7 Update golang-lru 2023-08-08 14:21:12 +02:00
Frank Denis 894d20191f Update deps 2023-08-07 17:32:25 +02:00
Frank Denis 0a98be94a7 Update quic-go to fix two regressions 2023-08-01 00:06:22 +02:00
Frank Denis 1792c06bc7
Merge pull request #2442 from Expertcoderz/patch-1
Add note regarding block_unqualified setting
2023-07-25 14:55:42 +02:00
Expertcoderz 63e414021b
Add note regarding block_unqualified 2023-07-25 12:36:07 +00:00
Frank Denis d659a801c2 Big update to Update quic-go 2023-07-22 00:41:27 +02:00
Frank Denis a4eda39563
Merge pull request #2438 from Expertcoderz/patch-1
Add .mail & .home.arpa undelegated names
2023-07-15 20:07:05 +02:00
Expertcoderz 4114f032c3
Add .mail & .home.arpa undelegated names
Both names have been recognized for internal use in private networks.
2023-07-15 13:12:40 +00:00
Frank Denis a352a3035c Update deps 2023-07-08 14:56:13 +02:00
Frank Denis 60684f8ee4
Merge pull request #2429 from lifenjoiner/quic-go
Upgrade quic-go to v0.36.1
2023-07-08 14:54:39 +02:00
YX Hao be369a1f7a Shorten a line 2023-07-06 21:01:41 +08:00
YX Hao 89ccc59f0e Upgrade quic-go to v0.36.1
quic-go has made breaking changes since v0.35.0, includes implementing
`CloseIdleConnections`.
Now, the local listener UDPConn are reused, and don't pile up. But,
1 instance (IPv4/IPv6) persists for each connected server.
2023-07-05 21:19:54 +08:00
Frank Denis 16b2c84147 Tone down some errors 2023-06-24 22:38:59 +02:00
Carlo Teubner b46775ae0c
Add some missing error checks (#2420)
I found these with the 'errcheck' tool (via 'golangci-lint').

I aimed to apply reasonable judgement when deciding which errors
actually need handling, and how to handle them.
2023-06-24 22:23:12 +02:00
Frank Denis cef4b041d7 Don't call "bin" what is actually text 2023-06-24 22:11:47 +02:00
Carlo Teubner d8b1f4e7cd
Fix miscellaneous style issues (#2421)
Found by running: golangci-lint run --enable-all

I have only addressed the reported issues that seemed relevant to me.
2023-06-24 21:56:03 +02:00
Frank Denis 23a6cd7504 Revert "Update quic-go"
This reverts commit f9f68cf0a3.

quic-go >= 1.0.35 panics

We may not be using the new API correctly.
2023-06-22 11:06:37 +02:00
Frank Denis f42b7dad17 Update deps 2023-06-22 10:36:33 +02:00
Frank Denis 4f3ce0cbae Update deps 2023-06-18 04:19:52 +02:00
Frank Denis 0f1e3b4ba8 error check all the rand.Read() calls 2023-06-06 09:16:44 +02:00
Frank Denis 62ef5c9d02 Update quic-go to fix a regression 2023-06-01 11:32:10 +02:00
Frank Denis f9f68cf0a3 Update quic-go 2023-05-30 18:17:27 +02:00
Frank Denis 0c26d1637a Add suport for TLS key logging 2023-05-24 09:21:49 +02:00
Frank Denis 9f86ffdd1e Update deps 2023-05-13 11:39:11 +02:00
lifenjoiner 9b2c674744
Base on clientProto value explicitly to dereference clientAddr (#2393)
There are variants local_doh and trampoline for internal flow.
2023-05-13 11:22:52 +02:00
Frank Denis d381af5510 Update deps 2023-05-01 16:05:02 +02:00
Frank Denis c66023c7d7 Clarify that TLS cipher suites are for TLS 1.2
Fixes #2377
2023-04-18 13:15:59 -06:00
Frank Denis 5b8e7d4114 Use the same command as on the wiki to create a local DoH cert 2023-04-14 23:08:10 +02:00
KOLANICH f4007f709d
Add DOH certificate generation commands into the example config. (#2367) 2023-04-14 21:34:29 +02:00
lifenjoiner dd1c066724
CI: Don't downloading already vendored dependencies (#2370) 2023-04-14 21:21:22 +02:00
lifenjoiner 5d551e54ce
CI: Fix actions/setup-go@v4 warning (#2371)
Warning: Restore cache failed: Dependencies file is not found ...
https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs
2023-04-14 21:20:37 +02:00
Thad Guidry fbc7817366
fix grammar in example file (#2372) 2023-04-14 21:19:55 +02:00
Frank Denis 9b61b73852 Update deps 2023-04-07 16:42:57 +02:00
Frank Denis af6340df09 Comment 2023-04-07 16:20:26 +02:00
Frank Denis 9c73ab3070 Simplify updateCache() 2023-04-07 16:18:50 +02:00
Frank Denis ea3625bcfd Try to simplify updateCache() to understand what it does 2023-04-07 16:09:51 +02:00
Frank Denis f567f57150 up 2023-04-07 15:58:34 +02:00
Frank Denis c03f1a31eb Go named return parameters are utterly confusing 2023-04-07 15:37:09 +02:00
Frank Denis c3c51bb435 Partially re-merge 92ed5b95e0 2023-04-07 15:21:00 +02:00
Frank Denis 0f30b3b028 Revert "Try to understand how cache files are updated"
This reverts commit 92ed5b95e0.
2023-04-07 15:16:15 +02:00
lifenjoiner 6d826afac5
Reduce a local variable (#2363) 2023-04-06 14:22:21 +02:00
Frank Denis b341c21dbd Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Bump softprops/action-gh-release (#2357)
  Bump actions/setup-go from 3 to 4 (#2354)
  Update deps
  Format
  Better description for ignore_system_dns
  Move booleans together for alignment, avoid unneeded format string
  Try dnscrypt-proxy to resolve configured hosts when ignore_system_dns (#2204)
  Downgrade to TLS 1.2 if an 1.3-incompatible cipher suite is set
2023-04-06 14:21:15 +02:00
Frank Denis 92ed5b95e0 Try to understand how cache files are updated
Having to keep a copy of all the files in memory is weird.

We shouldn't have to do that.
2023-04-06 14:19:25 +02:00
Frank Denis b898e07066 A source URL may have an IP address that doesn't exist any more 2023-04-06 14:18:38 +02:00
dependabot[bot] 92063aa76d
Bump softprops/action-gh-release (#2357)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from d4e8205d7e959a9107da6396278b2f1f07af0f9b to c9b46fe7aad9f02afd89b12450b780f52dacfb2d.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](d4e8205d7e...c9b46fe7aa)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 21:43:53 +02:00
dependabot[bot] 4be5264529
Bump actions/setup-go from 3 to 4 (#2354)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 21:43:43 +02:00
Frank Denis 13d78c042b Update deps 2023-04-05 21:36:29 +02:00
Frank Denis 36c17eb59a Format 2023-04-05 21:33:21 +02:00
Frank Denis b9f8f78c6e Better description for ignore_system_dns 2023-04-05 21:31:07 +02:00
Frank Denis fc16e3c31c Move booleans together for alignment, avoid unneeded format string 2023-04-05 21:20:42 +02:00
lifenjoiner b3318a94b7
Try dnscrypt-proxy to resolve configured hosts when ignore_system_dns (#2204) 2023-04-05 21:17:51 +02:00
Frank Denis ca0f353087 Downgrade to TLS 1.2 if an 1.3-incompatible cipher suite is set
Fixes #2359
2023-04-05 20:53:27 +02:00
Frank Denis cf7d60a704 Update miekg/dns 2023-03-29 22:00:24 +09:00
Frank Denis a47f7fe750 Update deps 2023-03-23 12:54:45 +01:00
Frank Denis beb002335f Add an example forwarding rule with Tor 2023-03-23 12:53:08 +01:00
Frank Denis 15c87a68a1 Update quic-go 2023-02-25 23:45:38 +01:00
Frank Denis 47e6a56b16 Logger: pre-create log files before lumberjack does
Clunky workaround for https://github.com/natefinch/lumberjack/issues/164
2023-02-25 23:42:38 +01:00
Frank Denis 03c6f92a5f Use crypto_rand() everywhere 2023-02-24 16:20:39 +01:00
lifenjoiner 24a301b1af
Fix DoH3 connections piling up (#2337)
DoH3 creates a new connection for each request without closing.

* `Conn` should be self maintained if it's created by customized `Dial` of `http3.RoundTripper`.
https://pkg.go.dev/github.com/quic-go/quic-go#DialAddrEarlyContext

* http3 doesn't have a `CloseIdleConnections`.
https://pkg.go.dev/net/http#Client.CloseIdleConnections
2023-02-24 16:14:43 +01:00
lifenjoiner a8d1c2fd24
`dlog.SetLogLevel(dlog.SeverityDebug)` if `go test -v` (#2331) 2023-02-21 16:24:11 +01:00
Frank Denis 96ffb21228 Update deps 2023-02-15 18:42:36 +01:00
Frank Denis acc25fcefb Format with gofumpt 2023-02-11 14:27:12 +01:00
Frank Denis 07b4ec33c5 Minor update of x/net and x/crypto 2023-02-09 17:23:48 +01:00
Frank Denis 9f3ef735f2 Bump 2023-02-07 11:03:09 +01:00
Frank Denis d568e43937 Update deps 2023-02-07 11:03:09 +01:00
Frank Denis 68f3ab249c Unbreak cloaking plugin
In version 2.1.3, when the cloaking pluging was enabled, a blocked
response was returned for records that were not A/AAAA/PTR, even
with names that were not in the cloaked list.
2023-02-07 11:03:05 +01:00
dependabot[bot] 2edfdc48b8
Bump roots/issue-closer from 1.1 to 1.2 (#2307)
Bumps [roots/issue-closer](https://github.com/roots/issue-closer) from 1.1 to 1.2.
- [Release notes](https://github.com/roots/issue-closer/releases)
- [Commits](https://github.com/roots/issue-closer/compare/v1.1...v1.2)

---
updated-dependencies:
- dependency-name: roots/issue-closer
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-06 10:46:14 +01:00
Frank Denis 3f31c4d3e2 Update issue-close-message 2023-02-04 22:15:18 +01:00
Frank Denis 84184bbad8 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  GitHub Actions: Deprecating save-state and set-output commands (#2295)
  Nits (#2293)
  Make CodeQL happy (#2294)
2023-02-04 22:11:14 +01:00
Frank Denis f630094e8d Add autocloser 2023-02-04 22:11:05 +01:00
lifenjoiner 3517dec376
GitHub Actions: Deprecating save-state and set-output commands (#2295) 2023-02-03 16:24:32 +01:00
lifenjoiner 683aad75da
Nits (#2293) 2023-02-03 16:23:57 +01:00
lifenjoiner e1c7ea1770
Make CodeQL happy (#2294) 2023-02-03 16:22:32 +01:00
Frank Denis 470460f069 GO386=387 -> GO386=softfloat
Fixes #2296
2023-02-03 16:21:51 +01:00
Frank Denis 8694753866 Bump action-gh-release up 2023-02-02 20:28:37 +01:00
Frank Denis b4b58366cc Update ChangeLog 2023-02-02 20:27:16 +01:00
Frank Denis f7df72eafa Bump to 2.1.3 2023-02-02 20:10:54 +01:00
Frank Denis fb15535282 Format 2023-02-02 20:10:49 +01:00
Frank Denis c32aad3dfd Update GitHub status badge 2023-02-02 20:09:09 +01:00
Frank Denis 9e208e6090 Cloak plugin: reject uncloaked records, except NS & SOA
Fixes #2220
2023-02-02 19:59:47 +01:00
Frank Denis 5f88a9146c Get rid of the latest ioutil bits 2023-02-02 19:44:51 +01:00
Frank Denis 3f23ff5c08 Mostly get rid of ioutil 2023-02-02 19:38:24 +01:00
Frank Denis 33c8027e0a Use a custom dialer for HTTP/3 2023-02-02 19:32:17 +01:00
Frank Denis 11e824bd13 Update go-acl 2023-02-02 12:44:12 +01:00
Deltadroid c3fd855831
Update quic-go dependency to support go 1.20 (#2292) 2023-02-02 12:42:11 +01:00
Frank Denis 5438eed2f4 Update go/x/crypto 2023-01-08 21:37:50 +01:00
Frank Denis a868e2b306 2023 2023-01-05 14:06:00 +01:00
Frank Denis f21eca0764 Add time.google.com IP addresses to the captive portals example 2022-12-30 13:50:31 +01:00
Frank Denis c883949a97 Document cert_ignore_timestamp 2022-12-29 22:39:12 +01:00
Frank Denis e13b4842e8 Updater deps 2022-12-17 14:50:10 +01:00
dependabot[bot] 5705b60936
Bump softprops/action-gh-release (#2247)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from cd28b0f5ee8571b76cfdaa62a30d51d752317477 to 1. This release includes the previously tagged commit.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](cd28b0f5ee...de2c0eb89a)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 11:42:01 +01:00
Frank Denis 5f4dfc5d6e Update quic-go 2022-11-19 23:01:21 +01:00
Frank Denis a89d96144f Update deps 2022-11-16 21:38:52 +01:00
Frank Denis 4aa415de6e Update deps 2022-10-24 10:20:25 +02:00
Frank Denis f4389559ef Pin softprops/action-gh-release 2022-10-24 10:10:58 +02:00
Frank Denis 361455cd58 ServiceManagerReadyNotify is not just for systemd 2022-10-20 15:33:43 +02:00
cobratbq 77059ce450
systemd: report Ready earlier as dnscrypt-proxy can itself manage retries for updates/refreshes (#2225) 2022-10-20 15:32:26 +02:00
Frank Denis 09a6918226 Use os.Geteuid()
Fixes #2224
2022-10-18 14:56:39 +02:00
Frank Denis c748630691 Update deps 2022-10-15 10:37:07 +02:00
Frank Denis 94cba8cf78 Update quic-go 2022-09-26 15:12:20 +02:00
Maurizio Pasquinelli ca253923eb
Rename macOS binary (#2191) 2022-08-31 12:05:35 +02:00
Frank Denis 08d44241b9 Update deps 2022-08-30 20:45:06 +02:00
lifenjoiner 4881186dcf
Optimize adopted relay name to show (#2188)
* Optimize adopted relay name to show

DNSCrypt relay requires ServerAddrStr;
ODoH relay requires ProviderName, port 443 can be either present or not;
raw stamp can be both.

Displaying specified stamp makes it easier to debug.

* Fix pasto
2022-08-25 19:28:04 +02:00
Frank Denis 41f192a907 Mention HTTP/3 2022-08-24 17:35:34 +02:00
Frank Denis 937c1e63e2 Revert "xtransport layer to netip and immediate dependencies (#2159)"
This reverts commit baee50f1dc.
2022-08-10 22:24:36 +02:00
Frank Denis e124623ffc Mention HTTP/3 support 2022-08-03 15:31:57 +02:00
lifenjoiner 55fc4c207b
Log to console when in command mode (#2167)
Quick results.
Avoid overwriting the log file in use, by the same config most of the time.
2022-08-03 14:52:08 +02:00
Ian Bashford baee50f1dc
xtransport layer to netip and immediate dependencies (#2159) 2022-08-01 22:31:12 +02:00
Frank Denis 6e1bc06477 Update quic-go 2022-08-01 17:59:01 +02:00
Frank Denis 8523a92437 Update example to include http3 configuration 2022-07-24 16:16:21 +02:00
Frank Denis 442f2e15cb Make HTTP/3 support configurable 2022-07-24 16:13:14 +02:00
Frank Denis 0c88e2a1a0 ChangeLog update 2022-07-24 15:43:03 +02:00
Frank Denis 35063a1eec Quit dep update before the release 2022-07-24 15:06:14 +02:00
Frank Denis 07266e4d4f Update toml dep 2022-07-21 19:02:28 +02:00
Frank Denis 5977de660b Add suport for DoH over HTTP/3 2022-07-21 18:50:10 +02:00
lifenjoiner 91388b148c
Optimize stopping CaptivePortalHandler - 2 (#2155)
1. Fix early return that triggers port rebinding error by 8e46f447.
2. Reduce waiting time while there are multiple listen_addresses.
2022-07-19 12:35:52 +02:00
lifenjoiner 8e46f44799
Optimize stopping CaptivePortalHandler (#2151)
* Optimize stopping CaptivePortalHandler

* Still use unbuffered channel as we close it instead of sending a signal
2022-07-14 21:53:13 +02:00
Frank Denis 3d641b758a Bump 2022-07-13 18:49:50 +02:00
Frank Denis 49ea894ce8 Update dependencies 2022-07-13 18:48:17 +02:00
lifenjoiner 568f54fabb
Reduce comparisons (#2148) 2022-07-08 14:11:51 +02:00
pc-v2 dc2fff05be
Adding flush dns to clean up previous dns cache (#2141) 2022-07-03 14:13:12 +02:00
Frank Denis 38e87f9a7b Add a constant for the maximum number of attempts 2022-06-28 18:30:15 +02:00
lifenjoiner 0e2bb13254
Fix goroutines memory leak by unbuffered channel blocking (#2136)
* Use buffered channel to avoid goroutine hanging on

A send on an unbuffered channel can proceed if a receiver is ready.

* Balance captivePortalHandler.cancelChannels for Stop
2022-06-28 18:28:57 +02:00
Frank Denis 59ce17e0ab No need to warn if this is then going to be an error 2022-06-24 15:41:05 +02:00
Frank Denis ee5c9d67a4 Update miekg/dns 2022-06-24 15:18:33 +02:00
Frank Denis 8c43118b03 Stop mentioning "SERVFAIL" in info messages 2022-06-19 20:38:49 +02:00
ignoramous 7177a0ec74
dns64: preserve cnames in translated response (#2129)
* dns64: preserve cnames in translated response

* dns64: rename synthAAAAs to synth64
2022-06-16 00:53:50 +02:00
lifenjoiner 72a602577a
Raise error for invalid relay (#2128)
* Raise error for invalid relay

* Keep error messages the same

* Distinguish this from validation failed
2022-06-15 13:16:06 +02:00
lifenjoiner 0a0b69d93d
RUnlock for early exit (#2127) 2022-06-14 14:25:52 +02:00
lifenjoiner 6916c047e1
Use registeredServers slice copy during ServerInfo refreshing period (#2125)
goroutines:
proxy.updateRegisteredServers() versus proxy.serversInfo.refresh(proxy)
2022-06-13 17:51:33 +02:00
ignoramous 8d737a69f5
PluginDNS64: Use read and write mutexes as approp (#2124) 2022-06-12 11:27:55 +02:00
Frank Denis 866954fbad PreferServerCipherSuites has been deprecated 2022-06-11 19:26:26 +02:00
Frank Denis e477d0e126 We may not have a configured IP address 2022-06-11 19:23:58 +02:00
Frank Denis e24fdd2235 Nits 2022-06-07 21:33:50 +02:00
livingentity 74fb5dabb9
fix negative rtt / shorten lines (#2118)
* fix negative rtt / shorten lines

* Update serversInfo.go
2022-05-18 17:57:57 +02:00
Frank Denis 1afd573b0d Add builds for linux/riscv64 2022-05-18 13:31:46 +02:00
Frank Denis c367a82ac0 Bump miekg/dns 2022-05-11 19:38:45 +02:00
dependabot[bot] 9c8c327703
Bump github/codeql-action from 1 to 2 (#2102)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 08:20:41 -07:00
livingentity 207d44323d
Update serversInfo.go (#2092) 2022-04-16 21:26:38 +02:00
Frank Denis 3eac156789 Update burntSushi/toml 2022-04-05 14:05:53 +02:00
Frank Denis 5fca7ea49e Back to VividCortex/ewma 2022-04-05 14:04:26 +02:00
Frank Denis 77dc3b1e85 Update miekg/dns 2022-04-03 23:08:29 +02:00
Frank Denis 66f019d886 Revert "regression: fix ewma warmup again (#2079)"
This reverts commit f67e9cab32.
2022-04-03 23:01:03 +02:00
livingentity f67e9cab32
regression: fix ewma warmup again (#2079)
* Update estimators.go

* Update go.mod

* Update modules.txt

* Update go.sum

* Update serversInfo.go

* Update estimators.go

* Update serversInfo.go
2022-04-02 17:41:36 +02:00
Frank Denis 5d023d2a7c Revert "New feature: sleep mode"
This reverts commit e931b234b7.
2022-04-02 09:33:49 +02:00
Frank Denis e931b234b7 New feature: sleep mode 2022-03-31 20:51:34 +02:00
quindecim ed2c880648
Add sources in [domains-blocklist.conf] (#2039)
* Remove duplicate in [domains-blocklist.conf]

__NextDNS CNAME cloaking list__ is already contained in [your big source](https://github.com/notracking/hosts-blocklists/blob/master/SOURCES.md?plain=1#L47). No reasons to merge it two times.

* Remove more duplicates in [domains-blocklist.conf]

* Revert previous commit

* Add anudeepND and lightswitch05 blocklists
2022-03-29 14:46:46 +02:00
Frank Denis c467e20311 Update deps 2022-03-28 12:35:01 +02:00
Frank Denis df3fb0c9f8 Keep lines short
$ golines -w -m 120 --shorten-comments .
2022-03-23 17:48:48 +01:00
Frank Denis c0435772d4 -resolve: report ECS support
Note that we can't randomize the source network, as Google and
possible others refuse networks that don't get BGP announcements.
2022-03-14 17:04:54 +01:00
Frank Denis 49c17f8e98 -resolve: use TXT records to get resolver information 2022-03-14 16:11:10 +01:00
Frank Denis 0465cd35ef miekg/dns update 2022-03-14 16:02:51 +01:00
dependabot[bot] 08420917a5
Bump actions/setup-go from 2.2.0 to 3 (#2054)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.2.0 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2.2.0...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-07 17:01:40 +01:00
livingentity 87d9653ec2
Remove unused functions (#2057)
They aren't used anywhere.
2022-03-07 17:01:18 +01:00
BigDargon d30c44a6a8
Change bootstrap resolver Quad9 (with ECS) (#2056) 2022-03-02 13:18:20 +01:00
dependabot[bot] 911108149b
Bump actions/checkout from 2 to 3 (#2055)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-02 10:15:29 +01:00
dependabot[bot] a8aa4cb8e6
Bump actions/setup-go from 2.1.5 to 2.2.0 (#2035)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.1.5 to 2.2.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2.1.5...v2.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-21 14:17:33 +01:00
Frank Denis ca076ce133 Size estimator: provide the slide size 2022-02-21 14:16:13 +01:00
Frank Denis 034d3bd424 Switch to lifenjoiner's ewma variant 2022-02-21 09:14:24 +01:00
Frank Denis c08852feb1 Update deps; sync vendor 2022-02-21 09:00:21 +01:00
Frank Denis 9373cc7162 Use SimpleEWMA for the question size estimator 2022-02-20 23:40:32 +01:00
Frank Denis cb140673fa Set the number of warmup samples to 1 for the RTT estimator 2022-02-20 23:38:42 +01:00
Frank Denis 7956ba5b10 Switch to an ewma fork that allows setting the warmup samples # 2022-02-20 23:38:06 +01:00
livingentity 9ec8a35468
restore old logic/constants (#2045)
* fix indices

* Update serversInfo.go

For safety go back to former logic, just generalized for lbStrategy, until someone comes up with an actual improvement.

* restore old logic/constants
2022-02-19 17:55:36 +01:00
livingentity ac6abfb985
LBStrategy-aware estimator (#2043)
* fix estimator

* LBStrategy-aware estimator

* typo

* cosmetics
2022-02-15 20:17:48 +01:00
quindecim a20d1685b2
Another minor cosmetic fix to [example-dnscrypt-proxy.toml] (#2036) 2022-02-10 15:27:53 +01:00
livingentity 62092726ec
Minor cosmetic toml changes (#2034)
* Minor cosmetic toml changes

* Minor cosmetic toml changes
2022-02-10 08:49:04 +01:00
Frank Denis f38a5463b0 Indent comments 2022-02-09 12:57:02 +01:00
quindecim 7a54406415
Use the same format logic throughout the document (#2029)
* Use the same spacing logic throughout the document

* Fix previous commit

* Fix previous commit, again

* Use the same logic in comments too
2022-02-09 12:49:22 +01:00
quindecim bce0405c0a
Add more sources in [domains-blocklist.conf] file (#2031)
[OISD nsfw] in [Block pornography] section

[Developer Dan's Hosts: Dating Services] in [Block dating websites] section
[Developer Dan's Hosts: Facebook] in [Block social media sites] section
2022-02-09 12:47:39 +01:00
quindecim 29a3442306
[FIX] Start to use wildcards urls for OISD lists (#2027) 2022-02-08 13:31:32 +01:00
Frank Denis 8ed98cacae Bump miekg/dns up 2022-02-08 09:26:55 +01:00
Peter Dave Hello ef1c70e87d
Remove README.md "Contribute" link as the target doesn't exist (#2015) 2022-02-03 01:30:10 +01:00
Frank Denis 4c67e790f6 -list command: print ODoH targets addresses 2022-02-01 08:19:46 +01:00
Frank Denis 4eeed5816f Fix funky indentation for CloakedPTR 2022-02-01 08:18:45 +01:00
Frank Denis c10e6e0635 Local DoH: add support for request using the GET method
Fixes #2012
2022-01-31 14:56:46 +01:00
Frank Denis e6089449b6 Update domains-blocklist.conf examples:
- Change KADhosts file to the base one
- Remove CHEF-KOCH and GameIndustry lists that don't exist any more

Reported by @remyabel2 - Thanks!
2022-01-30 21:20:19 +01:00
mibere 706c1ab286
Download mirror dnscrypt.net removed (#2003) 2022-01-24 01:36:30 +01:00
Frank Denis f7e3381650 generate-domains-blocklist: parse names prefixed with `*.` 2022-01-23 00:55:26 +01:00
cobratbq 7a8bd35009
systemd: use constants and update status on ready (#1993)
Systemd-notify signaling indicates the status of dnscrypt-proxy when
starting as 'Type=notify' systemd service. However, the status is not
updated when initialization completes, instead it always shows
"Starting". Now fixed.
2022-01-19 20:30:15 +01:00
Frank Denis 06733f57ed If a relay has multiple names, print the one matching the protocol
Fixes #1992
2022-01-17 19:43:12 +01:00
Frank Denis 4fd26029c7 Update BurntSushi/toml 2022-01-13 09:22:22 +01:00
Frank Denis 351bced7c5 New kardianos/service with support for OpenRC 2022-01-11 16:05:08 +01:00
Frank Denis 916e84e798 2022 2021-12-31 23:56:13 +01:00
ValdikSS 53f3a0e63d
Fix broken odoh link in readme (#1976)
* Fix broken odoh link in readme
2021-12-27 12:30:41 +01:00
Frank Denis 4e9f0382ee miekg/dns update 2021-12-24 09:16:59 +01:00
dependabot[bot] 9121f4f359
Bump actions/setup-go from 2.1.4 to 2.1.5 (#1968)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.1.4 to 2.1.5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2.1.4...v2.1.5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-22 17:59:04 +01:00
Frank Denis e73459f558 Update deps 2021-12-22 14:00:37 +01:00
Frank Denis fbfc2d57a7 omit comparison to bool constant, can be simplified to !cloakedName.isIP
Reported by GitHub's code scanning
2021-12-16 10:43:40 +01:00
Frank Denis b9d6b22ce1 Align 2021-12-13 14:01:13 +01:00
Ian Bashford 1b6caba307
allow ptr queries for cloaked domains (#1958)
* allow ptr queries for cloaked domains

* multi ips per PTR returned + cleanup

* some string tidy up

* enable config file switch

* add cloaked ptr test

* enable cloak ptrs in test scenario

* fix reverse ipv6 ptr lookup

* added ipv6 cloaked ptr test
2021-12-13 14:00:13 +01:00
CNMan 27e93a53cf
minor typo fix (#1951) 2021-11-30 18:26:34 +01:00
Frank Denis 9e7221c31c Update deps 2021-11-05 13:29:16 +01:00
Frank Denis f6f63743ce Update go-minisign 2021-10-28 19:54:01 +02:00
Frank Denis 561e849889 Add a forwarding example for local reverse entries 2021-10-17 15:53:54 +02:00
a1346054 766e149699
Fix typo and alignment in example-dnscrypt-proxy.toml (#1915) 2021-10-10 19:19:45 +02:00
Frank Denis e2ada45598 Update deps 2021-10-09 13:35:18 +02:00
Frank Denis fc0ff3b26a Update ChangeLog 2021-09-27 17:59:16 +02:00
Frank Denis 88b174e5eb Sync deps 2021-09-27 17:49:02 +02:00
Frank Denis e1f3f58eed Bump 2021-09-27 15:51:48 +02:00
Frank Denis efcd392279 StaleResponseTtl -> StaleResponseTTL 2021-09-27 15:47:19 +02:00
Frank Denis 8da1b698ad Revert "Pasto, thanks to @lifenjoiner"
This reverts commit 14ef11447e.
2021-09-27 15:42:54 +02:00
Frank Denis 180e75bfdb Update deps 2021-09-27 12:35:04 +02:00
Frank Denis 77b27d9293 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Make return value explicit
  Repair stale respones for DoH
  Define a constant for the TTL of stale responses
  Update plugin_cache.go (#1900)
2021-09-27 12:31:43 +02:00
Frank Denis 4c29840040 Revert "Print an error if a block/allow rule contains more than a pattern"
This reverts commit 6e8628f796.
2021-09-27 12:29:41 +02:00
Frank Denis b7704a05c5 Make return value explicit 2021-09-25 20:09:29 +02:00
Frank Denis d82021b545 Repair stale respones for DoH 2021-09-25 19:53:43 +02:00
Frank Denis e5608e08cf Define a constant for the TTL of stale responses 2021-09-25 19:53:25 +02:00
livingentity 2a3e59c4bf
Update plugin_cache.go (#1900) 2021-09-25 19:04:17 +02:00
Frank Denis 69019b7a80 generate-domains-blocklist: fix the rx_timed regex
Fixes #1896
2021-09-25 03:10:00 +02:00
Frank Denis 8e913d8bf9 Udate deps 2021-09-24 09:30:49 +02:00
Frank Denis 3bae61dbe1 Properly round the TTL 2021-09-24 09:26:31 +02:00
Frank Denis 5fedbe4c6e // +build -> //go:build 2021-09-23 19:16:26 +02:00
Frank Denis b2f26192e1 gofmt 2021-09-23 19:16:12 +02:00
Frank Denis a4684d3bf5 Round TTLs 2021-09-23 19:10:40 +02:00
Frank Denis 34f0caaa34 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Enable HTTP/2 pings
  Remove leftovers from the daemonize option
  DoH/ODoH: strip optional port number when caching a hostname
  Bump actions/setup-go from 2.1.3 to 2.1.4 (#1843)
  fix minor typo in example config (#1847)
2021-09-23 18:38:50 +02:00
Frank Denis 75e917ae49 plugin_cache: update the response TTL, not the cached data
Fixes #1895
2021-09-23 18:37:40 +02:00
Frank Denis 8fc0ffc35f Enable HTTP/2 pings 2021-09-21 12:57:42 +02:00
Frank Denis 97a983c6b3 Remove leftovers from the daemonize option 2021-09-09 11:26:17 +02:00
Frank Denis 0f00cd27f9 DoH/ODoH: strip optional port number when caching a hostname
Fixes #1861
2021-09-06 12:02:56 +02:00
dependabot[bot] 8178f3419f
Bump actions/setup-go from 2.1.3 to 2.1.4 (#1843)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2.1.3...v2.1.4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-30 16:34:03 +02:00
Alison Winters d8358b795f
fix minor typo in example config (#1847) 2021-08-29 11:11:52 +02:00
Aaron b8c5790716
Add captive portal entry for dual stack setups (#1835)
For users with a dual stack set up at home (IPv4+native IPv6), Windows 21H1 will report that there is no Internet connection if this entry is missing from their captive portals file.

Signed-off-by: Aaron <admin@datahoarder.dev>
2021-08-20 17:32:32 +02:00
a1346054 f6a2d2ea44
Minor cleanup (#1822)
Make github recognize the licence properly
2021-08-18 11:08:18 +02:00
Frank Denis 68b0d87522 Remove comment decorators around license text
Apparently, this is confusing some license checking tool
2021-08-18 11:03:06 +02:00
Frank Denis bb5dc3b1fc Remove trailing blanks 2021-08-18 11:01:38 +02:00
Frank Denis 59b6b8359c Format as single lines 2021-08-17 20:46:01 +02:00
Frank Denis 785f86f50f powerman update 2021-08-17 16:56:09 +02:00
Frank Denis d8c4a90501 The party is over :) 2021-08-15 03:07:19 +02:00
Frank Denis 9cb89ae410 odoh.md has been deprecated 2021-08-14 13:01:12 +02:00
Frank Denis e83cb28ef5 Split ODoH servers and relays 2021-08-14 12:33:10 +02:00
Frank Denis 73dc3dd1d8 Update/merge ChangeLog 2021-08-13 19:27:11 +02:00
Frank Denis 35c82e3dcf Next will be 2.1.0 2021-08-13 19:20:27 +02:00
Frank Denis 9f0b409375 Update deps 2021-08-13 19:19:23 +02:00
Frank Denis 49fdb461d8 ECHO -> ECH 2021-08-12 19:44:33 +02:00
Frank Denis c9d5d81e6a Update toml dependency 2021-08-08 10:21:25 +02:00
Frank Denis 1052fa6323 serve-stale on overflow 2021-08-04 14:30:32 +02:00
Frank Denis c8a61abb79 Update comment 2021-08-04 14:27:58 +02:00
Frank Denis e64425b5e7 On overflow, only respond to cached/synthesized queries 2021-08-04 14:27:24 +02:00
Frank Denis da69583bd2 When we run out of connections, handle an extra one synchronously 2021-08-04 13:35:33 +02:00
Frank Denis d996e3424d No need to get the time if the connection is refused 2021-08-04 13:23:21 +02:00
Frank Denis b4a073f54f Typo 2021-08-03 11:24:16 +02:00
Frank Denis 0ca90dd8cc xtransport: set a default error status code 2021-07-31 13:21:45 +02:00
Frank Denis 026c42424f Workaround for ODoH relays not properly forwarding the status code
Some ODoH relays return a 200 status code even when the upstream
server returns something different. This is an issue after a key
update, where a 401 code is expected.

Handle empty responses with a 200 status code as a response with
a 401 code as a workaround until these relays are fixed.
2021-07-31 12:54:23 +02:00
Frank Denis cedd4f3b54 xtransport: properly forward the status code on error 2021-07-31 12:38:10 +02:00
dependabot[bot] d3b1976208
Bump actions/setup-go from 1 to 2.1.3 (#1773)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 1 to 2.1.3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v1...v2.1.3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-30 20:39:48 +02:00
Frank Denis bc529f0076 Update deps 2021-07-30 20:39:23 +02:00
Frank Denis 796a7f6d31 Add an example for blocking private relay 2021-07-17 14:22:10 +02:00
Frank Denis 03e1916527 🥂 2021-07-16 18:51:03 +02:00
Frank Denis d35c1c3cb2 Lower reject_ttl even more 2021-07-16 16:46:50 +02:00
Frank Denis 8b3b7d38ac Set ttl to reject_ttl for HINFO refused responses
Also lower the example TTL
2021-07-16 16:40:21 +02:00
Frank Denis 9acf257fe5 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Update dependabot.yml (#1770)
2021-07-03 10:57:19 +02:00
Frank Denis 5a091c6da4 Update deps 2021-07-03 10:56:53 +02:00
Jauder Ho 59690da355
Update dependabot.yml (#1770) 2021-07-03 09:42:19 +02:00
Frank Denis f033bb3034 miekg/dns update 2021-06-23 09:45:43 +02:00
Frank Denis 4caa7b6d64 Verbose 2021-06-12 14:48:02 +02:00
Frank Denis 6117a1111c CI: use odoh-crypto-sx which is the only ODoH server yet 2021-06-12 14:21:22 +02:00
Frank Denis 9bea0e8f20 Nits 2021-06-12 14:16:20 +02:00
Frank Denis da9c95ec5c up 2021-06-12 14:08:22 +02:00
Frank Denis b472fb3b21 Bump 2021-06-12 14:03:26 +02:00
Frank Denis 5fb2901dbc Fuzz the stamps parser 2021-06-11 22:20:54 +02:00
Frank Denis ccddb18424 Time to start fuzzing 2021-06-11 22:13:58 +02:00
Frank Denis 1b03ac817e ODoH: supoprt config version 0x0001 in addition to 0xff06 2021-06-11 21:18:05 +02:00
Frank Denis a34258c3aa Nits 2021-06-08 12:07:35 +02:00
Frank Denis 95c9fa75f8 Bump 2021-06-08 11:00:01 +02:00
Frank Denis d6be6f97ea Update ChangeLog 2021-06-08 10:59:26 +02:00
Frank Denis a85a003d2b Filter relays by compatible type before selecting them
Fixes #1739
2021-06-08 10:52:06 +02:00
Frank Denis 5a9a6467df Correctly check for empty/wrong relays 2021-06-08 10:27:03 +02:00
Frank Denis ec581597a2 Require ODoH relays to be present
ODoH target stamps don't include certificate hashes; they are not
meant to be used directly.
2021-06-08 10:19:02 +02:00
Frank Denis 33ed882efe Warn if fallback_resolvers is still in use 2021-06-08 09:53:53 +02:00
Frank Denis b39232e996 this -> that 2021-06-08 01:14:11 +02:00
Frank Denis 9ebb90b22e fallback -> bootstrap 2021-06-08 00:44:06 +02:00
Frank Denis b1dd11be72 Dash 2021-06-07 19:05:15 +02:00
Frank Denis 6076e2dd03 www.msftncsi.com IPs update 2021-06-07 18:47:31 +02:00
Frank Denis a0ecfd6a77 Thanks Chris! 2021-06-07 18:36:37 +02:00
Frank Denis 6c3cec2753 Accept warning for the direct ODoH test 2021-06-07 18:32:20 +02:00
Frank Denis d0e27a1366 Update ChangeLog 2021-06-07 18:25:52 +02:00
Frank Denis d5e9ed3aa9 Don't hardcode the HPKE cipher 2021-06-07 18:16:15 +02:00
Frank Denis 72a354caf9 Update go-hpke-compact 2021-06-07 18:11:58 +02:00
Frank Denis 45d3afc8f9 Call ObliviousDoHQuery() on the initial test 2021-06-07 17:32:34 +02:00
Frank Denis e7f017c592 ODoH: try POST first, even without a relay 2021-06-07 17:19:56 +02:00
Frank Denis 4eab70b770 Indentation matters 2021-06-07 16:44:55 +02:00
Frank Denis 9263d1a01c CI: odoh relays are not servers 2021-06-07 16:15:29 +02:00
Frank Denis f8144f8db6 CI stamp update 2021-06-07 16:13:27 +02:00
Frank Denis 98ab4aee23 Update deps 2021-06-07 15:54:56 +02:00
Frank Denis a2ebe0c4a4 dnscrypt-proxy/odoh.go -> dnscrypt-proxy/oblivious_doh.go 2021-06-07 15:53:11 +02:00
Frank Denis e11b8b0c04 + ODoH 2021-06-07 15:52:52 +02:00
Frank Denis 527c38ebc4 Update CI 2021-06-07 14:27:22 +02:00
Frank Denis 083fa0ad3c Add an extra retry since ODoH servers are currently unstable 2021-06-07 13:49:37 +02:00
Frank Denis f5a69c3bdc Reduce delay 2021-06-07 13:46:44 +02:00
Frank Denis 9e96bbc20b Continue, don't return 2021-06-07 13:44:08 +02:00
Frank Denis a181a23263 Send a dummy initial query before RTT measurement in ODoH 2021-06-07 13:42:33 +02:00
Frank Denis fad415f05a Update example documentation 2021-06-07 13:37:08 +02:00
Frank Denis 29613096da ODoH servers should not require a static configuration 2021-06-07 13:21:58 +02:00
Frank Denis 7980af6f46 Error propagation 2021-06-07 12:38:36 +02:00
Frank Denis 94151f9f96 Use ODoH relays in probes 2021-06-07 12:23:26 +02:00
Frank Denis a11da2d4fb ODoH: check certificate hashes 2021-06-07 12:09:27 +02:00
Frank Denis e0483bbb27 Pretend not to always use the first ODoH config 2021-06-07 12:06:36 +02:00
Frank Denis b35e27bd51 Shuffle ODoH target configs and use different NX queries 2021-06-07 12:05:42 +02:00
Frank Denis 4a4f69edb7 ODoH: only store working configurations
Actually, we only store the first one right now.

We should at least randomize them.
2021-06-07 12:02:21 +02:00
Frank Denis 96b05e57ca Preliminary propoer ODoH initialization 2021-06-07 11:47:11 +02:00
Frank Denis 56f2e9adcc server_name is ignored for x509 certs 2021-06-07 11:27:33 +02:00
Frank Denis dc99f1bc2c If you need this, implement it 2021-06-07 11:26:37 +02:00
Frank Denis 0d81fa2796 Remove doh_client_x509_auth stuf from fetchServerInfo
It doesn't belong there, and that feature doesn't do what it's
documented to do. It sets client certificates globally instead of
doing it per server.
2021-06-07 11:23:48 +02:00
Frank Denis 402860e2a6 ODoH broke DNSCrypt relays with wildcards - repair 2021-06-07 11:06:41 +02:00
Frank Denis cd45f64c18 ODoH: until relay auto selection is implemented, pick random ones 2021-06-07 11:00:21 +02:00
Frank Denis 27a82c54c8 ODoH: handle relay IP addresses 2021-06-07 10:46:01 +02:00
Frank Denis dce4db4c86 Construct net.URL directly 2021-06-07 10:08:55 +02:00
Frank Denis 525927e797 Don't use net/http 2021-06-07 10:05:20 +02:00
Frank Denis 3159bc3191 CI: use odoh-crypto-sx and odohrelay-fastly for testing ODoH 2021-06-06 22:42:33 +02:00
Frank Denis e57d5173e9 Support GET in ODoH targets 2021-06-06 01:22:48 +02:00
Frank Denis f542edacaa ODoH: until detection is in place, without a relay, prefer GET 2021-06-06 01:15:28 +02:00
Frank Denis 92792f0e8b Prevent remotely triggerable crash in ODoH config parser 2021-06-06 01:05:14 +02:00
Frank Denis 1cdb71cd7c Avoid double slashes in ODoH relay URLs 2021-06-06 01:01:39 +02:00
Frank Denis d2947cad75 Unbreak compilation 2021-06-06 00:14:56 +02:00
Frank Denis 3cf5c1ab8e Limit the number of ODoH target configs 2021-06-05 18:35:45 +02:00
Frank Denis 06135b6141 Reduce MaxHTTPBodyLength 2021-06-05 18:29:13 +02:00
Frank Denis 44f3db31ee Just a safeguard 2021-06-05 17:57:48 +02:00
Frank Denis 0a1d3b725c Rename ODoHTarget to ODoHTargetConfig for clarity 2021-06-05 17:49:19 +02:00
Frank Denis 2cf29f9fab CI: check the tests after running them
The ODoH tests don't seem to pass.
2021-06-05 17:04:35 +02:00
Frank Denis e27419f73d x509.SystemCertPool() may fail 2021-06-03 20:59:05 +02:00
Frank Denis ddcc40c954 Hardcode Let's Encrypt ISRG X1 cert
Some operating systems don't include it yet.

Thanks to @rs for the heads up
2021-06-03 12:48:33 +02:00
Frank Denis 14ef11447e Pasto, thanks to @lifenjoiner 2021-05-13 10:30:57 +02:00
Frank Denis 6e8628f796 Print an error if a block/allow rule contains more than a pattern
... and it is not a time range.
2021-05-12 17:43:13 +02:00
Frank Denis 31f4d7aa03 Do not ignore ODoH encryption errors 2021-05-09 16:16:38 +02:00
Frank Denis f9cecd1215 Update miekg/dns 2021-05-07 20:28:25 +02:00
Frank Denis 30779a40a6 Remove sysctl list, which is now updated any more
Fixes #1694
2021-05-01 01:16:35 +02:00
Frank Denis 367b5062ec Add another IP block list 2021-04-30 20:51:22 +02:00
Frank Denis d751781996 Update deps 2021-04-27 14:28:39 +02:00
Frank Denis 58e1410e66 Nits 2021-04-17 16:42:18 +02:00
Frank Denis e2e32406fb Improve ODoH log messages 2021-04-17 16:41:10 +02:00
Christopher Wood 23588733ae
Synchronously update the target configuration upon failure. (#1671)
* Synchronously update the target configuration upon failure.

* Notice a serverInfo failure when key updates fail.

* Add server name to debug logs.
2021-04-17 16:35:55 +02:00
Frank Denis 9759dd90a2 Limit the number of dependabot pull requests 2021-04-14 18:26:38 +02:00
milgradesec 754c2bdb93
Create dependabot.yml (#1670) 2021-04-14 18:25:51 +02:00
Alison Winters eda8dd5181
replace TrimFunc(s, IsSpace) with TrimSpace for ASCII optimization (#1663) 2021-04-05 11:46:57 +02:00
Christopher Wood 03413eae2f
Add ODoH test files. (#1656) 2021-03-30 15:11:09 +02:00
Frank Denis 81692a3a80 Update xsecretbox again 2021-03-30 13:38:50 +02:00
Frank Denis 8213a96cd5 Revert "Remove the need for two chacha20 implementations"
This reverts commit 8e8a4bd024.
2021-03-30 12:29:07 +02:00
Frank Denis cee31b646e Update Go version in CI 2021-03-30 11:57:10 +02:00
Frank Denis 8e8a4bd024 Remove the need for two chacha20 implementations 2021-03-30 11:54:04 +02:00
Frank Denis 3efbacc0d4 Rename 2021-03-30 11:53:59 +02:00
Christopher Wood c748f93752 Add ODoH support. (#1653) 2021-03-30 11:53:51 +02:00
Frank Denis f7219b2bfa Recommend discussions 2021-03-30 10:56:01 +02:00
Frank Denis 09e5812a23 Send info messages to stdout if an output file has been specified
Fixes #1651
2021-03-27 18:39:24 +01:00
Frank Denis 873522c7f7 Add discussions link 2021-03-27 01:57:20 +01:00
Frank Denis 789e5bf162 ... 2021-03-27 01:51:44 +01:00
Frank Denis 795c56f518 Shorten 2021-03-27 01:31:24 +01:00
Frank Denis 4201e7f373 Rename 2021-03-27 01:28:53 +01:00
Frank Denis 1e3f03ab0c ... 2021-03-27 01:19:01 +01:00
Frank Denis 6430ae942c Nits 2021-03-26 09:49:25 +01:00
Frank Denis 76392285e2 Nits 2021-03-26 00:02:54 +01:00
Frank Denis 1ca67270b8 pluralize 2021-03-26 00:01:34 +01:00
Frank Denis a6ea636117 Rename questions to protocol 2021-03-25 23:50:34 +01:00
Frank Denis 0f7208f7ef up 2021-03-25 23:47:55 +01:00
Frank Denis 7d10a2c84d Update issue template 2021-03-25 23:42:10 +01:00
Frank Denis 10ded3d9f2 Update deps 2021-03-22 22:57:07 +01:00
Frank Denis 54d85d7298 Filters don't apply to static entries 2021-03-12 20:05:58 +01:00
Frank Denis 84ea8dc9b0 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Fix domain blocklist config example (#1632)
2021-03-08 15:36:53 +01:00
Frank Denis b1e96b69fd Save 1.4 MiB of memory 2021-03-08 15:36:42 +01:00
synthtech 6bdeaaa70c
Fix domain blocklist config example (#1632) 2021-03-02 01:04:48 +01:00
Frank Denis 09866acdb5 New miekg/dns to fix a resource leak (not affecting us) 2021-02-26 10:38:52 +01:00
Frank Denis 1ae2281588 Update miekg/dns 2021-02-24 15:38:04 +01:00
Frank Denis 34909babfb Typo 2021-02-20 19:11:06 +01:00
Frank Denis bbf4094508 Deps update 2021-02-20 18:55:58 +01:00
Frank Denis 1a82786e07 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  Update codeql-analysis.yml (#1612)
2021-02-20 18:51:20 +01:00
Frank Denis c500287498 Rename fallback_resolvers to bootstrap_resolvers
Clarify what they are used for.

Remove the legacy `fallback_resolver`.
2021-02-20 18:50:42 +01:00
miracle091 1795c7be3a Update codeql-analysis.yml (#1612)
using a custom name for the job and removed a step not more needed

Signed-off-by: Frank Denis <github@pureftpd.org>
2021-02-15 01:17:51 +01:00
miracle091 1b4045a098
Update codeql-analysis.yml (#1612)
using a custom name for the job and removed a step not more needed
2021-02-08 17:17:23 +01:00
Frank Denis b131708cd6 Update miekg/dns 2021-02-02 09:48:11 +01:00
Frank Denis 472ab609e4 Update miekg/dns to v1.1.37 2021-01-30 19:29:12 +01:00
Frank Denis 6517bae0c3 Disable full hosts.oisd.nl, replace with the shorter version 2021-01-29 16:20:32 +01:00
Frank Denis 5d6b35213c Add the file name ;) 2021-01-22 18:39:04 +01:00
Frank Denis 96ba551836 Revert "The source tests are completely brok4n :("
This reverts commit a76ffb0143.
2021-01-22 17:50:01 +01:00
Frank Denis d8ff82cf45 IP addresses of queries over local DoH are now logged 2021-01-22 16:13:56 +01:00
lifenjoiner a9cf16b33e
Fix: Randomize source URLs (#1593) 2021-01-22 15:06:49 +01:00
Frank Denis 0ab9e30fa9 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  The source tests are completely brok4n :(
  Explain how to listen to all IP addresses
  In the query log, consider everything that's not UDP as TCP
2021-01-22 09:16:56 +01:00
Frank Denis d0f981156b Add the base inherited fd to the application logging fd
Fixes #1585
2021-01-22 09:15:40 +01:00
Frank Denis a76ffb0143 The source tests are completely brok4n :(
Fix at least the fact that URLs are now randomized
2021-01-21 14:59:34 +01:00
Frank Denis 53c8e25352 Explain how to listen to all IP addresses
Fixes #1588
2021-01-21 14:38:36 +01:00
Frank Denis ac0b9cdfe8 In the query log, consider everything that's not UDP as TCP
Fixes #1589
2021-01-21 14:35:06 +01:00
Frank Denis 213695f651 Update deps 2021-01-19 23:05:16 +01:00
Ian Bashford fcd9225121
Threadsafe update (#1579)
* threadsafe update for relays

* locks around registeredRelays
2021-01-09 22:44:32 +01:00
Frank Denis 85d268f2b9 Randomize source URLs
Fixes #1577
2021-01-04 16:41:39 +01:00
Frank Denis 19dbd13c1b Explain the example allowlist 2021-01-03 18:18:46 +01:00
Frank Denis 3b61f50fa9 Add NOT_READY 2021-01-03 18:12:30 +01:00
Frank Denis daa1f3d3b1 Add a NOT_READY return code 2021-01-03 18:09:03 +01:00
Frank Denis f9ec0a9c09 Deep copy cached responses 2021-01-03 17:37:19 +01:00
Frank Denis 3a5585f8a1 Remove test leftover 2021-01-03 17:16:04 +01:00
Frank Denis 1f7b247138 Lower severity 2021-01-03 17:00:39 +01:00
Frank Denis bc42eda1c8 Shorten 2021-01-03 16:58:21 +01:00
Frank Denis 5c3db0dcf5 Try to rely on proxy.serversInfo rather than proxy.registeredServers
With the introduction of background updates, I'm a little bit worried
about race conditions that can happen when a new server or relay is
registered (or even when a stamp changes).
2021-01-03 16:40:38 +01:00
Frank Denis fbd598f027 Nits 2021-01-03 16:22:23 +01:00
Frank Denis 00abe06676 up 2021-01-03 16:03:29 +01:00
Frank Denis 197f13ea0f Fix typo and update message 2021-01-03 16:00:02 +01:00
Frank Denis 5861a58089 Nits 2021-01-03 14:44:02 +01:00
Frank Denis 7c6f0823ea Doc update 2021-01-03 14:41:23 +01:00
Frank Denis 7b962dff98 Nits 2021-01-03 13:58:08 +01:00
Frank Denis 5a079a3eb9 Resolve: print host info 2021-01-03 13:49:43 +01:00
Frank Denis 1e10251407 Only find the farthest route on wildcards 2021-01-03 13:33:51 +01:00
Frank Denis 0f54b2b34c Automatic relay selection 2021-01-03 13:01:44 +01:00
Frank Denis 79cb9451bd Remove log messages that are not really needed 2021-01-02 22:59:21 +01:00
Frank Denis 662b4c0c62 Make staticcheck happier 2021-01-02 22:55:16 +01:00
Frank Denis 9e4a2fc6e1 Format 2021-01-02 22:38:26 +01:00
Frank Denis af80f57a58 Increase timeouts on retry 2021-01-02 22:31:47 +01:00
Frank Denis 996ea0dd89 Don't print the whole response 2021-01-02 22:28:00 +01:00
mibere 1ceb2b1fbd
New download mirror (#1570) 2021-01-02 22:25:41 +01:00
glitsj16 8a9e61d6cd
Fix typo (#1571) 2021-01-02 22:24:11 +01:00
Frank Denis fc82a6c05e Revamp dnscrypt-proxy -resolve 2021-01-02 22:20:52 +01:00
Frank Denis a584effbe9 Remove HTTPS record creation 2021-01-02 19:05:18 +01:00
Frank Denis 7ec5ed127e Repair server randomization 2021-01-02 19:04:53 +01:00
Frank Denis 5398dab58e Lower log level 2021-01-02 17:04:59 +01:00
Frank Denis 93733971a9 Reformat ChangeLog 2021-01-02 16:36:06 +01:00
Frank Denis 930dcd2f8e Update ChangeLog 2021-01-02 16:33:49 +01:00
Frank Denis 8f0b38f4c0 Double comments 2021-01-02 15:37:41 +01:00
Frank Denis 9f5c034c3d Add staticcheck.conf 2021-01-02 15:36:30 +01:00
Frank Denis d2517bd42e Update .gitignore 2021-01-02 15:23:08 +01:00
Frank Denis ee5711fbd6 Disable captive portals by default 2021-01-02 15:22:58 +01:00
Frank Denis 56acb7b5ab Log when the ECS plugin is enabled 2021-01-02 15:10:30 +01:00
Frank Denis a713e1a517 Move captive portals config to a dedicated section
Add examples
2021-01-02 15:10:04 +01:00
Frank Denis dfee2aa546 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  add tests for statics (#1569)
2021-01-02 13:20:54 +01:00
Ian Bashford 7afc43ab35
add tests for statics (#1569) 2021-01-02 13:11:26 +01:00
Frank Denis 45829aaa45 Update deps 2021-01-02 12:53:28 +01:00
Frank Denis 3b18058ae5 Add IPv6 cleanbrowsing servers 2021-01-02 12:53:10 +01:00
Ian Bashford 5b8c9c495f
register servers after loading statics (#1568) 2021-01-02 11:57:18 +01:00
Frank Denis b8d17debfd Remove final stops from errors 2021-01-02 11:16:12 +01:00
Frank Denis 2cdafa4bb3 Remove debug leftover 2021-01-02 10:24:32 +01:00
Frank Denis f245189f02 Handle captive portal names after coldstart 2021-01-01 21:39:17 +01:00
Frank Denis c308727d15 Add unsigned macOS packages 2021-01-01 17:55:29 +01:00
Ian Bashford 87fb44a588
Run from in memory cache updates (#1564)
* ConfigFile change to allowlist and blocklist

* revised names and warnings

* consistent file naming in kebab case, and generic use of blocklist and allowlist in cmoments for clarity

* update ci files

* impose maximum delay and document

* live update of servers

* update for source prefixes

* fixup test

* stop registerServers being called twice at startup

* prevent double registration at startup

* tidy function signature for loadSource

Co-authored-by: Ian Bashford <ianbashford@gmail.com>
2021-01-01 14:04:12 +01:00
Frank Denis f19b14e74c Replace the logo with a version that looks better on a dark background
Contributed by @jacob755, thanks!

Closes #1541
2021-01-01 14:01:14 +01:00
Frank Denis 3087eff6d3 CI: update Go 2021-01-01 13:55:14 +01:00
Frank Denis e40e42edf5 Update deps 2021-01-01 13:31:07 +01:00
Frank Denis e519dd17bc 2021 2021-01-01 00:01:59 +01:00
Frank Denis 06cf3a1311 Remove another defunct blocklist source
Fixes #1566
2020-12-31 23:46:06 +01:00
Frank Denis a3ef181e4f Update deps 2020-12-26 18:06:52 +01:00
Frank Denis 254a4a6532 Use , not | to match multiples items
Fixes #1558
2020-12-26 17:55:31 +01:00
Frank Denis 2baabbca8a Ignore regexes in time-based entries
Fixes #1548
2020-12-20 13:49:08 +01:00
Frank Denis 859710d6f5 RIP malwaredomains and malwaredomainlist
Fixes #1549
2020-12-19 16:39:49 +01:00
Frank Denis c17461ed42 generate-domains-blocklist: use the same name for the directory and the tool 2020-12-18 21:27:33 +01:00
Frank Denis 77f81cc8c2 Add recommendation for fallback resolvers in the example config
This is the same recommendation as c4d9860577/dnscrypt-proxy/serversInfo.go (L429-L432)

that has been here for a while as a comment, but having it in the configuration
file gives it more visibility.
2020-12-17 11:10:35 +01:00
Frank Denis c4d9860577 cloak plugin: return multiple the entire set of IPv4 or IPv6 addresses
Fixes #1547
2020-12-17 09:47:44 +01:00
Frank Denis a8a0677ea9 h1 -> http/1.x 2020-12-17 01:13:11 +01:00
Frank Denis 7d851366bb Do not only warn if the protocol is empty 2020-12-17 01:08:06 +01:00
Frank Denis 85e7dddc9b Move a few DNS things to dnsutils 2020-12-12 23:09:15 +01:00
Frank Denis a24b009667 Filler 2020-12-12 22:35:51 +01:00
Frank Denis d700ab6085 Nits 2020-12-12 22:19:09 +01:00
Frank Denis a384011e71 Support relays in static entries 2020-12-12 21:57:04 +01:00
Frank Denis 7f46f4820c Don't use distinct pointers for UDP and TCP relay addresses 2020-12-12 21:18:32 +01:00
Frank Denis ab8ebead34 Remove support for {ip|host}[:port] syntax for specifying a relay
It's very likely that no one ever used it.
2020-12-12 20:46:40 +01:00
Frank Denis 70bffc73f1 go-dnsstamps update 2020-12-11 14:14:09 +01:00
Frank Denis fc785f9f69 Print details when an unsupported protocol is found 2020-12-11 12:26:05 +01:00
Frank Denis d6d8c37ef6 Format 2020-12-11 12:25:57 +01:00
Frank Denis 54b0a5a18d go-dnsstamps update 2020-12-10 22:57:31 +01:00
Frank Denis 3ae3fe2f18 x/crypto update 2020-12-10 19:00:57 +01:00
Frank Denis 0d260d0e2d pattern_matcher: check exact matches first 2020-12-07 12:58:05 +01:00
Frank Denis 1239e64cd9 Correctly check for HTTPS type 2020-12-01 16:08:33 +01:00
Frank Denis b7dfdb1372 Factorize 2020-12-01 16:08:10 +01:00
Frank Denis 24a9539d08 Filter names on SVCB and HTTPS records in addition to CNAME 2020-12-01 16:00:18 +01:00
Frank Denis 5c1e3f0b15 Update deps 2020-12-01 14:46:14 +01:00
Frank Denis 9c50963f69 Add Captain Miao ad list, whitelist mobiledl.adobe.com 2020-12-01 08:46:15 +01:00
Frank Denis 01e60ab31b Add localhost to the allowlist 2020-12-01 08:37:03 +01:00
Frank Denis df8cfe3f3c dnsdist has been fixed 2020-11-30 14:31:30 +01:00
mibere f5827520d8
download mirror download.dnscrypt.net (#1527)
Files are locally hosted on download.dnscrypt.net. A cronjob updates the files every 3 hours, source is https://download.dnscrypt.info
download.dnscrypt.net has IPv4 and IPv6, DNSSEC, HTTPS
2020-11-27 22:35:27 +01:00
Frank Denis f9c11f0897 Allow arbitrary addresses to be set in listen_addresses
Only works on OpenBSD/FreeBSD/Linux (including Android)

Fixes #1362
2020-11-25 19:23:30 +01:00
Frank Denis 02a6ca1098 Keep .home in forwarding rules 2020-11-25 01:39:11 +01:00
petercooperjr 715c32f0fc
Change example forwarding rule to match recommended .home.arpa TLD (#1523)
The ".home" TLD was proposed at one point, and while it's probably not going to actually ever get delegated it's not best practice to just start using your own TLD. The .home.arpa domain has been specifically set aside for use in home networks (RFC 8375) and is probably the better example to put here.
2020-11-25 01:38:14 +01:00
Frank Denis 9e4131c6f7 Add ipv6.download.dnscrypt.info for testing 2020-11-23 21:10:22 +01:00
Frank Denis cae3719464 CI: verify that queries sent over local DoH are properly logged 2020-11-18 20:22:16 +01:00
Frank Denis 7e3e9aa5d2 New version of kardianos/service 2020-11-18 10:19:58 +01:00
Ian Bashford 90a9a9d992
allowed ips plugin (#1510) 2020-11-15 20:59:58 +01:00
Frank Denis 6b6c6753aa Revert struct packing changes for the configuration
structlop is nice, but strips renames
2020-11-14 15:34:03 +01:00
Frank Denis 4fa643ef4d Repack structures to save some memory 2020-11-14 14:46:59 +01:00
Frank Denis e6fdb08d3d Update deps 2020-11-06 07:43:29 +01:00
lifenjoiner 078f69357e
Update example-dnscrypt-proxy.toml (#1489)
* Update lb_strategy usage

* Update example-dnscrypt-proxy.toml
2020-10-21 14:21:39 +02:00
Frank Denis 6ee164a3c9 Update miekg/dns and other dependencies 2020-10-19 16:46:12 +02:00
Frank Denis 7a03369d01 Debug log certificate TTL 2020-10-12 17:58:08 +02:00
Frank Denis d0674ef4d2 Cleanup go.sum 2020-10-12 10:38:44 +02:00
Frank Denis 2b826bbb64 Update deps 2020-10-12 10:35:37 +02:00
Ian Bashford f2700874fd
user-friendly comments - follow up to #1412 (#1486) 2020-10-04 21:05:24 +02:00
Frank Denis 7b7107902b Update deps 2020-09-21 02:15:51 +02:00
Frank Denis 8b72e58656 Make key exchange behaviors consistent 2020-09-21 02:14:17 +02:00
Frank Denis e54056bc38 Update deps 2020-09-18 18:19:55 +02:00
Frank Denis 687fe27371 Nits 2020-09-18 00:14:50 +02:00
Frank Denis 272984a640 Add support for EDNS-client-subnet
Fixes #1471
2020-09-18 00:11:26 +02:00
Frank Denis 4d7f253e6b Don't spawn new connections if we are full 2020-09-17 00:49:49 +02:00
Frank Denis 8411e5a91b Revert "Error out if the dns64 plugin is enabled without listening sockets"
This reverts commit b02649f774.
2020-09-17 00:45:48 +02:00
Frank Denis 4eab88c017 plugin_dns64: don't send queries to self
Fixes #1477
2020-09-17 00:44:37 +02:00
Frank Denis b460ca9fa8 Simplify hasAAAAQuestion 2020-09-17 00:24:04 +02:00
Frank Denis b02649f774 Error out if the dns64 plugin is enabled without listening sockets 2020-09-17 00:19:00 +02:00
Frank Denis c74b993cbb dns64: check the original question, not the returned one 2020-09-17 00:10:11 +02:00
Frank Denis 26505ab560 Merge declaration and assignment 2020-09-13 20:24:06 +02:00
Frank Denis 018d8412be Format generate-domains-blocklist.py with Black 2020-09-12 23:34:39 +02:00
Frank Denis 5a1b87130d Use single quotes for strings
Fixes #1466
2020-09-03 21:21:05 +02:00
Ian Bashford a510b97d86
Update to generate-domains-blocklist.py (#1412)
* ConfigFile change to allowlist and blocklist

* revised names and warnings

* consistent file naming in kebab case, and generic use of blocklist and allowlist in cmoments for clarity

* update ci files

* message about deprecation of -w

Co-authored-by: Ian Bashford <ianbashford@gmail.com>
2020-09-01 23:05:25 +02:00
Frank Denis d175642df3 Quad9 seems to have upgraded their dnsdist version! 2020-08-31 17:13:14 +02:00
Frank Denis f678f39535 CHEF-KOCH lists have moved to Gitlab 2020-08-31 16:06:30 +02:00
Frank Denis d5c3c6747e Revert "CHEF-KOCH is not on GitHub any more :("
This reverts commit b448324e1a.
2020-08-31 16:05:51 +02:00
Frank Denis b448324e1a CHEF-KOCH is not on GitHub any more :(
Fixes #1462
2020-08-30 17:16:49 +02:00
IceCodeNew fd98ced18d
fix `bblck.me` domain not exist error (#1447) 2020-08-15 08:58:59 +02:00
Frank Denis fa5c55c64a Debug log query names 2020-08-09 13:09:37 +02:00
Frank Denis dadb38c32e Lower severity 2020-08-05 15:50:48 +02:00
Frank Denis 0ac96fec30 Add some logging back to fetchDoHServerInfo() 2020-08-05 15:39:30 +02:00
Frank Denis b583fb5314 Turns out that the "test." zone is directly served by the Tencent CDN
without hitting the actual resolvers.

So, we need to use a different test zone.
2020-08-05 15:03:16 +02:00
Frank Denis f3157b0a42 Check DoH servers with a query to a random name
The issue with benchmarking DoH servers is that some responses can
be directly served by a CDN, while others require a round trip to
the origin that can be significantly more expensive.

Random padding was an attempt at mitigating this. Unfortunately,
some servers (Tencent) ignore the padding. We end up with a query
for the root zone served by the Tencent CDN very quickly, but
anything else is orders of magnitude slower.

So, measure a query within the reserved "test." zone instead.
Caching resolvers should either know that "test." is undelegated,
or have it in their negative cache already, so this is unlikely to
trigger an actual query to authoritative servers.

Take it as an opportunity to check that we don't get anything but
a NXDOMAIN response for nonexistent domains.
2020-08-05 14:54:14 +02:00
Frank Denis 60d4c98f31 Unbreak running without a captive portal configuration file 2020-08-04 00:50:59 +02:00
Frank Denis 5843e49188 Sync vendor 2020-08-03 18:23:57 +02:00
dependabot-preview[bot] 1ed325b5c5
Bump github.com/miekg/dns from 1.1.30 to 1.1.31 (#1432)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.30 to 1.1.31.
- [Release notes](https://github.com/miekg/dns/releases)
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.30...v1.1.31)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-03 18:23:09 +02:00
Frank Denis f7f84fd871 Add ipv4only.arpa 2020-08-03 18:20:12 +02:00
Frank Denis 4424602e39 Start experimenting with better support for captive portals
MacOS (and probably Windows and other systems) tries to fetch a URL
before marking a network interface as available.

During this time, applications cannot use the interface at all, not
even bind their address.

When DNS queries are sent to dnscrypt-proxy, this causes the system
to wait for a response that can't come from the network, since we
hit a dead lock here.

The only option is to return hard-coded responses directly until
te interface is available.

The same captive portal configuration file can also serve a different
purpose.

Once the network is available, captive portal detection may not
work as expected if the answer is cached for too long. In fact, it
probably can't work at all since routers can't hijack DNS queries.

Once thing we can do is redirect the list of names used for captive
portal detection to the fallback resolvers. This may allow detection
to work as expected while still using a secure channel for all
other queries.
2020-08-03 18:05:42 +02:00
Frank Denis 210ba8c60f coldstart experiment 2020-08-03 15:40:39 +02:00
Frank Denis 1c52451025 Minor deps update 2020-07-31 16:01:41 +02:00
Frank Denis 162b51c791 Remove confusing "Domain exists: probably not, or blocked by the proxy" 2020-07-30 19:25:17 +02:00
Alison Winters 617629c180
initialize the log file before reporting config errors (#1426)
* initialize the log file before reporting config errors

* consistently return error instead of calling dlog.Fatal when parsing config
2020-07-27 16:01:44 +02:00
Frank Denis d3ff3a6bb1 Remove facebookgo/{atomicfile,pidfile}
Fixes #1411
2020-07-10 14:37:35 +02:00
Frank Denis 1a34c8d5ff Add max-stale cache control directive to requests 2020-07-09 21:42:35 +02:00
Frank Denis 8dd4612ea7 Don't use Lumberjack for non-regular files
Fixes #1407
2020-07-08 13:48:04 +02:00
Frank Denis 04b49fd355 Rename the generate-domains-blacklists folder
This is going to break all the scripts using this in a cron job
after an update :/
2020-07-08 12:07:23 +02:00
Frank Denis 77a27a46a4 Rename the python script name in the example config 2020-07-08 12:05:42 +02:00
Ian Bashford af564522ec
Further block/allow updates (#1406)
* ConfigFile change to allowlist and blocklist

* revised names and warnings

* consistent file naming in kebab case, and generic use of blocklist and allowlist in cmoments for clarity

* update ci files

* further allow/blocklist updates

* improve language in comments

Co-authored-by: Ian Bashford <ianbashford@gmail.com>
2020-07-08 12:01:06 +02:00
Frank Denis 10710def50 Make loggers io.Writer implementations, not directly lumberjack objects 2020-07-08 11:36:58 +02:00
Frank Denis 7bec554709 Remove fritz.box after all 2020-07-08 11:03:45 +02:00
Frank Denis 45b915882a Update deps 2020-07-07 14:12:02 +02:00
dependabot-preview[bot] 29c2b76edd
Bump github.com/miekg/dns from 1.1.29 to 1.1.30 (#1403)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.29 to 1.1.30.
- [Release notes](https://github.com/miekg/dns/releases)
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.29...v1.1.30)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-07-07 13:27:53 +02:00
hugepants 038ebea0ed
Update broken_implementations with Quad9 -pri suffix (#1398) 2020-07-03 15:28:09 +02:00
hugepants 63c8f0610f
Update broken_implementations list with updated Quad9 v3 names (#1390) 2020-07-03 14:05:39 +02:00
Frank Denis 9bc5bb0e14 Clarify 2020-07-03 13:03:57 +02:00
Frank Denis 90df0292c8 Remove unneeded brackets 2020-07-03 12:59:51 +02:00
yofiji 7a6f1461f8
Add option to go direct for failed certificate retrieval via relay (#1397)
* Add option to go direct for failed certificate retrieval via relay

* add direct_cert_fallback to example config file

Co-authored-by: yofiji <you@example.com>
2020-07-03 12:58:36 +02:00
Frank Denis 5e2f1c4146 Clarify that skipAnonIncompatbibleResolvers does what it says 2020-07-02 13:45:19 +02:00
Frank Denis ece0c76172 Add fritz.box IP to the cloaking rules example
Fixes #1392
2020-07-01 09:20:44 +02:00
Krish De Souza 7b1ccd1053
Issue #1380: Reenable HTTP/2 for local DoH (#1384)
+Updated ci-test number 25 looking for invalid 404 to reflect changes here
2020-06-28 18:20:20 +02:00
Ian Bashford b089d49d25
ConfigFile change to allowlist and blocklist (#1375)
* ConfigFile change to allowlist and blocklist

* revised names and warnings

* consistent file naming in kebab case, and generic use of blocklist and allowlist in cmoments for clarity

* update ci files

Co-authored-by: Ian Bashford <ianbashford@gmail.com>
2020-06-26 23:18:30 +02:00
hugepants 19c0c3f7db
Add forward slashes to example stamp for consistency (#1388)
Seems to work with or without, but makes it consistent with the toml, the documentation and the stamp calculator.
2020-06-26 17:36:15 +02:00
Frank Denis 8935fa454a v2 -> v3 2020-06-21 22:20:34 +02:00
Frank Denis 239b00b624 Add ShiftLeft scan badge 2020-06-20 19:38:58 +02:00
Frank Denis 80942eb231 Don't forget Linux 2020-06-19 21:43:45 +02:00
Frank Denis 55ce158e37 Do we need to duplicate descriptors twice? 2020-06-19 21:42:20 +02:00
Frank Denis 539924f85f Downgrade x/text to a single version 2020-06-19 20:56:21 +02:00
Frank Denis 1124b8304e Remove dependency 2020-06-19 20:16:37 +02:00
Frank Denis 80dfffc4ee Unbreak CI 2020-06-19 20:16:21 +02:00
Frank Denis 03746b76bf Capitalize 2020-06-19 11:39:44 +02:00
IceCodeNew c8d099735b
Disable Sysctl list and AdAway, introduce GameIndustry.eu; Remove Chinese IT companies' top domain from whitelist. (#1365)
* Disable Sysctl list, Introduce GameIndustry.eu

* The host file from http://sysctl.org/cameleon/ is no longer updated, therefore it should be disabled.

* Introduce a new rule maintained by GameIndustry.eu. I only pick the rule sets that NextDNS provides to its customers of their choice, as these rule sets are generally seen as stable and reliable.
However I don't play game so much, there is no way to perform a fully test on my side. There is no FP detected during the couple of days while I using this rule set. And I've gone through the entire contents of the host file in roughly, the entries all seem reasonable to me.

* Disable rule set from AdAway by default

~~It doesn't take long for jedisct1 add baidu.com, and 163.com into whitelist after I introduced this rule into the configuration file, so I guess that the AdAway rule set must have presented a lot of false positives.~~
~~However, these Chinese IT companies are notorious for their extensive user-tracking tactics. Whitelist their top domain may not a good idea.~~
~~My suggestion is to simply disable the ruleset present FP, and let software like ABP or AdGuard do the most elaborate work. - Blocking on the DNS level has its limitations.~~
2020-06-19 00:32:01 +02:00
Prabhu Subramanian 6ee8a14deb
Add shiftleft scan (#1372)
* Create shiftleft-analysis.yml

* Remove comments

Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com>
2020-06-19 00:30:59 +02:00
Frank Denis 6235c11c77 When forking, relocate descriptors higher up
Channels used by the `services` module may use descriptors, so we don't
want to overwrite them.

Maybe
fixes #1371
2020-06-19 00:04:54 +02:00
Frank Denis 6dc484c177 Update deps 2020-06-18 23:19:28 +02:00
Frank Denis e1d6e27a8a Add CodeQL scan result 2020-06-15 20:25:52 +02:00
Frank Denis 5f3b568de3
Add CodeQL scan 2020-06-15 20:02:22 +02:00
Frank Denis 703059922f Easylist has false positives 2020-06-14 15:25:45 +02:00
Frank Denis 06705a6d14 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Bump
2020-06-12 14:24:31 +02:00
Frank Denis 5b24439f99 Why the heck is Energized BLU blocking VK? 2020-06-12 14:24:11 +02:00
Frank Denis 65f42918a1 Bump 2020-06-11 17:10:33 +02:00
Frank Denis c59caf3a63 Try oisd list by default, mainly because it tries to avoid FPs 2020-06-11 13:16:50 +02:00
Frank Denis 576162d9bf Remove CodeQL/ShiftLeft until they are enabled for the org 2020-06-11 11:46:17 +02:00
Frank Denis d55421df96 Don't bind listening sockets with the -list/-list-all options
Fixes https://github.com/Homebrew/homebrew-core/pull/55998
2020-06-11 11:41:17 +02:00
Frank Denis 9cce77cc53 No need to import the dnsstamps package twice 2020-06-11 11:13:41 +02:00
Frank Denis 4f47cd0f4f Avoid implicit memory aliasing in for loop 2020-06-11 11:10:33 +02:00
Frank Denis de6afd5a4c Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Create shiftleft-analysis.yml
  Create codeql-analysis.yml
  Revert "Fix unit tests on Win10 (attempts 1 and 2)"
  sources_test: set bit 16 of the port instead of adding zeros (#1358)
  Fix unit tests on Win10 (attempt 2)
2020-06-11 11:03:30 +02:00
Frank Denis 9f9a17ed6b doh_client_x509_auth: don't ignore errors 2020-06-11 11:03:17 +02:00
Frank Denis 0fd0a1a939
Create shiftleft-analysis.yml 2020-06-11 10:55:08 +02:00
Frank Denis 8a99b3ed93
Create codeql-analysis.yml 2020-06-11 10:53:45 +02:00
William Elwood 2018945fdf Revert "Fix unit tests on Win10 (attempts 1 and 2)"
This reverts commit 92dda0d55a.
This reverts commit 5a1fdc8cd6.
2020-06-10 19:45:11 +01:00
Frank Denis f4d519092b
sources_test: set bit 16 of the port instead of adding zeros (#1358)
Ok @welwood08
2020-06-10 20:24:41 +02:00
William Elwood 92dda0d55a Fix unit tests on Win10 (attempt 2)
Thanks to @lifenjoiner for testing! Windows 10 behaves even more unexpectedly.
After it parses the "ip:port" string as a hostname, it attempts to upgrade from
http to https by appending `:443` and parsing that new URL again.
This seems to happen concurrently with the doomed DNS lookup and we see the
error from whichever fails first.
2020-06-10 12:10:51 +01:00
Frank Denis 8c42c465b2 Be even more explicit 2020-06-10 11:38:46 +02:00
Frank Denis 5416891056 Temporarily parse [tls_client_auth] for backward compatibility
Document the change.

Fixes #1355
2020-06-10 11:37:03 +02:00
Frank Denis d7f16f6be4 Uncomment sections for consistency 2020-06-10 11:04:50 +02:00
Frank Denis adcdcffdec Skip netprobe & listeners when -show-cert or -check are used
Fixes #1354
2020-06-10 11:01:59 +02:00
Huhni c07ed55b16
update domains-blacklist.conf (#1353)
* remove isc.sans.edu lists

It says "Service Suspended" when opening these links

* change Peter Lowe's list to domains only

There is no need to manually strip away all the 127.0.0.1 at the beginning of each line if there is already a method for domains only provided on the website. Could also be modified to ignore old entries with `&startdate%5Byear%5D=2015`.
Adding `&mimetype=plaintext` doesn't seem to change anything for `hostformat=nohtml`, but could be added as well.

* Remove lists intended for adblockers

The Adblock Warning Removal List currently has 559 lines, only two of which are actually useable for dnscypt-proxy (adscat.ru, shellcat.ru).
Fanboy Social currently has 20162 lines and only 118 lines can be used, which is about 0.6%.
CJX Annoyance List: 512 lines, 19 lines usable, but it's just a lite version of the already included Easylists.
Prebake: 1160 lines, 4 lines usable (also not updated since May 2018)

Most of the remaining domains should be covered by a larger domains blocklist, such as Energized BLU, therefore I think it's safe to remove them.

* remove lists included in Energized Blu

Since Energized Blu is enabled by default, there is no need to also enable lists by default that are already contained in it.

Energized Blu contains the following sources:
1hosts, add.2o7Net, add.Dead, Risk & Spam, Adguard Filters, Ador, Anti-PopAds, Coin Blocker, Disconnectme Ads, Malware & Malvertising, EasyPrivacy Specific, hBlock, Lightswitch Ads & Tracking, Spam404, KADhosts, MoaAB, MobileAdTrackers, No Tracking, NSABlocklist, someonewhocares, StevenBlack, Wally3K_Blacklist & Zeus Tracker
2020-06-09 17:43:15 +02:00
William Elwood 5a1fdc8cd6 Fix unit tests on Win10
Untested attempt to fix unit tests that fail on Windows 10 build 1909.
From the test output mentioned in #1332, it looks like this version of Windows
doesn't report an "invalid port" error when asked to connect to an invalid port,
instead it treats the port as part of the host name and attempts a DNS lookup.
Naturally, this fails because the colon character is not valid in a host name.
This change simply makes this inexplicable error an expected result since the
outcome is the same and we can't fix Windows.
2020-06-09 15:51:23 +01:00
Frank Denis f9268be0c2 Recompute deps 2020-06-09 12:09:51 +02:00
Frank Denis 08e9dfe46e Nits 2020-06-09 09:55:40 +02:00
Frank Denis 506f727f1f Another place worth force GC'ing 2020-06-09 09:52:59 +02:00
Frank Denis b794d47a76 Force GC where it seems to matter most 2020-06-09 09:42:09 +02:00
Frank Denis 8200616655 dlog update 2020-06-08 22:52:07 +02:00
Frank Denis 8945cb1b90 Add log_file_latest 2020-06-08 22:31:03 +02:00
Frank Denis 9f9318701f Update dlog 2020-06-08 20:33:18 +02:00
Frank Denis 87c161ab76 Clarify what log_file is 2020-06-08 20:07:24 +02:00
Frank Denis e9227daf6a Update deps 2020-06-08 19:22:36 +02:00
Frank Denis 9c5cf611a4 Preliminary ChangeLog 2020-06-08 19:20:55 +02:00
Frank Denis b32ffbb807 Discourage from blindly using dns64 2020-06-08 18:59:39 +02:00
s-s f48b13f7b8 Add DNS64 support 2020-06-08 18:42:54 +02:00
Frank Denis d766dc8bf7 doh_client_x509_auth: make it clear that root_ca is optional 2020-06-08 18:09:37 +02:00
Kevin O'Sullivan 5db4365540
Adding support for additional root CAs for DoH TLS Auth (#1281) 2020-06-08 18:01:40 +02:00
Frank Denis 68ccd1410f Support multiple stamps per resolver
For now, a single stamp is randomly chosen in order to spread the load,
but we may eventually want to also use this for failover mechanisms.
2020-06-08 17:54:49 +02:00
Frank Denis b0e883ebc6 Android: use getprop persist.sys.timezone to get and set the time zone
Untested. Maybe
fixes #1351
2020-06-06 15:32:27 +02:00
Frank Denis 9d1eee4b29 Expand default whitelist 2020-06-03 09:47:34 +02:00
IceCodeNew dd1f32ebfd
Add comments for domains-blacklist.conf, with a little sorting on it. (#1350)
* Update domains-blacklist.conf

0. Add more comments so it should be much easier for anyone to get understanding how to choose the rules which is delivered in varies levels.
1. Sort rules from Energized so it is ordered in the sort of size, which would make sense.

* Add rule from AdAway

AdAway seems to be a project last more than 9 years. I tried it for several days and haven't experienced any false positive yet.
2020-06-03 09:42:50 +02:00
Frank Denis 2739db2733 Update deps 2020-06-02 13:56:05 +02:00
Frank Denis 010cedd7b8 whitelist qualtrics.com 2020-06-02 13:51:37 +02:00
Frank Denis 45628702b6 Add SANS lists 2020-06-02 13:03:41 +02:00
Frank Denis 1f6d8cc53c Nits 2020-05-31 13:46:44 +02:00
Frank Denis c5d2459752 Whitelist domains required to check for captive portals 2020-05-31 13:36:15 +02:00
Frank Denis 8ddd5fe36e Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Fallback to cache_file avoiding termination for not offline_mode (#1332)
  Minor update to GH Actions workflow (#1341)
2020-05-31 13:27:28 +02:00
Frank Denis d59d9427b3 Don't wait for the whole server list before accepting connections
Blocking until all servers have been checked is safe, but significantly
increases startup times.

OTOH, we shouldn't accept connections unless we have at least one live
server.

So, a better approach may be to add the ability for `serversInfo.refresh()`
to write to a channel after a live server has been found, and block on
that channel in the main thread before accepting client connections.
2020-05-31 13:24:35 +02:00
lifenjoiner c4a13d25ce
Fallback to cache_file avoiding termination for not offline_mode (#1332)
Ignore downloading error from `NewSource` when startup (cache loaded).
2020-05-30 07:38:04 +01:00
Will Elwood ee62eb7b23
Minor update to GH Actions workflow (#1341)
* Update releases.yml

Run CI for pull requests and new tags, and only when relevant files change in a push or PR.

* Update releases.yml

"Path filters are not evaluated for pushes to tags."
2020-05-29 15:06:02 +02:00
IceCodeNew 3d5f877058
Added Ads-blocking rules from Disconnect (#1336) 2020-05-28 12:10:59 +02:00
IceCodeNew da8620cdda
Update link to Spam404 rule (#1334)
Though the old link is still available, the rule file has stop updating for a long while.
2020-05-27 21:46:15 +02:00
Stathis Xantinidis 230c171c71
Added Block spying and tracking on Windows (#1327)
Reduces a great amount of telemetry on Microsoft based operating systems, for those who need it :)
2020-05-21 14:05:39 +02:00
Frank Denis 7e2404ffef Use domain lists for energized.pro 2020-05-20 16:01:25 +02:00
Linuxfreak ece0d2e8b9
Update EnergizedProtection URLs (#1325)
* Update Energized Protection URLs

EnergizedProtection url links have changed, it seems they had to delete them from github and moved them to their self hosted domain (block.energized.pro).

* Re enabling EnergizedProtection BLU

I commented it out by mistake oops :)
2020-05-20 15:57:11 +02:00
Frank Denis 82f78ef4fa s/BrokenQueryPadding/FragmentsBlocked/
Maybe
fixes #1323
2020-05-19 15:57:56 +02:00
Frank Denis 5c911ad2aa Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  move mis-categorized line (#1308)
2020-05-06 19:34:55 +02:00
Frank Denis fa2b693506 Remove parse_time_restricted_list 2020-05-06 19:34:41 +02:00
David Refoua 5c36dcb818
move mis-categorized line (#1308) 2020-05-01 21:33:48 +02:00
Frank Denis 35a6fc858f CI: stop publishing MacOS binaries since they now require notarization
Fixes #1300
2020-04-28 10:00:49 +02:00
Frank Denis 3e264b9da9 Rename tls_client_auth to doh_client_x509_auth
Maybe improves clarity? I can never remember what tls_client_auth does.
2020-04-26 21:21:00 +02:00
Frank Denis 3775d59217 Add some comments for an obscure feature 2020-04-26 21:05:23 +02:00
Frank Denis 8f7015f0bc Avoid UTF-8 in domains-blacklist.conf
Fixes #1299
2020-04-26 20:53:47 +02:00
Frank Denis c6b32e0590 Another example of an IP blocklist 2020-04-26 19:42:42 +02:00
Frank Denis 80b95b1ba6 Use accessors for systemd things, too 2020-04-26 17:08:24 +02:00
Frank Denis 436bce9edf Define functions to register socket handles, to improve clarity 2020-04-26 16:52:50 +02:00
Frank Denis 38cfa437db Repair Local DoH; should fix CI tests 2020-04-26 16:34:26 +02:00
Frank Denis 12219c7490 listener->pc
Spotted by @welwood08
2020-04-26 16:19:49 +02:00
Frank Denis 52f87aee8e Accept data from systemd sockets at the same time as everything else 2020-04-26 15:00:39 +02:00
Frank Denis 4029d3d4f3 proxy.dropPrivilege() doesn't return on success 2020-04-26 14:49:43 +02:00
Frank Denis 3c510b74bb Start listeners as goroutines 2020-04-26 14:26:40 +02:00
Frank Denis c6b2869317 Update Poly1305 dep 2020-04-26 13:03:48 +02:00
Frank Denis 4a50736457 Only start accepting connections after everyting has been initialized
Fixes #1295

And more. The estimator, key and servers list were not initialized either.
2020-04-26 12:52:55 +02:00
Frank Denis 7d0e1440e1 ESNI has been renamed to ECHO 2020-04-24 11:15:40 +02:00
Frank Denis 252b10c996 Remove blacklisted names due to globbing patterns
This is very clumsy, as it doesn't handle time-based rules properly,
and doesn't handle whitelists at all.

Adding globs to the "names" list is also an ugly hack just to have
them included in the final output.
2020-04-22 17:55:24 +02:00
Frank Denis a71b531d2e Re-add -o / --output-file 2020-04-21 23:40:58 +02:00
Frank Denis dcd6f8448d Revert "Improve generate-domains-blacklist.py to remove redundant lines (#1184)"
This reverts commit 58871de725.
2020-04-21 23:08:40 +02:00
Huhni 58871de725
Improve generate-domains-blacklist.py to remove redundant lines (#1184)
* Improve script to remove redundant lines

Let the script remove those lines that are covered by regular expressions already

* add optional "-o OUTPUT_FILE" argument 

This ensures that UTF-8 is used.
The redirect to file functionality from before is maintained, because "default=None" is used for the -o argument

I also fixed the formatting slightly to avoid newlines at the beginning of the file.

* improve glob matching

- rename regexes into globs 
- only check trusted (local) files for globs
- use fnmatch instead of manually converting globs into regular expressions and matching them
- modify is_glob function to check only for the following characters: * [ ] ?
- improve get_lines_with_globs function, by using the native filter and lambda functions
- improve covered_by_glob function, by checking if line is part of glob_list, instead of calling is_glob again
- print "ignored entries due to globs in local-additions" to the output as well to better differentiate from other duplicates
2020-04-21 23:07:32 +02:00
Frank Denis 9519472bbe Don't print the proxy version in the child 2020-04-20 12:34:59 +02:00
Frank Denis 6f2dcb900a Drop privileges early
Fixes #1265
2020-04-20 12:27:53 +02:00
Frank Denis b6b7ed3a67 Dropping privileges doesn't work reliably on MacOS 2020-04-20 11:50:27 +02:00
Frank Denis abfd195e51 Use Kadhosts without controversies
Fixes #1288
2020-04-19 17:55:46 +02:00
Frank Denis 69a7d832c4 Remove lists that are pretty much empty 2020-04-19 17:52:16 +02:00
Frank Denis ccc91e28a3 Try enabling energized blu by default
Quite a lot of domains in that list don't exist any more, though.
2020-04-19 17:46:18 +02:00
Frank Denis 900ed13ff1 Remove banbenek's list 2020-04-19 17:39:53 +02:00
Frank Denis dd522bb726 Merge branch 'master' of github.com:DNSCrypt/dnscrypt-proxy
* 'master' of github.com:DNSCrypt/dnscrypt-proxy:
  use global 'timeout' option for forwarding queries (#1284)
2020-04-18 21:18:53 +02:00
Frank Denis 2779d92f01 Add some blacklists 2020-04-18 21:18:40 +02:00
29f f71244ed74
use global 'timeout' option for forwarding queries (#1284)
* Update plugins.go

* Update plugin_forward.go
2020-04-17 20:57:23 +02:00
Frank Denis 4f41fc3fee Add Geoffrey Frogeye's block list 2020-04-12 23:34:15 +02:00
Frank Denis 527764aba7 Upper case 2020-04-05 20:50:28 +02:00
Kiril Angov d2602fd142
Respect proxy.mainProto in forward plugin (#1259)
* Respect proxy.mainProto in forward plugin

* Make the serverProtocol part of pluginsState instead
2020-04-05 20:49:30 +02:00
Frank Denis f4631b9121 Remove unreachable code
Spotted by @komapa
2020-04-05 20:48:00 +02:00
Linuxfreak 76f6d02e52
Change URL of Block Spotify ads (#1266)
The url of the Spotify-HOSTS.txt is changed. Path of "/filter/" is now "/filters/"
2020-04-04 22:18:21 +02:00
Frank Denis 5930b45116 Farewall host-files.net domain list
Fixes #1262
2020-04-02 14:56:38 +02:00
kimw 4ce28473f4
Update example-ip-blacklist.txt (#1264)
fix https://github.com/DNSCrypt/dnscrypt-proxy/issues/1261. remove `[` & `]`.
2020-04-02 14:55:18 +02:00
Frank Denis f6b9706322 This reverts commit 876e389a0a.
April 1st is almost over :)
2020-04-01 21:55:17 +02:00
Frank Denis 876e389a0a Make doh.nsa.gov the default DNS server 2020-04-01 12:22:52 +02:00
Frank Denis 17fbad3648 Update deps 2020-04-01 12:15:13 +02:00
Frank Denis 1ff31f14f1 Remove the ct parameter from DoH queries
That was a workaround for Google, but Google doesn't seem to need
it any more.
2020-04-01 12:12:57 +02:00
Frank Denis eb372e7ce5 First release using GitHub Actions: success! Farewell, Travis. 2020-03-26 18:53:35 +01:00
Frank Denis 89dd0de6af Update ChangeLog 2020-03-26 18:38:15 +01:00
Frank Denis 8fc2f00ffb Probably older than version 1.4.0 2020-03-26 18:33:39 +01:00
Frank Denis 3ca80afb19 packets -> client queries 2020-03-26 17:25:52 +01:00
Frank Denis 74095d38ed Remove LargerResponsesDropped
dnsdist drops DNSCrypt queries shorter than 256 bytes, interpreting them
as not being encrypted instead. This is surprising when doing ad-hoc
testing, but absolutely fine, and we will never send shorter encrypted
queries on normal circumstances.

So, remove a useless knob.
2020-03-26 17:20:34 +01:00
Frank Denis fb04a62470 ChangeLog 2020-03-26 15:39:48 +01:00
Frank Denis b3fbc2304d All dnsdist servers exhibit the same behavior re: sending truncated responses
A 128 bytes query will not get a 200 bytes response (randomly tested on
3.tlu.dl.delivery.mp.microsoft.com), not even a truncated one.

It may be related to fragments being blocked on the server socket, or a
different issue. We can expect everything to be back to normal in dnsdist
1.5.0 no matter what.
2020-03-26 15:19:17 +01:00
Frank Denis 5049516f53 Add an option to ignore servers incompatible with anonymization 2020-03-26 13:41:57 +01:00
Frank Denis 7621737dde Improve debugging 2020-03-26 13:30:39 +01:00
Frank Denis 9542109d66 Cancel dnsExchange goroutines as soon as we have a best response 2020-03-26 12:53:22 +01:00
Frank Denis ad36321dc8 Add cleanbrowsing until dnsdist 1.5.0 is out 2020-03-26 12:31:12 +01:00
Frank Denis 98e53c4013 Replace Travis status badge with the GitHub Action badge 2020-03-26 11:15:12 +01:00
Frank Denis c54e8a2c60 Goodbye Travis, let's switch to GitHub Actions! 2020-03-26 11:02:19 +01:00
Frank Denis 8896787e66 Add other dnsdist servers until the MTU issue is fixed
https://github.com/PowerDNS/pdns/pull/7410
2020-03-26 10:57:09 +01:00
Frank Denis 9f65457b1c Wait a little bit more between UDP attempts 2020-03-26 10:37:56 +01:00
Frank Denis 1d090eb194 Unfortunately, blocking stats.* has too many implications 2020-03-25 20:33:36 +01:00
Frank Denis 7424f1a8b7 Try harder to work around Cisco and Quad9 bugs 2020-03-25 20:10:11 +01:00
Frank Denis 64935c9b92 Bump 2020-03-25 18:24:25 +01:00
Frank Denis 0860245c73 Nits 2020-03-25 18:24:03 +01:00
Frank Denis 25b89e57ae Add Quad9 back to the list of servers with broken padding 2020-03-25 18:11:16 +01:00
Frank Denis 81c8d68462 Pad queries to 1472 bytes for implementations with broken padding
Quad9 doesn't return TC when responses are larger than the question;
it doesn't return anything instead :(
2020-03-25 18:06:02 +01:00
Frank Denis dd37eaed7c Retry over TCP on UDP timeouts 2020-03-25 17:45:59 +01:00
Frank Denis 4fe5929720 Typo
Fixes #1248
2020-03-25 09:11:10 +01:00
Frank Denis c39d66661b Remove ARMv6 and ARMv7 binaries 2020-03-24 21:56:10 +01:00
Frank Denis 16ac53810f Remove armv6 and armv7 builds, this is confusing 2020-03-24 21:55:09 +01:00
Frank Denis 9c15e90610 Update the Android NDK to r20 2020-03-24 18:00:58 +01:00
Frank Denis 39cc14da5c Update ChangeLog 2020-03-24 17:17:45 +01:00
Frank Denis 5d81e5a6db Build Linux binaries for armv7 while we're here 2020-03-24 16:43:48 +01:00
Frank Denis 77507abb62 Fix Travis build for freebsd/arm 2020-03-24 16:00:25 +01:00
Frank Denis 366413fa2d Travis: put commands on single lines
Just to make them easier to execute manually while Travis is b0rk3d
2020-03-24 15:37:23 +01:00
Frank Denis d905021856 Revert "Go back to Go 1.13"
This reverts commit d91df1a62c.

The issue on Mips was unrelated (due to an antique kernel), and
compiling for armv5 seem to work around issues reported on arm
2020-03-24 14:55:01 +01:00
Frank Denis 61d6cfa992 Use GOMIPS64=softfloat 2020-03-24 14:54:58 +01:00
Frank Denis d91df1a62c Go back to Go 1.13
We got too many reports of binaries built with Go 1.41.1 not working
any more on arm and mips CPUs.

So, downgrade until these issues are sorted out.
2020-03-24 14:39:06 +01:00
Frank Denis c13a69b040 Remove deepsource 2020-03-24 14:38:00 +01:00
Frank Denis a58044fed0 Bump 2020-03-24 14:37:35 +01:00
Frank Denis c4287c799f Quad9 doesn't seem to block fragments on all networks
So, remove them from the static list and trust the runtime checks
for detection.
2020-03-24 14:32:23 +01:00
Frank Denis 315f6f45ff Certificates that can't be loaded are fatal 2020-03-24 14:31:43 +01:00
Frank Denis 2670caa71e Print the anonymization incompatibility message even if detected at runtime 2020-03-24 14:19:41 +01:00
Frank Denis 18a8a207ab up deps 2020-03-24 13:01:57 +01:00
Frank Denis 3f07b6079a No need to explicit ignore this variable 2020-03-24 12:45:17 +01:00
Frank Denis b328a9768f Remove debugging code that prevented detection of fragmented UDP support 2020-03-24 12:38:23 +01:00
Frank Denis a3376db1b6 Create binaries for ARMv5 and ARMv6 2020-03-22 12:25:22 +01:00
Frank Denis 79c8fa30d0 Compile ARM binaries for ARMv5
Not sure what's going on here.

GOARM=6 is supposed to be the default when cross-compiling, but
some people reported that executables had illegal instructions without
GOARM=6.

After executables available for download were updated with GOARM=6,
people kept reporting "it doesn't load", even though we presumably
never shipped binaries for ARMv5.

Anyway, try that as a last move.
2020-03-22 12:15:28 +01:00
Frank Denis 6a608565bf arm builds: explicitly build for armv6
The Go documentation says that this is the default, but apparently not.

Fixes #1240
2020-03-21 18:12:47 +01:00
Frank Denis b644ec981c Update Travis config 2020-03-21 16:49:52 +01:00
Frank Denis 420c9682f9 Deps update for IllumOS 2020-03-21 16:41:20 +01:00
Frank Denis 06ca9b01f0 Nits 2020-03-21 10:24:09 +01:00
Frank Denis 9686e789c7 Bump 2020-03-21 10:18:37 +01:00
Frank Denis d80af74300 Fix unit tests 2020-03-20 22:40:29 +01:00
Frank Denis 0b87cc92b6 Fix data race 2020-03-20 21:45:09 +01:00
Frank Denis 44db53f58b Not dnsdist 2020-03-20 21:19:34 +01:00
Frank Denis d1710a4d2b Use single quotes for consistency 2020-03-20 21:18:30 +01:00
Frank Denis 094ea07dc2 Bump 2020-03-20 21:09:34 +01:00
Frank Denis d876c7b487 Keep the default LB strategy if an invalid p* one is used 2020-03-20 20:53:03 +01:00
Frank Denis 62a20fd97a up deps 2020-03-20 20:40:46 +01:00
Frank Denis d9a68abae9 Merge branch 'lbstrategy-interface'
* lbstrategy-interface:
  Support power-of-<arbitrary number>
  Use an interface for load-balancing strategies
2020-03-20 17:55:41 +01:00
Frank Denis 4c402a6012 Revert "Implement pN load balancing strategy (#1188)"
This reverts commit 014a75c0ec.
2020-03-20 17:55:33 +01:00
Timofey 014a75c0ec
Implement pN load balancing strategy (#1188) 2020-03-20 17:55:03 +01:00
Frank Denis 34d83f027f Support power-of-<arbitrary number> 2020-03-20 17:49:32 +01:00
Frank Denis b57cc19d70 Use an interface for load-balancing strategies 2020-03-20 17:37:34 +01:00
Frank Denis a1f3b34390 Update deps 2020-03-20 17:22:23 +01:00
Frank Denis bad3d0accd Update deps 2020-03-19 10:18:43 +01:00
Frank Denis 16708a0c20 Automatically detect servers blocking fragmented queries 2020-03-14 21:34:40 +01:00
Frank Denis 49910d2f72 Localize some error values 2020-03-13 18:44:30 +01:00
Frank Denis 26ebb55c8c Quite a lot of dependency updates 2020-03-13 17:58:44 +01:00
Frank Denis 19647e03a6 Overwrite the server name only when we need to send an upstream query 2020-03-13 17:52:09 +01:00
Dragonfir3 c17637c026
Don't log a server for blocked names by pattern (#1218)
* Update plugins.go

* reason update moved after reject confirmed

added boolean for direct rejects

* remove server with direct rejects

name pattern blocked cases
2020-03-13 17:50:38 +01:00
Frank Denis a8db53e36f Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Bump github.com/miekg/dns from 1.1.27 to 1.1.28 (#1223)
2020-03-13 17:38:06 +01:00
Frank Denis 5bb5a26150 Update notracking list URL
Fixes #1174
2020-03-13 17:37:50 +01:00
dependabot-preview[bot] 76106944cc
Bump github.com/miekg/dns from 1.1.27 to 1.1.28 (#1223)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.27 to 1.1.28.
- [Release notes](https://github.com/miekg/dns/releases)
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.27...v1.1.28)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-13 17:33:56 +01:00
Frank Denis 810f6043d2 People are used to seeing the [static] section at the end 2020-03-09 22:14:31 +01:00
Kevin O'Sullivan c040b13d59
Adding the ability to do TLS client authentication for DoH (#1203)
* Adding the ability to do TLS client authentication for DoH

* whitespace nit

* Check for server specific creds before wildcard

* small comma ok idiom change
2020-03-09 22:11:53 +01:00
Frank Denis c2271c8079 Remove snapcraft file, that was never used for anything 2020-03-02 11:16:09 +01:00
Frank Denis 647b14cd19 Update go.mod/go.sum 2020-02-26 16:52:37 +01:00
Frank Denis ee070be530 Update deps 2020-02-26 15:38:43 +01:00
Frank Denis 92e842126d Skip the Firefox plugin for connections through the local_doh protocol
Fixes #1205
2020-02-26 15:29:28 +01:00
Will Elwood b2be617e6b Update example-dnscrypt-proxy.toml
Fixes to grammar and other minor issues.
2020-02-26 15:13:49 +01:00
Will Elwood 11b31dea4f Update example-dnscrypt-proxy.toml
Attempt to clarify the behaviour of server_names.
2020-02-26 15:13:49 +01:00
Frank Denis aa0e7f42d3 Make the xTransport functions return the HTTP body directly
This simplifies things, but also make RTT computation way more reliable
2020-02-21 22:33:34 +01:00
Frank Denis a6d946c41f Shorten the default broken_query_padding list 2020-02-21 20:33:13 +01:00
Frank Denis 4608b6d18d Add auad9 to the broken_query_padding list
Fixes #1169
2020-02-21 20:31:45 +01:00
Frank Denis 673eea65af Add random padding to the initial DoH query
Fixes #1199
2020-02-21 20:24:24 +01:00
Alison Winters 0ef2737ffe fix minor typos in comment 2020-02-14 18:48:48 +00:00
Alison Winters 1fa26eec0a gofmt whitespace 2020-02-14 18:48:48 +00:00
Alison Winters 8c42609475 fix minor typoS in config file 2020-02-14 18:48:48 +00:00
Frank Denis 323c4a4758 Don't explain the format of other config files in the main config file
This is confusing if you don't read the documentation.

Fixes #1179
2020-02-05 12:17:14 +01:00
Frank Denis 824fa90f94 Forwarding plugin: force set the response ID to match the query ID
Shouldn't be necessary, but just to be safe in case `dns.Exchange()`
does something unexpected.
2020-02-05 02:52:54 +01:00
Frank Denis 63d28fc9b2 Forwarding plugin: retry over TCP if a truncated response is received
dns.Exchange() doesn't do it automatically.

Fixes #1178
2020-02-05 02:44:43 +01:00
Frank Denis 170c690996 Bump 2020-01-31 11:25:04 +01:00
Frank Denis 2dda74647d Don't add padding unless the query has padding
Or else Firefox craps out
2020-01-31 11:17:36 +01:00
Frank Denis 70311614a0 Improve error message on DNSSEC failure 2020-01-31 10:58:07 +01:00
Frank Denis 0f78684e5f Add a test for the local DoH feature 2020-01-31 10:58:07 +01:00
Frank Denis cf1498c9f4 Properly compute the padding length for local DoH
Fixes #1173
2020-01-31 10:58:03 +01:00
Frank Denis d14d2b613a Bump 2020-01-30 16:19:38 +01:00
Frank Denis a6026ce48a Ignore lines starting with '#'
Fixes #1171
2020-01-30 16:16:05 +01:00
Frank Denis 3a94523d65 Bump the cache size a little bit 2020-01-30 15:08:23 +01:00
Frank Denis 0d0c5afd29 Update ChangeLog 2020-01-30 13:39:54 +01:00
Frank Denis c84a394817 Bump 2020-01-30 13:23:03 +01:00
Frank Denis f34d7b60fa Implement serve-stale 2020-01-30 13:15:29 +01:00
Frank Denis f22461374c Retry UDP queries on timeout 2020-01-29 18:53:39 +01:00
Frank Denis cd35e2e854 Update deps 2020-01-29 17:58:43 +01:00
Frank Denis f17ce1ae0d Use constant, but arbitrary long padding 2020-01-29 17:57:59 +01:00
Frank Denis c323bcde21 Update deps 2020-01-29 01:07:08 +01:00
Frank Denis 4d788aed85 Make UDP and TCP code similar when it comes to SOCKS proxying
Actually use the relay when both a relay and a SOCKS proxy are
configured.

Keep forcing TCP when SOCKS is enabled. I couldn't get UDP proxying
to work with Shadowsocks.
2020-01-27 16:07:08 +01:00
Frank Denis 349320f291 Add support for inline comments in patterns lists
Fixes #1162
2020-01-25 15:45:23 +01:00
dependabot-preview[bot] 6fa865d538 Bump github.com/hashicorp/golang-lru from 0.5.3 to 0.5.4
Bumps [github.com/hashicorp/golang-lru](https://github.com/hashicorp/golang-lru) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/hashicorp/golang-lru/releases)
- [Commits](https://github.com/hashicorp/golang-lru/compare/v0.5.3...v0.5.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-17 10:04:17 +01:00
Frank Denis b041eba311 Update deps 2020-01-15 19:58:59 +01:00
Frank Denis 7ada3fcfb8 Support multiple fallback resolvers 2020-01-15 19:58:14 +01:00
Frank Denis f1bd4bf420 Update deps 2020-01-14 12:14:49 +01:00
Frank Denis 26971d254d go crypto update 2020-01-11 08:57:16 -07:00
Frank Denis 4f03575943 ransomware tracker is no more 2020-01-11 08:55:22 -07:00
Cristian-J 05593a8bbd Ignore links that start with a hyphen or a dot
If you use filter blacklists you'll end up with many invalid links that start with a hyphen or a dot in the final blacklist.
2020-01-08 12:57:22 -07:00
Frank Denis 7fb62d98ea Use EDNS0 padding for local DoH 2020-01-05 21:12:29 -05:00
Frank Denis 6fb42d0eae Improve error message when local DoH is enabled without a certificate
Fixes #1136
2020-01-05 19:02:57 -05:00
Frank Denis 5c37d3b9de Update deps 2020-01-05 19:00:16 -05:00
Frank Denis 19cebfdb0a Mention that /dev/stdout is not for Windows systems
Fixes #1131
2020-01-03 21:13:04 -05:00
Frank Denis 817d92fce0 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  sys/unix update
  Blacklist motd.ubuntu.com
2020-01-01 11:37:30 +01:00
Frank Denis 33c968b346 2020 2020-01-01 11:36:07 +01:00
Frank Denis fc1754c45f sys/unix update 2019-12-30 20:25:29 +01:00
Frank Denis 1c45d9e156 Blacklist motd.ubuntu.com 2019-12-27 21:21:05 +01:00
Frank Denis abd221738b Explicit brackets 2019-12-23 23:17:46 +01:00
Frank Denis 5ede397d33 Mention ipsum 2019-12-23 19:52:27 +01:00
Frank Denis 69f00ca977 Don't use the message attribute to get an error message
Fixes #1123
2019-12-23 18:58:39 +01:00
Frank Denis a308c76191 Format 2019-12-23 18:55:37 +01:00
Frank Denis 0e644c4b86 Add -config <config file> to the service configuration arguments
Maybe
fixes #1122
2019-12-23 15:35:52 +01:00
Frank Denis 7e45b50d58 Move things around 2019-12-23 15:33:57 +01:00
Frank Denis c27d41faa0 Avoid unneeded DNS packet unpacking 2019-12-23 11:37:45 +01:00
Frank Denis adb6dac420 Strip EDNS0 options in responses 2019-12-22 18:02:33 +01:00
Frank Denis 5118ed21fd Use dumb padding even for GET queries
Resolvers such as Cloudflare always add padding to DoH responses
Resolvers such as Google only do if the question had dumb padding
Resolvers such as Cisco blindly return a copy of the question's padding
Some resolvers don't return any padding no matter what's in the question
Finally, other resolvers return FORMERR

This is a mess. A bad design inherited from DoT, that didn't fix
anything from Unbound's original experiment.

Also, padding with zeros as recommended is a bad idea. When using
GET, escaping makes the actual padding size 3 times as big as needed.
2019-12-22 17:34:16 +01:00
Frank Denis 1585ede954 Use EDNS0 padding when using DoH over POST
This mechanism is horrible, slow (requires re-unpacking and re-packing
the query), should be done at transport layer and not at content layer, and
of course, it is incompatible with some resolvers.

However, in spite of https://go-review.googlesource.com/c/go/+/114316/2/src/net/http/transfer.go ,
we may still end up sending the header and the content in distinct packets.

So, use that horror for POST queries only. For GET, this is not needed.
2019-12-22 15:31:02 +01:00
Frank Denis 0454463539 Pad GET queries 2019-12-22 14:43:21 +01:00
Frank Denis 48817a4642 Unbeta 2019-12-21 21:29:13 +01:00
Frank Denis 6f62a82496 Update deps 2019-12-21 21:28:07 +01:00
unknown a7922a81fb add some nonexistent zones 2019-12-21 14:34:29 +01:00
Frank Denis ebe3a37ddc Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  CI: don't hardcode the HTTP port
  2.0.36-beta.1
  agl/ed25519 is not required any more
  CI: Change the local DoH port, run go tests, enable -race
2019-12-18 23:22:41 +01:00
Frank Denis ffb1a1b718 People really blacklist cdn.cloudflare.net ¯\_(ツ)_/¯ 2019-12-18 23:21:50 +01:00
Frank Denis 99c86283f6 CI: don't hardcode the HTTP port 2019-12-18 12:54:55 +01:00
Frank Denis 80d45a2343 2.0.36-beta.1 2019-12-18 12:44:24 +01:00
Frank Denis 44735cb97e agl/ed25519 is not required any more 2019-12-18 12:40:52 +01:00
Frank Denis 3ec6a814c2 CI: Change the local DoH port, run go tests, enable -race 2019-12-17 23:48:01 +01:00
Frank Denis 3fce30d7a5 Rename PluginsActionForward to PluginsActionContinue
Set the correct response code when forwarding
2019-12-17 19:19:36 +01:00
Frank Denis 2c295e3702 Add an additional CI test for standard, cached queries 2019-12-17 19:03:02 +01:00
Frank Denis daf6d5881d The default return code must be PASS 2019-12-17 18:54:49 +01:00
Frank Denis 515968e414 Run the CI test in the before_install phase
This is unorthodox, but Travis will stop right after most errors
instead of spending forever installing the Android NDK
2019-12-17 18:38:40 +01:00
Frank Denis 56bd9dcd04 Add some temporary basic CI tests 2019-12-17 18:29:33 +01:00
Frank Denis b1c08f8931 Handle Drop/Synth actions the same way in query and response plugins 2019-12-17 16:28:12 +01:00
Frank Denis a23f07a93d Add an IP blacklist example 2019-12-17 15:25:39 +01:00
William Elwood d88995aac6 Minor comment fix
I noticed while writing the functionality tests that comments about relative paths disagreed with what the code was doing.
While the executable directory is used if the configuration file itself can't be found, `cdFileDir(foundConfigFile)` is always executed after the configuration file is found whether that's the same as the executable's directory or not.
Also a couple of punctuation nits.
2019-12-17 14:28:06 +01:00
William Elwood 7d08ba2536 Fix module name
I'm not sure why an extra "dnscrypt-proxy" was added to the module name when the rest of the URL was updated, but it resulted in the following:
```
$ go test -mod=vendor ./...
ok  	github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/dnscrypt-proxy	0.173s
ok  	github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/test	0.006s
$ go list ./...
github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/dnscrypt-proxy
github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/test
```
Not critical, but it looks wrong that these packages will not be found at those URLs.
2019-12-17 14:27:05 +01:00
Frank Denis 8e5a5b734f Make the doc link more visible 2019-12-17 14:11:05 +01:00
Frank Denis 3c6f87527f Undelegated zones are not dot suffixed any more 2019-12-17 11:08:22 +01:00
Frank Denis 4fd54a4919 Store the normalized qName in the plugin state
We now enforce the fact that a query always include a question.
It holds true for all practical use cases of dnscrypt-proxy.

This avoids quite a lot of redundant code in plugins, and is faster.
2019-12-17 10:11:41 +01:00
Frank Denis ee24bf0421 Bump 2019-12-16 23:06:56 +01:00
Frank Denis a35d08394a Update ChangeLog 2019-12-16 23:06:41 +01:00
Frank Denis 49e8328dd6 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy 2019-12-16 20:04:52 +01:00
Frank Denis ed5431d7b9 Update deps 2019-12-16 20:04:38 +01:00
Frank 10f33d39fc Update deps 2019-12-16 20:02:53 +01:00
Frank Denis 07e605e9f4 Add a note about dnsmasq
In the config file, so that it has more visibility than in the doc.

Synthetic responses cannot contain NSEC or RRSIG records, and that
seems to be confusing dnsmasq.
2019-12-16 17:23:22 +01:00
Frank Denis eedabcbd4a Reverse 2019-12-16 17:05:05 +01:00
Frank Denis cba755b4d1 Lowercase the question 2019-12-16 17:03:16 +01:00
Frank Denis 7066e53843 Pre-add the final dot 2019-12-16 16:39:30 +01:00
Frank Denis 1b276be85d Rewrite block_undelegated without the generic pattern matcher 2019-12-16 16:35:08 +01:00
Frank Denis 2d25719a69 Reuse the same variable 2019-12-16 16:32:49 +01:00
Frank Denis 66799c4159 Add the ability to block undelegated DNS zones
Using the generic pattern matcher as a first iteration, but we can
save some memory and CPU cycles by building and using a critbit tree
directly.
2019-12-16 16:18:47 +01:00
Frank Denis aa5350c7fd Missed blockedName->xBlockedName renaming
Fixes #1116
2019-12-16 12:13:23 +01:00
Frank Denis 56f838341b travis-ci.org -> travis-ci.com 2019-12-11 14:19:39 +01:00
Frank Denis 76de8a955a Update deps 2019-12-11 14:10:16 +01:00
Frank Denis c1202457bf Json -> JSON 2019-12-11 14:08:48 +01:00
Frank Denis a7b7bdc11e Compress synthetic responses 2019-12-11 14:02:56 +01:00
Frank Denis 9553d7f8c5 Copy the DO bit from questions to synthetic responses 2019-12-11 13:56:25 +01:00
Frank Denis 1674bb1742 Force clear the AD bit unless the DO bit was also set 2019-12-11 09:41:16 +01:00
Frank Denis ee1c0fed93 Properly set DNS flags when creating empty responses 2019-12-11 09:00:29 +01:00
Frank Denis 3b4d6c532d A URL path must start with a / 2019-12-10 16:04:37 +01:00
Frank Denis 4d5c940616 Remove domains-blacklist-all.conf 2019-12-10 15:51:04 +01:00
Frank Denis 45d506efa1 Add the full path to minisign 2019-12-10 00:33:59 +01:00
Frank Denis 6d1e4a9b5d Travis: create /tmp/bin 2019-12-10 00:18:36 +01:00
Frank Denis 279d5619e3 Don't block '.' 2019-12-10 00:03:41 +01:00
Frank Denis 5d396d5f52 Grammar 2019-12-09 23:57:00 +01:00
Frank Denis 7fe2ac4b39 Pasto 2019-12-09 23:55:22 +01:00
Frank Denis c4673d86f5 Define _GNU_SOURCE 2019-12-09 23:45:41 +01:00
Frank Denis c68c672b47 libsodium -> libsodium-dev and move to before_deploy 2019-12-09 23:20:22 +01:00
Frank Denis 548a439528 Bump 2019-12-09 20:56:59 +01:00
Frank Denis a635e92606 Add a new plugin to block unqualified host names 2019-12-09 20:25:38 +01:00
Frank Denis 56d02597a6 Extend the grace period and log when it's used 2019-12-09 17:08:59 +01:00
Frank Denis 21a5765527 Rename resolveWithCache() and make the comment match what the fn does 2019-12-09 17:03:16 +01:00
Frank Denis 2d8fd40481 Don't use named return values just for one value, especially an error
Be consistent with the rest of the code
2019-12-09 16:59:02 +01:00
Frank Denis 3e32d38f29 Explicit initialization 2019-12-09 16:56:43 +01:00
Frank Denis 49460f1d6f pidfile.Write() can fail if no pid file was configured, it's ok 2019-12-09 13:34:14 +01:00
Frank Denis 7991b91f21 Downgrade error level of pidfile.Write() to Critical 2019-12-09 13:08:03 +01:00
Frank Denis b5bb0fd504 If we can't disconnect from the Service Manager, it's no big deal 2019-12-09 13:07:47 +01:00
Frank Denis bfd74185f5 Don't prevent DNS queries from being answered if the partition is full 2019-12-09 12:55:26 +01:00
milgradesec 8efbf401c8 add error checks 2019-12-09 12:50:30 +01:00
Frank Denis 51a842b838 Reapply miekg/dns updates to the vendor dir 2019-12-09 12:03:03 +01:00
Frank Denis 3d11d1d4e0 Revert "Update deps"
This reverts commit 915c90ae37.

The x/sys update breaks compilation on arm
2019-12-09 12:02:42 +01:00
Frank Denis 915c90ae37 Update deps 2019-12-09 10:09:37 +01:00
dependabot-preview[bot] f5880667c9 Bump github.com/miekg/dns from 1.1.22 to 1.1.24
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.22 to 1.1.24.
- [Release notes](https://github.com/miekg/dns/releases)
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.22...v1.1.24)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-09 10:07:29 +01:00
Frank Denis ba8565a59e Shorten conditions 2019-12-09 10:07:05 +01:00
milgradesec 96d15771e2 add multiple error checks 2019-12-09 09:56:47 +01:00
Frank Denis 61a1637650 Use the singular 2019-12-09 00:01:43 +01:00
Frank Denis 5d778f998a Add issue templates 2019-12-09 00:00:22 +01:00
Frank Denis 2d28e5808d Add fastly.net 2019-12-08 12:30:52 +01:00
Frank Denis de10601a8e Add edgesuite.net to the example whitelist 2019-12-08 10:59:19 +01:00
Frank Denis 59f2df6318 Recommend more names to be forwarded 2019-12-07 17:38:07 +01:00
Frank Denis 62f0b80c66 Add a comment regarding forwarding and ipv6 blocking 2019-12-06 19:41:33 +01:00
Frank Denis db33c69fe5 Log the original qName when a CNAME pointer is blocked 2019-12-05 17:50:04 +01:00
Frank Denis 4d0c5ad569 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy
* 'master' of github.com:jedisct1/dnscrypt-proxy:
  Travis: use Ubuntu Bionic so we don't have to compile libsodium
  Downcase wiki
  Handle clientsCount in the local DoH handler, too
  Remove beta
  Bump deps
  Fix typo
  Bump
  whitelist
  Add some extra blacklists
2019-12-05 16:49:48 +01:00
Frank Denis 57a88eda56 Add (indirect) to the logged pattern for indirect blocks 2019-12-05 16:49:08 +01:00
Frank Denis 63e8494b44 Travis: use Ubuntu Bionic so we don't have to compile libsodium 2019-12-03 13:31:44 +01:00
Frank Denis 36cffffb11 Downcase wiki 2019-12-03 13:19:38 +01:00
Frank Denis 3a4bc98073 Handle clientsCount in the local DoH handler, too 2019-12-03 13:04:58 +01:00
Frank Denis 0de2246af2 Remove beta
Fixes #1086
2019-12-03 12:34:42 +01:00
Frank Denis 722089e561 Bump deps 2019-12-03 12:02:08 +01:00
glitsj16 443bdce879 Fix typo 2019-12-01 23:38:05 +01:00
Frank Denis 8da3107809 Bump 2019-12-01 17:57:42 +01:00
Frank Denis 98eb5ccd49 whitelist 2019-12-01 17:56:48 +01:00
Frank Denis ef5bde30a7 Merge branch 'master' of github.com:jedisct1/dnscrypt-proxy 2019-12-01 16:01:38 +01:00
Frank Denis 941cd63508 Add some extra blacklists 2019-12-01 16:01:17 +01:00
Frank Denis 21c63a5608 Local-DoH: pad responses 2019-11-29 21:34:21 +01:00
Frank Denis f9019f46a4 2019 2019-11-29 21:24:06 +01:00
Frank Denis 53dd5cd6c5 Clarify 2019-11-29 14:18:48 +01:00
Frank Denis 53924d4cf7 Unset GODEBUG - WHich means that Go 1.13 is now required for TLS 1.3
We could keep setting GODEBUG for compatibility with older versions, but
people complain that it prints debug warnings.
2019-11-29 14:00:21 +01:00
Frank Denis 4a613aa68d Explain what the path is in a URL 2019-11-29 13:42:35 +01:00
Frank Denis c84e104061 Mention local DoH 2019-11-29 09:30:41 +01:00
Frank Denis 3b50caf4cd Add a default local DoH path, print the URLs 2019-11-29 08:53:13 +01:00
Frank Denis 3b2eeea544 Include localhost.pem 2019-11-29 01:13:11 +01:00
Frank Denis 640b949976 Bump to 2.0.34-beta.1 2019-11-29 00:07:36 +01:00
Frank Denis b4356b9fc8 Update deps 2019-11-29 00:06:14 +01:00
Frank Denis 1f55b798c0 Update ChangeLog 2019-11-29 00:05:11 +01:00
Frank Denis f18dbc71ec Make the local DoH path configurable 2019-11-28 23:49:28 +01:00
Frank Denis 583ca09946 Reuse dataType 2019-11-28 23:33:34 +01:00
Frank Denis aad9c8f19c Limit the query body size 2019-11-28 23:32:56 +01:00
Frank Denis 5d6f9358c9 Print something useful when browsing the local DoH URL 2019-11-28 23:30:54 +01:00
Frank Denis 3ef9ec8732 Local DoH tweaks 2019-11-28 23:08:23 +01:00
Frank Denis 3e5dbee75a We don't need to store local copies of cachedResponses 2019-11-28 22:34:02 +01:00
Frank Denis 068509ef30 Rename http to local_doh 2019-11-28 17:11:14 +01:00
Frank Denis 6a679cc543 Move local DoH configuration to its own section 2019-11-28 17:04:29 +01:00
Frank Denis be996c486f Local DoH support, continued 2019-11-28 16:46:25 +01:00
Frank Denis 1966a8604b up 2019-11-26 01:36:35 +01:00
Frank Denis f249813cc5 First bits towards providing access over DoH in addition to DNS
Mainly to deal with the Firefox+ESNI situation
2019-11-24 22:46:27 +01:00
Frank Denis 30b5507bf4 Make the part that creates or gets sockets more readable 2019-11-24 22:12:23 +01:00
Frank Denis bc22f94eeb Don't listen to IPv6 in the example config file
Some hosts don't support IPv6, and the default (without anything in
the config file) is only the IPv4 address anyway.
2019-11-24 10:31:40 +01:00
Frank Denis 67c7254dc5 block_name plugin: also check names found in CNAME records 2019-11-24 10:18:46 +01:00
Frank Denis 1152491b2d Move PluginCache before PluginCacheResponse 2019-11-24 09:14:36 +01:00
Frank Denis 6e3916556f Downcase the query name in BlockedNames.check() 2019-11-20 19:16:37 +01:00
1708 changed files with 315841 additions and 160106 deletions

1
.ci/allowed-names.txt Normal file
View File

@ -0,0 +1 @@
tracker.debian.org

1
.ci/blocked-ips.txt Normal file
View File

@ -0,0 +1 @@
8.8.8.8

21
.ci/blocked-names.txt Normal file
View File

@ -0,0 +1,21 @@
##################
# Test blocklist #
##################
ad.*
ads.*
banner.*
banners.*
creatives.*
oas.*
oascentral.* # test inline comment
stats.* # test inline comment with trailing spaces
tag.*
telemetry.*
tracker.*
*.local
eth0.me
*.workgroup
*.youtube.* @time-to-sleep
facebook.com @work

204
.ci/ci-build.sh Executable file
View File

@ -0,0 +1,204 @@
#! /bin/sh
PACKAGE_VERSION="$1"
cd dnscrypt-proxy || exit 1
go clean
env GOOS=windows GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir win32
ln dnscrypt-proxy.exe win32/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt win32/
for i in win32/LICENSE win32/*.toml win32/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx "$i"; done
ln ../windows/* win32/
zip -9 -r dnscrypt-proxy-win32-${PACKAGE_VERSION:-dev}.zip win32
go clean
env GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir win64
ln dnscrypt-proxy.exe win64/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt win64/
for i in win64/LICENSE win64/*.toml win64/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx "$i"; done
ln ../windows/* win64/
zip -9 -r dnscrypt-proxy-win64-${PACKAGE_VERSION:-dev}.zip win64
go clean
env GO386=softfloat GOOS=openbsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir openbsd-i386
ln dnscrypt-proxy openbsd-i386/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt openbsd-i386/
tar czpvf dnscrypt-proxy-openbsd_i386-${PACKAGE_VERSION:-dev}.tar.gz openbsd-i386
go clean
env GOOS=openbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir openbsd-amd64
ln dnscrypt-proxy openbsd-amd64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt openbsd-amd64/
tar czpvf dnscrypt-proxy-openbsd_amd64-${PACKAGE_VERSION:-dev}.tar.gz openbsd-amd64
go clean
env GOOS=freebsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir freebsd-i386
ln dnscrypt-proxy freebsd-i386/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt freebsd-i386/
tar czpvf dnscrypt-proxy-freebsd_i386-${PACKAGE_VERSION:-dev}.tar.gz freebsd-i386
go clean
env GOOS=freebsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir freebsd-amd64
ln dnscrypt-proxy freebsd-amd64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt freebsd-amd64/
tar czpvf dnscrypt-proxy-freebsd_amd64-${PACKAGE_VERSION:-dev}.tar.gz freebsd-amd64
go clean
env GOOS=freebsd GOARCH=arm GOARM=5 go build -mod vendor -ldflags="-s -w"
mkdir freebsd-arm
ln dnscrypt-proxy freebsd-arm/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt freebsd-arm/
tar czpvf dnscrypt-proxy-freebsd_arm-${PACKAGE_VERSION:-dev}.tar.gz freebsd-arm
go clean
env GOOS=dragonfly GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir dragonflybsd-amd64
ln dnscrypt-proxy dragonflybsd-amd64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt dragonflybsd-amd64/
tar czpvf dnscrypt-proxy-dragonflybsd_amd64-${PACKAGE_VERSION:-dev}.tar.gz dragonflybsd-amd64
go clean
env GOOS=netbsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir netbsd-i386
ln dnscrypt-proxy netbsd-i386/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt netbsd-i386/
tar czpvf dnscrypt-proxy-netbsd_i386-${PACKAGE_VERSION:-dev}.tar.gz netbsd-i386
go clean
env GOOS=netbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir netbsd-amd64
ln dnscrypt-proxy netbsd-amd64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt netbsd-amd64/
tar czpvf dnscrypt-proxy-netbsd_amd64-${PACKAGE_VERSION:-dev}.tar.gz netbsd-amd64
go clean
env GOOS=solaris GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir solaris-amd64
ln dnscrypt-proxy solaris-amd64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt solaris-amd64/
tar czpvf dnscrypt-proxy-solaris_amd64-${PACKAGE_VERSION:-dev}.tar.gz solaris-amd64
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir linux-i386
ln dnscrypt-proxy linux-i386/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-i386/
tar czpvf dnscrypt-proxy-linux_i386-${PACKAGE_VERSION:-dev}.tar.gz linux-i386
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir linux-x86_64
ln dnscrypt-proxy linux-x86_64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-x86_64/
tar czpvf dnscrypt-proxy-linux_x86_64-${PACKAGE_VERSION:-dev}.tar.gz linux-x86_64
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -mod vendor -ldflags="-s -w"
mkdir linux-arm
ln dnscrypt-proxy linux-arm/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-arm/
tar czpvf dnscrypt-proxy-linux_arm-${PACKAGE_VERSION:-dev}.tar.gz linux-arm
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -ldflags="-s -w"
mkdir linux-arm64
ln dnscrypt-proxy linux-arm64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-arm64/
tar czpvf dnscrypt-proxy-linux_arm64-${PACKAGE_VERSION:-dev}.tar.gz linux-arm64
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod vendor -ldflags="-s -w"
mkdir linux-mips
ln dnscrypt-proxy linux-mips/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-mips/
tar czpvf dnscrypt-proxy-linux_mips-${PACKAGE_VERSION:-dev}.tar.gz linux-mips
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod vendor -ldflags="-s -w"
mkdir linux-mipsle
ln dnscrypt-proxy linux-mipsle/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-mipsle/
tar czpvf dnscrypt-proxy-linux_mipsle-${PACKAGE_VERSION:-dev}.tar.gz linux-mipsle
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -mod vendor -ldflags="-s -w"
mkdir linux-mips64
ln dnscrypt-proxy linux-mips64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-mips64/
tar czpvf dnscrypt-proxy-linux_mips64-${PACKAGE_VERSION:-dev}.tar.gz linux-mips64
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le GOMIPS64=softfloat go build -mod vendor -ldflags="-s -w"
mkdir linux-mips64le
ln dnscrypt-proxy linux-mips64le/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-mips64le/
tar czpvf dnscrypt-proxy-linux_mips64le-${PACKAGE_VERSION:-dev}.tar.gz linux-mips64le
go clean
env CGO_ENABLED=0 GOOS=linux GOARCH=riscv64 go build -mod vendor -ldflags="-s -w"
mkdir linux-riscv64
ln dnscrypt-proxy linux-riscv64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt linux-riscv64/
tar czpvf dnscrypt-proxy-linux_riscv64-${PACKAGE_VERSION:-dev}.tar.gz linux-riscv64
go clean
env GOOS=darwin GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir macos-x86_64
ln dnscrypt-proxy macos-x86_64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt macos-x86_64/
tar czpvf dnscrypt-proxy-macos_x86_64-${PACKAGE_VERSION:-dev}.tar.gz macos-x86_64
go clean
env GOOS=darwin GOARCH=arm64 go build -mod vendor -ldflags="-s -w"
mkdir macos-arm64
ln dnscrypt-proxy macos-arm64/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt macos-arm64/
tar czpvf dnscrypt-proxy-macos_arm64-${PACKAGE_VERSION:-dev}.tar.gz macos-arm64
# Android
NDK_VER=r20
curl -LOs https://dl.google.com/android/repository/android-ndk-${NDK_VER}-linux-x86_64.zip
unzip -q android-ndk-${NDK_VER}-linux-x86_64.zip -d ${HOME}
rm android-ndk-${NDK_VER}-linux-x86_64.zip
NDK_TOOLS=${HOME}/android-ndk-${NDK_VER}
export PATH=${PATH}:${NDK_TOOLS}/toolchains/llvm/prebuilt/linux-x86_64/bin
go clean
env CC=armv7a-linux-androideabi19-clang CXX=armv7a-linux-androideabi19-clang++ CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 go build -mod vendor -ldflags="-s -w"
mkdir android-arm
ln dnscrypt-proxy android-arm/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt android-arm/
zip -9 -r dnscrypt-proxy-android_arm-${PACKAGE_VERSION:-dev}.zip android-arm
go clean
env CC=aarch64-linux-android21-clang CXX=aarch64-linux-android21-clang++ CGO_ENABLED=1 GOOS=android GOARCH=arm64 go build -mod vendor -ldflags="-s -w"
mkdir android-arm64
ln dnscrypt-proxy android-arm64/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt android-arm64/
zip -9 -r dnscrypt-proxy-android_arm64-${PACKAGE_VERSION:-dev}.zip android-arm64
go clean
env CC=i686-linux-android19-clang CXX=i686-linux-android19-clang++ CGO_ENABLED=1 GOOS=android GOARCH=386 go build -mod vendor -ldflags="-s -w"
mkdir android-i386
ln dnscrypt-proxy android-i386/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt android-i386/
zip -9 -r dnscrypt-proxy-android_i386-${PACKAGE_VERSION:-dev}.zip android-i386
go clean
env CC=x86_64-linux-android21-clang CXX=x86_64-linux-android21-clang++ CGO_ENABLED=1 GOOS=android GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
mkdir android-x86_64
ln dnscrypt-proxy android-x86_64/
cp ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt android-x86_64/
zip -9 -r dnscrypt-proxy-android_x86_64-${PACKAGE_VERSION:-dev}.zip android-x86_64
# Done
ls -l dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip

58
.ci/ci-package.sh Executable file
View File

@ -0,0 +1,58 @@
#! /bin/sh
PACKAGE_VERSION="$1"
cd dnscrypt-proxy || exit 1
# setup the environment
sudo apt-get update -y
sudo apt-get install -y wget wine dotnet-sdk-6.0
sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install -y wine32
sudo apt-get install -y unzip
export WINEPREFIX="$HOME"/.wine32
export WINEARCH=win32
export WINEDEBUG=-all
wget https://dl.winehq.org/wine/wine-mono/8.1.0/wine-mono-8.1.0-x86.msi
WINEPREFIX="$HOME/.wine32" WINEARCH=win32 wineboot --init
WINEPREFIX="$HOME/.wine32" WINEARCH=win32 wine msiexec /i wine-mono-8.1.0-x86.msi
mkdir "$HOME"/.wine32/drive_c/temp
mkdir -p "$HOME"/.wine/drive_c/temp
wget https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip -nv -O wix.zip
unzip wix.zip -d "$HOME"/wix
rm -f wix.zip
builddir=$(pwd)
srcdir=$(
cd ..
pwd
)
version=$PACKAGE_VERSION
cd "$HOME"/wix || exit
ln -s "$builddir" "$HOME"/wix/build
ln -s "$srcdir"/contrib/msi "$HOME"/wix/wixproj
echo "builddir: $builddir"
# build the msi's
#################
for arch in x64 x86; do
binpath="win32"
if [ "$arch" = "x64" ]; then
binpath="win64"
fi
echo $arch
wine candle.exe -dVersion="$version" -dPlatform=$arch -dPath=build\\$binpath -arch $arch wixproj\\dnscrypt.wxs -out build\\dnscrypt-$arch.wixobj
wine light.exe -out build\\dnscrypt-proxy-$arch-"$version".msi build\\dnscrypt-$arch.wixobj -sval
done
cd "$builddir" || exit

164
.ci/ci-test.sh Executable file
View File

@ -0,0 +1,164 @@
#! /bin/sh
DNS_PORT=5300
HTTP_PORT=3053
TEST_COUNT=0
exec 2>error.log
t() {
TEST_COUNT=$((TEST_COUNT + 1))
echo "Test #${TEST_COUNT}..."
false
}
fail() (
echo "*** Test #${TEST_COUNT} FAILED ***" >&2
)
section() {
true
}
rm -f blocked-names.log blocked-ips.log query.log nx.log allowed-names.log
t || (
cd ../dnscrypt-proxy
go test -mod vendor
go build -mod vendor -race
) || fail
section
sed -e "s/127.0.0.1:53/127.0.0.1:${DNS_PORT}/g" -e "s/# server_names =.*/server_names = ['scaleway-fr']/" ../dnscrypt-proxy/example-dnscrypt-proxy.toml >test-dnscrypt-proxy.toml
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test-dnscrypt-proxy.toml -pidfile /tmp/dnscrypt-proxy.pidfile &
sleep 5
t ||
dig -p${DNS_PORT} . @127.0.0.1 | grep -Fq 'root-servers.net.' || fail
t || dig -p${DNS_PORT} +dnssec . @127.0.0.1 | grep -Fq 'root-servers.net.' || fail
t || dig -p${DNS_PORT} +dnssec . @127.0.0.1 | grep -Fq 'flags: do;' || fail
t || dig -p${DNS_PORT} +short one.one.one.one @127.0.0.1 | grep -Fq '1.1.1.1' || fail
t || dig -p${DNS_PORT} +dnssec dnscrypt.info @127.0.0.1 | grep -Fq 'flags: qr rd ra ad' || fail
t || dig -p${DNS_PORT} +dnssec dnscrypt.info @127.0.0.1 | grep -Fq 'flags: do;' || fail
kill $(cat /tmp/dnscrypt-proxy.pidfile)
sleep 5
section
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test2-dnscrypt-proxy.toml -pidfile /tmp/dnscrypt-proxy.pidfile &
sleep 5
section
t || dig -p${DNS_PORT} A microsoft.com @127.0.0.1 | grep -Fq "NOERROR" || fail
t || dig -p${DNS_PORT} A MICROSOFT.COM @127.0.0.1 | grep -Fq "NOERROR" || fail
section
t || dig -p${DNS_PORT} AAAA ipv6.google.com @127.0.0.1 | grep -Fq 'locally blocked' || fail
section
t || dig -p${DNS_PORT} invalid. @127.0.0.1 | grep -Fq NXDOMAIN || fail
t || dig -p${DNS_PORT} +dnssec invalid. @127.0.0.1 | grep -Fq 'flags: do;' || fail
t || dig -p${DNS_PORT} PTR 168.192.in-addr.arpa @127.0.0.1 | grep -Fq 'NXDOMAIN' || fail
t || dig -p${DNS_PORT} +dnssec PTR 168.192.in-addr.arpa @127.0.0.1 | grep -Fq 'flags: do;' || fail
section
t || dig -p${DNS_PORT} +dnssec darpa.mil @127.0.0.1 2>&1 | grep -Fvq 'RRSIG' || fail
t || dig -p${DNS_PORT} +dnssec www.darpa.mil @127.0.0.1 2>&1 | grep -Fvq 'RRSIG' || fail
section
t || dig -p${DNS_PORT} +short cloakedunregistered.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
t || dig -p${DNS_PORT} +short MX cloakedunregistered.com @127.0.0.1 | grep -Fq 'locally blocked' || fail
t || dig -p${DNS_PORT} +short MX example.com @127.0.0.1 | grep -Fvq 'locally blocked' || fail
t || dig -p${DNS_PORT} NS cloakedunregistered.com @127.0.0.1 | grep -Fiq 'gtld-servers.net' || fail
t || dig -p${DNS_PORT} +short www.cloakedunregistered2.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
t || dig -p${DNS_PORT} +short www.dnscrypt-test @127.0.0.1 | grep -Fq '192.168.100.100' || fail
t || dig -p${DNS_PORT} a.www.dnscrypt-test @127.0.0.1 | grep -Fq 'NXDOMAIN' || fail
t || dig -p${DNS_PORT} +short ptr 101.100.168.192.in-addr.arpa. @127.0.0.1 | grep -Eq 'www.dnscrypt-test.com' || fail
t || dig -p${DNS_PORT} +short ptr 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.d.f.ip6.arpa. @127.0.0.1 | grep -Eq 'ipv6.dnscrypt-test.com' || fail
section
t || dig -p${DNS_PORT} telemetry.example @127.0.0.1 | grep -Fq 'locally blocked' || fail
section
t || dig -p${DNS_PORT} dns.google @127.0.0.1 | grep -Fq 'locally blocked' || fail
section
t || dig -p${DNS_PORT} tracker.xdebian.org @127.0.0.1 | grep -Fq 'locally blocked' || fail
t || dig -p${DNS_PORT} tracker.debian.org @127.0.0.1 | grep -Fqv 'locally blocked' || fail
section
t || curl --insecure -siL https://127.0.0.1:${HTTP_PORT}/ | grep -Fq 'HTTP/2 404' || fail
t || curl --insecure -sL https://127.0.0.1:${HTTP_PORT}/dns-query | grep -Fq 'dnscrypt-proxy local DoH server' || fail
t ||
echo yv4BAAABAAAAAAABAAACAAEAACkQAAAAgAAAAA== | base64 -d |
curl -H'Content-Type: application/dns-message' -H'Accept: application/dns-message' --data-binary @- -D - --insecure https://127.0.0.1:${HTTP_PORT}/dns-query 2>/dev/null |
grep -Fq application/dns-message || fail
kill $(cat /tmp/dnscrypt-proxy.pidfile)
sleep 5
section
t || grep -Fq 'telemetry.example' blocked-names.log || fail
t || grep -Fq 'telemetry.*' blocked-names.log || fail
t || grep -Fq 'tracker.xdebian.org' blocked-names.log || fail
t || grep -Fq 'tracker.*' blocked-names.log || fail
section
t || grep -Fq 'dns.google' blocked-ips.log || fail
t || grep -Fq '8.8.8.8' blocked-ips.log || fail
section
t || grep -Fq 'a.www.dnscrypt-test' nx.log || fail
section
t || grep -Fq 'a.www.dnscrypt-test' nx.log || fail
section
t || grep -Eq 'microsoft.com.*PASS.*[^-]$' query.log || fail
t || grep -Eq 'microsoft.com.*PASS.*-$' query.log || fail
t || grep -Eq 'ipv6.google.com.*SYNTH' query.log || fail
t || grep -Eq 'invalid.*SYNTH' query.log || fail
t || grep -Eq '168.192.in-addr.arpa.*SYNTH' query.log || fail
t || grep -Eq 'darpa.mil.*FORWARD' query.log || fail
t || grep -Eq 'www.darpa.mil.*FORWARD' query.log || fail
t || grep -Eq 'cloakedunregistered.com.*CLOAK' query.log || fail
t || grep -Eq 'www.cloakedunregistered2.com.*CLOAK' query.log || fail
t || grep -Eq 'www.dnscrypt-test.*CLOAK' query.log || fail
t || grep -Eq 'a.www.dnscrypt-test.*NXDOMAIN' query.log || fail
t || grep -Eq 'telemetry.example.*REJECT' query.log || fail
t || grep -Eq 'dns.google.*REJECT' query.log || fail
t || grep -Eq 'tracker.xdebian.org.*REJECT' query.log || fail
t || grep -Eq 'tracker.debian.org.*PASS' query.log || fail
t || grep -Eq '[.].*NS.*PASS' query.log || fail
section
t || grep -Fq 'tracker.debian.org' allowed-names.log || fail
t || grep -Fq '*.tracker.debian' allowed-names.log || fail
section
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test3-dnscrypt-proxy.toml -pidfile /tmp/dnscrypt-proxy.pidfile &
sleep 5
section
t || dig -p${DNS_PORT} A microsoft.com @127.0.0.1 | grep -Fq "NOERROR" || fail
t || dig -p${DNS_PORT} A MICROSOFT.COM @127.0.0.1 | grep -Fq "NOERROR" || fail
kill $(cat /tmp/dnscrypt-proxy.pidfile)
sleep 5
section
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test-odoh-proxied.toml -pidfile /tmp/odoh-proxied.pidfile &
sleep 5
section
t || dig -p${DNS_PORT} A microsoft.com @127.0.0.1 | grep -Fq "NOERROR" || fail
t || dig -p${DNS_PORT} A cloudflare.com @127.0.0.1 | grep -Fq "NOERROR" || fail
kill $(cat /tmp/odoh-proxied.pidfile)
sleep 5
if [ -s error.log ]; then
cat *.log
exit 1
fi

5
.ci/cloaking-rules.txt Normal file
View File

@ -0,0 +1,5 @@
cloakedunregistered.* one.one.one.one
*.cloakedunregistered2.* one.one.one.one # inline comment
=www.dnscrypt-test 192.168.100.100
=www.dnscrypt-test.com 192.168.100.101
=ipv6.dnscrypt-test.com fd02::1

2
.ci/forwarding-rules.txt Normal file
View File

@ -0,0 +1,2 @@
darpa.mil 208.67.222.222

View File

@ -0,0 +1,17 @@
server_names = ['odohtarget']
listen_addresses = ['127.0.0.1:5300']
[query_log]
file = 'query.log'
[static]
[static.'odohtarget']
stamp = 'sdns://BQcAAAAAAAAADm9kb2guY3J5cHRvLnN4Ci9kbnMtcXVlcnk'
[static.'odohrelay']
stamp = 'sdns://hQcAAAAAAAAADDg5LjM4LjEzMS4zOAAYb2RvaC1ubC5hbGVrYmVyZy5uZXQ6NDQzBi9wcm94eQ'
[anonymized_dns]
routes = [
{ server_name='odohtarget', via=['odohrelay'] }
]

View File

@ -0,0 +1,69 @@
server_names = ['public-scaleway-fr']
listen_addresses = ['127.0.0.1:5300']
require_dnssec = true
dnscrypt_ephemeral_keys = true
tls_disable_session_tickets = false
ignore_system_dns = false
lb_strategy = 'p12'
block_ipv6 = true
block_unqualified = true
block_undelegated = true
forwarding_rules = 'forwarding-rules.txt'
cloaking_rules = 'cloaking-rules.txt'
cloak_ptr = true
cache = true
[local_doh]
listen_addresses = ['127.0.0.1:3053']
cert_file = "../dnscrypt-proxy/localhost.pem"
cert_key_file = "../dnscrypt-proxy/localhost.pem"
[query_log]
file = 'query.log'
[nx_log]
file = 'nx.log'
[blocked_names]
blocked_names_file = 'blocked-names.txt'
log_file = 'blocked-names.log'
[blocked_ips]
blocked_ips_file = 'blocked-ips.txt'
log_file = 'blocked-ips.log'
[allowed_names]
allowed_names_file = 'allowed-names.txt'
log_file = 'allowed-names.log'
[schedules]
[schedules.'time-to-sleep']
mon = [{after='21:00', before='7:00'}]
tue = [{after='21:00', before='7:00'}]
wed = [{after='21:00', before='7:00'}]
thu = [{after='21:00', before='7:00'}]
fri = [{after='23:00', before='7:00'}]
sat = [{after='23:00', before='7:00'}]
sun = [{after='21:00', before='7:00'}]
[schedules.'work']
mon = [{after='9:00', before='18:00'}]
tue = [{after='9:00', before='18:00'}]
wed = [{after='9:00', before='18:00'}]
thu = [{after='9:00', before='18:00'}]
fri = [{after='9:00', before='17:00'}]
[sources]
[sources.'public-resolvers']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md']
cache_file = 'public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
prefix = 'public-'
[sources.'relays']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/relays.md', 'https://download.dnscrypt.info/resolvers-list/v2/relays.md']
cache_file = 'relays.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = 'relay-'

View File

@ -0,0 +1,19 @@
server_names = ['myserver']
listen_addresses = ['127.0.0.1:5300']
require_dnssec = true
dnscrypt_ephemeral_keys = true
tls_disable_session_tickets = false
ignore_system_dns = false
lb_strategy = 'p12'
block_ipv6 = true
block_unqualified = true
block_undelegated = true
cache = true
[query_log]
file = 'query.log'
[static]
[static.'myserver']
stamp = 'sdns://AQcAAAAAAAAADjIxMi40Ny4yMjguMTM2IOgBuE6mBr-wusDOQ0RbsV66ZLAvo8SqMa4QY2oHkDJNHzIuZG5zY3J5cHQtY2VydC5mci5kbnNjcnlwdC5vcmc'

65
.github/ISSUE_TEMPLATE/bugs.md vendored Normal file
View File

@ -0,0 +1,65 @@
---
name: "\U0001F41E Issues"
about: Bug reports
title: ''
labels: ''
assignees: ''
---
THE TRACKER IS DEDICATED TO KEEPING TRACK OF *BUGS*,
preferably after they have been already discussed and confirmed to be reproducible.
FOR ASSISTANCE, PLEASE CLOSE THIS FORM AND USE THE DISCUSSIONS SECTION INSTEAD:
https://github.com/DNSCrypt/dnscrypt-proxy/discussions/categories/q-a
~~~
Reported bugs must reproducible in the context described in the "Context" section.
Installation and configuration issues are not bugs, but individual assistance request.
Context: the LATEST version of `dnscrypt-proxy` (precompiled binaries downloaded from this repository) is correctly installed and configured on your system, but something doesn't seem to produce the expected result.
If the bug is not trivial to reproduce on any platform, please include ALL the steps required to reliably duplicate it, on a vanilla, generic install of macOS, Windows, OpenBSD or Ubuntu Linux system, in their most current version.
If you don't have any clear understanding of the issue or can't enumerate the steps to reproduce it, open a discussion instead:
https://github.com/DNSCrypt/dnscrypt-proxy/discussions
## Output of the following commands:
./dnscrypt-proxy -version
./dnscrypt-proxy -check
./dnscrypt-proxy -resolve example.com
- [ ] Initially raised as discussion #...
## *What* is affected by this bug?
## *When* does this occur?
## *Where* does it happen?
## *How* do we replicate the issue?
<!-- Please list all the steps required to reliably replicate it, starting from a newly installed operating system -->
## Expected behavior (i.e. solution)
## Other Comments

22
.github/ISSUE_TEMPLATE/suggestions.md vendored Normal file
View File

@ -0,0 +1,22 @@
---
name: "🙋🏽 Planned changes"
about: List of planned changes
title: ''
labels: ''
assignees: ''
---
The starting point should be a discussion.
https://github.com/DNSCrypt/dnscrypt-proxy/discussions/
Suggestions may be raised as an "Ideas" discussion.
We can then determine if the discussion needs to be escalated into a "planned change" or not.
This will help us ensure that the issue tracker properly reflects ongoing or needed work on the project.
---
- [ ] Initially raised as discussion #...

15
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,15 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Maintain dependencies for Go
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10

12
.github/workflows/autocloser.yml vendored Normal file
View File

@ -0,0 +1,12 @@
name: Autocloser
on: [issues]
jobs:
autoclose:
runs-on: ubuntu-latest
steps:
- name: Autoclose issues that did not follow issue template
uses: roots/issue-closer@v1.2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-close-message: "This issue was automatically closed because it did not follow the issue template. We use the issue tracker exclusively for bug reports and feature additions that have been previously discussed. However, this issue appears to be a support request. Please use the discussion forums for support requests."
issue-pattern: ".*(do we replicate the issue|Expected behavior|raised as discussion|# Impact).*"

32
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: "CodeQL scan"
on:
push:
pull_request:
schedule:
- cron: '0 14 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

95
.github/workflows/releases.yml vendored Normal file
View File

@ -0,0 +1,95 @@
on:
push:
paths:
- "**.go"
- "go.*"
- "**/testdata/**"
- ".ci/**"
- ".git*"
- ".github/workflows/releases.yml"
pull_request:
paths:
- "**.go"
- "go.*"
- "**/testdata/**"
- ".ci/**"
- ".git*"
- ".github/workflows/releases.yml"
name: GitHub CI
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Get the version
id: get_version
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Check out code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1
check-latest: true
id: go
- name: Test suite
run: |
go version
cd .ci
./ci-test.sh
cd -
- name: Build all
if: startsWith(github.ref, 'refs/tags/')
run: |
.ci/ci-build.sh "${{ steps.get_version.outputs.VERSION }}"
- name: Package
if: startsWith(github.ref, 'refs/tags/')
run: |
.ci/ci-package.sh "${{ steps.get_version.outputs.VERSION }}"
- name: Install minisign and sign
if: startsWith(github.ref, 'refs/tags/')
run: |
sudo apt-get -y install libsodium-dev
git clone --depth 1 https://github.com/jedisct1/minisign.git
cd minisign/src
mkdir -p /tmp/bin
cc -O2 -o /tmp/bin/minisign -D_GNU_SOURCE *.c -lsodium
cd -
/tmp/bin/minisign -v
echo '#' > /tmp/minisign.key
echo "${{ secrets.MINISIGN_SK }}" >> /tmp/minisign.key
cd dnscrypt-proxy
echo | /tmp/bin/minisign -s /tmp/minisign.key -Sm *.tar.gz *.zip
ls -l dnscrypt-proxy*
- name: Create release
id: create_release
uses: actions/create-release@v1
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload release assets
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: |
dnscrypt-proxy/*.zip
dnscrypt-proxy/*.tar.gz
dnscrypt-proxy/*.minisig
dnscrypt-proxy/*.msi

View File

@ -0,0 +1,23 @@
name: ShiftLeft Scan
on: push
jobs:
Scan-Build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Perform ShiftLeft Scan
uses: ShiftLeftSecurity/scan-action@master
env:
WORKSPACE: ""
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SCAN_AUTO_BUILD: true
with:
output: reports
- name: Upload report
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: reports

7
.gitignore vendored
View File

@ -10,3 +10,10 @@
dnscrypt-proxy/dnscrypt-proxy2
dnscrypt-proxy/dnscrypt-proxy
.idea
.ci/*.log
.ci/*.md
.ci/*.md.minisig
.ci/test-dnscrypt-proxy.toml
contrib/msi/*.msi
contrib/msi/*.wixpdb
contrib/msi/*.wixobj

View File

@ -1,252 +0,0 @@
language: go
os:
- linux
go:
- 1.x
script:
- gimme --list
- echo $TRAVIS_GO_VERSION
- cd dnscrypt-proxy
- go clean
- env GOOS=windows GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir win32
- ln dnscrypt-proxy.exe win32/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt win32/
- for i in win32/LICENSE win32/*.toml win32/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"'
-cx "$i"; done
- ln ../windows/* win32/
- zip -9 -r dnscrypt-proxy-win32-${TRAVIS_TAG:-dev}.zip win32
- go clean
- env GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir win64
- ln dnscrypt-proxy.exe win64/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt win64/
- for i in win64/LICENSE win64/*.toml win64/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"'
-cx "$i"; done
- ln ../windows/* win64/
- zip -9 -r dnscrypt-proxy-win64-${TRAVIS_TAG:-dev}.zip win64
- go clean
- env GO386=387 GOOS=openbsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir openbsd-i386
- ln dnscrypt-proxy openbsd-i386/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt openbsd-i386/
- tar czpvf dnscrypt-proxy-openbsd_i386-${TRAVIS_TAG:-dev}.tar.gz openbsd-i386
- go clean
- env GOOS=openbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir openbsd-amd64
- ln dnscrypt-proxy openbsd-amd64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt openbsd-amd64/
- tar czpvf dnscrypt-proxy-openbsd_amd64-${TRAVIS_TAG:-dev}.tar.gz openbsd-amd64
- go clean
- env GOOS=freebsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir freebsd-i386
- ln dnscrypt-proxy freebsd-i386/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-i386/
- tar czpvf dnscrypt-proxy-freebsd_i386-${TRAVIS_TAG:-dev}.tar.gz freebsd-i386
- go clean
- env GOOS=freebsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir freebsd-amd64
- ln dnscrypt-proxy freebsd-amd64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-amd64/
- tar czpvf dnscrypt-proxy-freebsd_amd64-${TRAVIS_TAG:-dev}.tar.gz freebsd-amd64
- go clean
- env GOOS=freebsd GOARCH=arm go build -mod vendor -ldflags="-s -w"
- mkdir freebsd-arm
- ln dnscrypt-proxy freebsd-arm/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-arm/
- tar czpvf dnscrypt-proxy-freebsd_arm-${TRAVIS_TAG:-dev}.tar.gz freebsd-arm
- go clean
- env GOOS=freebsd GOARCH=arm GOARM=7 go build -mod vendor -ldflags="-s -w"
- mkdir freebsd-armv7
- ln dnscrypt-proxy freebsd-armv7/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-armv7/
- tar czpvf dnscrypt-proxy-freebsd_armv7-${TRAVIS_TAG:-dev}.tar.gz freebsd-armv7
- go clean
- env GOOS=dragonfly GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir dragonflybsd-amd64
- ln dnscrypt-proxy dragonflybsd-amd64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt dragonflybsd-amd64/
- tar czpvf dnscrypt-proxy-dragonflybsd_amd64-${TRAVIS_TAG:-dev}.tar.gz dragonflybsd-amd64
- go clean
- env GOOS=netbsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir netbsd-i386
- ln dnscrypt-proxy netbsd-i386/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt netbsd-i386/
- tar czpvf dnscrypt-proxy-netbsd_i386-${TRAVIS_TAG:-dev}.tar.gz netbsd-i386
- go clean
- env GOOS=netbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir netbsd-amd64
- ln dnscrypt-proxy netbsd-amd64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt netbsd-amd64/
- tar czpvf dnscrypt-proxy-netbsd_amd64-${TRAVIS_TAG:-dev}.tar.gz netbsd-amd64
- go clean
- env GOOS=solaris GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir solaris-amd64
- ln dnscrypt-proxy solaris-amd64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt solaris-amd64/
- tar czpvf dnscrypt-proxy-solaris_amd64-${TRAVIS_TAG:-dev}.tar.gz solaris-amd64
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir linux-i386
- ln dnscrypt-proxy linux-i386/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-i386/
- tar czpvf dnscrypt-proxy-linux_i386-${TRAVIS_TAG:-dev}.tar.gz linux-i386
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir linux-x86_64
- ln dnscrypt-proxy linux-x86_64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-x86_64/
- tar czpvf dnscrypt-proxy-linux_x86_64-${TRAVIS_TAG:-dev}.tar.gz linux-x86_64
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -mod vendor -ldflags="-s -w"
- mkdir linux-arm
- ln dnscrypt-proxy linux-arm/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-arm/
- tar czpvf dnscrypt-proxy-linux_arm-${TRAVIS_TAG:-dev}.tar.gz linux-arm
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -ldflags="-s -w"
- mkdir linux-arm64
- ln dnscrypt-proxy linux-arm64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-arm64/
- tar czpvf dnscrypt-proxy-linux_arm64-${TRAVIS_TAG:-dev}.tar.gz linux-arm64
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod vendor -ldflags="-s -w"
- mkdir linux-mips
- ln dnscrypt-proxy linux-mips/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips/
- tar czpvf dnscrypt-proxy-linux_mips-${TRAVIS_TAG:-dev}.tar.gz linux-mips
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod vendor -ldflags="-s -w"
- mkdir linux-mipsle
- ln dnscrypt-proxy linux-mipsle/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mipsle/
- tar czpvf dnscrypt-proxy-linux_mipsle-${TRAVIS_TAG:-dev}.tar.gz linux-mipsle
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -mod vendor -ldflags="-s -w"
- mkdir linux-mips64
- ln dnscrypt-proxy linux-mips64/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips64/
- tar czpvf dnscrypt-proxy-linux_mips64-${TRAVIS_TAG:-dev}.tar.gz linux-mips64
- go clean
- env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -mod vendor -ldflags="-s -w"
- mkdir linux-mips64le
- ln dnscrypt-proxy linux-mips64le/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips64le/
- tar czpvf dnscrypt-proxy-linux_mips64le-${TRAVIS_TAG:-dev}.tar.gz linux-mips64le
- go clean
- env GOOS=darwin GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir macos
- ln dnscrypt-proxy macos/
- ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt macos/
- tar czpvf dnscrypt-proxy-macos-${TRAVIS_TAG:-dev}.tar.gz macos
- go clean
- env CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++
CGO_ENABLED=1
GOOS=android GOARCH=arm GOARM=7 go build -mod vendor -ldflags="-s -w"
- mkdir android-arm
- ln dnscrypt-proxy android-arm/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-arm/
- zip -9 -r dnscrypt-proxy-android_arm-${TRAVIS_TAG:-dev}.zip android-arm
- go clean
- env CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++
CGO_ENABLED=1
GOOS=android GOARCH=arm64 go build -mod vendor -ldflags="-s -w"
- mkdir android-arm64
- ln dnscrypt-proxy android-arm64/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-arm64/
- zip -9 -r dnscrypt-proxy-android_arm64-${TRAVIS_TAG:-dev}.zip android-arm64
- go clean
- env CC=i686-linux-android-clang CXX=i686-linux-android-clang++
CGO_ENABLED=1 GOOS=android
GOARCH=386 go build -mod vendor -ldflags="-s -w"
- mkdir android-i386
- ln dnscrypt-proxy android-i386/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-i386/
- zip -9 -r dnscrypt-proxy-android_i386-${TRAVIS_TAG:-dev}.zip android-i386
- go clean
- env CC=x86_64-linux-android-clang CXX=x86_64-linux-android-clang++
CGO_ENABLED=1
GOOS=android GOARCH=amd64 go build -mod vendor -ldflags="-s -w"
- mkdir android-x86_64
- ln dnscrypt-proxy android-x86_64/
- cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-x86_64/
- zip -9 -r dnscrypt-proxy-android_x86_64-${TRAVIS_TAG:-dev}.zip android-x86_64
after_success:
- ls -l dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip
deploy:
provider: releases
api_key:
secure: J3K/wo3oW/ySl6X4Zk5PX+EVy4fa0qa4fbpKNivogch5yjYw2pgrlSvwto9TM12Gxi4tTMKiWYK4YBapNf+tm501s4OyS1G1rJR1fZ+iyaHgGBLD+QppbivZt+P7Do56agSili68Zcgm7MQfZbvOq9z42z3AJ71+UNTJmTp63voaAuyOF/VdLsmJHMd/5nmFJH6mfMrgMs720GCWxFgdq3NRM2AdVldsp1YmNb4qKqIzunmfxqG9TqVlpq35tNOhWA/Ll3rbsiDVeUpBAW5ked/qHyGRkFVk44O6cPSFGe035Txx0JviBshGxsNSP+aJL9T55hIj1dmuk6g5uhPqABU/zcdJvXOv11oqJuV/DGHO31UfVN6u744LJY6Y1lkd+UUNiOJDPGC80+6M2GbP7BFMZiO5qnYkxzktnYg9b6zIPwmj96XZSniDTAn+qemJf2S8rzShvBtWX29Q4odIaCfFUY8i0muowQ4Vep5S5FqVG+r/rQTXOUIUsNv4r/gP/y5hdtOMC2r1VSvWk068upmW6ovCtcmTghSfYcLCG5r+g5OE2mKj9kbx6RQMspewk9+pvOhNZKXsn/AIvvDC4V46MaDjFkdYN0VbsYB5NH11DGCPH7vDwJnAzzMWnofCkiTG07dJYlLUnD9iUgYoNkrxivAgQKnDP8C6Ka0RGdk=
file:
- dnscrypt-proxy-*.tar.gz
- dnscrypt-proxy-*.zip
- dnscrypt-proxy-*.minisig
file_glob: true
skip_cleanup: true
on:
repo: DNSCrypt/dnscrypt-proxy
tags: true
before_deploy:
- mkdir -p /tmp/bin /tmp/lib /tmp/include
- export LD_LIBRARY_PATH=/tmp/lib:LD_LIBRARY_PATH
- export PATH=/tmp/bin:$PATH
- git clone --depth 1 https://github.com/jedisct1/libsodium.git --branch=stable
- cd libsodium
- env ./configure --disable-dependency-tracking --prefix=/tmp
- make -j$(nproc) install
- cd -
- git clone --depth 1 https://github.com/jedisct1/minisign.git
- cd minisign/src
- gcc -O2 -o /tmp/bin/minisign -I/tmp/include -L/tmp/lib *.c -lsodium
- cd -
- minisign -v
- echo '#' > /tmp/minisign.key
- echo "$MINISIGN_SK" >> /tmp/minisign.key
- echo | minisign -s /tmp/minisign.key -Sm dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip
before_install:
- NDK_VER=r18
- curl -LO http://dl.google.com/android/repository/android-ndk-${NDK_VER}-linux-x86_64.zip
- unzip -q android-ndk-${NDK_VER}-linux-x86_64.zip -d $HOME
- rm android-ndk-${NDK_VER}-linux-x86_64.zip
- NDK_TOOLS=$HOME/android-ndk-${NDK_VER}
- NDK_STANDALONE=$HOME/ndk-standalone-${NDK_VER}
- MAKE_TOOLCHAIN=$NDK_TOOLS/build/tools/make_standalone_toolchain.py
- for arch in x86 arm; do python $MAKE_TOOLCHAIN --arch $arch --api 19
--install-dir $NDK_STANDALONE/$arch; PATH=$PATH:$NDK_STANDALONE/$arch/bin; done
- for arch in x86_64 arm64; do python $MAKE_TOOLCHAIN --arch $arch --api 21
--install-dir $NDK_STANDALONE/$arch; PATH=$PATH:$NDK_STANDALONE/$arch/bin; done
- rm -rf $NDK_TOOLS
env:
global:
- secure: cuTXb4v5NwTr1XmkiHGkFir8fMiiBMnraCrls3thdDRlSTix0CiQc/H5Vh8SHauuG6VwVyrCT/Xsf0UQUmnULkPHjvuiNehb+bG4J3fz7hF94glBdQ8vxTuMmnHfJEYTQRLwCsWMBEC1wekw13O8J/0opFNG5neduns3Z1/rD5VSlBwgc8/4lomEp0fadIvzLeS7f5mxeXAD5Z9KBmc09uCjxVoF9Qsk1r901B0c0RMxIbJWyW9ZhDIVr/aEUN/tU0EXMKOA85sizg2moAigb8RZ1WCTh7utLGKpAyQegNk/unkksKMzZFkUCwHkrxlujwoe93wUS4rvZ+3nHMtLQdR+OfMeVs4/zvvQVq2f3bOXgkxgvhq6Bop0RK0xyEJffa5hUFbGNKhIFkLFLn1Ok28t2q7NOFPr0H2egHlkwgPztyhYMYb9C5PW4zd9buI0LS5452C4jXH5raBMfx844wTzaBbN689AKiYb84Qesqczss/o7eC7V48kh823dlZ/s2//gtp1ceqdAtNvp4dy7X/ECA/vNlpYisrtkR/CsFpJjGoTvS37leVMpmc5bn39dkoa5ZLliu7CaFRefbavcWWEImVStll9FBQ6+Ck9+41gczl9Rr7eGIV9ZZ/fmdkLNgxIpoAhZRee/dZD+/0gUExHxXXn10MqPuNVytVPiuU=

341
ChangeLog
View File

@ -1,7 +1,282 @@
* Version 2.0.33
# Version 2.1.5
- dnscrypt-proxy can be compiled with Go 1.21.0+
- Responses to blocked queries now include extended error codes
- Reliability of connections using HTTP/3 has been improved
- New configuration directive: `tls_key_log_file`. When defined, this
is the path to a file where TLS secret keys will be written to, so
that DoH traffic can be locally inspected.
# Version 2.1.4
- Fixes a regression from version 2.1.3: when cloaking was enabled,
blocked responses were returned for records that were not A/AAAA/PTR
even for names that were not in the cloaked list.
# Version 2.1.3
- DNS-over-HTTP/3 (QUIC) should be more reliable. In particular,
version 2.1.2 required another (non-QUIC) resolver to be present for
bootstrapping, or the resolver's IP address to be present in the
stamp. This is not the case any more.
- dnscrypt-proxy is now compatible with Go 1.20+
- Commands (-check, -show-certs, -list, -list-all) now ignore log
files and directly output the result to the standard output.
- The `cert_ignore_timestamp` configuration switch is now documented.
It allows ignoring timestamps for DNSCrypt certificate verification,
until a first server is available. This should only be used on devices
that don't have any ways to set the clock before DNS service is up.
However, a safer alternative remains to use an NTP server with a fixed
IP address (such as time.google.com), configured in the captive portals
file.
- Cloaking: when a name is cloaked, unsupported record types now
return a blocked response rather than the actual records.
- systemd: report Ready earlier as dnscrypt-proxy can itself manage
retries for updates/refreshes.
# Version 2.1.2
- Support for DoH over HTTP/3 (DoH3, HTTP over QUIC) has been added.
Compatible servers will automatically use it. Note that QUIC uses UDP
(usually over port 443, like DNSCrypt) instead of TCP.
- In previous versions, memory usage kept growing due to channels not
being properly closed, causing goroutines to pile up. This was fixed,
resulting in an important reduction of memory usage. Thanks to
@lifenjoiner for investigating and fixing this!
- DNS64: `CNAME` records are now translated like other responses.
Thanks to @ignoramous for this!
- A relay whose name has been configured, but doesn't exist in the
list of available relays is now a hard error. Thanks to @lifenjoiner!
- Mutexes/locking: bug fixes and improvements, by @ignoramous
- Official packages now include linux/riscv64 builds.
- `dnscrypt-proxy -resolve` now reports if ECS (EDNS-clientsubnet) is
supported by the server.
- `dnscrypt-proxy -list` now includes ODoH (Oblivious DoH) servers.
- Local DoH: queries made using the `GET` method are now handled.
- The service can now be installed on OpenRC-based systems.
- `PTR` queries are now supported for cloaked domains. Contributed by
Ian Bashford, thanks!
# Version 2.1.1
This is a bugfix only release, addressing regressions introduced in
version 2.1.0:
- When using DoH, cached responses were not served any more when
experiencing connectivity issues. This has been fixed.
- Time attributes in allow/block lists were ignored. This has been
fixed.
- The TTL as served to clients is now rounded and starts decreasing
before the first query is received.
- Time-based rules are properly handled again in
generate-domains-blocklist.
- DoH/ODoH: entries with an IP address and using a non-standard port
used to require help from a bootstrap resolver. This is not the case
any more.
# Version 2.1.0
- `dnscrypt-proxy` now includes support for Oblivious DoH.
- If the proxy is overloaded, cached and synthetic queries now keep being
served, while non-cached queries are delayed.
- A deprecation warning was added for `fallback_resolvers`.
- Source URLs are now randomized.
- On some platforms, redirecting the application log to a file was not
compatible with user switching; this has been fixed.
- `fallback_resolvers` was renamed to `bootstrap_resolvers` for
clarity. Please update your configuration file accordingly.
# Version 2.0.45
- Configuration changes (to be required in versions 2.1.x):
* `[blacklist]` has been renamed to `[blocked_names]`
* `[ip_blacklist]` has been renamed to `[blocked_ips]`
* `[whitelist]` has been renamed to `[allowed_names]`
* `generate-domains-blacklist.py` has been renamed to
`generate-domains-blocklist.py`, and the configuration files have been
renamed as well.
- `dnscrypt-proxy -resolve` has been completely revamped, and now requires
the configuration file to be accessible. It will send a query to an IP address
of the `dnscrypt-proxy` server by default. Sending queries to arbitrary
servers is also supported with the new `-resolve name,address` syntax.
- Relay lists can be set to `*` for automatic relay selection. When a wildcard
is used, either for the list of servers or relays, the proxy ensures that
relays and servers are on distinct networks.
- Lying resolvers are detected and reported.
- New return code: `NOT_READY` for queries received before the proxy has
been initialized.
- Server lists can't be older than a week any more, even if directory
permissions are incorrect and cache files cannot be written.
- macOS/arm64 is now officially supported.
- New feature: `allowed_ips`, to configure a set of IP addresses to
never block no matter what DNS name resolves to them.
- Hard-coded IP addresses can be immediately returned for test queries
sent by operating systems in order to check for connectivity and captive
portals. Such responses can be sent even before an interface is considered
as enabled by the operating system. This can be configured in a new section
called `[captive_portals]`.
- On Linux, OpenBSD and FreeBSD, `listen_addresses` can now include IP
addresses that haven't been assigned to an interface yet.
- The logo has been tweaked to look fine on a dark background.
- `generate-domains-blocklist.py`: regular expressions are now ignored in
time-based entries.
- Minor bug fixes and logging improvements.
- Cloaking plugin: if an entry has multiple IP addresses for a type,
all the IP addresses are now returned instead of a random one.
- Static entries can now include DNSCrypt relays.
- Name blocking: aliases relying on `SVCB` and `HTTPS` records can now
be blocked in addition to aliases via regular `CNAME` records.
- EDNS-Client-Subnet information can be added to outgoing queries.
Instead of sending the actual client IP, ECS information is user
configurable, and IP addresses will be randomly chosen for every query.
- Initial DoH queries are now checked using random names in order to
properly measure CDNs such as Tencent that ignore the padding.
- DoH: the `max-stale` cache control directive is now present in queries.
- Logs can now be sent to `/dev/stdout` instead of actual files.
- User switching is now supported on macOS.
- New download mirror (https://download.dnscrypt.net) for resolvers,
relays and parental-control.
Thanks to the nice people who contributed to this release:
- Ian Bashford
- Will Elwood
- Alison Winters
- Krish De Souza
- @hugepants
- @IceCodeNew
- @lifenjoiner
- @mibere
- @jacob755
- @petercooperjr
- @yofiji
# Version 2.0.44
- More updates to the set of block lists, thanks again to IceCodeNew.
- Netprobes and listening sockets are now ignored when the `-list`,
`-list-all`, `-show-certs` or `-check` command-line switches are used.
- `tls_client_auth` was renamed to `doh_client_x509_auth`. A section
with the previous name is temporarily ignored if empty, but will error
out if not.
- Unit tests are now working on 32-bit systems. Thanks to Will Elwood
and @lifenjoiner.
# Version 2.0.43
- Built-in support for DNS64 translation has been implemented.
(Contributed by Sergey Smirnov, thanks!)
- Connections to DoH servers can be authenticated using TLS client
certificates (Contributed by Kevin O'Sullivan, thanks!)
- Multiple stamps are now allowed for a single server in resolvers
and relays lists.
- Android: the time zone for log files is now set to the system
time zone.
- Quite a lot of updates and additions have been made to the
example domain block lists. Thanks to `IceCodeNew`!
- Cached configuration files can now be temporarily used if
they are out of date, but bootstraping is impossible. Contributed by
`lifenjoiner`, thanks!
- Precompiled macOS binaries are now notarized.
- `generate-domains-blacklists` now tries to deduplicate entries
clobbered by wildcard rules. Thanks to `Huhni`!
- `generate-domains-blacklists` can now directly write lists to a
file with the `-o` command-line option.
- cache files are now downloaded as the user the daemon will be running
as. This fixes permission issues at startup time.
- Forwarded queries are now subject to global timeouts, and can be
forced to use TCP.
- The `ct` parameter has been removed from DoH queries, as Google doesn't
require it any more.
- Service installation is now supported on FreeBSD.
- When stored into a file, service logs now only contain data from the most
recent launch. This can be changed with the new `log_file_latest` option.
- Breaking change: the `tls_client_auth` section was renamed to
`doh_client_x509_auth`. If you had a tls_client_auth section in the
configuration file, it needs to be updated.
# Version 2.0.42
- The current versions of the `dnsdist` load balancer (presumably used
by quad9, cleanbrowsing, qualityology, freetsa.org, ffmuc.net,
opennic-bongobow, sth-dnscrypt-se, ams-dnscrypt-nl and more)
is preventing queries over 1500 bytes from being received over UDP.
Temporary workarounds have been introduced to improve reliability
with these resolvers for regular DNSCrypt. Unfortunately, anonymized
DNS cannot be reliable until the issue is fixed server-side. `dnsdist`
authors are aware of it and are working on a fix.
- New option in the `[anonymized_dns]` section: `skip_incompatible`,
to ignore resolvers incompatible with Anonymized DNS instead of
using them without a relay.
- The server latency benchmark is faster while being able to perform
more retries if necessary.
- Continuous integration has been moved to GitHub Actions.
# Version 2.0.41
- Precompiled ARM binaries are compatible with ARMv5 CPUs. The
default arm builds were not compatible with older CPUs when compiled
with Go 1.14. mips64 binaries are explicitly compiled with `softfloat`
to improve compatibility.
- Quad9 seems to be only blocking fragmented queries over UDP for
some networks. They have been removed from the default list of broken
resolvers; runtime detection of support for fragments should now do
the job.
- Runtime detection of support for fragments was actually enabled.
# Version 2.0.40
- Servers blocking fragmented queries are now automatically detected.
- The server name is now only present in query logs when an actual
upstream servers was required to resolve a query.
- TLS client authentication has been added for DoH.
- The Firefox plugin is now skipped for connections coming from the
local DoH server.
- DoH RTT computation is now more accurate, especially when CDNs are
in the middle.
- The forwarding plugin is now more reliable, and handles retries over
TCP.
# Version 2.0.39
- The Firefox Local DoH service didn't properly work in version 2.0.38;
this has been fixed. Thanks to Simon Brand for the report!
# Version 2.0.38
- Entries from lists (forwarding, blacklists, whitelists) now support
inline comments.
- Reliability improvement: queries over UDP are retried after a timeout
instead of solely relying on the client.
- Reliability improvement: during temporary network outages, cached records
are now served even if they are stale.
- Bug fix: SOCKS proxies and DNS relays can be combined.
- New feature: multiple fallback resolvers are now supported (see the
new `fallback_resolvers` option. Note that `fallback_resolver` is
still supported for backward compatibility).
- Windows: the service can be installed with a configuration file
stored separately from the application.
- Security (affecting DoH): precompiled binaries of dnscrypt-proxy 2.0.37 are
built using Go 1.13.7 that fixes a TLS certificate parsing issue present in
previous versions of the compiler.
# Version 2.0.36
- New option: `block_undelegated`. When enabled, `dnscrypt-proxy` will
directly respond to queries for locally-served zones (https://sk.tl/2QqB971U)
and nonexistent zones that should have been kept local, but are frequently
leaked. This reduces latency and improves privacy.
- Conformance: the `DO` bit is now set in synthetic responses if it was
set in a question, and the `AD` bit is cleared.
- The `miegkg/dns` module was updated to version 1.1.26, that fixes a
security issue affecting non-encrypted/non-authenticated DNS traffic. In
`dnscrypt-proxy`, this only affects the forwarding feature.
# Version 2.0.35
- New option: `block_unqualified` to block `A`/`AAAA` queries with
unqualified host names. These will very rarely get an answer from upstream
resolvers, but can leak private information to these, as well as to root
servers.
- When a `CNAME` pointer is blocked, the original query name is now logged
along with the pointer. This makes it easier to know what the original
query name, so it can be whitelisted, or what the pointer was, so it
can be removed from the blacklist.
# Version 2.0.34
- Blacklisted names are now also blocked if they appear in `CNAME`
pointers.
- `dnscrypt-proxy` can now act as a local DoH *server*. Firefox can
be configured to use it, so that ESNI can be enabled without bypassing
your DNS proxy.
# Version 2.0.33
- Fixes an issue that caused some valid queries to return `PARSE_ERROR`.
* Version 2.0.32
# Version 2.0.32
- On certificate errors, the server name is now logged instead of the
provider name, which is generally more useful.
- IP addresses for DoH servers that require DNS lookups are now cached
@ -25,18 +300,18 @@ work by Alison Winters, thanks!
but it includes a `SERVFAIL` error code).
- Responses are now always compressed.
* Version 2.0.31
# Version 2.0.31
- This version fixes two regressions introduced in version 2.0.29:
DoH server couldn't be reached over IPv6 any more, and the proxy
couldn't be interrupted while servers were being benchmarked.
* Version 2.0.30
# Version 2.0.30
- This version fixes a startup issue introduced in version 2.0.29,
on systems for which the service cannot be automatically installed
(such as OpenBSD and FreeBSD). Reported by @5ch17 and Vinícius Zavam,
and fixed by Will Elwood, thanks!
* Version 2.0.29
# Version 2.0.29
- Support for Anonymized DNS has been added!
- Wait before stopping, fixing an issue with Unbound (thanks to
Vladimir Bauer)
@ -54,7 +329,7 @@ Linnala)
Bauer)
- A lot of internal cleanups, thanks to Markus Linnala.
* Version 2.0.28
# Version 2.0.28
- Invalid server entries are now skipped instead of preventing a
source from being used. Thanks to Alison Winters for the contribution!
- Truncated responses are immediately retried over TCP instead of
@ -67,14 +342,14 @@ bytes. This also reduces latency.
and cloaked responses. And the forwarder is logged instead of the
regular server for forwarded responses.
* Version 2.0.27
# Version 2.0.27
- The X25519 implementation was changed from using the Go standard
implementation to using Cloudflare's CIRCL library. Unfortunately,
CIRCL appears to be broken on big-endian systems. That change has been
reverted.
- All the dependencies have been updated.
* Version 2.0.26
# Version 2.0.26
- A new plugin was added to prevent Firefox from bypassing the system
DNS settings.
- New configuration parameter to set how to respond to blocked
@ -94,12 +369,12 @@ bootstrapped.
- A new option, `query_meta`, is now available to add optional records
to client queries.
* Version 2.0.25
# Version 2.0.25
- The example IP address for network probes didn't work on Windows.
The example configuration file has been updated and the fallback
resolver IP is now used when no netprobe address has been configured.
* Version 2.0.24
# Version 2.0.24
- The query log now includes the time it took to complete the
transaction, the name of the resolver that sent the response and if
the response was served from the cache. Thanks to Ferdinand Holzer for
@ -121,7 +396,7 @@ Thanks to @inkblotadmirer for the report.
- Resolvers are now tried in random order to avoid favoring the first
ones at startup.
* Version 2.0.23
# Version 2.0.23
- Binaries for FreeBSD/armv7 are now available.
- .onion servers are now automatically ignored if Tor routing is not
enabled.
@ -130,15 +405,15 @@ using proxies.
- DNSCrypt communications are now automatically forced to using TCP
when a SOCKS proxy has been set up.
* Version 2.0.22
# Version 2.0.22
- The previous version had issues with the .org TLD when used in
conjunction with dnsmasq. This has been fixed.
* Version 2.0.21
# Version 2.0.21
- The change to run the Windows service as `NT AUTHORITY\NetworkService`
has been reverted, as it was reported to break logging (Windows only).
* Version 2.0.20
# Version 2.0.20
- Startup is now *way* faster, especially when using DoH servers.
- A new action: `CLOAK` is logged when queries are being cloaked.
- A cloaking rule can now map to multiple IPv4 and IPv6 addresses,
@ -152,7 +427,7 @@ generate-domains-blacklist.py script.
script.
- The Windows service is now installed as `NT AUTHORITY\NetworkService`.
* Version 2.0.19
# Version 2.0.19
- The value for `netprobe_timeout` was read from the command-line, but
not from the configuration file any more. This is a regression introduced
in the previous version, that has been fixed.
@ -161,7 +436,7 @@ in the previous version, that has been fixed.
queries with the POST method in order to work around badly configured
proxies.
* Version 2.0.18
# Version 2.0.18
- Official builds now support TLS 1.3.
- The timeout for the initial connectivity check can now be set from
the command line.
@ -170,7 +445,7 @@ the command line.
- In addition to SOCKS, HTTP and HTTPS proxies are now supported for
DoH servers.
* Version 2.0.17
# Version 2.0.17
- Go >= 1.11 is now supported
- The flipside is that Windows XP is not supported any more :(
- When dropping privileges, there is no supervisor process any more.
@ -179,7 +454,7 @@ of flags and payload sizes. This is not the case any more.
- DoH queries are smaller, since workarounds are not required any more
after Google updated their implementation.
* Version 2.0.16
# Version 2.0.16
- On Unix-like systems, the server can run as an unprivileged user,
and the main process will automatically restart if an error occurs.
- pledge() on OpenBSD.
@ -191,7 +466,7 @@ cloaking module for local development.
- The proxy doesn't quit any more if new TCP connections cannot be
created.
* Version 2.0.15
# Version 2.0.15
- Support for proxies (HTTP/SOCKS) was added. All it takes to route
all TCP queries to Tor is add `proxy = "socks5://127.0.0.1:9050"` to
the configuration file.
@ -200,16 +475,16 @@ transaction.
- Pre-built binaries for Linux are statically linked on all
architectures.
* Version 2.0.14
# Version 2.0.14
- Supports DNS-over-HTTPS draft 08.
- Netprobes don't use port 0 by default, as this causes issues with
Little Snitch and FreeBSD.
* Version 2.0.13
# Version 2.0.13
- This version fixes a crash when using DoH for queries whose size
were a multiple of the block size. Reported by @char101, thanks!
* Version 2.0.12
# Version 2.0.12
- Further compatibility fixes for Alpine Linux/i386 and Android/i386
have been made. Thanks to @aead for his help!
- The proxy will now wait for network connectivity before starting.
@ -218,7 +493,7 @@ before the network is fully configured.
- The IPv6 blocking module now returns synthetic SOA records to
improve compatibility with downstream resolvers and stub resolvers.
* Version 2.0.11
# Version 2.0.11
- This release fixes a long-standing bug that caused the proxy to
block or crash when Position-Independent Executables were produced.
This bug only showed up when compiled on (not for) Alpine Linux and
@ -226,13 +501,13 @@ Android, for some CPU architectures.
- New configuration settings: cache_neg_min_ttl and
cache_neg_max_ttl, to clamp the negative caching TTL.
* Version 2.0.10
# Version 2.0.10
- This version fixes a crash when an incomplete size is sent by a
local client for a query over TCP.
- Slight performance improvement of DNSCrypt on non-Intel CPUs such
as Raspberry Pi.
* Version 2.0.9
# Version 2.0.9
- Whitelists have been implemented: one a name matches a pattern in
the whitelist, rules from the name-based and IP-based blacklists will
be bypassed. Whitelists support the same patterns as blacklists, as
@ -258,7 +533,7 @@ especially on Mips and ARM systems.
- The ephemeral keys mode of dnscrypt-proxy v1.x was reimplemented: this
creates a new unique key for every single query.
* Version 2.0.8
# Version 2.0.8
- Multiple URLs can be defined for a source in order to improve
resiliency when servers are temporarily unreachable.
- Connections over IPv6 will be preferred over IPv4 for DoH servers
@ -272,41 +547,41 @@ Android/x86.
- `dnscrypt-proxy -list -json` and `-list-all -json` now include the
remove servers names and IP addresses.
* Version 2.0.7
# Version 2.0.7
- Bug fix: optional ports were not properly parsed with IPv6
addresses -- thanks to @bleeee for the report and fix.
- Bug fix: truncate TCP queries to the prefixed length.
- Certificates are force-refreshed after a time jump (e.g. when a
system resumes from hibernation).
* Version 2.0.6
# Version 2.0.6
- Automatic log files rotation was finally implemented.
- A new -pidfile command-line option to write the PID file was added.
* Version 2.0.5
# Version 2.0.5
- Fixes a crash occasionally happening when using DoH servers, with
stamps not containing any IP addresses, a DNSSEC-signed name, a
non-working system DNS configuration, and a fallback server supporting
DNSSEC.
* Version 2.0.4
# Version 2.0.4
- Fixes a regression with truncated packets. Thanks to @mazesy and
@the-w1nd for spotting a case triggering this!
* Version 2.0.3
# Version 2.0.3
- Load balancing: resolvers that respond promptly, but with bogus
responses are now gradually removed from the preferred pool.
- Due to popular request, Android binaries are now available! Thanks
to @sporif for his help on getting these built.
- Binaries are built using Go 1.10-final.
* Version 2.0.2
# Version 2.0.2
- Properly error out on FreeBSD and other platforms where built-in
service installation is not supported yet.
- Improved load-balancing algorithm, which should result in lower
latency.
* Version 2.0.1
# Version 2.0.1
- Cached source data were not redownloaded if the proxy was used
without interruption. This has been fixed.
- If the network is down at startup time, fall back to cached source

33
LICENSE
View File

@ -1,18 +1,15 @@
/*
* ISC License
*
* Copyright (c) 2018
* Frank Denis <j at pureftpd dot org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
ISC License
Copyright (c) 2018-2023, Frank Denis <j at pureftpd dot org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,13 +1,19 @@
# ![dnscrypt-proxy 2](https://raw.github.com/dnscrypt/dnscrypt-proxy/master/logo.png?3)
[![Financial Contributors on Open Collective](https://opencollective.com/dnscrypt/all/badge.svg?label=financial+contributors)](https://opencollective.com/dnscrypt) [![DNSCrypt-Proxy Release](https://img.shields.io/github/release/dnscrypt/dnscrypt-proxy.svg?label=Latest%20Release&style=popout)](https://github.com/dnscrypt/dnscrypt-proxy/releases/latest) [![Build Status](https://travis-ci.org/dnscrypt/dnscrypt-proxy.svg?branch=master)](https://travis-ci.org/dnscrypt/dnscrypt-proxy?branch=master) [![#dnscrypt-proxy:matrix.org](https://img.shields.io/matrix/dnscrypt-proxy:matrix.org.svg?label=DNSCrypt-Proxy%20Matrix%20Chat&server_fqdn=matrix.org&style=popout)](https://matrix.to/#/#dnscrypt-proxy:matrix.org)
[![Financial Contributors on Open Collective](https://opencollective.com/dnscrypt/all/badge.svg?label=financial+contributors)](https://opencollective.com/dnscrypt)
[![DNSCrypt-Proxy Release](https://img.shields.io/github/release/dnscrypt/dnscrypt-proxy.svg?label=Latest%20Release&style=popout)](https://github.com/dnscrypt/dnscrypt-proxy/releases/latest)
[![Build Status](https://github.com/DNSCrypt/dnscrypt-proxy/actions/workflows/releases.yml/badge.svg)](https://github.com/DNSCrypt/dnscrypt-proxy/actions/workflows/releases.yml)
![CodeQL scan](https://github.com/DNSCrypt/dnscrypt-proxy/workflows/CodeQL%20scan/badge.svg)
![ShiftLeft Scan](https://github.com/DNSCrypt/dnscrypt-proxy/workflows/ShiftLeft%20Scan/badge.svg)
[![#dnscrypt-proxy:matrix.org](https://img.shields.io/matrix/dnscrypt-proxy:matrix.org.svg?label=DNSCrypt-Proxy%20Matrix%20Chat&server_fqdn=matrix.org&style=popout)](https://matrix.to/#/#dnscrypt-proxy:matrix.org)
## Overview
A flexible DNS proxy, with support for modern encrypted DNS protocols such as [DNSCrypt v2](https://dnscrypt.info/protocol), [DNS-over-HTTPS](https://www.rfc-editor.org/rfc/rfc8484.txt) and [Anonymized DNSCrypt](https://github.com/DNSCrypt/dnscrypt-protocol/blob/master/ANONYMIZED-DNSCRYPT.txt).
A flexible DNS proxy, with support for modern encrypted DNS protocols such as [DNSCrypt v2](https://dnscrypt.info/protocol), [DNS-over-HTTPS](https://www.rfc-editor.org/rfc/rfc8484.txt), [Anonymized DNSCrypt](https://github.com/DNSCrypt/dnscrypt-protocol/blob/master/ANONYMIZED-DNSCRYPT.txt) and [ODoH (Oblivious DoH)](https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v3/odoh-servers.md).
* [dnscrypt-proxy documentation](https://dnscrypt.info/doc) This project's documentation (Wiki)
* **[dnscrypt-proxy documentation](https://dnscrypt.info/doc) ← Start here**
* [DNSCrypt project home page](https://dnscrypt.info/)
* [Discussions](https://github.com/DNSCrypt/dnscrypt-proxy/discussions)
* [DNS-over-HTTPS and DNSCrypt resolvers](https://dnscrypt.info/public-servers)
* [Server and client implementations](https://dnscrypt.info/implementations)
* [DNS stamps](https://dnscrypt.info/stamps)
@ -19,7 +25,7 @@ Available as source code and pre-built binaries for most operating systems and a
## Features
* DNS traffic encryption and authentication. Supports DNS-over-HTTPS (DoH) using TLS 1.3, DNSCrypt and Anonymized DNS
* DNS traffic encryption and authentication. Supports DNS-over-HTTPS (DoH) using TLS 1.3 and QUIC, DNSCrypt, Anonymized DNS and ODoH
* Client IP addresses can be hidden using Tor, SOCKS proxies or Anonymized DNS relays
* DNS query monitoring, with separate log files for regular and suspicious queries
* Filtering: block ads, malware, and other unwanted content. Compatible with all DNS services
@ -32,6 +38,7 @@ Available as source code and pre-built binaries for most operating systems and a
* Automatic background updates of resolvers lists
* Can force outgoing connections to use TCP
* Compatible with DNSSEC
* Includes a local DoH server in order to support ECH (ESNI)
## Pre-built binaries
@ -53,7 +60,8 @@ Up-to-date, pre-built binaries are available for:
* Linux/mips64le
* Linux/x86
* Linux/x86_64
* MacOS X
* macOS/arm64
* macOS/x86_64
* NetBSD/x86
* NetBSD/x86_64
* OpenBSD/x86
@ -67,7 +75,7 @@ How to use these files, as well as how to verify their signatures, are documente
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
This project exists thanks to all the people who contribute.
<a href="https://github.com/dnscrypt/dnscrypt-proxy/graphs/contributors"><img src="https://opencollective.com/dnscrypt/contributors.svg?width=890&button=false" /></a>
### Financial Contributors

21
contrib/msi/Dockerfile Normal file
View File

@ -0,0 +1,21 @@
FROM ubuntu:latest
MAINTAINER dnscrypt-authors
RUN apt-get update && \
apt-get install -y wget wine dotnet-sdk-6.0 && \
dpkg --add-architecture i386 && apt-get update && apt-get install -y wine32
ENV WINEPREFIX=/root/.wine32 WINEARCH=win32 WINEDEBUG=-all
RUN wget https://dl.winehq.org/wine/wine-mono/8.1.0/wine-mono-8.1.0-x86.msi && \
WINEPREFIX="$HOME/.wine32" WINEARCH=win32 wineboot --init && \
WINEPREFIX="$HOME/.wine32" WINEARCH=win32 wine msiexec /i wine-mono-8.1.0-x86.msi && \
mkdir $WINEPREFIX/drive_c/temp && \
apt-get install -y unzip && \
wget https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip -nv -O wix.zip && \
unzip wix.zip -d /wix && \
rm -f wix.zip
WORKDIR /wix

13
contrib/msi/README.md Normal file
View File

@ -0,0 +1,13 @@
# Scripts and utilities related to building an .msi (Microsoft Standard Installer) file.
## Docker test image for building an MSI locally
```sh
docker build . -f Dockerfile -t ubuntu:dnscrypt-msi
```
## Test building msi files for intel win32 & win64
```sh
./build.sh
```

30
contrib/msi/build.sh Executable file
View File

@ -0,0 +1,30 @@
#! /bin/sh
version=0.0.0
gitver=$(git describe --tags --always --match="[0-9]*.[0-9]*.[0-9]*" --exclude='*[^0-9.]*')
if [ "$gitver" != "" ]; then
version=$gitver
fi
# build the image by running: docker build . -f Dockerfile -t ubuntu:dnscrypt-msi
if [ "$(docker image list -q ubuntu:dnscrypt-msi)" = "" ]; then
docker build . -f Dockerfile -t ubuntu:dnscrypt-msi
fi
image=ubuntu:dnscrypt-msi
for arch in x64 x86; do
binpath="win32"
if [ "$arch" = "x64" ]; then
binpath="win64"
fi
src=$(
cd ../../dnscrypt-proxy/$binpath || exit
pwd
)
echo "$src"
docker run --rm -v "$(pwd)":/wixproj -v "$src":/src $image wine candle.exe -dVersion="$version" -dPlatform=$arch -dPath=\\src -arch $arch \\wixproj\\dnscrypt.wxs -out \\wixproj\\dnscrypt-$arch.wixobj
docker run --rm -v "$(pwd)":/wixproj -v "$src":/src $image wine light.exe -out \\wixproj\\dnscrypt-proxy-$arch-"$version".msi \\wixproj\\dnscrypt-$arch.wixobj -sval
done

60
contrib/msi/dnscrypt.wxs Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0"?>
<?if $(var.Platform)="x64" ?>
<?define Program_Files="ProgramFiles64Folder"?>
<?else ?>
<?define Program_Files="ProgramFilesFolder"?>
<?endif ?>
<?ifndef var.Version?>
<?error Undefined Version variable?>
<?endif ?>
<?ifndef var.Path?>
<?error Undefined Path variable?>
<?endif ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
UpgradeCode="fbf99dd8-c21e-4f9b-a632-de53bb64c45e"
Name="dnscrypt-proxy"
Version="$(var.Version)"
Manufacturer="DNSCrypt"
Language="1033">
<Package InstallerVersion="200" Compressed="yes" Comments="Windows Installer Package" InstallScope="perMachine" />
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
<MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
<Upgrade Id="fbf99dd8-c21e-4f9b-a632-de53bb64c45e">
<UpgradeVersion Minimum="$(var.Version)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
<UpgradeVersion Minimum="2.1.0" Maximum="$(var.Version)" IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>
<Condition Message="A newer version of this software is already installed.">NOT NEWERVERSIONDETECTED</Condition>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.Program_Files)">
<Directory Id="INSTALLDIR" Name="DNSCrypt">
<Component Id="ApplicationFiles" Guid="7d693c0b-71d8-436a-9c84-60a11dc74092">
<File Id="dnscryptproxy.exe" KeyPath="yes" Source="$(var.Path)\dnscrypt-proxy.exe" DiskId="1"/>
<File Source="$(var.Path)\LICENSE"></File>
<File Source="$(var.Path)\service-install.bat"></File>
<File Source="$(var.Path)\service-restart.bat"></File>
<File Source="$(var.Path)\service-uninstall.bat"></File>
<File Source="$(var.Path)\example-dnscrypt-proxy.toml"></File>
</Component>
<Component Id="ConfigInstall" Guid="db7b691e-f7c7-4c9a-92e1-c6f21ce6430f" KeyPath="yes">
<Condition><![CDATA[CONFIGFILE]]></Condition>
<CopyFile Id="dnscryptproxytoml" DestinationDirectory="INSTALLDIR" DestinationName="dnscrypt-proxy.toml" SourceProperty="CONFIGFILE">
</CopyFile>
<RemoveFile Id="RemoveConfig" Directory="INSTALLDIR" Name="dnscrypt-proxy.toml" On="uninstall" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="Complete" Level="1">
<ComponentRef Id="ApplicationFiles" />
<ComponentRef Id="ConfigInstall" />
</Feature>
</Product>
</Wix>

View File

@ -1,12 +0,0 @@
version = 1
test_patterns = [ ]
exclude_patterns = [ ]
[[analyzers]]
name = 'go'
enabled = true
[analyzers.meta]
import_path = 'github.com/dnscrypt/dnscrypt-proxy/dnscrypt-proxy'

203
dnscrypt-proxy/coldstart.go Normal file
View File

@ -0,0 +1,203 @@
package main
import (
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type CaptivePortalEntryIPs []net.IP
type CaptivePortalMap map[string]CaptivePortalEntryIPs
type CaptivePortalHandler struct {
wg sync.WaitGroup
cancelChannel chan struct{}
}
func (captivePortalHandler *CaptivePortalHandler) Stop() {
close(captivePortalHandler.cancelChannel)
captivePortalHandler.wg.Wait()
}
func (ipsMap *CaptivePortalMap) GetEntry(msg *dns.Msg) (*dns.Question, *CaptivePortalEntryIPs) {
if len(msg.Question) != 1 {
return nil, nil
}
question := &msg.Question[0]
name, err := NormalizeQName(question.Name)
if err != nil {
return nil, nil
}
ips, ok := (*ipsMap)[name]
if !ok {
return nil, nil
}
if question.Qclass != dns.ClassINET {
return nil, nil
}
return question, &ips
}
func HandleCaptivePortalQuery(msg *dns.Msg, question *dns.Question, ips *CaptivePortalEntryIPs) *dns.Msg {
respMsg := EmptyResponseFromMessage(msg)
ttl := uint32(1)
if question.Qtype == dns.TypeA {
for _, xip := range *ips {
if ip := xip.To4(); ip != nil {
rr := new(dns.A)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
rr.A = ip
respMsg.Answer = append(respMsg.Answer, rr)
}
}
} else if question.Qtype == dns.TypeAAAA {
for _, xip := range *ips {
if xip.To4() == nil {
if ip := xip.To16(); ip != nil {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = ip
respMsg.Answer = append(respMsg.Answer, rr)
}
}
}
}
qType, ok := dns.TypeToString[question.Qtype]
if !ok {
qType = fmt.Sprint(question.Qtype)
}
dlog.Infof("Query for captive portal detection: [%v] (%v)", question.Name, qType)
return respMsg
}
func handleColdStartClient(clientPc *net.UDPConn, cancelChannel chan struct{}, ipsMap *CaptivePortalMap) bool {
buffer := make([]byte, MaxDNSPacketSize-1)
clientPc.SetDeadline(time.Now().Add(time.Duration(1) * time.Second))
length, clientAddr, err := clientPc.ReadFrom(buffer)
exit := false
select {
case <-cancelChannel:
exit = true
default:
}
if exit {
return true
}
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
return false
}
if err != nil {
dlog.Warn(err)
return true
}
packet := buffer[:length]
msg := &dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return false
}
question, ips := ipsMap.GetEntry(msg)
if ips == nil {
return false
}
respMsg := HandleCaptivePortalQuery(msg, question, ips)
if respMsg == nil {
return false
}
if response, err := respMsg.Pack(); err == nil {
clientPc.WriteTo(response, clientAddr)
}
return false
}
func addColdStartListener(
ipsMap *CaptivePortalMap,
listenAddrStr string,
captivePortalHandler *CaptivePortalHandler,
) error {
network := "udp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
network = "udp4"
}
listenUDPAddr, err := net.ResolveUDPAddr(network, listenAddrStr)
if err != nil {
return err
}
clientPc, err := net.ListenUDP(network, listenUDPAddr)
if err != nil {
return err
}
captivePortalHandler.wg.Add(1)
go func() {
for !handleColdStartClient(clientPc, captivePortalHandler.cancelChannel, ipsMap) {
}
clientPc.Close()
captivePortalHandler.wg.Done()
}()
return nil
}
func ColdStart(proxy *Proxy) (*CaptivePortalHandler, error) {
if len(proxy.captivePortalMapFile) == 0 {
return nil, nil
}
lines, err := ReadTextFile(proxy.captivePortalMapFile)
if err != nil {
dlog.Warn(err)
return nil, err
}
ipsMap := make(CaptivePortalMap)
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
name, ipsStr, ok := StringTwoFields(line)
if !ok {
return nil, fmt.Errorf(
"Syntax error for a captive portal rule at line %d",
1+lineNo,
)
}
name, err = NormalizeQName(name)
if err != nil {
continue
}
var ips []net.IP
for _, ip := range strings.Split(ipsStr, ",") {
ipStr := strings.TrimSpace(ip)
if ip := net.ParseIP(ipStr); ip != nil {
ips = append(ips, ip)
} else {
return nil, fmt.Errorf(
"Syntax error for a captive portal rule at line %d",
1+lineNo,
)
}
}
ipsMap[name] = ips
}
listenAddrStrs := proxy.listenAddresses
captivePortalHandler := CaptivePortalHandler{
cancelChannel: make(chan struct{}),
}
ok := false
for _, listenAddrStr := range listenAddrStrs {
err = addColdStartListener(&ipsMap, listenAddrStr, &captivePortalHandler)
if err == nil {
ok = true
}
}
if ok {
err = nil
}
proxy.captivePortalMap = &ipsMap
return &captivePortalHandler, err
}

View File

@ -4,12 +4,14 @@ import (
"bytes"
"encoding/binary"
"errors"
"io/ioutil"
"net"
"os"
"path"
"strconv"
"strings"
"unicode"
"github.com/jedisct1/dlog"
)
type CryptoConstruction uint16
@ -25,7 +27,7 @@ const (
)
const (
MaxHTTPBodyLength = 4000000
MaxHTTPBodyLength = 1000000
)
var (
@ -40,7 +42,11 @@ var (
var (
FileDescriptors = make([]*os.File, 0)
FileDescriptorNum = 0
FileDescriptorNum = uintptr(0)
)
const (
InheritedDescriptorsBase = uintptr(50)
)
func PrefixWithSize(packet []byte) ([]byte, error) {
@ -92,20 +98,6 @@ func Max(a, b int) int {
return b
}
func MinF(a, b float64) float64 {
if a < b {
return a
}
return b
}
func MaxF(a, b float64) float64 {
if a > b {
return a
}
return b
}
func StringReverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
@ -122,7 +114,7 @@ func StringTwoFields(str string) (string, string, bool) {
if pos == -1 {
return "", "", false
}
a, b := strings.TrimFunc(str[:pos], unicode.IsSpace), strings.TrimFunc(str[pos+1:], unicode.IsSpace)
a, b := strings.TrimSpace(str[:pos]), strings.TrimSpace(str[pos+1:])
if len(a) == 0 || len(b) == 0 {
return a, b, false
}
@ -143,6 +135,18 @@ func StringStripSpaces(str string) string {
}, str)
}
func TrimAndStripInlineComments(str string) string {
if idx := strings.LastIndexByte(str, '#'); idx >= 0 {
if idx == 0 || str[0] == '#' {
return ""
}
if prev := str[idx-1]; prev == ' ' || prev == '\t' {
str = str[:idx-1]
}
}
return strings.TrimSpace(str)
}
func ExtractHostAndPort(str string, defaultPort int) (host string, port int) {
host, port = str, defaultPort
if idx := strings.LastIndex(str, ":"); idx >= 0 && idx < len(str)-1 {
@ -154,10 +158,40 @@ func ExtractHostAndPort(str string, defaultPort int) (host string, port int) {
}
func ReadTextFile(filename string) (string, error) {
bin, err := ioutil.ReadFile(filename)
bin, err := os.ReadFile(filename)
if err != nil {
return "", err
}
bin = bytes.TrimPrefix(bin, []byte{0xef, 0xbb, 0xbf})
return string(bin), nil
}
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
func maybeWritableByOtherUsers(p string) (bool, string, error) {
p = path.Clean(p)
for p != "/" && p != "." {
st, err := os.Stat(p)
if err != nil {
return false, p, err
}
mode := st.Mode()
if mode.Perm()&2 != 0 && !(st.IsDir() && mode&os.ModeSticky == os.ModeSticky) {
return true, p, nil
}
p = path.Dir(p)
}
return false, "", nil
}
func WarnIfMaybeWritableByOtherUsers(p string) {
if ok, px, err := maybeWritableByOtherUsers(p); ok {
if px == p {
dlog.Criticalf("[%s] is writable by other system users - If this is not intentional, it is recommended to fix the access permissions", p)
} else {
dlog.Warnf("[%s] can be modified by other system users because [%s] is writable by other users - If this is not intentional, it is recommended to fix the access permissions", p, px)
}
} else if err != nil {
dlog.Warnf("Error while checking if [%s] is accessible: [%s] : [%s]", p, px, err)
}
}

View File

@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"math/rand"
"net"
"net/http"
"net/url"
"os"
@ -27,24 +28,29 @@ const (
)
type Config struct {
LogLevel int `toml:"log_level"`
LogFile *string `toml:"log_file"`
UseSyslog bool `toml:"use_syslog"`
ServerNames []string `toml:"server_names"`
DisabledServerNames []string `toml:"disabled_server_names"`
ListenAddresses []string `toml:"listen_addresses"`
Daemonize bool
UserName string `toml:"user_name"`
ForceTCP bool `toml:"force_tcp"`
Timeout int `toml:"timeout"`
KeepAlive int `toml:"keepalive"`
Proxy string `toml:"proxy"`
CertRefreshDelay int `toml:"cert_refresh_delay"`
CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"`
EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"`
LBStrategy string `toml:"lb_strategy"`
LBEstimator bool `toml:"lb_estimator"`
BlockIPv6 bool `toml:"block_ipv6"`
LogLevel int `toml:"log_level"`
LogFile *string `toml:"log_file"`
LogFileLatest bool `toml:"log_file_latest"`
UseSyslog bool `toml:"use_syslog"`
ServerNames []string `toml:"server_names"`
DisabledServerNames []string `toml:"disabled_server_names"`
ListenAddresses []string `toml:"listen_addresses"`
LocalDoH LocalDoHConfig `toml:"local_doh"`
UserName string `toml:"user_name"`
ForceTCP bool `toml:"force_tcp"`
HTTP3 bool `toml:"http3"`
Timeout int `toml:"timeout"`
KeepAlive int `toml:"keepalive"`
Proxy string `toml:"proxy"`
CertRefreshConcurrency int `toml:"cert_refresh_concurrency"`
CertRefreshDelay int `toml:"cert_refresh_delay"`
CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"`
EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"`
LBStrategy string `toml:"lb_strategy"`
LBEstimator bool `toml:"lb_estimator"`
BlockIPv6 bool `toml:"block_ipv6"`
BlockUnqualified bool `toml:"block_unqualified"`
BlockUndelegated bool `toml:"block_undelegated"`
Cache bool
CacheSize int `toml:"cache_size"`
CacheNegTTL uint32 `toml:"cache_neg_ttl"`
@ -56,11 +62,16 @@ type Config struct {
CloakTTL uint32 `toml:"cloak_ttl"`
QueryLog QueryLogConfig `toml:"query_log"`
NxLog NxLogConfig `toml:"nx_log"`
BlockName BlockNameConfig `toml:"blacklist"`
WhitelistName WhitelistNameConfig `toml:"whitelist"`
BlockIP BlockIPConfig `toml:"ip_blacklist"`
BlockName BlockNameConfig `toml:"blocked_names"`
BlockNameLegacy BlockNameConfigLegacy `toml:"blacklist"`
WhitelistNameLegacy WhitelistNameConfigLegacy `toml:"whitelist"`
AllowedName AllowedNameConfig `toml:"allowed_names"`
BlockIP BlockIPConfig `toml:"blocked_ips"`
BlockIPLegacy BlockIPConfigLegacy `toml:"ip_blacklist"`
AllowIP AllowIPConfig `toml:"allowed_ips"`
ForwardFile string `toml:"forwarding_rules"`
CloakFile string `toml:"cloaking_rules"`
CaptivePortals CaptivePortalsConfig `toml:"captive_portals"`
StaticsConfig map[string]StaticConfig `toml:"static"`
SourcesConfig map[string]SourceConfig `toml:"sources"`
BrokenImplementations BrokenImplementationsConfig `toml:"broken_implementations"`
@ -69,10 +80,12 @@ type Config struct {
SourceRequireNoFilter bool `toml:"require_nofilter"`
SourceDNSCrypt bool `toml:"dnscrypt_servers"`
SourceDoH bool `toml:"doh_servers"`
SourceODoH bool `toml:"odoh_servers"`
SourceIPv4 bool `toml:"ipv4_servers"`
SourceIPv6 bool `toml:"ipv6_servers"`
MaxClients uint32 `toml:"max_clients"`
FallbackResolver string `toml:"fallback_resolver"`
BootstrapResolversLegacy []string `toml:"fallback_resolvers"`
BootstrapResolvers []string `toml:"bootstrap_resolvers"`
IgnoreSystemDNS bool `toml:"ignore_system_dns"`
AllWeeklyRanges map[string]WeeklyRangesStr `toml:"schedules"`
LogMaxSize int `toml:"log_files_max_size"`
@ -80,6 +93,7 @@ type Config struct {
LogMaxBackups int `toml:"log_files_max_backups"`
TLSDisableSessionTickets bool `toml:"tls_disable_session_tickets"`
TLSCipherSuite []uint16 `toml:"tls_cipher_suite"`
TLSKeyLogFile string `toml:"tls_key_log_file"`
NetprobeAddress string `toml:"netprobe_address"`
NetprobeTimeout int `toml:"netprobe_timeout"`
OfflineMode bool `toml:"offline_mode"`
@ -87,16 +101,25 @@ type Config struct {
RefusedCodeInResponses bool `toml:"refused_code_in_responses"`
BlockedQueryResponse string `toml:"blocked_query_response"`
QueryMeta []string `toml:"query_meta"`
CloakedPTR bool `toml:"cloak_ptr"`
AnonymizedDNS AnonymizedDNSConfig `toml:"anonymized_dns"`
DoHClientX509Auth DoHClientX509AuthConfig `toml:"doh_client_x509_auth"`
DoHClientX509AuthLegacy DoHClientX509AuthConfig `toml:"tls_client_auth"`
DNS64 DNS64Config `toml:"dns64"`
EDNSClientSubnet []string `toml:"edns_client_subnet"`
}
func newConfig() Config {
return Config{
LogLevel: int(dlog.LogLevel()),
LogFileLatest: true,
ListenAddresses: []string{"127.0.0.1:53"},
LocalDoH: LocalDoHConfig{Path: "/dns-query"},
Timeout: 5000,
KeepAlive: 5,
CertRefreshConcurrency: 10,
CertRefreshDelay: 240,
HTTP3: false,
CertIgnoreTimestamp: false,
EphemeralKeys: false,
Cache: true,
@ -114,22 +137,31 @@ func newConfig() Config {
SourceIPv6: false,
SourceDNSCrypt: true,
SourceDoH: true,
SourceODoH: false,
MaxClients: 250,
FallbackResolver: DefaultFallbackResolver,
BootstrapResolvers: []string{DefaultBootstrapResolver},
IgnoreSystemDNS: false,
LogMaxSize: 10,
LogMaxAge: 7,
LogMaxBackups: 1,
TLSDisableSessionTickets: false,
TLSCipherSuite: nil,
TLSKeyLogFile: "",
NetprobeTimeout: 60,
OfflineMode: false,
RefusedCodeInResponses: false,
LBEstimator: true,
BlockedQueryResponse: "hinfo",
BrokenImplementations: BrokenImplementationsConfig{
BrokenQueryPadding: []string{"cisco", "cisco-ipv6", "cisco-familyshield"},
FragmentsBlocked: []string{
"cisco", "cisco-ipv6", "cisco-familyshield", "cisco-familyshield-ipv6",
"cleanbrowsing-adult", "cleanbrowsing-adult-ipv6", "cleanbrowsing-family", "cleanbrowsing-family-ipv6", "cleanbrowsing-security", "cleanbrowsing-security-ipv6",
},
},
AnonymizedDNS: AnonymizedDNSConfig{
DirectCertFallback: true,
},
CloakedPTR: false,
}
}
@ -159,34 +191,68 @@ type NxLogConfig struct {
}
type BlockNameConfig struct {
File string `toml:"blocked_names_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type BlockNameConfigLegacy struct {
File string `toml:"blacklist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type WhitelistNameConfig struct {
type WhitelistNameConfigLegacy struct {
File string `toml:"whitelist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type AllowedNameConfig struct {
File string `toml:"allowed_names_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type BlockIPConfig struct {
File string `toml:"blocked_ips_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type BlockIPConfigLegacy struct {
File string `toml:"blacklist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type AllowIPConfig struct {
File string `toml:"allowed_ips_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type AnonymizedDNSRouteConfig struct {
ServerName string `toml:"server_name"`
RelayNames []string `toml:"via"`
}
type AnonymizedDNSConfig struct {
Routes []AnonymizedDNSRouteConfig `toml:"routes"`
Routes []AnonymizedDNSRouteConfig `toml:"routes"`
SkipIncompatible bool `toml:"skip_incompatible"`
DirectCertFallback bool `toml:"direct_cert_fallback"`
}
type BrokenImplementationsConfig struct {
BrokenQueryPadding []string `toml:"broken_query_padding"`
FragmentsBlocked []string `toml:"fragments_blocked"`
}
type LocalDoHConfig struct {
ListenAddresses []string `toml:"listen_addresses"`
Path string `toml:"path"`
CertFile string `toml:"cert_file"`
CertKeyFile string `toml:"cert_key_file"`
}
type ServerSummary struct {
@ -195,17 +261,39 @@ type ServerSummary struct {
IPv6 bool `json:"ipv6"`
Addrs []string `json:"addrs,omitempty"`
Ports []int `json:"ports"`
DNSSEC bool `json:"dnssec"`
DNSSEC *bool `json:"dnssec,omitempty"`
NoLog bool `json:"nolog"`
NoFilter bool `json:"nofilter"`
Description string `json:"description,omitempty"`
Stamp string `json:"stamp"`
}
type TLSClientAuthCredsConfig struct {
ServerName string `toml:"server_name"`
ClientCert string `toml:"client_cert"`
ClientKey string `toml:"client_key"`
RootCA string `toml:"root_ca"`
}
type DoHClientX509AuthConfig struct {
Creds []TLSClientAuthCredsConfig `toml:"creds"`
}
type DNS64Config struct {
Prefixes []string `toml:"prefix"`
Resolvers []string `toml:"resolver"`
}
type CaptivePortalsConfig struct {
MapFile string `toml:"map_file"`
}
type ConfigFlags struct {
Resolve *string
List *bool
ListAll *bool
JsonOutput *bool
IncludeRelays *bool
JSONOutput *bool
Check *bool
ConfigFile *string
Child *bool
@ -233,35 +321,59 @@ func findConfigFile(configFile *string) (string, error) {
func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
foundConfigFile, err := findConfigFile(flags.ConfigFile)
if err != nil {
dlog.Fatalf("Unable to load the configuration file [%s] -- Maybe use the -config command-line switch?", *flags.ConfigFile)
return fmt.Errorf(
"Unable to load the configuration file [%s] -- Maybe use the -config command-line switch?",
*flags.ConfigFile,
)
}
WarnIfMaybeWritableByOtherUsers(foundConfigFile)
config := newConfig()
md, err := toml.DecodeFile(foundConfigFile, &config)
if err != nil {
return err
}
undecoded := md.Undecoded()
if len(undecoded) > 0 {
return fmt.Errorf("Unsupported key in configuration file: [%s]", undecoded[0])
if flags.Resolve != nil && len(*flags.Resolve) > 0 {
addr := "127.0.0.1:53"
if len(config.ListenAddresses) > 0 {
addr = config.ListenAddresses[0]
}
Resolve(addr, *flags.Resolve, len(config.ServerNames) == 1)
os.Exit(0)
}
if err := cdFileDir(foundConfigFile); err != nil {
return err
}
cdFileDir(foundConfigFile)
if config.LogLevel >= 0 && config.LogLevel < int(dlog.SeverityLast) {
dlog.SetLogLevel(dlog.Severity(config.LogLevel))
}
if dlog.LogLevel() <= dlog.SeverityDebug && os.Getenv("DEBUG") == "" {
dlog.SetLogLevel(dlog.SeverityInfo)
}
if config.UseSyslog {
dlog.TruncateLogFile(config.LogFileLatest)
proxy.showCerts = *flags.ShowCerts || len(os.Getenv("SHOW_CERTS")) > 0
isCommandMode := *flags.Check || proxy.showCerts || *flags.List || *flags.ListAll
if isCommandMode {
} else if config.UseSyslog {
dlog.UseSyslog(true)
} else if config.LogFile != nil {
dlog.UseLogFile(*config.LogFile)
if !*flags.Child {
FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor())
} else {
dlog.SetFileDescriptor(os.NewFile(uintptr(InheritedDescriptorsBase+FileDescriptorNum), "logFile"))
FileDescriptorNum++
dlog.SetFileDescriptor(os.NewFile(uintptr(3), "logFile"))
}
}
if !*flags.Child {
dlog.Noticef("dnscrypt-proxy %s", AppVersion)
}
undecoded := md.Undecoded()
if len(undecoded) > 0 {
return fmt.Errorf("Unsupported key in configuration file: [%s]", undecoded[0])
}
proxy.logMaxSize = config.LogMaxSize
proxy.logMaxAge = config.LogMaxAge
proxy.logMaxBackups = config.LogMaxBackups
@ -273,20 +385,27 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets
proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite
proxy.xTransport.mainProto = proxy.mainProto
if len(config.FallbackResolver) > 0 {
if err := isIPAndPort(config.FallbackResolver); err != nil {
dlog.Fatalf("fallback_resolver [%v]", err)
proxy.xTransport.http3 = config.HTTP3
if len(config.BootstrapResolvers) == 0 && len(config.BootstrapResolversLegacy) > 0 {
dlog.Warnf("fallback_resolvers was renamed to bootstrap_resolvers - Please update your configuration")
config.BootstrapResolvers = config.BootstrapResolversLegacy
}
if len(config.BootstrapResolvers) > 0 {
for _, resolver := range config.BootstrapResolvers {
if err := isIPAndPort(resolver); err != nil {
return fmt.Errorf("Bootstrap resolver [%v]: %v", resolver, err)
}
}
proxy.xTransport.ignoreSystemDNS = config.IgnoreSystemDNS
}
proxy.xTransport.fallbackResolver = config.FallbackResolver
proxy.xTransport.bootstrapResolvers = config.BootstrapResolvers
proxy.xTransport.useIPv4 = config.SourceIPv4
proxy.xTransport.useIPv6 = config.SourceIPv6
proxy.xTransport.keepAlive = time.Duration(config.KeepAlive) * time.Second
if len(config.HTTPProxyURL) > 0 {
httpProxyURL, err := url.Parse(config.HTTPProxyURL)
if err != nil {
dlog.Fatalf("Unable to parse the HTTP proxy URL [%v]", config.HTTPProxyURL)
return fmt.Errorf("Unable to parse the HTTP proxy URL [%v]", config.HTTPProxyURL)
}
proxy.xTransport.httpProxyFunction = http.ProxyURL(httpProxyURL)
}
@ -294,11 +413,11 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
if len(config.Proxy) > 0 {
proxyDialerURL, err := url.Parse(config.Proxy)
if err != nil {
dlog.Fatalf("Unable to parse the proxy URL [%v]", config.Proxy)
return fmt.Errorf("Unable to parse the proxy URL [%v]", config.Proxy)
}
proxyDialer, err := netproxy.FromURL(proxyDialerURL, netproxy.Direct)
if err != nil {
dlog.Fatalf("Unable to use the proxy: [%v]", err)
return fmt.Errorf("Unable to use the proxy: [%v]", err)
}
proxy.xTransport.proxyDialer = &proxyDialer
proxy.mainProto = "tcp"
@ -321,36 +440,53 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
if config.ForceTCP {
proxy.mainProto = "tcp"
}
proxy.certRefreshConcurrency = Max(1, config.CertRefreshConcurrency)
proxy.certRefreshDelay = time.Duration(Max(60, config.CertRefreshDelay)) * time.Minute
proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second)
proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp
proxy.ephemeralKeys = config.EphemeralKeys
if len(config.ListenAddresses) == 0 {
if len(config.ListenAddresses) == 0 && len(config.LocalDoH.ListenAddresses) == 0 {
dlog.Debug("No local IP/port configured")
}
lbStrategy := DefaultLBStrategy
switch strings.ToLower(config.LBStrategy) {
lbStrategy := LBStrategy(DefaultLBStrategy)
switch lbStrategyStr := strings.ToLower(config.LBStrategy); lbStrategyStr {
case "":
// default
case "p2":
lbStrategy = LBStrategyP2
lbStrategy = LBStrategyP2{}
case "ph":
lbStrategy = LBStrategyPH
lbStrategy = LBStrategyPH{}
case "fastest":
case "first":
lbStrategy = LBStrategyFirst
lbStrategy = LBStrategyFirst{}
case "random":
lbStrategy = LBStrategyRandom
lbStrategy = LBStrategyRandom{}
default:
dlog.Warnf("Unknown load balancing strategy: [%s]", config.LBStrategy)
if strings.HasPrefix(lbStrategyStr, "p") {
n, err := strconv.ParseInt(strings.TrimPrefix(lbStrategyStr, "p"), 10, 32)
if err != nil || n <= 0 {
dlog.Warnf("Invalid load balancing strategy: [%s]", config.LBStrategy)
} else {
lbStrategy = LBStrategyPN{n: int(n)}
}
} else {
dlog.Warnf("Unknown load balancing strategy: [%s]", config.LBStrategy)
}
}
proxy.serversInfo.lbStrategy = lbStrategy
proxy.serversInfo.lbEstimator = config.LBEstimator
proxy.listenAddresses = config.ListenAddresses
proxy.daemonize = config.Daemonize
proxy.localDoHListenAddresses = config.LocalDoH.ListenAddresses
if len(config.LocalDoH.Path) > 0 && config.LocalDoH.Path[0] != '/' {
return fmt.Errorf("local DoH: [%s] cannot be a valid URL path. Read the documentation", config.LocalDoH.Path)
}
proxy.localDoHPath = config.LocalDoH.Path
proxy.localDoHCertFile = config.LocalDoH.CertFile
proxy.localDoHCertKeyFile = config.LocalDoH.CertKeyFile
proxy.pluginBlockIPv6 = config.BlockIPv6
proxy.pluginBlockUnqualified = config.BlockUnqualified
proxy.pluginBlockUndelegated = config.BlockUndelegated
proxy.cache = config.Cache
proxy.cacheSize = config.CacheSize
@ -366,9 +502,21 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.cacheMaxTTL = config.CacheMaxTTL
proxy.rejectTTL = config.RejectTTL
proxy.cloakTTL = config.CloakTTL
proxy.cloakedPTR = config.CloakedPTR
proxy.queryMeta = config.QueryMeta
if len(config.EDNSClientSubnet) != 0 {
proxy.ednsClientSubnets = make([]*net.IPNet, 0)
for _, cidr := range config.EDNSClientSubnet {
_, net, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("Invalid EDNS-client-subnet CIDR: [%v]", cidr)
}
proxy.ednsClientSubnets = append(proxy.ednsClientSubnets, net)
}
}
if len(config.QueryLog.Format) == 0 {
config.QueryLog.Format = "tsv"
} else {
@ -392,6 +540,15 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.nxLogFile = config.NxLog.File
proxy.nxLogFormat = config.NxLog.Format
if len(config.BlockName.File) > 0 && len(config.BlockNameLegacy.File) > 0 {
return errors.New("Don't specify both [blocked_names] and [blacklist] sections - Update your config file")
}
if len(config.BlockNameLegacy.File) > 0 {
dlog.Notice("Use of [blacklist] is deprecated - Update your config file")
config.BlockName.File = config.BlockNameLegacy.File
config.BlockName.Format = config.BlockNameLegacy.Format
config.BlockName.LogFile = config.BlockNameLegacy.LogFile
}
if len(config.BlockName.Format) == 0 {
config.BlockName.Format = "tsv"
} else {
@ -404,18 +561,36 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.blockNameFormat = config.BlockName.Format
proxy.blockNameLogFile = config.BlockName.LogFile
if len(config.WhitelistName.Format) == 0 {
config.WhitelistName.Format = "tsv"
if len(config.AllowedName.File) > 0 && len(config.WhitelistNameLegacy.File) > 0 {
return errors.New("Don't specify both [whitelist] and [allowed_names] sections - Update your config file")
}
if len(config.WhitelistNameLegacy.File) > 0 {
dlog.Notice("Use of [whitelist] is deprecated - Update your config file")
config.AllowedName.File = config.WhitelistNameLegacy.File
config.AllowedName.Format = config.WhitelistNameLegacy.Format
config.AllowedName.LogFile = config.WhitelistNameLegacy.LogFile
}
if len(config.AllowedName.Format) == 0 {
config.AllowedName.Format = "tsv"
} else {
config.WhitelistName.Format = strings.ToLower(config.WhitelistName.Format)
config.AllowedName.Format = strings.ToLower(config.AllowedName.Format)
}
if config.WhitelistName.Format != "tsv" && config.WhitelistName.Format != "ltsv" {
return errors.New("Unsupported whitelist log format")
if config.AllowedName.Format != "tsv" && config.AllowedName.Format != "ltsv" {
return errors.New("Unsupported allowed_names log format")
}
proxy.whitelistNameFile = config.WhitelistName.File
proxy.whitelistNameFormat = config.WhitelistName.Format
proxy.whitelistNameLogFile = config.WhitelistName.LogFile
proxy.allowNameFile = config.AllowedName.File
proxy.allowNameFormat = config.AllowedName.Format
proxy.allowNameLogFile = config.AllowedName.LogFile
if len(config.BlockIP.File) > 0 && len(config.BlockIPLegacy.File) > 0 {
return errors.New("Don't specify both [blocked_ips] and [ip_blacklist] sections - Update your config file")
}
if len(config.BlockIPLegacy.File) > 0 {
dlog.Notice("Use of [ip_blacklist] is deprecated - Update your config file")
config.BlockIP.File = config.BlockIPLegacy.File
config.BlockIP.Format = config.BlockIPLegacy.Format
config.BlockIP.LogFile = config.BlockIPLegacy.LogFile
}
if len(config.BlockIP.Format) == 0 {
config.BlockIP.Format = "tsv"
} else {
@ -428,8 +603,21 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.blockIPFormat = config.BlockIP.Format
proxy.blockIPLogFile = config.BlockIP.LogFile
if len(config.AllowIP.Format) == 0 {
config.AllowIP.Format = "tsv"
} else {
config.AllowIP.Format = strings.ToLower(config.AllowIP.Format)
}
if config.AllowIP.Format != "tsv" && config.AllowIP.Format != "ltsv" {
return errors.New("Unsupported allowed_ips log format")
}
proxy.allowedIPFile = config.AllowIP.File
proxy.allowedIPFormat = config.AllowIP.Format
proxy.allowedIPLogFile = config.AllowIP.LogFile
proxy.forwardFile = config.ForwardFile
proxy.cloakFile = config.CloakFile
proxy.captivePortalMapFile = config.CaptivePortals.MapFile
allWeeklyRanges, err := ParseAllWeeklyRanges(config.AllWeeklyRanges)
if err != nil {
@ -444,7 +632,46 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
}
proxy.routes = &routes
}
proxy.serversWithBrokenQueryPadding = config.BrokenImplementations.BrokenQueryPadding
proxy.skipAnonIncompatibleResolvers = config.AnonymizedDNS.SkipIncompatible
proxy.anonDirectCertFallback = config.AnonymizedDNS.DirectCertFallback
if len(config.TLSKeyLogFile) > 0 {
f, err := os.OpenFile(config.TLSKeyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
dlog.Fatalf("Unable to create key log file [%s]: [%s]", config.TLSKeyLogFile, err)
}
dlog.Warnf("TLS key log file [%s] enabled", config.TLSKeyLogFile)
proxy.xTransport.keyLogWriter = f
proxy.xTransport.rebuildTransport()
}
if config.DoHClientX509AuthLegacy.Creds != nil {
return errors.New("[tls_client_auth] has been renamed to [doh_client_x509_auth] - Update your config file")
}
dohClientCreds := config.DoHClientX509Auth.Creds
if len(dohClientCreds) > 0 {
dlog.Noticef("Enabling TLS authentication")
configClientCred := dohClientCreds[0]
if len(dohClientCreds) > 1 {
dlog.Fatal("Only one tls_client_auth entry is currently supported")
}
proxy.xTransport.tlsClientCreds = DOHClientCreds{
clientCert: configClientCred.ClientCert,
clientKey: configClientCred.ClientKey,
rootCA: configClientCred.RootCA,
}
proxy.xTransport.rebuildTransport()
}
// Backwards compatibility
config.BrokenImplementations.FragmentsBlocked = append(
config.BrokenImplementations.FragmentsBlocked,
config.BrokenImplementations.BrokenQueryPadding...)
proxy.serversBlockingFragments = config.BrokenImplementations.FragmentsBlocked
proxy.dns64Prefixes = config.DNS64.Prefixes
proxy.dns64Resolvers = config.DNS64.Resolvers
if *flags.ListAll {
config.ServerNames = nil
@ -456,8 +683,28 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
config.SourceIPv6 = true
config.SourceDNSCrypt = true
config.SourceDoH = true
config.SourceODoH = true
}
var requiredProps stamps.ServerInformalProperties
if config.SourceRequireDNSSEC {
requiredProps |= stamps.ServerInformalPropertyDNSSEC
}
if config.SourceRequireNoLog {
requiredProps |= stamps.ServerInformalPropertyNoLog
}
if config.SourceRequireNoFilter {
requiredProps |= stamps.ServerInformalPropertyNoFilter
}
proxy.requiredProps = requiredProps
proxy.ServerNames = config.ServerNames
proxy.DisabledServerNames = config.DisabledServerNames
proxy.SourceIPv4 = config.SourceIPv4
proxy.SourceIPv6 = config.SourceIPv6
proxy.SourceDNSCrypt = config.SourceDNSCrypt
proxy.SourceDoH = config.SourceDoH
proxy.SourceODoH = config.SourceODoH
netprobeTimeout := config.NetprobeTimeout
flag.Visit(func(flag *flag.Flag) {
if flag.Name == "netprobe-timeout" && flags.NetprobeTimeoutOverride != nil {
@ -467,16 +714,29 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
netprobeAddress := DefaultNetprobeAddress
if len(config.NetprobeAddress) > 0 {
netprobeAddress = config.NetprobeAddress
} else if len(config.FallbackResolver) > 0 {
netprobeAddress = config.FallbackResolver
} else if len(config.BootstrapResolvers) > 0 {
netprobeAddress = config.BootstrapResolvers[0]
}
proxy.showCerts = *flags.ShowCerts || len(os.Getenv("SHOW_CERTS")) > 0
if proxy.showCerts {
proxy.listenAddresses = nil
if !isCommandMode {
if err := NetProbe(proxy, netprobeAddress, netprobeTimeout); err != nil {
return err
}
for _, listenAddrStr := range proxy.listenAddresses {
proxy.addDNSListener(listenAddrStr)
}
for _, listenAddrStr := range proxy.localDoHListenAddresses {
proxy.addLocalDoHListener(listenAddrStr)
}
if err := proxy.addSystemDListeners(); err != nil {
return err
}
}
dlog.Noticef("dnscrypt-proxy %s", AppVersion)
if err := NetProbe(netprobeAddress, netprobeTimeout); err != nil {
return err
// if 'userName' is set and we are the parent process drop privilege and exit
if len(proxy.userName) > 0 && !proxy.child {
proxy.dropPrivilege(proxy.userName, FileDescriptors)
return errors.New(
"Dropping privileges is not supporting on this operating system. Unset `user_name` in the configuration file",
)
}
if !config.OfflineMode {
if err := config.loadSources(proxy); err != nil {
@ -487,15 +747,21 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
}
}
if *flags.List || *flags.ListAll {
config.printRegisteredServers(proxy, *flags.JsonOutput)
if err := config.printRegisteredServers(proxy, *flags.JSONOutput, *flags.IncludeRelays); err != nil {
return err
}
os.Exit(0)
}
if proxy.routes != nil && len(*proxy.routes) > 0 {
hasSpecificRoutes := false
for _, server := range proxy.registeredServers {
if via, ok := (*proxy.routes)[server.name]; ok {
if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt {
dlog.Errorf("DNS anonymization is only supported with the DNSCrypt protocol - Connections to [%v] cannot be anonymized", server.name)
if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt &&
server.stamp.Proto != stamps.StampProtoTypeODoHTarget {
dlog.Errorf(
"DNS anonymization is only supported with the DNSCrypt and ODoH protocols - Connections to [%v] cannot be anonymized",
server.name,
)
} else {
dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via)
}
@ -517,14 +783,54 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
return nil
}
func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) {
func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool, includeRelays bool) error {
var summary []ServerSummary
if includeRelays {
for _, registeredRelay := range proxy.registeredRelays {
addrStr, port := registeredRelay.stamp.ServerAddrStr, stamps.DefaultPort
var hostAddr string
hostAddr, port = ExtractHostAndPort(addrStr, port)
addrs := make([]string, 0)
if (registeredRelay.stamp.Proto == stamps.StampProtoTypeDoH || registeredRelay.stamp.Proto == stamps.StampProtoTypeODoHTarget) &&
len(registeredRelay.stamp.ProviderName) > 0 {
providerName := registeredRelay.stamp.ProviderName
var host string
host, port = ExtractHostAndPort(providerName, port)
addrs = append(addrs, host)
}
if len(addrStr) > 0 {
addrs = append(addrs, hostAddr)
}
nolog := true
nofilter := true
if registeredRelay.stamp.Proto == stamps.StampProtoTypeODoHRelay {
nolog = registeredRelay.stamp.Props&stamps.ServerInformalPropertyNoLog != 0
}
serverSummary := ServerSummary{
Name: registeredRelay.name,
Proto: registeredRelay.stamp.Proto.String(),
IPv6: strings.HasPrefix(addrStr, "["),
Ports: []int{port},
Addrs: addrs,
NoLog: nolog,
NoFilter: nofilter,
Description: registeredRelay.description,
Stamp: registeredRelay.stamp.String(),
}
if jsonOutput {
summary = append(summary, serverSummary)
} else {
fmt.Println(serverSummary.Name)
}
}
}
for _, registeredServer := range proxy.registeredServers {
addrStr, port := registeredServer.stamp.ServerAddrStr, stamps.DefaultPort
var hostAddr string
hostAddr, port = ExtractHostAndPort(addrStr, port)
addrs := make([]string, 0)
if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH && len(registeredServer.stamp.ProviderName) > 0 {
if (registeredServer.stamp.Proto == stamps.StampProtoTypeDoH || registeredServer.stamp.Proto == stamps.StampProtoTypeODoHTarget) &&
len(registeredServer.stamp.ProviderName) > 0 {
providerName := registeredServer.stamp.ProviderName
var host string
host, port = ExtractHostAndPort(providerName, port)
@ -533,13 +839,14 @@ func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) {
if len(addrStr) > 0 {
addrs = append(addrs, hostAddr)
}
dnssec := registeredServer.stamp.Props&stamps.ServerInformalPropertyDNSSEC != 0
serverSummary := ServerSummary{
Name: registeredServer.name,
Proto: registeredServer.stamp.Proto.String(),
IPv6: strings.HasPrefix(addrStr, "["),
Ports: []int{port},
Addrs: addrs,
DNSSEC: registeredServer.stamp.Props&stamps.ServerInformalPropertyDNSSEC != 0,
DNSSEC: &dnssec,
NoLog: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoLog != 0,
NoFilter: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoFilter != 0,
Description: registeredServer.description,
@ -554,28 +861,32 @@ func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) {
if jsonOutput {
jsonStr, err := json.MarshalIndent(summary, "", " ")
if err != nil {
dlog.Fatal(err)
return err
}
fmt.Print(string(jsonStr))
}
return nil
}
func (config *Config) loadSources(proxy *Proxy) error {
var requiredProps stamps.ServerInformalProperties
if config.SourceRequireDNSSEC {
requiredProps |= stamps.ServerInformalPropertyDNSSEC
}
if config.SourceRequireNoLog {
requiredProps |= stamps.ServerInformalPropertyNoLog
}
if config.SourceRequireNoFilter {
requiredProps |= stamps.ServerInformalPropertyNoFilter
}
for cfgSourceName, cfgSource := range config.SourcesConfig {
if err := config.loadSource(proxy, requiredProps, cfgSourceName, &cfgSource); err != nil {
for cfgSourceName, cfgSource_ := range config.SourcesConfig {
cfgSource := cfgSource_
rand.Shuffle(len(cfgSource.URLs), func(i, j int) {
cfgSource.URLs[i], cfgSource.URLs[j] = cfgSource.URLs[j], cfgSource.URLs[i]
})
if err := config.loadSource(proxy, cfgSourceName, &cfgSource); err != nil {
return err
}
}
for name, config := range config.StaticsConfig {
if stamp, err := stamps.NewServerStampFromString(config.Stamp); err == nil {
if stamp.Proto == stamps.StampProtoTypeDNSCryptRelay || stamp.Proto == stamps.StampProtoTypeODoHRelay {
dlog.Debugf("Adding [%s] to the set of available static relays", name)
registeredServer := RegisteredServer{name: name, stamp: stamp, description: "static relay"}
proxy.registeredRelays = append(proxy.registeredRelays, registeredServer)
}
}
}
if len(config.ServerNames) == 0 {
for serverName := range config.StaticsConfig {
config.ServerNames = append(config.ServerNames, serverName)
@ -587,22 +898,29 @@ func (config *Config) loadSources(proxy *Proxy) error {
continue
}
if len(staticConfig.Stamp) == 0 {
dlog.Fatalf("Missing stamp for the static [%s] definition", serverName)
return fmt.Errorf("Missing stamp for the static [%s] definition", serverName)
}
stamp, err := stamps.NewServerStampFromString(staticConfig.Stamp)
if err != nil {
dlog.Fatalf("Stamp error for the static [%s] definition: [%v]", serverName, err)
return fmt.Errorf("Stamp error for the static [%s] definition: [%v]", serverName, err)
}
proxy.registeredServers = append(proxy.registeredServers, RegisteredServer{name: serverName, stamp: stamp})
}
rand.Shuffle(len(proxy.registeredServers), func(i, j int) {
proxy.registeredServers[i], proxy.registeredServers[j] = proxy.registeredServers[j], proxy.registeredServers[i]
if err := proxy.updateRegisteredServers(); err != nil {
return err
}
rs1 := proxy.registeredServers
rs2 := proxy.serversInfo.registeredServers
rand.Shuffle(len(rs1), func(i, j int) {
rs1[i], rs1[j] = rs1[j], rs1[i]
})
rand.Shuffle(len(rs2), func(i, j int) {
rs2[i], rs2[j] = rs2[j], rs2[i]
})
return nil
}
func (config *Config) loadSource(proxy *Proxy, requiredProps stamps.ServerInformalProperties, cfgSourceName string, cfgSource *SourceConfig) error {
func (config *Config) loadSource(proxy *Proxy, cfgSourceName string, cfgSource *SourceConfig) error {
if len(cfgSource.URLs) == 0 {
if len(cfgSource.URL) == 0 {
dlog.Debugf("Missing URLs for source [%s]", cfgSourceName)
@ -622,57 +940,25 @@ func (config *Config) loadSource(proxy *Proxy, requiredProps stamps.ServerInform
if cfgSource.RefreshDelay <= 0 {
cfgSource.RefreshDelay = 72
}
source, err := NewSource(cfgSourceName, proxy.xTransport, cfgSource.URLs, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour)
cfgSource.RefreshDelay = Min(168, Max(24, cfgSource.RefreshDelay))
source, err := NewSource(
cfgSourceName,
proxy.xTransport,
cfgSource.URLs,
cfgSource.MinisignKeyStr,
cfgSource.CacheFile,
cfgSource.FormatStr,
time.Duration(cfgSource.RefreshDelay)*time.Hour,
cfgSource.Prefix,
)
if err != nil {
dlog.Criticalf("Unable to retrieve source [%s]: [%s]", cfgSourceName, err)
return err
}
proxy.sources = append(proxy.sources, source)
registeredServers, err := source.Parse(cfgSource.Prefix)
if err != nil {
if len(registeredServers) == 0 {
dlog.Criticalf("Unable to use source [%s]: [%s]", cfgSourceName, err)
if len(source.bin) <= 0 {
dlog.Criticalf("Unable to retrieve source [%s]: [%s]", cfgSourceName, err)
return err
}
dlog.Warnf("Error in source [%s]: [%s] -- Continuing with reduced server count [%d]", cfgSourceName, err, len(registeredServers))
}
for _, registeredServer := range registeredServers {
if registeredServer.stamp.Proto != stamps.StampProtoTypeDNSCryptRelay {
if len(config.ServerNames) > 0 {
if !includesName(config.ServerNames, registeredServer.name) {
continue
}
} else if registeredServer.stamp.Props&requiredProps != requiredProps {
continue
}
}
if includesName(config.DisabledServerNames, registeredServer.name) {
continue
}
if config.SourceIPv4 || config.SourceIPv6 {
isIPv4, isIPv6 := true, false
if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH {
isIPv4, isIPv6 = true, true
}
if strings.HasPrefix(registeredServer.stamp.ServerAddrStr, "[") {
isIPv4, isIPv6 = false, true
}
if !(config.SourceIPv4 == isIPv4 || config.SourceIPv6 == isIPv6) {
continue
}
}
if registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCryptRelay {
dlog.Debugf("Adding [%s] to the set of available relays", registeredServer.name)
proxy.registeredRelays = append(proxy.registeredRelays, registeredServer)
} else {
if !((config.SourceDNSCrypt && registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCrypt) ||
(config.SourceDoH && registeredServer.stamp.Proto == stamps.StampProtoTypeDoH)) {
continue
}
dlog.Debugf("Adding [%s] to the set of wanted resolvers", registeredServer.name)
proxy.registeredServers = append(proxy.registeredServers, registeredServer)
}
dlog.Infof("Downloading [%s] failed: %v, using cache file to startup", source.name, err)
}
proxy.sources = append(proxy.sources, source)
return nil
}
@ -685,17 +971,20 @@ func includesName(names []string, name string) bool {
return false
}
func cdFileDir(fileName string) {
os.Chdir(filepath.Dir(fileName))
func cdFileDir(fileName string) error {
return os.Chdir(filepath.Dir(fileName))
}
func cdLocal() {
exeFileName, err := os.Executable()
if err != nil {
dlog.Warnf("Unable to determine the executable directory: [%s] -- You will need to specify absolute paths in the configuration file", err)
return
dlog.Warnf(
"Unable to determine the executable directory: [%s] -- You will need to specify absolute paths in the configuration file",
err,
)
} else if err := os.Chdir(filepath.Dir(exeFileName)); err != nil {
dlog.Warnf("Unable to change working directory to [%s]: %s", exeFileName, err)
}
os.Chdir(filepath.Dir(exeFileName))
}
func isIPAndPort(addrStr string) error {

View File

@ -5,7 +5,6 @@ import (
crypto_rand "crypto/rand"
"crypto/sha512"
"errors"
"math/rand"
"github.com/jedisct1/dlog"
"github.com/jedisct1/xsecretbox"
@ -45,22 +44,43 @@ func unpad(packet []byte) ([]byte, error) {
}
}
func ComputeSharedKey(cryptoConstruction CryptoConstruction, secretKey *[32]byte, serverPk *[32]byte, providerName *string) (sharedKey [32]byte) {
func ComputeSharedKey(
cryptoConstruction CryptoConstruction,
secretKey *[32]byte,
serverPk *[32]byte,
providerName *string,
) (sharedKey [32]byte) {
if cryptoConstruction == XChacha20Poly1305 {
var err error
sharedKey, err = xsecretbox.SharedKey(*secretKey, *serverPk)
if err != nil {
dlog.Criticalf("[%v] Weak public key", providerName)
dlog.Criticalf("[%v] Weak XChaCha20 public key", providerName)
}
} else {
box.Precompute(&sharedKey, serverPk, secretKey)
c := byte(0)
for i := 0; i < 32; i++ {
c |= sharedKey[i]
}
if c == 0 {
dlog.Criticalf("[%v] Weak XSalsa20 public key", providerName)
if _, err := crypto_rand.Read(sharedKey[:]); err != nil {
dlog.Fatal(err)
}
}
}
return
}
func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) (sharedKey *[32]byte, encrypted []byte, clientNonce []byte, err error) {
func (proxy *Proxy) Encrypt(
serverInfo *ServerInfo,
packet []byte,
proto string,
) (sharedKey *[32]byte, encrypted []byte, clientNonce []byte, err error) {
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
crypto_rand.Read(clientNonce)
if _, err := crypto_rand.Read(clientNonce); err != nil {
return nil, nil, nil, err
}
copy(nonce, clientNonce)
var publicKey *[PublicKeySize]byte
if proxy.ephemeralKeys {
@ -79,18 +99,19 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
publicKey = &proxy.proxyPublicKey
}
minQuestionSize := QueryOverhead + len(packet)
if !serverInfo.knownBugs.incorrectPadding {
if proto == "udp" {
minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
} else {
var xpad [1]byte
rand.Read(xpad[:])
minQuestionSize += int(xpad[0])
if proto == "udp" {
minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
} else {
var xpad [1]byte
if _, err := crypto_rand.Read(xpad[:]); err != nil {
return nil, nil, nil, err
}
minQuestionSize += int(xpad[0])
}
paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, QueryOverhead)+1+63) & ^63)
if serverInfo.RelayUDPAddr != nil && proto == "tcp" {
// XXX - Note: Cisco's broken implementation doesn't accept more than 1472 bytes
if serverInfo.knownBugs.fragmentsBlocked && proto == "udp" {
paddedLength = MaxDNSUDPSafePacketSize
} else if serverInfo.Relay != nil && proto == "tcp" {
paddedLength = MaxDNSPacketSize
}
if QueryOverhead+len(packet)+1 > paddedLength {
@ -110,7 +131,12 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
return
}
func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, sharedKey *[32]byte, encrypted []byte, nonce []byte) ([]byte, error) {
func (proxy *Proxy) Decrypt(
serverInfo *ServerInfo,
sharedKey *[32]byte,
encrypted []byte,
nonce []byte,
) ([]byte, error) {
serverMagicLen := len(ServerMagic)
responseHeaderLen := serverMagicLen + NonceSize
if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) ||

View File

@ -4,7 +4,6 @@ import (
"bytes"
"encoding/binary"
"errors"
"net"
"strings"
"time"
@ -21,26 +20,56 @@ type CertInfo struct {
ForwardSecurity bool
}
func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk ed25519.PublicKey, serverAddress string, providerName string, isNew bool, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr) (CertInfo, int, error) {
func FetchCurrentDNSCryptCert(
proxy *Proxy,
serverName *string,
proto string,
pk ed25519.PublicKey,
serverAddress string,
providerName string,
isNew bool,
relay *DNSCryptRelay,
knownBugs ServerBugs,
) (CertInfo, int, bool, error) {
if len(pk) != ed25519.PublicKeySize {
return CertInfo{}, 0, errors.New("Invalid public key length")
return CertInfo{}, 0, false, errors.New("Invalid public key length")
}
if !strings.HasSuffix(providerName, ".") {
providerName = providerName + "."
providerName += "."
}
if serverName == nil {
serverName = &providerName
}
query := new(dns.Msg)
query := dns.Msg{}
query.SetQuestion(providerName, dns.TypeTXT)
if !strings.HasPrefix(providerName, "2.dnscrypt-cert.") {
dlog.Warnf("[%v] uses a non-standard provider name ('%v' doesn't start with '2.dnscrypt-cert.')", *serverName, providerName)
relayUDPAddr, relayTCPAddr = nil, nil
if relay != nil && !proxy.anonDirectCertFallback {
dlog.Warnf(
"[%v] uses a non-standard provider name, enable direct cert fallback to use with a relay ('%v' doesn't start with '2.dnscrypt-cert.')",
*serverName,
providerName,
)
} else {
dlog.Warnf("[%v] uses a non-standard provider name ('%v' doesn't start with '2.dnscrypt-cert.')", *serverName, providerName)
relay = nil
}
}
in, rtt, err := dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr, serverName)
tryFragmentsSupport := true
if knownBugs.fragmentsBlocked {
tryFragmentsSupport = false
}
in, rtt, fragmentsBlocked, err := DNSExchange(
proxy,
proto,
&query,
serverAddress,
relay,
serverName,
tryFragmentsSupport,
)
if err != nil {
dlog.Noticef("[%s] TIMEOUT", *serverName)
return CertInfo{}, 0, err
return CertInfo{}, 0, fragmentsBlocked, err
}
now := uint32(time.Now().Unix())
certInfo := CertInfo{CryptoConstruction: UndefinedConstruction}
@ -54,7 +83,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
} else {
txt = strings.Join(t.Txt, "")
}
binCert := packTxtString(txt)
binCert := PackTXTRR(txt)
if len(binCert) < 124 {
dlog.Warnf("[%v] Certificate too short", *serverName)
continue
@ -88,14 +117,23 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
}
ttl := tsEnd - tsBegin
if ttl > 86400*7 {
dlog.Infof("[%v] the key validity period for this server is excessively long (%d days), significantly reducing reliability and forward security.", *serverName, ttl/86400)
dlog.Infof(
"[%v] the key validity period for this server is excessively long (%d days), significantly reducing reliability and forward security.",
*serverName,
ttl/86400,
)
daysLeft := (tsEnd - now) / 86400
if daysLeft < 1 {
dlog.Criticalf("[%v] certificate will expire today -- Switch to a different resolver as soon as possible", *serverName)
dlog.Criticalf(
"[%v] certificate will expire today -- Switch to a different resolver as soon as possible",
*serverName,
)
} else if daysLeft <= 7 {
dlog.Warnf("[%v] certificate is about to expire -- if you don't manage this server, tell the server operator about it", *serverName)
} else if daysLeft <= 30 {
dlog.Infof("[%v] certificate will expire in %d days", *serverName, daysLeft)
} else {
dlog.Debugf("[%v] certificate still valid for %d days", *serverName, daysLeft)
}
certInfo.ForwardSecurity = false
} else {
@ -103,7 +141,13 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
}
if !proxy.certIgnoreTimestamp {
if now > tsEnd || now < tsBegin {
dlog.Debugf("[%v] Certificate not valid at the current date (now: %v is not in [%v..%v])", *serverName, now, tsBegin, tsEnd)
dlog.Debugf(
"[%v] Certificate not valid at the current date (now: %v is not in [%v..%v])",
*serverName,
now,
tsBegin,
tsEnd,
)
continue
}
}
@ -139,143 +183,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
certCountStr = " - additional certificate"
}
if certInfo.CryptoConstruction == UndefinedConstruction {
return certInfo, 0, errors.New("No useable certificate found")
return certInfo, 0, fragmentsBlocked, errors.New("No usable certificate found")
}
return certInfo, int(rtt.Nanoseconds() / 1000000), nil
}
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
func dddToByte(s []byte) byte {
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
}
func packTxtString(s string) []byte {
bs := make([]byte, len(s))
msg := make([]byte, 0)
copy(bs, s)
for i := 0; i < len(bs); i++ {
if bs[i] == '\\' {
i++
if i == len(bs) {
break
}
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
msg = append(msg, dddToByte(bs[i:]))
i += 2
} else if bs[i] == 't' {
msg = append(msg, '\t')
} else if bs[i] == 'r' {
msg = append(msg, '\r')
} else if bs[i] == 'n' {
msg = append(msg, '\n')
} else {
msg = append(msg, bs[i])
}
} else {
msg = append(msg, bs[i])
}
}
return msg
}
func dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr, serverName *string) (*dns.Msg, time.Duration, error) {
response, ttl, err := _dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr)
if err != nil && relayUDPAddr != nil {
dlog.Debugf("Unable to get a certificate for [%v] via relay [%v], retrying over a direct connection", *serverName, relayUDPAddr.IP)
response, ttl, err = _dnsExchange(proxy, proto, query, serverAddress, nil, nil)
if err == nil {
dlog.Infof("Direct certificate retrieval for [%v] succeeded", *serverName)
}
}
return response, ttl, err
}
func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr) (*dns.Msg, time.Duration, error) {
var packet []byte
var rtt time.Duration
if proto == "udp" {
qNameLen, padding := len(query.Question[0].Name), 0
if qNameLen < 480 {
padding = 480 - qNameLen
}
if padding > 0 {
opt := new(dns.OPT)
opt.Hdr.Name = "."
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, padding)
opt.Option = append(opt.Option, ext)
query.Extra = []dns.RR{opt}
}
binQuery, err := query.Pack()
if err != nil {
return nil, 0, err
}
udpAddr, err := net.ResolveUDPAddr("udp", serverAddress)
if err != nil {
return nil, 0, err
}
upstreamAddr := udpAddr
if relayUDPAddr != nil {
proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery)
upstreamAddr = relayUDPAddr
}
now := time.Now()
pc, err := net.DialUDP("udp", nil, upstreamAddr)
if err != nil {
return nil, 0, err
}
defer pc.Close()
pc.SetDeadline(time.Now().Add(proxy.timeout))
pc.Write(binQuery)
packet = make([]byte, MaxDNSPacketSize)
length, err := pc.Read(packet)
if err != nil {
return nil, 0, err
}
rtt = time.Since(now)
packet = packet[:length]
} else {
binQuery, err := query.Pack()
if err != nil {
return nil, 0, err
}
tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddress)
if err != nil {
return nil, 0, err
}
upstreamAddr := tcpAddr
if relayTCPAddr != nil {
proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery)
upstreamAddr = relayTCPAddr
}
now := time.Now()
var pc net.Conn
proxyDialer := proxy.xTransport.proxyDialer
if proxyDialer == nil {
pc, err = net.DialTCP("tcp", nil, upstreamAddr)
} else {
pc, err = (*proxyDialer).Dial("tcp", tcpAddr.String())
}
if err != nil {
return nil, 0, err
}
defer pc.Close()
pc.SetDeadline(time.Now().Add(proxy.timeout))
binQuery, err = PrefixWithSize(binQuery)
if err != nil {
return nil, 0, err
}
pc.Write(binQuery)
packet, err = ReadPrefixed(&pc)
if err != nil {
return nil, 0, err
}
rtt = time.Since(now)
}
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return nil, 0, err
}
return &msg, rtt, nil
return certInfo, int(rtt.Nanoseconds() / 1000000), fragmentsBlocked, nil
}

View File

@ -2,75 +2,94 @@ package main
import (
"encoding/binary"
"errors"
"net"
"strings"
"time"
"unicode/utf8"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
func EmptyResponseFromMessage(srcMsg *dns.Msg) *dns.Msg {
dstMsg := dns.Msg{MsgHdr: srcMsg.MsgHdr, Compress: true}
dstMsg.Question = srcMsg.Question
dstMsg.Response = true
if srcMsg.RecursionDesired {
dstMsg.RecursionAvailable = true
}
dstMsg.RecursionDesired = false
dstMsg.CheckingDisabled = false
dstMsg.AuthenticatedData = false
if edns0 := srcMsg.IsEdns0(); edns0 != nil {
dstMsg.SetEdns0(edns0.UDPSize(), edns0.Do())
}
return &dstMsg
}
func TruncatedResponse(packet []byte) ([]byte, error) {
srcMsg := new(dns.Msg)
srcMsg := dns.Msg{}
if err := srcMsg.Unpack(packet); err != nil {
return nil, err
}
dstMsg := srcMsg
dstMsg.Response = true
dstMsg.Answer = make([]dns.RR, 0)
dstMsg.Ns = make([]dns.RR, 0)
dstMsg.Extra = make([]dns.RR, 0)
dstMsg := EmptyResponseFromMessage(&srcMsg)
dstMsg.Truncated = true
return dstMsg.Pack()
}
func EmptyResponseFromMessage(srcMsg *dns.Msg) *dns.Msg {
dstMsg := srcMsg
dstMsg.Response = true
dstMsg.Answer = make([]dns.RR, 0)
dstMsg.Ns = make([]dns.RR, 0)
dstMsg.Extra = make([]dns.RR, 0)
return dstMsg
}
func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ipv4 net.IP, ipv6 net.IP, ttl uint32) *dns.Msg {
dstMsg := EmptyResponseFromMessage(srcMsg)
ede := new(dns.EDNS0_EDE)
if edns0 := dstMsg.IsEdns0(); edns0 != nil {
edns0.Option = append(edns0.Option, ede)
}
ede.InfoCode = dns.ExtendedErrorCodeFiltered
if refusedCode {
dstMsg.Rcode = dns.RcodeRefused
} else {
dstMsg.Rcode = dns.RcodeSuccess
questions := srcMsg.Question
if len(questions) > 0 {
question := questions[0]
sendHInfoResponse := true
if len(questions) == 0 {
return dstMsg
}
question := questions[0]
sendHInfoResponse := true
if ipv4 != nil && question.Qtype == dns.TypeA {
rr := new(dns.A)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
rr.A = ipv4.To4()
if rr.A != nil {
dstMsg.Answer = []dns.RR{rr}
sendHInfoResponse = false
}
} else if ipv6 != nil && question.Qtype == dns.TypeAAAA {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = ipv6.To16()
if rr.AAAA != nil {
dstMsg.Answer = []dns.RR{rr}
sendHInfoResponse = false
}
if ipv4 != nil && question.Qtype == dns.TypeA {
rr := new(dns.A)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
rr.A = ipv4.To4()
if rr.A != nil {
dstMsg.Answer = []dns.RR{rr}
sendHInfoResponse = false
ede.InfoCode = dns.ExtendedErrorCodeForgedAnswer
}
if sendHInfoResponse {
hinfo := new(dns.HINFO)
hinfo.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeHINFO,
Class: dns.ClassINET, Ttl: 1}
hinfo.Cpu = "This query has been locally blocked"
hinfo.Os = "by dnscrypt-proxy"
dstMsg.Answer = []dns.RR{hinfo}
} else if ipv6 != nil && question.Qtype == dns.TypeAAAA {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = ipv6.To16()
if rr.AAAA != nil {
dstMsg.Answer = []dns.RR{rr}
sendHInfoResponse = false
ede.InfoCode = dns.ExtendedErrorCodeForgedAnswer
}
}
if sendHInfoResponse {
hinfo := new(dns.HINFO)
hinfo.Hdr = dns.RR_Header{
Name: question.Name, Rrtype: dns.TypeHINFO,
Class: dns.ClassINET, Ttl: ttl,
}
hinfo.Cpu = "This query has been locally blocked"
hinfo.Os = "by dnscrypt-proxy"
dstMsg.Answer = []dns.RR{hinfo}
} else {
ede.ExtraText = "This query has been locally blocked by dnscrypt-proxy"
}
}
return dstMsg
}
@ -90,7 +109,7 @@ func Rcode(packet []byte) uint8 {
return packet[3] & 0xf
}
func NormalizeName(name *[]byte) {
func NormalizeRawQName(name *[]byte) {
for i, c := range *name {
if c >= 65 && c <= 90 {
(*name)[i] = c + 32
@ -98,15 +117,38 @@ func NormalizeName(name *[]byte) {
}
}
func StripTrailingDot(str string) string {
if len(str) > 1 && strings.HasSuffix(str, ".") {
str = str[:len(str)-1]
func NormalizeQName(str string) (string, error) {
if len(str) == 0 || str == "." {
return ".", nil
}
return str
hasUpper := false
str = strings.TrimSuffix(str, ".")
strLen := len(str)
for i := 0; i < strLen; i++ {
c := str[i]
if c >= utf8.RuneSelf {
return str, errors.New("Query name is not an ASCII string")
}
hasUpper = hasUpper || ('A' <= c && c <= 'Z')
}
if !hasUpper {
return str, nil
}
var b strings.Builder
b.Grow(len(str))
for i := 0; i < strLen; i++ {
c := str[i]
if 'A' <= c && c <= 'Z' {
c += 'a' - 'A'
}
b.WriteByte(c)
}
return b.String(), nil
}
func getMinTTL(msg *dns.Msg, minTTL uint32, maxTTL uint32, cacheNegMinTTL uint32, cacheNegMaxTTL uint32) time.Duration {
if (msg.Rcode != dns.RcodeSuccess && msg.Rcode != dns.RcodeNameError) || (len(msg.Answer) <= 0 && len(msg.Ns) <= 0) {
if (msg.Rcode != dns.RcodeSuccess && msg.Rcode != dns.RcodeNameError) ||
(len(msg.Answer) <= 0 && len(msg.Ns) <= 0) {
return time.Duration(cacheNegMinTTL) * time.Second
}
var ttl uint32
@ -167,6 +209,9 @@ func updateTTL(msg *dns.Msg, expiration time.Time) {
ttl := uint32(0)
if until > 0 {
ttl = uint32(until / time.Second)
if until-time.Duration(ttl)*time.Second >= time.Second/2 {
ttl += 1
}
}
for _, rr := range msg.Answer {
rr.Header().Ttl = ttl
@ -180,3 +225,286 @@ func updateTTL(msg *dns.Msg, expiration time.Time) {
}
}
}
func hasEDNS0Padding(packet []byte) (bool, error) {
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return false, err
}
if edns0 := msg.IsEdns0(); edns0 != nil {
for _, option := range edns0.Option {
if option.Option() == dns.EDNS0PADDING {
return true, nil
}
}
}
return false, nil
}
func addEDNS0PaddingIfNoneFound(msg *dns.Msg, unpaddedPacket []byte, paddingLen int) ([]byte, error) {
edns0 := msg.IsEdns0()
if edns0 == nil {
msg.SetEdns0(uint16(MaxDNSPacketSize), false)
edns0 = msg.IsEdns0()
if edns0 == nil {
return unpaddedPacket, nil
}
}
for _, option := range edns0.Option {
if option.Option() == dns.EDNS0PADDING {
return unpaddedPacket, nil
}
}
ext := new(dns.EDNS0_PADDING)
padding := make([]byte, paddingLen)
for i := range padding {
padding[i] = 'X'
}
ext.Padding = padding[:paddingLen]
edns0.Option = append(edns0.Option, ext)
return msg.Pack()
}
func removeEDNS0Options(msg *dns.Msg) bool {
edns0 := msg.IsEdns0()
if edns0 == nil {
return false
}
edns0.Option = []dns.EDNS0{}
return true
}
func dddToByte(s []byte) byte {
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
}
func PackTXTRR(s string) []byte {
bs := make([]byte, len(s))
msg := make([]byte, 0)
copy(bs, s)
for i := 0; i < len(bs); i++ {
if bs[i] == '\\' {
i++
if i == len(bs) {
break
}
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
msg = append(msg, dddToByte(bs[i:]))
i += 2
} else if bs[i] == 't' {
msg = append(msg, '\t')
} else if bs[i] == 'r' {
msg = append(msg, '\r')
} else if bs[i] == 'n' {
msg = append(msg, '\n')
} else {
msg = append(msg, bs[i])
}
} else {
msg = append(msg, bs[i])
}
}
return msg
}
type DNSExchangeResponse struct {
response *dns.Msg
rtt time.Duration
priority int
fragmentsBlocked bool
err error
}
func DNSExchange(
proxy *Proxy,
proto string,
query *dns.Msg,
serverAddress string,
relay *DNSCryptRelay,
serverName *string,
tryFragmentsSupport bool,
) (*dns.Msg, time.Duration, bool, error) {
for {
cancelChannel := make(chan struct{})
maxTries := 3
channel := make(chan DNSExchangeResponse, 2*maxTries)
var err error
options := 0
for tries := 0; tries < maxTries; tries++ {
if tryFragmentsSupport {
queryCopy := query.Copy()
queryCopy.Id += uint16(options)
go func(query *dns.Msg, delay time.Duration) {
time.Sleep(delay)
option := DNSExchangeResponse{err: errors.New("Canceled")}
select {
case <-cancelChannel:
default:
option = _dnsExchange(proxy, proto, query, serverAddress, relay, 1500)
}
option.fragmentsBlocked = false
option.priority = 0
channel <- option
}(queryCopy, time.Duration(200*tries)*time.Millisecond)
options++
}
queryCopy := query.Copy()
queryCopy.Id += uint16(options)
go func(query *dns.Msg, delay time.Duration) {
time.Sleep(delay)
option := DNSExchangeResponse{err: errors.New("Canceled")}
select {
case <-cancelChannel:
default:
option = _dnsExchange(proxy, proto, query, serverAddress, relay, 480)
}
option.fragmentsBlocked = true
option.priority = 1
channel <- option
}(queryCopy, time.Duration(250*tries)*time.Millisecond)
options++
}
var bestOption *DNSExchangeResponse
for i := 0; i < options; i++ {
if dnsExchangeResponse := <-channel; dnsExchangeResponse.err == nil {
if bestOption == nil || dnsExchangeResponse.priority < bestOption.priority ||
(dnsExchangeResponse.priority == bestOption.priority && dnsExchangeResponse.rtt < bestOption.rtt) {
bestOption = &dnsExchangeResponse
if bestOption.priority == 0 {
close(cancelChannel)
break
}
}
} else {
err = dnsExchangeResponse.err
}
}
if bestOption != nil {
if bestOption.fragmentsBlocked {
dlog.Debugf("[%v] public key retrieval succeeded but server is blocking fragments", *serverName)
} else {
dlog.Debugf("[%v] public key retrieval succeeded", *serverName)
}
return bestOption.response, bestOption.rtt, bestOption.fragmentsBlocked, nil
}
if relay == nil || !proxy.anonDirectCertFallback {
if err == nil {
err = errors.New("Unable to reach the server")
}
return nil, 0, false, err
}
dlog.Infof(
"Unable to get the public key for [%v] via relay [%v], retrying over a direct connection",
*serverName,
relay.RelayUDPAddr.IP,
)
relay = nil
}
}
func _dnsExchange(
proxy *Proxy,
proto string,
query *dns.Msg,
serverAddress string,
relay *DNSCryptRelay,
paddedLen int,
) DNSExchangeResponse {
var packet []byte
var rtt time.Duration
if proto == "udp" {
qNameLen, padding := len(query.Question[0].Name), 0
if qNameLen < paddedLen {
padding = paddedLen - qNameLen
}
if padding > 0 {
opt := new(dns.OPT)
opt.Hdr.Name = "."
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, padding)
opt.Option = append(opt.Option, ext)
query.Extra = []dns.RR{opt}
}
binQuery, err := query.Pack()
if err != nil {
return DNSExchangeResponse{err: err}
}
udpAddr, err := net.ResolveUDPAddr("udp", serverAddress)
if err != nil {
return DNSExchangeResponse{err: err}
}
upstreamAddr := udpAddr
if relay != nil {
proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery)
upstreamAddr = relay.RelayUDPAddr
}
now := time.Now()
pc, err := net.DialUDP("udp", nil, upstreamAddr)
if err != nil {
return DNSExchangeResponse{err: err}
}
defer pc.Close()
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
return DNSExchangeResponse{err: err}
}
if _, err := pc.Write(binQuery); err != nil {
return DNSExchangeResponse{err: err}
}
packet = make([]byte, MaxDNSPacketSize)
length, err := pc.Read(packet)
if err != nil {
return DNSExchangeResponse{err: err}
}
rtt = time.Since(now)
packet = packet[:length]
} else {
binQuery, err := query.Pack()
if err != nil {
return DNSExchangeResponse{err: err}
}
tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddress)
if err != nil {
return DNSExchangeResponse{err: err}
}
upstreamAddr := tcpAddr
if relay != nil {
proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery)
upstreamAddr = relay.RelayTCPAddr
}
now := time.Now()
var pc net.Conn
proxyDialer := proxy.xTransport.proxyDialer
if proxyDialer == nil {
pc, err = net.DialTCP("tcp", nil, upstreamAddr)
} else {
pc, err = (*proxyDialer).Dial("tcp", tcpAddr.String())
}
if err != nil {
return DNSExchangeResponse{err: err}
}
defer pc.Close()
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
return DNSExchangeResponse{err: err}
}
binQuery, err = PrefixWithSize(binQuery)
if err != nil {
return DNSExchangeResponse{err: err}
}
if _, err := pc.Write(binQuery); err != nil {
return DNSExchangeResponse{err: err}
}
packet, err = ReadPrefixed(&pc)
if err != nil {
return DNSExchangeResponse{err: err}
}
rtt = time.Since(now)
}
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return DNSExchangeResponse{err: err}
}
return DNSExchangeResponse{response: &msg, rtt: rtt, err: nil}
}

View File

@ -6,10 +6,6 @@ import (
"github.com/VividCortex/ewma"
)
const (
SizeEstimatorEwmaDecay = 100.0
)
type QuestionSizeEstimator struct {
sync.RWMutex
minQuestionSize int
@ -17,7 +13,10 @@ type QuestionSizeEstimator struct {
}
func NewQuestionSizeEstimator() QuestionSizeEstimator {
return QuestionSizeEstimator{minQuestionSize: InitialMinQuestionSize, ewma: ewma.NewMovingAverage(SizeEstimatorEwmaDecay)}
return QuestionSizeEstimator{
minQuestionSize: InitialMinQuestionSize,
ewma: &ewma.SimpleEWMA{},
}
}
func (questionSizeEstimator *QuestionSizeEstimator) MinQuestionSize() int {

View File

@ -0,0 +1,7 @@
##############################
# Allowed IPs List #
##############################
#192.168.0.*
#fe80:53:* # IPv6 prefix example
#81.169.145.105

View File

@ -0,0 +1,31 @@
###########################
# Allowlist #
###########################
## Rules for allowing queries based on name, one per line
##
## Example of valid patterns:
##
## ads.* | matches anything with an "ads." prefix
## *.example.com | matches example.com and all names within that zone such as www.example.com
## example.com | identical to the above
## =example.com | allows example.com but not *.example.com
## *sex* | matches any name containing that substring
## ads[0-9]* | matches "ads" followed by one or more digits
## ads*.example* | *, ? and [] can be used anywhere, but prefixes/suffixes are faster
# That one may be blocked due to 'tracker' being in the name.
tracker.debian.org
# That one may be blocked due to 'ads' being in the name.
# However, blocking it prevents all sponsored links from the Google
# search engine from being opened.
googleadservices.com
## Time-based rules
# *.youtube.* @time-to-play
# facebook.com @play

View File

@ -1,38 +0,0 @@
###########################
# Blacklist #
###########################
## Rules for name-based query blocking, one per line
##
## Example of valid patterns:
##
## ads.* | matches anything with an "ads." prefix
## *.example.com | matches example.com and all names within that zone such as www.example.com
## example.com | identical to the above
## =example.com | block example.com but not *.example.com
## *sex* | matches any name containing that substring
## ads[0-9]* | matches "ads" followed by one or more digits
## ads*.example* | *, ? and [] can be used anywhere, but prefixes/suffixes are faster
ad.*
ads.*
banner.*
banners.*
creatives.*
oas.*
oascentral.*
stats.*
tag.*
telemetry.*
tracker.*
*.local
eth0.me
*.workgroup
## Time-based rules
# *.youtube.* @time-to-sleep
# facebook.com @work

View File

@ -0,0 +1,16 @@
##############################
# IP blocklist #
##############################
## Rules for IP-based response blocking
##
## Sample feeds of suspect IP addresses:
## - https://github.com/stamparm/ipsum
## - https://github.com/tg12/bad_packets_blocklist
## - https://isc.sans.edu/block.txt
## - https://block.energized.pro/extensions/ips/formats/list.txt
## - https://www.iblocklist.com/lists
163.5.1.4
94.46.118.*
fe80:53:* # IPv6 prefix example

View File

@ -0,0 +1,45 @@
###########################
# Blocklist #
###########################
## Rules for name-based query blocking, one per line
##
## Example of valid patterns:
##
## ads.* | matches anything with an "ads." prefix
## *.example.com | matches example.com and all names within that zone such as www.example.com
## example.com | identical to the above
## =example.com | block example.com but not *.example.com
## *sex* | matches any name containing that substring
## ads[0-9]* | matches "ads" followed by one or more digits
## ads*.example* | *, ? and [] can be used anywhere, but prefixes/suffixes are faster
ad.*
ads.*
banner.*
banners.*
creatives.*
oas.*
oascentral.* # inline comments are allowed after a pound sign
stats.*
tag.*
telemetry.*
tracker.*
*.local
eth0.me
*.workgroup
## Prevent usage of Apple private relay, that bypasses DNS
# mask.apple-dns.net
# mask.icloud.com
# mask-api.icloud.com
# doh.dns.apple.com
## Time-based rules
# *.youtube.* @time-to-sleep
# facebook.com @work

View File

@ -0,0 +1,27 @@
###########################################
# Captive portal test names #
###########################################
## Some operating systems send queries to these names after a network change,
## in order to check if connectivity beyond the router is possible without
## going through a captive portal.
##
## This is a list of hard-coded IP addresses that will be returned when queries
## for these names are received, even before the operating system reports an interface
## as usable for reaching the Internet.
##
## Note that IPv6 addresses don't need to be specified within brackets,
## as there are no port numbers.
captive.apple.com 17.253.109.201, 17.253.113.202
connectivitycheck.gstatic.com 64.233.162.94, 64.233.164.94, 64.233.165.94, 64.233.177.94, 64.233.185.94, 74.125.132.94, 74.125.136.94, 74.125.20.94, 74.125.21.94, 74.125.28.94
connectivitycheck.android.com 64.233.162.100, 64.233.162.101, 64.233.162.102, 64.233.162.113, 64.233.162.138, 64.233.162.139
www.msftncsi.com 2.16.106.89, 2.16.106.91, 23.0.175.137, 23.0.175.146, 23.192.47.155, 23.192.47.203, 23.199.63.160, 23.199.63.184, 23.199.63.208, 23.204.146.160, 23.204.146.163, 23.46.238.243, 23.46.239.24, 23.48.39.16, 23.48.39.48, 23.55.38.139, 23.55.38.146, 23.59.190.185, 23.59.190.195
dns.msftncsi.com 131.107.255.255, fd3e:4f5a:5b81::1
www.msftconnecttest.com 13.107.4.52
ipv6.msftconnecttest.com 2a01:111:2003::52
ipv4only.arpa 192.0.0.170, 192.0.0.171
## Adding IP addresses of NTP servers is also a good idea
time.google.com 216.239.35.0, 2001:4860:4806::

View File

@ -13,7 +13,7 @@ www.google.* forcesafesearch.google.com
www.bing.com strict.bing.com
yandex.ru familysearch.yandex.ru
yandex.ru familysearch.yandex.ru # inline comments are allowed after a pound sign
=duckduckgo.com safe.duckduckgo.com
@ -35,3 +35,10 @@ localhost ::1
# ads.* 192.168.100.1
# ads.* 192.168.100.2
# ads.* ::1
# PTR records can be created by setting cloak_ptr in the main configuration file
# Entries with wild cards will not have PTR records created, but multiple
# names for the same IP are supported
# example.com 192.168.100.1
# my.example.com 192.168.100.1

View File

@ -21,18 +21,25 @@
## Servers from the "public-resolvers" source (see down below) can
## be viewed here: https://dnscrypt.info/public-servers
##
## If this line is commented, all registered servers matching the require_* filters
## will be used.
## The proxy will automatically pick working servers from this list.
## Note that the require_* filters do NOT apply when using this setting.
##
## By default, this list is empty and all registered servers matching the
## require_* filters will be used instead.
##
## The proxy will automatically pick the fastest, working servers from the list.
## Remove the leading # first to enable this; lines starting with # are ignored.
# server_names = ['scaleway-fr', 'google', 'yandex', 'cloudflare']
## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6.
## Example with both IPv4 and IPv6:
## listen_addresses = ['127.0.0.1:53', '[::1]:53']
##
## To listen to all IPv4 addresses, use `listen_addresses = ['0.0.0.0:53']`
## To listen to all IPv4+IPv6 addresses, use `listen_addresses = ['[::]:53']`
listen_addresses = ['127.0.0.1:53', '[::1]:53']
listen_addresses = ['127.0.0.1:53']
## Maximum number of simultaneous client connections to accept
@ -48,7 +55,7 @@ max_clients = 250
# user_name = 'nobody'
## Require servers (from static + remote sources) to satisfy specific properties
## Require servers (from remote sources) to satisfy specific properties
# Use servers reachable over IPv4
ipv4_servers = true
@ -62,6 +69,9 @@ dnscrypt_servers = true
# Use servers implementing the DNS-over-HTTPS protocol
doh_servers = true
# Use servers implementing the Oblivious DoH protocol
odoh_servers = false
## Require servers defined by remote sources to satisfy specific properties
@ -71,7 +81,7 @@ require_dnssec = false
# Server must not log user queries (declarative)
require_nolog = true
# Server must not enforce its own blacklist (for parental control, ads blocking...)
# Server must not enforce its own blocklist (for parental control, ads blocking...)
require_nofilter = true
# Server names to avoid even if they match all criteria
@ -87,6 +97,13 @@ disabled_server_names = []
force_tcp = false
## Enable *experimental* support for HTTP/3 (DoH3, HTTP over QUIC)
## Note that, like DNSCrypt but unlike other HTTP versions, this uses
## UDP and (usually) port 443 instead of TCP.
http3 = false
## SOCKS proxy
## Uncomment the following line to route all TCP connections to a local Tor node
## Tor doesn't support UDP, so set `force_tcp` to `true` as well.
@ -108,25 +125,36 @@ force_tcp = false
timeout = 5000
## Keepalive for HTTP (HTTPS, HTTP/2) queries, in seconds
## Keepalive for HTTP (HTTPS, HTTP/2, HTTP/3) queries, in seconds
keepalive = 30
## Response for blocked queries. Options are `refused`, `hinfo` (default) or
## an IP response. To give an IP response, use the format `a:<IPv4>,aaaa:<IPv6>`.
## Add EDNS-client-subnet information to outgoing queries
##
## Multiple networks can be listed; they will be randomly chosen.
## These networks don't have to match your actual networks.
# edns_client_subnet = ['0.0.0.0/0', '2001:db8::/32']
## Response for blocked queries. Options are `refused`, `hinfo` (default) or
## an IP response. To give an IP response, use the format `a:<IPv4>,aaaa:<IPv6>`.
## Using the `hinfo` option means that some responses will be lies.
## Unfortunately, the `hinfo` option appears to be required for Android 8+
# blocked_query_response = 'refused'
## Load-balancing strategy: 'p2' (default), 'ph', 'first' or 'random'
## Load-balancing strategy: 'p2' (default), 'ph', 'p<n>', 'first' or 'random'
## Randomly choose 1 of the fastest 2, half, n, 1 or all live servers by latency.
## The response quality still depends on the server itself.
# lb_strategy = 'p2'
## Set to `true` to constantly try to estimate the latency of all the resolvers
## and adjust the load-balancing parameters accordingly, or to `false` to disable.
## Default is `true` that makes 'p2' `lb_strategy` work well.
# lb_estimator = true
@ -136,21 +164,43 @@ keepalive = 30
# log_level = 2
## log file for the application
## Log file for the application, as an alternative to sending logs to
## the standard system logging service (syslog/Windows event log).
##
## This file is different from other log files, and will not be
## automatically rotated by the application.
# log_file = 'dnscrypt-proxy.log'
## When using a log file, only keep logs from the most recent launch.
# log_file_latest = true
## Use the system logger (syslog on Unix, Event Log on Windows)
# use_syslog = true
## The maximum concurrency to reload certificates from the resolvers.
## Default is 10.
# cert_refresh_concurrency = 10
## Delay, in minutes, after which certificates are reloaded
cert_refresh_delay = 240
## Initially don't check DNSCrypt server certificates for expiration, and
## only start checking them after a first successful connection to a resolver.
## This can be useful on routers with no battery-backed clock.
# cert_ignore_timestamp = false
## DNSCrypt: Create a new, unique key for every single DNS query
## This may improve privacy but can also have a significant impact on CPU usage
## Only enable if you don't have a lot of network load
@ -163,42 +213,74 @@ cert_refresh_delay = 240
# tls_disable_session_tickets = false
## DoH: Use a specific cipher suite instead of the server preference
## DoH: Use TLS 1.2 and specific cipher suite instead of the server preference
## 49199 = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
## 49195 = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
## 52392 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
## 52393 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
## 4865 = TLS_AES_128_GCM_SHA256
## 4867 = TLS_CHACHA20_POLY1305_SHA256
##
## On non-Intel CPUs such as MIPS routers and ARM systems (Android, Raspberry Pi...),
## the following suite improves performance.
## This may also help on Intel CPUs running 32-bit operating systems.
##
## Keep tls_cipher_suite empty if you have issues fetching sources or
## connecting to some DoH servers. Google and Cloudflare are fine with it.
## connecting to some DoH servers.
# tls_cipher_suite = [52392, 49199]
## Fallback resolver
## This is a normal, non-encrypted DNS resolver, that will be only used
## for one-shot queries when retrieving the initial resolvers list, and
## only if the system DNS configuration doesn't work.
## No user application queries will ever be leaked through this resolver,
## and it will not be used after IP addresses of resolvers URLs have been found.
## It will never be used if lists have already been cached, and if stamps
## don't include host names without IP addresses.
## It will not be used if the configured system DNS works.
## A resolver supporting DNSSEC is recommended.
## Log TLS key material to a file, for debugging purposes only.
## This file will contain the TLS master key, which can be used to decrypt
## all TLS traffic to/from DoH servers.
## Never ever enable except for debugging purposes with a tool such as mitmproxy.
# tls_key_log_file = '/tmp/keylog.txt'
## Bootstrap resolvers
##
## People in China may need to use 114.114.114.114:53 here.
## Other popular options include 8.8.8.8 and 1.1.1.1.
## These are normal, non-encrypted DNS resolvers, that will be only used
## for one-shot queries when retrieving the initial resolvers list and if
## the system DNS configuration doesn't work.
##
## No user queries will ever be leaked through these resolvers, and they will
## not be used after IP addresses of DoH resolvers have been found (if you are
## using DoH).
##
## They will never be used if lists have already been cached, and if the stamps
## of the configured servers already include IP addresses (which is the case for
## most of DoH servers, and for all DNSCrypt servers and relays).
##
## They will not be used if the configured system DNS works, or after the
## proxy already has at least one usable secure resolver.
##
## Resolvers supporting DNSSEC are recommended, and, if you are using
## DoH, bootstrap resolvers should ideally be operated by a different entity
## than the DoH servers you will be using, especially if you have IPv6 enabled.
##
## People in China may want to use 114.114.114.114:53 here.
## Other popular options include 8.8.8.8, 9.9.9.9 and 1.1.1.1.
##
## If more than one resolver is specified, they will be tried in sequence.
##
## TL;DR: put valid standard resolver addresses here. Your actual queries will
## not be sent there. If you're using DNSCrypt or Anonymized DNS and your
## lists are up to date, these resolvers will not even be used.
fallback_resolver = '9.9.9.9:53'
bootstrap_resolvers = ['9.9.9.11:53', '8.8.8.8:53']
## Always use the fallback resolver before the system DNS settings
## When internal DNS resolution is required, for example to retrieve
## the resolvers list:
##
## - queries will be sent to dnscrypt-proxy itself, if it is already
## running with active servers (*)
## - or else, queries will be sent to fallback servers
## - finally, if `ignore_system_dns` is `false`, queries will be sent
## to the system DNS
##
## (*) this is incompatible with systemd sockets.
## `listen_addrs` must not be empty.
ignore_system_dns = true
@ -235,8 +317,10 @@ netprobe_address = '9.9.9.9:53'
## These strings will be added as TXT records to queries.
## Do not use, except on servers explicitly asking for extra data
## to be present.
## encrypted-dns-server can be configured to use this for access control
## in the [access_control] section
# query_meta = ["key1:value1", "key2:value2", "key3:value3"]
# query_meta = ['key1:value1', 'key2:value2', 'token:MySecretToken']
## Automatic log files rotation
@ -256,19 +340,35 @@ log_files_max_backups = 1
# Filters #
#########################
## Note: if you are using dnsmasq, disable the `dnssec` option in dnsmasq if you
## configure dnscrypt-proxy to do any kind of filtering (including the filters
## below and blocklists).
## You can still choose resolvers that do DNSSEC validation.
## Immediately respond to IPv6-related queries with an empty response
## This makes things faster when there is no IPv6 connectivity, but can
## also cause reliability issues with some stub resolvers.
## Do not enable if you added a validating resolver such as dnsmasq in front
## of the proxy.
block_ipv6 = false
## TTL for synthetic responses sent when a request has been blocked (due to
## IPv6 or blacklists).
## Immediately respond to A and AAAA queries for host names without a domain name
## This also prevents "dotless domain names" from being resolved upstream.
reject_ttl = 600
block_unqualified = true
## Immediately respond to queries for local zones instead of leaking them to
## upstream resolvers (always causing errors or timeouts).
block_undelegated = true
## TTL for synthetic responses sent when a request has been blocked (due to
## IPv6 or blocklists).
reject_ttl = 10
@ -276,9 +376,7 @@ reject_ttl = 600
# Route queries for specific domains to a dedicated set of servers #
##################################################################################
## Example map entries (one entry per line):
## example.com 9.9.9.9
## example.net 9.9.9.9,8.8.8.8,1.1.1.1
## See the `example-forwarding-rules.txt` file for an example
# forwarding_rules = 'forwarding-rules.txt'
@ -291,16 +389,18 @@ reject_ttl = 600
## Cloaking returns a predefined address for a specific name.
## In addition to acting as a HOSTS file, it can also return the IP address
## of a different name. It will also do CNAME flattening.
## If 'cloak_ptr' is set, then PTR (reverse lookups) are enabled
## for cloaking rules that do not contain wild cards.
##
## Example map entries (one entry per line)
## example.com 10.1.1.1
## www.google.com forcesafesearch.google.com
## See the `example-cloaking-rules.txt` file for an example
# cloaking_rules = 'cloaking-rules.txt'
## TTL used when serving entries in cloaking-rules.txt
# cloak_ttl = 600
# cloak_ptr = false
###########################
@ -314,7 +414,7 @@ cache = true
## Cache size
cache_size = 1024
cache_size = 4096
## Minimum TTL for cached entries
@ -338,6 +438,53 @@ cache_neg_max_ttl = 600
########################################
# Captive portal handling #
########################################
[captive_portals]
## A file that contains a set of names used by operating systems to
## check for connectivity and captive portals, along with hard-coded
## IP addresses to return.
# map_file = 'example-captive-portals.txt'
##################################
# Local DoH server #
##################################
[local_doh]
## dnscrypt-proxy can act as a local DoH server. By doing so, web browsers
## requiring a direct connection to a DoH server in order to enable some
## features will enable these, without bypassing your DNS proxy.
## Addresses that the local DoH server should listen to
# listen_addresses = ['127.0.0.1:3000']
## Path of the DoH URL. This is not a file, but the part after the hostname
## in the URL. By convention, `/dns-query` is frequently chosen.
## For each `listen_address` the complete URL to access the server will be:
## `https://<listen_address><path>` (ex: `https://127.0.0.1/dns-query`)
# path = '/dns-query'
## Certificate file and key - Note that the certificate has to be trusted.
## Can be generated using the following command:
## openssl req -x509 -nodes -newkey rsa:2048 -days 5000 -sha256 -keyout localhost.pem -out localhost.pem
## See the documentation (wiki) for more information.
# cert_file = 'localhost.pem'
# cert_key_file = 'localhost.pem'
###############################
# Query logging #
###############################
@ -346,20 +493,20 @@ cache_neg_max_ttl = 600
[query_log]
## Path to the query log file (absolute, or relative to the same directory as the executable file)
## Can be /dev/stdout to log to the standard output (and set log_files_max_size to 0)
## Path to the query log file (absolute, or relative to the same directory as the config file)
## Can be set to /dev/stdout in order to log to the standard output.
# file = 'query.log'
# file = 'query.log'
## Query log format (currently supported: tsv and ltsv)
## Query log format (currently supported: tsv and ltsv)
format = 'tsv'
format = 'tsv'
## Do not log these query types, to reduce verbosity. Keep empty to log everything.
## Do not log these query types, to reduce verbosity. Keep empty to log everything.
# ignored_qtypes = ['DNSKEY', 'NS']
# ignored_qtypes = ['DNSKEY', 'NS']
@ -373,22 +520,22 @@ cache_neg_max_ttl = 600
[nx_log]
## Path to the query log file (absolute, or relative to the same directory as the executable file)
## Path to the query log file (absolute, or relative to the same directory as the config file)
# file = 'nx.log'
# file = 'nx.log'
## Query log format (currently supported: tsv and ltsv)
## Query log format (currently supported: tsv and ltsv)
format = 'tsv'
format = 'tsv'
######################################################
# Pattern-based blocking (blacklists) #
# Pattern-based blocking (blocklists) #
######################################################
## Blacklists are made of one pattern per line. Example of valid patterns:
## Blocklists are made of one pattern per line. Example of valid patterns:
##
## example.com
## =example.com
@ -397,81 +544,108 @@ cache_neg_max_ttl = 600
## ads*.example.*
## ads*.example[0-9]*.com
##
## Example blacklist files can be found at https://download.dnscrypt.info/blacklists/
## A script to build blacklists from public feeds can be found in the
## `utils/generate-domains-blacklists` directory of the dnscrypt-proxy source code.
## Example blocklist files can be found at https://download.dnscrypt.info/blocklists/
## A script to build blocklists from public feeds can be found in the
## `utils/generate-domains-blocklists` directory of the dnscrypt-proxy source code.
[blacklist]
[blocked_names]
## Path to the file of blocking rules (absolute, or relative to the same directory as the executable file)
## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
# blacklist_file = 'blacklist.txt'
# blocked_names_file = 'blocked-names.txt'
## Optional path to a file logging blocked queries
## Optional path to a file logging blocked queries
# log_file = 'blocked.log'
# log_file = 'blocked-names.log'
## Optional log format: tsv or ltsv (default: tsv)
## Optional log format: tsv or ltsv (default: tsv)
# log_format = 'tsv'
# log_format = 'tsv'
###########################################################
# Pattern-based IP blocking (IP blacklists) #
# Pattern-based IP blocking (IP blocklists) #
###########################################################
## IP blacklists are made of one pattern per line. Example of valid patterns:
## IP blocklists are made of one pattern per line. Example of valid patterns:
##
## 127.*
## fe80:abcd:*
## 192.168.1.4
[ip_blacklist]
[blocked_ips]
## Path to the file of blocking rules (absolute, or relative to the same directory as the executable file)
## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
# blacklist_file = 'ip-blacklist.txt'
# blocked_ips_file = 'blocked-ips.txt'
## Optional path to a file logging blocked queries
## Optional path to a file logging blocked queries
# log_file = 'ip-blocked.log'
# log_file = 'blocked-ips.log'
## Optional log format: tsv or ltsv (default: tsv)
## Optional log format: tsv or ltsv (default: tsv)
# log_format = 'tsv'
# log_format = 'tsv'
######################################################
# Pattern-based whitelisting (blacklists bypass) #
# Pattern-based allow lists (blocklists bypass) #
######################################################
## Whitelists support the same patterns as blacklists
## If a name matches a whitelist entry, the corresponding session
## Allowlists support the same patterns as blocklists
## If a name matches an allowlist entry, the corresponding session
## will bypass names and IP filters.
##
## Time-based rules are also supported to make some websites only accessible at specific times of the day.
[whitelist]
[allowed_names]
## Path to the file of whitelisting rules (absolute, or relative to the same directory as the executable file)
## Path to the file of allow list rules (absolute, or relative to the same directory as the config file)
# whitelist_file = 'whitelist.txt'
# allowed_names_file = 'allowed-names.txt'
## Optional path to a file logging whitelisted queries
## Optional path to a file logging allowed queries
# log_file = 'whitelisted.log'
# log_file = 'allowed-names.log'
## Optional log format: tsv or ltsv (default: tsv)
## Optional log format: tsv or ltsv (default: tsv)
# log_format = 'tsv'
# log_format = 'tsv'
#########################################################
# Pattern-based allowed IPs lists (blocklists bypass) #
#########################################################
## Allowed IP lists support the same patterns as IP blocklists
## If an IP response matches an allowed entry, the corresponding session
## will bypass IP filters.
##
## Time-based rules are also supported to make some websites only accessible at specific times of the day.
[allowed_ips]
## Path to the file of allowed ip rules (absolute, or relative to the same directory as the config file)
# allowed_ips_file = 'allowed-ips.txt'
## Optional path to a file logging allowed queries
# log_file = 'allowed-ips.log'
## Optional log format: tsv or ltsv (default: tsv)
# log_format = 'tsv'
@ -480,34 +654,33 @@ cache_neg_max_ttl = 600
##########################################
## One or more weekly schedules can be defined here.
## Patterns in the name-based blocklist can optionally be followed with @schedule_name
## Patterns in the name-based blocked_names file can optionally be followed with @schedule_name
## to apply the pattern 'schedule_name' only when it matches a time range of that schedule.
##
## For example, the following rule in a blacklist file:
## For example, the following rule in a blocklist file:
## *.youtube.* @time-to-sleep
## would block access to YouTube only during the days, and period of the days
## define by the 'time-to-sleep' schedule.
## would block access to YouTube during the times defined by the 'time-to-sleep' schedule.
##
## {after='21:00', before= '7:00'} matches 0:00-7:00 and 21:00-0:00
## {after= '9:00', before='18:00'} matches 9:00-18:00
[schedules]
# [schedules.'time-to-sleep']
# mon = [{after='21:00', before='7:00'}]
# tue = [{after='21:00', before='7:00'}]
# wed = [{after='21:00', before='7:00'}]
# thu = [{after='21:00', before='7:00'}]
# fri = [{after='23:00', before='7:00'}]
# sat = [{after='23:00', before='7:00'}]
# sun = [{after='21:00', before='7:00'}]
# [schedules.time-to-sleep]
# mon = [{after='21:00', before='7:00'}]
# tue = [{after='21:00', before='7:00'}]
# wed = [{after='21:00', before='7:00'}]
# thu = [{after='21:00', before='7:00'}]
# fri = [{after='23:00', before='7:00'}]
# sat = [{after='23:00', before='7:00'}]
# sun = [{after='21:00', before='7:00'}]
# [schedules.'work']
# mon = [{after='9:00', before='18:00'}]
# tue = [{after='9:00', before='18:00'}]
# wed = [{after='9:00', before='18:00'}]
# thu = [{after='9:00', before='18:00'}]
# fri = [{after='9:00', before='17:00'}]
# [schedules.work]
# mon = [{after='9:00', before='18:00'}]
# tue = [{after='9:00', before='18:00'}]
# wed = [{after='9:00', before='18:00'}]
# thu = [{after='9:00', before='18:00'}]
# fri = [{after='9:00', before='17:00'}]
@ -527,45 +700,71 @@ cache_neg_max_ttl = 600
## must include the prefixes.
##
## If the `urls` property is missing, cache files and valid signatures
## must be already present; This doesn't prevent these cache files from
## must already be present. This doesn't prevent these cache files from
## expiring after `refresh_delay` hours.
## `refreshed_delay` must be in the [24..168] interval.
## The minimum delay of 24 hours (1 day) avoids unnecessary requests to servers.
## The maximum delay of 168 hours (1 week) ensures cache freshness.
[sources]
## An example of a remote source from https://github.com/DNSCrypt/dnscrypt-resolvers
### An example of a remote source from https://github.com/DNSCrypt/dnscrypt-resolvers
[sources.'public-resolvers']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md']
cache_file = 'public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
prefix = ''
[sources.public-resolvers]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
cache_file = 'public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
## Anonymized DNS relays
### Anonymized DNS relays
[sources.'relays']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/relays.md', 'https://download.dnscrypt.info/resolvers-list/v2/relays.md']
cache_file = 'relays.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
[sources.relays]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/relays.md']
cache_file = 'relays.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
## Quad9 over DNSCrypt - https://quad9.net/
### ODoH (Oblivious DoH) servers and relays
# [sources.odoh-servers]
# urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/odoh-servers.md', 'https://download.dnscrypt.info/resolvers-list/v3/odoh-servers.md']
# cache_file = 'odoh-servers.md'
# minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
# refresh_delay = 24
# prefix = ''
# [sources.odoh-relays]
# urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/odoh-relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/odoh-relays.md']
# cache_file = 'odoh-relays.md'
# minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
# refresh_delay = 24
# prefix = ''
### Quad9
# [sources.quad9-resolvers]
# urls = ['https://www.quad9.net/quad9-resolvers.md']
# minisign_key = 'RWQBphd2+f6eiAqBsvDZEBXBGHQBJfeG6G+wJPPKxCZMoEQYpmoysKUN'
# cache_file = 'quad9-resolvers.md'
# prefix = 'quad9-'
# urls = ['https://www.quad9.net/quad9-resolvers.md']
# minisign_key = 'RWQBphd2+f6eiAqBsvDZEBXBGHQBJfeG6G+wJPPKxCZMoEQYpmoysKUN'
# cache_file = 'quad9-resolvers.md'
# prefix = 'quad9-'
## Another example source, with resolvers censoring some websites not appropriate for children
## This is a subset of the `public-resolvers` list, so enabling both is useless
### Another example source, with resolvers censoring some websites not appropriate for children
### This is a subset of the `public-resolvers` list, so enabling both is useless.
# [sources.'parental-control']
# urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/parental-control.md', 'https://download.dnscrypt.info/resolvers-list/v2/parental-control.md']
# cache_file = 'parental-control.md'
# minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
# [sources.parental-control]
# urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/parental-control.md', 'https://download.dnscrypt.info/resolvers-list/v3/parental-control.md']
# cache_file = 'parental-control.md'
# minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
### dnscry.pt servers - See https://www.dnscry.pt
# [sources.dnscry-pt-resolvers]
# urls = ["https://www.dnscry.pt/resolvers.md"]
# minisign_key = "RWQM31Nwkqh01x88SvrBL8djp1NH56Rb4mKLHz16K7qsXgEomnDv6ziQ"
# cache_file = "dnscry.pt-resolvers.md"
# refresh_delay = 72
# prefix = "dnscry.pt-"
#########################################
@ -574,16 +773,38 @@ cache_neg_max_ttl = 600
[broken_implementations]
# Cisco servers currently cannot handle queries larger than 1472 bytes, and don't
# truncate reponses larger than questions as expected by the DNSCrypt protocol.
# This prevents large responses from being received, and breaks relaying.
# A workaround for the first issue will be applied to servers in list below.
# Do not change that list until the bugs are fixed server-side.
## Cisco servers currently cannot handle queries larger than 1472 bytes, and don't
## truncate responses larger than questions as expected by the DNSCrypt protocol.
## This prevents large responses from being received over UDP and over relays.
##
## Older versions of the `dnsdist` server software had a bug with queries larger
## than 1500 bytes. This is fixed since `dnsdist` version 1.5.0, but
## some server may still run an outdated version.
##
## The list below enables workarounds to make non-relayed usage more reliable
## until the servers are fixed.
broken_query_padding = ['cisco', 'cisco-ipv6', 'cisco-familyshield']
fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'cleanbrowsing-adult', 'cleanbrowsing-adult-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-security', 'cleanbrowsing-security-ipv6']
#################################################################
# Certificate-based client authentication for DoH #
#################################################################
## Use a X509 certificate to authenticate yourself when connecting to DoH servers.
## This is only useful if you are operating your own, private DoH server(s).
## 'creds' maps servers to certificates, and supports multiple entries.
## If you are not using the standard root CA, an optional "root_ca"
## property set to the path to a root CRT file can be added to a server entry.
[doh_client_x509_auth]
# creds = [
# { server_name='*', client_cert='client.crt', client_key='client.key' }
# ]
################################
# Anonymized DNS #
@ -597,21 +818,28 @@ broken_query_padding = ['cisco', 'cisco-ipv6', 'cisco-familyshield']
## used to connect to that server.
##
## A relay can be specified as a DNS Stamp (either a relay stamp, or a
## DNSCrypt stamp), an IP:port, a hostname:port, or a server name.
## DNSCrypt stamp) or a server name.
##
## The following example routes "example-server-1" via `anon-example-1` or `anon-example-2``,
## and "example-server-2" via the relay whose relay DNS stamp
## is "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM".
## The following example routes "example-server-1" via `anon-example-1` or `anon-example-2`,
## and "example-server-2" via the relay whose relay DNS stamp is
## "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM".
##
## !!! THESE ARE JUST EXAMPLES !!!
##
## Review the list of available relays from the "relays.md` file, and, for each
## Review the list of available relays from the "relays.md" file, and, for each
## server you want to use, define the relays you want connections to go through.
##
## Carefully choose relays and servers so that the are run by different entities.
## Carefully choose relays and servers so that they are run by different entities.
##
## "server_name" can also be set to "*" to define a default route, but this is not
## recommended. if you do so, keep "server_names" short and distinct from relays.
## "server_name" can also be set to "*" to define a default route, for all servers:
## { server_name='*', via=['anon-example-1', 'anon-example-2'] }
##
## If a route is ["*"], the proxy automatically picks a relay on a distinct network.
## { server_name='*', via=['*'] } is also an option, but is likely to be suboptimal.
##
## Manual selection is always recommended over automatic selection, so that you can
## select (relay,server) pairs that work well and fit your own criteria (close by or
## in different countries, operated by different entities, on distinct ISPs...)
# routes = [
# { server_name='example-server-1', via=['anon-example-1', 'anon-example-2'] },
@ -619,10 +847,62 @@ broken_query_padding = ['cisco', 'cisco-ipv6', 'cisco-familyshield']
# ]
## Skip resolvers incompatible with anonymization instead of using them directly
skip_incompatible = false
## If public server certificates for a non-conformant server cannot be
## retrieved via a relay, try getting them directly. Actual queries
## will then always go through relays.
# direct_cert_fallback = false
###############################
# DNS64 #
###############################
## DNS64 is a mechanism for synthesizing AAAA records from A records.
## It is used with an IPv6/IPv4 translator to enable client-server
## communication between an IPv6-only client and an IPv4-only server,
## without requiring any changes to either the IPv6 or the IPv4 node,
## for the class of applications that work through NATs.
##
## There are two options to synthesize such records:
## Option 1: Using a set of static IPv6 prefixes;
## Option 2: By discovering the IPv6 prefix from DNS64-enabled resolver.
##
## If both options are configured - only static prefixes are used.
## (Ref. RFC6147, RFC6052, RFC7050)
##
## Do not enable unless you know what DNS64 is and why you need it, or else
## you won't be able to connect to anything at all.
[dns64]
## Static prefix(es) as Pref64::/n CIDRs
# prefix = ['64:ff9b::/96']
## DNS64-enabled resolver(s) to discover Pref64::/n CIDRs
## These resolvers are used to query for Well-Known IPv4-only Name (WKN) "ipv4only.arpa." to discover only.
## Set with your ISP's resolvers in case of custom prefixes (other than Well-Known Prefix 64:ff9b::/96).
## IMPORTANT: Default resolvers listed below support Well-Known Prefix 64:ff9b::/96 only.
# resolver = ['[2606:4700:4700::64]:53', '[2001:4860:4860::64]:53']
########################################
# Static entries #
########################################
## Optional, local, static list of additional servers
## Mostly useful for testing your own servers.
[static]
# [static.'myserver']
# stamp = 'sdns:AQcAAAAAAAAAAAAQMi5kbnNjcnlwdC1jZXJ0Lg'
# [static.myserver]
# stamp = 'sdns://AQcAAAAAAAAAAAAQMi5kbnNjcnlwdC1jZXJ0Lg'

View File

@ -10,5 +10,27 @@
## In order to enable this feature, the "forwarding_rules" property needs to
## be set to this file name inside the main configuration file.
## Blocking IPv6 may prevent local devices from being discovered.
## If this happens, set `block_ipv6` to `false` in the main config file.
## Forward *.lan, *.local, *.home, *.home.arpa, *.internal and *.localdomain to 192.168.1.1
# lan 192.168.1.1
# local 192.168.1.1
# home 192.168.1.1
# home.arpa 192.168.1.1
# internal 192.168.1.1
# localdomain 192.168.1.1
# 192.in-addr.arpa 192.168.1.1
## Forward queries for example.com and *.example.com to 9.9.9.9 and 8.8.8.8
# example.com 9.9.9.9,8.8.8.8
# example.com 9.9.9.9,8.8.8.8
## Forward queries to a resolver using IPv6
# ipv6.example.com [2001:DB8::42]:53
## Forward queries for .onion names to a local Tor client
## Tor must be configured with the following in the torrc file:
## DNSPort 9053
## AutomapHostsOnResolve 1
# onion 127.0.0.1:9053

View File

@ -1,25 +0,0 @@
###########################
# Whitelist #
###########################
## Rules for name-based query whitelisting, one per line
##
## Example of valid patterns:
##
## ads.* | matches anything with an "ads." prefix
## *.example.com | matches example.com and all names within that zone such as www.example.com
## example.com | identical to the above
## =example.com | whitelists example.com but not *.example.com
## *sex* | matches any name containing that substring
## ads[0-9]* | matches "ads" followed by one or more digits
## ads*.example* | *, ? and [] can be used anywhere, but prefixes/suffixes are faster
tracker.debian.org
## Time-based rules
# *.youtube.* @time-to-play
# facebook.com @play

View File

@ -0,0 +1,41 @@
//go:build gofuzzbeta
// +build gofuzzbeta
package main
import (
"encoding/hex"
"testing"
stamps "github.com/jedisct1/go-dnsstamps"
)
func FuzzParseODoHTargetConfigs(f *testing.F) {
configs_hex := "0020000100010020aacc53b3df0c6eb2d7d5ce4ddf399593376c9903ba6a52a52c3a2340f97bb764"
configs, _ := hex.DecodeString(configs_hex)
f.Add(configs)
f.Fuzz(func(t *testing.T, configs []byte) {
if _, err := parseODoHTargetConfigs(configs); err != nil {
t.Skip()
}
})
}
func FuzzParseStampParser(f *testing.F) {
f.Add("sdns://AgcAAAAAAAAACzEwNC4yMS42Ljc4AA1kb2guY3J5cHRvLnN4Ci9kbnMtcXVlcnk")
f.Add("sdns://AgcAAAAAAAAAGlsyNjA2OjQ3MDA6MzAzNzo6NjgxNTo2NGVdABJkb2gtaXB2Ni5jcnlwdG8uc3gKL2Rucy1xdWVyeQ")
f.Add(
"sdns://AQcAAAAAAAAADTUxLjE1LjEyMi4yNTAg6Q3ZfapcbHgiHKLF7QFoli0Ty1Vsz3RXs1RUbxUrwZAcMi5kbnNjcnlwdC1jZXJ0LnNjYWxld2F5LWFtcw",
)
f.Add(
"sdns://AQcAAAAAAAAAFlsyMDAxOmJjODoxODIwOjUwZDo6MV0g6Q3ZfapcbHgiHKLF7QFoli0Ty1Vsz3RXs1RUbxUrwZAcMi5kbnNjcnlwdC1jZXJ0LnNjYWxld2F5LWFtcw",
)
f.Add("sdns://gQ8xNjMuMTcyLjE4MC4xMjU")
f.Add("sdns://BQcAAAAAAAAADm9kb2guY3J5cHRvLnN4Ci9kbnMtcXVlcnk")
f.Add("sdns://hQcAAAAAAAAAACCi3jNJDEdtNW4tvHN8J3lpIklSa2Wrj7qaNCgEgci9_BpvZG9oLXJlbGF5LmVkZ2Vjb21wdXRlLmFwcAEv")
f.Fuzz(func(t *testing.T, stamp string) {
if _, err := stamps.NewServerStampFromString(stamp); err != nil {
t.Skip()
}
})
}

141
dnscrypt-proxy/local-doh.go Normal file
View File

@ -0,0 +1,141 @@
package main
import (
"encoding/base64"
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type localDoHHandler struct {
proxy *Proxy
}
func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
proxy := handler.proxy
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
dataType := "application/dns-message"
writer.Header().Set("Server", "dnscrypt-proxy")
if request.URL.Path != proxy.localDoHPath {
writer.WriteHeader(404)
return
}
packet := []byte{}
var err error
start := time.Now()
if request.Method == "POST" &&
request.Header.Get("Content-Type") == dataType {
packet, err = io.ReadAll(io.LimitReader(request.Body, int64(MaxDNSPacketSize)))
if err != nil {
dlog.Warnf("No body in a local DoH query")
return
}
} else if request.Method == "GET" && request.Header.Get("Accept") == dataType {
encodedPacket := request.URL.Query().Get("dns")
if len(encodedPacket) >= MinDNSPacketSize*4/3 && len(encodedPacket) <= MaxDNSPacketSize*4/3 {
packet, err = base64.RawURLEncoding.DecodeString(encodedPacket)
if err != nil {
dlog.Warnf("Invalid base64 in a local DoH query")
return
}
}
}
if len(packet) < MinDNSPacketSize {
writer.Header().Set("Content-Type", "text/plain")
writer.WriteHeader(400)
writer.Write([]byte("dnscrypt-proxy local DoH server\n"))
return
}
clientAddr, err := net.ResolveTCPAddr("tcp", request.RemoteAddr)
if err != nil {
dlog.Errorf("Unable to get the client address: [%v]", err)
return
}
xClientAddr := net.Addr(clientAddr)
hasEDNS0Padding, err := hasEDNS0Padding(packet)
if err != nil {
writer.WriteHeader(400)
return
}
response := proxy.processIncomingQuery("local_doh", proxy.mainProto, packet, &xClientAddr, nil, start, false)
if len(response) == 0 {
writer.WriteHeader(500)
return
}
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
writer.WriteHeader(500)
return
}
responseLen := len(response)
paddedLen := dohPaddedLen(responseLen)
padLen := paddedLen - responseLen
if hasEDNS0Padding {
response, err = addEDNS0PaddingIfNoneFound(&msg, response, padLen)
if err != nil {
dlog.Critical(err)
return
}
} else {
pad := strings.Repeat("X", padLen)
writer.Header().Set("X-Pad", pad)
}
writer.Header().Set("Content-Type", dataType)
writer.Header().Set("Content-Length", fmt.Sprint(len(response)))
writer.WriteHeader(200)
writer.Write(response)
}
func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
defer acceptPc.Close()
if len(proxy.localDoHCertFile) == 0 || len(proxy.localDoHCertKeyFile) == 0 {
dlog.Fatal("A certificate and a key are required to start a local DoH service")
}
httpServer := &http.Server{
ReadTimeout: proxy.timeout,
WriteTimeout: proxy.timeout,
Handler: localDoHHandler{proxy: proxy},
}
httpServer.SetKeepAlivesEnabled(true)
if err := httpServer.ServeTLS(acceptPc, proxy.localDoHCertFile, proxy.localDoHCertKeyFile); err != nil {
dlog.Fatal(err)
}
}
func dohPaddedLen(unpaddedLen int) int {
boundaries := [...]int{
64,
128,
192,
256,
320,
384,
512,
704,
768,
896,
960,
1024,
1088,
1152,
2688,
4080,
MaxDNSPacketSize,
}
for _, boundary := range boundaries {
if boundary >= unpaddedLen {
return boundary
}
}
return unpaddedLen
}

View File

@ -0,0 +1,47 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDb7g6EQhbfby97
k4oMbZTzdi2TWFBs7qK/QwgOu+L6EhNHPO1ZEU29v0APFBFJO5zyyAk9bZ9k9tPB
bCuVVI9jEUfLH3UCjEQPG6XI2w++uVh0yALvc/uurCvRHVlle/V7cAoikndc2SjE
RQUALbACIqwD5g0F77BYwcsreB4GH253/R6Q2/CJZ4jNHPjkocOJiVr3ejA0kkoN
MXpGUXWcrVVk20M2A1CeO7HAulLRcklEdoHE3v46pjp0iZK0F9LyZX1U1ql+4QL3
iQttoZ4tMg83lFHSt4G9PrpIhzXr9W4NW822faSvrIwwN/JbItUmRa7n/3+MkuJQ
IGGNDayXAgMBAAECggEBANs0fmGSocuXvYL1Pi4+9qxnCOwIpTi97Zam0BwnZwcL
Bw4FCyiwV4UdX1LoFIailT9i49rHLYzre4oZL6OKgdQjQCSTuQOOHLPWQbpdpWba
w/C5/jr+pkemMZIfJ6BAGiArPt7Qj4oKpFhj1qUj5H9sYXkNTcOx8Fm25rLv6TT9
O7wg0oCpyG+iBSbCYBp9mDMz8pfo4P3BhcFiyKCKeiAC6KuHU81dvuKeFB4XQK+X
no2NqDqe6MBkmTqjNNy+wi1COR7lu34LPiWU5Hq5PdIEqBBUMjlMI6oYlhlgNTdx
SvsqFz3Xs6kpAhJTrSiAqscPYosgaMQxo+LI26PJnikCgYEA9n0OERkm0wSBHnHY
Kx8jaxNYg93jEzVnEgI/MBTJZqEyCs9fF6Imv737VawEN/BhesZZX7bGZQfDo8AT
aiSa5upkkSGXEqTu5ytyoKFTb+dJ/qmx3+zP6dPVzDnc8WPYMoUg7vvjZkXXJgZX
+oMlMUW1wWiDNI3wP19W9Is6xssCgYEA5GqkUBEns6eTFJV0JKqbEORJJ7lx5NZe
cIx+jPpLkILG4mOKOg1TBx0wkxa9cELtsNsM+bPtu9OqRMhsfPBmsXDHhJwg0Z6G
eDTfYYPkpRhwZvl6jBZn9sLVR9wfg2hE+n0lfV3mceg336KOkwAehDU84SWZ2e0S
esqkpbHJa+UCgYA7PY0O8POSzcdWkNf6bS5vAqRIdSCpMjGGc4HKRYSuJNnJHVPm
czNK7Bcm3QPaiexzvI4oYd5G09niVjyUSx3rl7P56Y/MjFVau+d90agjAfyXtyMo
BVtnAGGnBtUiMvP4GGT06xcZMnnmCqpEbBaZQ/7N8Bdwnxh5sqlMdtX2hwKBgAhL
hyQRO2vezgyVUN50A6WdZLq4lVZGIq/bqkzcWhopZaebDc4F5doASV9OGBsXkyI1
EkePLTcA/NH6pVX0NQaEnfpG4To7k46R/PrBm3ATbyGONdEYjzX65VvytoJDKx4d
pVrkKhZA5KaOdLcJ7hHHDSrv/qJXZbBn44rQ5guxAoGBAJ6oeUsUUETakxlmIhmK
xuQmWqLf97BKt8r6Z8CqHKWK7vpG2OmgFYCQGaR7angQ8hmAOv6jM56XhoagDBoc
UoaoEyo9/uCk6NRUkUMj7Tk/5UQSiWLceVH27w+icMFhf1b7EmmNfk+APsiathO5
j4edf1AinVCPwRVVu1dtLL5P
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDAjCCAeoCCQCptj0+TjjIJjANBgkqhkiG9w0BAQsFADBDMREwDwYDVQQKDAhE
TlNDcnlwdDEaMBgGA1UECwwRTG9jYWwgdGVzdCBzZXJ2ZXIxEjAQBgNVBAMMCWxv
Y2FsaG9zdDAeFw0xOTExMTgxNDA2MzBaFw0zMzA3MjcxNDA2MzBaMEMxETAPBgNV
BAoMCEROU0NyeXB0MRowGAYDVQQLDBFMb2NhbCB0ZXN0IHNlcnZlcjESMBAGA1UE
AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2+4O
hEIW328ve5OKDG2U83Ytk1hQbO6iv0MIDrvi+hITRzztWRFNvb9ADxQRSTuc8sgJ
PW2fZPbTwWwrlVSPYxFHyx91AoxEDxulyNsPvrlYdMgC73P7rqwr0R1ZZXv1e3AK
IpJ3XNkoxEUFAC2wAiKsA+YNBe+wWMHLK3geBh9ud/0ekNvwiWeIzRz45KHDiYla
93owNJJKDTF6RlF1nK1VZNtDNgNQnjuxwLpS0XJJRHaBxN7+OqY6dImStBfS8mV9
VNapfuEC94kLbaGeLTIPN5RR0reBvT66SIc16/VuDVvNtn2kr6yMMDfyWyLVJkWu
5/9/jJLiUCBhjQ2slwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA6Vz5HnGuy8jZz
5i8ipbcDMCZNdpYYnxgD53hEKOfoSv7LaF0ztD8Kmg3s5LHv9EHlkK3+G6FWRGiP
9f6IbtRITaiVQP3M13T78hpN5Qq5jgsqjR7ZcN7Etr6ZFd7G/0+mzqbyBuW/3szt
RdX/YLy1csvjbZoNNuXGWRohXjg0Mjko2tRLmARvxA/gZV5zWycv3BD2BPzyCdS9
MDMYSF0RPiL8+alfwLNqLcqMA5liHlmZa85uapQyoUI3ksKJkEgU53aD8cYhH9Yn
6mVpsrvrcRLBiHlbi24QBolhFkCSRK8bXes8XDIPuD8iYRwlrVBwOakMFQWMqNfI
IMOKJomU
-----END CERTIFICATE-----

40
dnscrypt-proxy/logger.go Normal file
View File

@ -0,0 +1,40 @@
package main
import (
"io"
"os"
"github.com/jedisct1/dlog"
"gopkg.in/natefinch/lumberjack.v2"
)
func Logger(logMaxSize int, logMaxAge int, logMaxBackups int, fileName string) io.Writer {
if fileName == "/dev/stdout" {
return os.Stdout
}
if st, _ := os.Stat(fileName); st != nil && !st.Mode().IsRegular() {
if st.Mode().IsDir() {
dlog.Fatalf("[%v] is a directory", fileName)
}
fp, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
if err != nil {
dlog.Fatalf("Unable to access [%v]: [%v]", fileName, err)
}
return fp
}
if fp, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644); err == nil {
fp.Close()
} else {
dlog.Errorf("Unable to create [%v]: [%v]", fileName, err)
}
logger := &lumberjack.Logger{
LocalTime: true,
MaxSize: logMaxSize,
MaxAge: logMaxAge,
MaxBackups: logMaxBackups,
Filename: fileName,
Compress: true,
}
return logger
}

View File

@ -7,15 +7,15 @@ import (
"fmt"
"math/rand"
"os"
"runtime"
"sync"
"github.com/facebookgo/pidfile"
"github.com/jedisct1/dlog"
"github.com/kardianos/service"
)
const (
AppVersion = "2.0.33"
AppVersion = "2.1.5"
DefaultConfigFileName = "dnscrypt-proxy.toml"
)
@ -27,30 +27,32 @@ type App struct {
}
func main() {
tzErr := TimezoneSetup()
dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON")
os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1")
if tzErr != nil {
dlog.Warnf("Timezone setup failed: [%v]", tzErr)
}
runtime.MemProfileRate = 0
seed := make([]byte, 8)
crypto_rand.Read(seed)
rand.Seed(int64(binary.LittleEndian.Uint64(seed[:])))
if _, err := crypto_rand.Read(seed); err != nil {
dlog.Fatal(err)
}
rand.Seed(int64(binary.LittleEndian.Uint64(seed)))
pwd, err := os.Getwd()
if err != nil {
dlog.Fatal("Unable to find the path to the current directory")
}
svcConfig := &service.Config{
Name: "dnscrypt-proxy",
DisplayName: "DNSCrypt client proxy",
Description: "Encrypted/authenticated DNS proxy",
WorkingDirectory: pwd,
}
svcFlag := flag.String("service", "", fmt.Sprintf("Control the system service: %q", service.ControlAction))
version := flag.Bool("version", false, "print current proxy version")
resolve := flag.String("resolve", "", "resolve a name using system libraries")
flags := ConfigFlags{}
flags.Resolve = flag.String("resolve", "", "resolve a DNS name (string can be <name> or <name>,<resolver address>)")
flags.List = flag.Bool("list", false, "print the list of available resolvers for the enabled filters")
flags.ListAll = flag.Bool("list-all", false, "print the complete list of available resolvers, ignoring filters")
flags.JsonOutput = flag.Bool("json", false, "output list as JSON")
flags.IncludeRelays = flag.Bool("include-relays", false, "include the list of available relays in the output of -list and -list-all")
flags.JSONOutput = flag.Bool("json", false, "output list as JSON")
flags.Check = flag.Bool("check", false, "check the configuration file and exit")
flags.ConfigFile = flag.String("config", DefaultConfigFileName, "Path to the configuration file")
flags.Child = flag.Bool("child", false, "Invokes program as a child process")
@ -63,19 +65,28 @@ func main() {
fmt.Println(AppVersion)
os.Exit(0)
}
if resolve != nil && len(*resolve) > 0 {
Resolve(*resolve)
os.Exit(0)
if fullexecpath, err := os.Executable(); err == nil {
WarnIfMaybeWritableByOtherUsers(fullexecpath)
}
app := &App{
flags: &flags,
}
svcConfig := &service.Config{
Name: "dnscrypt-proxy",
DisplayName: "DNSCrypt client proxy",
Description: "Encrypted/authenticated DNS proxy",
WorkingDirectory: pwd,
Arguments: []string{"-config", *flags.ConfigFile},
}
svc, err := service.New(app, svcConfig)
if err != nil {
svc = nil
dlog.Debug(err)
}
app.proxy = NewProxy()
_ = ServiceManagerStartNotify()
if len(*svcFlag) != 0 {
@ -99,7 +110,7 @@ func main() {
return
}
if svc != nil {
if err = svc.Run(); err != nil {
if err := svc.Run(); err != nil {
dlog.Fatal(err)
}
} else {
@ -122,21 +133,24 @@ func (app *App) AppMain() {
if err := ConfigLoad(app.proxy, app.flags); err != nil {
dlog.Fatal(err)
}
if err := PidFileCreate(); err != nil {
dlog.Errorf("Unable to create the PID file: [%v]", err)
}
if err := app.proxy.InitPluginsGlobals(); err != nil {
dlog.Fatal(err)
}
app.quit = make(chan struct{})
app.wg.Add(1)
pidfile.Write()
app.proxy.StartProxy()
runtime.GC()
<-app.quit
dlog.Notice("Quit signal received...")
app.wg.Done()
}
func (app *App) Stop(service service.Service) error {
if pidFilePath := pidfile.GetPidfilePath(); len(pidFilePath) > 1 {
os.Remove(pidFilePath)
if err := PidFileRemove(); err != nil {
dlog.Warnf("Failed to remove the PID file: [%v]", err)
}
dlog.Notice("Stopped.")
return nil

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package main
@ -9,10 +10,17 @@ import (
"github.com/jedisct1/dlog"
)
func NetProbe(address string, timeout int) error {
func NetProbe(proxy *Proxy, address string, timeout int) error {
if len(address) <= 0 || timeout == 0 {
return nil
}
if captivePortalHandler, err := ColdStart(proxy); err == nil {
if captivePortalHandler != nil {
defer captivePortalHandler.Stop()
}
} else {
dlog.Critical(err)
}
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
return err

View File

@ -7,10 +7,17 @@ import (
"github.com/jedisct1/dlog"
)
func NetProbe(address string, timeout int) error {
func NetProbe(proxy *Proxy, address string, timeout int) error {
if len(address) <= 0 || timeout == 0 {
return nil
}
if captivePortalHandler, err := ColdStart(proxy); err == nil {
if captivePortalHandler != nil {
defer captivePortalHandler.Stop()
}
} else {
dlog.Critical(err)
}
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
return err
@ -25,8 +32,9 @@ func NetProbe(address string, timeout int) error {
pc, err := net.DialUDP("udp", nil, remoteUDPAddr)
if err == nil {
// Write at least 1 byte. This ensures that sockets are ready to use for writing.
// Windows specific: during the system startup, sockets can be created but the underlying buffers may not be setup yet. If this is the case
// Write fails with WSAENOBUFS: "An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full"
// Windows specific: during the system startup, sockets can be created but the underlying buffers may not be
// setup yet. If this is the case Write fails with WSAENOBUFS: "An operation on a socket could not be
// performed because the system lacked sufficient buffer space or because a queue was full"
_, err = pc.Write([]byte{0})
}
if err != nil {

View File

@ -0,0 +1,191 @@
package main
import (
"crypto/subtle"
"encoding/binary"
"fmt"
"github.com/jedisct1/dlog"
hpkecompact "github.com/jedisct1/go-hpke-compact"
)
const (
odohVersion = uint16(0x0001)
odohTestVersion = uint16(0xff06)
maxODoHConfigs = 10
)
type ODoHTargetConfig struct {
suite *hpkecompact.Suite
keyID []byte
publicKey []byte
}
func encodeLengthValue(b []byte) []byte {
lengthBuffer := make([]byte, 2)
binary.BigEndian.PutUint16(lengthBuffer, uint16(len(b)))
return append(lengthBuffer, b...)
}
func parseODoHTargetConfig(config []byte) (ODoHTargetConfig, error) {
if len(config) < 8 {
return ODoHTargetConfig{}, fmt.Errorf("Malformed config")
}
kemID := binary.BigEndian.Uint16(config[0:2])
kdfID := binary.BigEndian.Uint16(config[2:4])
aeadID := binary.BigEndian.Uint16(config[4:6])
publicKeyLength := binary.BigEndian.Uint16(config[6:8])
publicKey := config[8:]
if len(publicKey) != int(publicKeyLength) {
return ODoHTargetConfig{}, fmt.Errorf("Malformed config")
}
suite, err := hpkecompact.NewSuite(hpkecompact.KemID(kemID), hpkecompact.KdfID(kdfID), hpkecompact.AeadID(aeadID))
if err != nil {
return ODoHTargetConfig{}, err
}
_, _, err = suite.NewClientContext(publicKey, []byte("odoh query"), nil)
if err != nil {
return ODoHTargetConfig{}, err
}
keyID, err := suite.Expand(suite.Extract(config, nil), []byte("odoh key id"), uint16(suite.Hash().Size()))
if err != nil {
return ODoHTargetConfig{}, err
}
return ODoHTargetConfig{
suite: suite,
publicKey: publicKey,
keyID: encodeLengthValue(keyID),
}, nil
}
func parseODoHTargetConfigs(configs []byte) ([]ODoHTargetConfig, error) {
if len(configs) <= 2 {
return nil, fmt.Errorf("Server didn't return any ODoH configurations")
}
length := binary.BigEndian.Uint16(configs)
if len(configs) != int(length)+2 {
return nil, fmt.Errorf("Malformed configs")
}
targets := make([]ODoHTargetConfig, 0)
offset := 2
for {
if offset+4 > len(configs) || len(targets) >= maxODoHConfigs {
break
}
configVersion := binary.BigEndian.Uint16(configs[offset : offset+2])
configLength := binary.BigEndian.Uint16(configs[offset+2 : offset+4])
if configVersion == odohVersion || configVersion == odohTestVersion {
if configVersion != odohVersion {
dlog.Debugf("Server still uses the legacy 0x%x ODoH version", configVersion)
}
target, err := parseODoHTargetConfig(configs[offset+4 : offset+4+int(configLength)])
if err == nil {
targets = append(targets, target)
}
}
offset = offset + int(configLength) + 4
}
return targets, nil
}
type ODoHQuery struct {
suite *hpkecompact.Suite
ctx hpkecompact.ClientContext
odohPlaintext []byte
odohMessage []byte
}
func (t ODoHTargetConfig) encryptQuery(query []byte) (ODoHQuery, error) {
clientCtx, encryptedSharedSecret, err := t.suite.NewClientContext(t.publicKey, []byte("odoh query"), nil)
if err != nil {
return ODoHQuery{}, err
}
odohPlaintext := make([]byte, 4+len(query))
binary.BigEndian.PutUint16(odohPlaintext[0:2], uint16(len(query)))
copy(odohPlaintext[2:], query)
aad := append([]byte{0x01}, t.keyID...)
ciphertext, err := clientCtx.EncryptToServer(odohPlaintext, aad)
if err != nil {
return ODoHQuery{}, err
}
encryptedMessage := encodeLengthValue(append(encryptedSharedSecret, ciphertext...))
odohMessage := append(append([]byte{0x01}, t.keyID...), encryptedMessage...)
return ODoHQuery{
suite: t.suite,
odohPlaintext: odohPlaintext,
odohMessage: odohMessage,
ctx: clientCtx,
}, nil
}
func (q ODoHQuery) decryptResponse(response []byte) ([]byte, error) {
if len(response) < 3 {
return nil, fmt.Errorf("Malformed response")
}
messageType := response[0]
if messageType != uint8(0x02) {
return nil, fmt.Errorf("Malformed response")
}
responseNonceLength := binary.BigEndian.Uint16(response[1:3])
if len(response) < 5+int(responseNonceLength) {
return nil, fmt.Errorf("Malformed response")
}
responseNonceEnc := response[1 : 3+responseNonceLength]
secret, err := q.ctx.Export([]byte("odoh response"), q.suite.KeyBytes)
if err != nil {
return nil, err
}
salt := append(q.odohPlaintext, responseNonceEnc...)
prk := q.suite.Extract(secret, salt)
key, err := q.suite.Expand(prk, []byte("odoh key"), q.suite.KeyBytes)
if err != nil {
return nil, err
}
nonce, err := q.suite.Expand(prk, []byte("odoh nonce"), q.suite.NonceBytes)
if err != nil {
return nil, err
}
cipher, err := q.suite.NewRawCipher(key)
if err != nil {
return nil, err
}
ctLength := binary.BigEndian.Uint16(response[3+int(responseNonceLength) : 5+int(responseNonceLength)])
if int(ctLength) != len(response[5+int(responseNonceLength):]) {
return nil, fmt.Errorf("Malformed response")
}
ct := response[5+int(responseNonceLength):]
aad := response[0 : 3+int(responseNonceLength)]
responsePlaintext, err := cipher.Open(nil, nonce, ct, aad)
if err != nil {
return nil, err
}
responseLength := binary.BigEndian.Uint16(responsePlaintext[0:2])
valid := 1
for i := 4 + int(responseLength); i < len(responsePlaintext); i++ {
valid &= subtle.ConstantTimeByteEq(response[i], 0x00)
}
if valid != 1 {
return nil, fmt.Errorf("Malformed response")
}
return responsePlaintext[2 : 2+int(responseLength)], nil
}

View File

@ -30,7 +30,7 @@ type PatternMatcher struct {
indirectVals map[string]interface{}
}
func NewPatternPatcher() *PatternMatcher {
func NewPatternMatcher() *PatternMatcher {
patternMatcher := PatternMatcher{
blockedPrefixes: critbitgo.NewTrie(),
blockedSuffixes: critbitgo.NewTrie(),
@ -120,9 +120,13 @@ func (patternMatcher *PatternMatcher) Eval(qName string) (reject bool, reason st
return false, "", nil
}
if xval := patternMatcher.blockedExact[qName]; xval != nil {
return true, qName, xval
}
revQname := StringReverse(qName)
if match, xval, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(revQname)); found {
if len(match) == len(qName) || revQname[len(match)] == '.' {
if len(match) == len(revQname) || revQname[len(match)] == '.' {
return true, "*." + StringReverse(string(match)), xval
}
if len(match) < len(revQname) && len(revQname) > 0 {
@ -153,9 +157,5 @@ func (patternMatcher *PatternMatcher) Eval(qName string) (reject bool, reason st
}
}
if xval := patternMatcher.blockedExact[qName]; xval != nil {
return true, qName, xval
}
return false, "", nil
}

29
dnscrypt-proxy/pidfile.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"flag"
"os"
"path/filepath"
"strconv"
"github.com/dchest/safefile"
)
var pidFile = flag.String("pidfile", "", "Store the PID into a file")
func PidFileCreate() error {
if pidFile == nil || len(*pidFile) == 0 {
return nil
}
if err := os.MkdirAll(filepath.Dir(*pidFile), 0o755); err != nil {
return err
}
return safefile.WriteFile(*pidFile, []byte(strconv.Itoa(os.Getpid())), 0o644)
}
func PidFileRemove() error {
if pidFile == nil || len(*pidFile) == 0 {
return nil
}
return os.Remove(*pidFile)
}

View File

@ -0,0 +1,157 @@
package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
iradix "github.com/hashicorp/go-immutable-radix"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type PluginAllowedIP struct {
allowedPrefixes *iradix.Tree
allowedIPs map[string]interface{}
logger io.Writer
format string
}
func (plugin *PluginAllowedIP) Name() string {
return "allow_ip"
}
func (plugin *PluginAllowedIP) Description() string {
return "Allows DNS queries containing specific IP addresses"
}
func (plugin *PluginAllowedIP) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of allowed IP rules from [%s]", proxy.allowedIPFile)
lines, err := ReadTextFile(proxy.allowedIPFile)
if err != nil {
return err
}
plugin.allowedPrefixes = iradix.New()
plugin.allowedIPs = make(map[string]interface{})
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
ip := net.ParseIP(line)
trailingStar := strings.HasSuffix(line, "*")
if len(line) < 2 || (ip != nil && trailingStar) {
dlog.Errorf("Suspicious allowed IP rule [%s] at line %d", line, lineNo)
continue
}
if trailingStar {
line = line[:len(line)-1]
}
if strings.HasSuffix(line, ":") || strings.HasSuffix(line, ".") {
line = line[:len(line)-1]
}
if len(line) == 0 {
dlog.Errorf("Empty allowed IP rule at line %d", lineNo)
continue
}
if strings.Contains(line, "*") {
dlog.Errorf("Invalid rule: [%s] - wildcards can only be used as a suffix at line %d", line, lineNo)
continue
}
line = strings.ToLower(line)
if trailingStar {
plugin.allowedPrefixes, _, _ = plugin.allowedPrefixes.Insert([]byte(line), 0)
} else {
plugin.allowedIPs[line] = true
}
}
if len(proxy.allowedIPLogFile) == 0 {
return nil
}
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.allowedIPLogFile)
plugin.format = proxy.allowedIPFormat
return nil
}
func (plugin *PluginAllowedIP) Drop() error {
return nil
}
func (plugin *PluginAllowedIP) Reload() error {
return nil
}
func (plugin *PluginAllowedIP) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
answers := msg.Answer
if len(answers) == 0 {
return nil
}
allowed, reason, ipStr := false, "", ""
for _, answer := range answers {
header := answer.Header()
Rrtype := header.Rrtype
if header.Class != dns.ClassINET || (Rrtype != dns.TypeA && Rrtype != dns.TypeAAAA) {
continue
}
if Rrtype == dns.TypeA {
ipStr = answer.(*dns.A).A.String()
} else if Rrtype == dns.TypeAAAA {
ipStr = answer.(*dns.AAAA).AAAA.String() // IPv4-mapped IPv6 addresses are converted to IPv4
}
if _, found := plugin.allowedIPs[ipStr]; found {
allowed, reason = true, ipStr
break
}
match, _, found := plugin.allowedPrefixes.Root().LongestPrefix([]byte(ipStr))
if found {
if len(match) == len(ipStr) || (ipStr[len(match)] == '.' || ipStr[len(match)] == ':') {
allowed, reason = true, string(match)+"*"
break
}
}
}
if allowed {
pluginsState.sessionData["whitelisted"] = true
if plugin.logger != nil {
qName := pluginsState.qName
var clientIPStr string
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return nil
}
var line string
if plugin.format == "tsv" {
now := time.Now()
year, month, day := now.Date()
hour, minute, second := now.Clock()
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
line = fmt.Sprintf(
"%s\t%s\t%s\t%s\t%s\n",
tsStr,
clientIPStr,
StringQuote(qName),
StringQuote(ipStr),
StringQuote(reason),
)
} else if plugin.format == "ltsv" {
line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tip:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(ipStr), StringQuote(reason))
} else {
dlog.Fatalf("Unexpected log format: [%s]", plugin.format)
}
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
_, _ = plugin.logger.Write([]byte(line))
}
}
return nil
}

View File

@ -0,0 +1,127 @@
package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type PluginAllowName struct {
allWeeklyRanges *map[string]WeeklyRanges
patternMatcher *PatternMatcher
logger io.Writer
format string
}
func (plugin *PluginAllowName) Name() string {
return "allow_name"
}
func (plugin *PluginAllowName) Description() string {
return "Allow names matching patterns"
}
func (plugin *PluginAllowName) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of allowed names from [%s]", proxy.allowNameFile)
lines, err := ReadTextFile(proxy.allowNameFile)
if err != nil {
return err
}
plugin.allWeeklyRanges = proxy.allWeeklyRanges
plugin.patternMatcher = NewPatternMatcher()
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
parts := strings.Split(line, "@")
timeRangeName := ""
if len(parts) == 2 {
line = strings.TrimSpace(parts[0])
timeRangeName = strings.TrimSpace(parts[1])
} else if len(parts) > 2 {
dlog.Errorf("Syntax error in allowed names at line %d -- Unexpected @ character", 1+lineNo)
continue
}
var weeklyRanges *WeeklyRanges
if len(timeRangeName) > 0 {
weeklyRangesX, ok := (*plugin.allWeeklyRanges)[timeRangeName]
if !ok {
dlog.Errorf("Time range [%s] not found at line %d", timeRangeName, 1+lineNo)
} else {
weeklyRanges = &weeklyRangesX
}
}
if err := plugin.patternMatcher.Add(line, weeklyRanges, lineNo+1); err != nil {
dlog.Error(err)
continue
}
}
if len(proxy.allowNameLogFile) == 0 {
return nil
}
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.allowNameLogFile)
plugin.format = proxy.allowNameFormat
return nil
}
func (plugin *PluginAllowName) Drop() error {
return nil
}
func (plugin *PluginAllowName) Reload() error {
return nil
}
func (plugin *PluginAllowName) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
qName := pluginsState.qName
allowList, reason, xweeklyRanges := plugin.patternMatcher.Eval(qName)
var weeklyRanges *WeeklyRanges
if xweeklyRanges != nil {
weeklyRanges = xweeklyRanges.(*WeeklyRanges)
}
if allowList {
if weeklyRanges != nil && !weeklyRanges.Match() {
allowList = false
}
}
if allowList {
pluginsState.sessionData["whitelisted"] = true
if plugin.logger != nil {
var clientIPStr string
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return nil
}
var line string
if plugin.format == "tsv" {
now := time.Now()
year, month, day := now.Date()
hour, minute, second := now.Clock()
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
line = fmt.Sprintf("%s\t%s\t%s\t%s\n", tsStr, clientIPStr, StringQuote(qName), StringQuote(reason))
} else if plugin.format == "ltsv" {
line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(reason))
} else {
dlog.Fatalf("Unexpected log format: [%s]", plugin.format)
}
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
_, _ = plugin.logger.Write([]byte(line))
}
}
return nil
}

View File

@ -3,21 +3,20 @@ package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
"unicode"
iradix "github.com/hashicorp/go-immutable-radix"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
"gopkg.in/natefinch/lumberjack.v2"
)
type PluginBlockIP struct {
blockedPrefixes *iradix.Tree
blockedIPs map[string]interface{}
logger *lumberjack.Logger
logger io.Writer
format string
}
@ -31,15 +30,15 @@ func (plugin *PluginBlockIP) Description() string {
func (plugin *PluginBlockIP) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of IP blocking rules from [%s]", proxy.blockIPFile)
bin, err := ReadTextFile(proxy.blockIPFile)
lines, err := ReadTextFile(proxy.blockIPFile)
if err != nil {
return err
}
plugin.blockedPrefixes = iradix.New()
plugin.blockedIPs = make(map[string]interface{})
for lineNo, line := range strings.Split(string(bin), "\n") {
line = strings.TrimFunc(line, unicode.IsSpace)
if len(line) == 0 || strings.HasPrefix(line, "#") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
ip := net.ParseIP(line)
@ -72,7 +71,7 @@ func (plugin *PluginBlockIP) Init(proxy *Proxy) error {
if len(proxy.blockIPLogFile) == 0 {
return nil
}
plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.blockIPLogFile, Compress: true}
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.blockIPLogFile)
plugin.format = proxy.blockIPFormat
return nil
@ -122,19 +121,16 @@ func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
pluginsState.action = PluginsActionReject
pluginsState.returnCode = PluginsReturnCodeReject
if plugin.logger != nil {
questions := msg.Question
if len(questions) != 1 {
return nil
}
qName := strings.ToLower(StripTrailingDot(questions[0].Name))
if len(qName) < 2 {
return nil
}
qName := pluginsState.qName
var clientIPStr string
if pluginsState.clientProto == "udp" {
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
} else {
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return nil
}
var line string
if plugin.format == "tsv" {
@ -142,7 +138,14 @@ func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
year, month, day := now.Date()
hour, minute, second := now.Clock()
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
line = fmt.Sprintf("%s\t%s\t%s\t%s\t%s\n", tsStr, clientIPStr, StringQuote(qName), StringQuote(ipStr), StringQuote(reason))
line = fmt.Sprintf(
"%s\t%s\t%s\t%s\t%s\n",
tsStr,
clientIPStr,
StringQuote(qName),
StringQuote(ipStr),
StringQuote(reason),
)
} else if plugin.format == "ltsv" {
line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tip:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(ipStr), StringQuote(reason))
} else {
@ -151,7 +154,7 @@ func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
plugin.logger.Write([]byte(line))
_, _ = plugin.logger.Write([]byte(line))
}
}
return nil

View File

@ -29,20 +29,18 @@ func (plugin *PluginBlockIPv6) Reload() error {
}
func (plugin *PluginBlockIPv6) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) != 1 {
return nil
}
question := questions[0]
question := msg.Question[0]
if question.Qclass != dns.ClassINET || question.Qtype != dns.TypeAAAA {
return nil
}
synth := EmptyResponseFromMessage(msg)
hinfo := new(dns.HINFO)
hinfo.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeHINFO,
Class: dns.ClassINET, Ttl: 86400}
hinfo.Hdr = dns.RR_Header{
Name: question.Name, Rrtype: dns.TypeHINFO,
Class: dns.ClassINET, Ttl: 86400,
}
hinfo.Cpu = "AAAA queries have been locally blocked by dnscrypt-proxy"
hinfo.Os = "Set block_ipv6 to false to disable this feature"
hinfo.Os = "Set block_ipv6 to false to disable that feature"
synth.Answer = []dns.RR{hinfo}
qName := question.Name
i := strings.Index(qName, ".")
@ -58,8 +56,10 @@ func (plugin *PluginBlockIPv6) Eval(pluginsState *PluginsState, msg *dns.Msg) er
soa.Minttl = 2400
soa.Expire = 604800
soa.Retry = 300
soa.Hdr = dns.RR_Header{Name: parentZone, Rrtype: dns.TypeSOA,
Class: dns.ClassINET, Ttl: 60}
soa.Hdr = dns.RR_Header{
Name: parentZone, Rrtype: dns.TypeSOA,
Class: dns.ClassINET, Ttl: 60,
}
synth.Ns = []dns.RR{soa}
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth

View File

@ -3,105 +3,31 @@ package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
"unicode"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
type BlockedNames struct {
allWeeklyRanges *map[string]WeeklyRanges
patternMatcher *PatternMatcher
logger *lumberjack.Logger
logger io.Writer
format string
}
type PluginBlockName struct {
blockedNames *BlockedNames
}
const aliasesLimit = 8
func (plugin *PluginBlockName) Name() string {
return "block_name"
}
var blockedNames *BlockedNames
func (plugin *PluginBlockName) Description() string {
return "Block DNS queries matching name patterns"
}
func (plugin *PluginBlockName) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of blocking rules from [%s]", proxy.blockNameFile)
bin, err := ReadTextFile(proxy.blockNameFile)
if err != nil {
return err
}
blockedNames := BlockedNames{
allWeeklyRanges: proxy.allWeeklyRanges,
patternMatcher: NewPatternPatcher(),
}
for lineNo, line := range strings.Split(string(bin), "\n") {
line = strings.TrimFunc(line, unicode.IsSpace)
if len(line) == 0 || strings.HasPrefix(line, "#") {
continue
}
parts := strings.Split(line, "@")
timeRangeName := ""
if len(parts) == 2 {
line = strings.TrimFunc(parts[0], unicode.IsSpace)
timeRangeName = strings.TrimFunc(parts[1], unicode.IsSpace)
} else if len(parts) > 2 {
dlog.Errorf("Syntax error in block rules at line %d -- Unexpected @ character", 1+lineNo)
continue
}
var weeklyRanges *WeeklyRanges
if len(timeRangeName) > 0 {
weeklyRangesX, ok := (*blockedNames.allWeeklyRanges)[timeRangeName]
if !ok {
dlog.Errorf("Time range [%s] not found at line %d", timeRangeName, 1+lineNo)
} else {
weeklyRanges = &weeklyRangesX
}
}
if err := blockedNames.patternMatcher.Add(line, weeklyRanges, lineNo+1); err != nil {
dlog.Error(err)
continue
}
}
plugin.blockedNames = &blockedNames
if len(proxy.blockNameLogFile) == 0 {
return nil
}
blockedNames.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.blockNameLogFile, Compress: true}
blockedNames.format = proxy.blockNameFormat
return nil
}
func (plugin *PluginBlockName) Drop() error {
return nil
}
func (plugin *PluginBlockName) Reload() error {
return nil
}
func (plugin *PluginBlockName) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
if plugin.blockedNames == nil || pluginsState.sessionData["whitelisted"] != nil {
return nil
}
questions := msg.Question
if len(questions) != 1 {
return nil
}
qName := strings.ToLower(StripTrailingDot(questions[0].Name))
return plugin.blockedNames.check(pluginsState, qName)
}
func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string) error {
func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string, aliasFor *string) (bool, error) {
reject, reason, xweeklyRanges := blockedNames.patternMatcher.Eval(qName)
if aliasFor != nil {
reason = reason + " (alias for [" + *aliasFor + "])"
}
var weeklyRanges *WeeklyRanges
if xweeklyRanges != nil {
weeklyRanges = xweeklyRanges.(*WeeklyRanges)
@ -112,16 +38,20 @@ func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string
}
}
if !reject {
return nil
return false, nil
}
pluginsState.action = PluginsActionReject
pluginsState.returnCode = PluginsReturnCodeReject
if blockedNames.logger != nil {
var clientIPStr string
if pluginsState.clientProto == "udp" {
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
} else {
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return false, nil
}
var line string
if blockedNames.format == "tsv" {
@ -136,9 +66,146 @@ func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string
dlog.Fatalf("Unexpected log format: [%s]", blockedNames.format)
}
if blockedNames.logger == nil {
return errors.New("Log file not initialized")
return false, errors.New("Log file not initialized")
}
_, _ = blockedNames.logger.Write([]byte(line))
}
return true, nil
}
// ---
type PluginBlockName struct{}
func (plugin *PluginBlockName) Name() string {
return "block_name"
}
func (plugin *PluginBlockName) Description() string {
return "Block DNS queries matching name patterns"
}
func (plugin *PluginBlockName) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of blocking rules from [%s]", proxy.blockNameFile)
lines, err := ReadTextFile(proxy.blockNameFile)
if err != nil {
return err
}
xBlockedNames := BlockedNames{
allWeeklyRanges: proxy.allWeeklyRanges,
patternMatcher: NewPatternMatcher(),
}
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
parts := strings.Split(line, "@")
timeRangeName := ""
if len(parts) == 2 {
line = strings.TrimSpace(parts[0])
timeRangeName = strings.TrimSpace(parts[1])
} else if len(parts) > 2 {
dlog.Errorf("Syntax error in block rules at line %d -- Unexpected @ character", 1+lineNo)
continue
}
var weeklyRanges *WeeklyRanges
if len(timeRangeName) > 0 {
weeklyRangesX, ok := (*xBlockedNames.allWeeklyRanges)[timeRangeName]
if !ok {
dlog.Errorf("Time range [%s] not found at line %d", timeRangeName, 1+lineNo)
} else {
weeklyRanges = &weeklyRangesX
}
}
if err := xBlockedNames.patternMatcher.Add(line, weeklyRanges, lineNo+1); err != nil {
dlog.Error(err)
continue
}
}
blockedNames = &xBlockedNames
if len(proxy.blockNameLogFile) == 0 {
return nil
}
blockedNames.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.blockNameLogFile)
blockedNames.format = proxy.blockNameFormat
return nil
}
func (plugin *PluginBlockName) Drop() error {
return nil
}
func (plugin *PluginBlockName) Reload() error {
return nil
}
func (plugin *PluginBlockName) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
if blockedNames == nil || pluginsState.sessionData["whitelisted"] != nil {
return nil
}
_, err := blockedNames.check(pluginsState, pluginsState.qName, nil)
return err
}
// ---
type PluginBlockNameResponse struct{}
func (plugin *PluginBlockNameResponse) Name() string {
return "block_name"
}
func (plugin *PluginBlockNameResponse) Description() string {
return "Block DNS responses matching name patterns"
}
func (plugin *PluginBlockNameResponse) Init(proxy *Proxy) error {
return nil
}
func (plugin *PluginBlockNameResponse) Drop() error {
return nil
}
func (plugin *PluginBlockNameResponse) Reload() error {
return nil
}
func (plugin *PluginBlockNameResponse) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
if blockedNames == nil || pluginsState.sessionData["whitelisted"] != nil {
return nil
}
aliasFor := pluginsState.qName
aliasesLeft := aliasesLimit
answers := msg.Answer
for _, answer := range answers {
header := answer.Header()
if header.Class != dns.ClassINET {
continue
}
var target string
if header.Rrtype == dns.TypeCNAME {
target = answer.(*dns.CNAME).Target
} else if header.Rrtype == dns.TypeSVCB && answer.(*dns.SVCB).Priority == 0 {
target = answer.(*dns.SVCB).Target
} else if header.Rrtype == dns.TypeHTTPS && answer.(*dns.HTTPS).Priority == 0 {
target = answer.(*dns.HTTPS).Target
} else {
continue
}
target, err := NormalizeQName(target)
if err != nil {
return err
}
if blocked, err := blockedNames.check(pluginsState, target, &aliasFor); blocked || err != nil {
return err
}
aliasesLeft--
if aliasesLeft == 0 {
break
}
blockedNames.logger.Write([]byte(line))
}
return nil
}

View File

@ -0,0 +1,202 @@
package main
import (
"github.com/k-sone/critbitgo"
"github.com/miekg/dns"
)
var undelegatedSet = []string{
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
"0.in-addr.arpa",
"1",
"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
"10.in-addr.arpa",
"100.100.in-addr.arpa",
"100.51.198.in-addr.arpa",
"101.100.in-addr.arpa",
"102.100.in-addr.arpa",
"103.100.in-addr.arpa",
"104.100.in-addr.arpa",
"105.100.in-addr.arpa",
"106.100.in-addr.arpa",
"107.100.in-addr.arpa",
"108.100.in-addr.arpa",
"109.100.in-addr.arpa",
"110.100.in-addr.arpa",
"111.100.in-addr.arpa",
"112.100.in-addr.arpa",
"113.0.203.in-addr.arpa",
"113.100.in-addr.arpa",
"114.100.in-addr.arpa",
"115.100.in-addr.arpa",
"116.100.in-addr.arpa",
"117.100.in-addr.arpa",
"118.100.in-addr.arpa",
"119.100.in-addr.arpa",
"120.100.in-addr.arpa",
"121.100.in-addr.arpa",
"122.100.in-addr.arpa",
"123.100.in-addr.arpa",
"124.100.in-addr.arpa",
"125.100.in-addr.arpa",
"126.100.in-addr.arpa",
"127.100.in-addr.arpa",
"127.in-addr.arpa",
"16.172.in-addr.arpa",
"168.192.in-addr.arpa",
"17.172.in-addr.arpa",
"18.172.in-addr.arpa",
"19.172.in-addr.arpa",
"2.0.192.in-addr.arpa",
"20.172.in-addr.arpa",
"21.172.in-addr.arpa",
"22.172.in-addr.arpa",
"23.172.in-addr.arpa",
"24.172.in-addr.arpa",
"25.172.in-addr.arpa",
"254.169.in-addr.arpa",
"255.255.255.255.in-addr.arpa",
"26.172.in-addr.arpa",
"27.172.in-addr.arpa",
"28.172.in-addr.arpa",
"29.172.in-addr.arpa",
"30.172.in-addr.arpa",
"31.172.in-addr.arpa",
"64.100.in-addr.arpa",
"65.100.in-addr.arpa",
"66.100.in-addr.arpa",
"67.100.in-addr.arpa",
"68.100.in-addr.arpa",
"69.100.in-addr.arpa",
"70.100.in-addr.arpa",
"71.100.in-addr.arpa",
"72.100.in-addr.arpa",
"73.100.in-addr.arpa",
"74.100.in-addr.arpa",
"75.100.in-addr.arpa",
"76.100.in-addr.arpa",
"77.100.in-addr.arpa",
"78.100.in-addr.arpa",
"79.100.in-addr.arpa",
"8.b.d.0.1.0.0.2.ip6.arpa",
"8.e.f.ip6.arpa",
"80.100.in-addr.arpa",
"81.100.in-addr.arpa",
"82.100.in-addr.arpa",
"83.100.in-addr.arpa",
"84.100.in-addr.arpa",
"85.100.in-addr.arpa",
"86.100.in-addr.arpa",
"87.100.in-addr.arpa",
"88.100.in-addr.arpa",
"89.100.in-addr.arpa",
"9.e.f.ip6.arpa",
"90.100.in-addr.arpa",
"91.100.in-addr.arpa",
"92.100.in-addr.arpa",
"93.100.in-addr.arpa",
"94.100.in-addr.arpa",
"95.100.in-addr.arpa",
"96.100.in-addr.arpa",
"97.100.in-addr.arpa",
"98.100.in-addr.arpa",
"99.100.in-addr.arpa",
"a.e.f.ip6.arpa",
"airdream",
"api",
"b.e.f.ip6.arpa",
"bbrouter",
"belkin",
"bind",
"blinkap",
"corp",
"d.f.ip6.arpa",
"davolink",
"dearmyrouter",
"dhcp",
"dlink",
"domain",
"envoy",
"example",
"f.f.ip6.arpa",
"fritz.box",
"grp",
"gw==",
"home",
"home.arpa",
"hub",
"internal",
"intra",
"intranet",
"invalid",
"ksyun",
"lan",
"loc",
"local",
"localdomain",
"localhost",
"localnet",
"mail",
"modem",
"mynet",
"myrouter",
"novalocal",
"onion",
"openstacklocal",
"priv",
"private",
"prv",
"router",
"telus",
"test",
"totolink",
"wlan_ap",
"workgroup",
"zghjccbob3n0",
}
type PluginBlockUndelegated struct {
suffixes *critbitgo.Trie
}
func (plugin *PluginBlockUndelegated) Name() string {
return "block_undelegated"
}
func (plugin *PluginBlockUndelegated) Description() string {
return "Block undelegated DNS names"
}
func (plugin *PluginBlockUndelegated) Init(proxy *Proxy) error {
suffixes := critbitgo.NewTrie()
for _, line := range undelegatedSet {
pattern := StringReverse(line)
suffixes.Insert([]byte(pattern), true)
}
plugin.suffixes = suffixes
return nil
}
func (plugin *PluginBlockUndelegated) Drop() error {
return nil
}
func (plugin *PluginBlockUndelegated) Reload() error {
return nil
}
func (plugin *PluginBlockUndelegated) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
revQname := StringReverse(pluginsState.qName)
match, _, found := plugin.suffixes.LongestPrefix([]byte(revQname))
if !found {
return nil
}
if len(match) == len(revQname) || revQname[len(match)] == '.' {
synth := EmptyResponseFromMessage(msg)
synth.Rcode = dns.RcodeNameError
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.returnCode = PluginsReturnCodeSynth
}
return nil
}

View File

@ -0,0 +1,46 @@
package main
import (
"strings"
"github.com/miekg/dns"
)
type PluginBlockUnqualified struct{}
func (plugin *PluginBlockUnqualified) Name() string {
return "block_unqualified"
}
func (plugin *PluginBlockUnqualified) Description() string {
return "Block unqualified DNS names"
}
func (plugin *PluginBlockUnqualified) Init(proxy *Proxy) error {
return nil
}
func (plugin *PluginBlockUnqualified) Drop() error {
return nil
}
func (plugin *PluginBlockUnqualified) Reload() error {
return nil
}
func (plugin *PluginBlockUnqualified) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
question := msg.Question[0]
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
return nil
}
if strings.IndexByte(pluginsState.qName, '.') >= 0 {
return nil
}
synth := EmptyResponseFromMessage(msg)
synth.Rcode = dns.RcodeNameError
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.returnCode = PluginsReturnCodeSynth
return nil
}

View File

@ -3,14 +3,15 @@ package main
import (
"crypto/sha512"
"encoding/binary"
"errors"
"sync"
"time"
lru "github.com/hashicorp/golang-lru"
"github.com/miekg/dns"
sieve "github.com/opencoff/go-sieve"
)
const StaleResponseTTL = 30 * time.Second
type CachedResponse struct {
expiration time.Time
msg dns.Msg
@ -18,70 +19,33 @@ type CachedResponse struct {
type CachedResponses struct {
sync.RWMutex
cache *lru.ARCCache
cache *sieve.Sieve[[32]byte, CachedResponse]
}
var cachedResponses CachedResponses
type PluginCacheResponse struct {
cachedResponses *CachedResponses
}
func (plugin *PluginCacheResponse) Name() string {
return "cache_response"
}
func (plugin *PluginCacheResponse) Description() string {
return "DNS cache (writer)."
}
func (plugin *PluginCacheResponse) Init(proxy *Proxy) error {
return nil
}
func (plugin *PluginCacheResponse) Drop() error {
return nil
}
func (plugin *PluginCacheResponse) Reload() error {
return nil
}
func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
plugin.cachedResponses = &cachedResponses
if msg.Rcode != dns.RcodeSuccess && msg.Rcode != dns.RcodeNameError && msg.Rcode != dns.RcodeNotAuth {
return nil
func computeCacheKey(pluginsState *PluginsState, msg *dns.Msg) [32]byte {
question := msg.Question[0]
h := sha512.New512_256()
var tmp [5]byte
binary.LittleEndian.PutUint16(tmp[0:2], question.Qtype)
binary.LittleEndian.PutUint16(tmp[2:4], question.Qclass)
if pluginsState.dnssec {
tmp[4] = 1
}
if msg.Truncated {
return nil
}
cacheKey, err := computeCacheKey(pluginsState, msg)
if err != nil {
return err
}
ttl := getMinTTL(msg, pluginsState.cacheMinTTL, pluginsState.cacheMaxTTL, pluginsState.cacheNegMinTTL, pluginsState.cacheNegMaxTTL)
cachedResponse := CachedResponse{
expiration: time.Now().Add(ttl),
msg: *msg,
}
plugin.cachedResponses.Lock()
if plugin.cachedResponses.cache == nil {
plugin.cachedResponses.cache, err = lru.NewARC(pluginsState.cacheSize)
if err != nil {
plugin.cachedResponses.Unlock()
return err
}
}
plugin.cachedResponses.cache.Add(cacheKey, cachedResponse)
plugin.cachedResponses.Unlock()
updateTTL(msg, cachedResponse.expiration)
h.Write(tmp[:])
normalizedRawQName := []byte(question.Name)
NormalizeRawQName(&normalizedRawQName)
h.Write(normalizedRawQName)
var sum [32]byte
h.Sum(sum[:0])
return nil
return sum
}
type PluginCache struct {
cachedResponses *CachedResponses
}
// ---
type PluginCache struct{}
func (plugin *PluginCache) Name() string {
return "cache"
@ -104,57 +68,97 @@ func (plugin *PluginCache) Reload() error {
}
func (plugin *PluginCache) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
plugin.cachedResponses = &cachedResponses
cacheKey := computeCacheKey(pluginsState, msg)
cacheKey, err := computeCacheKey(pluginsState, msg)
if err != nil {
cachedResponses.RLock()
if cachedResponses.cache == nil {
cachedResponses.RUnlock()
return nil
}
plugin.cachedResponses.RLock()
defer plugin.cachedResponses.RUnlock()
if plugin.cachedResponses.cache == nil {
return nil
}
cachedAny, ok := plugin.cachedResponses.cache.Get(cacheKey)
cached, ok := cachedResponses.cache.Get(cacheKey)
if !ok {
cachedResponses.RUnlock()
return nil
}
cached := cachedAny.(CachedResponse)
if time.Now().After(cached.expiration) {
return nil
}
expiration := cached.expiration
synth := cached.msg.Copy()
cachedResponses.RUnlock()
updateTTL(&cached.msg, cached.expiration)
synth := cached.msg
synth.Id = msg.Id
synth.Response = true
synth.Compress = true
synth.Question = msg.Question
pluginsState.synthResponse = &synth
if time.Now().After(expiration) {
expiration2 := time.Now().Add(StaleResponseTTL)
updateTTL(synth, expiration2)
pluginsState.sessionData["stale"] = synth
return nil
}
updateTTL(synth, expiration)
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.cacheHit = true
return nil
}
func computeCacheKey(pluginsState *PluginsState, msg *dns.Msg) ([32]byte, error) {
questions := msg.Question
if len(questions) != 1 {
return [32]byte{}, errors.New("No question present")
}
question := questions[0]
h := sha512.New512_256()
var tmp [5]byte
binary.LittleEndian.PutUint16(tmp[0:2], question.Qtype)
binary.LittleEndian.PutUint16(tmp[2:4], question.Qclass)
if pluginsState.dnssec {
tmp[4] = 1
}
h.Write(tmp[:])
normalizedName := []byte(question.Name)
NormalizeName(&normalizedName)
h.Write(normalizedName)
var sum [32]byte
h.Sum(sum[:0])
return sum, nil
// ---
type PluginCacheResponse struct{}
func (plugin *PluginCacheResponse) Name() string {
return "cache_response"
}
func (plugin *PluginCacheResponse) Description() string {
return "DNS cache (writer)."
}
func (plugin *PluginCacheResponse) Init(proxy *Proxy) error {
return nil
}
func (plugin *PluginCacheResponse) Drop() error {
return nil
}
func (plugin *PluginCacheResponse) Reload() error {
return nil
}
func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
if msg.Rcode != dns.RcodeSuccess && msg.Rcode != dns.RcodeNameError && msg.Rcode != dns.RcodeNotAuth {
return nil
}
if msg.Truncated {
return nil
}
cacheKey := computeCacheKey(pluginsState, msg)
ttl := getMinTTL(
msg,
pluginsState.cacheMinTTL,
pluginsState.cacheMaxTTL,
pluginsState.cacheNegMinTTL,
pluginsState.cacheNegMaxTTL,
)
cachedResponse := CachedResponse{
expiration: time.Now().Add(ttl),
msg: *msg,
}
cachedResponses.Lock()
if cachedResponses.cache == nil {
var err error
cachedResponses.cache = sieve.New[[32]byte, CachedResponse](pluginsState.cacheSize)
if cachedResponses.cache == nil {
cachedResponses.Unlock()
return err
}
}
cachedResponses.cache.Add(cacheKey, cachedResponse)
cachedResponses.Unlock()
updateTTL(msg, cachedResponse.expiration)
return nil
}

View File

@ -0,0 +1,44 @@
package main
import (
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type PluginCaptivePortal struct {
captivePortalMap *CaptivePortalMap
}
func (plugin *PluginCaptivePortal) Name() string {
return "captive portal handlers"
}
func (plugin *PluginCaptivePortal) Description() string {
return "Handle test queries operating systems make to detect Wi-Fi captive portal"
}
func (plugin *PluginCaptivePortal) Init(proxy *Proxy) error {
plugin.captivePortalMap = proxy.captivePortalMap
dlog.Notice("Captive portals handler enabled")
return nil
}
func (plugin *PluginCaptivePortal) Drop() error {
return nil
}
func (plugin *PluginCaptivePortal) Reload() error {
return nil
}
func (plugin *PluginCaptivePortal) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
question, ips := plugin.captivePortalMap.GetEntry(msg)
if ips == nil {
return nil
}
if synth := HandleCaptivePortalQuery(msg, question, ips); synth != nil {
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
}
return nil
}

View File

@ -19,12 +19,14 @@ type CloakedName struct {
lastUpdate *time.Time
lineNo int
isIP bool
PTR []string
}
type PluginCloak struct {
sync.RWMutex
patternMatcher *PatternMatcher
ttl uint32
createPTR bool
}
func (plugin *PluginCloak) Name() string {
@ -37,23 +39,24 @@ func (plugin *PluginCloak) Description() string {
func (plugin *PluginCloak) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of cloaking rules from [%s]", proxy.cloakFile)
bin, err := ReadTextFile(proxy.cloakFile)
lines, err := ReadTextFile(proxy.cloakFile)
if err != nil {
return err
}
plugin.ttl = proxy.cloakTTL
plugin.patternMatcher = NewPatternPatcher()
plugin.createPTR = proxy.cloakedPTR
plugin.patternMatcher = NewPatternMatcher()
cloakedNames := make(map[string]*CloakedName)
for lineNo, line := range strings.Split(string(bin), "\n") {
line = strings.TrimFunc(line, unicode.IsSpace)
if len(line) == 0 || strings.HasPrefix(line, "#") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
var target string
parts := strings.FieldsFunc(line, unicode.IsSpace)
if len(parts) == 2 {
line = strings.TrimFunc(parts[0], unicode.IsSpace)
target = strings.TrimFunc(parts[1], unicode.IsSpace)
line = strings.TrimSpace(parts[0])
target = strings.TrimSpace(parts[1])
} else if len(parts) > 2 {
dlog.Errorf("Syntax error in cloaking rules at line %d -- Unexpected space character", 1+lineNo)
continue
@ -67,11 +70,12 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
if !found {
cloakedName = &CloakedName{}
}
if ip := net.ParseIP(target); ip != nil {
ip := net.ParseIP(target)
if ip != nil {
if ipv4 := ip.To4(); ipv4 != nil {
cloakedName.ipv4 = append((*cloakedName).ipv4, ipv4)
cloakedName.ipv4 = append(cloakedName.ipv4, ipv4)
} else if ipv6 := ip.To16(); ipv6 != nil {
cloakedName.ipv6 = append((*cloakedName).ipv6, ipv6)
cloakedName.ipv6 = append(cloakedName.ipv6, ipv6)
} else {
dlog.Errorf("Invalid IP address in cloaking rule at line %d", 1+lineNo)
continue
@ -82,13 +86,46 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
}
cloakedName.lineNo = lineNo + 1
cloakedNames[line] = cloakedName
if !plugin.createPTR || strings.Contains(line, "*") || !cloakedName.isIP {
continue
}
var ptrLine string
if ipv4 := ip.To4(); ipv4 != nil {
reversed, _ := dns.ReverseAddr(ip.To4().String())
ptrLine = strings.TrimSuffix(reversed, ".")
} else {
reversed, _ := dns.ReverseAddr(cloakedName.ipv6[0].To16().String())
ptrLine = strings.TrimSuffix(reversed, ".")
}
ptrQueryLine := ptrEntryToQuery(ptrLine)
ptrCloakedName, found := cloakedNames[ptrQueryLine]
if !found {
ptrCloakedName = &CloakedName{}
}
ptrCloakedName.isIP = true
ptrCloakedName.PTR = append((*ptrCloakedName).PTR, ptrNameToFQDN(line))
ptrCloakedName.lineNo = lineNo + 1
cloakedNames[ptrQueryLine] = ptrCloakedName
}
for line, cloakedName := range cloakedNames {
plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo)
if err := plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo); err != nil {
return err
}
}
return nil
}
func ptrEntryToQuery(ptrEntry string) string {
return "=" + ptrEntry
}
func ptrNameToFQDN(ptrLine string) string {
ptrLine = strings.TrimPrefix(ptrLine, "=")
return ptrLine + "."
}
func (plugin *PluginCloak) Drop() error {
return nil
}
@ -98,25 +135,23 @@ func (plugin *PluginCloak) Reload() error {
}
func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) != 1 {
return nil
}
question := questions[0]
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
return nil
}
qName := strings.ToLower(StripTrailingDot(questions[0].Name))
if len(qName) < 2 {
question := msg.Question[0]
if question.Qclass != dns.ClassINET || question.Qtype == dns.TypeNS || question.Qtype == dns.TypeSOA {
return nil
}
now := time.Now()
plugin.RLock()
_, _, xcloakedName := plugin.patternMatcher.Eval(qName)
_, _, xcloakedName := plugin.patternMatcher.Eval(pluginsState.qName)
if xcloakedName == nil {
plugin.RUnlock()
return nil
}
if question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA && question.Qtype != dns.TypePTR {
plugin.RUnlock()
pluginsState.action = PluginsActionReject
pluginsState.returnCode = PluginsReturnCodeCloak
return nil
}
cloakedName := xcloakedName.(*CloakedName)
ttl, expired := plugin.ttl, false
if cloakedName.lastUpdate != nil {
@ -153,33 +188,35 @@ func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error
plugin.Unlock()
plugin.RLock()
}
var ip *net.IP
if question.Qtype == dns.TypeA {
ipLen := len(cloakedName.ipv4)
if ipLen > 0 {
ip = &cloakedName.ipv4[rand.Intn(ipLen)]
}
} else {
ipLen := len(cloakedName.ipv6)
if ipLen > 0 {
ip = &cloakedName.ipv6[rand.Intn(ipLen)]
}
}
plugin.RUnlock()
synth := EmptyResponseFromMessage(msg)
if ip == nil {
synth.Answer = []dns.RR{}
} else if question.Qtype == dns.TypeA {
rr := new(dns.A)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
rr.A = *ip
synth.Answer = []dns.RR{rr}
} else {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = *ip
synth.Answer = []dns.RR{rr}
synth.Answer = []dns.RR{}
if question.Qtype == dns.TypeA {
for _, ip := range cloakedName.ipv4 {
rr := new(dns.A)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
rr.A = ip
synth.Answer = append(synth.Answer, rr)
}
} else if question.Qtype == dns.TypeAAAA {
for _, ip := range cloakedName.ipv6 {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = ip
synth.Answer = append(synth.Answer, rr)
}
} else if question.Qtype == dns.TypePTR {
for _, ptr := range cloakedName.PTR {
rr := new(dns.PTR)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}
rr.Ptr = ptr
synth.Answer = append(synth.Answer, rr)
}
}
rand.Shuffle(
len(synth.Answer),
func(i, j int) { synth.Answer[i], synth.Answer[j] = synth.Answer[j], synth.Answer[i] },
)
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.returnCode = PluginsReturnCodeCloak

View File

@ -0,0 +1,265 @@
package main
import (
"errors"
"net"
"sync"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
const rfc7050WKN = "ipv4only.arpa."
var (
rfc7050WKA1 = net.IPv4(192, 0, 0, 170)
rfc7050WKA2 = net.IPv4(192, 0, 0, 171)
)
type PluginDNS64 struct {
pref64Mutex *sync.RWMutex
pref64 []*net.IPNet
dns64Resolvers []string
ipv4Resolver string
proxy *Proxy
}
func (plugin *PluginDNS64) Name() string {
return "dns64"
}
func (plugin *PluginDNS64) Description() string {
return "Synthesize DNS64 AAAA responses"
}
func (plugin *PluginDNS64) Init(proxy *Proxy) error {
if len(proxy.listenAddresses) == 0 {
return errors.New("At least one listening IP address must be configured for the DNS64 plugin to work")
}
plugin.ipv4Resolver = proxy.listenAddresses[0] // query is sent to ourselves
plugin.pref64Mutex = new(sync.RWMutex)
plugin.proxy = proxy
if len(proxy.dns64Prefixes) != 0 {
plugin.pref64Mutex.Lock()
defer plugin.pref64Mutex.Unlock()
for _, prefStr := range proxy.dns64Prefixes {
_, pref, err := net.ParseCIDR(prefStr)
if err != nil {
return err
}
dlog.Noticef("Registered DNS64 prefix [%s]", pref.String())
plugin.pref64 = append(plugin.pref64, pref)
}
} else if len(proxy.dns64Resolvers) != 0 {
plugin.dns64Resolvers = proxy.dns64Resolvers
if err := plugin.refreshPref64(); err != nil {
return err
}
} else {
return nil
}
dlog.Notice("DNS64 map enabled")
return nil
}
func (plugin *PluginDNS64) Drop() error {
return nil
}
func (plugin *PluginDNS64) Reload() error {
return nil
}
func (plugin *PluginDNS64) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
if hasAAAAAnswer(msg) {
return nil
}
question := pluginsState.questionMsg.Question[0]
if question.Qclass != dns.ClassINET || question.Qtype != dns.TypeAAAA {
return nil
}
msgA := pluginsState.questionMsg.Copy()
msgA.SetQuestion(question.Name, dns.TypeA)
msgAPacket, err := msgA.Pack()
if err != nil {
return err
}
if !plugin.proxy.clientsCountInc() {
return errors.New("Too many concurrent connections to handle DNS64 subqueries")
}
respPacket := plugin.proxy.processIncomingQuery(
"trampoline",
plugin.proxy.mainProto,
msgAPacket,
nil,
nil,
time.Now(),
false,
)
plugin.proxy.clientsCountDec()
resp := dns.Msg{}
if err := resp.Unpack(respPacket); err != nil {
return err
}
if resp.Rcode != dns.RcodeSuccess {
return nil
}
if len(resp.Answer) == 0 {
return nil
}
initialTTL := uint32(600)
for _, ns := range resp.Ns {
header := ns.Header()
if header.Rrtype == dns.TypeSOA {
initialTTL = header.Ttl
}
}
synth64 := make([]dns.RR, 0)
for _, answer := range resp.Answer {
header := answer.Header()
if header.Rrtype == dns.TypeCNAME {
synth64 = append(synth64, answer)
} else if header.Rrtype == dns.TypeA {
ttl := initialTTL
if ttl > header.Ttl {
ttl = header.Ttl
}
ipv4 := answer.(*dns.A).A.To4()
if ipv4 != nil {
plugin.pref64Mutex.RLock()
for _, prefix := range plugin.pref64 {
ipv6 := translateToIPv6(ipv4, prefix)
synthAAAA := new(dns.AAAA)
synthAAAA.Hdr = dns.RR_Header{
Name: header.Name,
Rrtype: dns.TypeAAAA,
Class: header.Class,
Ttl: ttl,
}
synthAAAA.AAAA = ipv6
synth64 = append(synth64, synthAAAA)
}
plugin.pref64Mutex.RUnlock()
}
}
}
msg.Answer = synth64
msg.AuthenticatedData = false
msg.SetEdns0(uint16(MaxDNSUDPSafePacketSize), false)
pluginsState.returnCode = PluginsReturnCodeCloak
return nil
}
func hasAAAAAnswer(msg *dns.Msg) bool {
for _, answer := range msg.Answer {
if answer.Header().Rrtype == dns.TypeAAAA {
return true
}
}
return false
}
func translateToIPv6(ipv4 net.IP, prefix *net.IPNet) net.IP {
ipv6 := make(net.IP, net.IPv6len)
copy(ipv6, prefix.IP)
n, _ := prefix.Mask.Size()
ipShift := n / 8
for i := 0; i < net.IPv4len; i++ {
if ipShift+i == 8 {
ipShift++
}
ipv6[ipShift+i] = ipv4[i]
}
return ipv6
}
func (plugin *PluginDNS64) fetchPref64(resolver string) error {
msg := new(dns.Msg)
msg.SetQuestion(rfc7050WKN, dns.TypeAAAA)
client := new(dns.Client)
resp, _, err := client.Exchange(msg, resolver)
if err != nil {
return err
}
if resp == nil || resp.Rcode != dns.RcodeSuccess {
return errors.New("Unable to fetch Pref64")
}
uniqPrefixes := make(map[string]struct{})
prefixes := make([]*net.IPNet, 0)
for _, answer := range resp.Answer {
if answer.Header().Rrtype == dns.TypeAAAA {
ipv6 := answer.(*dns.AAAA).AAAA
if ipv6 != nil && len(ipv6) == net.IPv6len {
prefEnd := 0
if wka := net.IPv4(ipv6[12], ipv6[13], ipv6[14], ipv6[15]); wka.Equal(rfc7050WKA1) ||
wka.Equal(rfc7050WKA2) { // 96
prefEnd = 12
} else if wka := net.IPv4(ipv6[9], ipv6[10], ipv6[11], ipv6[12]); wka.Equal(rfc7050WKA1) || wka.Equal(rfc7050WKA2) { // 64
prefEnd = 8
} else if wka := net.IPv4(ipv6[7], ipv6[9], ipv6[10], ipv6[11]); wka.Equal(rfc7050WKA1) || wka.Equal(rfc7050WKA2) { // 56
prefEnd = 7
} else if wka := net.IPv4(ipv6[6], ipv6[7], ipv6[9], ipv6[10]); wka.Equal(rfc7050WKA1) || wka.Equal(rfc7050WKA2) { // 48
prefEnd = 6
} else if wka := net.IPv4(ipv6[5], ipv6[6], ipv6[7], ipv6[9]); wka.Equal(rfc7050WKA1) || wka.Equal(rfc7050WKA2) { // 40
prefEnd = 5
} else if wka := net.IPv4(ipv6[4], ipv6[5], ipv6[6], ipv6[7]); wka.Equal(rfc7050WKA1) || wka.Equal(rfc7050WKA2) { // 32
prefEnd = 4
}
if prefEnd > 0 {
prefix := new(net.IPNet)
prefix.IP = append(ipv6[:prefEnd], net.IPv6zero[prefEnd:]...)
prefix.Mask = net.CIDRMask(prefEnd*8, 128)
if _, ok := uniqPrefixes[prefix.String()]; !ok {
prefixes = append(prefixes, prefix)
uniqPrefixes[prefix.String()] = struct{}{}
dlog.Infof("Registered DNS64 prefix [%s]", prefix.String())
}
}
}
}
}
if len(prefixes) == 0 {
return errors.New("Empty Pref64 list")
}
plugin.pref64Mutex.Lock()
defer plugin.pref64Mutex.Unlock()
plugin.pref64 = prefixes
return nil
}
func (plugin *PluginDNS64) refreshPref64() error {
for _, resolver := range plugin.dns64Resolvers {
if err := plugin.fetchPref64(resolver); err == nil {
break
}
}
plugin.pref64Mutex.RLock()
defer plugin.pref64Mutex.RUnlock()
if len(plugin.pref64) == 0 {
return errors.New("Empty Pref64 list")
}
return nil
}

View File

@ -0,0 +1,80 @@
package main
import (
"math/rand"
"net"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
type PluginECS struct {
nets []*net.IPNet
}
func (plugin *PluginECS) Name() string {
return "ecs"
}
func (plugin *PluginECS) Description() string {
return "Set EDNS-client-subnet information in outgoing queries."
}
func (plugin *PluginECS) Init(proxy *Proxy) error {
plugin.nets = proxy.ednsClientSubnets
dlog.Notice("ECS plugin enabled")
return nil
}
func (plugin *PluginECS) Drop() error {
return nil
}
func (plugin *PluginECS) Reload() error {
return nil
}
func (plugin *PluginECS) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
var options *[]dns.EDNS0
for _, extra := range msg.Extra {
if extra.Header().Rrtype == dns.TypeOPT {
options = &extra.(*dns.OPT).Option
for _, option := range *options {
if option.Option() == dns.EDNS0SUBNET {
return nil
}
}
break
}
}
if options == nil {
msg.SetEdns0(uint16(pluginsState.maxPayloadSize), false)
for _, extra := range msg.Extra {
if extra.Header().Rrtype == dns.TypeOPT {
options = &extra.(*dns.OPT).Option
break
}
}
}
if options == nil {
return nil
}
prr := dns.EDNS0_SUBNET{}
prr.Code = dns.EDNS0SUBNET
net := plugin.nets[rand.Intn(len(plugin.nets))]
bits, totalSize := net.Mask.Size()
if totalSize == 32 {
prr.Family = 1
} else if totalSize == 128 {
prr.Family = 2
} else {
return nil
}
prr.SourceNetmask = uint8(bits)
prr.SourceScope = 0
prr.Address = net.IP
*options = append(*options, &prr)
return nil
}

View File

@ -9,8 +9,7 @@ import (
"github.com/miekg/dns"
)
type PluginFirefox struct {
}
type PluginFirefox struct{}
func (plugin *PluginFirefox) Name() string {
return "firefox"
@ -34,16 +33,15 @@ func (plugin *PluginFirefox) Reload() error {
}
func (plugin *PluginFirefox) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) != 1 {
if pluginsState.clientProto == "local_doh" {
return nil
}
question := questions[0]
question := msg.Question[0]
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
return nil
}
qName := strings.ToLower(question.Name)
if qName != "use-application-dns.net." && !strings.HasSuffix(qName, ".use-application-dns.net.") {
qName := pluginsState.qName
if qName != "use-application-dns.net" && !strings.HasSuffix(qName, ".use-application-dns.net") {
return nil
}
synth := EmptyResponseFromMessage(msg)

View File

@ -5,7 +5,6 @@ import (
"math/rand"
"net"
"strings"
"unicode"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
@ -30,33 +29,44 @@ func (plugin *PluginForward) Description() string {
func (plugin *PluginForward) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of forwarding rules from [%s]", proxy.forwardFile)
bin, err := ReadTextFile(proxy.forwardFile)
lines, err := ReadTextFile(proxy.forwardFile)
if err != nil {
return err
}
for lineNo, line := range strings.Split(string(bin), "\n") {
line = strings.TrimFunc(line, unicode.IsSpace)
if len(line) == 0 || strings.HasPrefix(line, "#") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
}
domain, serversStr, ok := StringTwoFields(line)
if !ok {
return fmt.Errorf("Syntax error for a forwarding rule at line %d. Expected syntax: example.com: 9.9.9.9,8.8.8.8", 1+lineNo)
return fmt.Errorf(
"Syntax error for a forwarding rule at line %d. Expected syntax: example.com 9.9.9.9,8.8.8.8",
1+lineNo,
)
}
domain = strings.ToLower(domain)
var servers []string
for _, server := range strings.Split(serversStr, ",") {
server = strings.TrimFunc(server, unicode.IsSpace)
if net.ParseIP(server) != nil {
server = fmt.Sprintf("%s:%d", server, 53)
server = strings.TrimSpace(server)
server = strings.TrimPrefix(server, "[")
server = strings.TrimSuffix(server, "]")
if ip := net.ParseIP(server); ip != nil {
if ip.To4() != nil {
server = fmt.Sprintf("%s:%d", server, 53)
} else {
server = fmt.Sprintf("[%s]:%d", server, 53)
}
}
dlog.Infof("Forwarding [%s] to %s", domain, server)
servers = append(servers, server)
}
if len(servers) == 0 {
continue
}
plugin.forwardMap = append(plugin.forwardMap, PluginForwardEntry{
domain: domain, servers: servers,
domain: domain,
servers: servers,
})
}
return nil
@ -71,19 +81,17 @@ func (plugin *PluginForward) Reload() error {
}
func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) != 1 {
return nil
}
question := strings.ToLower(StripTrailingDot(questions[0].Name))
questionLen := len(question)
qName := pluginsState.qName
qNameLen := len(qName)
var servers []string
for _, candidate := range plugin.forwardMap {
candidateLen := len(candidate.domain)
if candidateLen > questionLen {
if candidateLen > qNameLen {
continue
}
if question[questionLen-candidateLen:] == candidate.domain && (candidateLen == questionLen || (question[questionLen-candidateLen-1] == '.')) {
if (qName[qNameLen-candidateLen:] == candidate.domain &&
(candidateLen == qNameLen || (qName[qNameLen-candidateLen-1] == '.'))) ||
(candidate.domain == ".") {
servers = candidate.servers
break
}
@ -93,11 +101,24 @@ func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
}
server := servers[rand.Intn(len(servers))]
pluginsState.serverName = server
respMsg, err := dns.Exchange(msg, server)
client := dns.Client{Net: pluginsState.serverProto, Timeout: pluginsState.timeout}
respMsg, _, err := client.Exchange(msg, server)
if err != nil {
return err
}
if respMsg.Truncated {
client.Net = "tcp"
respMsg, _, err = client.Exchange(msg, server)
if err != nil {
return err
}
}
if edns0 := respMsg.IsEdns0(); edns0 == nil || !edns0.Do() {
respMsg.AuthenticatedData = false
}
respMsg.Id = msg.Id
pluginsState.synthResponse = respMsg
pluginsState.action = PluginsActionSynth
pluginsState.returnCode = PluginsReturnCodeForward
return nil
}

View File

@ -30,12 +30,18 @@ func (plugin *PluginGetSetPayloadSize) Eval(pluginsState *PluginsState, msg *dns
dnssec := false
if edns0 != nil {
pluginsState.maxUnencryptedUDPSafePayloadSize = int(edns0.UDPSize())
pluginsState.originalMaxPayloadSize = Max(pluginsState.maxUnencryptedUDPSafePayloadSize-ResponseOverhead, pluginsState.originalMaxPayloadSize)
pluginsState.originalMaxPayloadSize = Max(
pluginsState.maxUnencryptedUDPSafePayloadSize-ResponseOverhead,
pluginsState.originalMaxPayloadSize,
)
dnssec = edns0.Do()
}
var options *[]dns.EDNS0
pluginsState.dnssec = dnssec
pluginsState.maxPayloadSize = Min(MaxDNSUDPPacketSize-ResponseOverhead, Max(pluginsState.originalMaxPayloadSize, pluginsState.maxPayloadSize))
pluginsState.maxPayloadSize = Min(
MaxDNSUDPPacketSize-ResponseOverhead,
Max(pluginsState.originalMaxPayloadSize, pluginsState.maxPayloadSize),
)
if pluginsState.maxPayloadSize > 512 {
extra2 := []dns.RR{}
for _, extra := range msg.Extra {

View File

@ -3,16 +3,16 @@ package main
import (
"errors"
"fmt"
"io"
"net"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
type PluginNxLog struct {
logger *lumberjack.Logger
logger io.Writer
format string
}
@ -25,7 +25,7 @@ func (plugin *PluginNxLog) Description() string {
}
func (plugin *PluginNxLog) Init(proxy *Proxy) error {
plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.nxLogFile, Compress: true}
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.nxLogFile)
plugin.format = proxy.nxLogFormat
return nil
@ -43,22 +43,22 @@ func (plugin *PluginNxLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error
if msg.Rcode != dns.RcodeNameError {
return nil
}
questions := msg.Question
if len(questions) == 0 {
var clientIPStr string
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return nil
}
question := questions[0]
question := msg.Question[0]
qType, ok := dns.TypeToString[question.Qtype]
if !ok {
qType = string(qType)
}
var clientIPStr string
if pluginsState.clientProto == "udp" {
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
} else {
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
}
qName := StripTrailingDot(question.Name)
qName := pluginsState.qName
var line string
if plugin.format == "tsv" {
@ -76,7 +76,7 @@ func (plugin *PluginNxLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
plugin.logger.Write([]byte(line))
_, _ = plugin.logger.Write([]byte(line))
return nil
}

View File

@ -3,17 +3,17 @@ package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
type PluginQueryLog struct {
logger *lumberjack.Logger
logger io.Writer
format string
ignoredQtypes []string
}
@ -27,7 +27,7 @@ func (plugin *PluginQueryLog) Description() string {
}
func (plugin *PluginQueryLog) Init(proxy *Proxy) error {
plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.queryLogFile, Compress: true}
plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.queryLogFile)
plugin.format = proxy.queryLogFormat
plugin.ignoredQtypes = proxy.queryLogIgnoredQtypes
@ -43,11 +43,17 @@ func (plugin *PluginQueryLog) Reload() error {
}
func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) == 0 {
var clientIPStr string
switch pluginsState.clientProto {
case "udp":
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
case "tcp", "local_doh":
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
default:
// Ignore internal flow.
return nil
}
question := questions[0]
question := msg.Question[0]
qType, ok := dns.TypeToString[question.Qtype]
if !ok {
qType = string(qType)
@ -59,13 +65,7 @@ func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) err
}
}
}
var clientIPStr string
if pluginsState.clientProto == "udp" {
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
} else {
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
}
qName := StripTrailingDot(question.Name)
qName := pluginsState.qName
if pluginsState.cacheHit {
pluginsState.serverName = "-"
@ -90,8 +90,16 @@ func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) err
year, month, day := now.Date()
hour, minute, second := now.Clock()
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
line = fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%dms\t%s\n", tsStr, clientIPStr, StringQuote(qName), qType, returnCode, requestDuration/time.Millisecond,
StringQuote(pluginsState.serverName))
line = fmt.Sprintf(
"%s\t%s\t%s\t%s\t%s\t%dms\t%s\n",
tsStr,
clientIPStr,
StringQuote(qName),
qType,
returnCode,
requestDuration/time.Millisecond,
StringQuote(pluginsState.serverName),
)
} else if plugin.format == "ltsv" {
cached := 0
if pluginsState.cacheHit {
@ -105,6 +113,7 @@ func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) err
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
plugin.logger.Write([]byte(line))
_, _ = plugin.logger.Write([]byte(line))
return nil
}

View File

@ -18,8 +18,10 @@ func (plugin *PluginQueryMeta) Description() string {
func (plugin *PluginQueryMeta) Init(proxy *Proxy) error {
queryMetaRR := new(dns.TXT)
queryMetaRR.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeTXT,
Class: dns.ClassINET, Ttl: 86400}
queryMetaRR.Hdr = dns.RR_Header{
Name: ".", Rrtype: dns.TypeTXT,
Class: dns.ClassINET, Ttl: 86400,
}
queryMetaRR.Txt = proxy.queryMeta
plugin.queryMetaRR = queryMetaRR
return nil
@ -34,10 +36,6 @@ func (plugin *PluginQueryMeta) Reload() error {
}
func (plugin *PluginQueryMeta) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) == 0 {
return nil
}
msg.Extra = []dns.RR{plugin.queryMetaRR}
return nil
}

View File

@ -1,131 +0,0 @@
package main
import (
"errors"
"fmt"
"net"
"strings"
"time"
"unicode"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
type PluginWhitelistName struct {
allWeeklyRanges *map[string]WeeklyRanges
patternMatcher *PatternMatcher
logger *lumberjack.Logger
format string
}
func (plugin *PluginWhitelistName) Name() string {
return "whitelist_name"
}
func (plugin *PluginWhitelistName) Description() string {
return "Whitelists DNS queries matching name patterns"
}
func (plugin *PluginWhitelistName) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of whitelisting rules from [%s]", proxy.whitelistNameFile)
bin, err := ReadTextFile(proxy.whitelistNameFile)
if err != nil {
return err
}
plugin.allWeeklyRanges = proxy.allWeeklyRanges
plugin.patternMatcher = NewPatternPatcher()
for lineNo, line := range strings.Split(string(bin), "\n") {
line = strings.TrimFunc(line, unicode.IsSpace)
if len(line) == 0 || strings.HasPrefix(line, "#") {
continue
}
parts := strings.Split(line, "@")
timeRangeName := ""
if len(parts) == 2 {
line = strings.TrimFunc(parts[0], unicode.IsSpace)
timeRangeName = strings.TrimFunc(parts[1], unicode.IsSpace)
} else if len(parts) > 2 {
dlog.Errorf("Syntax error in whitelist rules at line %d -- Unexpected @ character", 1+lineNo)
continue
}
var weeklyRanges *WeeklyRanges
if len(timeRangeName) > 0 {
weeklyRangesX, ok := (*plugin.allWeeklyRanges)[timeRangeName]
if !ok {
dlog.Errorf("Time range [%s] not found at line %d", timeRangeName, 1+lineNo)
} else {
weeklyRanges = &weeklyRangesX
}
}
if err := plugin.patternMatcher.Add(line, weeklyRanges, lineNo+1); err != nil {
dlog.Error(err)
continue
}
}
if len(proxy.whitelistNameLogFile) == 0 {
return nil
}
plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.whitelistNameLogFile, Compress: true}
plugin.format = proxy.whitelistNameFormat
return nil
}
func (plugin *PluginWhitelistName) Drop() error {
return nil
}
func (plugin *PluginWhitelistName) Reload() error {
return nil
}
func (plugin *PluginWhitelistName) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
questions := msg.Question
if len(questions) != 1 {
return nil
}
qName := strings.ToLower(StripTrailingDot(questions[0].Name))
whitelist, reason, xweeklyRanges := plugin.patternMatcher.Eval(qName)
var weeklyRanges *WeeklyRanges
if xweeklyRanges != nil {
weeklyRanges = xweeklyRanges.(*WeeklyRanges)
}
if whitelist {
if weeklyRanges != nil && !weeklyRanges.Match() {
whitelist = false
}
}
if whitelist {
if pluginsState.sessionData == nil {
pluginsState.sessionData = make(map[string]interface{})
}
pluginsState.sessionData["whitelisted"] = true
if plugin.logger != nil {
var clientIPStr string
if pluginsState.clientProto == "udp" {
clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String()
} else {
clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String()
}
var line string
if plugin.format == "tsv" {
now := time.Now()
year, month, day := now.Date()
hour, minute, second := now.Clock()
tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second)
line = fmt.Sprintf("%s\t%s\t%s\t%s\n", tsStr, clientIPStr, StringQuote(qName), StringQuote(reason))
} else if plugin.format == "ltsv" {
line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(reason))
} else {
dlog.Fatalf("Unexpected log format: [%s]", plugin.format)
}
if plugin.logger == nil {
return errors.New("Log file not initialized")
}
plugin.logger.Write([]byte(line))
}
}
return nil
}

View File

@ -14,11 +14,11 @@ import (
type PluginsAction int
const (
PluginsActionNone = 0
PluginsActionForward = 1
PluginsActionDrop = 2
PluginsActionReject = 3
PluginsActionSynth = 4
PluginsActionNone = 0
PluginsActionContinue = 1
PluginsActionDrop = 2
PluginsActionReject = 3
PluginsActionSynth = 4
)
type PluginsGlobals struct {
@ -46,6 +46,7 @@ const (
PluginsReturnCodeNetworkError
PluginsReturnCodeCloak
PluginsReturnCodeServerTimeout
PluginsReturnCodeNotReady
)
var PluginsReturnCodeToString = map[PluginsReturnCode]string{
@ -61,44 +62,54 @@ var PluginsReturnCodeToString = map[PluginsReturnCode]string{
PluginsReturnCodeNetworkError: "NETWORK_ERROR",
PluginsReturnCodeCloak: "CLOAK",
PluginsReturnCodeServerTimeout: "SERVER_TIMEOUT",
PluginsReturnCodeNotReady: "NOT_READY",
}
type PluginsState struct {
sessionData map[string]interface{}
action PluginsAction
maxUnencryptedUDPSafePayloadSize int
originalMaxPayloadSize int
maxPayloadSize int
clientProto string
clientAddr *net.Addr
synthResponse *dns.Msg
dnssec bool
cacheSize int
cacheNegMinTTL uint32
cacheNegMaxTTL uint32
cacheMinTTL uint32
cacheMaxTTL uint32
rejectTTL uint32
questionMsg *dns.Msg
requestStart time.Time
requestEnd time.Time
cacheHit bool
returnCode PluginsReturnCode
clientProto string
serverName string
serverProto string
qName string
clientAddr *net.Addr
synthResponse *dns.Msg
questionMsg *dns.Msg
sessionData map[string]interface{}
action PluginsAction
timeout time.Duration
returnCode PluginsReturnCode
maxPayloadSize int
cacheSize int
originalMaxPayloadSize int
maxUnencryptedUDPSafePayloadSize int
rejectTTL uint32
cacheMaxTTL uint32
cacheNegMaxTTL uint32
cacheNegMinTTL uint32
cacheMinTTL uint32
cacheHit bool
dnssec bool
}
func (proxy *Proxy) InitPluginsGlobals() error {
queryPlugins := &[]Plugin{}
if proxy.captivePortalMap != nil {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginCaptivePortal)))
}
if len(proxy.queryMeta) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginQueryMeta)))
}
if len(proxy.whitelistNameFile) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginWhitelistName)))
if len(proxy.allowNameFile) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginAllowName)))
}
*queryPlugins = append(*queryPlugins, Plugin(new(PluginFirefox)))
if len(proxy.ednsClientSubnets) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginECS)))
}
if len(proxy.blockNameFile) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockName)))
}
@ -115,14 +126,29 @@ func (proxy *Proxy) InitPluginsGlobals() error {
if len(proxy.forwardFile) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginForward)))
}
if proxy.pluginBlockUnqualified {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockUnqualified)))
}
if proxy.pluginBlockUndelegated {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockUndelegated)))
}
responsePlugins := &[]Plugin{}
if len(proxy.nxLogFile) != 0 {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginNxLog)))
}
if len(proxy.allowedIPFile) != 0 {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginAllowedIP)))
}
if len(proxy.blockNameFile) != 0 {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginBlockNameResponse)))
}
if len(proxy.blockIPFile) != 0 {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginBlockIP)))
}
if len(proxy.dns64Resolvers) != 0 || len(proxy.dns64Prefixes) != 0 {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginDNS64)))
}
if proxy.cache {
*responsePlugins = append(*responsePlugins, Plugin(new(PluginCacheResponse)))
}
@ -163,11 +189,11 @@ func parseBlockedQueryResponse(blockedResponse string, pluginsGlobals *PluginsGl
if strings.HasPrefix(blockedResponse, "a:") {
blockedIPStrings := strings.Split(blockedResponse, ",")
(*pluginsGlobals).respondWithIPv4 = net.ParseIP(strings.TrimPrefix(blockedIPStrings[0], "a:"))
pluginsGlobals.respondWithIPv4 = net.ParseIP(strings.TrimPrefix(blockedIPStrings[0], "a:"))
if (*pluginsGlobals).respondWithIPv4 == nil {
if pluginsGlobals.respondWithIPv4 == nil {
dlog.Notice("Error parsing IPv4 response given in blocked_query_response option, defaulting to `hinfo`")
(*pluginsGlobals).refusedCodeInResponses = false
pluginsGlobals.refusedCodeInResponses = false
return
}
@ -177,29 +203,30 @@ func parseBlockedQueryResponse(blockedResponse string, pluginsGlobals *PluginsGl
if strings.HasPrefix(ipv6Response, "[") {
ipv6Response = strings.Trim(ipv6Response, "[]")
}
(*pluginsGlobals).respondWithIPv6 = net.ParseIP(ipv6Response)
pluginsGlobals.respondWithIPv6 = net.ParseIP(ipv6Response)
if (*pluginsGlobals).respondWithIPv6 == nil {
dlog.Notice("Error parsing IPv6 response given in blocked_query_response option, defaulting to IPv4")
if pluginsGlobals.respondWithIPv6 == nil {
dlog.Notice(
"Error parsing IPv6 response given in blocked_query_response option, defaulting to IPv4",
)
}
} else {
dlog.Noticef("Invalid IPv6 response given in blocked_query_response option [%s], the option should take the form 'a:<IPv4>,aaaa:<IPv6>'", blockedIPStrings[1])
}
}
if (*pluginsGlobals).respondWithIPv6 == nil {
(*pluginsGlobals).respondWithIPv6 = (*pluginsGlobals).respondWithIPv4
if pluginsGlobals.respondWithIPv6 == nil {
pluginsGlobals.respondWithIPv6 = pluginsGlobals.respondWithIPv4
}
} else {
switch blockedResponse {
case "refused":
(*pluginsGlobals).refusedCodeInResponses = true
pluginsGlobals.refusedCodeInResponses = true
case "hinfo":
(*pluginsGlobals).refusedCodeInResponses = false
pluginsGlobals.refusedCodeInResponses = false
default:
dlog.Noticef("Invalid blocked_query_response option [%s], defaulting to `hinfo`", blockedResponse)
(*pluginsGlobals).refusedCodeInResponses = false
pluginsGlobals.refusedCodeInResponses = false
}
}
}
@ -213,9 +240,16 @@ type Plugin interface {
Eval(pluginsState *PluginsState, msg *dns.Msg) error
}
func NewPluginsState(proxy *Proxy, clientProto string, clientAddr *net.Addr, start time.Time) PluginsState {
func NewPluginsState(
proxy *Proxy,
clientProto string,
clientAddr *net.Addr,
serverProto string,
start time.Time,
) PluginsState {
return PluginsState{
action: PluginsActionForward,
action: PluginsActionContinue,
returnCode: PluginsReturnCodePass,
maxPayloadSize: MaxDNSUDPPacketSize - ResponseOverhead,
clientProto: clientProto,
clientAddr: clientAddr,
@ -226,25 +260,38 @@ func NewPluginsState(proxy *Proxy, clientProto string, clientAddr *net.Addr, sta
cacheMaxTTL: proxy.cacheMaxTTL,
rejectTTL: proxy.rejectTTL,
questionMsg: nil,
qName: "",
serverName: "-",
serverProto: serverProto,
timeout: proxy.timeout,
requestStart: start,
maxUnencryptedUDPSafePayloadSize: MaxDNSUDPSafePacketSize,
sessionData: make(map[string]interface{}),
}
}
func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGlobals, packet []byte, serverName string) ([]byte, error) {
if len(*pluginsGlobals.queryPlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 {
return packet, nil
}
pluginsState.serverName = serverName
pluginsState.action = PluginsActionForward
func (pluginsState *PluginsState) ApplyQueryPlugins(
pluginsGlobals *PluginsGlobals,
packet []byte,
needsEDNS0Padding bool,
) ([]byte, error) {
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return packet, err
}
if len(msg.Question) > 1 {
if len(msg.Question) != 1 {
return packet, errors.New("Unexpected number of questions")
}
qName, err := NormalizeQName(msg.Question[0].Name)
if err != nil {
return packet, err
}
dlog.Debugf("Handling query for [%v]", qName)
pluginsState.qName = qName
pluginsState.questionMsg = &msg
if len(*pluginsGlobals.queryPlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 {
return packet, nil
}
pluginsGlobals.RLock()
defer pluginsGlobals.RUnlock()
for _, plugin := range *pluginsGlobals.queryPlugins {
@ -253,25 +300,38 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGloba
return packet, err
}
if pluginsState.action == PluginsActionReject {
synth := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.rejectTTL)
synth := RefusedResponseFromMessage(
&msg,
pluginsGlobals.refusedCodeInResponses,
pluginsGlobals.respondWithIPv4,
pluginsGlobals.respondWithIPv6,
pluginsState.rejectTTL,
)
pluginsState.synthResponse = synth
}
if pluginsState.action != PluginsActionForward {
if pluginsState.action != PluginsActionContinue {
break
}
}
packet2, err := msg.PackBuffer(packet)
if err != nil {
return packet, err
}
if needsEDNS0Padding && pluginsState.action == PluginsActionContinue {
padLen := 63 - ((len(packet2) + 63) & 63)
if paddedPacket2, _ := addEDNS0PaddingIfNoneFound(&msg, packet2, padLen); paddedPacket2 != nil {
return paddedPacket2, nil
}
}
return packet2, nil
}
func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGlobals, packet []byte, ttl *uint32) ([]byte, error) {
if len(*pluginsGlobals.responsePlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 {
return packet, nil
}
pluginsState.action = PluginsActionForward
func (pluginsState *PluginsState) ApplyResponsePlugins(
pluginsGlobals *PluginsGlobals,
packet []byte,
ttl *uint32,
) ([]byte, error) {
msg := dns.Msg{Compress: true}
if err := msg.Unpack(packet); err != nil {
if len(packet) >= MinDNSPacketSize && HasTCFlag(packet) {
@ -289,6 +349,7 @@ func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGl
default:
pluginsState.returnCode = PluginsReturnCodeResponseError
}
removeEDNS0Options(&msg)
pluginsGlobals.RLock()
defer pluginsGlobals.RUnlock()
for _, plugin := range *pluginsGlobals.responsePlugins {
@ -297,11 +358,16 @@ func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGl
return packet, err
}
if pluginsState.action == PluginsActionReject {
synth := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.rejectTTL)
dlog.Infof("Blocking [%s]", synth.Question[0].Name)
synth := RefusedResponseFromMessage(
&msg,
pluginsGlobals.refusedCodeInResponses,
pluginsGlobals.respondWithIPv4,
pluginsGlobals.respondWithIPv6,
pluginsState.rejectTTL,
)
pluginsState.synthResponse = synth
}
if pluginsState.action != PluginsActionForward {
if pluginsState.action != PluginsActionContinue {
break
}
}
@ -321,8 +387,8 @@ func (pluginsState *PluginsState) ApplyLoggingPlugins(pluginsGlobals *PluginsGlo
}
pluginsState.requestEnd = time.Now()
questionMsg := pluginsState.questionMsg
if questionMsg == nil || len(questionMsg.Question) > 1 {
return errors.New("Unexpected number of questions")
if questionMsg == nil {
return errors.New("Question not found")
}
pluginsGlobals.RLock()
defer pluginsGlobals.RUnlock()

View File

@ -15,8 +15,7 @@ import (
)
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
currentUser, err := user.Current()
if err != nil && currentUser.Uid != "0" {
if os.Geteuid() != 0 {
dlog.Fatal("Root privileges are required in order to switch to a different user. Maybe try again with 'sudo'")
}
userInfo, err := user.Lookup(userStr)
@ -25,9 +24,19 @@ func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
if err != nil {
uid, err2 := strconv.Atoi(userStr)
if err2 != nil || uid <= 0 {
dlog.Fatalf("Unable to retrieve any information about user [%s]: [%s] - Remove the user_name directive from the configuration file in order to avoid identity switch", userStr, err)
dlog.Fatalf(
"Unable to retrieve any information about user [%s]: [%s] - Remove the user_name directive from the configuration file in order to avoid identity switch",
userStr,
err,
)
}
dlog.Warnf("Unable to retrieve any information about user [%s]: [%s] - Switching to user id [%v] with the same group id, as [%v] looks like a user id. But you should remove or fix the user_name directive in the configuration file if possible", userStr, err, uid, uid)
dlog.Warnf(
"Unable to retrieve any information about user [%s]: [%s] - Switching to user id [%v] with the same group id, as [%v] looks like a user id. But you should remove or fix the user_name directive in the configuration file if possible",
userStr,
err,
uid,
uid,
)
userInfo = &user.User{Uid: userStr, Gid: userStr}
}
uid, err := strconv.Atoi(userInfo.Uid)
@ -65,26 +74,17 @@ func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
if _, _, rcode := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0); rcode != 0 {
dlog.Fatalf("Unable to drop user privileges: [%s]", rcode.Error())
}
maxfd := uintptr(0)
for _, fd := range fds {
if fd.Fd() > maxfd {
maxfd = fd.Fd()
}
}
fdbase := maxfd + 1
for i, fd := range fds {
if err := unix.Dup2(int(fd.Fd()), int(fdbase+uintptr(i))); err != nil {
if fd.Fd() >= InheritedDescriptorsBase {
dlog.Fatal("Duplicated file descriptors are above base")
}
if err := unix.Dup2(int(fd.Fd()), int(InheritedDescriptorsBase+uintptr(i))); err != nil {
dlog.Fatalf("Unable to clone file descriptor: [%s]", err)
}
if _, err := unix.FcntlInt(fd.Fd(), unix.F_SETFD, unix.FD_CLOEXEC); err != nil {
dlog.Fatalf("Unable to set the close on exec flag: [%s]", err)
}
}
for i := range fds {
if err := unix.Dup2(int(fdbase+uintptr(i)), int(i)+3); err != nil {
dlog.Fatalf("Unable to reassign descriptor: [%s]", err)
}
}
err = unix.Exec(path, args, os.Environ())
dlog.Fatalf("Unable to reexecute [%s]: [%s]", path, err)
os.Exit(1)

View File

@ -1,3 +1,4 @@
//go:build !windows && !linux
// +build !windows,!linux
package main
@ -16,8 +17,7 @@ import (
)
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
currentUser, err := user.Current()
if err != nil && currentUser.Uid != "0" {
if os.Geteuid() != 0 {
dlog.Fatal("Root privileges are required in order to switch to a different user. Maybe try again with 'sudo'")
}
userInfo, err := user.Lookup(userStr)
@ -26,9 +26,19 @@ func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
if err != nil {
uid, err2 := strconv.Atoi(userStr)
if err2 != nil || uid <= 0 {
dlog.Fatalf("Unable to retrieve any information about user [%s]: [%s] - Remove the user_name directive from the configuration file in order to avoid identity switch", userStr, err)
dlog.Fatalf(
"Unable to retrieve any information about user [%s]: [%s] - Remove the user_name directive from the configuration file in order to avoid identity switch",
userStr,
err,
)
}
dlog.Warnf("Unable to retrieve any information about user [%s]: [%s] - Switching to user id [%v] with the same group id, as [%v] looks like a user id. But you should remove or fix the user_name directive in the configuration file if possible", userStr, err, uid, uid)
dlog.Warnf(
"Unable to retrieve any information about user [%s]: [%s] - Switching to user id [%v] with the same group id, as [%v] looks like a user id. But you should remove or fix the user_name directive in the configuration file if possible",
userStr,
err,
uid,
uid,
)
userInfo = &user.User{Uid: userStr, Gid: userStr}
}
uid, err := strconv.Atoi(userInfo.Uid)
@ -66,26 +76,17 @@ func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
if err := unix.Setuid(uid); err != nil {
dlog.Fatalf("Unable to drop user privileges: %s", err)
}
maxfd := uintptr(0)
for _, fd := range fds {
if fd.Fd() > maxfd {
maxfd = fd.Fd()
}
}
fdbase := maxfd + 1
for i, fd := range fds {
if err := unix.Dup2(int(fd.Fd()), int(fdbase+uintptr(i))); err != nil {
if fd.Fd() >= InheritedDescriptorsBase {
dlog.Fatal("Duplicated file descriptors are above base")
}
if err := unix.Dup2(int(fd.Fd()), int(InheritedDescriptorsBase+uintptr(i))); err != nil {
dlog.Fatalf("Unable to clone file descriptor: [%s]", err)
}
if _, err := unix.FcntlInt(fd.Fd(), unix.F_SETFD, unix.FD_CLOEXEC); err != nil {
dlog.Fatalf("Unable to set the close on exec flag: [%s]", err)
}
}
for i := range fds {
if err := unix.Dup2(int(fdbase+uintptr(i)), int(i)+3); err != nil {
dlog.Fatalf("Unable to reassign descriptor: [%s]", err)
}
}
err = unix.Exec(path, args, os.Environ())
dlog.Fatalf("Unable to reexecute [%s]: [%s]", path, err)
os.Exit(1)

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,384 @@
package main
import (
"errors"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/miekg/dns"
)
const myResolverHost string = "resolver.dnscrypt.info"
const (
myResolverHost string = "resolver.dnscrypt.info."
nonexistentName string = "nonexistent-zone.dnscrypt-test."
)
func Resolve(name string) {
fmt.Printf("Resolving [%s]\n\n", name)
fmt.Printf("Domain exists: ")
ns, err := net.LookupNS(name)
if err != nil || len(ns) == 0 {
if name == "." {
fmt.Println("'No' would mean that the Internet doesn't exist any more, and that would be very sad. On the bright side, you just found an easter egg.")
} else {
fmt.Println("probably not, or blocked by the proxy")
}
} else {
fmt.Printf("yes, %d name servers found\n", len(ns))
func resolveQuery(server string, qName string, qType uint16, sendClientSubnet bool) (*dns.Msg, error) {
client := new(dns.Client)
client.ReadTimeout = 2 * time.Second
msg := &dns.Msg{
MsgHdr: dns.MsgHdr{
RecursionDesired: true,
Opcode: dns.OpcodeQuery,
},
Question: make([]dns.Question, 1),
}
options := &dns.OPT{
Hdr: dns.RR_Header{
Name: ".",
Rrtype: dns.TypeOPT,
},
}
fmt.Printf("Canonical name: ")
cname, err := net.LookupCNAME(name)
if err != nil {
fmt.Println("-")
} else {
if sendClientSubnet {
subnet := net.IPNet{IP: net.IPv4(93, 184, 216, 0), Mask: net.CIDRMask(24, 32)}
prr := dns.EDNS0_SUBNET{}
prr.Code = dns.EDNS0SUBNET
bits, totalSize := subnet.Mask.Size()
if totalSize == 32 {
prr.Family = 1
} else if totalSize == 128 { // if we want to test with IPv6
prr.Family = 2
}
prr.SourceNetmask = uint8(bits)
prr.SourceScope = 0
prr.Address = subnet.IP
options.Option = append(options.Option, &prr)
}
msg.Extra = append(msg.Extra, options)
options.SetDo()
options.SetUDPSize(uint16(MaxDNSPacketSize))
msg.Question[0] = dns.Question{Name: qName, Qtype: qType, Qclass: dns.ClassINET}
msg.Id = dns.Id()
for i := 0; i < 3; i++ {
response, rtt, err := client.Exchange(msg, server)
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
client.ReadTimeout *= 2
continue
}
_ = rtt
if err != nil {
return nil, err
}
return response, nil
}
return nil, errors.New("Timeout")
}
func Resolve(server string, name string, singleResolver bool) {
parts := strings.SplitN(name, ",", 2)
if len(parts) == 2 {
name, server = parts[0], parts[1]
singleResolver = true
}
host, port := ExtractHostAndPort(server, 53)
if host == "0.0.0.0" {
host = "127.0.0.1"
} else if host == "[::]" {
host = "[::1]"
}
server = fmt.Sprintf("%s:%d", host, port)
fmt.Printf("Resolving [%s] using %s port %d\n\n", name, host, port)
name = dns.Fqdn(name)
cname := name
var clientSubnet string
for once := true; once; once = false {
response, err := resolveQuery(server, myResolverHost, dns.TypeTXT, true)
if err != nil {
fmt.Printf("Unable to resolve: [%s]\n", err)
os.Exit(1)
}
fmt.Printf("Resolver : ")
res := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Class != dns.ClassINET || answer.Header().Rrtype != dns.TypeTXT {
continue
}
var ip string
for _, txt := range answer.(*dns.TXT).Txt {
if strings.HasPrefix(txt, "Resolver IP: ") {
ip = strings.TrimPrefix(txt, "Resolver IP: ")
} else if strings.HasPrefix(txt, "EDNS0 client subnet: ") {
clientSubnet = strings.TrimPrefix(txt, "EDNS0 client subnet: ")
}
}
if ip == "" {
continue
}
if rev, err := dns.ReverseAddr(ip); err == nil {
response, err = resolveQuery(server, rev, dns.TypePTR, false)
if err != nil {
break
}
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypePTR || answer.Header().Class != dns.ClassINET {
continue
}
ip = ip + " (" + answer.(*dns.PTR).Ptr + ")"
break
}
}
res = append(res, ip)
}
if len(res) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(res, ", "))
}
}
if singleResolver {
for once := true; once; once = false {
fmt.Printf("Lying : ")
response, err := resolveQuery(server, nonexistentName, dns.TypeA, false)
if err != nil {
fmt.Printf("[%v]", err)
break
}
if response.Rcode == dns.RcodeSuccess {
fmt.Println("yes. That resolver returns wrong responses")
} else if response.Rcode == dns.RcodeNameError {
fmt.Println("no")
} else {
fmt.Printf("unknown - query returned %s\n", dns.RcodeToString[response.Rcode])
}
if response.Rcode == dns.RcodeNameError {
fmt.Printf("DNSSEC : ")
if response.AuthenticatedData {
fmt.Println("yes, the resolver supports DNSSEC")
} else {
fmt.Println("no, the resolver doesn't support DNSSEC")
}
}
fmt.Printf("ECS : ")
if clientSubnet != "" {
fmt.Println("client network address is sent to authoritative servers")
} else {
fmt.Println("ignored or selective")
}
}
}
fmt.Println("")
cname:
for once := true; once; once = false {
fmt.Printf("Canonical name: ")
for i := 0; i < 100; i++ {
response, err := resolveQuery(server, cname, dns.TypeCNAME, false)
if err != nil {
break cname
}
found := false
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeCNAME || answer.Header().Class != dns.ClassINET {
continue
}
cname = answer.(*dns.CNAME).Target
found = true
break
}
if !found {
break
}
}
fmt.Println(cname)
}
fmt.Printf("IP addresses: ")
addrs, err := net.LookupHost(name)
if err != nil {
fmt.Println("-")
} else {
fmt.Println(strings.Join(addrs, ", "))
}
fmt.Println("")
fmt.Printf("TXT records: ")
txt, err := net.LookupTXT(name)
if err != nil {
fmt.Println("-")
} else {
fmt.Println(strings.Join(txt, " "))
}
resIP, err := net.LookupHost(myResolverHost)
if err == nil && len(resIP) > 0 {
fmt.Printf("Resolver IP: %s", resIP[0])
rev, err := net.LookupAddr(resIP[0])
if err == nil && len(rev) > 0 {
fmt.Printf(" (%s)", rev[0])
for once := true; once; once = false {
fmt.Printf("IPv4 addresses: ")
response, err := resolveQuery(server, cname, dns.TypeA, false)
if err != nil {
break
}
ipv4 := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeA || answer.Header().Class != dns.ClassINET {
continue
}
ipv4 = append(ipv4, answer.(*dns.A).A.String())
}
if len(ipv4) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(ipv4, ", "))
}
fmt.Println("")
}
for once := true; once; once = false {
fmt.Printf("IPv6 addresses: ")
response, err := resolveQuery(server, cname, dns.TypeAAAA, false)
if err != nil {
break
}
ipv6 := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeAAAA || answer.Header().Class != dns.ClassINET {
continue
}
ipv6 = append(ipv6, answer.(*dns.AAAA).AAAA.String())
}
if len(ipv6) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(ipv6, ", "))
}
}
fmt.Println("")
for once := true; once; once = false {
fmt.Printf("Name servers : ")
response, err := resolveQuery(server, cname, dns.TypeNS, false)
if err != nil {
break
}
nss := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeNS || answer.Header().Class != dns.ClassINET {
continue
}
nss = append(nss, answer.(*dns.NS).Ns)
}
if response.Rcode == dns.RcodeNameError {
fmt.Println("name does not exist")
} else if response.Rcode != dns.RcodeSuccess {
fmt.Printf("server returned %s", dns.RcodeToString[response.Rcode])
} else if len(nss) == 0 {
fmt.Println("no name servers found")
} else {
fmt.Println(strings.Join(nss, ", "))
}
fmt.Printf("DNSSEC signed : ")
if response.AuthenticatedData {
fmt.Println("yes")
} else {
fmt.Println("no")
}
}
for once := true; once; once = false {
fmt.Printf("Mail servers : ")
response, err := resolveQuery(server, cname, dns.TypeMX, false)
if err != nil {
break
}
mxs := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeMX || answer.Header().Class != dns.ClassINET {
continue
}
mxs = append(mxs, answer.(*dns.MX).Mx)
}
if len(mxs) == 0 {
fmt.Println("no mail servers found")
} else if len(mxs) > 1 {
fmt.Printf("%d mail servers found\n", len(mxs))
} else {
fmt.Println("1 mail servers found")
}
}
fmt.Println("")
for once := true; once; once = false {
fmt.Printf("HTTPS alias : ")
response, err := resolveQuery(server, cname, dns.TypeHTTPS, false)
if err != nil {
break
}
aliases := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeHTTPS || answer.Header().Class != dns.ClassINET {
continue
}
https := answer.(*dns.HTTPS)
if https.Priority != 0 || len(https.Target) < 2 {
continue
}
aliases = append(aliases, https.Target)
}
if len(aliases) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(aliases, ", "))
}
fmt.Printf("HTTPS info : ")
info := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeHTTPS || answer.Header().Class != dns.ClassINET {
continue
}
https := answer.(*dns.HTTPS)
if https.Priority == 0 || len(https.Target) > 1 {
continue
}
for _, value := range https.Value {
info = append(info, fmt.Sprintf("[%s]=[%s]", value.Key(), value.String()))
}
}
if len(info) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(info, ", "))
}
}
fmt.Println("")
for once := true; once; once = false {
fmt.Printf("Host info : ")
response, err := resolveQuery(server, cname, dns.TypeHINFO, false)
if err != nil {
break
}
hinfo := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeHINFO || answer.Header().Class != dns.ClassINET {
continue
}
hinfo = append(hinfo, fmt.Sprintf("%s %s", answer.(*dns.HINFO).Cpu, answer.(*dns.HINFO).Os))
}
if len(hinfo) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(hinfo, ", "))
}
}
for once := true; once; once = false {
fmt.Printf("TXT records : ")
response, err := resolveQuery(server, cname, dns.TypeTXT, false)
if err != nil {
break
}
txt := make([]string, 0)
for _, answer := range response.Answer {
if answer.Header().Rrtype != dns.TypeTXT || answer.Header().Class != dns.ClassINET {
continue
}
txt = append(txt, strings.Join(answer.(*dns.TXT).Txt, " "))
}
if len(txt) == 0 {
fmt.Println("-")
} else {
fmt.Println(strings.Join(txt, ", "))
}
}
fmt.Println("")
}

View File

@ -1,12 +1,12 @@
package main
import (
crypto_rand "crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"math/bits"
"math/rand"
"net"
"net/url"
@ -17,7 +17,9 @@ import (
"github.com/VividCortex/ewma"
"github.com/jedisct1/dlog"
clocksmith "github.com/jedisct1/go-clocksmith"
stamps "github.com/jedisct1/go-dnsstamps"
"github.com/miekg/dns"
"golang.org/x/crypto/ed25519"
)
@ -32,52 +34,125 @@ type RegisteredServer struct {
}
type ServerBugs struct {
incorrectPadding bool
fragmentsBlocked bool
}
type DOHClientCreds struct {
clientCert string
clientKey string
rootCA string
}
type ServerInfo struct {
Proto stamps.StampProtoType
MagicQuery [8]byte
ServerPk [32]byte
SharedKey [32]byte
CryptoConstruction CryptoConstruction
DOHClientCreds DOHClientCreds
lastActionTS time.Time
rtt ewma.MovingAverage
Name string
Timeout time.Duration
URL *url.URL
HostName string
UDPAddr *net.UDPAddr
TCPAddr *net.TCPAddr
RelayUDPAddr *net.UDPAddr
RelayTCPAddr *net.TCPAddr
knownBugs ServerBugs
lastActionTS time.Time
rtt ewma.MovingAverage
Relay *Relay
URL *url.URL
initialRtt int
Timeout time.Duration
CryptoConstruction CryptoConstruction
ServerPk [32]byte
SharedKey [32]byte
MagicQuery [8]byte
knownBugs ServerBugs
Proto stamps.StampProtoType
useGet bool
odohTargetConfigs []ODoHTargetConfig
}
type LBStrategy int
type LBStrategy interface {
getCandidate(serversCount int) int
getActiveCount(serversCount int) int
}
const (
LBStrategyNone = LBStrategy(iota)
LBStrategyP2
LBStrategyPH
LBStrategyFirst
LBStrategyRandom
)
type LBStrategyP2 struct{}
const DefaultLBStrategy = LBStrategyP2
func (LBStrategyP2) getCandidate(serversCount int) int {
return rand.Intn(Min(serversCount, 2))
}
func (LBStrategyP2) getActiveCount(serversCount int) int {
return Min(serversCount, 2)
}
type LBStrategyPN struct{ n int }
func (s LBStrategyPN) getCandidate(serversCount int) int {
return rand.Intn(Min(serversCount, s.n))
}
func (s LBStrategyPN) getActiveCount(serversCount int) int {
return Min(serversCount, s.n)
}
type LBStrategyPH struct{}
func (LBStrategyPH) getCandidate(serversCount int) int {
return rand.Intn(Max(Min(serversCount, 2), serversCount/2))
}
func (LBStrategyPH) getActiveCount(serversCount int) int {
return Max(Min(serversCount, 2), serversCount/2)
}
type LBStrategyFirst struct{}
func (LBStrategyFirst) getCandidate(int) int {
return 0
}
func (LBStrategyFirst) getActiveCount(int) int {
return 1
}
type LBStrategyRandom struct{}
func (LBStrategyRandom) getCandidate(serversCount int) int {
return rand.Intn(serversCount)
}
func (LBStrategyRandom) getActiveCount(serversCount int) int {
return serversCount
}
var DefaultLBStrategy = LBStrategyP2{}
type DNSCryptRelay struct {
RelayUDPAddr *net.UDPAddr
RelayTCPAddr *net.TCPAddr
}
type ODoHRelay struct {
URL *url.URL
}
type Relay struct {
Proto stamps.StampProtoType
Dnscrypt *DNSCryptRelay
ODoH *ODoHRelay
}
type ServersInfo struct {
sync.RWMutex
inner []*ServerInfo
registeredServers []RegisteredServer
registeredRelays []RegisteredServer
lbStrategy LBStrategy
lbEstimator bool
}
func NewServersInfo() ServersInfo {
return ServersInfo{lbStrategy: DefaultLBStrategy, lbEstimator: true, registeredServers: make([]RegisteredServer, 0)}
return ServersInfo{
lbStrategy: DefaultLBStrategy,
lbEstimator: true,
registeredServers: make([]RegisteredServer, 0),
registeredRelays: make([]RegisteredServer, 0),
}
}
func (serversInfo *ServersInfo) registerServer(name string, stamp stamps.ServerStamp) {
@ -93,6 +168,19 @@ func (serversInfo *ServersInfo) registerServer(name string, stamp stamps.ServerS
serversInfo.registeredServers = append(serversInfo.registeredServers, newRegisteredServer)
}
func (serversInfo *ServersInfo) registerRelay(name string, stamp stamps.ServerStamp) {
newRegisteredServer := RegisteredServer{name: name, stamp: stamp}
serversInfo.Lock()
defer serversInfo.Unlock()
for i, oldRegisteredServer := range serversInfo.registeredRelays {
if oldRegisteredServer.name == name {
serversInfo.registeredRelays[i] = newRegisteredServer
return
}
}
serversInfo.registeredRelays = append(serversInfo.registeredRelays, newRegisteredServer)
}
func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp stamps.ServerStamp) error {
serversInfo.RLock()
isNew := true
@ -121,26 +209,49 @@ func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp s
break
}
}
if isNew {
serversInfo.inner = append(serversInfo.inner, &newServer)
serversInfo.registeredServers = append(serversInfo.registeredServers, RegisteredServer{name: name, stamp: stamp})
}
serversInfo.Unlock()
if isNew {
serversInfo.Lock()
serversInfo.inner = append(serversInfo.inner, &newServer)
serversInfo.Unlock()
proxy.serversInfo.registerServer(name, stamp)
}
return nil
}
func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) {
dlog.Debug("Refreshing certificates")
serversInfo.RLock()
registeredServers := serversInfo.registeredServers
// Appending registeredServers slice from sources may allocate new memory.
serversCount := len(serversInfo.registeredServers)
registeredServers := make([]RegisteredServer, serversCount)
copy(registeredServers, serversInfo.registeredServers)
serversInfo.RUnlock()
countChannel := make(chan struct{}, proxy.certRefreshConcurrency)
errorChannel := make(chan error, serversCount)
for i := range registeredServers {
countChannel <- struct{}{}
go func(registeredServer *RegisteredServer) {
err := serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp)
if err == nil {
proxy.xTransport.internalResolverReady = true
}
errorChannel <- err
<-countChannel
}(&registeredServers[i])
}
liveServers := 0
var err error
for _, registeredServer := range registeredServers {
if err = serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp); err == nil {
for i := 0; i < serversCount; i++ {
err = <-errorChannel
if err == nil {
liveServers++
}
}
if liveServers > 0 {
err = nil
}
serversInfo.Lock()
sort.SliceStable(serversInfo.inner, func(i, j int) bool {
return serversInfo.inner[i].initialRtt < serversInfo.inner[j].initialRtt
@ -160,31 +271,44 @@ func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) {
return liveServers, err
}
func (serversInfo *ServersInfo) estimatorUpdate() {
func (serversInfo *ServersInfo) estimatorUpdate(currentActive int) {
// serversInfo.RWMutex is assumed to be Locked
candidate := rand.Intn(len(serversInfo.inner))
if candidate == 0 {
serversCount := len(serversInfo.inner)
activeCount := serversInfo.lbStrategy.getActiveCount(serversCount)
if activeCount == serversCount {
return
}
candidateRtt, currentBestRtt := serversInfo.inner[candidate].rtt.Value(), serversInfo.inner[0].rtt.Value()
if currentBestRtt < 0 {
currentBestRtt = candidateRtt
serversInfo.inner[0].rtt.Set(currentBestRtt)
candidate := rand.Intn(serversCount-activeCount) + activeCount
candidateRtt, currentActiveRtt := serversInfo.inner[candidate].rtt.Value(), serversInfo.inner[currentActive].rtt.Value()
if currentActiveRtt < 0 {
currentActiveRtt = candidateRtt
serversInfo.inner[currentActive].rtt.Set(currentActiveRtt)
return
}
partialSort := false
if candidateRtt < currentBestRtt {
serversInfo.inner[candidate], serversInfo.inner[0] = serversInfo.inner[0], serversInfo.inner[candidate]
if candidateRtt < currentActiveRtt {
serversInfo.inner[candidate], serversInfo.inner[currentActive] = serversInfo.inner[currentActive], serversInfo.inner[candidate]
dlog.Debugf(
"New preferred candidate: %s (RTT: %d vs previous: %d)",
serversInfo.inner[currentActive].Name,
int(candidateRtt),
int(currentActiveRtt),
)
partialSort = true
dlog.Debugf("New preferred candidate: %v (rtt: %d vs previous: %d)", serversInfo.inner[0].Name, int(candidateRtt), int(currentBestRtt))
} else if candidateRtt > 0 && candidateRtt >= currentBestRtt*4.0 {
} else if candidateRtt > 0 && candidateRtt >= (serversInfo.inner[0].rtt.Value()+serversInfo.inner[activeCount-1].rtt.Value())/2.0*4.0 {
if time.Since(serversInfo.inner[candidate].lastActionTS) > time.Duration(1*time.Minute) {
serversInfo.inner[candidate].rtt.Add(MinF(MaxF(candidateRtt/2.0, currentBestRtt*2.0), candidateRtt))
dlog.Debugf("Giving a new chance to candidate [%s], lowering its RTT from %d to %d (best: %d)", serversInfo.inner[candidate].Name, int(candidateRtt), int(serversInfo.inner[candidate].rtt.Value()), int(currentBestRtt))
serversInfo.inner[candidate].rtt.Add(candidateRtt / 2.0)
dlog.Debugf(
"Giving a new chance to candidate [%s], lowering its RTT from %d to %d (best: %d)",
serversInfo.inner[candidate].Name,
int(candidateRtt),
int(serversInfo.inner[candidate].rtt.Value()),
int(serversInfo.inner[0].rtt.Value()),
)
partialSort = true
}
}
if partialSort {
serversCount := len(serversInfo.inner)
for i := 1; i < serversCount; i++ {
if serversInfo.inner[i-1].rtt.Value() > serversInfo.inner[i].rtt.Value() {
serversInfo.inner[i-1], serversInfo.inner[i] = serversInfo.inner[i], serversInfo.inner[i-1]
@ -200,22 +324,12 @@ func (serversInfo *ServersInfo) getOne() *ServerInfo {
serversInfo.Unlock()
return nil
}
candidate := serversInfo.lbStrategy.getCandidate(serversCount)
if serversInfo.lbEstimator {
serversInfo.estimatorUpdate()
}
var candidate int
switch serversInfo.lbStrategy {
case LBStrategyFirst:
candidate = 0
case LBStrategyPH:
candidate = rand.Intn(Max(Min(serversCount, 2), serversCount/2))
case LBStrategyRandom:
candidate = rand.Intn(serversCount)
default:
candidate = rand.Intn(Min(serversCount, 2))
serversInfo.estimatorUpdate(candidate)
}
serverInfo := serversInfo.inner[candidate]
dlog.Debugf("Using candidate [%s] RTT: %d", (*serverInfo).Name, int((*serverInfo).rtt.Value()))
dlog.Debugf("Using candidate [%s] RTT: %d", serverInfo.Name, int(serverInfo.rtt.Value()))
serversInfo.Unlock()
return serverInfo
@ -226,72 +340,218 @@ func fetchServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew
return fetchDNSCryptServerInfo(proxy, name, stamp, isNew)
} else if stamp.Proto == stamps.StampProtoTypeDoH {
return fetchDoHServerInfo(proxy, name, stamp, isNew)
} else if stamp.Proto == stamps.StampProtoTypeODoHTarget {
return fetchODoHTargetInfo(proxy, name, stamp, isNew)
}
return ServerInfo{}, errors.New("Unsupported protocol")
return ServerInfo{}, fmt.Errorf("Unsupported protocol for [%s]: [%s]", name, stamp.Proto.String())
}
func route(proxy *Proxy, name string) (*net.UDPAddr, *net.TCPAddr, error) {
func findFarthestRoute(proxy *Proxy, name string, relayStamps []stamps.ServerStamp) *stamps.ServerStamp {
serverIdx := -1
proxy.serversInfo.RLock()
for i, registeredServer := range proxy.serversInfo.registeredServers {
if registeredServer.name == name {
serverIdx = i
break
}
}
if serverIdx < 0 {
proxy.serversInfo.RUnlock()
return nil
}
server := proxy.serversInfo.registeredServers[serverIdx]
proxy.serversInfo.RUnlock()
// Fall back to random relays until the logic is implemented for non-DNSCrypt relays
if server.stamp.Proto == stamps.StampProtoTypeODoHTarget {
candidates := make([]int, 0)
for relayIdx, relayStamp := range relayStamps {
if relayStamp.Proto != stamps.StampProtoTypeODoHRelay {
continue
}
candidates = append(candidates, relayIdx)
}
return &relayStamps[candidates[rand.Intn(len(candidates))]]
} else if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt {
return nil
}
// Anonymized DNSCrypt relays
serverAddrStr, _ := ExtractHostAndPort(server.stamp.ServerAddrStr, 443)
serverAddr := net.ParseIP(serverAddrStr)
if serverAddr == nil {
return nil
}
if len(proxy.serversInfo.registeredRelays) == 0 {
return nil
}
bestRelayIdxs := make([]int, 0)
bestRelaySamePrefixBits := 128
for relayIdx, relayStamp := range relayStamps {
if relayStamp.Proto != stamps.StampProtoTypeDNSCryptRelay {
continue
}
relayAddrStr, _ := ExtractHostAndPort(relayStamp.ServerAddrStr, 443)
relayAddr := net.ParseIP(relayAddrStr)
if relayAddr == nil {
continue
}
relayIsIPv6 := relayAddr.To4() == nil
if relayIsIPv6 != (serverAddr.To4() == nil) {
continue
}
firstByte := 0
if !relayIsIPv6 {
firstByte = 12
}
samePrefixBits := 0
for i := firstByte; i < 16; i++ {
x := serverAddr[i] ^ relayAddr[i]
samePrefixBits += bits.LeadingZeros8(x)
if x != 0 {
break
}
}
if samePrefixBits <= bestRelaySamePrefixBits {
bestRelaySamePrefixBits = samePrefixBits
bestRelayIdxs = append(bestRelayIdxs, relayIdx)
}
}
return &relayStamps[bestRelayIdxs[rand.Intn(len(bestRelayIdxs))]]
}
func relayProtoForServerProto(proto stamps.StampProtoType) (stamps.StampProtoType, error) {
switch proto {
case stamps.StampProtoTypeDNSCrypt:
return stamps.StampProtoTypeDNSCryptRelay, nil
case stamps.StampProtoTypeODoHTarget:
return stamps.StampProtoTypeODoHRelay, nil
default:
return 0, errors.New("protocol cannot be anonymized")
}
}
func route(proxy *Proxy, name string, serverProto stamps.StampProtoType) (*Relay, error) {
routes := proxy.routes
if routes == nil {
return nil, nil, nil
return nil, nil
}
wildcard := false
relayNames, ok := (*routes)[name]
if !ok {
wildcard = true
relayNames, ok = (*routes)["*"]
}
if !ok {
return nil, nil, nil
if !ok || len(relayNames) == 0 {
return nil, nil
}
var relayName string
if len(relayNames) > 0 {
candidate := rand.Intn(len(relayNames))
relayName = relayNames[candidate]
relayProto, err := relayProtoForServerProto(serverProto)
if err != nil {
dlog.Errorf("Server [%v]'s protocol doesn't support anonymization", name)
return nil, nil
}
relayStamps := make([]stamps.ServerStamp, 0)
relayStampToName := make(map[string]string)
for _, relayName := range relayNames {
if relayStamp, err := stamps.NewServerStampFromString(relayName); err == nil {
if relayStamp.Proto == relayProto {
relayStamps = append(relayStamps, relayStamp)
relayStampToName[relayStamp.String()] = relayName
}
} else if relayName == "*" {
proxy.serversInfo.RLock()
for _, registeredServer := range proxy.serversInfo.registeredRelays {
if registeredServer.stamp.Proto == relayProto {
relayStamps = append(relayStamps, registeredServer.stamp)
relayStampToName[registeredServer.stamp.String()] = registeredServer.name
}
}
proxy.serversInfo.RUnlock()
wildcard = true
break
} else {
proxy.serversInfo.RLock()
for _, registeredServer := range proxy.serversInfo.registeredRelays {
if registeredServer.name == relayName && registeredServer.stamp.Proto == relayProto {
relayStamps = append(relayStamps, registeredServer.stamp)
relayStampToName[registeredServer.stamp.String()] = relayName
break
}
}
proxy.serversInfo.RUnlock()
}
}
if len(relayStamps) == 0 {
err := fmt.Errorf("Non-existent relay set for server [%v]", name)
return nil, err
}
var relayCandidateStamp *stamps.ServerStamp
if len(relayName) == 0 {
return nil, nil, fmt.Errorf("Route declared for [%v] but an empty relay list", name)
} else if relayStamp, err := stamps.NewServerStampFromString(relayName); err == nil {
relayCandidateStamp = &relayStamp
} else if _, err := net.ResolveUDPAddr("udp", relayName); err == nil {
relayCandidateStamp = &stamps.ServerStamp{
ServerAddrStr: relayName,
Proto: stamps.StampProtoTypeDNSCryptRelay,
}
if !wildcard || len(relayStamps) == 1 {
relayCandidateStamp = &relayStamps[rand.Intn(len(relayStamps))]
} else {
for _, registeredServer := range proxy.registeredRelays {
if registeredServer.name == relayName {
relayCandidateStamp = &registeredServer.stamp
break
}
}
for _, registeredServer := range proxy.registeredServers {
if registeredServer.name == relayName {
relayCandidateStamp = &registeredServer.stamp
break
}
}
relayCandidateStamp = findFarthestRoute(proxy, name, relayStamps)
}
if relayCandidateStamp == nil {
return nil, nil, fmt.Errorf("Undefined relay [%v] for server [%v]", relayName, name)
return nil, fmt.Errorf("No valid relay for server [%v]", name)
}
if relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCrypt ||
relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCryptRelay {
relayName := relayStampToName[relayCandidateStamp.String()]
switch relayCandidateStamp.Proto {
case stamps.StampProtoTypeDNSCrypt, stamps.StampProtoTypeDNSCryptRelay:
relayUDPAddr, err := net.ResolveUDPAddr("udp", relayCandidateStamp.ServerAddrStr)
if err != nil {
return nil, nil, err
return nil, err
}
relayTCPAddr, err := net.ResolveTCPAddr("tcp", relayCandidateStamp.ServerAddrStr)
if err != nil {
return nil, nil, err
return nil, err
}
return relayUDPAddr, relayTCPAddr, nil
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayName)
return &Relay{
Proto: stamps.StampProtoTypeDNSCryptRelay,
Dnscrypt: &DNSCryptRelay{RelayUDPAddr: relayUDPAddr, RelayTCPAddr: relayTCPAddr},
}, nil
case stamps.StampProtoTypeODoHRelay:
relayBaseURL, err := url.Parse(
"https://" + url.PathEscape(relayCandidateStamp.ProviderName) + relayCandidateStamp.Path,
)
if err != nil {
return nil, err
}
var relayURLforTarget *url.URL
for _, server := range proxy.registeredServers {
if server.name != name || server.stamp.Proto != stamps.StampProtoTypeODoHTarget {
continue
}
qs := relayBaseURL.Query()
qs.Add("targethost", server.stamp.ProviderName)
qs.Add("targetpath", server.stamp.Path)
tmp := *relayBaseURL
tmp.RawQuery = qs.Encode()
relayURLforTarget = &tmp
break
}
if relayURLforTarget == nil {
return nil, fmt.Errorf("Relay [%v] not found", relayName)
}
if len(relayCandidateStamp.ServerAddrStr) > 0 {
ipOnly, _ := ExtractHostAndPort(relayCandidateStamp.ServerAddrStr, -1)
if ip := ParseIP(ipOnly); ip != nil {
host, _ := ExtractHostAndPort(relayCandidateStamp.ProviderName, -1)
proxy.xTransport.saveCachedIP(host, ip, -1*time.Second)
}
}
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayName)
return &Relay{Proto: stamps.StampProtoTypeODoHRelay, ODoH: &ODoHRelay{
URL: relayURLforTarget,
}}, nil
}
return nil, nil, fmt.Errorf("Invalid relay [%v] for server [%v]", relayName, name)
return nil, fmt.Errorf("Invalid relay set for server [%v]", name)
}
func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
if len(stamp.ServerPk) != ed25519.PublicKeySize {
serverPk, err := hex.DecodeString(strings.Replace(string(stamp.ServerPk), ":", "", -1))
serverPk, err := hex.DecodeString(strings.ReplaceAll(string(stamp.ServerPk), ":", ""))
if err != nil || len(serverPk) != ed25519.PublicKeySize {
dlog.Fatalf("Unsupported public key for [%s]: [%s]", name, stamp.ServerPk)
}
@ -299,22 +559,44 @@ func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp
stamp.ServerPk = serverPk
}
knownBugs := ServerBugs{}
for _, buggyServerName := range proxy.serversWithBrokenQueryPadding {
for _, buggyServerName := range proxy.serversBlockingFragments {
if buggyServerName == name {
knownBugs.incorrectPadding = true
dlog.Infof("Known bug in [%v]: padded queries are not correctly parsed", name)
knownBugs.fragmentsBlocked = true
dlog.Infof("Known bug in [%v]: fragmented questions over UDP are blocked", name)
break
}
}
relayUDPAddr, relayTCPAddr, err := route(proxy, name)
if knownBugs.incorrectPadding && (relayUDPAddr != nil || relayTCPAddr != nil) {
relayTCPAddr, relayUDPAddr = nil, nil
dlog.Warnf("[%v] is incompatible with anonymization", name)
}
relay, err := route(proxy, name, stamp.Proto)
if err != nil {
return ServerInfo{}, err
}
certInfo, rtt, err := FetchCurrentDNSCryptCert(proxy, &name, proxy.mainProto, stamp.ServerPk, stamp.ServerAddrStr, stamp.ProviderName, isNew, relayUDPAddr, relayTCPAddr)
var dnscryptRelay *DNSCryptRelay
if relay != nil {
dnscryptRelay = relay.Dnscrypt
}
certInfo, rtt, fragmentsBlocked, err := FetchCurrentDNSCryptCert(
proxy,
&name,
proxy.mainProto,
stamp.ServerPk,
stamp.ServerAddrStr,
stamp.ProviderName,
isNew,
dnscryptRelay,
knownBugs,
)
if !knownBugs.fragmentsBlocked && fragmentsBlocked {
dlog.Debugf("[%v] drops fragmented queries", name)
knownBugs.fragmentsBlocked = true
}
if knownBugs.fragmentsBlocked && relay != nil && relay.Dnscrypt != nil {
relay = nil
if proxy.skipAnonIncompatibleResolvers {
dlog.Infof("[%v] couldn't be reached anonymously, it will be ignored", name)
return ServerInfo{}, errors.New("Resolver couldn't be reached anonymously")
}
dlog.Warnf("[%v] couldn't be reached anonymously", name)
}
if err != nil {
return ServerInfo{}, err
}
@ -336,13 +618,53 @@ func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp
Timeout: proxy.timeout,
UDPAddr: remoteUDPAddr,
TCPAddr: remoteTCPAddr,
RelayUDPAddr: relayUDPAddr,
RelayTCPAddr: relayTCPAddr,
Relay: relay,
initialRtt: rtt,
knownBugs: knownBugs,
}, nil
}
func dohTestPacket(msgID uint16) []byte {
msg := dns.Msg{}
msg.SetQuestion(".", dns.TypeNS)
msg.Id = msgID
msg.MsgHdr.RecursionDesired = true
msg.SetEdns0(uint16(MaxDNSPacketSize), false)
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, 16)
_, _ = crypto_rand.Read(ext.Padding)
edns0 := msg.IsEdns0()
edns0.Option = append(edns0.Option, ext)
body, err := msg.Pack()
if err != nil {
dlog.Fatal(err)
}
return body
}
func dohNXTestPacket(msgID uint16) []byte {
msg := dns.Msg{}
qName := make([]byte, 16)
charset := "abcdefghijklmnopqrstuvwxyz"
for i := range qName {
qName[i] = charset[rand.Intn(len(charset))]
}
msg.SetQuestion(string(qName)+".test.dnscrypt.", dns.TypeNS)
msg.Id = msgID
msg.MsgHdr.RecursionDesired = true
msg.SetEdns0(uint16(MaxDNSPacketSize), false)
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, 16)
_, _ = crypto_rand.Read(ext.Padding)
edns0 := msg.IsEdns0()
edns0.Option = append(edns0.Option, ext)
body, err := msg.Pack()
if err != nil {
dlog.Fatal(err)
}
return body
}
func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
// If an IP has been provided, use it forever.
// Or else, if the fallback server and the DoH server are operated
@ -351,7 +673,8 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
if len(stamp.ServerAddrStr) > 0 {
ipOnly, _ := ExtractHostAndPort(stamp.ServerAddrStr, -1)
if ip := ParseIP(ipOnly); ip != nil {
proxy.xTransport.saveCachedIP(stamp.ProviderName, ip, -1*time.Second)
host, _ := ExtractHostAndPort(stamp.ProviderName, -1)
proxy.xTransport.saveCachedIP(host, ip, -1*time.Second)
}
}
url := &url.URL{
@ -359,29 +682,38 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
Host: stamp.ProviderName,
Path: stamp.Path,
}
body := []byte{
0xca, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
}
body := dohTestPacket(0xcafe)
useGet := false
if _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
if _, _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
useGet = true
if _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
if _, _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
return ServerInfo{}, err
}
dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name)
}
resp, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout)
body = dohNXTestPacket(0xcafe)
serverResponse, _, tls, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout)
if err != nil {
dlog.Infof("[%s] [%s]: %v", name, url, err)
return ServerInfo{}, err
}
tls := resp.TLS
if tls == nil || !tls.HandshakeComplete {
return ServerInfo{}, errors.New("TLS handshake failed")
}
msg := dns.Msg{}
if err := msg.Unpack(serverResponse); err != nil {
dlog.Warnf("[%s]: %v", name, err)
return ServerInfo{}, err
}
if msg.Rcode != dns.RcodeNameError {
dlog.Criticalf("[%s] may be a lying resolver", name)
}
protocol := tls.NegotiatedProtocol
if len(protocol) == 0 {
protocol = "h1"
dlog.Warnf("[%s] does not support HTTP/2", name)
protocol = "http/1.x"
}
if strings.HasPrefix(protocol, "http/1.") {
dlog.Warnf("[%s] does not support HTTP/2 nor HTTP/3", name)
}
dlog.Infof("[%s] TLS version: %x - Protocol: %v - Cipher suite: %v", name, tls.Version, protocol, tls.CipherSuite)
showCerts := proxy.showCerts
@ -408,14 +740,12 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
}
}
if !found && len(stamp.Hashes) > 0 {
return ServerInfo{}, fmt.Errorf("Certificate hash [%x] not found for [%s]", wantedHash, name)
dlog.Criticalf("[%s] Certificate hash [%x] not found", name, wantedHash)
return ServerInfo{}, fmt.Errorf("Certificate hash not found")
}
respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength))
if err != nil {
return ServerInfo{}, err
}
if len(respBody) < MinDNSPacketSize || len(respBody) > MaxDNSPacketSize ||
respBody[0] != 0xca || respBody[1] != 0xfe || respBody[4] != 0x00 || respBody[5] != 0x01 {
if len(serverResponse) < MinDNSPacketSize || len(serverResponse) > MaxDNSPacketSize ||
serverResponse[0] != 0xca || serverResponse[1] != 0xfe || serverResponse[4] != 0x00 || serverResponse[5] != 0x01 {
dlog.Info("Webserver returned an unexpected response")
return ServerInfo{}, errors.New("Webserver returned an unexpected response")
}
xrtt := int(rtt.Nanoseconds() / 1000000)
@ -435,6 +765,202 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
}, nil
}
func fetchTargetConfigsFromWellKnown(proxy *Proxy, url *url.URL) ([]ODoHTargetConfig, error) {
bin, statusCode, _, _, err := proxy.xTransport.Get(url, "application/binary", 0)
if err != nil {
return nil, err
}
if statusCode < 200 || statusCode >= 300 {
return nil, fmt.Errorf("HTTP status code was %v", statusCode)
}
return parseODoHTargetConfigs(bin)
}
func _fetchODoHTargetInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
configURL := &url.URL{Scheme: "https", Host: stamp.ProviderName, Path: "/.well-known/odohconfigs"}
odohTargetConfigs, err := fetchTargetConfigsFromWellKnown(proxy, configURL)
if err != nil {
dlog.Debug(configURL)
return ServerInfo{}, fmt.Errorf("[%s] didn't return an ODoH configuration - [%v]", name, err)
} else if len(odohTargetConfigs) == 0 {
dlog.Debug(configURL)
return ServerInfo{}, fmt.Errorf("[%s] has an empty ODoH configuration", name)
}
relay, err := route(proxy, name, stamp.Proto)
if err != nil {
return ServerInfo{}, err
}
if relay == nil {
dlog.Criticalf(
"No relay defined for [%v] - Configuring a relay is required for ODoH servers (see the `[anonymized_dns]` section)",
name,
)
return ServerInfo{}, errors.New("No ODoH relay")
} else {
if relay.ODoH == nil {
dlog.Criticalf("Wrong relay type defined for [%v] - ODoH servers require an ODoH relay", name)
return ServerInfo{}, errors.New("Wrong ODoH relay type")
}
}
dlog.Debugf("Pausing after ODoH configuration retrieval")
delay := time.Duration(rand.Intn(5*1000)) * time.Millisecond
clocksmith.Sleep(time.Duration(delay))
dlog.Debugf("Pausing done")
targetURL := &url.URL{
Scheme: "https",
Host: stamp.ProviderName,
Path: stamp.Path,
}
workingConfigs := make([]ODoHTargetConfig, 0)
rand.Shuffle(len(odohTargetConfigs), func(i, j int) {
odohTargetConfigs[i], odohTargetConfigs[j] = odohTargetConfigs[j], odohTargetConfigs[i]
})
for _, odohTargetConfig := range odohTargetConfigs {
url := relay.ODoH.URL
query := dohTestPacket(0xcafe)
odohQuery, err := odohTargetConfig.encryptQuery(query)
if err != nil {
continue
}
useGet := false
if _, _, _, _, err := proxy.xTransport.ObliviousDoHQuery(useGet, url, odohQuery.odohMessage, proxy.timeout); err != nil {
useGet = true
if _, _, _, _, err := proxy.xTransport.ObliviousDoHQuery(useGet, url, odohQuery.odohMessage, proxy.timeout); err != nil {
continue
}
dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name)
}
query = dohNXTestPacket(0xcafe)
odohQuery, err = odohTargetConfig.encryptQuery(query)
if err != nil {
continue
}
responseBody, responseCode, tls, rtt, err := proxy.xTransport.ObliviousDoHQuery(
useGet,
url,
odohQuery.odohMessage,
proxy.timeout,
)
if err != nil {
continue
}
if responseCode == 401 {
return ServerInfo{}, fmt.Errorf("Configuration changed during a probe")
}
serverResponse, err := odohQuery.decryptResponse(responseBody)
if err != nil {
dlog.Warnf("Unable to decrypt response from [%v]: [%v]", name, err)
continue
}
workingConfigs = append(workingConfigs, odohTargetConfig)
msg := dns.Msg{}
if err := msg.Unpack(serverResponse); err != nil {
dlog.Warnf("[%s]: %v", name, err)
return ServerInfo{}, err
}
if msg.Rcode != dns.RcodeNameError {
dlog.Criticalf("[%s] may be a lying resolver", name)
}
protocol := "http"
tlsVersion := uint16(0)
tlsCipherSuite := uint16(0)
if tls != nil {
protocol = tls.NegotiatedProtocol
if len(protocol) == 0 {
protocol = "http/1.x"
} else {
tlsVersion = tls.Version
tlsCipherSuite = tls.CipherSuite
}
}
if strings.HasPrefix(protocol, "http/1.") {
dlog.Warnf("[%s] does not support HTTP/2", name)
}
dlog.Infof(
"[%s] TLS version: %x - Protocol: %v - Cipher suite: %v",
name,
tlsVersion,
protocol,
tlsCipherSuite,
)
showCerts := proxy.showCerts
found := false
var wantedHash [32]byte
if tls != nil {
for _, cert := range tls.PeerCertificates {
h := sha256.Sum256(cert.RawTBSCertificate)
if showCerts {
dlog.Noticef("Advertised relay cert: [%s] [%x]", cert.Subject, h)
} else {
dlog.Debugf("Advertised relay cert: [%s] [%x]", cert.Subject, h)
}
for _, hash := range stamp.Hashes {
if len(hash) == len(wantedHash) {
copy(wantedHash[:], hash)
if h == wantedHash {
found = true
break
}
}
}
if found {
break
}
}
if !found && len(stamp.Hashes) > 0 {
dlog.Criticalf("[%s] Certificate hash [%x] not found", name, wantedHash)
return ServerInfo{}, fmt.Errorf("Certificate hash not found")
}
}
if len(serverResponse) < MinDNSPacketSize || len(serverResponse) > MaxDNSPacketSize ||
serverResponse[0] != 0xca || serverResponse[1] != 0xfe || serverResponse[4] != 0x00 || serverResponse[5] != 0x01 {
dlog.Info("Webserver returned an unexpected response")
return ServerInfo{}, errors.New("Webserver returned an unexpected response")
}
xrtt := int(rtt.Nanoseconds() / 1000000)
if isNew {
dlog.Noticef("[%s] OK (ODoH) - rtt: %dms", name, xrtt)
} else {
dlog.Infof("[%s] OK (ODoH) - rtt: %dms", name, xrtt)
}
return ServerInfo{
Proto: stamps.StampProtoTypeODoHTarget,
Name: name,
Timeout: proxy.timeout,
URL: targetURL,
HostName: stamp.ProviderName,
initialRtt: xrtt,
useGet: useGet,
Relay: relay,
odohTargetConfigs: workingConfigs,
}, nil
}
return ServerInfo{}, fmt.Errorf("No valid network configuration for [%v]", name)
}
func fetchODoHTargetInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
var err error
var serverInfo ServerInfo
for i := 0; i < 3; i += 1 {
serverInfo, err = _fetchODoHTargetInfo(proxy, name, stamp, isNew)
if err == nil {
break
}
dlog.Infof("Trying to fetch the [%v] configuration again", name)
}
return serverInfo, err
}
func (serverInfo *ServerInfo) noticeFailure(proxy *Proxy) {
proxy.serversInfo.Lock()
serverInfo.rtt.Add(float64(proxy.timeout.Nanoseconds() / 1000000))

View File

@ -1,3 +1,4 @@
//go:build android
// +build android
package main

View File

@ -1,3 +1,4 @@
//go:build !android
// +build !android
package main
@ -7,13 +8,15 @@ import (
clocksmith "github.com/jedisct1/go-clocksmith"
)
const SdNotifyStatus = "STATUS="
func ServiceManagerStartNotify() error {
daemon.SdNotify(false, "STATUS=Starting")
daemon.SdNotify(false, SdNotifyStatus+"Starting...")
return nil
}
func ServiceManagerReadyNotify() error {
daemon.SdNotify(false, "READY=1")
daemon.SdNotify(false, daemon.SdNotifyReady+"\n"+SdNotifyStatus+"Ready")
return systemDWatchdog()
}
@ -25,10 +28,9 @@ func systemDWatchdog() error {
refreshInterval := watchdogFailureDelay / 3
go func() {
for {
daemon.SdNotify(false, "WATCHDOG=1")
daemon.SdNotify(false, daemon.SdNotifyWatchdog)
clocksmith.Sleep(refreshInterval)
}
}()
return nil
}

View File

@ -1,3 +1,4 @@
//go:build !linux && !windows
// +build !linux,!windows
package main

View File

@ -7,7 +7,8 @@ func ServiceManagerStartNotify() error {
if err != nil {
return err
}
mgr.Disconnect()
_ = mgr.Disconnect()
return nil
}

View File

@ -0,0 +1,31 @@
package main
import (
"net"
"syscall"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_DF, 0)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, 4096)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, 4096)
})
return nil
},
}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
})
return nil
},
}, nil
}

View File

@ -0,0 +1,35 @@
package main
import (
"net"
"syscall"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_DF, 0)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, 4096)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, 4096)
})
return nil
},
}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
})
return nil
},
}, nil
}

View File

@ -0,0 +1,40 @@
package main
import (
"net"
"syscall"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_FREEBIND, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_DF, 0)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(
int(fd),
syscall.IPPROTO_IP,
syscall.IP_MTU_DISCOVER,
syscall.IP_PMTUDISC_DONT,
)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUFFORCE, 4096)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUFFORCE, 4096)
})
return nil
},
}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_FREEBIND, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_QUICKACK, 1)
})
return nil
},
}, nil
}

View File

@ -0,0 +1,33 @@
package main
import (
"net"
"syscall"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_DF, 0)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, 4096)
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, 4096)
})
return nil
},
}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_BINDANY, 1)
_ = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
})
return nil
},
}, nil
}

View File

@ -0,0 +1,16 @@
//go:build !freebsd && !openbsd && !windows && !darwin && !linux
// +build !freebsd,!openbsd,!windows,!darwin,!linux
package main
import (
"net"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{}, nil
}

View File

@ -0,0 +1,30 @@
package main
import (
"net"
"syscall"
)
func (proxy *Proxy) udpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
_ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, 4096)
_ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, 4096)
})
return nil
},
}, nil
}
func (proxy *Proxy) tcpListenerConfig() (*net.ListenConfig, error) {
return &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
_ = c.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x70)
})
return nil
},
}, nil
}

View File

@ -3,20 +3,17 @@ package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"math/rand"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"unicode"
"github.com/dchest/safefile"
"github.com/jedisct1/dlog"
stamps "github.com/jedisct1/go-dnsstamps"
"github.com/jedisct1/go-dnsstamps"
"github.com/jedisct1/go-minisign"
)
@ -35,90 +32,92 @@ type Source struct {
name string
urls []*url.URL
format SourceFormat
in []byte
bin []byte
minisignKey *minisign.PublicKey
cacheFile string
cacheTTL, prefetchDelay time.Duration
refresh time.Time
prefix string
}
func (source *Source) checkSignature(bin, sig []byte) (err error) {
var signature minisign.Signature
if signature, err = minisign.DecodeSignature(string(sig)); err == nil {
_, err = source.minisignKey.Verify(bin, signature)
}
return
}
// timeNow can be replaced by tests to provide a static value
// timeNow() is replaced by tests to provide a static value
var timeNow = time.Now
func (source *Source) fetchFromCache(now time.Time) (delay time.Duration, err error) {
var bin, sig []byte
if bin, err = ioutil.ReadFile(source.cacheFile); err != nil {
return
func (source *Source) checkSignature(bin, sig []byte) error {
signature, err := minisign.DecodeSignature(string(sig))
if err == nil {
_, err = source.minisignKey.Verify(bin, signature)
}
if sig, err = ioutil.ReadFile(source.cacheFile + ".minisig"); err != nil {
return
return err
}
func (source *Source) fetchFromCache(now time.Time) (time.Duration, error) {
var err error
var bin, sig []byte
if bin, err = os.ReadFile(source.cacheFile); err != nil {
return 0, err
}
if sig, err = os.ReadFile(source.cacheFile + ".minisig"); err != nil {
return 0, err
}
if err = source.checkSignature(bin, sig); err != nil {
return
return 0, err
}
source.in = bin
source.bin = bin
var fi os.FileInfo
if fi, err = os.Stat(source.cacheFile); err != nil {
return
return 0, err
}
var ttl time.Duration = 0
if elapsed := now.Sub(fi.ModTime()); elapsed < source.cacheTTL {
delay = source.prefetchDelay - elapsed
dlog.Debugf("Source [%s] cache file [%s] is still fresh, next update: %v", source.name, source.cacheFile, delay)
ttl = source.prefetchDelay - elapsed
dlog.Debugf("Source [%s] cache file [%s] is still fresh, next update: %v", source.name, source.cacheFile, ttl)
} else {
dlog.Debugf("Source [%s] cache file [%s] needs to be refreshed", source.name, source.cacheFile)
}
return
return ttl, nil
}
func writeSource(f string, bin, sig []byte) (err error) {
func writeSource(f string, bin, sig []byte) error {
var err error
var fSrc, fSig *safefile.File
if fSrc, err = safefile.Create(f, 0644); err != nil {
return
if fSrc, err = safefile.Create(f, 0o644); err != nil {
return err
}
defer fSrc.Close()
if fSig, err = safefile.Create(f+".minisig", 0644); err != nil {
return
if fSig, err = safefile.Create(f+".minisig", 0o644); err != nil {
return err
}
defer fSig.Close()
if _, err = fSrc.Write(bin); err != nil {
return
return err
}
if _, err = fSig.Write(sig); err != nil {
return
return err
}
if err = fSrc.Commit(); err != nil {
return
return err
}
return fSig.Commit()
}
func (source *Source) writeToCache(bin, sig []byte, now time.Time) {
f := source.cacheFile
var writeErr error // an error writing cache isn't fatal
defer func() {
source.in = bin
if writeErr == nil {
return
}
if absPath, absErr := filepath.Abs(f); absErr == nil {
f = absPath
}
dlog.Warnf("%s: %s", f, writeErr)
}()
if !bytes.Equal(source.in, bin) {
if writeErr = writeSource(f, bin, sig); writeErr != nil {
return
func (source *Source) updateCache(bin, sig []byte, now time.Time) {
file := source.cacheFile
absPath := file
if resolved, err := filepath.Abs(file); err != nil {
absPath = resolved
}
if !bytes.Equal(source.bin, bin) {
if err := writeSource(file, bin, sig); err != nil {
dlog.Warnf("Couldn't write cache file [%s]: %s", absPath, err) // an error writing to the cache isn't fatal
}
}
writeErr = os.Chtimes(f, now, now)
if err := os.Chtimes(file, now, now); err != nil {
dlog.Warnf("Couldn't update cache file [%s]: %s", absPath, err)
}
source.bin = bin
}
func (source *Source) parseURLs(urls []string) {
@ -131,32 +130,32 @@ func (source *Source) parseURLs(urls []string) {
}
}
func fetchFromURL(xTransport *XTransport, u *url.URL) (bin []byte, err error) {
var resp *http.Response
if resp, _, err = xTransport.Get(u, "", DefaultTimeout); err == nil {
bin, err = ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength))
resp.Body.Close()
}
return
func fetchFromURL(xTransport *XTransport, u *url.URL) ([]byte, error) {
bin, _, _, _, err := xTransport.GetWithCompression(u, "", DefaultTimeout)
return bin, err
}
func (source *Source) fetchWithCache(xTransport *XTransport, now time.Time) (delay time.Duration, err error) {
if delay, err = source.fetchFromCache(now); err != nil {
func (source *Source) fetchWithCache(xTransport *XTransport, now time.Time) (time.Duration, error) {
var err error
var ttl time.Duration
if ttl, err = source.fetchFromCache(now); err != nil {
if len(source.urls) == 0 {
dlog.Errorf("Source [%s] cache file [%s] not present and no valid URL", source.name, source.cacheFile)
return
return 0, err
}
dlog.Debugf("Source [%s] cache file [%s] not present", source.name, source.cacheFile)
}
if len(source.urls) > 0 {
defer func() {
source.refresh = now.Add(delay)
}()
if len(source.urls) == 0 {
return 0, err
}
if len(source.urls) == 0 || delay > 0 {
return
if ttl > 0 {
source.refresh = now.Add(ttl)
return 0, err
}
delay = MinimumPrefetchInterval
ttl = MinimumPrefetchInterval
source.refresh = now.Add(ttl)
var bin, sig []byte
for _, srcURL := range source.urls {
dlog.Infof("Source [%s] loading from URL [%s]", source.name, srcURL)
@ -171,25 +170,43 @@ func (source *Source) fetchWithCache(xTransport *XTransport, now time.Time) (del
dlog.Debugf("Source [%s] failed to download signature from URL [%s]", source.name, sigURL)
continue
}
if err = source.checkSignature(bin, sig); err == nil {
break // valid signature
} // above err check inverted to make use of implicit continue
dlog.Debugf("Source [%s] failed signature check using URL [%s]", source.name, srcURL)
if err = source.checkSignature(bin, sig); err != nil {
dlog.Debugf("Source [%s] failed signature check using URL [%s]", source.name, srcURL)
continue
}
break // valid signature
}
if err != nil {
return
return 0, err
}
source.writeToCache(bin, sig, now)
delay = source.prefetchDelay
return
source.updateCache(bin, sig, now)
ttl = source.prefetchDelay
source.refresh = now.Add(ttl)
return ttl, nil
}
// NewSource loads a new source using the given cacheFile and urls, ensuring it has a valid signature
func NewSource(name string, xTransport *XTransport, urls []string, minisignKeyStr string, cacheFile string, formatStr string, refreshDelay time.Duration) (source *Source, err error) {
func NewSource(
name string,
xTransport *XTransport,
urls []string,
minisignKeyStr string,
cacheFile string,
formatStr string,
refreshDelay time.Duration,
prefix string,
) (*Source, error) {
if refreshDelay < DefaultPrefetchDelay {
refreshDelay = DefaultPrefetchDelay
}
source = &Source{name: name, urls: []*url.URL{}, cacheFile: cacheFile, cacheTTL: refreshDelay, prefetchDelay: DefaultPrefetchDelay}
source := &Source{
name: name,
urls: []*url.URL{},
cacheFile: cacheFile,
cacheTTL: refreshDelay,
prefetchDelay: DefaultPrefetchDelay,
prefix: prefix,
}
if formatStr == "v2" {
source.format = SourceFormatV2
} else {
@ -201,10 +218,11 @@ func NewSource(name string, xTransport *XTransport, urls []string, minisignKeySt
return source, err
}
source.parseURLs(urls)
if _, err = source.fetchWithCache(xTransport, timeNow()); err == nil {
_, err := source.fetchWithCache(xTransport, timeNow())
if err == nil {
dlog.Noticef("Source [%s] loaded", name)
}
return
return source, err
}
// PrefetchSources downloads latest versions of given sources, ensuring they have a valid signature before caching
@ -217,9 +235,9 @@ func PrefetchSources(xTransport *XTransport, sources []*Source) time.Duration {
}
dlog.Debugf("Prefetching [%s]", source.name)
if delay, err := source.fetchWithCache(xTransport, now); err != nil {
dlog.Infof("Prefetching [%s] failed: %v", source.name, err)
dlog.Infof("Prefetching [%s] failed: %v, will retry in %v", source.name, err, interval)
} else {
dlog.Debugf("Prefetching [%s] succeeded, next update: %v", source.name, delay)
dlog.Debugf("Prefetching [%s] succeeded, next update in %v min", source.name, delay)
if delay >= MinimumPrefetchInterval && (interval == MinimumPrefetchInterval || interval > delay) {
interval = delay
}
@ -228,15 +246,15 @@ func PrefetchSources(xTransport *XTransport, sources []*Source) time.Duration {
return interval
}
func (source *Source) Parse(prefix string) ([]RegisteredServer, error) {
func (source *Source) Parse() ([]RegisteredServer, error) {
if source.format == SourceFormatV2 {
return source.parseV2(prefix)
return source.parseV2()
}
dlog.Fatal("Unexpected source format")
return []RegisteredServer{}, nil
}
func (source *Source) parseV2(prefix string) ([]RegisteredServer, error) {
func (source *Source) parseV2() ([]RegisteredServer, error) {
var registeredServers []RegisteredServer
var stampErrs []string
appendStampErr := func(format string, a ...interface{}) {
@ -244,34 +262,30 @@ func (source *Source) parseV2(prefix string) ([]RegisteredServer, error) {
stampErrs = append(stampErrs, stampErr)
dlog.Warn(stampErr)
}
in := string(source.in)
in := string(source.bin)
parts := strings.Split(in, "## ")
if len(parts) < 2 {
return registeredServers, fmt.Errorf("Invalid format for source at [%v]", source.urls)
}
parts = parts[1:]
PartsLoop:
for _, part := range parts {
part = strings.TrimFunc(part, unicode.IsSpace)
part = strings.TrimSpace(part)
subparts := strings.Split(part, "\n")
if len(subparts) < 2 {
return registeredServers, fmt.Errorf("Invalid format for source at [%v]", source.urls)
}
name := strings.TrimFunc(subparts[0], unicode.IsSpace)
name := strings.TrimSpace(subparts[0])
if len(name) == 0 {
return registeredServers, fmt.Errorf("Invalid format for source at [%v]", source.urls)
}
subparts = subparts[1:]
name = prefix + name
name = source.prefix + name
var stampStr, description string
stampStrs := make([]string, 0)
for _, subpart := range subparts {
subpart = strings.TrimFunc(subpart, unicode.IsSpace)
if strings.HasPrefix(subpart, "sdns:") {
if len(stampStr) > 0 {
appendStampErr("Multiple stamps for server [%s]", name)
continue PartsLoop
}
stampStr = subpart
subpart = strings.TrimSpace(subpart)
if strings.HasPrefix(subpart, "sdns:") && len(subpart) >= 6 {
stampStrs = append(stampStrs, subpart)
continue
} else if len(subpart) == 0 || strings.HasPrefix(subpart, "//") {
continue
@ -281,13 +295,23 @@ PartsLoop:
}
description += subpart
}
if len(stampStr) < 6 {
stampStrsLen := len(stampStrs)
if stampStrsLen <= 0 {
appendStampErr("Missing stamp for server [%s]", name)
continue
} else if stampStrsLen > 1 {
rand.Shuffle(stampStrsLen, func(i, j int) { stampStrs[i], stampStrs[j] = stampStrs[j], stampStrs[i] })
}
stamp, err := stamps.NewServerStampFromString(stampStr)
if err != nil {
var stamp dnsstamps.ServerStamp
var err error
for _, stampStr = range stampStrs {
stamp, err = dnsstamps.NewServerStampFromString(stampStr)
if err == nil {
break
}
appendStampErr("Invalid or unsupported stamp [%v]: %s", stampStr, err.Error())
}
if err != nil {
continue
}
registeredServer := RegisteredServer{

View File

@ -2,7 +2,7 @@ package main
import (
"bytes"
"io/ioutil"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
@ -14,9 +14,9 @@ import (
"time"
"github.com/hectane/go-acl"
"github.com/powerman/check"
"github.com/jedisct1/dlog"
"github.com/jedisct1/go-minisign"
"github.com/powerman/check"
)
type SourceFixture struct {
@ -33,8 +33,8 @@ const (
TestStateExpired // modification time of files set in distant past (cache only)
TestStatePartial // incomplete files
TestStatePartialSig // incomplete .minisig
TestStateMissing // non-existant files
TestStateMissingSig // non-existant .minisig
TestStateMissing // non-existent files
TestStateMissingSig // non-existent .minisig
TestStateReadErr // I/O error on reading files (download only)
TestStateReadSigErr // I/O error on reading .minisig (download only)
TestStateOpenErr // I/O error on opening files
@ -64,10 +64,11 @@ type SourceTestExpect struct {
urls []string
Source *Source
delay time.Duration
prefix string
}
func readFixture(t *testing.T, name string) []byte {
bin, err := ioutil.ReadFile(filepath.Join("testdata", name))
bin, err := os.ReadFile(filepath.Join("testdata", name))
if err != nil {
t.Fatalf("Unable to read test fixture %s: %v", name, err)
}
@ -82,9 +83,9 @@ func writeSourceCache(t *testing.T, e *SourceTestExpect) {
path := e.cachePath + f.suffix
perms := f.perms
if perms == 0 {
perms = 0644
perms = 0o644
}
if err := ioutil.WriteFile(path, f.content, perms); err != nil {
if err := os.WriteFile(path, f.content, perms); err != nil {
t.Fatalf("Unable to write cache file %s: %v", path, err)
}
if err := acl.Chmod(path, perms); err != nil {
@ -106,8 +107,8 @@ func writeSourceCache(t *testing.T, e *SourceTestExpect) {
func checkSourceCache(c *check.C, e *SourceTestExpect) {
for _, f := range e.cache {
path := e.cachePath + f.suffix
_ = acl.Chmod(path, 0644) // don't worry if this fails, reading it will catch the same problem
got, err := ioutil.ReadFile(path)
_ = acl.Chmod(path, 0o644) // don't worry if this fails, reading it will catch the same problem
got, err := os.ReadFile(path)
c.DeepEqual(got, f.content, "Unexpected content for cache file '%s', err %v", path, err)
if f.suffix != "" {
continue
@ -132,7 +133,7 @@ func loadSnakeoil(t *testing.T, d *SourceTestData) {
}
func loadTestSourceNames(t *testing.T, d *SourceTestData) {
files, err := ioutil.ReadDir(filepath.Join("testdata", "sources"))
files, err := os.ReadDir(filepath.Join("testdata", "sources"))
if err != nil {
t.Fatalf("Unable to load list of test sources: %v", err)
}
@ -143,7 +144,7 @@ func loadTestSourceNames(t *testing.T, d *SourceTestData) {
}
}
func generateFixtureState(t *testing.T, d *SourceTestData, suffix, file string, state SourceTestState) {
func generateFixtureState(_ *testing.T, d *SourceTestData, suffix, file string, state SourceTestState) {
if _, ok := d.fixtures[state]; !ok {
d.fixtures[state] = map[string]SourceFixture{}
}
@ -163,7 +164,7 @@ func generateFixtureState(t *testing.T, d *SourceTestData, suffix, file string,
case TestStateReadErr, TestStateReadSigErr:
f.content, f.length = []byte{}, "1"
case TestStateOpenErr, TestStateOpenSigErr:
f.content, f.perms = d.fixtures[TestStateCorrect][file].content[:1], 0200
f.content, f.perms = d.fixtures[TestStateCorrect][file].content[:1], 0o200
}
d.fixtures[state][file] = f
}
@ -194,7 +195,7 @@ func loadFixtures(t *testing.T, d *SourceTestData) {
}
func makeTempDir(t *testing.T, d *SourceTestData) {
name, err := ioutil.TempDir("", "sources_test.go."+t.Name())
name, err := os.MkdirTemp("", "sources_test.go."+t.Name())
if err != nil {
t.Fatalf("Unable to create temporary directory: %v", err)
}
@ -283,9 +284,9 @@ func prepSourceTestCache(t *testing.T, d *SourceTestData, e *SourceTestExpect, s
e.cache = []SourceFixture{d.fixtures[state][source], d.fixtures[state][source+".minisig"]}
switch state {
case TestStateCorrect:
e.Source.in, e.success = e.cache[0].content, true
e.Source.bin, e.success = e.cache[0].content, true
case TestStateExpired:
e.Source.in = e.cache[0].content
e.Source.bin = e.cache[0].content
case TestStatePartial, TestStatePartialSig:
e.err = "signature"
case TestStateMissing, TestStateMissingSig, TestStateOpenErr, TestStateOpenSigErr:
@ -294,12 +295,19 @@ func prepSourceTestCache(t *testing.T, d *SourceTestData, e *SourceTestExpect, s
writeSourceCache(t, e)
}
func prepSourceTestDownload(t *testing.T, d *SourceTestData, e *SourceTestExpect, source string, downloadTest []SourceTestState) {
func prepSourceTestDownload(
_ *testing.T,
d *SourceTestData,
e *SourceTestExpect,
source string,
downloadTest []SourceTestState,
) {
if len(downloadTest) == 0 {
return
}
for _, state := range downloadTest {
path := "/" + strconv.FormatUint(uint64(state), 10) + "/" + source
serverURL := d.server.URL
switch state {
case TestStateMissing, TestStateMissingSig:
e.err = "404 Not Found"
@ -308,22 +316,30 @@ func prepSourceTestDownload(t *testing.T, d *SourceTestData, e *SourceTestExpect
case TestStateReadErr, TestStateReadSigErr:
e.err = "unexpected EOF"
case TestStateOpenErr, TestStateOpenSigErr:
path = "00000" + path // high numeric port is parsed but then fails to connect
if u, err := url.Parse(serverURL + path); err == nil {
host, port := ExtractHostAndPort(u.Host, -1)
u.Host = fmt.Sprintf(
"%s:%d",
host,
port|0x10000,
) // high numeric port is parsed but then fails to connect
serverURL = u.String()
}
e.err = "invalid port"
case TestStatePathErr:
path = "..." + path // non-numeric port fails URL parsing
}
if u, err := url.Parse(d.server.URL + path); err == nil {
if u, err := url.Parse(serverURL + path); err == nil {
e.Source.urls = append(e.Source.urls, u)
}
e.urls = append(e.urls, d.server.URL+path)
e.urls = append(e.urls, serverURL+path)
if e.success {
continue
}
switch state {
case TestStateCorrect:
e.cache = []SourceFixture{d.fixtures[state][source], d.fixtures[state][source+".minisig"]}
e.Source.in, e.success = e.cache[0].content, true
e.Source.bin, e.success = e.cache[0].content, true
fallthrough
case TestStateMissingSig, TestStatePartial, TestStatePartialSig, TestStateReadSigErr:
d.reqExpect[path+".minisig"]++
@ -346,14 +362,17 @@ func prepSourceTestDownload(t *testing.T, d *SourceTestData, e *SourceTestExpect
}
func setupSourceTestCase(t *testing.T, d *SourceTestData, i int,
cacheTest *SourceTestState, downloadTest []SourceTestState) (id string, e *SourceTestExpect) {
cacheTest *SourceTestState, downloadTest []SourceTestState,
) (id string, e *SourceTestExpect) {
id = strconv.Itoa(d.n) + "-" + strconv.Itoa(i)
e = &SourceTestExpect{
cachePath: filepath.Join(d.tempDir, id),
mtime: d.timeNow,
}
e.Source = &Source{name: id, urls: []*url.URL{}, format: SourceFormatV2, minisignKey: d.key,
cacheFile: e.cachePath, cacheTTL: DefaultPrefetchDelay * 3, prefetchDelay: DefaultPrefetchDelay}
e.Source = &Source{
name: id, urls: []*url.URL{}, format: SourceFormatV2, minisignKey: d.key,
cacheFile: e.cachePath, cacheTTL: DefaultPrefetchDelay * 3, prefetchDelay: DefaultPrefetchDelay,
}
if cacheTest != nil {
prepSourceTestCache(t, d, e, d.sources[i], *cacheTest)
i = (i + 1) % len(d.sources) // make the cached and downloaded fixtures different
@ -363,6 +382,10 @@ func setupSourceTestCase(t *testing.T, d *SourceTestData, i int,
}
func TestNewSource(t *testing.T) {
if testing.Verbose() {
dlog.SetLogLevel(dlog.SeverityDebug)
dlog.UseSyslog(false)
}
teardown, d := setupSourceTest(t)
defer teardown()
checkResult := func(t *testing.T, e *SourceTestExpect, got *Source, err error) {
@ -382,12 +405,21 @@ func TestNewSource(t *testing.T) {
refreshDelay time.Duration
e *SourceTestExpect
}{
{"", "", 0, &SourceTestExpect{err: " ", Source: &Source{name: "short refresh delay", urls: []*url.URL{}, cacheTTL: DefaultPrefetchDelay, prefetchDelay: DefaultPrefetchDelay}}},
{"", "", 0, &SourceTestExpect{err: " ", Source: &Source{name: "short refresh delay", urls: []*url.URL{}, cacheTTL: DefaultPrefetchDelay, prefetchDelay: DefaultPrefetchDelay, prefix: ""}}},
{"v1", d.keyStr, DefaultPrefetchDelay * 2, &SourceTestExpect{err: "Unsupported source format", Source: &Source{name: "old format", urls: []*url.URL{}, cacheTTL: DefaultPrefetchDelay * 2, prefetchDelay: DefaultPrefetchDelay}}},
{"v2", "", DefaultPrefetchDelay * 3, &SourceTestExpect{err: "Invalid encoded public key", Source: &Source{name: "invalid public key", urls: []*url.URL{}, cacheTTL: DefaultPrefetchDelay * 3, prefetchDelay: DefaultPrefetchDelay}}},
} {
t.Run(tt.e.Source.name, func(t *testing.T) {
got, err := NewSource(tt.e.Source.name, d.xTransport, tt.e.urls, tt.key, tt.e.cachePath, tt.v, tt.refreshDelay)
got, err := NewSource(
tt.e.Source.name,
d.xTransport,
tt.e.urls,
tt.key,
tt.e.cachePath,
tt.v,
tt.refreshDelay,
tt.e.prefix,
)
checkResult(t, tt.e, got, err)
})
}
@ -397,7 +429,16 @@ func TestNewSource(t *testing.T) {
for i := range d.sources {
id, e := setupSourceTestCase(t, d, i, &cacheTest, downloadTest)
t.Run("cache "+cacheTestName+", download "+downloadTestName+"/"+id, func(t *testing.T) {
got, err := NewSource(id, d.xTransport, e.urls, d.keyStr, e.cachePath, "v2", DefaultPrefetchDelay*3)
got, err := NewSource(
id,
d.xTransport,
e.urls,
d.keyStr,
e.cachePath,
"v2",
DefaultPrefetchDelay*3,
"",
)
checkResult(t, e, got, err)
})
}
@ -406,6 +447,10 @@ func TestNewSource(t *testing.T) {
}
func TestPrefetchSources(t *testing.T) {
if testing.Verbose() {
dlog.SetLogLevel(dlog.SeverityDebug)
dlog.UseSyslog(false)
}
teardown, d := setupSourceTest(t)
defer teardown()
checkResult := func(t *testing.T, expects []*SourceTestExpect, got time.Duration) {
@ -432,7 +477,7 @@ func TestPrefetchSources(t *testing.T) {
e.mtime = d.timeUpd
s := &Source{}
*s = *e.Source
s.in = nil
s.bin = nil
sources = append(sources, s)
expects = append(expects, e)
}

View File

@ -0,0 +1 @@
checks = ["all", "-ST1005"]

View File

@ -1,5 +1,5 @@
package main
func (proxy *Proxy) SystemDListeners() error {
func (proxy *Proxy) addSystemDListeners() error {
return nil
}

View File

@ -1,7 +1,8 @@
//go:build !linux
// +build !linux
package main
func (proxy *Proxy) SystemDListeners() error {
func (proxy *Proxy) addSystemDListeners() error {
return nil
}

View File

@ -1,40 +1,35 @@
//go:build !android
// +build !android
package main
import (
"fmt"
"net"
"github.com/coreos/go-systemd/activation"
"github.com/jedisct1/dlog"
)
func (proxy *Proxy) SystemDListeners() error {
func (proxy *Proxy) addSystemDListeners() error {
files := activation.Files(true)
if len(files) > 0 {
if len(proxy.userName) > 0 || proxy.child {
dlog.Fatal("Systemd activated sockets are incompatible with privilege dropping. Remove activated sockets and fill `listen_addresses` in the dnscrypt-proxy configuration file instead.")
dlog.Fatal(
"Systemd activated sockets are incompatible with privilege dropping. Remove activated sockets and fill `listen_addresses` in the dnscrypt-proxy configuration file instead.",
)
}
dlog.Warn("Systemd sockets are untested and unsupported - use at your own risk")
}
for i, file := range files {
defer file.Close()
ok := false
if listener, err := net.FileListener(file); err == nil {
proxy.registerTCPListener(listener.(*net.TCPListener))
dlog.Noticef("Wiring systemd TCP socket #%d, %s, %s", i, file.Name(), listener.Addr())
ok = true
go proxy.tcpListener(listener.(*net.TCPListener))
} else if pc, err := net.FilePacketConn(file); err == nil {
proxy.registerUDPListener(pc.(*net.UDPConn))
dlog.Noticef("Wiring systemd UDP socket #%d, %s, %s", i, file.Name(), pc.LocalAddr())
ok = true
go proxy.udpListener(pc.(*net.UDPConn))
}
if !ok {
return fmt.Errorf("Could not wire systemd socket #%d, %s", i, file.Name())
}
}
return nil
}

View File

@ -62,7 +62,15 @@ func parseTimeRanges(timeRangesStr []TimeRangeStr) ([]TimeRange, error) {
func parseWeeklyRanges(weeklyRangesStr WeeklyRangesStr) (WeeklyRanges, error) {
weeklyRanges := WeeklyRanges{}
weeklyRangesStrX := [7][]TimeRangeStr{weeklyRangesStr.Sun, weeklyRangesStr.Mon, weeklyRangesStr.Tue, weeklyRangesStr.Wed, weeklyRangesStr.Thu, weeklyRangesStr.Fri, weeklyRangesStr.Sat}
weeklyRangesStrX := [7][]TimeRangeStr{
weeklyRangesStr.Sun,
weeklyRangesStr.Mon,
weeklyRangesStr.Tue,
weeklyRangesStr.Wed,
weeklyRangesStr.Thu,
weeklyRangesStr.Fri,
weeklyRangesStr.Sat,
}
for day, weeklyRangeStrX := range weeklyRangesStrX {
timeRanges, err := parseTimeRanges(weeklyRangeStrX)
if err != nil {

View File

@ -0,0 +1,20 @@
package main
import (
"os/exec"
"strings"
"time"
)
func TimezoneSetup() error {
out, err := exec.Command("/system/bin/getprop", "persist.sys.timezone").Output()
if err != nil {
return err
}
z, err := time.LoadLocation(strings.TrimSpace(string(out)))
if err != nil {
return err
}
time.Local = z
return nil
}

View File

@ -0,0 +1,8 @@
//go:build !android
// +build !android
package main
func TimezoneSetup() error {
return nil
}

View File

@ -2,17 +2,20 @@ package main
import (
"bytes"
"compress/gzip"
"context"
"crypto/sha512"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"errors"
"io/ioutil"
"io"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
@ -21,17 +24,19 @@ import (
"github.com/jedisct1/dlog"
stamps "github.com/jedisct1/go-dnsstamps"
"github.com/miekg/dns"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"golang.org/x/net/http2"
netproxy "golang.org/x/net/proxy"
)
const (
DefaultFallbackResolver = "9.9.9.9:53"
DefaultKeepAlive = 5 * time.Second
DefaultTimeout = 30 * time.Second
SystemResolverIPTTL = 24 * time.Hour
MinResolverIPTTL = 12 * time.Hour
ExpiredCachedIPGraceTTL = 5 * time.Minute
DefaultBootstrapResolver = "9.9.9.9:53"
DefaultKeepAlive = 5 * time.Second
DefaultTimeout = 30 * time.Second
SystemResolverIPTTL = 24 * time.Hour
MinResolverIPTTL = 12 * time.Hour
ExpiredCachedIPGraceTTL = 15 * time.Minute
)
type CachedIPItem struct {
@ -44,37 +49,51 @@ type CachedIPs struct {
cache map[string]*CachedIPItem
}
type AltSupport struct {
sync.RWMutex
cache map[string]uint16
}
type XTransport struct {
transport *http.Transport
h3Transport *http3.RoundTripper
keepAlive time.Duration
timeout time.Duration
cachedIPs CachedIPs
fallbackResolver string
altSupport AltSupport
internalResolvers []string
bootstrapResolvers []string
mainProto string
ignoreSystemDNS bool
internalResolverReady bool
useIPv4 bool
useIPv6 bool
http3 bool
tlsDisableSessionTickets bool
tlsCipherSuite []uint16
proxyDialer *netproxy.Dialer
httpProxyFunction func(*http.Request) (*url.URL, error)
tlsClientCreds DOHClientCreds
keyLogWriter io.Writer
}
func NewXTransport() *XTransport {
if err := isIPAndPort(DefaultFallbackResolver); err != nil {
panic("DefaultFallbackResolver does not parse")
if err := isIPAndPort(DefaultBootstrapResolver); err != nil {
panic("DefaultBootstrapResolver does not parse")
}
xTransport := XTransport{
cachedIPs: CachedIPs{cache: make(map[string]*CachedIPItem)},
altSupport: AltSupport{cache: make(map[string]uint16)},
keepAlive: DefaultKeepAlive,
timeout: DefaultTimeout,
fallbackResolver: DefaultFallbackResolver,
bootstrapResolvers: []string{DefaultBootstrapResolver},
mainProto: "",
ignoreSystemDNS: true,
useIPv4: true,
useIPv6: false,
tlsDisableSessionTickets: false,
tlsCipherSuite: nil,
keyLogWriter: nil,
}
return &xTransport
}
@ -118,7 +137,7 @@ func (xTransport *XTransport) loadCachedIP(host string) (ip net.IP, expired bool
func (xTransport *XTransport) rebuildTransport() {
dlog.Debug("Rebuilding transport")
if xTransport.transport != nil {
(*xTransport.transport).CloseIdleConnections()
xTransport.transport.CloseIdleConnections()
}
timeout := xTransport.timeout
transport := &http.Transport{
@ -132,7 +151,7 @@ func (xTransport *XTransport) rebuildTransport() {
DialContext: func(ctx context.Context, network, addrStr string) (net.Conn, error) {
host, port := ExtractHostAndPort(addrStr, stamps.DefaultPort)
ipOnly := host
// resolveWithCache() is always called in `Fetch()` before the `Dial()`
// resolveAndUpdateCache() is always called in `Fetch()` before the `Dial()`
// method is used, so that a cached entry must be present at this point.
cachedIP, _ := xTransport.loadCachedIP(host)
if cachedIP != nil {
@ -142,7 +161,7 @@ func (xTransport *XTransport) rebuildTransport() {
ipOnly = "[" + cachedIP.String() + "]"
}
} else {
dlog.Debugf("[%s] IP address was not cached", host)
dlog.Debugf("[%s] IP address was not cached in DialContext", host)
}
addrStr = ipOnly + ":" + strconv.Itoa(port)
if xTransport.proxyDialer == nil {
@ -155,21 +174,128 @@ func (xTransport *XTransport) rebuildTransport() {
if xTransport.httpProxyFunction != nil {
transport.Proxy = xTransport.httpProxyFunction
}
if xTransport.tlsDisableSessionTickets || xTransport.tlsCipherSuite != nil {
tlsClientConfig := tls.Config{
SessionTicketsDisabled: xTransport.tlsDisableSessionTickets,
clientCreds := xTransport.tlsClientCreds
tlsClientConfig := tls.Config{}
certPool, certPoolErr := x509.SystemCertPool()
if xTransport.keyLogWriter != nil {
tlsClientConfig.KeyLogWriter = xTransport.keyLogWriter
}
if clientCreds.rootCA != "" {
if certPool == nil {
dlog.Fatalf("Additional CAs not supported on this platform: %v", certPoolErr)
}
additionalCaCert, err := os.ReadFile(clientCreds.rootCA)
if err != nil {
dlog.Fatal(err)
}
certPool.AppendCertsFromPEM(additionalCaCert)
}
if certPool != nil {
// Some operating systems don't include Let's Encrypt ISRG Root X1 certificate yet
letsEncryptX1Cert := []byte(`-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----`)
certPool.AppendCertsFromPEM(letsEncryptX1Cert)
tlsClientConfig.RootCAs = certPool
}
if clientCreds.clientCert != "" {
cert, err := tls.LoadX509KeyPair(clientCreds.clientCert, clientCreds.clientKey)
if err != nil {
dlog.Fatalf(
"Unable to use certificate [%v] (key: [%v]): %v",
clientCreds.clientCert,
clientCreds.clientKey,
err,
)
}
tlsClientConfig.Certificates = []tls.Certificate{cert}
}
if xTransport.tlsDisableSessionTickets || xTransport.tlsCipherSuite != nil {
tlsClientConfig.SessionTicketsDisabled = xTransport.tlsDisableSessionTickets
if !xTransport.tlsDisableSessionTickets {
tlsClientConfig.ClientSessionCache = tls.NewLRUClientSessionCache(10)
}
if xTransport.tlsCipherSuite != nil {
tlsClientConfig.PreferServerCipherSuites = false
tlsClientConfig.CipherSuites = xTransport.tlsCipherSuite
// Go doesn't allow changing the cipher suite with TLS 1.3
// So, check if the requested set of ciphers matches the TLS 1.3 suite.
// If it doesn't, downgrade to TLS 1.2
compatibleSuitesCount := 0
for _, suite := range tls.CipherSuites() {
if suite.Insecure {
continue
}
for _, supportedVersion := range suite.SupportedVersions {
if supportedVersion != tls.VersionTLS13 {
for _, expectedSuiteID := range xTransport.tlsCipherSuite {
if expectedSuiteID == suite.ID {
compatibleSuitesCount += 1
break
}
}
}
}
}
if compatibleSuitesCount != len(tls.CipherSuites()) {
dlog.Notice("Explicit cipher suite configured - downgrading to TLS 1.2")
tlsClientConfig.MaxVersion = tls.VersionTLS12
}
}
transport.TLSClientConfig = &tlsClientConfig
}
http2.ConfigureTransport(transport)
transport.TLSClientConfig = &tlsClientConfig
if http2Transport, err := http2.ConfigureTransports(transport); err != nil {
http2Transport.ReadIdleTimeout = timeout
http2Transport.AllowHTTP = false
}
xTransport.transport = transport
if xTransport.http3 {
dial := func(ctx context.Context, addrStr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
dlog.Debugf("Dialing for H3: [%v]", addrStr)
host, port := ExtractHostAndPort(addrStr, stamps.DefaultPort)
ipOnly := host
cachedIP, _ := xTransport.loadCachedIP(host)
network := "udp4"
if cachedIP != nil {
if ipv4 := cachedIP.To4(); ipv4 != nil {
ipOnly = ipv4.String()
} else {
ipOnly = "[" + cachedIP.String() + "]"
network = "udp6"
}
} else {
dlog.Debugf("[%s] IP address was not cached in H3 context", host)
if xTransport.useIPv6 {
if xTransport.useIPv4 {
network = "udp"
} else {
network = "udp6"
}
}
}
addrStr = ipOnly + ":" + strconv.Itoa(port)
udpAddr, err := net.ResolveUDPAddr(network, addrStr)
if err != nil {
return nil, err
}
udpConn, err := net.ListenUDP(network, nil)
if err != nil {
return nil, err
}
tlsCfg.ServerName = host
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
}
h3Transport := &http3.RoundTripper{DisableCompression: true, TLSClientConfig: &tlsClientConfig, Dial: dial}
xTransport.h3Transport = h3Transport
}
}
func (xTransport *XTransport) resolveUsingSystem(host string) (ip net.IP, ttl time.Duration, err error) {
@ -200,14 +326,17 @@ func (xTransport *XTransport) resolveUsingSystem(host string) (ip net.IP, ttl ti
return
}
func (xTransport *XTransport) resolveUsingResolver(proto, host string, resolver string) (ip net.IP, ttl time.Duration, err error) {
func (xTransport *XTransport) resolveUsingResolver(
proto, host string,
resolver string,
) (ip net.IP, ttl time.Duration, err error) {
dnsClient := dns.Client{Net: proto}
if xTransport.useIPv4 {
msg := new(dns.Msg)
msg := dns.Msg{}
msg.SetQuestion(dns.Fqdn(host), dns.TypeA)
msg.SetEdns0(uint16(MaxDNSPacketSize), true)
var in *dns.Msg
if in, _, err = dnsClient.Exchange(msg, resolver); err == nil {
if in, _, err = dnsClient.Exchange(&msg, resolver); err == nil {
answers := make([]dns.RR, 0)
for _, answer := range in.Answer {
if answer.Header().Rrtype == dns.TypeA {
@ -223,11 +352,11 @@ func (xTransport *XTransport) resolveUsingResolver(proto, host string, resolver
}
}
if xTransport.useIPv6 {
msg := new(dns.Msg)
msg := dns.Msg{}
msg.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)
msg.SetEdns0(uint16(MaxDNSPacketSize), true)
var in *dns.Msg
if in, _, err = dnsClient.Exchange(msg, resolver); err == nil {
if in, _, err = dnsClient.Exchange(&msg, resolver); err == nil {
answers := make([]dns.RR, 0)
for _, answer := range in.Answer {
if answer.Header().Rrtype == dns.TypeAAAA {
@ -245,42 +374,80 @@ func (xTransport *XTransport) resolveUsingResolver(proto, host string, resolver
return
}
// Return a cached entry, or resolve a name and update the cache
func (xTransport *XTransport) resolveWithCache(host string) (err error) {
func (xTransport *XTransport) resolveUsingResolvers(
proto, host string,
resolvers []string,
) (ip net.IP, ttl time.Duration, err error) {
err = errors.New("Empty resolvers")
for i, resolver := range resolvers {
ip, ttl, err = xTransport.resolveUsingResolver(proto, host, resolver)
if err == nil {
if i > 0 {
dlog.Infof("Resolution succeeded with resolver %s[%s]", proto, resolver)
resolvers[0], resolvers[i] = resolvers[i], resolvers[0]
}
break
}
dlog.Infof("Unable to resolve [%s] using resolver [%s] (%s): %v", host, resolver, proto, err)
}
return
}
// If a name is not present in the cache, resolve the name and update the cache
func (xTransport *XTransport) resolveAndUpdateCache(host string) error {
if xTransport.proxyDialer != nil || xTransport.httpProxyFunction != nil {
return
return nil
}
if ParseIP(host) != nil {
return
return nil
}
cachedIP, expired := xTransport.loadCachedIP(host)
if cachedIP != nil && !expired {
return
return nil
}
var foundIP net.IP
var ttl time.Duration
if !xTransport.ignoreSystemDNS {
foundIP, ttl, err = xTransport.resolveUsingSystem(host)
var err error
protos := []string{"udp", "tcp"}
if xTransport.mainProto == "tcp" {
protos = []string{"tcp", "udp"}
}
if xTransport.ignoreSystemDNS || err != nil {
protos := []string{"udp", "tcp"}
if xTransport.mainProto == "tcp" {
protos = []string{"tcp", "udp"}
if xTransport.ignoreSystemDNS {
if xTransport.internalResolverReady {
for _, proto := range protos {
foundIP, ttl, err = xTransport.resolveUsingResolvers(proto, host, xTransport.internalResolvers)
if err == nil {
break
}
}
} else {
err = errors.New("Service is not usable yet")
dlog.Notice(err)
}
} else {
foundIP, ttl, err = xTransport.resolveUsingSystem(host)
if err != nil {
err = errors.New("System DNS is not usable yet")
dlog.Notice(err)
}
}
if err != nil {
for _, proto := range protos {
if err != nil {
dlog.Noticef("System DNS configuration not usable yet, exceptionally resolving [%s] using resolver [%s] over %s", host, xTransport.fallbackResolver, proto)
} else {
dlog.Debugf("Resolving [%s] using resolver %s[%s]", host, proto, xTransport.fallbackResolver)
dlog.Noticef(
"Resolving server host [%s] using bootstrap resolvers over %s",
host,
proto,
)
}
foundIP, ttl, err = xTransport.resolveUsingResolver(proto, host, xTransport.fallbackResolver)
foundIP, ttl, err = xTransport.resolveUsingResolvers(proto, host, xTransport.bootstrapResolvers)
if err == nil {
break
}
}
}
if err != nil && xTransport.ignoreSystemDNS {
dlog.Noticef("Fallback resolver [%v] didn't respond - Trying with the system resolver as a last resort", xTransport.fallbackResolver)
dlog.Noticef("Bootstrap resolvers didn't respond - Trying with the system resolver as a last resort")
foundIP, ttl, err = xTransport.resolveUsingSystem(host)
}
if ttl < MinResolverIPTTL {
@ -288,22 +455,57 @@ func (xTransport *XTransport) resolveWithCache(host string) (err error) {
}
if err != nil {
if cachedIP != nil {
dlog.Noticef("Using stale [%v] cached address for a grace period", host)
foundIP = cachedIP
ttl = ExpiredCachedIPGraceTTL
} else {
return
return err
}
}
if foundIP == nil {
if !xTransport.useIPv4 && xTransport.useIPv6 {
dlog.Warnf("no IPv6 address found for [%s]", host)
} else if xTransport.useIPv4 && !xTransport.useIPv6 {
dlog.Warnf("no IPv4 address found for [%s]", host)
} else {
dlog.Errorf("no IP address found for [%s]", host)
}
}
xTransport.saveCachedIP(host, foundIP, ttl)
dlog.Debugf("[%s] IP address [%s] added to the cache, valid for %v", host, foundIP, ttl)
return
return nil
}
func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration, padding *string) (*http.Response, time.Duration, error) {
func (xTransport *XTransport) Fetch(
method string,
url *url.URL,
accept string,
contentType string,
body *[]byte,
timeout time.Duration,
compress bool,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
if timeout <= 0 {
timeout = xTransport.timeout
}
client := http.Client{Transport: xTransport.transport, Timeout: timeout}
client := http.Client{
Transport: xTransport.transport,
Timeout: timeout,
}
host, port := ExtractHostAndPort(url.Host, 443)
hasAltSupport := false
if xTransport.h3Transport != nil {
xTransport.altSupport.RLock()
var altPort uint16
altPort, hasAltSupport = xTransport.altSupport.cache[url.Host]
xTransport.altSupport.RUnlock()
if hasAltSupport {
if int(altPort) == port {
client.Transport = xTransport.h3Transport
dlog.Debugf("Using HTTP/3 transport for [%s]", url.Host)
}
}
}
header := map[string][]string{"User-Agent": {"dnscrypt-proxy"}}
if len(accept) > 0 {
header["Accept"] = []string{accept}
@ -311,9 +513,7 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
if len(contentType) > 0 {
header["Content-Type"] = []string{contentType}
}
if padding != nil {
header["X-Pad"] = []string{*padding}
}
header["Cache-Control"] = []string{"max-stale"}
if body != nil {
h := sha512.Sum512(*body)
qs := url.Query()
@ -322,13 +522,18 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
url2.RawQuery = qs.Encode()
url = &url2
}
host, _ := ExtractHostAndPort(url.Host, 0)
if xTransport.proxyDialer == nil && strings.HasSuffix(host, ".onion") {
return nil, 0, errors.New("Onion service is not reachable without Tor")
return nil, 0, nil, 0, errors.New("Onion service is not reachable without Tor")
}
if err := xTransport.resolveWithCache(host); err != nil {
dlog.Errorf("Unable to resolve [%v] - Make sure that the system resolver works, or that `fallback_resolver` has been set to a resolver that can be reached", host)
return nil, 0, err
if err := xTransport.resolveAndUpdateCache(host); err != nil {
dlog.Errorf(
"Unable to resolve [%v] - Make sure that the system resolver works, or that `bootstrap_resolvers` has been set to resolvers that can be reached",
host,
)
return nil, 0, nil, 0, err
}
if compress && body == nil {
header["Accept-Encoding"] = []string{"gzip"}
}
req := &http.Request{
Method: method,
@ -338,7 +543,7 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
}
if body != nil {
req.ContentLength = int64(len(*body))
req.Body = ioutil.NopCloser(bytes.NewReader(*body))
req.Body = io.NopCloser(bytes.NewReader(*body))
}
start := time.Now()
resp, err := client.Do(req)
@ -350,48 +555,128 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
err = errors.New(resp.Status)
}
} else {
(*xTransport.transport).CloseIdleConnections()
dlog.Debugf("HTTP client error: [%v] - closing idle connections", err)
xTransport.transport.CloseIdleConnections()
}
statusCode := 503
if resp != nil {
statusCode = resp.StatusCode
}
if err != nil {
dlog.Debugf("[%s]: [%s]", req.URL, err)
if xTransport.tlsCipherSuite != nil && strings.Contains(err.Error(), "handshake failure") {
dlog.Warnf("TLS handshake failure - Try changing or deleting the tls_cipher_suite value in the configuration file")
dlog.Warnf(
"TLS handshake failure - Try changing or deleting the tls_cipher_suite value in the configuration file",
)
xTransport.tlsCipherSuite = nil
xTransport.rebuildTransport()
}
return nil, statusCode, nil, rtt, err
}
return resp, rtt, err
if xTransport.h3Transport != nil && !hasAltSupport {
if alt, found := resp.Header["Alt-Svc"]; found {
dlog.Debugf("Alt-Svc [%s]: [%s]", url.Host, alt)
altPort := uint16(port & 0xffff)
for i, xalt := range alt {
for j, v := range strings.Split(xalt, ";") {
if i >= 8 || j >= 16 {
break
}
v = strings.TrimSpace(v)
if strings.HasPrefix(v, "h3=\":") {
v = strings.TrimPrefix(v, "h3=\":")
v = strings.TrimSuffix(v, "\"")
if xAltPort, err := strconv.ParseUint(v, 10, 16); err == nil && xAltPort <= 65535 {
altPort = uint16(xAltPort)
dlog.Debugf("Using HTTP/3 for [%s]", url.Host)
break
}
}
}
}
xTransport.altSupport.Lock()
xTransport.altSupport.cache[url.Host] = altPort
dlog.Debugf("Caching altPort for [%v]", url.Host)
xTransport.altSupport.Unlock()
}
}
tls := resp.TLS
var bodyReader io.ReadCloser = resp.Body
if compress && resp.Header.Get("Content-Encoding") == "gzip" {
bodyReader, err = gzip.NewReader(io.LimitReader(resp.Body, MaxHTTPBodyLength))
if err != nil {
return nil, statusCode, tls, rtt, err
}
defer bodyReader.Close()
}
bin, err := io.ReadAll(io.LimitReader(bodyReader, MaxHTTPBodyLength))
if err != nil {
return nil, statusCode, tls, rtt, err
}
resp.Body.Close()
return bin, statusCode, tls, rtt, err
}
func (xTransport *XTransport) Get(url *url.URL, accept string, timeout time.Duration) (*http.Response, time.Duration, error) {
return xTransport.Fetch("GET", url, accept, "", nil, timeout, nil)
func (xTransport *XTransport) GetWithCompression(
url *url.URL,
accept string,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("GET", url, accept, "", nil, timeout, true)
}
func (xTransport *XTransport) Post(url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration, padding *string) (*http.Response, time.Duration, error) {
return xTransport.Fetch("POST", url, accept, contentType, body, timeout, padding)
func (xTransport *XTransport) Get(
url *url.URL,
accept string,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("GET", url, accept, "", nil, timeout, false)
}
func (xTransport *XTransport) DoHQuery(useGet bool, url *url.URL, body []byte, timeout time.Duration) (*http.Response, time.Duration, error) {
padLen := 63 - (len(body)+63)&63
padding := xTransport.makePad(padLen)
dataType := "application/dns-message"
func (xTransport *XTransport) Post(
url *url.URL,
accept string,
contentType string,
body *[]byte,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("POST", url, accept, contentType, body, timeout, false)
}
func (xTransport *XTransport) dohLikeQuery(
dataType string,
useGet bool,
url *url.URL,
body []byte,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
if useGet {
qs := url.Query()
qs.Add("ct", "")
encBody := base64.RawURLEncoding.EncodeToString(body)
qs.Add("dns", encBody)
url2 := *url
url2.RawQuery = qs.Encode()
return xTransport.Get(&url2, dataType, timeout)
}
return xTransport.Post(url, dataType, dataType, &body, timeout, padding)
return xTransport.Post(url, dataType, dataType, &body, timeout)
}
func (xTransport *XTransport) makePad(padLen int) *string {
if padLen <= 0 {
return nil
}
padding := strings.Repeat("X", padLen)
return &padding
func (xTransport *XTransport) DoHQuery(
useGet bool,
url *url.URL,
body []byte,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.dohLikeQuery("application/dns-message", useGet, url, body, timeout)
}
func (xTransport *XTransport) ObliviousDoHQuery(
useGet bool,
url *url.URL,
body []byte,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.dohLikeQuery("application/oblivious-dns-message", useGet, url, body, timeout)
}

Some files were not shown because too many files have changed in this diff Show More