Compare commits

...

741 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
1693 changed files with 296786 additions and 75079 deletions

View File

@ -1,21 +0,0 @@
##################
# Test blacklist #
##################
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

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

View File

@ -2,7 +2,7 @@
PACKAGE_VERSION="$1"
cd dnscrypt-proxy
cd dnscrypt-proxy || exit 1
go clean
env GOOS=windows GOARCH=386 go build -mod vendor -ldflags="-s -w"
@ -23,7 +23,7 @@ ln ../windows/* win64/
zip -9 -r dnscrypt-proxy-win64-${PACKAGE_VERSION:-dev}.zip win64
go clean
env GO386=387 GOOS=openbsd GOARCH=386 go build -mod vendor -ldflags="-s -w"
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/
@ -141,12 +141,26 @@ 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
ln dnscrypt-proxy macos/
ln ../LICENSE example-dnscrypt-proxy.toml localhost.pem example-*.txt macos/
tar czpvf dnscrypt-proxy-macos-${PACKAGE_VERSION:-dev}.tar.gz macos
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

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

View File

@ -20,7 +20,7 @@ section() {
true
}
rm -f blocked.log ip-blocked.log query.log nx.log whitelisted.log
rm -f blocked-names.log blocked-ips.log query.log nx.log allowed-names.log
t || (
cd ../dnscrypt-proxy
@ -66,10 +66,15 @@ t || dig -p${DNS_PORT} +dnssec darpa.mil @127.0.0.1 2>&1 | grep -Fvq 'RRSIG' ||
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 cloaked.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
t || dig -p${DNS_PORT} +short www.cloaked2.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
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
@ -82,7 +87,7 @@ t || dig -p${DNS_PORT} tracker.xdebian.org @127.0.0.1 | grep -Fq 'locally blocke
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 '404 Not Found' || fail
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 |
@ -94,14 +99,14 @@ kill $(cat /tmp/dnscrypt-proxy.pidfile)
sleep 5
section
t || grep -Fq 'telemetry.example' blocked.log || fail
t || grep -Fq 'telemetry.*' blocked.log || fail
t || grep -Fq 'tracker.xdebian.org' blocked.log || fail
t || grep -Fq 'tracker.*' blocked.log || fail
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' ip-blocked.log || fail
t || grep -Fq '8.8.8.8' ip-blocked.log || fail
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
@ -117,18 +122,41 @@ 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 'cloaked.com.*CLOAK' query.log || fail
t || grep -Eq 'www.cloaked2.com.*CLOAK' 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' whitelisted.log || fail
t || grep -Fq '*.tracker.debian' whitelisted.log || fail
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

View File

@ -1,3 +1,5 @@
cloaked.* one.one.one.one
*.cloaked2.* one.one.one.one # inline comment
=www.dnscrypt-test 192.168.100.100
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

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

@ -10,6 +10,7 @@ block_unqualified = true
block_undelegated = true
forwarding_rules = 'forwarding-rules.txt'
cloaking_rules = 'cloaking-rules.txt'
cloak_ptr = true
cache = true
[local_doh]
@ -23,17 +24,17 @@ file = 'query.log'
[nx_log]
file = 'nx.log'
[blacklist]
blacklist_file = 'blacklist.txt'
log_file = 'blocked.log'
[blocked_names]
blocked_names_file = 'blocked-names.txt'
log_file = 'blocked-names.log'
[ip_blacklist]
blacklist_file = 'ip-blacklist.txt'
log_file = 'ip-blocked.log'
[blocked_ips]
blocked_ips_file = 'blocked-ips.txt'
log_file = 'blocked-ips.log'
[whitelist]
whitelist_file = 'whitelist.txt'
log_file = 'whitelisted.log'
[allowed_names]
allowed_names_file = 'allowed-names.txt'
log_file = 'allowed-names.log'
[schedules]

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'

View File

@ -1,38 +0,0 @@
---
name: "\U0001F41E Bug report"
about: Create a report about a reproducible bug
title: ''
labels: ''
assignees: ''
---
<!-- Thanks for the bug report! -->
## *Who* is the bug affecting?
## *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

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

View File

@ -1,42 +0,0 @@
---
name: "\U0001F389 Feature request"
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
## Is your feature request related to a problem?
<!-- A clear and concise description of what the problem is. -->
## Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
## Alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Who will that feature be useful to?
<!-- Is it solving a common problem, or is it an improvement that all current users will benefit from? -->
## What have you done already?
<!-- If you already built a prototype or a complete implementation of that feature, where is it possible to see it? -->
## What are you going to contribute?
<!-- Besides testing volunteering work made by other people, what are you going to contribute in order to help have that feature implemented, tested and documented? -->
## Additional context
<!-- Add any other context about the feature request here. -->

View File

@ -1,24 +0,0 @@
---
name: "❓ Question"
about: Ask a question about dnscrypt-proxy or the DNSCrypt protocol
title: ''
labels: ''
assignees: ''
---
## Subject
<!-- Select one: -->
* [ ] I'd like to contribute to the project (code, documentation, website, advocacy, integration, ...)
* [ ] I want to run my own DNSCrypt and/or DoH server
* [ ] I'd like to have my existing server added to a public list of encrypted DNS servers
* [ ] I'd like to announce a new application/website/article related to the project
* [ ] I have questions about the DNSCrypt or Anonymized DNSCrypt protocols
* [ ] I am a MacOS developer and I'd like to contribute a GUI for dnscrypt-proxy
* [ ] I am an Android developer and I'd like to contribute an Android app
* [ ] I am a technical writer and I'd like to help write an IETF Internet Draft for the DNSCrypt protocol
* [ ] Other
## Description

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

View File

@ -1,6 +1,22 @@
on: push
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: CI and optionally publish
name: GitHub CI
jobs:
build:
@ -9,18 +25,18 @@ jobs:
steps:
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Set up Go 1.14
uses: actions/setup-go@v1
- name: Check out code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.14
go-version: 1
check-latest: true
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test suite
run: |
go version
@ -33,6 +49,11 @@ jobs:
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: |
@ -62,7 +83,7 @@ jobs:
prerelease: false
- name: Upload release assets
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -71,3 +92,4 @@ jobs:
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,231 +0,0 @@
dist: bionic
language: go
os:
- linux
go:
- 1.x
addons:
apt:
update: true
script:
- 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 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-${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 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-${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 localhost.pem 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 localhost.pem 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 localhost.pem 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 localhost.pem example-*.txt freebsd-amd64/
- tar czpvf dnscrypt-proxy-freebsd_amd64-${TRAVIS_TAG:-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-${TRAVIS_TAG:-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-${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 localhost.pem 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 localhost.pem 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 localhost.pem 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 localhost.pem 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 localhost.pem 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 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-${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 localhost.pem 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 localhost.pem 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 localhost.pem 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 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-${TRAVIS_TAG:-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-${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 localhost.pem example-*.txt macos/
- tar czpvf dnscrypt-proxy-macos-${TRAVIS_TAG:-dev}.tar.gz macos
- 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-${TRAVIS_TAG:-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-${TRAVIS_TAG:-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-${TRAVIS_TAG:-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-${TRAVIS_TAG:-dev}.zip android-x86_64
after_success:
- ls -l dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip
deploy:
provider: releases
token:
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
on:
repo: DNSCrypt/dnscrypt-proxy
tags: true
before_deploy:
- sudo apt-get -y install libsodium-dev
- git clone --depth 1 https://github.com/jedisct1/minisign.git
- cd minisign/src
- mkdir -p /tmp/bin
- gcc -O2 -o /tmp/bin/minisign -D_GNU_SOURCE *.c -lsodium
- cd -
- /tmp/bin/minisign -v
- echo '#' > /tmp/minisign.key
- echo "$MINISIGN_SK" >> /tmp/minisign.key
- echo | /tmp/bin/minisign -s /tmp/minisign.key -Sm dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip
before_install:
- gimme --list
- echo $TRAVIS_GO_VERSION
- cd .ci
- ./ci-test.sh || travis_terminate 1
- cd -
- NDK_VER=r20
- curl -LO 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}
- PATH=$PATH:$NDK_TOOLS/toolchains/llvm/prebuilt/linux-x86_64/bin
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=

269
ChangeLog
View File

@ -1,4 +1,191 @@
* Version 2.0.42
# 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)
@ -14,7 +201,7 @@ using them without a relay.
more retries if necessary.
- Continuous integration has been moved to GitHub Actions.
* Version 2.0.41
# 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`
@ -25,7 +212,7 @@ 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
# 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.
@ -37,11 +224,11 @@ in the middle.
- The forwarding plugin is now more reliable, and handles retries over
TCP.
* Version 2.0.39
# 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
# Version 2.0.38
- Entries from lists (forwarding, blacklists, whitelists) now support
inline comments.
- Reliability improvement: queries over UDP are retried after a timeout
@ -58,7 +245,7 @@ stored separately from the application.
built using Go 1.13.7 that fixes a TLS certificate parsing issue present in
previous versions of the compiler.
* Version 2.0.36
# 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
@ -69,7 +256,7 @@ set in a question, and the `AD` bit is cleared.
security issue affecting non-encrypted/non-authenticated DNS traffic. In
`dnscrypt-proxy`, this only affects the forwarding feature.
* Version 2.0.35
# 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
@ -79,17 +266,17 @@ 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
# 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
# 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
@ -113,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)
@ -142,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
@ -155,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
@ -182,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
@ -209,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.
@ -218,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,
@ -240,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.
@ -249,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.
@ -258,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.
@ -267,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.
@ -279,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.
@ -288,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.
@ -306,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
@ -314,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
@ -346,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
@ -360,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-2020
* 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

@ -2,15 +2,18 @@
[![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/workflows/CI%20and%20optionally%20publish/badge.svg)](https://github.com/DNSCrypt/dnscrypt-proxy/actions)
[![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) ← 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)
@ -22,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
@ -35,7 +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 ESNI
* Includes a local DoH server in order to support ECH (ESNI)
## Pre-built binaries
@ -57,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
@ -71,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>

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
}
@ -152,7 +144,7 @@ func TrimAndStripInlineComments(str string) string {
str = str[:idx-1]
}
}
return strings.TrimFunc(str, unicode.IsSpace)
return strings.TrimSpace(str)
}
func ExtractHostAndPort(str string, defaultPort int) (host string, port int) {
@ -166,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"
@ -29,25 +30,27 @@ const (
type Config struct {
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"`
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"`
BlockUnqualified bool `toml:"block_unqualified"`
BlockUndelegated bool `toml:"block_undelegated"`
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"`
@ -59,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"`
@ -72,11 +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"`
FallbackResolvers []string `toml:"fallback_resolvers"`
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"`
@ -84,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"`
@ -91,18 +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"`
TLSClientAuth TLSClientAuthConfig `toml:"tls_client_auth"`
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,
@ -120,14 +137,16 @@ func newConfig() Config {
SourceIPv6: false,
SourceDNSCrypt: true,
SourceDoH: true,
SourceODoH: false,
MaxClients: 250,
FallbackResolvers: []string{DefaultFallbackResolver},
BootstrapResolvers: []string{DefaultBootstrapResolver},
IgnoreSystemDNS: false,
LogMaxSize: 10,
LogMaxAge: 7,
LogMaxBackups: 1,
TLSDisableSessionTickets: false,
TLSCipherSuite: nil,
TLSKeyLogFile: "",
NetprobeTimeout: 60,
OfflineMode: false,
RefusedCodeInResponses: false,
@ -136,10 +155,13 @@ func newConfig() Config {
BrokenImplementations: BrokenImplementationsConfig{
FragmentsBlocked: []string{
"cisco", "cisco-ipv6", "cisco-familyshield", "cisco-familyshield-ipv6",
"quad9-dnscrypt-ip4-filter-alt", "quad9-dnscrypt-ip4-filter-pri", "quad9-dnscrypt-ip4-nofilter-alt", "quad9-dnscrypt-ip4-nofilter-pri", "quad9-dnscrypt-ip6-filter-alt", "quad9-dnscrypt-ip6-filter-pri", "quad9-dnscrypt-ip6-nofilter-alt", "quad9-dnscrypt-ip6-nofilter-pri",
"cleanbrowsing-adult", "cleanbrowsing-family-ipv6", "cleanbrowsing-family", "cleanbrowsing-security",
"cleanbrowsing-adult", "cleanbrowsing-adult-ipv6", "cleanbrowsing-family", "cleanbrowsing-family-ipv6", "cleanbrowsing-security", "cleanbrowsing-security-ipv6",
},
},
AnonymizedDNS: AnonymizedDNSConfig{
DirectCertFallback: true,
},
CloakedPTR: false,
}
}
@ -169,31 +191,56 @@ 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"`
SkipIncompatible bool `toml:"skip_incompatible"`
Routes []AnonymizedDNSRouteConfig `toml:"routes"`
SkipIncompatible bool `toml:"skip_incompatible"`
DirectCertFallback bool `toml:"direct_cert_fallback"`
}
type BrokenImplementationsConfig struct {
@ -214,7 +261,7 @@ 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"`
@ -225,15 +272,27 @@ type TLSClientAuthCredsConfig struct {
ServerName string `toml:"server_name"`
ClientCert string `toml:"client_cert"`
ClientKey string `toml:"client_key"`
RootCA string `toml:"root_ca"`
}
type TLSClientAuthConfig struct {
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
IncludeRelays *bool
JSONOutput *bool
Check *bool
ConfigFile *string
@ -262,17 +321,27 @@ 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
}
@ -282,17 +351,29 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
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
@ -304,25 +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 {
config.FallbackResolvers = []string{config.FallbackResolver}
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.FallbackResolvers) > 0 {
for _, resolver := range config.FallbackResolvers {
if len(config.BootstrapResolvers) > 0 {
for _, resolver := range config.BootstrapResolvers {
if err := isIPAndPort(resolver); err != nil {
dlog.Fatalf("Fallback resolver [%v]: %v", resolver, err)
return fmt.Errorf("Bootstrap resolver [%v]: %v", resolver, err)
}
}
proxy.xTransport.ignoreSystemDNS = config.IgnoreSystemDNS
}
proxy.xTransport.fallbackResolvers = config.FallbackResolvers
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)
}
@ -330,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"
@ -357,6 +440,7 @@ 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
@ -395,12 +479,11 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.listenAddresses = config.ListenAddresses
proxy.localDoHListenAddresses = config.LocalDoH.ListenAddresses
if len(config.LocalDoH.Path) > 0 && config.LocalDoH.Path[0] != '/' {
dlog.Fatalf("local DoH: [%s] cannot be a valid URL path. Read the documentation", config.LocalDoH.Path)
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.daemonize = config.Daemonize
proxy.pluginBlockIPv6 = config.BlockIPv6
proxy.pluginBlockUnqualified = config.BlockUnqualified
proxy.pluginBlockUndelegated = config.BlockUndelegated
@ -419,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 {
@ -445,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 {
@ -457,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 {
@ -481,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 {
@ -497,23 +632,46 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
}
proxy.routes = &routes
}
proxy.skipAnonIncompatbibleResolvers = config.AnonymizedDNS.SkipIncompatible
proxy.skipAnonIncompatibleResolvers = config.AnonymizedDNS.SkipIncompatible
proxy.anonDirectCertFallback = config.AnonymizedDNS.DirectCertFallback
configClientCreds := config.TLSClientAuth.Creds
creds := make(map[string]DOHClientCreds)
for _, configClientCred := range configClientCreds {
credFiles := DOHClientCreds{
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,
}
creds[configClientCred.ServerName] = credFiles
proxy.xTransport.rebuildTransport()
}
proxy.dohCreds = &creds
// Backwards compatibility
config.BrokenImplementations.FragmentsBlocked = append(config.BrokenImplementations.FragmentsBlocked, config.BrokenImplementations.BrokenQueryPadding...)
config.BrokenImplementations.FragmentsBlocked = append(
config.BrokenImplementations.FragmentsBlocked,
config.BrokenImplementations.BrokenQueryPadding...)
proxy.serversBlockingFragments = config.BrokenImplementations.BrokenQueryPadding
proxy.serversBlockingFragments = config.BrokenImplementations.FragmentsBlocked
proxy.dns64Prefixes = config.DNS64.Prefixes
proxy.dns64Resolvers = config.DNS64.Resolvers
if *flags.ListAll {
config.ServerNames = nil
@ -525,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 {
@ -536,16 +714,29 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
netprobeAddress := DefaultNetprobeAddress
if len(config.NetprobeAddress) > 0 {
netprobeAddress = config.NetprobeAddress
} else if len(config.FallbackResolvers) > 0 {
netprobeAddress = config.FallbackResolvers[0]
} 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 {
@ -556,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)
}
@ -586,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)
@ -602,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,
@ -623,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)
@ -656,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)
@ -691,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
}
@ -761,7 +978,10 @@ func cdFileDir(fileName string) error {
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)
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)
}

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 {
@ -83,14 +103,15 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
} else {
var xpad [1]byte
rand.Read(xpad[:])
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 proto == "udp" && serverInfo.knownBugs.fragmentsBlocked {
if serverInfo.knownBugs.fragmentsBlocked && proto == "udp" {
paddedLength = MaxDNSUDPSafePacketSize
}
if serverInfo.RelayUDPAddr != nil && proto == "tcp" {
} 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,12 +20,22 @@ 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, knownBugs ServerBugs) (CertInfo, int, bool, 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, false, errors.New("Invalid public key length")
}
if !strings.HasSuffix(providerName, ".") {
providerName = providerName + "."
providerName += "."
}
if serverName == nil {
serverName = &providerName
@ -34,14 +43,30 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
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
}
}
tryFragmentsSupport := true
if knownBugs.fragmentsBlocked {
tryFragmentsSupport = false
}
in, rtt, fragmentsBlocked, err := dnsExchange(proxy, proto, &query, serverAddress, relayUDPAddr, relayTCPAddr, serverName, tryFragmentsSupport)
in, rtt, fragmentsBlocked, err := DNSExchange(
proxy,
proto,
&query,
serverAddress,
relay,
serverName,
tryFragmentsSupport,
)
if err != nil {
dlog.Noticef("[%s] TIMEOUT", *serverName)
return CertInfo{}, 0, fragmentsBlocked, err
@ -58,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
@ -92,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 {
@ -107,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
}
}
@ -143,224 +183,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
certCountStr = " - additional certificate"
}
if certInfo.CryptoConstruction == UndefinedConstruction {
return certInfo, 0, fragmentsBlocked, errors.New("No useable certificate found")
return certInfo, 0, fragmentsBlocked, errors.New("No usable certificate found")
}
return certInfo, int(rtt.Nanoseconds() / 1000000), fragmentsBlocked, 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
}
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, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr, serverName *string, tryFragmentsSupport bool) (*dns.Msg, time.Duration, bool, error) {
for {
cancelChannel := make(chan struct{})
channel := make(chan dnsExchangeResponse)
var err error
options := 0
for tries := 0; tries < 3; tries++ {
if tryFragmentsSupport {
queryCopy := query.Copy()
queryCopy.Id += uint16(options)
go func(query *dns.Msg, delay time.Duration) {
option := _dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr, 1500)
option.fragmentsBlocked = false
option.priority = 0
channel <- option
time.Sleep(delay)
select {
case <-cancelChannel:
return
default:
}
}(queryCopy, time.Duration(200*tries)*time.Millisecond)
options++
}
queryCopy := query.Copy()
queryCopy.Id += uint16(options)
go func(query *dns.Msg, delay time.Duration) {
option := _dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr, 480)
option.fragmentsBlocked = true
option.priority = 1
channel <- option
time.Sleep(delay)
select {
case <-cancelChannel:
return
default:
}
}(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("Certificate retrieval for [%v] succeeded but server is blocking fragments", *serverName)
} else {
dlog.Debugf("Certificate retrieval for [%v] succeeded", *serverName)
}
return bestOption.response, bestOption.rtt, bestOption.fragmentsBlocked, nil
}
if relayUDPAddr == nil {
if err == nil {
err = errors.New("Unable to reach the server")
}
return nil, 0, false, err
}
dlog.Infof("Unable to get a certificate for [%v] via relay [%v], retrying over a direct connection", *serverName, relayUDPAddr.IP)
relayUDPAddr, relayTCPAddr = nil, nil
}
}
func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr, 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 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 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 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 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

@ -8,6 +8,7 @@ import (
"time"
"unicode/utf8"
"github.com/jedisct1/dlog"
"github.com/miekg/dns"
)
@ -39,6 +40,11 @@ func TruncatedResponse(packet []byte) ([]byte, error) {
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 {
@ -57,6 +63,7 @@ func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ipv4 net.IP,
if rr.A != nil {
dstMsg.Answer = []dns.RR{rr}
sendHInfoResponse = false
ede.InfoCode = dns.ExtendedErrorCodeForgedAnswer
}
} else if ipv6 != nil && question.Qtype == dns.TypeAAAA {
rr := new(dns.AAAA)
@ -65,18 +72,24 @@ func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ipv4 net.IP,
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: 1}
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
}
@ -134,7 +147,8 @@ func NormalizeQName(str string) (string, error) {
}
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
@ -195,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
@ -256,3 +273,238 @@ func removeEDNS0Options(msg *dns.Msg) bool {
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.* # inline comments are allowed after a pound sign
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

@ -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

@ -35,6 +35,9 @@
## 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']
@ -52,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
@ -66,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
@ -75,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
@ -91,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.
@ -112,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
@ -140,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
@ -167,44 +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 resolvers
## These are normal, non-encrypted DNS resolvers, 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 these resolvers,
## and they will not be used after IP addresses of resolvers URLs have been found.
## They will never be used if lists have already been cached, and if stamps
## don't include host names without IP addresses.
## They will not be used if the configured system DNS works.
## Resolvers supporting DNSSEC are 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_resolvers = ['9.9.9.9:53', '8.8.8.8: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
@ -244,7 +320,7 @@ netprobe_address = '9.9.9.9:53'
## encrypted-dns-server can be configured to use this for access control
## in the [access_control] section
# query_meta = ["key1:value1", "key2:value2", "token:MySecretToken"]
# query_meta = ['key1:value1', 'key2:value2', 'token:MySecretToken']
## Automatic log files rotation
@ -266,7 +342,7 @@ log_files_max_backups = 1
## 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 blacklists).
## below and blocklists).
## You can still choose resolvers that do DNSSEC validation.
@ -278,6 +354,7 @@ block_ipv6 = false
## Immediately respond to A and AAAA queries for host names without a domain name
## This also prevents "dotless domain names" from being resolved upstream.
block_unqualified = true
@ -289,9 +366,9 @@ block_undelegated = true
## TTL for synthetic responses sent when a request has been blocked (due to
## IPv6 or blacklists).
## IPv6 or blocklists).
reject_ttl = 600
reject_ttl = 10
@ -312,6 +389,8 @@ 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.
##
## See the `example-cloaking-rules.txt` file for an example
@ -320,6 +399,8 @@ reject_ttl = 600
## TTL used when serving entries in cloaking-rules.txt
# cloak_ttl = 600
# cloak_ptr = false
###########################
@ -357,6 +438,20 @@ 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 #
##################################
@ -377,14 +472,16 @@ cache_neg_max_ttl = 600
## 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"
# 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"
# cert_file = 'localhost.pem'
# cert_key_file = 'localhost.pem'
@ -396,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 config file)
## On non-Windows systems, can be /dev/stdout to log to the standard output (also 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']
@ -423,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 config 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
@ -447,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 config 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 config 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 config 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'
@ -530,10 +654,10 @@ 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 during the times defined by the 'time-to-sleep' schedule.
##
@ -542,21 +666,21 @@ cache_neg_max_ttl = 600
[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'}]
@ -578,42 +702,69 @@ cache_neg_max_ttl = 600
## If the `urls` property is missing, cache files and valid signatures
## 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-"
#########################################
@ -622,32 +773,35 @@ 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 over UDP and over relays.
#
# The `dnsdist` server software drops client queries larger than 1500 bytes.
# They are aware of it and are working on a fix.
#
# The list below enables workarounds to make non-relayed usage more reliable
# until the servers are fixed.
## 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.
fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'quad9-dnscrypt-ip4-filter-alt', 'quad9-dnscrypt-ip4-filter-pri', 'quad9-dnscrypt-ip4-nofilter-alt', 'quad9-dnscrypt-ip4-nofilter-pri', 'quad9-dnscrypt-ip6-filter-alt', 'quad9-dnscrypt-ip6-filter-pri', 'quad9-dnscrypt-ip6-nofilter-alt', 'quad9-dnscrypt-ip6-nofilter-pri', 'cleanbrowsing-adult', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-security']
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 #
#################################################################
################################
# TLS Client Authentication #
################################
## 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.
# This is only useful if you are operating your own, private DoH server(s).
# (for DNSCrypt, see the `query_meta` feature instead)
[tls_client_auth]
[doh_client_x509_auth]
# creds = [
# { server_name='myserver', client_cert='client.crt', client_key='client.key' }
# { server_name='*', client_cert='client.crt', client_key='client.key' }
# ]
@ -664,11 +818,11 @@ fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familys
## 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".
## and "example-server-2" via the relay whose relay DNS stamp is
## "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM".
##
## !!! THESE ARE JUST EXAMPLES !!!
##
@ -677,8 +831,15 @@ fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familys
##
## 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'] },
@ -686,17 +847,62 @@ fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familys
# ]
# skip resolvers incompatible with anonymization instead of using them directly
## 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

@ -13,12 +13,24 @@
## 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, *.internal and *.localdomain to 192.168.1.1
# lan 192.168.1.1
# local 192.168.1.1
# home 192.168.1.1
# internal 192.168.1.1
# localdomain 192.168.1.1
## 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,13 +0,0 @@
##############################
# IP blacklist #
##############################
## Response containing blacklisted IP addresses will be blocked
##
## Sample feed of suspect IP addresses: https://github.com/stamparm/ipsum
## Use only one of the levels from that list. 1 is not very reliable
## (may have a lot of false positives), 8 is the most reliable subset.
163.5.1.4
94.46.118.*
[fe80:53:*] # IPv6 prefix example

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()
}
})
}

View File

@ -1,9 +1,9 @@
package main
import (
"crypto/tls"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
@ -30,30 +30,44 @@ func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *ht
writer.WriteHeader(404)
return
}
if request.Header.Get("Content-Type") != dataType {
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
}
start := time.Now()
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)
packet, err := ioutil.ReadAll(io.LimitReader(request.Body, MaxHTTPBodyLength))
if err != nil {
dlog.Warnf("No body in a local DoH query")
return
}
hasEDNS0Padding, err := hasEDNS0Padding(packet)
if err != nil {
writer.WriteHeader(400)
return
}
response := proxy.processIncomingQuery("local_doh", proxy.mainProto, packet, &xClientAddr, nil, start)
response := proxy.processIncomingQuery("local_doh", proxy.mainProto, packet, &xClientAddr, nil, start, false)
if len(response) == 0 {
writer.WriteHeader(500)
return
@ -77,6 +91,7 @@ func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *ht
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)
}
@ -86,11 +101,9 @@ func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
if len(proxy.localDoHCertFile) == 0 || len(proxy.localDoHCertKeyFile) == 0 {
dlog.Fatal("A certificate and a key are required to start a local DoH service")
}
noh2 := make(map[string]func(*http.Server, *tls.Conn, http.Handler))
httpServer := &http.Server{
ReadTimeout: proxy.timeout,
WriteTimeout: proxy.timeout,
TLSNextProto: noh2,
Handler: localDoHHandler{proxy: proxy},
}
httpServer.SetKeepAlivesEnabled(true)
@ -100,7 +113,25 @@ func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
}
func dohPaddedLen(unpaddedLen int) int {
boundaries := [...]int{64, 128, 192, 256, 320, 384, 512, 704, 768, 896, 960, 1024, 1088, 1152, 2688, 4080, MaxDNSPacketSize}
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

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.42"
AppVersion = "2.1.5"
DefaultConfigFileName = "dnscrypt-proxy.toml"
)
@ -27,11 +27,18 @@ type App struct {
}
func main() {
tzErr := TimezoneSetup()
dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON")
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 {
@ -40,10 +47,11 @@ func main() {
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.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")
@ -57,9 +65,9 @@ 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{
@ -125,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

@ -120,6 +120,10 @@ 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(revQname) || revQname[len(match)] == '.' {
@ -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,6 +3,7 @@ package main
import (
"errors"
"fmt"
"io"
"net"
"strings"
"time"
@ -10,13 +11,12 @@ import (
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
}
@ -30,13 +30,13 @@ 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") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
@ -71,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
@ -123,10 +123,14 @@ func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
if plugin.logger != 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" {
@ -134,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 {

View File

@ -35,10 +35,12 @@ func (plugin *PluginBlockIPv6) Eval(pluginsState *PluginsState, msg *dns.Msg) er
}
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, ".")
@ -54,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,20 +3,19 @@ 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
}
@ -45,10 +44,14 @@ func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string
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" {
@ -72,8 +75,7 @@ func (blockedNames *BlockedNames) check(pluginsState *PluginsState, qName string
// ---
type PluginBlockName struct {
}
type PluginBlockName struct{}
func (plugin *PluginBlockName) Name() string {
return "block_name"
@ -85,7 +87,7 @@ func (plugin *PluginBlockName) Description() string {
func (plugin *PluginBlockName) Init(proxy *Proxy) error {
dlog.Noticef("Loading the set of blocking rules from [%s]", proxy.blockNameFile)
bin, err := ReadTextFile(proxy.blockNameFile)
lines, err := ReadTextFile(proxy.blockNameFile)
if err != nil {
return err
}
@ -93,7 +95,7 @@ func (plugin *PluginBlockName) Init(proxy *Proxy) error {
allWeeklyRanges: proxy.allWeeklyRanges,
patternMatcher: NewPatternMatcher(),
}
for lineNo, line := range strings.Split(string(bin), "\n") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
@ -101,8 +103,8 @@ func (plugin *PluginBlockName) Init(proxy *Proxy) error {
parts := strings.Split(line, "@")
timeRangeName := ""
if len(parts) == 2 {
line = strings.TrimFunc(parts[0], unicode.IsSpace)
timeRangeName = strings.TrimFunc(parts[1], unicode.IsSpace)
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
@ -125,7 +127,7 @@ func (plugin *PluginBlockName) Init(proxy *Proxy) error {
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.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.blockNameLogFile)
blockedNames.format = proxy.blockNameFormat
return nil
@ -149,8 +151,7 @@ func (plugin *PluginBlockName) Eval(pluginsState *PluginsState, msg *dns.Msg) er
// ---
type PluginBlockNameResponse struct {
}
type PluginBlockNameResponse struct{}
func (plugin *PluginBlockNameResponse) Name() string {
return "block_name"
@ -181,10 +182,20 @@ func (plugin *PluginBlockNameResponse) Eval(pluginsState *PluginsState, msg *dns
answers := msg.Answer
for _, answer := range answers {
header := answer.Header()
if header.Class != dns.ClassINET || header.Rrtype != dns.TypeCNAME {
if header.Class != dns.ClassINET {
continue
}
target, err := NormalizeQName(answer.(*dns.CNAME).Target)
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
}

View File

@ -119,9 +119,11 @@ var undelegatedSet = []string{
"envoy",
"example",
"f.f.ip6.arpa",
"fritz.box",
"grp",
"gw==",
"home",
"home.arpa",
"hub",
"internal",
"intra",
@ -134,6 +136,7 @@ var undelegatedSet = []string{
"localdomain",
"localhost",
"localnet",
"mail",
"modem",
"mynet",
"myrouter",

View File

@ -6,8 +6,7 @@ import (
"github.com/miekg/dns"
)
type PluginBlockUnqualified struct {
}
type PluginBlockUnqualified struct{}
func (plugin *PluginBlockUnqualified) Name() string {
return "block_unqualified"

View File

@ -6,10 +6,12 @@ import (
"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
@ -17,7 +19,7 @@ type CachedResponse struct {
type CachedResponses struct {
sync.RWMutex
cache *lru.ARCCache
cache *sieve.Sieve[[32]byte, CachedResponse]
}
var cachedResponses CachedResponses
@ -43,8 +45,7 @@ func computeCacheKey(pluginsState *PluginsState, msg *dns.Msg) [32]byte {
// ---
type PluginCache struct {
}
type PluginCache struct{}
func (plugin *PluginCache) Name() string {
return "cache"
@ -68,31 +69,36 @@ func (plugin *PluginCache) Reload() error {
func (plugin *PluginCache) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
cacheKey := computeCacheKey(pluginsState, msg)
cachedResponses.RLock()
defer cachedResponses.RUnlock()
if cachedResponses.cache == nil {
return nil
}
cachedAny, ok := cachedResponses.cache.Get(cacheKey)
if !ok {
return nil
}
cached := cachedAny.(CachedResponse)
synth := cached.msg
cachedResponses.RLock()
if cachedResponses.cache == nil {
cachedResponses.RUnlock()
return nil
}
cached, ok := cachedResponses.cache.Get(cacheKey)
if !ok {
cachedResponses.RUnlock()
return nil
}
expiration := cached.expiration
synth := cached.msg.Copy()
cachedResponses.RUnlock()
synth.Id = msg.Id
synth.Response = true
synth.Compress = true
synth.Question = msg.Question
if time.Now().After(cached.expiration) {
pluginsState.sessionData["stale"] = &synth
if time.Now().After(expiration) {
expiration2 := time.Now().Add(StaleResponseTTL)
updateTTL(synth, expiration2)
pluginsState.sessionData["stale"] = synth
return nil
}
updateTTL(&cached.msg, cached.expiration)
updateTTL(synth, expiration)
pluginsState.synthResponse = &synth
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.cacheHit = true
return nil
@ -100,8 +106,7 @@ func (plugin *PluginCache) Eval(pluginsState *PluginsState, msg *dns.Msg) error
// ---
type PluginCacheResponse struct {
}
type PluginCacheResponse struct{}
func (plugin *PluginCacheResponse) Name() string {
return "cache_response"
@ -131,7 +136,13 @@ func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg
return nil
}
cacheKey := computeCacheKey(pluginsState, msg)
ttl := getMinTTL(msg, pluginsState.cacheMinTTL, pluginsState.cacheMaxTTL, pluginsState.cacheNegMinTTL, pluginsState.cacheNegMaxTTL)
ttl := getMinTTL(
msg,
pluginsState.cacheMinTTL,
pluginsState.cacheMaxTTL,
pluginsState.cacheNegMinTTL,
pluginsState.cacheNegMaxTTL,
)
cachedResponse := CachedResponse{
expiration: time.Now().Add(ttl),
msg: *msg,
@ -139,8 +150,8 @@ func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg
cachedResponses.Lock()
if cachedResponses.cache == nil {
var err error
cachedResponses.cache, err = lru.NewARC(pluginsState.cacheSize)
if err != nil {
cachedResponses.cache = sieve.New[[32]byte, CachedResponse](pluginsState.cacheSize)
if cachedResponses.cache == nil {
cachedResponses.Unlock()
return err
}

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,14 +39,15 @@ 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.createPTR = proxy.cloakedPTR
plugin.patternMatcher = NewPatternMatcher()
cloakedNames := make(map[string]*CloakedName)
for lineNo, line := range strings.Split(string(bin), "\n") {
for lineNo, line := range strings.Split(lines, "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
continue
@ -52,8 +55,8 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
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,6 +86,28 @@ 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 {
if err := plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo); err != nil {
@ -91,6 +117,15 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
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
}
@ -101,7 +136,7 @@ func (plugin *PluginCloak) Reload() error {
func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
question := msg.Question[0]
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
if question.Qclass != dns.ClassINET || question.Qtype == dns.TypeNS || question.Qtype == dns.TypeSOA {
return nil
}
now := time.Now()
@ -111,6 +146,12 @@ func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error
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 {
@ -147,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"

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") {
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
@ -79,7 +89,9 @@ func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
if candidateLen > qNameLen {
continue
}
if qName[qNameLen-candidateLen:] == candidate.domain && (candidateLen == qNameLen || (qName[qNameLen-candidateLen-1] == '.')) {
if (qName[qNameLen-candidateLen:] == candidate.domain &&
(candidateLen == qNameLen || (qName[qNameLen-candidateLen-1] == '.'))) ||
(candidate.domain == ".") {
servers = candidate.servers
break
}
@ -89,7 +101,7 @@ func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
}
server := servers[rand.Intn(len(servers))]
pluginsState.serverName = server
client := dns.Client{Net: "udp"}
client := dns.Client{Net: pluginsState.serverProto, Timeout: pluginsState.timeout}
respMsg, _, err := client.Exchange(msg, server)
if err != nil {
return err

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,17 +43,21 @@ func (plugin *PluginNxLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error
if msg.Rcode != dns.RcodeNameError {
return 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
}
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 := pluginsState.qName
var line string

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,6 +43,16 @@ func (plugin *PluginQueryLog) Reload() error {
}
func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
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 := msg.Question[0]
qType, ok := dns.TypeToString[question.Qtype]
if !ok {
@ -55,12 +65,6 @@ 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 := pluginsState.qName
if pluginsState.cacheHit {
@ -86,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 {

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

View File

@ -1,124 +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 = NewPatternMatcher()
for lineNo, line := range strings.Split(string(bin), "\n") {
line = TrimAndStripInlineComments(line)
if len(line) == 0 {
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 {
qName := pluginsState.qName
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 {
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

@ -46,6 +46,7 @@ const (
PluginsReturnCodeNetworkError
PluginsReturnCodeCloak
PluginsReturnCodeServerTimeout
PluginsReturnCodeNotReady
)
var PluginsReturnCodeToString = map[PluginsReturnCode]string{
@ -61,45 +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
qName string
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)))
}
@ -127,12 +137,18 @@ func (proxy *Proxy) InitPluginsGlobals() error {
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)))
}
@ -173,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
}
@ -187,28 +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
}
}
}
@ -222,7 +240,13 @@ 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: PluginsActionContinue,
returnCode: PluginsReturnCodePass,
@ -238,13 +262,19 @@ func NewPluginsState(proxy *Proxy, clientProto string, clientAddr *net.Addr, sta
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, needsEDNS0Padding bool) ([]byte, error) {
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
@ -256,6 +286,7 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGloba
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 {
@ -269,13 +300,20 @@ 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 != PluginsActionContinue {
break
}
}
packet2, err := msg.PackBuffer(packet)
if err != nil {
return packet, err
@ -289,7 +327,11 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGloba
return packet2, nil
}
func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGlobals, packet []byte, ttl *uint32) ([]byte, error) {
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) {
@ -316,7 +358,13 @@ func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGl
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 != PluginsActionContinue {

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)

View File

@ -1,10 +1,14 @@
package main
import (
"context"
crypto_rand "crypto/rand"
"encoding/binary"
"math/rand"
"net"
"os"
"runtime"
"strings"
"sync/atomic"
"time"
@ -16,77 +20,116 @@ import (
)
type Proxy struct {
userName string
child bool
proxyPublicKey [32]byte
proxySecretKey [32]byte
ephemeralKeys bool
questionSizeEstimator QuestionSizeEstimator
serversInfo ServersInfo
timeout time.Duration
certRefreshDelay time.Duration
certRefreshDelayAfterFailure time.Duration
certIgnoreTimestamp bool
mainProto string
listenAddresses []string
localDoHListenAddresses []string
localDoHPath string
localDoHCertFile string
localDoHCertKeyFile string
daemonize bool
registeredServers []RegisteredServer
registeredRelays []RegisteredServer
pluginBlockIPv6 bool
pluginBlockUnqualified bool
pluginBlockUndelegated bool
cache bool
cacheSize int
cacheNegMinTTL uint32
cacheNegMaxTTL uint32
cacheMinTTL uint32
cacheMaxTTL uint32
rejectTTL uint32
cloakTTL uint32
queryLogFile string
queryLogFormat string
queryLogIgnoredQtypes []string
nxLogFile string
nxLogFormat string
blockNameFile string
whitelistNameFile string
blockNameLogFile string
whitelistNameLogFile string
blockNameFormat string
whitelistNameFormat string
blockIPFile string
blockIPLogFile string
blockIPFormat string
forwardFile string
cloakFile string
pluginsGlobals PluginsGlobals
sources []*Source
clientsCount uint32
maxClients uint32
xTransport *XTransport
allWeeklyRanges *map[string]WeeklyRanges
logMaxSize int
logMaxAge int
logMaxBackups int
blockedQueryResponse string
queryMeta []string
routes *map[string][]string
serversBlockingFragments []string
showCerts bool
dohCreds *map[string]DOHClientCreds
skipAnonIncompatbibleResolvers bool
pluginsGlobals PluginsGlobals
serversInfo ServersInfo
questionSizeEstimator QuestionSizeEstimator
registeredServers []RegisteredServer
dns64Resolvers []string
dns64Prefixes []string
serversBlockingFragments []string
ednsClientSubnets []*net.IPNet
queryLogIgnoredQtypes []string
localDoHListeners []*net.TCPListener
queryMeta []string
udpListeners []*net.UDPConn
sources []*Source
tcpListeners []*net.TCPListener
registeredRelays []RegisteredServer
listenAddresses []string
localDoHListenAddresses []string
xTransport *XTransport
allWeeklyRanges *map[string]WeeklyRanges
routes *map[string][]string
captivePortalMap *CaptivePortalMap
nxLogFormat string
localDoHCertFile string
localDoHCertKeyFile string
captivePortalMapFile string
localDoHPath string
mainProto string
cloakFile string
forwardFile string
blockIPFormat string
blockIPLogFile string
allowedIPFile string
allowedIPFormat string
allowedIPLogFile string
queryLogFormat string
blockIPFile string
allowNameFile string
allowNameFormat string
allowNameLogFile string
blockNameLogFile string
blockNameFormat string
blockNameFile string
queryLogFile string
blockedQueryResponse string
userName string
nxLogFile string
proxySecretKey [32]byte
proxyPublicKey [32]byte
ServerNames []string
DisabledServerNames []string
requiredProps stamps.ServerInformalProperties
certRefreshDelayAfterFailure time.Duration
timeout time.Duration
certRefreshDelay time.Duration
certRefreshConcurrency int
cacheSize int
logMaxBackups int
logMaxAge int
logMaxSize int
cacheNegMinTTL uint32
rejectTTL uint32
cacheMaxTTL uint32
clientsCount uint32
maxClients uint32
cacheMinTTL uint32
cacheNegMaxTTL uint32
cloakTTL uint32
cloakedPTR bool
cache bool
pluginBlockIPv6 bool
ephemeralKeys bool
pluginBlockUnqualified bool
showCerts bool
certIgnoreTimestamp bool
skipAnonIncompatibleResolvers bool
anonDirectCertFallback bool
pluginBlockUndelegated bool
child bool
SourceIPv4 bool
SourceIPv6 bool
SourceDNSCrypt bool
SourceDoH bool
SourceODoH bool
}
func (proxy *Proxy) registerUDPListener(conn *net.UDPConn) {
proxy.udpListeners = append(proxy.udpListeners, conn)
}
func (proxy *Proxy) registerTCPListener(listener *net.TCPListener) {
proxy.tcpListeners = append(proxy.tcpListeners, listener)
}
func (proxy *Proxy) registerLocalDoHListener(listener *net.TCPListener) {
proxy.localDoHListeners = append(proxy.localDoHListeners, listener)
}
func (proxy *Proxy) addDNSListener(listenAddrStr string) {
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
udp := "udp"
tcp := "tcp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
udp = "udp4"
tcp = "tcp4"
}
listenUDPAddr, err := net.ResolveUDPAddr(udp, listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
listenTCPAddr, err := net.ResolveTCPAddr(tcp, listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
@ -105,11 +148,11 @@ func (proxy *Proxy) addDNSListener(listenAddrStr string) {
// if 'userName' is set and we are the parent process
if !proxy.child {
// parent
listenerUDP, err := net.ListenUDP("udp", listenUDPAddr)
listenerUDP, err := net.ListenUDP(udp, listenUDPAddr)
if err != nil {
dlog.Fatal(err)
}
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
listenerTCP, err := net.ListenTCP(tcp, listenTCPAddr)
if err != nil {
dlog.Fatal(err)
}
@ -130,27 +173,32 @@ func (proxy *Proxy) addDNSListener(listenAddrStr string) {
}
// child
listenerUDP, err := net.FilePacketConn(os.NewFile(uintptr(3+FileDescriptorNum), "listenerUDP"))
listenerUDP, err := net.FilePacketConn(os.NewFile(InheritedDescriptorsBase+FileDescriptorNum, "listenerUDP"))
if err != nil {
dlog.Fatalf("Unable to switch to a different user: %v", err)
}
FileDescriptorNum++
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
listenerTCP, err := net.FileListener(os.NewFile(InheritedDescriptorsBase+FileDescriptorNum, "listenerTCP"))
if err != nil {
dlog.Fatalf("Unable to switch to a different user: %v", err)
}
FileDescriptorNum++
dlog.Noticef("Now listening to %v [UDP]", listenUDPAddr)
go proxy.udpListener(listenerUDP.(*net.UDPConn))
proxy.registerUDPListener(listenerUDP.(*net.UDPConn))
dlog.Noticef("Now listening to %v [TCP]", listenAddrStr)
go proxy.tcpListener(listenerTCP.(*net.TCPListener))
proxy.registerTCPListener(listenerTCP.(*net.TCPListener))
}
func (proxy *Proxy) addLocalDoHListener(listenAddrStr string) {
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
network := "tcp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
network = "tcp4"
}
listenTCPAddr, err := net.ResolveTCPAddr(network, listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
@ -166,7 +214,7 @@ func (proxy *Proxy) addLocalDoHListener(listenAddrStr string) {
// if 'userName' is set and we are the parent process
if !proxy.child {
// parent
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
listenerTCP, err := net.ListenTCP(network, listenTCPAddr)
if err != nil {
dlog.Fatal(err)
}
@ -181,14 +229,14 @@ func (proxy *Proxy) addLocalDoHListener(listenAddrStr string) {
// child
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
listenerTCP, err := net.FileListener(os.NewFile(InheritedDescriptorsBase+FileDescriptorNum, "listenerTCP"))
if err != nil {
dlog.Fatalf("Unable to switch to a different user: %v", err)
}
FileDescriptorNum++
proxy.registerLocalDoHListener(listenerTCP.(*net.TCPListener))
dlog.Noticef("Now listening to https://%v%v [DoH]", listenAddrStr, proxy.localDoHPath)
go proxy.localDoHListener(listenerTCP.(*net.TCPListener))
}
func (proxy *Proxy) StartProxy() {
@ -197,23 +245,18 @@ func (proxy *Proxy) StartProxy() {
dlog.Fatal(err)
}
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
for _, registeredServer := range proxy.registeredServers {
proxy.serversInfo.registerServer(registeredServer.name, registeredServer.stamp)
}
for _, listenAddrStr := range proxy.listenAddresses {
proxy.addDNSListener(listenAddrStr)
}
for _, listenAddrStr := range proxy.localDoHListenAddresses {
proxy.addLocalDoHListener(listenAddrStr)
}
// 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)
}
if err := proxy.SystemDListeners(); err != nil {
dlog.Fatal(err)
proxy.startAcceptingClients()
if !proxy.child {
// Notify the service manager that dnscrypt-proxy is ready. dnscrypt-proxy manages itself in case
// servers are not immediately live/reachable. The service manager may assume it is initialized and
// functioning properly. Note that the service manager 'Ready' signal is delayed if netprobe
// cannot reach the internet during start-up.
if err := ServiceManagerReadyNotify(); err != nil {
dlog.Fatal(err)
}
}
proxy.xTransport.internalResolverReady = false
proxy.xTransport.internalResolvers = proxy.listenAddresses
liveServers, err := proxy.serversInfo.refresh(proxy)
if liveServers > 0 {
proxy.certIgnoreTimestamp = false
@ -223,11 +266,6 @@ func (proxy *Proxy) StartProxy() {
}
if liveServers > 0 {
dlog.Noticef("dnscrypt-proxy is ready - live servers: %d", liveServers)
if !proxy.child {
if err := ServiceManagerReadyNotify(); err != nil {
dlog.Fatal(err)
}
}
} else if err != nil {
dlog.Error(err)
dlog.Notice("dnscrypt-proxy is waiting for at least one server to be reachable")
@ -235,6 +273,8 @@ func (proxy *Proxy) StartProxy() {
go func() {
for {
clocksmith.Sleep(PrefetchSources(proxy.xTransport, proxy.sources))
proxy.updateRegisteredServers()
runtime.GC()
}
}()
if len(proxy.serversInfo.registeredServers) > 0 {
@ -249,11 +289,108 @@ func (proxy *Proxy) StartProxy() {
if liveServers > 0 {
proxy.certIgnoreTimestamp = false
}
runtime.GC()
}
}()
}
}
func (proxy *Proxy) updateRegisteredServers() error {
for _, source := range proxy.sources {
registeredServers, err := source.Parse()
if err != nil {
if len(registeredServers) == 0 {
dlog.Criticalf("Unable to use source [%s]: [%s]", source.name, err)
return err
}
dlog.Warnf(
"Error in source [%s]: [%s] -- Continuing with reduced server count [%d]",
source.name,
err,
len(registeredServers),
)
}
for _, registeredServer := range registeredServers {
if registeredServer.stamp.Proto != stamps.StampProtoTypeDNSCryptRelay &&
registeredServer.stamp.Proto != stamps.StampProtoTypeODoHRelay {
if len(proxy.ServerNames) > 0 {
if !includesName(proxy.ServerNames, registeredServer.name) {
continue
}
} else if registeredServer.stamp.Props&proxy.requiredProps != proxy.requiredProps {
continue
}
}
if includesName(proxy.DisabledServerNames, registeredServer.name) {
continue
}
if proxy.SourceIPv4 || proxy.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 !(proxy.SourceIPv4 == isIPv4 || proxy.SourceIPv6 == isIPv6) {
continue
}
}
if registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCryptRelay ||
registeredServer.stamp.Proto == stamps.StampProtoTypeODoHRelay {
var found bool
for i, currentRegisteredRelay := range proxy.registeredRelays {
if currentRegisteredRelay.name == registeredServer.name {
found = true
if currentRegisteredRelay.stamp.String() != registeredServer.stamp.String() {
dlog.Infof(
"Updating stamp for [%s] was: %s now: %s",
registeredServer.name,
currentRegisteredRelay.stamp.String(),
registeredServer.stamp.String(),
)
proxy.registeredRelays[i].stamp = registeredServer.stamp
dlog.Debugf("Total count of registered relays %v", len(proxy.registeredRelays))
}
}
}
if !found {
dlog.Debugf("Adding [%s] to the set of available relays", registeredServer.name)
proxy.registeredRelays = append(proxy.registeredRelays, registeredServer)
}
} else {
if !((proxy.SourceDNSCrypt && registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCrypt) ||
(proxy.SourceDoH && registeredServer.stamp.Proto == stamps.StampProtoTypeDoH) ||
(proxy.SourceODoH && registeredServer.stamp.Proto == stamps.StampProtoTypeODoHTarget)) {
continue
}
var found bool
for i, currentRegisteredServer := range proxy.registeredServers {
if currentRegisteredServer.name == registeredServer.name {
found = true
if currentRegisteredServer.stamp.String() != registeredServer.stamp.String() {
dlog.Infof("Updating stamp for [%s] was: %s now: %s", registeredServer.name, currentRegisteredServer.stamp.String(), registeredServer.stamp.String())
proxy.registeredServers[i].stamp = registeredServer.stamp
}
}
}
if !found {
dlog.Debugf("Adding [%s] to the set of wanted resolvers", registeredServer.name)
proxy.registeredServers = append(proxy.registeredServers, registeredServer)
dlog.Debugf("Total count of registered servers %v", len(proxy.registeredServers))
}
}
}
}
for _, registeredServer := range proxy.registeredServers {
proxy.serversInfo.registerServer(registeredServer.name, registeredServer.stamp)
}
for _, registeredRelay := range proxy.registeredRelays {
proxy.serversInfo.registerRelay(registeredRelay.name, registeredRelay.stamp)
}
return nil
}
func (proxy *Proxy) udpListener(clientPc *net.UDPConn) {
defer clientPc.Close()
for {
@ -263,28 +400,26 @@ func (proxy *Proxy) udpListener(clientPc *net.UDPConn) {
return
}
packet := buffer[:length]
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
proxy.processIncomingQuery(
"udp",
proxy.mainProto,
packet,
&clientAddr,
clientPc,
time.Now(),
true,
) // respond synchronously, but only to cached/synthesized queries
continue
}
go func() {
start := time.Now()
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
proxy.processIncomingQuery("udp", proxy.mainProto, packet, &clientAddr, clientPc, start)
proxy.processIncomingQuery("udp", proxy.mainProto, packet, &clientAddr, clientPc, time.Now(), false)
}()
}
}
func (proxy *Proxy) udpListenerFromAddr(listenAddr *net.UDPAddr) error {
clientPc, err := net.ListenUDP("udp", listenAddr)
if err != nil {
return err
}
dlog.Noticef("Now listening to %v [UDP]", listenAddr)
go proxy.udpListener(clientPc)
return nil
}
func (proxy *Proxy) tcpListener(acceptPc *net.TCPListener) {
defer acceptPc.Close()
for {
@ -292,47 +427,103 @@ func (proxy *Proxy) tcpListener(acceptPc *net.TCPListener) {
if err != nil {
continue
}
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
clientPc.Close()
continue
}
go func() {
start := time.Now()
defer clientPc.Close()
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
if err := clientPc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
return
}
start := time.Now()
packet, err := ReadPrefixed(&clientPc)
if err != nil {
return
}
clientAddr := clientPc.RemoteAddr()
proxy.processIncomingQuery("tcp", "tcp", packet, &clientAddr, clientPc, start)
proxy.processIncomingQuery("tcp", "tcp", packet, &clientAddr, clientPc, start, false)
}()
}
}
func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
func (proxy *Proxy) udpListenerFromAddr(listenAddr *net.UDPAddr) error {
listenConfig, err := proxy.udpListenerConfig()
if err != nil {
return err
}
listenAddrStr := listenAddr.String()
network := "udp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
network = "udp4"
}
clientPc, err := listenConfig.ListenPacket(context.Background(), network, listenAddrStr)
if err != nil {
return err
}
proxy.registerUDPListener(clientPc.(*net.UDPConn))
dlog.Noticef("Now listening to %v [UDP]", listenAddr)
return nil
}
func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error {
listenConfig, err := proxy.tcpListenerConfig()
if err != nil {
return err
}
listenAddrStr := listenAddr.String()
network := "tcp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
network = "tcp4"
}
acceptPc, err := listenConfig.Listen(context.Background(), network, listenAddrStr)
if err != nil {
return err
}
proxy.registerTCPListener(acceptPc.(*net.TCPListener))
dlog.Noticef("Now listening to %v [TCP]", listenAddr)
go proxy.tcpListener(acceptPc)
return nil
}
func (proxy *Proxy) localDoHListenerFromAddr(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
listenConfig, err := proxy.tcpListenerConfig()
if err != nil {
return err
}
listenAddrStr := listenAddr.String()
network := "tcp"
isIPv4 := isDigit(listenAddrStr[0])
if isIPv4 {
network = "tcp4"
}
acceptPc, err := listenConfig.Listen(context.Background(), network, listenAddrStr)
if err != nil {
return err
}
proxy.registerLocalDoHListener(acceptPc.(*net.TCPListener))
dlog.Noticef("Now listening to https://%v%v [DoH]", listenAddr, proxy.localDoHPath)
go proxy.localDoHListener(acceptPc)
return nil
}
func (proxy *Proxy) startAcceptingClients() {
for _, clientPc := range proxy.udpListeners {
go proxy.udpListener(clientPc)
}
proxy.udpListeners = nil
for _, acceptPc := range proxy.tcpListeners {
go proxy.tcpListener(acceptPc)
}
proxy.tcpListeners = nil
for _, acceptPc := range proxy.localDoHListeners {
go proxy.localDoHListener(acceptPc)
}
proxy.localDoHListeners = nil
}
func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte) {
anonymizedDNSHeader := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}
relayedQuery := append(anonymizedDNSHeader, ip.To16()...)
@ -343,16 +534,21 @@ func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte)
*encryptedQuery = relayedQuery
}
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
func (proxy *Proxy) exchangeWithUDPServer(
serverInfo *ServerInfo,
sharedKey *[32]byte,
encryptedQuery []byte,
clientNonce []byte,
) ([]byte, error) {
upstreamAddr := serverInfo.UDPAddr
if serverInfo.RelayUDPAddr != nil {
upstreamAddr = serverInfo.RelayUDPAddr
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayUDPAddr
}
var err error
var pc net.Conn
proxyDialer := proxy.xTransport.proxyDialer
if proxyDialer == nil {
pc, err = net.DialUDP("udp", nil, upstreamAddr)
pc, err = net.DialTimeout("udp", upstreamAddr.String(), serverInfo.Timeout)
} else {
pc, err = (*proxyDialer).Dial("udp", upstreamAddr.String())
}
@ -363,7 +559,7 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32
if err := pc.SetDeadline(time.Now().Add(serverInfo.Timeout)); err != nil {
return nil, err
}
if serverInfo.RelayUDPAddr != nil {
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
proxy.prepareForRelay(serverInfo.UDPAddr.IP, serverInfo.UDPAddr.Port, &encryptedQuery)
}
encryptedResponse := make([]byte, MaxDNSPacketSize)
@ -381,16 +577,21 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32
return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
}
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
func (proxy *Proxy) exchangeWithTCPServer(
serverInfo *ServerInfo,
sharedKey *[32]byte,
encryptedQuery []byte,
clientNonce []byte,
) ([]byte, error) {
upstreamAddr := serverInfo.TCPAddr
if serverInfo.RelayUDPAddr != nil {
upstreamAddr = serverInfo.RelayTCPAddr
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayTCPAddr
}
var err error
var pc net.Conn
proxyDialer := proxy.xTransport.proxyDialer
if proxyDialer == nil {
pc, err = net.DialTCP("tcp", nil, upstreamAddr)
pc, err = net.DialTimeout("tcp", upstreamAddr.String(), serverInfo.Timeout)
} else {
pc, err = (*proxyDialer).Dial("tcp", upstreamAddr.String())
}
@ -401,7 +602,7 @@ func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32
if err := pc.SetDeadline(time.Now().Add(serverInfo.Timeout)); err != nil {
return nil, err
}
if serverInfo.RelayTCPAddr != nil {
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
proxy.prepareForRelay(serverInfo.TCPAddr.IP, serverInfo.TCPAddr.Port, &encryptedQuery)
}
encryptedQuery, err = PrefixWithSize(encryptedQuery)
@ -433,17 +634,27 @@ func (proxy *Proxy) clientsCountInc() bool {
func (proxy *Proxy) clientsCountDec() {
for {
if count := atomic.LoadUint32(&proxy.clientsCount); count == 0 || atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count-1) {
if count := atomic.LoadUint32(&proxy.clientsCount); count == 0 ||
atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count-1) {
break
}
}
}
func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string, query []byte, clientAddr *net.Addr, clientPc net.Conn, start time.Time) (response []byte) {
func (proxy *Proxy) processIncomingQuery(
clientProto string,
serverProto string,
query []byte,
clientAddr *net.Addr,
clientPc net.Conn,
start time.Time,
onlyCached bool,
) []byte {
var response []byte
if len(query) < MinDNSPacketSize {
return
return response
}
pluginsState := NewPluginsState(proxy, clientProto, clientAddr, start)
pluginsState := NewPluginsState(proxy, clientProto, clientAddr, serverProto, start)
serverName := "-"
needsEDNS0Padding := false
serverInfo := proxy.serversInfo.getOne()
@ -453,12 +664,12 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
}
query, _ = pluginsState.ApplyQueryPlugins(&proxy.pluginsGlobals, query, needsEDNS0Padding)
if len(query) < MinDNSPacketSize || len(query) > MaxDNSPacketSize {
return
return response
}
if pluginsState.action == PluginsActionDrop {
pluginsState.returnCode = PluginsReturnCodeDrop
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
var err error
if pluginsState.synthResponse != nil {
@ -466,9 +677,15 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
}
if onlyCached {
if len(response) == 0 {
return response
}
serverInfo = nil
}
if len(response) == 0 && serverInfo != nil {
var ttl *uint32
pluginsState.serverName = serverName
@ -482,7 +699,7 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
serverInfo.noticeBegin(proxy)
if serverProto == "udp" {
@ -500,7 +717,7 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
response, err = proxy.exchangeWithTCPServer(serverInfo, sharedKey, encryptedQuery, clientNonce)
}
@ -521,37 +738,84 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
}
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
return response
}
} else if serverInfo.Proto == stamps.StampProtoTypeDoH {
tid := TransactionID(query)
SetTransactionID(query, 0)
serverInfo.noticeBegin(proxy)
serverResponse, tls, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout)
serverResponse, _, tls, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout)
SetTransactionID(query, tid)
if err == nil || tls == nil || !tls.HandshakeComplete {
response = nil
} else if stale, ok := pluginsState.sessionData["stale"]; ok {
dlog.Debug("Serving stale response")
response, err = (stale.(*dns.Msg)).Pack()
if err != nil || tls == nil || !tls.HandshakeComplete {
if stale, ok := pluginsState.sessionData["stale"]; ok {
dlog.Debug("Serving stale response")
response, err = (stale.(*dns.Msg)).Pack()
}
}
if err != nil {
pluginsState.returnCode = PluginsReturnCodeNetworkError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
return response
}
if response == nil {
response = serverResponse
}
if len(response) >= MinDNSPacketSize {
SetTransactionID(response, tid)
}
} else if serverInfo.Proto == stamps.StampProtoTypeODoHTarget {
tid := TransactionID(query)
if len(serverInfo.odohTargetConfigs) == 0 {
return response
}
target := serverInfo.odohTargetConfigs[rand.Intn(len(serverInfo.odohTargetConfigs))]
odohQuery, err := target.encryptQuery(query)
if err != nil {
dlog.Errorf("Failed to encrypt query for [%v]", serverName)
response = nil
} else {
targetURL := serverInfo.URL
if serverInfo.Relay != nil && serverInfo.Relay.ODoH != nil {
targetURL = serverInfo.Relay.ODoH.URL
}
responseBody, responseCode, _, _, err := proxy.xTransport.ObliviousDoHQuery(serverInfo.useGet, targetURL, odohQuery.odohMessage, proxy.timeout)
if err == nil && len(responseBody) > 0 && responseCode == 200 {
response, err = odohQuery.decryptResponse(responseBody)
if err != nil {
dlog.Warnf("Failed to decrypt response from [%v]", serverName)
response = nil
}
} else if responseCode == 401 || (responseCode == 200 && len(responseBody) == 0) {
if responseCode == 200 {
dlog.Warnf("ODoH relay for [%v] is buggy and returns a 200 status code instead of 401 after a key update", serverInfo.Name)
}
dlog.Infof("Forcing key update for [%v]", serverInfo.Name)
for _, registeredServer := range proxy.serversInfo.registeredServers {
if registeredServer.name == serverInfo.Name {
if err = proxy.serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp); err != nil {
// Failed to refresh the proxy server information.
dlog.Noticef("Key update failed for [%v]", serverName)
serverInfo.noticeFailure(proxy)
clocksmith.Sleep(10 * time.Second)
}
break
}
}
response = nil
} else {
dlog.Warnf("Failed to receive successful response from [%v]", serverName)
}
}
if len(response) >= MinDNSPacketSize {
SetTransactionID(response, tid)
} else if response == nil {
pluginsState.returnCode = PluginsReturnCodeNetworkError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
}
if len(response) >= MinDNSPacketSize {
SetTransactionID(response, tid)
return response
}
} else {
dlog.Fatal("Unsupported protocol")
@ -560,33 +824,33 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
return response
}
response, err = pluginsState.ApplyResponsePlugins(&proxy.pluginsGlobals, response, ttl)
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
return response
}
if pluginsState.action == PluginsActionDrop {
pluginsState.returnCode = PluginsReturnCodeDrop
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
if pluginsState.synthResponse != nil {
response, err = pluginsState.synthResponse.PackBuffer(response)
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
}
if rcode := Rcode(response); rcode == dns.RcodeServerFailure { // SERVFAIL
if pluginsState.dnssec {
dlog.Debug("A response had an invalid DNSSEC signature")
} else {
dlog.Infof("Server [%v] returned temporary error code SERVFAIL -- Invalid DNSSEC signature received or server may be experiencing connectivity issues", serverInfo.Name)
dlog.Infof("A response with status code 2 was received - this is usually a temporary, remote issue with the configuration of the domain name")
serverInfo.noticeFailure(proxy)
}
} else {
@ -594,12 +858,16 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
}
}
if len(response) < MinDNSPacketSize || len(response) > MaxDNSPacketSize {
pluginsState.returnCode = PluginsReturnCodeParseError
if len(response) == 0 {
pluginsState.returnCode = PluginsReturnCodeNotReady
} else {
pluginsState.returnCode = PluginsReturnCodeParseError
}
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
if serverInfo != nil {
serverInfo.noticeFailure(proxy)
}
return
return response
}
if clientProto == "udp" {
if len(response) > pluginsState.maxUnencryptedUDPSafePayloadSize {
@ -607,7 +875,7 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
if err != nil {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
return response
}
}
clientPc.(net.PacketConn).WriteTo(response, *clientAddr)
@ -624,7 +892,7 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
if serverInfo != nil {
serverInfo.noticeFailure(proxy)
}
return
return response
}
if clientPc != nil {
clientPc.Write(response)

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

@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/bits"
"math/rand"
"net"
"net/url"
@ -16,6 +17,7 @@ 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"
@ -38,32 +40,34 @@ type ServerBugs struct {
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
DOHClientCreds DOHClientCreds
odohTargetConfigs []ODoHTargetConfig
}
type LBStrategy interface {
getCandidate(serversCount int) int
getActiveCount(serversCount int) int
}
type LBStrategyP2 struct{}
@ -72,42 +76,83 @@ 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) {
@ -123,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
@ -151,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
@ -190,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]
@ -230,12 +324,12 @@ func (serversInfo *ServersInfo) getOne() *ServerInfo {
serversInfo.Unlock()
return nil
}
if serversInfo.lbEstimator {
serversInfo.estimatorUpdate()
}
candidate := serversInfo.lbStrategy.getCandidate(serversCount)
if serversInfo.lbEstimator {
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
@ -246,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)
}
@ -326,21 +566,36 @@ func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp
break
}
}
relayUDPAddr, relayTCPAddr, err := route(proxy, name)
relay, err := route(proxy, name, stamp.Proto)
if err != nil {
return ServerInfo{}, err
}
certInfo, rtt, fragmentsBlocked, err := FetchCurrentDNSCryptCert(proxy, &name, proxy.mainProto, stamp.ServerPk, stamp.ServerAddrStr, stamp.ProviderName, isNew, relayUDPAddr, relayTCPAddr, knownBugs)
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 && (relayUDPAddr != nil || relayTCPAddr != nil) {
dlog.Warnf("[%v] is incompatible with anonymization", name)
relayTCPAddr, relayUDPAddr = nil, nil
if proxy.skipAnonIncompatbibleResolvers {
return ServerInfo{}, errors.New("Resolver is incompatible with anonymization")
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
@ -363,8 +618,7 @@ 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
@ -378,7 +632,30 @@ func dohTestPacket(msgID uint16) []byte {
msg.SetEdns0(uint16(MaxDNSPacketSize), false)
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, 16)
crypto_rand.Read(ext.Padding)
_, _ = 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()
@ -396,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{
@ -405,34 +683,37 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
Path: stamp.Path,
}
body := dohTestPacket(0xcafe)
dohClientCreds, ok := (*proxy.dohCreds)[name]
if !ok {
dohClientCreds, ok = (*proxy.dohCreds)["*"]
}
if ok {
dlog.Noticef("Enabling TLS authentication for [%s]", name)
proxy.xTransport.tlsClientCreds = dohClientCreds
proxy.xTransport.rebuildTransport()
}
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)
}
serverResponse, tls, 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
}
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
@ -459,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 := serverResponse
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)
@ -486,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

@ -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,18 +3,17 @@ package main
import (
"bytes"
"fmt"
"io/ioutil"
"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"
)
@ -33,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 {
// timeNow() is replaced by tests to provide a static value
var timeNow = time.Now
func (source *Source) checkSignature(bin, sig []byte) error {
signature, err := minisign.DecodeSignature(string(sig))
if err == nil {
_, err = source.minisignKey.Verify(bin, signature)
}
return err
}
// timeNow can be replaced by tests to provide a static value
var timeNow = time.Now
func (source *Source) fetchFromCache(now time.Time) (delay time.Duration, err error) {
func (source *Source) fetchFromCache(now time.Time) (time.Duration, error) {
var err error
var bin, sig []byte
if bin, err = ioutil.ReadFile(source.cacheFile); err != nil {
return
if bin, err = os.ReadFile(source.cacheFile); err != nil {
return 0, err
}
if sig, err = ioutil.ReadFile(source.cacheFile + ".minisig"); err != nil {
return
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) {
@ -129,28 +130,32 @@ func (source *Source) parseURLs(urls []string) {
}
}
func fetchFromURL(xTransport *XTransport, u *url.URL) (bin []byte, err error) {
bin, _, _, err = xTransport.Get(u, "", DefaultTimeout)
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)
@ -165,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 {
@ -195,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
@ -211,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
}
@ -222,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{}) {
@ -238,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
@ -275,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 {
@ -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
}

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