dnscrypt-proxy-2.0.31/0000755000175000017500000000000013557640411013256 5ustar ericericdnscrypt-proxy-2.0.31/logo.svg0000644000175000017500000001424513556612755014756 0ustar ericericdnscrypt-proxy-2.0.31/go.sum0000644000175000017500000001742713556612755014435 0ustar ericericgithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA= github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868 h1:KZ75X3ZCl6yy4jg9R1ziYoCZFDBRqildm+fGComWU7U= github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868/go.mod h1:3Hzo46xzfVpIdv4lJw7YBp9fUJ7HpUgbjH1fFDgy4qM= github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/jedisct1/dlog v0.0.0-20190909160351-692385b00b84 h1:7Q8p5MNx7fMvIRFirdWQpqPEtoSMyskdyOjdi6x4pLc= github.com/jedisct1/dlog v0.0.0-20190909160351-692385b00b84/go.mod h1:YXh1b5j+lwirsCCtTJW19DrbpaL9/5UzwNjI78Cvrg8= github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c h1:a/NQUT7AXkEfhaZ+nb7Uzqijo1Qc7C7SZpRrv+6UQDA= github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-dnsstamps v0.0.0-20191014084838-3e6e00f2b602 h1:5uRCzBAhT+2HfJYnQljeiMmethbUOrYHFSO6lMx2RJg= github.com/jedisct1/go-dnsstamps v0.0.0-20191014084838-3e6e00f2b602/go.mod h1:PCThSkefP5QIL83fIkZ3Qi5Jt02JlWTFB1j9h69Sf2Y= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 h1:nGfB2s9K0GyHuNkJmXkIjP+m7je6Q6gjirr+weAEtDo= github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9/go.mod h1:MipBKo+gZlzpd1JXA1OliuwvtQizlFeu4aMAyTLh8bo= github.com/k-sone/critbitgo v1.3.0 h1:e9OeQIJUtklj0WnAXsonS2JlMRk0nRN4wKiexmJsXTI= github.com/k-sone/critbitgo v1.3.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s= github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0= github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 h1:mgAKeshyNqWKdENOnQsg+8dRTwZFIwFaO3HNl52sweA= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190909082730-f460065e899a h1:mIzbOulag9/gXacgxKlFVwpCOWSfBT3/pDyyCwGA9as= golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= dnscrypt-proxy-2.0.31/ChangeLog0000644000175000017500000003146613556612755015053 0ustar ericeric* 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 - 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 - Support for Anonymized DNS has been added! - Wait before stopping, fixing an issue with Unbound (thanks to Vladimir Bauer) - DNS stamps are now included in the -list-all -json ouptut - The netprobe_timeout setting from the configuration file or command-line was ignored. This has been fixed. - The TTL or cloaked entries can now be adjusted (thanks to Markus Linnala) - Cached IP address from DoH servers now expire (thanks to Markus Linnala) - DNSCrypt certificates can be fetched over Tor and SOCKS proxies - Retries over TCP are faster - Improved logging (thanks to Alison Winters) - Ignore non-TXT records in certificate responses (thanks to Vladimir Bauer) - A lot of internal cleanups, thanks to Markus Linnala. * 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 waiting for the client to retry. This reduces the latency for large responses. - Responses sent to the local network are assumed to support at least 1252 bytes packets, and use optional information from EDNS up to 4096 bytes. This also reduces latency. - Logging improvements: servers are not logged for cached, synthetic and cloaked responses. And the forwarder is logged instead of the regular server for forwarded responses. * 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 - A new plugin was added to prevent Firefox from bypassing the system DNS settings. - New configuration parameter to set how to respond to blocked queries: `blocked_query_response`. Responses can now be empty record sets, REFUSED response codes, or predefined IPv4 and/or IPv6 addresses. - The `refused_code_in_responses` and `blocked_query_response` options have been folded into a new `blocked_query_response` option. - The fallback resolver is now accessed using TCP if `force_tcp` has been set to `true`. - CPU usage when enabling DNSCrypt ephemeral keys has been reduced. - New command-line option: `-show-certs` to print DoH certificate hashes. - Solaris packages are now provided. - DoH servers on a non-standard port, with stamps that don't include IP addresses, and without working system resolvers can now be properly bootstrapped. - A new option, `query_meta`, is now available to add optional records to client queries. * 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 - 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 his help! - The list of resolvers, sorted by latency, is now printed after all the resolvers have been probed. - The "fastest" load-balancing strategy has been renamed to "first". - On Windows, a nul byte is sent to the netprobe address. This is required to check for connectivity on this platform. Thanks to Mathias Berchtold. - The Malwaredomainlist URL was updated to directly parse the host list. Thanks to Encrypted.Town. - The Python script to generate lists of blacklisted domains is now compatible both with Python 2 and Python 3. Thanks to Simon R. - A warning is now displayed for DoH is requested but the server doesn't speak HTTP/2. - A crash with loaded-balanced sets of cloaked names was fixed. 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 - Binaries for FreeBSD/armv7 are now available. - .onion servers are now automatically ignored if Tor routing is not enabled. - Caching of server addresses has been improved, especially when using proxies. - DNSCrypt communications are now automatically forced to using TCP when a SOCKS proxy has been set up. * 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 - 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 - 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, with load-balancing. - New option: `refused_code_in_responses` to return (or not) a `REFUSED` code on blacklisted queries. This is disabled by default, in order to work around a bug in Android Pie. - Time-based restrictions are now properly handled in the generate-domains-blacklist.py script. - Other improvements have been made to the `generate-domains-blacklist.py` script. - The Windows service is now installed as `NT AUTHORITY\NetworkService`. * 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. - The default value for netprobe timeouts has been raised to 60 seconds. - A hash of the body is added to query parameters when sending DoH queries with the POST method in order to work around badly configured proxies. * 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. - An `Accept:` header is now always sent with `GET` queries. - BOMs are now ignored in configuration files. - In addition to SOCKS, HTTP and HTTPS proxies are now supported for DoH servers. * 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. - DNS options used to be cleared from DNS queries, with the exception 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 - 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. - New "offline" mode to serve queries locally without contacting any upstream servers. This can be especially useful along with the cloaking module for local development. - New logo. - TTL of OPT records is properly ignored by the caching module. - The proxy doesn't quit any more if new TCP connections cannot be created. * 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. - Querylog files have a new record indicating the outcome of each transaction. - Pre-built binaries for Linux are statically linked on all architectures. * 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 - 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 - 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. This is useful if the proxy is automatically started at boot, possibly 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 - 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 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 - 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 - 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 well as time-based rules, so that some website can be normally blocked, but accessible on specific days or times of the day. - Lists are now faster to load, and large lists require significantly less memory than before. - New options have been added to disable TLS session tickets as well as use a specific cipher suite. See the example configuration file for a recommended configuration to speed up DoH servers on ARM such as Android devices and Raspberry Pi. - The `-service install` command now remembers what the current directory was when the service was installed, in order to later load configuration files with relative paths. - DoH: The "Cache-Control: max-age" header is now ignored. - Patterns can now be prefixed with `=` to do exact matching: `=example.com` matches `example.com` but will not match `www.example.com`. - Patterns are now fully supported by the cloaking module. - A new option was added to use a specific cipher suite instead of the server's provided one. Using RSA+ChaChaPoly over ECDSA+AES-GCM has shown to decrease CPU usage and latency when connecting to Cloudflare, 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 - 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 when using a fallback resolver if `ipv6_servers` is set. - Improvements have been made to the example systemd configuration files. - The chacha20 implementation was updated to possibly fix a bug on Android/x86. - `generate-domains-blacklist.py` can now parse dnsmasq-style rules. - FreeBSD/arm builds have been added. - `dnscrypt-proxy -list -json` and `-list-all -json` now include the remove servers names and IP addresses. * 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 - Automatic log files rotation was finally implemented. - A new -pidfile command-line option to write the PID file was added. * 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 - Fixes a regression with truncated packets. Thanks to @mazesy and @the-w1nd for spotting a case triggering this! * 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 - 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 - 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 data, even if is it out of date, and schedule an immediate update after the networks is back. - RTT estimation for DNS-over-HTTP/2 servers was off. This has been fixed. - The generate-domains-blacklist script now has a configurable timeout value, and can produce time-based rules. - The timeout parameter in the example configuration file didn't had the correct name; this has been fixed. - Cache: TTLs are now decreasing. dnscrypt-proxy-2.0.31/go.mod0000644000175000017500000000232313556612755014375 0ustar ericericmodule github.com/jedisct1/dnscrypt-proxy go 1.13 require ( github.com/BurntSushi/toml v0.3.1 github.com/VividCortex/ewma v1.1.1 github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868 github.com/hashicorp/go-immutable-radix v1.1.0 github.com/hashicorp/golang-lru v0.5.3 github.com/jedisct1/dlog v0.0.0-20190909160351-692385b00b84 github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c github.com/jedisct1/go-dnsstamps v0.0.0-20191014084838-3e6e00f2b602 github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 github.com/k-sone/critbitgo v1.3.0 github.com/kardianos/service v1.0.0 github.com/miekg/dns v1.1.22 golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.2.2 // indirect ) dnscrypt-proxy-2.0.31/snapcraft.yaml0000644000175000017500000000136313556612755016137 0ustar ericericname: dnscrypt-proxy version: "2.0.27" summary: A flexible DNS proxy, with support for encrypted DNS protocols description: | A flexible DNS proxy, with support for encrypted DNS protocols. grade: stable confinement: classic base: core18 parts: dnscrypt-proxy: plugin: go source: https://github.com/dnscrypt/dnscrypt-proxy.git go-importpath: github.com/dnscrypt/dnscrypt-proxy build-packages: - gcc example-config-files: plugin: dump source: . organize: dnscrypt-proxy/*.toml: etc/ dnscrypt-proxy/*.txt: etc/ stage: - etc apps: dnscrypt-proxy: command: bin/dnscrypt-proxy daemon: simple restart-condition: on-abnormal plugs: - home - network - network-bind dnscrypt-proxy-2.0.31/README.md0000644000175000017500000001320013556612755014542 0ustar ericeric# ![dnscrypt-proxy 2](https://raw.github.com/dnscrypt/dnscrypt-proxy/master/logo.png?3) [![Financial Contributors on Open Collective](https://opencollective.com/dnscrypt/all/badge.svg?label=financial+contributors)](https://opencollective.com/dnscrypt) [![DNSCrypt-Proxy Release](https://img.shields.io/github/release/dnscrypt/dnscrypt-proxy.svg?label=Latest%20Release&style=popout)](https://github.com/dnscrypt/dnscrypt-proxy/releases/latest) [![Build Status](https://travis-ci.org/dnscrypt/dnscrypt-proxy.svg?branch=master)](https://travis-ci.org/dnscrypt/dnscrypt-proxy?branch=master) [![#dnscrypt-proxy:matrix.org](https://img.shields.io/matrix/dnscrypt-proxy:matrix.org.svg?label=DNSCrypt-Proxy%20Matrix%20Chat&server_fqdn=matrix.org&style=popout)](https://matrix.to/#/#dnscrypt-proxy:matrix.org) ## 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). * [dnscrypt-proxy documentation](https://dnscrypt.info/doc) – This project's documentation (Wiki) * [DNSCrypt project home page](https://dnscrypt.info/) * [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) * [FAQ](https://dnscrypt.info/faq) ## [Download the latest release](https://github.com/dnscrypt/dnscrypt-proxy/releases/latest) Available as source code and pre-built binaries for most operating systems and architectures (see below). ## Features * DNS traffic encryption and authentication. Supports DNS-over-HTTPS (DoH) using TLS 1.3, DNSCrypt and Anonymized DNS * 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 * Time-based filtering, with a flexible weekly schedule * Transparent redirection of specific domains to specific resolvers * DNS caching, to reduce latency and improve privacy * Local IPv6 blocking to reduce latency on IPv4-only networks * Load balancing: pick a set of resolvers, dnscrypt-proxy will automatically measure and keep track of their speed, and balance the traffic across the fastest available ones. * Cloaking: like a `HOSTS` file on steroids, that can return preconfigured addresses for specific names, or resolve and return the IP address of other names. This can be used for local development as well as to enforce safe search results on Google, Yahoo, DuckDuckGo and Bing * Automatic background updates of resolvers lists * Can force outgoing connections to use TCP * Compatible with DNSSEC ## Pre-built binaries Up-to-date, pre-built binaries are available for: * Android/arm * Android/arm64 * Android/x86 * Android/x86_64 * Dragonfly BSD * FreeBSD/arm * FreeBSD/x86 * FreeBSD/x86_64 * Linux/arm * Linux/arm64 * Linux/mips * Linux/mipsle * Linux/mips64 * Linux/mips64le * Linux/x86 * Linux/x86_64 * MacOS X * NetBSD/x86 * NetBSD/x86_64 * OpenBSD/x86 * OpenBSD/x86_64 * Windows * Windows 64 bit How to use these files, as well as how to verify their signatures, are documented in the [installation instructions](https://github.com/dnscrypt/dnscrypt-proxy/wiki/installation). ## Contributors ### Code Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. ### Financial Contributors Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/dnscrypt/contribute)] #### Individuals #### Organizations Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/dnscrypt/contribute)] dnscrypt-proxy-2.0.31/dnscrypt-logo.svg0000644000175000017500000035051613556612755016626 0ustar ericeric dnscrypt-proxy-2.0.31/windows/0000755000175000017500000000000013556612755014761 5ustar ericericdnscrypt-proxy-2.0.31/windows/service-restart.bat0000755000175000017500000000173513556612755020604 0ustar ericeric@set @_cmd=1 /* @echo off setlocal EnableExtensions title DNSCrypt-Proxy whoami /groups | findstr "S-1-16-12288" >nul && goto :admin if "%~1"=="RunAsAdmin" goto :error echo Requesting privileges elevation for managing the dnscrypt-proxy service . . . cscript /nologo /e:javascript "%~f0" || goto :error exit /b :error echo. echo Error: Administrator privileges elevation failed, echo please manually run this script as administrator. echo. goto :end :admin pushd "%~dp0" dnscrypt-proxy.exe -service stop dnscrypt-proxy.exe -service start popd echo. echo Thank you for using DNSCrypt-Proxy! :end set /p =Press [Enter] to exit . . . exit /b */ // JScript, restart batch script as administrator var objShell = WScript.CreateObject('Shell.Application'); var ComSpec = WScript.CreateObject('WScript.Shell').ExpandEnvironmentStrings('%ComSpec%'); objShell.ShellExecute(ComSpec, '/c ""' + WScript.ScriptFullName + '" RunAsAdmin"', '', 'runas', 1); dnscrypt-proxy-2.0.31/windows/service-install.bat0000755000175000017500000000174013556612755020562 0ustar ericeric@set @_cmd=1 /* @echo off setlocal EnableExtensions title DNSCrypt-Proxy whoami /groups | findstr "S-1-16-12288" >nul && goto :admin if "%~1"=="RunAsAdmin" goto :error echo Requesting privileges elevation for managing the dnscrypt-proxy service . . . cscript /nologo /e:javascript "%~f0" || goto :error exit /b :error echo. echo Error: Administrator privileges elevation failed, echo please manually run this script as administrator. echo. goto :end :admin pushd "%~dp0" dnscrypt-proxy.exe -service install dnscrypt-proxy.exe -service start popd echo. echo Thank you for using DNSCrypt-Proxy! :end set /p =Press [Enter] to exit . . . exit /b */ // JScript, restart batch script as administrator var objShell = WScript.CreateObject('Shell.Application'); var ComSpec = WScript.CreateObject('WScript.Shell').ExpandEnvironmentStrings('%ComSpec%'); objShell.ShellExecute(ComSpec, '/c ""' + WScript.ScriptFullName + '" RunAsAdmin"', '', 'runas', 1); dnscrypt-proxy-2.0.31/windows/service-uninstall.bat0000755000175000017500000000174113556612755021126 0ustar ericeric@set @_cmd=1 /* @echo off setlocal EnableExtensions title DNSCrypt-Proxy whoami /groups | findstr "S-1-16-12288" >nul && goto :admin if "%~1"=="RunAsAdmin" goto :error echo Requesting privileges elevation for managing the dnscrypt-proxy service . . . cscript /nologo /e:javascript "%~f0" || goto :error exit /b :error echo. echo Error: Administrator privileges elevation failed, echo please manually run this script as administrator. echo. goto :end :admin pushd "%~dp0" dnscrypt-proxy.exe -service stop dnscrypt-proxy.exe -service uninstall popd echo. echo Thank you for using DNSCrypt-Proxy! :end set /p =Press [Enter] to exit . . . exit /b */ // JScript, restart batch script as administrator var objShell = WScript.CreateObject('Shell.Application'); var ComSpec = WScript.CreateObject('WScript.Shell').ExpandEnvironmentStrings('%ComSpec%'); objShell.ShellExecute(ComSpec, '/c ""' + WScript.ScriptFullName + '" RunAsAdmin"', '', 'runas', 1); dnscrypt-proxy-2.0.31/logo.png0000644000175000017500000003555413556612755014751 0ustar ericericPNG  IHDR *&PLTEGpL!7;9>@CD I`G] GjCoAgD@ F8<:?D Gv@sA}==49=C G=<@?G  dE5:B I?F JaFlJ?G K@_F\G@bEBrJAFZ GxHBZN!AE K"@VOM S@ V5 Z2 \1 OPQC VH M: X 7 YH T#=>U= W8-D: 8 098`N!976 UfL6H7";-=-7DSL Lv$DW.GC6k5D>P6B7A=!NkT;+LGAS;O6<6IKAIBwgpJAOBa6ZFDGJEc\5:BG\?`9g7 1 4Ȩ'|f'Z.Ox8g(zpl"5FoL^-qkAi[fn!Q&'p#Qf!d{e_c#]#_'Y'X+V.T3V1S3O6L9,/:=H;18E=AA>F/\ =1!If+IBHNmM8vKiYFi G4NT'S ތfet:o蔀ryQD`Z xͅ٢>#0E 2>"&ir!p8Bȃ)8ZRB[Ȩ*A8%EY(Yi)TD$qc (RHX7O>żEr+!'@ >𜉂Gjp>PQXSv:  Y_)8EŕUR+i`!l2hZ7Kh-|~^HgmTi3:b`m:.'ֈĘ'FĖ]86s˸b:(1HusƘנ`ݷ!gő=)W(#~̱S`X?tN@=BB1޳yy񹆶u@H= LkbWvvtfdW*5zB^^-Vcˎ@.͡9gYtI>[V6[Ī>6-(K= f=ȑE}!{xZr=(1*(1#v}j#]r4Z 06V9r,͗hm*6E h*`V_%q#9FDw,+z9ъ!ʔHFƌE Nrb ka/9Z1 G"]h6$Axъ$mHx3-sp#+y/͵r2-9rW#T1/Kst?F@r ƺTw\ܿX"?E^X܎\&ȼ||}Aq3U,u^[N(ANy1>" :۬1oOv_ӌU/$(r p ܽ 1NԳe*ʐbEzLs_8zsBj?E Et>|۶oq i̅,もV~;TR4NZrW>Kϯ$ˆZG`Dd9Oدp\Gq+P&ѹl ;=A w)j1ց1~i?~/"D6Gӧ7&JJ% #e_aeX[zV׸_p@5X_%t?N~@_ArDHYHwY {6@ Kȱ._WuڪD)Y5P?F3H`i7y-sJr(l3/09Zx &Lf6cJV9N\Hؑt7G:ʤr{-oQf=Jb Ƙķ rLTuQ4ڍtxdè𠾋*&Yw=ct"D> w[JًCM I@FL,cvbw|g3C{{}]\V? eNX@lpօw1rAKޱ  D;U NbF(!ӸTռ9!>w,&<浴ARE!+cH>k~6|&1sh ̚3\ϒ88 ))rx`e@p$j.3tԝʍ㯊]38d &J93wZ f{MWV1f$,IX֕T!7AtGMTʨm*'I6:!l )f,#t X>sa71r#Du))wA~Z'.HĬ%]t{̎`176ޤ}>^W6y;J o;"\l: f7w6imQ8>PNs]so+wd.o]$6^{blb D;E!J<8L Zfu0/*Cki#mb}̚6^Q&8" d8Xz[ы\gHl4:q|i2A%H *a6rv8FN::F<`OT(E2406ꇧO :|̾ NF#C'9F}39&#`KFDI=BHQG F,?u_]몮VID|ᆰ:%S.ǎ%CbH&J#Lf&;OS8'ppr ʇ_[nf;95>ƱS ] !2Ye/|p޷/4lɼ4h24U@ U1,aӰ2*ha 1/7*L(_5j5p:P4҇ѸtU}6Bhp5e8cnAHҼ~EW Gyp!A o^ñ8Fs%E>5*djZ*QU,ښ_M15F8VQLC T)wƛ+eln:TJUT*f87LćK8*P0ύC4UTW`5,hTH}×]9ؘ%7T1n;T!߳_kBFeBUzPl D49Y7xõ n@[CTFu]( Awl6 BL o29ɧ퍀4TF>4$ `-GaL7I`FR7q;:GS U0QFCYCfV՛PQ!w QtlSεw@Xu*UiZ2i4B7Nk4~cG€ P{JN~a}4m+j)C ) Xn<`^%GTtkplojjDcqk44kA `(bcmn`ECi@Q8GH7x۝=uxf`uxce=ÌfH7|;`4tCἰY )ƤgZ5[FS ql4{|9/HE#%hbkR7bkUݤA@Ӏ9:\Gͣ^TWJa)`9Sv()9P7Bk/ [ި`@OxP481 g|.V% pҊuNGKƙl? A_`25$"0Oc>[B4XjVp RU+ Ӏ67m0c#ql( ١>D`T)޻Om*㘱X+ rk& uJ-; ;; kKC)Zި]Btes\UӠq=4e>h4m0X֨|/ b@ysA h;#<.Ŏc4KT{ :Db4&<A8Hb5gJeqwjW` ,mM݉}4p?R1ȗASh. u;.` D(wƠaT*>݉76zä11{C`CtM,`997U@嶋O)<(J髿]p0hV"`6`e>n:銃O27z:y-hpjꪯǙ1 ) 8S X7{^ O%M p(3Vs3j6 Ԡ$275.) =;6k|ϯ˃@ܚ Q e #K_ )[r|R˿,rE8x`ʶFLVw8:;{H b-7D+̡נqDҘa ;TW}n E5-| u+4|x0zq 4ZJ5B0*ިˡJd53B8.T$@ pY Tʚ6QoH R`bc7 lp?ck:H.{-G--U%hTTS 91 co@O #DJ!RK}jPL4gG9I0{*7 Jp+4DqGp 2w?&b0Q᪁40ԹP6& :255Ǩٌi/׃ 2 ‘(Fի{, hO,Hs1qwZ M\0(z5&h4 l)=ظymDQILmU&eg.'+,H7RPH 䜹]! ם -m1Yl rRu2 C qs |\< uq"l*~2)QxrhLgTFtʰ:W^vP /QAdJ|keH7U l@sЅ bXYժn.(5!6ʫd@L{HwfkfiⷆaF15~׳:c;w'7U=RQT=?xxgq 2f_Hep* xhO>(5m6X2$2P)aN#OJk8+6^=:ap?Odt\[" un.\:|06@{( =55DǓ`c}VG;ԇ \F B#|ǁqFZ6~G9oc5ac 6N~N/l1P$Ct (3tʓ]q$!0-ZӏiHo >6Oml'T%+${xlX P]] @m5NLTK~V˰%`7m{H8FP6U4CRr0 ${ϐahfÃ"}軪ۘb?mZfh"Y:|h`|P1"R4@o>zHH n}ț*8ZZL{v:_qP5@D*DE'& M>Yŷ ۻxy DGF(B0vۤJ*U/,bnЀ2EwC 0K?SǢ7DFi'8ZD'²(-vA!9jZͺh/!-0Gķ"w'15##e՗sQ怍@ ƀD1\86fѱ,Aiֲehd7DMĠb&8`- Fx C(q6Nj. n8 @7 "2P2g~:Ү7I(ݸ`HXv fw$!'TEIPJ3d}86~9Ix HM5^ͫ-׼@b~ .Tsf kRFE"t9=e1JHux"W($vBvJOW %)1"￳6Gc0xfLy3~H\#4j)x᤟af(>O?(H+KÁ,7och9$JJX}W( Ɓ?ea#@ bt #y?v4\FTꘚ+ F,>z"Rsbd1J0(7Wdj {0g9pJ w{q~煉cC3șJ5,H.gnM,q?/.Ӳ_x<\8Y/é.aɗL'c:l˧i._fDXmY乼M 2ߔm3(X0ҵ5݌< ORl>Q8O7EU7+&ƕ.p~MSWEFgaQ=1V(@eQzhyb:t.n[!X)M!2N A8;䭷º?A'8[<ǝ^XC !amQ=S D!)X dc~@\g$ "f\oro@'Ip\)2eE v$jmϡ!RLBNdŞB/T8[@D~$ )LqAh TU FU )w-!=bfw8!S#;2/TA}X@ܝ(1Ba<3aQ?EeH#ٚ%7dJ6}   0q$4aOEU6aR sbH9/ XG nKdP $j"(T8 I"Ї8)EQj0Wa0OR eHA%L Wr q "P ;

ݹ`CXIؼsRٱ+-QOwe{PQ=GÅ Êw? e\MX|WJzoZ`R-QĨ0NJsLj Mگ.f^HP-Kō`3V`{/8sS8š/eh]_R>< e.[1WhNX*QM.R!ցemN4 I ;VF#hC,O4(<2G1)QĘ-4;aΥ^8XerIDsb |}[m)lw(ǤFۚT@QɃ,??2 n!lJ\- f1{W|6a*U҃%e&,C(b2y桀8?v4)b;2P0l4zʹAmW69]Se?νlE&H!GY8,+:\IyL&Wm.g!رc5w ?=8tZSGo`0T*mG+vfC1;'X, OehD ]K<>S4(fS"FenIG PlJkhi?-iە|œBеar#ՂЈvW30A[H-`%&MaShAŭJwm '#VA-Mzv ʯG['E#lQT:Zۇ皼Mkbn, rQ/ Umx"9/!hly l BLšzYmCc`, u15Ned.C;M3fqf%uj(R5MVcew%QPqIXM=Jm!X0=e(;Kue藴+"€Ko7pHAA²>H>3m.e9b@en-ǘ扺}j 7>&p@(ԃl;;[i`p(a/;.M7Zgr@RVu1sO5]Gc}sz!末K/*N?0\%343/[?F48(g4UƁ}ܸ̺b4Ʀ}C#q"/n\B~LqFq3Co:Xv%0:m4b`%80|'hhC>#F@(j1u \ϔ#$ ˴Nb1s(ۚ "9-b1T9^~Ԁr/ l, 4`404g -G4Ԓ'4 j @&W3*BJ9Ekp,henF:+۔.Zm)ŰA;󨞗6R"F#5 V-8-tQ ^#1jSS\ vW4Lr^υghܖ'4x -N_ pOxЭh8t{FNFJ= hEuPb] TXw[ f^߽эfMbPCV'3ՙn-FOV]ٱЀ94r^;ޯEym_꧀Ev)TTr%1<5Phwsn.m}P>u*dN u)299a'y>4ϙF+|OzR Ya8+1vSJlШÊwjg pζ{G3~걕&&9g#єr5}RqZ]p2oj8[\"p=ba5g63ZZFcg$;gzL54z +U'3lWKBNCF;:y"$+90f,[!0QŌFO{mΗvO<0w?Kd ݅ΠƊG,Q!= MPC~MbtSCR+h`ʉ& ,崸ȨKvtq-A~(L?@2_N-`/M,놁F&7 Z?IF6ֶ@ϯO;n|P(YfBDjK1bI큆K85dC{#vAca,1}_tE[CDB)k{©ktQnLNh$a\Uu2ռ竍`VL'qEkc<3=i:HbazQyF-U%X ,]F)NT8<meiHVy n{C+[/ (]f@tG(B _ML3v۷ss]cDwnrZM ],x9&To _Y$5gsZ;,~֓{aE*kKClaJ[.i&mi hF[n΄ʉRE`LFoz}4dx['/[ FZS*BA\5ʳFUinؘҨ*i)|64DpxueإQPҲ{umiRЗ`Y`FJ o0L ^x-i"{3@t@7H8l46_;֧`:"Kt;JƂz/ܔi򉗟 4%6MT")T 4k^)*q1 z?}IENDB`dnscrypt-proxy-2.0.31/.gitignore0000644000175000017500000000016613556612755015262 0ustar ericeric#*# *.dll *.dylib *.exe *.out *.so *.swp *.test *~ dnscrypt-proxy/dnscrypt-proxy2 dnscrypt-proxy/dnscrypt-proxy .idea dnscrypt-proxy-2.0.31/.travis.yml0000644000175000017500000002541513556612755015407 0ustar ericericlanguage: go os: - linux go: - 1.x script: - gimme --list - echo $TRAVIS_GO_VERSION - cd dnscrypt-proxy - go clean - env GOOS=windows GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir win32 - ln dnscrypt-proxy.exe win32/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt win32/ - for i in win32/LICENSE win32/*.toml win32/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx "$i"; done - ln ../windows/* win32/ - zip -9 -r dnscrypt-proxy-win32-${TRAVIS_TAG:-dev}.zip win32 - go clean - env GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir win64 - ln dnscrypt-proxy.exe win64/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt win64/ - for i in win64/LICENSE win64/*.toml win64/*.txt; do ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx "$i"; done - ln ../windows/* win64/ - zip -9 -r dnscrypt-proxy-win64-${TRAVIS_TAG:-dev}.zip win64 - go clean - env GO386=387 GOOS=openbsd GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir openbsd-i386 - ln dnscrypt-proxy openbsd-i386/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt openbsd-i386/ - tar czpvf dnscrypt-proxy-openbsd_i386-${TRAVIS_TAG:-dev}.tar.gz openbsd-i386 - go clean - env GOOS=openbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir openbsd-amd64 - ln dnscrypt-proxy openbsd-amd64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt openbsd-amd64/ - tar czpvf dnscrypt-proxy-openbsd_amd64-${TRAVIS_TAG:-dev}.tar.gz openbsd-amd64 - go clean - env GOOS=freebsd GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir freebsd-i386 - ln dnscrypt-proxy freebsd-i386/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-i386/ - tar czpvf dnscrypt-proxy-freebsd_i386-${TRAVIS_TAG:-dev}.tar.gz freebsd-i386 - go clean - env GOOS=freebsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir freebsd-amd64 - ln dnscrypt-proxy freebsd-amd64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-amd64/ - tar czpvf dnscrypt-proxy-freebsd_amd64-${TRAVIS_TAG:-dev}.tar.gz freebsd-amd64 - go clean - env GOOS=freebsd GOARCH=arm go build -mod vendor -ldflags="-s -w" - mkdir freebsd-arm - ln dnscrypt-proxy freebsd-arm/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-arm/ - tar czpvf dnscrypt-proxy-freebsd_arm-${TRAVIS_TAG:-dev}.tar.gz freebsd-arm - go clean - env GOOS=freebsd GOARCH=arm GOARM=7 go build -mod vendor -ldflags="-s -w" - mkdir freebsd-armv7 - ln dnscrypt-proxy freebsd-armv7/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt freebsd-armv7/ - tar czpvf dnscrypt-proxy-freebsd_armv7-${TRAVIS_TAG:-dev}.tar.gz freebsd-armv7 - go clean - env GOOS=dragonfly GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir dragonflybsd-amd64 - ln dnscrypt-proxy dragonflybsd-amd64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt dragonflybsd-amd64/ - tar czpvf dnscrypt-proxy-dragonflybsd_amd64-${TRAVIS_TAG:-dev}.tar.gz dragonflybsd-amd64 - go clean - env GOOS=netbsd GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir netbsd-i386 - ln dnscrypt-proxy netbsd-i386/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt netbsd-i386/ - tar czpvf dnscrypt-proxy-netbsd_i386-${TRAVIS_TAG:-dev}.tar.gz netbsd-i386 - go clean - env GOOS=netbsd GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir netbsd-amd64 - ln dnscrypt-proxy netbsd-amd64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt netbsd-amd64/ - tar czpvf dnscrypt-proxy-netbsd_amd64-${TRAVIS_TAG:-dev}.tar.gz netbsd-amd64 - go clean - env GOOS=solaris GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir solaris-amd64 - ln dnscrypt-proxy solaris-amd64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt solaris-amd64/ - tar czpvf dnscrypt-proxy-solaris_amd64-${TRAVIS_TAG:-dev}.tar.gz solaris-amd64 - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir linux-i386 - ln dnscrypt-proxy linux-i386/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-i386/ - tar czpvf dnscrypt-proxy-linux_i386-${TRAVIS_TAG:-dev}.tar.gz linux-i386 - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir linux-x86_64 - ln dnscrypt-proxy linux-x86_64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-x86_64/ - tar czpvf dnscrypt-proxy-linux_x86_64-${TRAVIS_TAG:-dev}.tar.gz linux-x86_64 - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -mod vendor -ldflags="-s -w" - mkdir linux-arm - ln dnscrypt-proxy linux-arm/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-arm/ - tar czpvf dnscrypt-proxy-linux_arm-${TRAVIS_TAG:-dev}.tar.gz linux-arm - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -ldflags="-s -w" - mkdir linux-arm64 - ln dnscrypt-proxy linux-arm64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-arm64/ - tar czpvf dnscrypt-proxy-linux_arm64-${TRAVIS_TAG:-dev}.tar.gz linux-arm64 - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod vendor -ldflags="-s -w" - mkdir linux-mips - ln dnscrypt-proxy linux-mips/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips/ - tar czpvf dnscrypt-proxy-linux_mips-${TRAVIS_TAG:-dev}.tar.gz linux-mips - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod vendor -ldflags="-s -w" - mkdir linux-mipsle - ln dnscrypt-proxy linux-mipsle/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mipsle/ - tar czpvf dnscrypt-proxy-linux_mipsle-${TRAVIS_TAG:-dev}.tar.gz linux-mipsle - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -mod vendor -ldflags="-s -w" - mkdir linux-mips64 - ln dnscrypt-proxy linux-mips64/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips64/ - tar czpvf dnscrypt-proxy-linux_mips64-${TRAVIS_TAG:-dev}.tar.gz linux-mips64 - go clean - env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -mod vendor -ldflags="-s -w" - mkdir linux-mips64le - ln dnscrypt-proxy linux-mips64le/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt linux-mips64le/ - tar czpvf dnscrypt-proxy-linux_mips64le-${TRAVIS_TAG:-dev}.tar.gz linux-mips64le - go clean - env GOOS=darwin GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir macos - ln dnscrypt-proxy macos/ - ln ../LICENSE example-dnscrypt-proxy.toml example-*.txt macos/ - tar czpvf dnscrypt-proxy-macos-${TRAVIS_TAG:-dev}.tar.gz macos - go clean - env CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 go build -mod vendor -ldflags="-s -w" - mkdir android-arm - ln dnscrypt-proxy android-arm/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-arm/ - zip -9 -r dnscrypt-proxy-android_arm-${TRAVIS_TAG:-dev}.zip android-arm - go clean - env CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ CGO_ENABLED=1 GOOS=android GOARCH=arm64 go build -mod vendor -ldflags="-s -w" - mkdir android-arm64 - ln dnscrypt-proxy android-arm64/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-arm64/ - zip -9 -r dnscrypt-proxy-android_arm64-${TRAVIS_TAG:-dev}.zip android-arm64 - go clean - env CC=i686-linux-android-clang CXX=i686-linux-android-clang++ CGO_ENABLED=1 GOOS=android GOARCH=386 go build -mod vendor -ldflags="-s -w" - mkdir android-i386 - ln dnscrypt-proxy android-i386/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-i386/ - zip -9 -r dnscrypt-proxy-android_i386-${TRAVIS_TAG:-dev}.zip android-i386 - go clean - env CC=x86_64-linux-android-clang CXX=x86_64-linux-android-clang++ CGO_ENABLED=1 GOOS=android GOARCH=amd64 go build -mod vendor -ldflags="-s -w" - mkdir android-x86_64 - ln dnscrypt-proxy android-x86_64/ - cp ../LICENSE example-dnscrypt-proxy.toml example-*.txt android-x86_64/ - zip -9 -r dnscrypt-proxy-android_x86_64-${TRAVIS_TAG:-dev}.zip android-x86_64 after_success: - ls -l dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip deploy: provider: releases api_key: secure: J3K/wo3oW/ySl6X4Zk5PX+EVy4fa0qa4fbpKNivogch5yjYw2pgrlSvwto9TM12Gxi4tTMKiWYK4YBapNf+tm501s4OyS1G1rJR1fZ+iyaHgGBLD+QppbivZt+P7Do56agSili68Zcgm7MQfZbvOq9z42z3AJ71+UNTJmTp63voaAuyOF/VdLsmJHMd/5nmFJH6mfMrgMs720GCWxFgdq3NRM2AdVldsp1YmNb4qKqIzunmfxqG9TqVlpq35tNOhWA/Ll3rbsiDVeUpBAW5ked/qHyGRkFVk44O6cPSFGe035Txx0JviBshGxsNSP+aJL9T55hIj1dmuk6g5uhPqABU/zcdJvXOv11oqJuV/DGHO31UfVN6u744LJY6Y1lkd+UUNiOJDPGC80+6M2GbP7BFMZiO5qnYkxzktnYg9b6zIPwmj96XZSniDTAn+qemJf2S8rzShvBtWX29Q4odIaCfFUY8i0muowQ4Vep5S5FqVG+r/rQTXOUIUsNv4r/gP/y5hdtOMC2r1VSvWk068upmW6ovCtcmTghSfYcLCG5r+g5OE2mKj9kbx6RQMspewk9+pvOhNZKXsn/AIvvDC4V46MaDjFkdYN0VbsYB5NH11DGCPH7vDwJnAzzMWnofCkiTG07dJYlLUnD9iUgYoNkrxivAgQKnDP8C6Ka0RGdk= file: - dnscrypt-proxy-*.tar.gz - dnscrypt-proxy-*.zip - dnscrypt-proxy-*.minisig file_glob: true skip_cleanup: true on: repo: DNSCrypt/dnscrypt-proxy tags: true before_deploy: - mkdir -p /tmp/bin /tmp/lib /tmp/include - export LD_LIBRARY_PATH=/tmp/lib:LD_LIBRARY_PATH - export PATH=/tmp/bin:$PATH - git clone --depth 1 https://github.com/jedisct1/libsodium.git --branch=stable - cd libsodium - env ./configure --disable-dependency-tracking --prefix=/tmp - make -j$(nproc) install - cd - - git clone --depth 1 https://github.com/jedisct1/minisign.git - cd minisign/src - gcc -O2 -o /tmp/bin/minisign -I/tmp/include -L/tmp/lib *.c -lsodium - cd - - minisign -v - echo '#' > /tmp/minisign.key - echo "$MINISIGN_SK" >> /tmp/minisign.key - echo | minisign -s /tmp/minisign.key -Sm dnscrypt-proxy-*.tar.gz dnscrypt-proxy-*.zip before_install: - NDK_VER=r18 - curl -LO http://dl.google.com/android/repository/android-ndk-${NDK_VER}-linux-x86_64.zip - unzip -q android-ndk-${NDK_VER}-linux-x86_64.zip -d $HOME - rm android-ndk-${NDK_VER}-linux-x86_64.zip - NDK_TOOLS=$HOME/android-ndk-${NDK_VER} - NDK_STANDALONE=$HOME/ndk-standalone-${NDK_VER} - MAKE_TOOLCHAIN=$NDK_TOOLS/build/tools/make_standalone_toolchain.py - for arch in x86 arm; do python $MAKE_TOOLCHAIN --arch $arch --api 19 --install-dir $NDK_STANDALONE/$arch; PATH=$PATH:$NDK_STANDALONE/$arch/bin; done - for arch in x86_64 arm64; do python $MAKE_TOOLCHAIN --arch $arch --api 21 --install-dir $NDK_STANDALONE/$arch; PATH=$PATH:$NDK_STANDALONE/$arch/bin; done - rm -rf $NDK_TOOLS env: global: - secure: cuTXb4v5NwTr1XmkiHGkFir8fMiiBMnraCrls3thdDRlSTix0CiQc/H5Vh8SHauuG6VwVyrCT/Xsf0UQUmnULkPHjvuiNehb+bG4J3fz7hF94glBdQ8vxTuMmnHfJEYTQRLwCsWMBEC1wekw13O8J/0opFNG5neduns3Z1/rD5VSlBwgc8/4lomEp0fadIvzLeS7f5mxeXAD5Z9KBmc09uCjxVoF9Qsk1r901B0c0RMxIbJWyW9ZhDIVr/aEUN/tU0EXMKOA85sizg2moAigb8RZ1WCTh7utLGKpAyQegNk/unkksKMzZFkUCwHkrxlujwoe93wUS4rvZ+3nHMtLQdR+OfMeVs4/zvvQVq2f3bOXgkxgvhq6Bop0RK0xyEJffa5hUFbGNKhIFkLFLn1Ok28t2q7NOFPr0H2egHlkwgPztyhYMYb9C5PW4zd9buI0LS5452C4jXH5raBMfx844wTzaBbN689AKiYb84Qesqczss/o7eC7V48kh823dlZ/s2//gtp1ceqdAtNvp4dy7X/ECA/vNlpYisrtkR/CsFpJjGoTvS37leVMpmc5bn39dkoa5ZLliu7CaFRefbavcWWEImVStll9FBQ6+Ck9+41gczl9Rr7eGIV9ZZ/fmdkLNgxIpoAhZRee/dZD+/0gUExHxXXn10MqPuNVytVPiuU= dnscrypt-proxy-2.0.31/utils/0000755000175000017500000000000013556612755014427 5ustar ericericdnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/0000755000175000017500000000000013556612755022002 5ustar ericericdnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/domains-blacklist-local-additions.txt0000644000175000017500000000123613556612755031211 0ustar ericeric # Local set of patterns to block ad.* ads.* banner.* banners.* creatives.* oas.* oascentral.* stats.* tag.* telemetry.* tracker.* # My Macbook constantly sends a lot of useless queries to *.local, # so I block them. *.lan is apparently another common one, and # *.localdomain and *.workgroup are common on Windows. *.lan *.local *.localdomain *.workgroup # eth0.me is hardcoded in tools such as Archey, but is not available any # more, causing issues such as terminal sessions taking a long time to # start. eth0.me # ibpxl.com is a tracker that seems to frequently have issues, causing # page loads to stall. ibpxl.com # ditto for that one internetbrands.com dnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/domains-whitelist.txt0000644000175000017500000000057313556612755026214 0ustar ericerica-msedge.net amazon.com appsflyer.com azurewebsites.net cdnetworks.com cloudapp.net download.dnscrypt.info edgekey.net elasticbeanstalk.com github.com github.io raw.githubusercontent.com invalid j.mp l-msedge.net lan liveinternet.ru localdomain microsoft.com msedge.net nsatc.net ovh.net polyfill.io pusher.com pusherapp.com revinate.com spotify.com tagcommander.com windows.net dnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/domains-blacklist.conf0000644000175000017500000001223413556612755026253 0ustar ericeric ################################################################################## # # # Generate a black list of domains using public data sources, and the local # # domains-blacklist-local-additions.txt file. # # # # The default configuration is just indicative, and corresponds to the one # # used to produce the public "mybase" set. # # # # Comment out the URLs of the sources you wish to disable, leave the ones # # you would like enabled uncommented. Then run the script to build the # # dnscrypt-blacklist-domains.txt file: # # # # $ generate-domains-blacklist.py > dnscrypt-blacklist-domains.txt # # # # Domains that should never be blocked can be put into a file named # # domains-whitelist.txt. # # # # That blacklist file can then be used in the dnscrypt-proxy.toml file: # # # # [blacklist] # # # # blacklist_file = 'dnscrypt-blacklist-domains.txt' # # # ################################################################################## # Local additions file:domains-blacklist-local-additions.txt # Bambenek malware C2s https://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt # hpHosts’ Ad and tracking servers https://hosts-file.net/ad_servers.txt # Malware domains https://mirror1.malwaredomains.com/files/justdomains # Abuse.ch Ransomware Tracker https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt # Malware Domain List https://www.malwaredomainlist.com/hostslist/hosts.txt # Adblock Warning Removal List https://easylist-downloads.adblockplus.org/antiadblockfilters.txt # EasyList https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt # EasyList China https://easylist-downloads.adblockplus.org/easylistchina.txt # RU AdList # https://easylist-downloads.adblockplus.org/advblock.txt # Fanboy’s Social Blocking List https://easylist-downloads.adblockplus.org/fanboy-social.txt # Peter Lowe’s Ad and tracking server list https://pgl.yoyo.org/adservers/serverlist.php # Spam404 https://raw.githubusercontent.com/Dawsey21/Lists/master/adblock-list.txt # CJX Annoyance List https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt # EU: Prebake - Filter Obtrusive Cookie Notices https://raw.githubusercontent.com/liamja/Prebake/master/obtrusive.txt # Malvertising filter list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt # Malware filter list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_malware.txt # Basic tracking list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt # Sysctl list (ads) http://sysctl.org/cameleon/hosts # KAD host file (fraud/adware) - https://github.com/azet12/KADhosts https://raw.githubusercontent.com/azet12/KADhosts/master/KADhosts.txt # BarbBlock list (spurious and invalid DMCA takedowns) https://ssl.bblck.me/blacklists/domain-list.txt # Dan Pollock's hosts list https://someonewhocares.org/hosts/hosts # NoTracking's list - blocking ads, trackers and other online garbage https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt # CoinBlockerLists: blocks websites serving cryptocurrency miners - https://gitlab.com/ZeroDot1/CoinBlockerLists/ - Contains false positives # https://gitlab.com/ZeroDot1/CoinBlockerLists/raw/master/list_browser.txt # Websites potentially publishing fake news # https://raw.githubusercontent.com/marktron/fakenews/master/fakenews # Quidsup NoTrack Blocklist - Contains too many false positives to be enabled by default # https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt # Quidsup Malware Blocklist - Contains too many false positives to be enabled by default # https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-malware.txt # Dynamic DNS services, sadly often used by malware # https://mirror1.malwaredomains.com/files/dynamic_dns.txt # Block pornography # https://raw.githubusercontent.com/Clefspeare13/pornhosts/master/0.0.0.0/hosts # https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/pornography-hosts # Block gambling sites # https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/gambling-hosts # Block social media sites # https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/social-hosts dnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/generate-domains-blacklist.py0000755000175000017500000001707113556612755027555 0ustar ericeric#! /usr/bin/env python # run with python generate-domains-blacklist.py > list.txt.tmp && mv -f list.txt.tmp list import argparse import re import sys try: import urllib2 as urllib URLLIB_NEW = False except (ImportError, ModuleNotFoundError): import urllib.request as urllib from urllib.request import Request URLLIB_NEW = True def parse_time_restricted_list(content): rx_comment = re.compile(r"^(#|$)") rx_inline_comment = re.compile(r"\s*#\s*[a-z0-9-].*$") rx_trusted = re.compile(r"^([*a-z0-9.-]+)\s*(@\S+)?$") names = set() time_restrictions = {} rx_set = [rx_trusted] for line in content.splitlines(): line = str.lower(str.strip(line)) if rx_comment.match(line): continue line = rx_inline_comment.sub("", line) for rx in rx_set: matches = rx.match(line) if not matches: continue name = matches.group(1) names.add(name) time_restriction = matches.group(2) if time_restriction: time_restrictions[name] = time_restriction return names, time_restrictions def parse_trusted_list(content): names, _time_restrictions = parse_time_restricted_list(content) time_restrictions = {} return names, time_restrictions def parse_list(content, trusted=False): rx_comment = re.compile(r"^(#|$)") rx_inline_comment = re.compile(r"\s*#\s*[a-z0-9-].*$") rx_u = re.compile(r"^@*\|\|([a-z0-9.-]+[.][a-z]{2,})\^?(\$(popup|third-party))?$") rx_l = re.compile(r"^([a-z0-9.-]+[.][a-z]{2,})$") rx_h = re.compile( r"^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}\s+([a-z0-9.-]+[.][a-z]{2,})$" ) rx_mdl = re.compile(r'^"[^"]+","([a-z0-9.-]+[.][a-z]{2,})",') rx_b = re.compile(r"^([a-z0-9.-]+[.][a-z]{2,}),.+,[0-9: /-]+,") rx_dq = re.compile(r"^address=/([a-z0-9.-]+[.][a-z]{2,})/.") if trusted: return parse_trusted_list(content) names = set() time_restrictions = {} rx_set = [rx_u, rx_l, rx_h, rx_mdl, rx_b, rx_dq] for line in content.splitlines(): line = str.lower(str.strip(line)) if rx_comment.match(line): continue line = rx_inline_comment.sub("", line) for rx in rx_set: matches = rx.match(line) if not matches: continue name = matches.group(1) names.add(name) return names, time_restrictions def print_restricted_name(name, time_restrictions): if name in time_restrictions: print("{}\t{}".format(name, time_restrictions[name])) else: print( "# ignored: [{}] was in the time-restricted list, " "but without a time restriction label".format(name) ) def load_from_url(url): sys.stderr.write("Loading data from [{}]\n".format(url)) req = urllib.Request(url=url, headers={"User-Agent": "dnscrypt-proxy"}) trusted = False if URLLIB_NEW: req_type = req.type else: req_type = req.get_type() if req_type == "file": trusted = True response = None try: response = urllib.urlopen(req, timeout=int(args.timeout)) except urllib.URLError as err: raise Exception("[{}] could not be loaded: {}\n".format(url, err)) if trusted is False and response.getcode() != 200: raise Exception("[{}] returned HTTP code {}\n".format(url, response.getcode())) content = response.read() if URLLIB_NEW: content = content.decode("utf-8", errors="replace") return (content, trusted) def name_cmp(name): parts = name.split(".") parts.reverse() return str.join(".", parts) def has_suffix(names, name): parts = str.split(name, ".") while parts: parts = parts[1:] if str.join(".", parts) in names: return True return False def whitelist_from_url(url): if not url: return set() content, trusted = load_from_url(url) names, _time_restrictions = parse_list(content, trusted) return names def blacklists_from_config_file( file, whitelist, time_restricted_url, ignore_retrieval_failure ): blacklists = {} whitelisted_names = set() all_names = set() unique_names = set() # Load conf & blacklists with open(file) as fd: for line in fd: line = str.strip(line) if str.startswith(line, "#") or line == "": continue url = line try: content, trusted = load_from_url(url) names, _time_restrictions = parse_list(content, trusted) blacklists[url] = names all_names |= names except Exception as e: sys.stderr.write(e.message) if not ignore_retrieval_failure: exit(1) # Time-based blacklist if time_restricted_url and not re.match(r"^[a-z0-9]+:", time_restricted_url): time_restricted_url = "file:" + time_restricted_url if time_restricted_url: time_restricted_content, _trusted = load_from_url(time_restricted_url) time_restricted_names, time_restrictions = parse_time_restricted_list( time_restricted_content ) if time_restricted_names: print("########## Time-based blacklist ##########\n") for name in time_restricted_names: print_restricted_name(name, time_restrictions) # Time restricted names should be whitelisted, or they could be always blocked whitelisted_names |= time_restricted_names # Whitelist if whitelist and not re.match(r"^[a-z0-9]+:", whitelist): whitelist = "file:" + whitelist whitelisted_names |= whitelist_from_url(whitelist) # Process blacklists for url, names in blacklists.items(): print("\n\n########## Blacklist from {} ##########\n".format(url)) ignored, whitelisted = 0, 0 list_names = list() for name in names: if has_suffix(all_names, name) or name in unique_names: ignored = ignored + 1 elif has_suffix(whitelisted_names, name) or name in whitelisted_names: whitelisted = whitelisted + 1 else: list_names.append(name) unique_names.add(name) list_names.sort(key=name_cmp) if ignored: print("# Ignored duplicates: {}\n".format(ignored)) if whitelisted: print("# Ignored entries due to the whitelist: {}\n".format(whitelisted)) for name in list_names: print(name) argp = argparse.ArgumentParser( description="Create a unified blacklist from a set of local and remote files" ) argp.add_argument( "-c", "--config", default="domains-blacklist.conf", help="file containing blacklist sources", ) argp.add_argument( "-w", "--whitelist", default="domains-whitelist.txt", help="file containing a set of names to exclude from the blacklist", ) argp.add_argument( "-r", "--time-restricted", default="domains-time-restricted.txt", help="file containing a set of names to be time restricted", ) argp.add_argument( "-i", "--ignore-retrieval-failure", action="store_true", help="generate list even if some urls couldn't be retrieved", ) argp.add_argument("-t", "--timeout", default=30, help="URL open timeout") args = argp.parse_args() conf = args.config whitelist = args.whitelist time_restricted = args.time_restricted ignore_retrieval_failure = args.ignore_retrieval_failure blacklists_from_config_file(conf, whitelist, time_restricted, ignore_retrieval_failure) dnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/domains-blacklist-all.conf0000644000175000017500000000721613556612755027025 0ustar ericeric ################################################################################## # # # Generate a black list of domains using public data sources, and the local # # domains-blacklist-local-additions.txt file. # # # # Comment the URLs of the sources you want to disable, and run the script to # # build the dnscrypt-blacklist-domains.txt file: # # # # $ generate-domains-blacklist.py > blacklist.txt # # # # That blacklist file can then be used in the dnscrypt-proxy configuration: # # # # [blacklist] # # blacklist_file = 'blacklist.txt' # # # ################################################################################## # Local additions file:domains-blacklist-local-additions.txt # Bambenek malware C2s https://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt # hpHosts’ Ad and tracking servers https://hosts-file.net/ad_servers.txt # Malware domains https://mirror1.malwaredomains.com/files/justdomains # Abuse.ch Ransomware Tracker https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt # Malware Domain List https://www.malwaredomainlist.com/mdlcsv.php?inactive=off # Adblock Warning Removal List https://easylist-downloads.adblockplus.org/antiadblockfilters.txt # EasyList https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt # EasyList China https://easylist-downloads.adblockplus.org/easylistchina.txt # Fanboy’s Social Blocking List https://easylist-downloads.adblockplus.org/fanboy-social.txt # Peter Lowe’s Ad and tracking server list https://pgl.yoyo.org/adservers/serverlist.php # Spam404 https://raw.githubusercontent.com/Dawsey21/Lists/master/adblock-list.txt # CJX Annoyance List https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt # EU: Prebake - Filter Obtrusive Cookie Notices https://raw.githubusercontent.com/liamja/Prebake/master/obtrusive.txt # Malvertising filter list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt # Malware filter list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_malware.txt # Basic tracking list by Disconnect https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt # Quidsup NoTrack https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt # Sysctl list (ads) http://sysctl.org/cameleon/hosts # KAD host file (fraud/adware) - https://github.com/azet12/KADhosts https://raw.githubusercontent.com/azet12/KADhosts/master/KADhosts.txt # Fake news sites https://raw.githubusercontent.com/marktron/fakenews/master/fakenews # Dynamic DNS services, sadly often used by malware https://mirror1.malwaredomains.com/files/dynamic_dns.txt # Block pornography https://raw.githubusercontent.com/Clefspeare13/pornhosts/master/0.0.0.0/hosts https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/pornography-hosts # Block gambling sites https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/gambling-hosts # Block social media sites # https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/social-hosts dnscrypt-proxy-2.0.31/utils/generate-domains-blacklists/domains-time-restricted.txt0000644000175000017500000000034513556612755027301 0ustar ericeric## Rules to be applied at specific times ## ## This requires a time schedule to be defined in the ## dnscrypt-proxy.toml configuration file. # twitter.com @work # facebook.com @work # *.youtube.* @time-to-sleep dnscrypt-proxy-2.0.31/LICENSE0000644000175000017500000000146213556612755014277 0ustar ericeric/* * ISC License * * Copyright (c) 2018 * Frank Denis * * 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. */ dnscrypt-proxy-2.0.31/dnscrypt-proxy/0000755000175000017500000000000013556612755016314 5ustar ericericdnscrypt-proxy-2.0.31/dnscrypt-proxy/serversInfo.go0000644000175000017500000003241113556612755021151 0ustar ericericpackage main import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/ioutil" "math/rand" "net" "net/url" "sort" "strings" "sync" "time" "github.com/VividCortex/ewma" "github.com/jedisct1/dlog" stamps "github.com/jedisct1/go-dnsstamps" "golang.org/x/crypto/ed25519" ) const ( RTTEwmaDecay = 10.0 ) type RegisteredServer struct { name string stamp stamps.ServerStamp description string } type ServerInfo struct { Proto stamps.StampProtoType MagicQuery [8]byte ServerPk [32]byte SharedKey [32]byte CryptoConstruction CryptoConstruction Name string Timeout time.Duration URL *url.URL HostName string UDPAddr *net.UDPAddr TCPAddr *net.TCPAddr RelayUDPAddr *net.UDPAddr RelayTCPAddr *net.TCPAddr lastActionTS time.Time rtt ewma.MovingAverage initialRtt int useGet bool } type LBStrategy int const ( LBStrategyNone = LBStrategy(iota) LBStrategyP2 LBStrategyPH LBStrategyFirst LBStrategyRandom ) const DefaultLBStrategy = LBStrategyP2 type ServersInfo struct { sync.RWMutex inner []*ServerInfo registeredServers []RegisteredServer lbStrategy LBStrategy lbEstimator bool } func NewServersInfo() ServersInfo { return ServersInfo{lbStrategy: DefaultLBStrategy, lbEstimator: true, registeredServers: make([]RegisteredServer, 0)} } func (serversInfo *ServersInfo) registerServer(name string, stamp stamps.ServerStamp) error { newRegisteredServer := RegisteredServer{name: name, stamp: stamp} serversInfo.Lock() defer serversInfo.Unlock() for i, oldRegisteredServer := range serversInfo.registeredServers { if oldRegisteredServer.name == name { serversInfo.registeredServers[i] = newRegisteredServer return nil } } serversInfo.registeredServers = append(serversInfo.registeredServers, newRegisteredServer) return nil } func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp stamps.ServerStamp) error { serversInfo.RLock() isNew := true for _, oldServer := range serversInfo.inner { if oldServer.Name == name { isNew = false break } } serversInfo.RUnlock() newServer, err := fetchServerInfo(proxy, name, stamp, isNew) if err != nil { return err } if name != newServer.Name { dlog.Fatalf("[%s] != [%s]", name, newServer.Name) } newServer.rtt = ewma.NewMovingAverage(RTTEwmaDecay) newServer.rtt.Set(float64(newServer.initialRtt)) isNew = true serversInfo.Lock() for i, oldServer := range serversInfo.inner { if oldServer.Name == name { serversInfo.inner[i] = &newServer isNew = false break } } if isNew { serversInfo.inner = append(serversInfo.inner, &newServer) serversInfo.registeredServers = append(serversInfo.registeredServers, RegisteredServer{name: name, stamp: stamp}) } serversInfo.Unlock() return nil } func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) { dlog.Debug("Refreshing certificates") serversInfo.RLock() registeredServers := serversInfo.registeredServers serversInfo.RUnlock() liveServers := 0 var err error for _, registeredServer := range registeredServers { if err = serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp); err == nil { liveServers++ } } serversInfo.Lock() sort.SliceStable(serversInfo.inner, func(i, j int) bool { return serversInfo.inner[i].initialRtt < serversInfo.inner[j].initialRtt }) inner := serversInfo.inner innerLen := len(inner) if innerLen > 1 { dlog.Notice("Sorted latencies:") for i := 0; i < innerLen; i++ { dlog.Noticef("- %5dms %s", inner[i].initialRtt, inner[i].Name) } } if innerLen > 0 { dlog.Noticef("Server with the lowest initial latency: %s (rtt: %dms)", inner[0].Name, inner[0].initialRtt) } serversInfo.Unlock() return liveServers, err } func (serversInfo *ServersInfo) estimatorUpdate() { // serversInfo.RWMutex is assumed to be Locked candidate := rand.Intn(len(serversInfo.inner)) if candidate == 0 { return } candidateRtt, currentBestRtt := serversInfo.inner[candidate].rtt.Value(), serversInfo.inner[0].rtt.Value() if currentBestRtt < 0 { currentBestRtt = candidateRtt serversInfo.inner[0].rtt.Set(currentBestRtt) } partialSort := false if candidateRtt < currentBestRtt { serversInfo.inner[candidate], serversInfo.inner[0] = serversInfo.inner[0], serversInfo.inner[candidate] 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 { 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)) 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] } } } } func (serversInfo *ServersInfo) getOne() *ServerInfo { serversInfo.Lock() defer serversInfo.Unlock() serversCount := len(serversInfo.inner) if serversCount <= 0 { return nil } if serversInfo.lbEstimator { serversInfo.estimatorUpdate() } var candidate int switch serversInfo.lbStrategy { case LBStrategyFirst: candidate = 0 case LBStrategyPH: candidate = rand.Intn(Max(Min(serversCount, 2), serversCount/2)) case LBStrategyRandom: candidate = rand.Intn(serversCount) default: candidate = rand.Intn(Min(serversCount, 2)) } serverInfo := serversInfo.inner[candidate] dlog.Debugf("Using candidate [%s] RTT: %d", (*serverInfo).Name, int((*serverInfo).rtt.Value())) return serverInfo } func fetchServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) { if stamp.Proto == stamps.StampProtoTypeDNSCrypt { return fetchDNSCryptServerInfo(proxy, name, stamp, isNew) } else if stamp.Proto == stamps.StampProtoTypeDoH { return fetchDoHServerInfo(proxy, name, stamp, isNew) } return ServerInfo{}, errors.New("Unsupported protocol") } func route(proxy *Proxy, name string, stamp *stamps.ServerStamp) (*net.UDPAddr, *net.TCPAddr, error) { routes := proxy.routes if routes == nil { return nil, nil, nil } relayNames, ok := (*routes)[name] if !ok { relayNames, ok = (*routes)["*"] } if !ok { return nil, nil, nil } var relayName string if len(relayNames) > 0 { candidate := rand.Intn(len(relayNames)) relayName = relayNames[candidate] } 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, } } else { for _, registeredServer := range proxy.registeredRelays { if registeredServer.name == relayName { relayCandidateStamp = ®isteredServer.stamp break } } for _, registeredServer := range proxy.registeredServers { if registeredServer.name == relayName { relayCandidateStamp = ®isteredServer.stamp break } } } if relayCandidateStamp == nil { return nil, nil, fmt.Errorf("Undefined relay [%v] for server [%v]", relayName, name) } if relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCrypt || relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCryptRelay { relayUDPAddr, err := net.ResolveUDPAddr("udp", relayCandidateStamp.ServerAddrStr) if err != nil { return nil, nil, err } relayTCPAddr, err := net.ResolveTCPAddr("tcp", relayCandidateStamp.ServerAddrStr) if err != nil { return nil, nil, err } return relayUDPAddr, relayTCPAddr, nil } return nil, nil, fmt.Errorf("Invalid relay [%v] for server [%v]", relayName, 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)) if err != nil || len(serverPk) != ed25519.PublicKeySize { dlog.Fatalf("Unsupported public key for [%s]: [%s]", name, stamp.ServerPk) } dlog.Warnf("Public key [%s] shouldn't be hex-encoded any more", string(stamp.ServerPk)) stamp.ServerPk = serverPk } relayUDPAddr, relayTCPAddr, err := route(proxy, name, &stamp) if err != nil { return ServerInfo{}, err } certInfo, rtt, err := FetchCurrentDNSCryptCert(proxy, &name, proxy.mainProto, stamp.ServerPk, stamp.ServerAddrStr, stamp.ProviderName, isNew, relayUDPAddr, relayTCPAddr) if err != nil { return ServerInfo{}, err } remoteUDPAddr, err := net.ResolveUDPAddr("udp", stamp.ServerAddrStr) if err != nil { return ServerInfo{}, err } remoteTCPAddr, err := net.ResolveTCPAddr("tcp", stamp.ServerAddrStr) if err != nil { return ServerInfo{}, err } return ServerInfo{ Proto: stamps.StampProtoTypeDNSCrypt, MagicQuery: certInfo.MagicQuery, ServerPk: certInfo.ServerPk, SharedKey: certInfo.SharedKey, CryptoConstruction: certInfo.CryptoConstruction, Name: name, Timeout: proxy.timeout, UDPAddr: remoteUDPAddr, TCPAddr: remoteTCPAddr, RelayUDPAddr: relayUDPAddr, RelayTCPAddr: relayTCPAddr, initialRtt: rtt, }, nil } func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) { 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) } } url := &url.URL{ Scheme: "https", Host: stamp.ProviderName, Path: stamp.Path, } body := []byte{ 0xca, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, } useGet := false 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 { return ServerInfo{}, err } dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name) } resp, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout) if err != nil { return ServerInfo{}, err } tls := resp.TLS if tls == nil || !tls.HandshakeComplete { return ServerInfo{}, errors.New("TLS handshake failed") } protocol := tls.NegotiatedProtocol if len(protocol) == 0 { protocol = "h1" dlog.Warnf("[%s] does not support HTTP/2", name) } dlog.Infof("[%s] TLS version: %x - Protocol: %v - Cipher suite: %v", name, tls.Version, protocol, tls.CipherSuite) showCerts := proxy.showCerts found := false var wantedHash [32]byte for _, cert := range tls.PeerCertificates { h := sha256.Sum256(cert.RawTBSCertificate) if showCerts { dlog.Noticef("Advertised cert: [%s] [%x]", cert.Subject, h) } else { dlog.Debugf("Advertised 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 { return ServerInfo{}, fmt.Errorf("Certificate hash [%x] not found for [%s]", wantedHash, name) } respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength)) if err != nil { return ServerInfo{}, err } if len(respBody) < MinDNSPacketSize || len(respBody) > MaxDNSPacketSize || respBody[0] != 0xca || respBody[1] != 0xfe || respBody[4] != 0x00 || respBody[5] != 0x01 { return ServerInfo{}, errors.New("Webserver returned an unexpected response") } xrtt := int(rtt.Nanoseconds() / 1000000) if isNew { dlog.Noticef("[%s] OK (DoH) - rtt: %dms", name, xrtt) } else { dlog.Infof("[%s] OK (DoH) - rtt: %dms", name, xrtt) } return ServerInfo{ Proto: stamps.StampProtoTypeDoH, Name: name, Timeout: proxy.timeout, URL: url, HostName: stamp.ProviderName, initialRtt: xrtt, useGet: useGet, }, nil } func (serverInfo *ServerInfo) noticeFailure(proxy *Proxy) { proxy.serversInfo.Lock() serverInfo.rtt.Add(float64(proxy.timeout.Nanoseconds() / 1000000)) proxy.serversInfo.Unlock() } func (serverInfo *ServerInfo) noticeBegin(proxy *Proxy) { proxy.serversInfo.Lock() serverInfo.lastActionTS = time.Now() proxy.serversInfo.Unlock() } func (serverInfo *ServerInfo) noticeSuccess(proxy *Proxy) { now := time.Now() proxy.serversInfo.Lock() elapsed := now.Sub(serverInfo.lastActionTS) elapsedMs := elapsed.Nanoseconds() / 1000000 if elapsedMs > 0 && elapsed < proxy.timeout { serverInfo.rtt.Add(float64(elapsedMs)) } proxy.serversInfo.Unlock() } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_whitelist_name.go0000644000175000017500000000751313556612755023243 0ustar ericericpackage main import ( "errors" "fmt" "net" "strings" "time" "unicode" "github.com/jedisct1/dlog" "github.com/miekg/dns" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) type PluginWhitelistName struct { allWeeklyRanges *map[string]WeeklyRanges patternMatcher *PatternMatcher logger *lumberjack.Logger format string } func (plugin *PluginWhitelistName) Name() string { return "whitelist_name" } func (plugin *PluginWhitelistName) Description() string { return "Whitelists DNS queries matching name patterns" } func (plugin *PluginWhitelistName) Init(proxy *Proxy) error { dlog.Noticef("Loading the set of whitelisting rules from [%s]", proxy.whitelistNameFile) bin, err := ReadTextFile(proxy.whitelistNameFile) if err != nil { return err } plugin.allWeeklyRanges = proxy.allWeeklyRanges plugin.patternMatcher = NewPatternPatcher() for lineNo, line := range strings.Split(string(bin), "\n") { line = strings.TrimFunc(line, unicode.IsSpace) if len(line) == 0 || strings.HasPrefix(line, "#") { continue } parts := strings.Split(line, "@") timeRangeName := "" if len(parts) == 2 { line = strings.TrimFunc(parts[0], unicode.IsSpace) timeRangeName = strings.TrimFunc(parts[1], unicode.IsSpace) } else if len(parts) > 2 { dlog.Errorf("Syntax error in whitelist rules at line %d -- Unexpected @ character", 1+lineNo) continue } var weeklyRanges *WeeklyRanges if len(timeRangeName) > 0 { weeklyRangesX, ok := (*plugin.allWeeklyRanges)[timeRangeName] if !ok { dlog.Errorf("Time range [%s] not found at line %d", timeRangeName, 1+lineNo) } else { weeklyRanges = &weeklyRangesX } } if _, err := plugin.patternMatcher.Add(line, weeklyRanges, lineNo+1); err != nil { dlog.Error(err) continue } } if len(proxy.whitelistNameLogFile) == 0 { return nil } plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.whitelistNameLogFile, Compress: true} plugin.format = proxy.whitelistNameFormat return nil } func (plugin *PluginWhitelistName) Drop() error { return nil } func (plugin *PluginWhitelistName) Reload() error { return nil } func (plugin *PluginWhitelistName) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) != 1 { return nil } qName := strings.ToLower(StripTrailingDot(questions[0].Name)) whitelist, reason, xweeklyRanges := plugin.patternMatcher.Eval(qName) var weeklyRanges *WeeklyRanges if xweeklyRanges != nil { weeklyRanges = xweeklyRanges.(*WeeklyRanges) } if whitelist { if weeklyRanges != nil && !weeklyRanges.Match() { whitelist = false } } if whitelist { if pluginsState.sessionData == nil { pluginsState.sessionData = make(map[string]interface{}) } pluginsState.sessionData["whitelisted"] = true if plugin.logger != nil { var clientIPStr string if pluginsState.clientProto == "udp" { clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String() } else { clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String() } var line string if plugin.format == "tsv" { now := time.Now() year, month, day := now.Date() hour, minute, second := now.Clock() tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second) line = fmt.Sprintf("%s\t%s\t%s\t%s\n", tsStr, clientIPStr, StringQuote(qName), StringQuote(reason)) } else if plugin.format == "ltsv" { line = fmt.Sprintf("time:%d\thost:%s\tqname:%s\tmessage:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), StringQuote(reason)) } else { dlog.Fatalf("Unexpected log format: [%s]", plugin.format) } if plugin.logger == nil { return errors.New("Log file not initialized") } plugin.logger.Write([]byte(line)) } } return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/service_windows.go0000644000175000017500000000036613556612755022062 0ustar ericericpackage main import "golang.org/x/sys/windows/svc/mgr" func ServiceManagerStartNotify() error { mgr, err := mgr.Connect() if err != nil { return err } mgr.Disconnect() return nil } func ServiceManagerReadyNotify() error { return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_query_log.go0000644000175000017500000000605613556612755022236 0ustar ericericpackage main import ( "errors" "fmt" "net" "strings" "time" "github.com/jedisct1/dlog" "github.com/miekg/dns" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) type PluginQueryLog struct { logger *lumberjack.Logger format string ignoredQtypes []string } func (plugin *PluginQueryLog) Name() string { return "query_log" } func (plugin *PluginQueryLog) Description() string { return "Log DNS queries." } 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.format = proxy.queryLogFormat plugin.ignoredQtypes = proxy.queryLogIgnoredQtypes return nil } func (plugin *PluginQueryLog) Drop() error { return nil } func (plugin *PluginQueryLog) Reload() error { return nil } func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) == 0 { return nil } question := questions[0] qType, ok := dns.TypeToString[question.Qtype] if !ok { qType = string(qType) } if len(plugin.ignoredQtypes) > 0 { for _, ignoredQtype := range plugin.ignoredQtypes { if strings.EqualFold(ignoredQtype, qType) { return nil } } } var clientIPStr string if pluginsState.clientProto == "udp" { clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String() } else { clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String() } qName := StripTrailingDot(question.Name) if pluginsState.cacheHit { pluginsState.serverName = "-" } else { switch pluginsState.returnCode { case PluginsReturnCodeSynth, PluginsReturnCodeCloak, PluginsReturnCodeParseError: pluginsState.serverName = "-" } } returnCode, ok := PluginsReturnCodeToString[pluginsState.returnCode] if !ok { returnCode = string(returnCode) } var requestDuration time.Duration if !pluginsState.requestStart.IsZero() && !pluginsState.requestEnd.IsZero() { requestDuration = pluginsState.requestEnd.Sub(pluginsState.requestStart) } 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\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 { cached = 1 } line = fmt.Sprintf("time:%d\thost:%s\tmessage:%s\ttype:%s\treturn:%s\tcached:%d\tduration:%d\tserver:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), qType, returnCode, cached, requestDuration/time.Millisecond, StringQuote(pluginsState.serverName)) } 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 } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_cache.go0000644000175000017500000000673213556612755021274 0ustar ericericpackage main import ( "crypto/sha512" "encoding/binary" "errors" "sync" "time" lru "github.com/hashicorp/golang-lru" "github.com/miekg/dns" ) type CachedResponse struct { expiration time.Time msg dns.Msg } type CachedResponses struct { sync.RWMutex cache *lru.ARCCache } var cachedResponses CachedResponses type PluginCacheResponse struct { cachedResponses *CachedResponses } func (plugin *PluginCacheResponse) Name() string { return "cache_response" } func (plugin *PluginCacheResponse) Description() string { return "DNS cache (writer)." } func (plugin *PluginCacheResponse) Init(proxy *Proxy) error { return nil } func (plugin *PluginCacheResponse) Drop() error { return nil } func (plugin *PluginCacheResponse) Reload() error { return nil } func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg) error { plugin.cachedResponses = &cachedResponses if msg.Rcode != dns.RcodeSuccess && msg.Rcode != dns.RcodeNameError && msg.Rcode != dns.RcodeNotAuth { return nil } if msg.Truncated { return nil } cacheKey, err := computeCacheKey(pluginsState, msg) if err != nil { return err } ttl := getMinTTL(msg, pluginsState.cacheMinTTL, pluginsState.cacheMaxTTL, pluginsState.cacheNegMinTTL, pluginsState.cacheNegMaxTTL) cachedResponse := CachedResponse{ expiration: time.Now().Add(ttl), msg: *msg, } plugin.cachedResponses.Lock() if plugin.cachedResponses.cache == nil { plugin.cachedResponses.cache, err = lru.NewARC(pluginsState.cacheSize) if err != nil { plugin.cachedResponses.Unlock() return err } } plugin.cachedResponses.cache.Add(cacheKey, cachedResponse) plugin.cachedResponses.Unlock() updateTTL(msg, cachedResponse.expiration) return nil } type PluginCache struct { cachedResponses *CachedResponses } func (plugin *PluginCache) Name() string { return "cache" } func (plugin *PluginCache) Description() string { return "DNS cache (reader)." } func (plugin *PluginCache) Init(proxy *Proxy) error { return nil } func (plugin *PluginCache) Drop() error { return nil } func (plugin *PluginCache) Reload() error { return nil } func (plugin *PluginCache) Eval(pluginsState *PluginsState, msg *dns.Msg) error { plugin.cachedResponses = &cachedResponses cacheKey, err := computeCacheKey(pluginsState, msg) if err != nil { return nil } plugin.cachedResponses.RLock() defer plugin.cachedResponses.RUnlock() if plugin.cachedResponses.cache == nil { return nil } cachedAny, ok := plugin.cachedResponses.cache.Get(cacheKey) if !ok { return nil } cached := cachedAny.(CachedResponse) if time.Now().After(cached.expiration) { return nil } updateTTL(&cached.msg, cached.expiration) synth := cached.msg synth.Id = msg.Id synth.Response = true synth.Compress = true synth.Question = msg.Question pluginsState.synthResponse = &synth pluginsState.action = PluginsActionSynth pluginsState.cacheHit = true return nil } func computeCacheKey(pluginsState *PluginsState, msg *dns.Msg) ([32]byte, error) { questions := msg.Question if len(questions) != 1 { return [32]byte{}, errors.New("No question present") } question := questions[0] h := sha512.New512_256() var tmp [5]byte binary.LittleEndian.PutUint16(tmp[0:2], question.Qtype) binary.LittleEndian.PutUint16(tmp[2:4], question.Qclass) if pluginsState.dnssec { tmp[4] = 1 } h.Write(tmp[:]) normalizedName := []byte(question.Name) NormalizeName(&normalizedName) h.Write(normalizedName) var sum [32]byte h.Sum(sum[:0]) return sum, nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/crypto.go0000644000175000017500000001103613556612755020164 0ustar ericericpackage main import ( "bytes" crypto_rand "crypto/rand" "crypto/sha512" "errors" "math/rand" "github.com/jedisct1/dlog" "github.com/jedisct1/xsecretbox" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/nacl/box" "golang.org/x/crypto/nacl/secretbox" ) const ( NonceSize = xsecretbox.NonceSize HalfNonceSize = xsecretbox.NonceSize / 2 TagSize = xsecretbox.TagSize PublicKeySize = 32 QueryOverhead = ClientMagicLen + PublicKeySize + HalfNonceSize + TagSize ResponseOverhead = len(ServerMagic) + NonceSize + TagSize ) func pad(packet []byte, minSize int) []byte { packet = append(packet, 0x80) for len(packet) < minSize { packet = append(packet, 0) } return packet } func unpad(packet []byte) ([]byte, error) { for i := len(packet); ; { if i == 0 { return nil, errors.New("Invalid padding (short packet)") } i-- if packet[i] == 0x80 { return packet[:i], nil } else if packet[i] != 0x00 { return nil, errors.New("Invalid padding (delimiter not found)") } } } 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) } } else { box.Precompute(&sharedKey, serverPk, secretKey) } return } 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) copy(nonce, clientNonce) var publicKey *[PublicKeySize]byte if proxy.ephemeralKeys { h := sha512.New512_256() h.Write(clientNonce) h.Write(proxy.proxySecretKey[:]) var ephSk [32]byte h.Sum(ephSk[:0]) var xPublicKey [PublicKeySize]byte curve25519.ScalarBaseMult(&xPublicKey, &ephSk) publicKey = &xPublicKey xsharedKey := ComputeSharedKey(serverInfo.CryptoConstruction, &ephSk, &serverInfo.ServerPk, nil) sharedKey = &xsharedKey } else { sharedKey = &serverInfo.SharedKey publicKey = &proxy.proxyPublicKey } minQuestionSize := QueryOverhead + len(packet) if proto == "udp" { minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize) } else { var xpad [1]byte rand.Read(xpad[:]) minQuestionSize += int(xpad[0]) } paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, QueryOverhead)+63) & ^63) if serverInfo.RelayUDPAddr != nil && proto == "tcp" { // XXX - Note: Cisco's broken implementation doesn't accept more than 1472 bytes paddedLength = MaxDNSPacketSize } if QueryOverhead+len(packet)+1 > paddedLength { err = errors.New("Question too large; cannot be padded") return } encrypted = append(serverInfo.MagicQuery[:], publicKey[:]...) encrypted = append(encrypted, nonce[:HalfNonceSize]...) padded := pad(packet, paddedLength-QueryOverhead) if serverInfo.CryptoConstruction == XChacha20Poly1305 { encrypted = xsecretbox.Seal(encrypted, nonce, padded, sharedKey[:]) } else { var xsalsaNonce [24]byte copy(xsalsaNonce[:], nonce) encrypted = secretbox.Seal(encrypted, padded, &xsalsaNonce, sharedKey) } return } 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) || len(encrypted) > responseHeaderLen+TagSize+int(MaxDNSPacketSize) || !bytes.Equal(encrypted[:serverMagicLen], ServerMagic[:]) { return encrypted, errors.New("Invalid message size or prefix") } serverNonce := encrypted[serverMagicLen:responseHeaderLen] if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) { return encrypted, errors.New("Unexpected nonce") } var packet []byte var err error if serverInfo.CryptoConstruction == XChacha20Poly1305 { packet, err = xsecretbox.Open(nil, serverNonce, encrypted[responseHeaderLen:], sharedKey[:]) } else { var xsalsaServerNonce [24]byte copy(xsalsaServerNonce[:], serverNonce) var ok bool packet, ok = secretbox.Open(nil, encrypted[responseHeaderLen:], &xsalsaServerNonce, sharedKey) if !ok { err = errors.New("Incorrect tag") } } if err != nil { return encrypted, err } packet, err = unpad(packet) if err != nil || len(packet) < MinDNSPacketSize { return encrypted, errors.New("Incorrect padding") } return packet, nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/service_linux.go0000644000175000017500000000141713556612755021525 0ustar ericeric// +build !android package main import ( "github.com/coreos/go-systemd/daemon" clocksmith "github.com/jedisct1/go-clocksmith" ) func ServiceManagerStartNotify() error { daemon.SdNotify(false, "STATUS=Starting") return nil } func ServiceManagerReadyNotify() error { daemon.SdNotify(false, "READY=1") return systemDWatchdog() } func systemDWatchdog() error { watchdogFailureDelay, err := daemon.SdWatchdogEnabled(false) if err != nil || watchdogFailureDelay == 0 { return err } refreshInterval := watchdogFailureDelay / 3 go func() { for { // TODO There could be other checks this just // checks program is not totally stuck and can // run this goroutine daemon.SdNotify(false, "WATCHDOG=1") clocksmith.Sleep(refreshInterval) } }() return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/dnscrypt_certs.go0000644000175000017500000002165513556612755021722 0ustar ericericpackage main import ( "bytes" "encoding/binary" "errors" "net" "strings" "time" "github.com/jedisct1/dlog" "github.com/miekg/dns" "golang.org/x/crypto/ed25519" ) type CertInfo struct { ServerPk [32]byte SharedKey [32]byte MagicQuery [ClientMagicLen]byte CryptoConstruction CryptoConstruction ForwardSecurity bool } func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk ed25519.PublicKey, serverAddress string, providerName string, isNew bool, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr) (CertInfo, int, error) { if len(pk) != ed25519.PublicKeySize { return CertInfo{}, 0, errors.New("Invalid public key length") } if !strings.HasSuffix(providerName, ".") { providerName = providerName + "." } if serverName == nil { serverName = &providerName } query := new(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 } in, rtt, err := dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr, serverName) if err != nil { dlog.Noticef("[%s] TIMEOUT", *serverName) return CertInfo{}, 0, err } now := uint32(time.Now().Unix()) certInfo := CertInfo{CryptoConstruction: UndefinedConstruction} highestSerial := uint32(0) var certCountStr string for _, answerRr := range in.Answer { var txt string if t, ok := answerRr.(*dns.TXT); !ok { dlog.Noticef("[%v] Extra record of type [%v] found in certificate", providerName, answerRr.Header().Rrtype) continue } else { txt = strings.Join(t.Txt, "") } binCert, err := packTxtString(txt) if err != nil { dlog.Warnf("[%v] Unable to unpack the certificate", providerName) continue } if len(binCert) < 124 { dlog.Warnf("[%v] Certificate too short", providerName) continue } if !bytes.Equal(binCert[:4], CertMagic[:4]) { dlog.Warnf("[%v] Invalid cert magic", providerName) continue } cryptoConstruction := CryptoConstruction(0) switch esVersion := binary.BigEndian.Uint16(binCert[4:6]); esVersion { case 0x0001: cryptoConstruction = XSalsa20Poly1305 case 0x0002: cryptoConstruction = XChacha20Poly1305 default: dlog.Noticef("[%v] Unsupported crypto construction", providerName) continue } signature := binCert[8:72] signed := binCert[72:] if !ed25519.Verify(pk, signed, signature) { dlog.Warnf("[%v] Incorrect signature", providerName) continue } serial := binary.BigEndian.Uint32(binCert[112:116]) tsBegin := binary.BigEndian.Uint32(binCert[116:120]) tsEnd := binary.BigEndian.Uint32(binCert[120:124]) if tsBegin >= tsEnd { dlog.Warnf("[%v] certificate ends before it starts (%v >= %v)", providerName, tsBegin, tsEnd) continue } 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.", providerName, 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", providerName) } 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", providerName) } else if daysLeft <= 30 { dlog.Infof("[%v] certificate will expire in %d days", providerName, daysLeft) } certInfo.ForwardSecurity = false } else { certInfo.ForwardSecurity = true } 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])", providerName, now, tsBegin, tsEnd) continue } } if serial < highestSerial { dlog.Debugf("[%v] Superseded by a previous certificate", providerName) continue } if serial == highestSerial { if cryptoConstruction < certInfo.CryptoConstruction { dlog.Debugf("[%v] Keeping the previous, preferred crypto construction", providerName) continue } else { dlog.Debugf("[%v] Upgrading the construction from %v to %v", providerName, certInfo.CryptoConstruction, cryptoConstruction) } } if cryptoConstruction != XChacha20Poly1305 && cryptoConstruction != XSalsa20Poly1305 { dlog.Noticef("[%v] Cryptographic construction %v not supported", providerName, cryptoConstruction) continue } var serverPk [32]byte copy(serverPk[:], binCert[72:104]) sharedKey := ComputeSharedKey(cryptoConstruction, &proxy.proxySecretKey, &serverPk, &providerName) certInfo.SharedKey = sharedKey highestSerial = serial certInfo.CryptoConstruction = cryptoConstruction copy(certInfo.ServerPk[:], serverPk[:]) copy(certInfo.MagicQuery[:], binCert[104:112]) if isNew { dlog.Noticef("[%s] OK (DNSCrypt) - rtt: %dms%s", *serverName, rtt.Nanoseconds()/1000000, certCountStr) } else { dlog.Infof("[%s] OK (DNSCrypt) - rtt: %dms%s", *serverName, rtt.Nanoseconds()/1000000, certCountStr) } certCountStr = " - additional certificate" } if certInfo.CryptoConstruction == UndefinedConstruction { return certInfo, 0, errors.New("No useable certificate found") } return certInfo, int(rtt.Nanoseconds() / 1000000), nil } func isDigit(b byte) bool { return b >= '0' && b <= '9' } func dddToByte(s []byte) byte { return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } func packTxtString(s string) ([]byte, error) { 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, nil } func dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr, serverName *string) (*dns.Msg, time.Duration, error) { response, ttl, err := _dnsExchange(proxy, proto, query, serverAddress, relayUDPAddr, relayTCPAddr) if err != nil && relayUDPAddr != nil { dlog.Debugf("Unable to get a certificate for [%v] via relay [%v], retrying over a direct connection", *serverName, relayUDPAddr.IP) response, ttl, err = _dnsExchange(proxy, proto, query, serverAddress, nil, nil) if err == nil { dlog.Infof("Direct certificate retrieval for [%v] succeeded", *serverName) } } return response, ttl, err } func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relayUDPAddr *net.UDPAddr, relayTCPAddr *net.TCPAddr) (*dns.Msg, time.Duration, error) { var packet []byte var rtt time.Duration if proto == "udp" { qNameLen, padding := len(query.Question[0].Name), 0 if qNameLen < 480 { padding = 480 - qNameLen } if padding > 0 { opt := new(dns.OPT) opt.Hdr.Name = "." ext := new(dns.EDNS0_PADDING) ext.Padding = make([]byte, padding) opt.Option = append(opt.Option, ext) query.Extra = []dns.RR{opt} } binQuery, err := query.Pack() if err != nil { return nil, 0, err } udpAddr, err := net.ResolveUDPAddr("udp", serverAddress) if err != nil { return nil, 0, err } upstreamAddr := udpAddr if relayUDPAddr != nil { proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery) upstreamAddr = relayUDPAddr } now := time.Now() pc, err := net.DialUDP("udp", nil, upstreamAddr) if err != nil { return nil, 0, err } defer pc.Close() pc.SetDeadline(time.Now().Add(proxy.timeout)) pc.Write(binQuery) packet = make([]byte, MaxDNSPacketSize) length, err := pc.Read(packet) if err != nil { return nil, 0, err } rtt = time.Since(now) packet = packet[:length] } else { binQuery, err := query.Pack() if err != nil { return nil, 0, err } tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddress) if err != nil { return nil, 0, err } upstreamAddr := tcpAddr if relayUDPAddr != nil { proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery) upstreamAddr = relayTCPAddr } now := time.Now() var pc net.Conn proxyDialer := proxy.xTransport.proxyDialer if proxyDialer == nil { pc, err = net.DialTCP("tcp", nil, upstreamAddr) } else { pc, err = (*proxyDialer).Dial("tcp", tcpAddr.String()) } if err != nil { return nil, 0, err } defer pc.Close() pc.SetDeadline(time.Now().Add(proxy.timeout)) binQuery, err = PrefixWithSize(binQuery) if err != nil { return nil, 0, err } pc.Write(binQuery) packet, err = ReadPrefixed(&pc) if err != nil { return nil, 0, err } rtt = time.Since(now) } msg := dns.Msg{} if err := msg.Unpack(packet); err != nil { return nil, 0, err } return &msg, rtt, nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_firefox.go0000644000175000017500000000242413556612755021665 0ustar ericeric// Work around Mozilla's evil plan - https://sk.tl/3Ek6tzhq package main import ( "strings" "github.com/jedisct1/dlog" "github.com/miekg/dns" ) type PluginFirefox struct { } func (plugin *PluginFirefox) Name() string { return "firefox" } func (plugin *PluginFirefox) Description() string { return "Work around Firefox taking over DNS" } func (plugin *PluginFirefox) Init(proxy *Proxy) error { dlog.Noticef("Firefox workaround initialized") return nil } func (plugin *PluginFirefox) Drop() error { return nil } func (plugin *PluginFirefox) Reload() error { return nil } func (plugin *PluginFirefox) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) != 1 { return nil } question := questions[0] if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) { return nil } qName := strings.ToLower(question.Name) if qName != "use-application-dns.net." && !strings.HasSuffix(qName, ".use-application-dns.net.") { return nil } synth, err := EmptyResponseFromMessage(msg) if err != nil { return err } synth.Rcode = dns.RcodeNameError pluginsState.synthResponse = synth pluginsState.action = PluginsActionSynth pluginsState.returnCode = PluginsReturnCodeSynth return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/xtransport.go0000644000175000017500000002552713556612755021102 0ustar ericericpackage main import ( "bytes" "context" "crypto/sha512" "crypto/tls" "encoding/base64" "encoding/hex" "errors" "io/ioutil" "math/rand" "net" "net/http" "net/url" "strconv" "strings" "sync" "time" "github.com/jedisct1/dlog" stamps "github.com/jedisct1/go-dnsstamps" "github.com/miekg/dns" "golang.org/x/net/http2" netproxy "golang.org/x/net/proxy" ) const ( DefaultFallbackResolver = "9.9.9.9:53" DefaultKeepAlive = 5 * time.Second DefaultTimeout = 30 * time.Second SystemResolverTTL = 24 * time.Hour ) type CachedIPItem struct { ip net.IP expiration *time.Time } type CachedIPs struct { sync.RWMutex cache map[string]*CachedIPItem } type XTransport struct { transport *http.Transport keepAlive time.Duration timeout time.Duration cachedIPs CachedIPs fallbackResolver string mainProto string ignoreSystemDNS bool useIPv4 bool useIPv6 bool tlsDisableSessionTickets bool tlsCipherSuite []uint16 proxyDialer *netproxy.Dialer httpProxyFunction func(*http.Request) (*url.URL, error) } func NewXTransport() *XTransport { if err := CheckResolver(DefaultFallbackResolver); err != nil { panic("DefaultFallbackResolver does not parse") } xTransport := XTransport{ cachedIPs: CachedIPs{cache: make(map[string]*CachedIPItem)}, keepAlive: DefaultKeepAlive, timeout: DefaultTimeout, fallbackResolver: DefaultFallbackResolver, mainProto: "", ignoreSystemDNS: false, useIPv4: true, useIPv6: false, tlsDisableSessionTickets: false, tlsCipherSuite: nil, } return &xTransport } func ParseIP(ipStr string) net.IP { return net.ParseIP(strings.TrimRight(strings.TrimLeft(ipStr, "["), "]")) } // If ttl < 0, never expire // Otherwise, ttl is set to max(ttl, xTransport.timeout) func (xTransport *XTransport) saveCachedIP(host string, ip net.IP, ttl time.Duration) { item := &CachedIPItem{ip: ip, expiration: nil} if ttl >= 0 { if ttl < xTransport.timeout { ttl = xTransport.timeout } expiration := time.Now().Add(ttl) item.expiration = &expiration } xTransport.cachedIPs.Lock() xTransport.cachedIPs.cache[host] = item xTransport.cachedIPs.Unlock() } func (xTransport *XTransport) loadCachedIP(host string, deleteIfExpired bool) (net.IP, bool) { xTransport.cachedIPs.RLock() item, ok := xTransport.cachedIPs.cache[host] xTransport.cachedIPs.RUnlock() if !ok { return nil, false } expiration := item.expiration if deleteIfExpired && expiration != nil && time.Until(*expiration) < 0 { xTransport.cachedIPs.Lock() delete(xTransport.cachedIPs.cache, host) xTransport.cachedIPs.Unlock() return nil, false } return item.ip, ok } func (xTransport *XTransport) rebuildTransport() { dlog.Debug("Rebuilding transport") if xTransport.transport != nil { (*xTransport.transport).CloseIdleConnections() } timeout := xTransport.timeout transport := &http.Transport{ DisableKeepAlives: false, DisableCompression: true, MaxIdleConns: 1, IdleConnTimeout: xTransport.keepAlive, ResponseHeaderTimeout: timeout, ExpectContinueTimeout: timeout, MaxResponseHeaderBytes: 4096, DialContext: func(ctx context.Context, network, addrStr string) (net.Conn, error) { host, port := ExtractHostAndPort(addrStr, stamps.DefaultPort) ipOnly := host cachedIP, ok := xTransport.loadCachedIP(host, false) if ok { if ipv4 := cachedIP.To4(); ipv4 != nil { ipOnly = ipv4.String() } else { ipOnly = "[" + cachedIP.String() + "]" } } else { dlog.Debugf("[%s] IP address was not cached", host) } addrStr = ipOnly + ":" + strconv.Itoa(port) if xTransport.proxyDialer == nil { dialer := &net.Dialer{Timeout: timeout, KeepAlive: timeout, DualStack: true} return dialer.DialContext(ctx, network, addrStr) } return (*xTransport.proxyDialer).Dial(network, addrStr) }, } if xTransport.httpProxyFunction != nil { transport.Proxy = xTransport.httpProxyFunction } if xTransport.tlsDisableSessionTickets || xTransport.tlsCipherSuite != nil { tlsClientConfig := tls.Config{ SessionTicketsDisabled: xTransport.tlsDisableSessionTickets, } if !xTransport.tlsDisableSessionTickets { tlsClientConfig.ClientSessionCache = tls.NewLRUClientSessionCache(10) } if xTransport.tlsCipherSuite != nil { tlsClientConfig.PreferServerCipherSuites = false tlsClientConfig.CipherSuites = xTransport.tlsCipherSuite } transport.TLSClientConfig = &tlsClientConfig } http2.ConfigureTransport(transport) xTransport.transport = transport } func (xTransport *XTransport) resolveUsingSystem(host string) (ip net.IP, ttl time.Duration, err error) { ttl = SystemResolverTTL var foundIPs []string foundIPs, err = net.LookupHost(host) if err != nil { return } ips := make([]net.IP, 0) for _, ip := range foundIPs { if foundIP := net.ParseIP(ip); foundIP != nil { if xTransport.useIPv4 { if ipv4 := foundIP.To4(); ipv4 != nil { ips = append(ips, foundIP) } } if xTransport.useIPv6 { if ipv6 := foundIP.To16(); ipv6 != nil { ips = append(ips, foundIP) } } } } if len(ips) > 0 { ip = ips[rand.Intn(len(ips))] } return } func (xTransport *XTransport) resolveUsingResolver(proto, host string, resolver string) (ip net.IP, ttl time.Duration, err error) { dnsClient := dns.Client{Net: proto} if xTransport.useIPv4 { msg := new(dns.Msg) msg.SetQuestion(dns.Fqdn(host), dns.TypeA) msg.SetEdns0(uint16(MaxDNSPacketSize), true) var in *dns.Msg if in, _, err = dnsClient.Exchange(msg, resolver); err == nil { answers := make([]dns.RR, 0) for _, answer := range in.Answer { if answer.Header().Rrtype == dns.TypeA { answers = append(answers, answer) } } if len(answers) > 0 { answer := answers[rand.Intn(len(answers))] ip = answer.(*dns.A).A ttl = time.Duration(answer.Header().Ttl) * time.Second return } } } if xTransport.useIPv6 { msg := new(dns.Msg) msg.SetQuestion(dns.Fqdn(host), dns.TypeAAAA) msg.SetEdns0(uint16(MaxDNSPacketSize), true) var in *dns.Msg if in, _, err = dnsClient.Exchange(msg, resolver); err == nil { answers := make([]dns.RR, 0) for _, answer := range in.Answer { if answer.Header().Rrtype == dns.TypeAAAA { answers = append(answers, answer) } } if len(answers) > 0 { answer := answers[rand.Intn(len(answers))] ip = answer.(*dns.AAAA).AAAA ttl = time.Duration(answer.Header().Ttl) * time.Second return } } } return } func (xTransport *XTransport) resolveHost(host string) (err error) { if xTransport.proxyDialer != nil || xTransport.httpProxyFunction != nil { return } if ParseIP(host) != nil { return } if _, ok := xTransport.loadCachedIP(host, true); ok { return } var foundIP net.IP var ttl time.Duration if !xTransport.ignoreSystemDNS { foundIP, ttl, err = xTransport.resolveUsingSystem(host) } if xTransport.ignoreSystemDNS || err != nil { protos := []string{"udp", "tcp"} if xTransport.mainProto == "tcp" { protos = []string{"tcp", "udp"} } for _, proto := range protos { if err != nil { dlog.Noticef("System DNS configuration not usable yet, exceptionally resolving [%s] using resolver %s[%s]", host, proto, xTransport.fallbackResolver) } else { dlog.Debugf("Resolving [%s] using resolver %s[%s]", host, proto, xTransport.fallbackResolver) } foundIP, ttl, err = xTransport.resolveUsingResolver(proto, host, xTransport.fallbackResolver) if err == nil { break } } } if err != nil { return } xTransport.saveCachedIP(host, foundIP, ttl) dlog.Debugf("[%s] IP address [%s] added to the cache, valid until %v", host, foundIP, ttl) return } func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration, padding *string) (*http.Response, time.Duration, error) { if timeout <= 0 { timeout = xTransport.timeout } client := http.Client{Transport: xTransport.transport, Timeout: timeout} header := map[string][]string{"User-Agent": {"dnscrypt-proxy"}} if len(accept) > 0 { header["Accept"] = []string{accept} } if len(contentType) > 0 { header["Content-Type"] = []string{contentType} } if padding != nil { header["X-Pad"] = []string{*padding} } if body != nil { h := sha512.Sum512(*body) qs := url.Query() qs.Add("body_hash", hex.EncodeToString(h[:32])) url2 := *url url2.RawQuery = qs.Encode() url = &url2 } host, _ := ExtractHostAndPort(url.Host, 0) if xTransport.proxyDialer == nil && strings.HasSuffix(host, ".onion") { return nil, 0, errors.New("Onion service is not reachable without Tor") } if err := xTransport.resolveHost(host); err != nil { return nil, 0, err } req := &http.Request{ Method: method, URL: url, Header: header, Close: false, } if body != nil { req.ContentLength = int64(len(*body)) req.Body = ioutil.NopCloser(bytes.NewReader(*body)) } start := time.Now() resp, err := client.Do(req) rtt := time.Since(start) if err == nil { if resp == nil { err = errors.New("Webserver returned an error") } else if resp.StatusCode < 200 || resp.StatusCode > 299 { err = errors.New(resp.Status) } } else { (*xTransport.transport).CloseIdleConnections() } if err != nil { dlog.Debugf("[%s]: [%s]", req.URL, err) if xTransport.tlsCipherSuite != nil && strings.Contains(err.Error(), "handshake failure") { dlog.Warnf("TLS handshake failure - Try changing or deleting the tls_cipher_suite value in the configuration file") xTransport.tlsCipherSuite = nil xTransport.rebuildTransport() } } return resp, rtt, err } func (xTransport *XTransport) Get(url *url.URL, accept string, timeout time.Duration) (*http.Response, time.Duration, error) { return xTransport.Fetch("GET", url, accept, "", nil, timeout, nil) } func (xTransport *XTransport) Post(url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration, padding *string) (*http.Response, time.Duration, error) { return xTransport.Fetch("POST", url, accept, contentType, body, timeout, padding) } func (xTransport *XTransport) DoHQuery(useGet bool, url *url.URL, body []byte, timeout time.Duration) (*http.Response, time.Duration, error) { padLen := 63 - (len(body)+63)&63 padding := xTransport.makePad(padLen) dataType := "application/dns-message" if useGet { qs := url.Query() qs.Add("ct", "") encBody := base64.RawURLEncoding.EncodeToString(body) qs.Add("dns", encBody) url2 := *url url2.RawQuery = qs.Encode() return xTransport.Get(&url2, dataType, timeout) } return xTransport.Post(url, dataType, dataType, &body, timeout, padding) } func (xTransport *XTransport) makePad(padLen int) *string { if padLen <= 0 { return nil } padding := strings.Repeat("X", padLen) return &padding } dnscrypt-proxy-2.0.31/dnscrypt-proxy/config.go0000644000175000017500000006036713556612755020124 0ustar ericericpackage main import ( "encoding/json" "errors" "flag" "fmt" "math/rand" "net/http" "net/url" "os" "path" "path/filepath" "strconv" "strings" "time" "github.com/BurntSushi/toml" "github.com/jedisct1/dlog" stamps "github.com/jedisct1/go-dnsstamps" netproxy "golang.org/x/net/proxy" ) const ( MaxTimeout = 3600 DefaultNetprobeAddress = "9.9.9.9:53" ) type Config struct { LogLevel int `toml:"log_level"` LogFile *string `toml:"log_file"` UseSyslog bool `toml:"use_syslog"` ServerNames []string `toml:"server_names"` DisabledServerNames []string `toml:"disabled_server_names"` ListenAddresses []string `toml:"listen_addresses"` Daemonize bool UserName string `toml:"user_name"` ForceTCP bool `toml:"force_tcp"` Timeout int `toml:"timeout"` KeepAlive int `toml:"keepalive"` Proxy string `toml:"proxy"` CertRefreshDelay int `toml:"cert_refresh_delay"` CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"` EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"` LBStrategy string `toml:"lb_strategy"` LBEstimator bool `toml:"lb_estimator"` BlockIPv6 bool `toml:"block_ipv6"` Cache bool CacheSize int `toml:"cache_size"` CacheNegTTL uint32 `toml:"cache_neg_ttl"` CacheNegMinTTL uint32 `toml:"cache_neg_min_ttl"` CacheNegMaxTTL uint32 `toml:"cache_neg_max_ttl"` CacheMinTTL uint32 `toml:"cache_min_ttl"` CacheMaxTTL uint32 `toml:"cache_max_ttl"` RejectTTL uint32 `toml:"reject_ttl"` 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"` ForwardFile string `toml:"forwarding_rules"` CloakFile string `toml:"cloaking_rules"` StaticsConfig map[string]StaticConfig `toml:"static"` SourcesConfig map[string]SourceConfig `toml:"sources"` SourceRequireDNSSEC bool `toml:"require_dnssec"` SourceRequireNoLog bool `toml:"require_nolog"` SourceRequireNoFilter bool `toml:"require_nofilter"` SourceDNSCrypt bool `toml:"dnscrypt_servers"` SourceDoH bool `toml:"doh_servers"` SourceIPv4 bool `toml:"ipv4_servers"` SourceIPv6 bool `toml:"ipv6_servers"` MaxClients uint32 `toml:"max_clients"` FallbackResolver string `toml:"fallback_resolver"` IgnoreSystemDNS bool `toml:"ignore_system_dns"` AllWeeklyRanges map[string]WeeklyRangesStr `toml:"schedules"` LogMaxSize int `toml:"log_files_max_size"` LogMaxAge int `toml:"log_files_max_age"` LogMaxBackups int `toml:"log_files_max_backups"` TLSDisableSessionTickets bool `toml:"tls_disable_session_tickets"` TLSCipherSuite []uint16 `toml:"tls_cipher_suite"` NetprobeAddress string `toml:"netprobe_address"` NetprobeTimeout int `toml:"netprobe_timeout"` OfflineMode bool `toml:"offline_mode"` HTTPProxyURL string `toml:"http_proxy"` RefusedCodeInResponses bool `toml:"refused_code_in_responses"` BlockedQueryResponse string `toml:"blocked_query_response"` QueryMeta []string `toml:"query_meta"` AnonymizedDNS AnonymizedDNSConfig `toml:"anonymized_dns"` } func newConfig() Config { return Config{ LogLevel: int(dlog.LogLevel()), ListenAddresses: []string{"127.0.0.1:53"}, Timeout: 5000, KeepAlive: 5, CertRefreshDelay: 240, CertIgnoreTimestamp: false, EphemeralKeys: false, Cache: true, CacheSize: 512, CacheNegTTL: 0, CacheNegMinTTL: 60, CacheNegMaxTTL: 600, CacheMinTTL: 60, CacheMaxTTL: 86400, RejectTTL: 600, CloakTTL: 600, SourceRequireNoLog: true, SourceRequireNoFilter: true, SourceIPv4: true, SourceIPv6: false, SourceDNSCrypt: true, SourceDoH: true, MaxClients: 250, FallbackResolver: DefaultFallbackResolver, IgnoreSystemDNS: false, LogMaxSize: 10, LogMaxAge: 7, LogMaxBackups: 1, TLSDisableSessionTickets: false, TLSCipherSuite: nil, NetprobeTimeout: 60, OfflineMode: false, RefusedCodeInResponses: false, LBEstimator: true, BlockedQueryResponse: "hinfo", } } type StaticConfig struct { Stamp string } type SourceConfig struct { URL string URLs []string MinisignKeyStr string `toml:"minisign_key"` CacheFile string `toml:"cache_file"` FormatStr string `toml:"format"` RefreshDelay int `toml:"refresh_delay"` Prefix string } type QueryLogConfig struct { File string Format string IgnoredQtypes []string `toml:"ignored_qtypes"` } type NxLogConfig struct { File string Format string } type BlockNameConfig struct { File string `toml:"blacklist_file"` LogFile string `toml:"log_file"` Format string `toml:"log_format"` } type WhitelistNameConfig struct { File string `toml:"whitelist_file"` LogFile string `toml:"log_file"` Format string `toml:"log_format"` } type BlockIPConfig struct { File string `toml:"blacklist_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"` } type ServerSummary struct { Name string `json:"name"` Proto string `json:"proto"` IPv6 bool `json:"ipv6"` Addrs []string `json:"addrs,omitempty"` Ports []int `json:"ports"` DNSSEC bool `json:"dnssec"` NoLog bool `json:"nolog"` NoFilter bool `json:"nofilter"` Description string `json:"description,omitempty"` Stamp string `json:"stamp"` } func findConfigFile(configFile *string) (string, error) { if _, err := os.Stat(*configFile); os.IsNotExist(err) { cdLocal() if _, err := os.Stat(*configFile); err != nil { return "", err } } pwd, err := os.Getwd() if err != nil { return "", err } if filepath.IsAbs(*configFile) { return *configFile, nil } return path.Join(pwd, *configFile), nil } func ConfigLoad(proxy *Proxy, svcFlag *string) error { version := flag.Bool("version", false, "print current proxy version") resolve := flag.String("resolve", "", "resolve a name using system libraries") list := flag.Bool("list", false, "print the list of available resolvers for the enabled filters") listAll := flag.Bool("list-all", false, "print the complete list of available resolvers, ignoring filters") jsonOutput := flag.Bool("json", false, "output list as JSON") check := flag.Bool("check", false, "check the configuration file and exit") configFile := flag.String("config", DefaultConfigFileName, "Path to the configuration file") child := flag.Bool("child", false, "Invokes program as a child process") netprobeTimeoutOverride := flag.Int("netprobe-timeout", 60, "Override the netprobe timeout") showCerts := flag.Bool("show-certs", false, "print DoH certificate chain hashes") flag.Parse() if *svcFlag == "stop" || *svcFlag == "uninstall" { return nil } if *version { fmt.Println(AppVersion) os.Exit(0) } if resolve != nil && len(*resolve) > 0 { Resolve(*resolve) os.Exit(0) } foundConfigFile, err := findConfigFile(configFile) if err != nil { dlog.Fatalf("Unable to load the configuration file [%s] -- Maybe use the -config command-line switch?", *configFile) } 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]) } cdFileDir(foundConfigFile) if config.LogLevel >= 0 && config.LogLevel < int(dlog.SeverityLast) { dlog.SetLogLevel(dlog.Severity(config.LogLevel)) } if dlog.LogLevel() <= dlog.SeverityDebug && os.Getenv("DEBUG") == "" { dlog.SetLogLevel(dlog.SeverityInfo) } if config.UseSyslog { dlog.UseSyslog(true) } else if config.LogFile != nil { dlog.UseLogFile(*config.LogFile) if !*child { FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor()) } else { FileDescriptorNum++ dlog.SetFileDescriptor(os.NewFile(uintptr(3), "logFile")) } } proxy.logMaxSize = config.LogMaxSize proxy.logMaxAge = config.LogMaxAge proxy.logMaxBackups = config.LogMaxBackups proxy.userName = config.UserName proxy.child = *child proxy.xTransport = NewXTransport() proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite proxy.xTransport.mainProto = proxy.mainProto if len(config.FallbackResolver) > 0 { if err := CheckResolver(config.FallbackResolver); err != nil { dlog.Fatalf("fallback_resolver [%v]", err) } proxy.xTransport.ignoreSystemDNS = config.IgnoreSystemDNS } proxy.xTransport.fallbackResolver = config.FallbackResolver 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) } proxy.xTransport.httpProxyFunction = http.ProxyURL(httpProxyURL) } 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) } proxyDialer, err := netproxy.FromURL(proxyDialerURL, netproxy.Direct) if err != nil { dlog.Fatalf("Unable to use the proxy: [%v]", err) } proxy.xTransport.proxyDialer = &proxyDialer proxy.mainProto = "tcp" } proxy.xTransport.rebuildTransport() if md.IsDefined("refused_code_in_responses") { dlog.Notice("config option `refused_code_in_responses` is deprecated, use `blocked_query_response`") if config.RefusedCodeInResponses { config.BlockedQueryResponse = "refused" } else { config.BlockedQueryResponse = "hinfo" } } proxy.blockedQueryResponse = config.BlockedQueryResponse proxy.timeout = time.Duration(config.Timeout) * time.Millisecond proxy.maxClients = config.MaxClients proxy.mainProto = "udp" if config.ForceTCP { proxy.mainProto = "tcp" } proxy.certRefreshDelay = time.Duration(config.CertRefreshDelay) * time.Minute proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second) proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp proxy.ephemeralKeys = config.EphemeralKeys if len(config.ListenAddresses) == 0 { dlog.Debug("No local IP/port configured") } lbStrategy := DefaultLBStrategy switch strings.ToLower(config.LBStrategy) { case "": // default case "p2": lbStrategy = LBStrategyP2 case "ph": lbStrategy = LBStrategyPH case "fastest": case "first": lbStrategy = LBStrategyFirst case "random": lbStrategy = LBStrategyRandom default: dlog.Warnf("Unknown load balancing strategy: [%s]", config.LBStrategy) } proxy.serversInfo.lbStrategy = lbStrategy proxy.serversInfo.lbEstimator = config.LBEstimator proxy.listenAddresses = config.ListenAddresses proxy.daemonize = config.Daemonize proxy.pluginBlockIPv6 = config.BlockIPv6 proxy.cache = config.Cache proxy.cacheSize = config.CacheSize if config.CacheNegTTL > 0 { proxy.cacheNegMinTTL = config.CacheNegTTL proxy.cacheNegMaxTTL = config.CacheNegTTL } else { proxy.cacheNegMinTTL = config.CacheNegMinTTL proxy.cacheNegMaxTTL = config.CacheNegMaxTTL } proxy.cacheMinTTL = config.CacheMinTTL proxy.cacheMaxTTL = config.CacheMaxTTL proxy.rejectTTL = config.RejectTTL proxy.cloakTTL = config.CloakTTL proxy.queryMeta = config.QueryMeta if len(config.QueryLog.Format) == 0 { config.QueryLog.Format = "tsv" } else { config.QueryLog.Format = strings.ToLower(config.QueryLog.Format) } if config.QueryLog.Format != "tsv" && config.QueryLog.Format != "ltsv" { return errors.New("Unsupported query log format") } proxy.queryLogFile = config.QueryLog.File proxy.queryLogFormat = config.QueryLog.Format proxy.queryLogIgnoredQtypes = config.QueryLog.IgnoredQtypes if len(config.NxLog.Format) == 0 { config.NxLog.Format = "tsv" } else { config.NxLog.Format = strings.ToLower(config.NxLog.Format) } if config.NxLog.Format != "tsv" && config.NxLog.Format != "ltsv" { return errors.New("Unsupported NX log format") } proxy.nxLogFile = config.NxLog.File proxy.nxLogFormat = config.NxLog.Format if len(config.BlockName.Format) == 0 { config.BlockName.Format = "tsv" } else { config.BlockName.Format = strings.ToLower(config.BlockName.Format) } if config.BlockName.Format != "tsv" && config.BlockName.Format != "ltsv" { return errors.New("Unsupported block log format") } proxy.blockNameFile = config.BlockName.File proxy.blockNameFormat = config.BlockName.Format proxy.blockNameLogFile = config.BlockName.LogFile if len(config.WhitelistName.Format) == 0 { config.WhitelistName.Format = "tsv" } else { config.WhitelistName.Format = strings.ToLower(config.WhitelistName.Format) } if config.WhitelistName.Format != "tsv" && config.WhitelistName.Format != "ltsv" { return errors.New("Unsupported whitelist log format") } proxy.whitelistNameFile = config.WhitelistName.File proxy.whitelistNameFormat = config.WhitelistName.Format proxy.whitelistNameLogFile = config.WhitelistName.LogFile if len(config.BlockIP.Format) == 0 { config.BlockIP.Format = "tsv" } else { config.BlockIP.Format = strings.ToLower(config.BlockIP.Format) } if config.BlockIP.Format != "tsv" && config.BlockIP.Format != "ltsv" { return errors.New("Unsupported IP block log format") } proxy.blockIPFile = config.BlockIP.File proxy.blockIPFormat = config.BlockIP.Format proxy.blockIPLogFile = config.BlockIP.LogFile proxy.forwardFile = config.ForwardFile proxy.cloakFile = config.CloakFile allWeeklyRanges, err := ParseAllWeeklyRanges(config.AllWeeklyRanges) if err != nil { return err } proxy.allWeeklyRanges = allWeeklyRanges if configRoutes := config.AnonymizedDNS.Routes; configRoutes != nil { routes := make(map[string][]string) for _, configRoute := range configRoutes { routes[configRoute.ServerName] = configRoute.RelayNames } proxy.routes = &routes } if *listAll { config.ServerNames = nil config.DisabledServerNames = nil config.SourceRequireDNSSEC = false config.SourceRequireNoFilter = false config.SourceRequireNoLog = false config.SourceIPv4 = true config.SourceIPv6 = true config.SourceDNSCrypt = true config.SourceDoH = true } netprobeTimeout := config.NetprobeTimeout flag.Visit(func(flag *flag.Flag) { if flag.Name == "netprobe-timeout" && netprobeTimeoutOverride != nil { netprobeTimeout = *netprobeTimeoutOverride } }) netprobeAddress := DefaultNetprobeAddress if len(config.NetprobeAddress) > 0 { netprobeAddress = config.NetprobeAddress } else if len(config.FallbackResolver) > 0 { netprobeAddress = config.FallbackResolver } proxy.showCerts = *showCerts || len(os.Getenv("SHOW_CERTS")) > 0 if proxy.showCerts { proxy.listenAddresses = nil } dlog.Noticef("dnscrypt-proxy %s", AppVersion) if err := NetProbe(netprobeAddress, netprobeTimeout); err != nil { return err } if !config.OfflineMode { if err := config.loadSources(proxy); err != nil { return err } if len(proxy.registeredServers) == 0 { return errors.New("No servers configured") } } if *list || *listAll { config.printRegisteredServers(proxy, *jsonOutput) 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) } else { dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via) } hasSpecificRoutes = true } } if via, ok := (*proxy.routes)["*"]; ok { if hasSpecificRoutes { dlog.Noticef("Anonymized DNS: routing everything else via %v", via) } else { dlog.Noticef("Anonymized DNS: routing everything via %v", via) } } } if *check { dlog.Notice("Configuration successfully checked") os.Exit(0) } return nil } func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) { var summary []ServerSummary 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 { providerName := registeredServer.stamp.ProviderName var host string host, port = ExtractHostAndPort(providerName, port) addrs = append(addrs, host) } if len(addrStr) > 0 { addrs = append(addrs, hostAddr) } 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, NoLog: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoLog != 0, NoFilter: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoFilter != 0, Description: registeredServer.description, Stamp: registeredServer.stamp.String(), } if jsonOutput { summary = append(summary, serverSummary) } else { fmt.Println(serverSummary.Name) } } if jsonOutput { jsonStr, err := json.MarshalIndent(summary, "", " ") if err != nil { dlog.Fatal(err) } fmt.Print(string(jsonStr)) } } 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 { return err } } if len(config.ServerNames) == 0 { for serverName := range config.StaticsConfig { config.ServerNames = append(config.ServerNames, serverName) } } for _, serverName := range config.ServerNames { staticConfig, ok := config.StaticsConfig[serverName] if !ok { continue } if len(staticConfig.Stamp) == 0 { dlog.Fatalf("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) } 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] }) return nil } func (config *Config) loadSource(proxy *Proxy, requiredProps stamps.ServerInformalProperties, cfgSourceName string, cfgSource *SourceConfig) error { if len(cfgSource.URLs) == 0 { if len(cfgSource.URL) == 0 { dlog.Debugf("Missing URLs for source [%s]", cfgSourceName) } else { cfgSource.URLs = []string{cfgSource.URL} } } if cfgSource.MinisignKeyStr == "" { return fmt.Errorf("Missing Minisign key for source [%s]", cfgSourceName) } if cfgSource.CacheFile == "" { return fmt.Errorf("Missing cache file for source [%s]", cfgSourceName) } if cfgSource.FormatStr == "" { cfgSource.FormatStr = "v2" } if cfgSource.RefreshDelay <= 0 { cfgSource.RefreshDelay = 72 } source, sourceUrlsToPrefetch, err := NewSource(proxy.xTransport, cfgSource.URLs, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour) proxy.urlsToPrefetch = append(proxy.urlsToPrefetch, sourceUrlsToPrefetch...) if err != nil { dlog.Criticalf("Unable to retrieve source [%s]: [%s]", cfgSourceName, err) return err } registeredServers, err := source.Parse(cfgSource.Prefix) if err != nil { if len(registeredServers) == 0 { dlog.Criticalf("Unable to use 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) } } return nil } func includesName(names []string, name string) bool { for _, found := range names { if strings.EqualFold(found, name) { return true } } return false } func cdFileDir(fileName string) { os.Chdir(filepath.Dir(fileName)) } func cdLocal() { exeFileName, err := os.Executable() if err != nil { dlog.Warnf("Unable to determine the executable directory: [%s] -- You will need to specify absolute paths in the configuration file", err) return } os.Chdir(filepath.Dir(exeFileName)) } func CheckResolver(resolver string) error { host, port := ExtractHostAndPort(resolver, -1) if ip := ParseIP(host); ip == nil { return fmt.Errorf("Host does not parse as IP '%s'", resolver) } else if port == -1 { return fmt.Errorf("Port missing '%s'", resolver) } else if _, err := strconv.ParseUint(strconv.Itoa(port), 10, 16); err != nil { return fmt.Errorf("Port does not parse '%s' [%v]", resolver, err) } return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/example-blacklist.txt0000644000175000017500000000151113556612755022454 0ustar ericeric ########################### # Blacklist # ########################### ## Rules for name-based query blocking, one per line ## ## Example of valid patterns: ## ## ads.* | matches anything with an "ads." prefix ## *.example.com | matches example.com and all names within that zone such as www.example.com ## example.com | identical to the above ## =example.com | block example.com but not *.example.com ## *sex* | matches any name containing that substring ## ads[0-9]* | matches "ads" followed by one or more digits ## ads*.example* | *, ? and [] can be used anywhere, but prefixes/suffixes are faster ad.* ads.* banner.* banners.* creatives.* oas.* oascentral.* stats.* tag.* telemetry.* tracker.* *.local eth0.me *.workgroup ## Time-based rules # *.youtube.* @time-to-sleep # facebook.com @work dnscrypt-proxy-2.0.31/dnscrypt-proxy/main.go0000644000175000017500000000504313556612755017571 0ustar ericericpackage main import ( crypto_rand "crypto/rand" "encoding/binary" "flag" "fmt" "math/rand" "os" "sync" "github.com/facebookgo/pidfile" "github.com/jedisct1/dlog" "github.com/kardianos/service" ) const ( AppVersion = "2.0.31" DefaultConfigFileName = "dnscrypt-proxy.toml" ) type App struct { wg sync.WaitGroup quit chan struct{} proxy *Proxy } func main() { dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON") os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1") seed := make([]byte, 8) crypto_rand.Read(seed) rand.Seed(int64(binary.LittleEndian.Uint64(seed[:]))) pwd, err := os.Getwd() if err != nil { dlog.Fatal("Unable to find the path to the current directory") } svcConfig := &service.Config{ Name: "dnscrypt-proxy", DisplayName: "DNSCrypt client proxy", Description: "Encrypted/authenticated DNS proxy", WorkingDirectory: pwd, } svcFlag := flag.String("service", "", fmt.Sprintf("Control the system service: %q", service.ControlAction)) app := &App{} svc, err := service.New(app, svcConfig) if err != nil { svc = nil dlog.Debug(err) } app.proxy = NewProxy() _ = ServiceManagerStartNotify() if err := ConfigLoad(app.proxy, svcFlag); err != nil { dlog.Fatal(err) } if len(*svcFlag) != 0 { if svc == nil { dlog.Fatal("Built-in service installation is not supported on this platform") } if err := service.Control(svc, *svcFlag); err != nil { dlog.Fatal(err) } if *svcFlag == "install" { dlog.Notice("Installed as a service. Use `-service start` to start") } else if *svcFlag == "uninstall" { dlog.Notice("Service uninstalled") } else if *svcFlag == "start" { dlog.Notice("Service started") } else if *svcFlag == "stop" { dlog.Notice("Service stopped") } else if *svcFlag == "restart" { dlog.Notice("Service restarted") } return } if svc != nil { if err = svc.Run(); err != nil { dlog.Fatal(err) } } else { app.Start(nil) } } func (app *App) Start(service service.Service) error { if err := app.proxy.InitPluginsGlobals(); err != nil { dlog.Fatal(err) } app.quit = make(chan struct{}) app.wg.Add(1) if service != nil { go func() { app.AppMain() }() } else { app.AppMain() } return nil } func (app *App) AppMain() { pidfile.Write() app.proxy.StartProxy() <-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) } dlog.Notice("Stopped.") return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/service_android.go0000644000175000017500000000022013556612755021775 0ustar ericeric// +build android package main func ServiceManagerStartNotify() error { return nil } func ServiceManagerReadyNotify() error { return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_block_ipv6.go0000644000175000017500000000325513556612755022264 0ustar ericericpackage main import ( "strings" "github.com/miekg/dns" ) type PluginBlockIPv6 struct{} func (plugin *PluginBlockIPv6) Name() string { return "block_ipv6" } func (plugin *PluginBlockIPv6) Description() string { return "Immediately return a synthetic response to AAAA queries." } func (plugin *PluginBlockIPv6) Init(proxy *Proxy) error { return nil } func (plugin *PluginBlockIPv6) Drop() error { return nil } func (plugin *PluginBlockIPv6) Reload() error { return nil } func (plugin *PluginBlockIPv6) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) != 1 { return nil } question := questions[0] if question.Qclass != dns.ClassINET || question.Qtype != dns.TypeAAAA { return nil } synth, err := EmptyResponseFromMessage(msg) if err != nil { return err } hinfo := new(dns.HINFO) 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" synth.Answer = []dns.RR{hinfo} qName := question.Name i := strings.Index(qName, ".") parentZone := "." if !(i < 0 || i+1 >= len(qName)) { parentZone = qName[i+1:] } soa := new(dns.SOA) soa.Mbox = "h.invalid." soa.Ns = "a.root-servers.net." soa.Serial = 1 soa.Refresh = 10000 soa.Minttl = 2400 soa.Expire = 604800 soa.Retry = 300 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 pluginsState.returnCode = PluginsReturnCodeSynth return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_block_name.go0000644000175000017500000000741413556612755022321 0ustar ericericpackage main import ( "errors" "fmt" "net" "strings" "time" "unicode" "github.com/jedisct1/dlog" "github.com/miekg/dns" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) type PluginBlockName struct { allWeeklyRanges *map[string]WeeklyRanges patternMatcher *PatternMatcher logger *lumberjack.Logger format string } func (plugin *PluginBlockName) Name() string { return "block_name" } func (plugin *PluginBlockName) Description() string { return "Block DNS queries matching name patterns" } func (plugin *PluginBlockName) Init(proxy *Proxy) error { dlog.Noticef("Loading the set of blocking rules from [%s]", proxy.blockNameFile) bin, err := ReadTextFile(proxy.blockNameFile) if err != nil { return err } plugin.allWeeklyRanges = proxy.allWeeklyRanges plugin.patternMatcher = NewPatternPatcher() for lineNo, line := range strings.Split(string(bin), "\n") { line = strings.TrimFunc(line, unicode.IsSpace) if len(line) == 0 || strings.HasPrefix(line, "#") { continue } parts := strings.Split(line, "@") timeRangeName := "" if len(parts) == 2 { line = strings.TrimFunc(parts[0], unicode.IsSpace) timeRangeName = strings.TrimFunc(parts[1], unicode.IsSpace) } else if len(parts) > 2 { dlog.Errorf("Syntax error in block 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.blockNameLogFile) == 0 { return nil } plugin.logger = &lumberjack.Logger{LocalTime: true, MaxSize: proxy.logMaxSize, MaxAge: proxy.logMaxAge, MaxBackups: proxy.logMaxBackups, Filename: proxy.blockNameLogFile, Compress: true} plugin.format = proxy.blockNameFormat return nil } func (plugin *PluginBlockName) Drop() error { return nil } func (plugin *PluginBlockName) Reload() error { return nil } func (plugin *PluginBlockName) Eval(pluginsState *PluginsState, msg *dns.Msg) error { if pluginsState.sessionData["whitelisted"] != nil { return nil } questions := msg.Question if len(questions) != 1 { return nil } qName := strings.ToLower(StripTrailingDot(questions[0].Name)) reject, reason, xweeklyRanges := plugin.patternMatcher.Eval(qName) var weeklyRanges *WeeklyRanges if xweeklyRanges != nil { weeklyRanges = xweeklyRanges.(*WeeklyRanges) } if reject { if weeklyRanges != nil && !weeklyRanges.Match() { reject = false } } if reject { pluginsState.action = PluginsActionReject pluginsState.returnCode = PluginsReturnCodeReject 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 } dnscrypt-proxy-2.0.31/dnscrypt-proxy/proxy.go0000644000175000017500000003740613556612755020036 0ustar ericericpackage main import ( crypto_rand "crypto/rand" "encoding/binary" "io" "io/ioutil" "net" "os" "sync/atomic" "time" "github.com/jedisct1/dlog" clocksmith "github.com/jedisct1/go-clocksmith" stamps "github.com/jedisct1/go-dnsstamps" "github.com/miekg/dns" "golang.org/x/crypto/curve25519" ) 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 daemonize bool registeredServers []RegisteredServer registeredRelays []RegisteredServer pluginBlockIPv6 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 urlsToPrefetch []URLToPrefetch clientsCount uint32 maxClients uint32 xTransport *XTransport allWeeklyRanges *map[string]WeeklyRanges logMaxSize int logMaxAge int logMaxBackups int blockedQueryResponse string queryMeta []string routes *map[string][]string showCerts bool } func (proxy *Proxy) StartProxy() { proxy.questionSizeEstimator = NewQuestionSizeEstimator() if _, err := crypto_rand.Read(proxy.proxySecretKey[:]); err != nil { 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 { listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr) if err != nil { dlog.Fatal(err) } listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr) if err != nil { dlog.Fatal(err) } // if 'userName' is not set, continue as before if !(len(proxy.userName) > 0) { if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil { dlog.Fatal(err) } if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil { dlog.Fatal(err) } } else { // if 'userName' is set and we are the parent process if !proxy.child { // parent listenerUDP, err := net.ListenUDP("udp", listenUDPAddr) if err != nil { dlog.Fatal(err) } listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr) if err != nil { dlog.Fatal(err) } fdUDP, err := listenerUDP.File() // On Windows, the File method of UDPConn is not implemented. if err != nil { dlog.Fatalf("Unable to switch to a different user: %v", err) } fdTCP, err := listenerTCP.File() // On Windows, the File method of TCPListener is not implemented. if err != nil { dlog.Fatalf("Unable to switch to a different user: %v", err) } defer listenerUDP.Close() defer listenerTCP.Close() FileDescriptors = append(FileDescriptors, fdUDP) FileDescriptors = append(FileDescriptors, fdTCP) // if 'userName' is set and we are the child process } else { // child listenerUDP, err := net.FilePacketConn(os.NewFile(uintptr(3+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")) 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)) dlog.Noticef("Now listening to %v [TCP]", listenAddrStr) go proxy.tcpListener(listenerTCP.(*net.TCPListener)) } } } // 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) } liveServers, err := proxy.serversInfo.refresh(proxy) if liveServers > 0 { proxy.certIgnoreTimestamp = false } if proxy.showCerts { os.Exit(0) } 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") } proxy.prefetcher(&proxy.urlsToPrefetch) if len(proxy.serversInfo.registeredServers) > 0 { go func() { for { delay := proxy.certRefreshDelay if liveServers == 0 { delay = proxy.certRefreshDelayAfterFailure } clocksmith.Sleep(delay) liveServers, _ = proxy.serversInfo.refresh(proxy) if liveServers > 0 { proxy.certIgnoreTimestamp = false } } }() } } func (proxy *Proxy) prefetcher(urlsToPrefetch *[]URLToPrefetch) { go func() { for { now := time.Now() for i := range *urlsToPrefetch { urlToPrefetch := &(*urlsToPrefetch)[i] if now.After(urlToPrefetch.when) { dlog.Debugf("Prefetching [%s]", urlToPrefetch.url) if err := PrefetchSourceURL(proxy.xTransport, urlToPrefetch); err != nil { dlog.Debugf("Prefetching [%s] failed: %s", urlToPrefetch.url, err) } else { dlog.Debugf("Prefetching [%s] succeeded. Next refresh scheduled for %v", urlToPrefetch.url, urlToPrefetch.when) } } } clocksmith.Sleep(60 * time.Second) } }() } func (proxy *Proxy) udpListener(clientPc *net.UDPConn) { defer clientPc.Close() for { buffer := make([]byte, MaxDNSPacketSize-1) length, clientAddr, err := clientPc.ReadFrom(buffer) if err != nil { return } packet := buffer[:length] go func() { start := time.Now() if !proxy.clientsCountInc() { dlog.Warnf("Too many connections (max=%d)", proxy.maxClients) return } defer proxy.clientsCountDec() proxy.processIncomingQuery(proxy.serversInfo.getOne(), "udp", proxy.mainProto, packet, &clientAddr, clientPc, start) }() } } 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 { clientPc, err := acceptPc.Accept() if err != nil { continue } go func() { start := time.Now() defer clientPc.Close() if !proxy.clientsCountInc() { dlog.Warnf("Too many connections (max=%d)", proxy.maxClients) return } defer proxy.clientsCountDec() clientPc.SetDeadline(time.Now().Add(proxy.timeout)) packet, err := ReadPrefixed(&clientPc) if err != nil { return } clientAddr := clientPc.RemoteAddr() proxy.processIncomingQuery(proxy.serversInfo.getOne(), "tcp", "tcp", packet, &clientAddr, clientPc, start) }() } } func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error { acceptPc, err := net.ListenTCP("tcp", listenAddr) if err != nil { return err } dlog.Noticef("Now listening to %v [TCP]", listenAddr) go proxy.tcpListener(acceptPc) return 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()...) var tmp [2]byte binary.BigEndian.PutUint16(tmp[0:2], uint16(port)) relayedQuery = append(relayedQuery, tmp[:]...) relayedQuery = append(relayedQuery, *encryptedQuery...) *encryptedQuery = relayedQuery } 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 } pc, err := net.DialUDP("udp", nil, upstreamAddr) if err != nil { return nil, err } defer pc.Close() pc.SetDeadline(time.Now().Add(serverInfo.Timeout)) if serverInfo.RelayUDPAddr != nil { proxy.prepareForRelay(serverInfo.UDPAddr.IP, serverInfo.UDPAddr.Port, &encryptedQuery) } pc.Write(encryptedQuery) encryptedResponse := make([]byte, MaxDNSPacketSize) length, err := pc.Read(encryptedResponse) if err != nil { return nil, err } encryptedResponse = encryptedResponse[:length] return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce) } 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 } var err error var pc net.Conn proxyDialer := proxy.xTransport.proxyDialer if proxyDialer == nil { pc, err = net.DialTCP("tcp", nil, upstreamAddr) } else { pc, err = (*proxyDialer).Dial("tcp", serverInfo.TCPAddr.String()) } if err != nil { return nil, err } defer pc.Close() pc.SetDeadline(time.Now().Add(serverInfo.Timeout)) if serverInfo.RelayTCPAddr != nil { proxy.prepareForRelay(serverInfo.TCPAddr.IP, serverInfo.TCPAddr.Port, &encryptedQuery) } encryptedQuery, err = PrefixWithSize(encryptedQuery) if err != nil { return nil, err } pc.Write(encryptedQuery) encryptedResponse, err := ReadPrefixed(&pc) if err != nil { return nil, err } return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce) } func (proxy *Proxy) clientsCountInc() bool { for { count := atomic.LoadUint32(&proxy.clientsCount) if count >= proxy.maxClients { return false } if atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count+1) { dlog.Debugf("clients count: %d", count+1) return true } } } func (proxy *Proxy) clientsCountDec() { for { if count := atomic.LoadUint32(&proxy.clientsCount); count == 0 || atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count-1) { break } } } func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, clientProto string, serverProto string, query []byte, clientAddr *net.Addr, clientPc net.Conn, start time.Time) { if len(query) < MinDNSPacketSize { return } pluginsState := NewPluginsState(proxy, clientProto, clientAddr, start) defer pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals) serverName := "-" if serverInfo != nil { serverName = serverInfo.Name } query, _ = pluginsState.ApplyQueryPlugins(&proxy.pluginsGlobals, query, serverName) if len(query) < MinDNSPacketSize || len(query) > MaxDNSPacketSize { return } var response []byte var err error if pluginsState.action != PluginsActionForward { if pluginsState.synthResponse != nil { response, err = pluginsState.synthResponse.PackBuffer(response) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError return } } if pluginsState.action == PluginsActionDrop { pluginsState.returnCode = PluginsReturnCodeDrop return } } else { pluginsState.returnCode = PluginsReturnCodeForward } if len(response) == 0 && serverInfo != nil { var ttl *uint32 if serverInfo.Proto == stamps.StampProtoTypeDNSCrypt { sharedKey, encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError return } serverInfo.noticeBegin(proxy) if serverProto == "udp" { response, err = proxy.exchangeWithUDPServer(serverInfo, sharedKey, encryptedQuery, clientNonce) if err == nil && len(response) >= MinDNSPacketSize && response[2]&0x02 == 0x02 { serverProto = "tcp" sharedKey, encryptedQuery, clientNonce, err = proxy.Encrypt(serverInfo, query, serverProto) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError return } response, err = proxy.exchangeWithTCPServer(serverInfo, sharedKey, encryptedQuery, clientNonce) } } else { response, err = proxy.exchangeWithTCPServer(serverInfo, sharedKey, encryptedQuery, clientNonce) } if err != nil { if neterr, ok := err.(net.Error); ok && neterr.Timeout() { pluginsState.returnCode = PluginsReturnCodeServerTimeout } else { pluginsState.returnCode = PluginsReturnCodeServerError } serverInfo.noticeFailure(proxy) return } } else if serverInfo.Proto == stamps.StampProtoTypeDoH { tid := TransactionID(query) SetTransactionID(query, 0) serverInfo.noticeBegin(proxy) resp, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout) SetTransactionID(query, tid) if err != nil { pluginsState.returnCode = PluginsReturnCodeServerError serverInfo.noticeFailure(proxy) return } response, err = ioutil.ReadAll(io.LimitReader(resp.Body, int64(MaxDNSPacketSize))) if err != nil { pluginsState.returnCode = PluginsReturnCodeServerError serverInfo.noticeFailure(proxy) return } if len(response) >= MinDNSPacketSize { SetTransactionID(response, tid) } } else { dlog.Fatal("Unsupported protocol") } if len(response) < MinDNSPacketSize || len(response) > MaxDNSPacketSize { pluginsState.returnCode = PluginsReturnCodeParseError serverInfo.noticeFailure(proxy) return } response, err = pluginsState.ApplyResponsePlugins(&proxy.pluginsGlobals, response, ttl) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError serverInfo.noticeFailure(proxy) return } if rcode := Rcode(response); rcode == dns.RcodeServerFailure { // SERVFAIL dlog.Infof("Server [%v] returned temporary error code [%v] -- Upstream server may be experiencing connectivity issues", serverInfo.Name, rcode) serverInfo.noticeFailure(proxy) } else { serverInfo.noticeSuccess(proxy) } } if len(response) < MinDNSPacketSize || len(response) > MaxDNSPacketSize { pluginsState.returnCode = PluginsReturnCodeParseError if serverInfo != nil { serverInfo.noticeFailure(proxy) } return } if clientProto == "udp" { if len(response) > pluginsState.maxUnencryptedUDPSafePayloadSize { response, err = TruncatedResponse(response) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError return } } clientPc.(net.PacketConn).WriteTo(response, *clientAddr) if HasTCFlag(response) { proxy.questionSizeEstimator.blindAdjust() } else { proxy.questionSizeEstimator.adjust(ResponseOverhead + len(response)) } } else { response, err = PrefixWithSize(response) if err != nil { pluginsState.returnCode = PluginsReturnCodeParseError if serverInfo != nil { serverInfo.noticeFailure(proxy) } return } clientPc.Write(response) } } func NewProxy() *Proxy { return &Proxy{ serversInfo: NewServersInfo(), } } dnscrypt-proxy-2.0.31/dnscrypt-proxy/.deepsource.toml0000644000175000017500000000027313556612755021427 0ustar ericericversion = 1 test_patterns = [ ] exclude_patterns = [ ] [[analyzers]] name = 'go' enabled = true [analyzers.meta] import_path = 'github.com/dnscrypt/dnscrypt-proxy/dnscrypt-proxy' dnscrypt-proxy-2.0.31/dnscrypt-proxy/example-dnscrypt-proxy.toml0000644000175000017500000004351513556612755023677 0ustar ericeric ############################################## # # # dnscrypt-proxy configuration # # # ############################################## ## This is an example configuration file. ## You should adjust it to your needs, and save it as "dnscrypt-proxy.toml" ## ## Online documentation is available here: https://dnscrypt.info/doc ################################## # Global settings # ################################## ## List of servers to use ## ## Servers from the "public-resolvers" source (see down below) can ## be viewed here: https://dnscrypt.info/public-servers ## ## If this line is commented, all registered servers matching the require_* filters ## will be used. ## ## The proxy will automatically pick the fastest, working servers from the list. ## Remove the leading # first to enable this; lines starting with # are ignored. # server_names = ['scaleway-fr', 'google', 'yandex', 'cloudflare'] ## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6. listen_addresses = ['127.0.0.1:53', '[::1]:53'] ## Maximum number of simultaneous client connections to accept max_clients = 250 ## Switch to a different system user after listening sockets have been created. ## Note (1): this feature is currently unsupported on Windows. ## Note (2): this feature is not compatible with systemd socket activation. ## Note (3): when using -pidfile, the PID file directory must be writable by the new user # user_name = 'nobody' ## Require servers (from static + remote sources) to satisfy specific properties # Use servers reachable over IPv4 ipv4_servers = true # Use servers reachable over IPv6 -- Do not enable if you don't have IPv6 connectivity ipv6_servers = false # Use servers implementing the DNSCrypt protocol dnscrypt_servers = true # Use servers implementing the DNS-over-HTTPS protocol doh_servers = true ## Require servers defined by remote sources to satisfy specific properties # Server must support DNS security extensions (DNSSEC) 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...) require_nofilter = true # Server names to avoid even if they match all criteria disabled_server_names = [] ## Always use TCP to connect to upstream servers. ## This can be useful if you need to route everything through Tor. ## Otherwise, leave this to `false`, as it doesn't improve security ## (dnscrypt-proxy will always encrypt everything even using UDP), and can ## only increase latency. force_tcp = 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. # proxy = 'socks5://127.0.0.1:9050' ## HTTP/HTTPS proxy ## Only for DoH servers # http_proxy = 'http://127.0.0.1:8888' ## How long a DNS query will wait for a response, in milliseconds. ## If you have a network with *a lot* of latency, you may need to ## increase this. Startup may be slower if you do so. ## Don't increase it too much. 10000 is the highest reasonable value. timeout = 5000 ## Keepalive for HTTP (HTTPS, HTTP/2) 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:,aaaa:`. ## 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' # 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. # lb_estimator = true ## Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors) # log_level = 2 ## log file for the application # log_file = 'dnscrypt-proxy.log' ## Use the system logger (syslog on Unix, Event Log on Windows) # use_syslog = true ## Delay, in minutes, after which certificates are reloaded cert_refresh_delay = 240 ## 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 # dnscrypt_ephemeral_keys = false ## DoH: Disable TLS session tickets - increases privacy but also latency # tls_disable_session_tickets = false ## DoH: Use a 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. # tls_cipher_suite = [52392, 49199] ## Fallback resolver ## This is a normal, non-encrypted DNS resolver, that will be only used ## for one-shot queries when retrieving the initial resolvers list, and ## only if the system DNS configuration doesn't work. ## No user application queries will ever be leaked through this resolver, ## and it will not be used after IP addresses of resolvers URLs have been found. ## It will never be used if lists have already been cached, and if stamps ## don't include host names without IP addresses. ## It will not be used if the configured system DNS works. ## A resolver supporting DNSSEC is recommended. This may become mandatory. ## ## 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. fallback_resolver = '9.9.9.9:53' ## Never let dnscrypt-proxy try to use the system DNS settings; ## unconditionally use the fallback resolver. ignore_system_dns = false ## Maximum time (in seconds) to wait for network connectivity before ## initializing the proxy. ## Useful if the proxy is automatically started at boot, and network ## connectivity is not guaranteed to be immediately available. ## Use 0 to not test for connectivity at all (not recommended), ## and -1 to wait as much as possible. netprobe_timeout = 60 ## Address and port to try initializing a connection to, just to check ## if the network is up. It can be any address and any port, even if ## there is nothing answering these on the other side. Just don't use ## a local address, as the goal is to check for Internet connectivity. ## On Windows, a datagram with a single, nul byte will be sent, only ## when the system starts. ## On other operating systems, the connection will be initialized ## but nothing will be sent at all. netprobe_address = '9.9.9.9:53' ## Offline mode - Do not use any remote encrypted servers. ## The proxy will remain fully functional to respond to queries that ## plugins can handle directly (forwarding, cloaking, ...) # offline_mode = false ## Additional data to attach to outgoing queries. ## These strings will be added as TXT records to queries. ## Do not use, except on servers explicitly asking for extra data ## to be present. # query_meta = ["key1:value1", "key2:value2", "key3:value3"] ## Automatic log files rotation # Maximum log files size in MB - Set to 0 for unlimited. log_files_max_size = 10 # How long to keep backup files, in days log_files_max_age = 7 # Maximum log files backups to keep (or 0 to keep all backups) log_files_max_backups = 1 ######################### # Filters # ######################### ## Immediately respond to IPv6-related queries with an empty response ## This makes things faster when there is no IPv6 connectivity, but can ## also cause reliability issues with some stub resolvers. ## Do not enable if you added a validating resolver such as dnsmasq in front ## of the proxy. block_ipv6 = false ## TTL for synthetic responses sent when a request has been blocked (due to ## IPv6 or blacklists). reject_ttl = 600 ################################################################################## # Route queries for specific domains to a dedicated set of servers # ################################################################################## ## Example map entries (one entry per line): ## example.com 9.9.9.9 ## example.net 9.9.9.9,8.8.8.8,1.1.1.1 # forwarding_rules = 'forwarding-rules.txt' ############################### # Cloaking rules # ############################### ## 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. ## ## Example map entries (one entry per line) ## example.com 10.1.1.1 ## www.google.com forcesafesearch.google.com # cloaking_rules = 'cloaking-rules.txt' ## TTL used when serving entries in cloaking-rules.txt # cloak_ttl = 600 ########################### # DNS cache # ########################### ## Enable a DNS cache to reduce latency and outgoing traffic cache = true ## Cache size cache_size = 512 ## Minimum TTL for cached entries cache_min_ttl = 600 ## Maximum TTL for cached entries cache_max_ttl = 86400 ## Minimum TTL for negatively cached entries cache_neg_min_ttl = 60 ## Maximum TTL for negatively cached entries cache_neg_max_ttl = 600 ############################### # Query logging # ############################### ## Log client queries to a file [query_log] ## Path to the query log file (absolute, or relative to the same directory as the executable file) ## Can be /dev/stdout to log to the standard output (and set log_files_max_size to 0) # file = 'query.log' ## Query log format (currently supported: tsv and ltsv) format = 'tsv' ## Do not log these query types, to reduce verbosity. Keep empty to log everything. # ignored_qtypes = ['DNSKEY', 'NS'] ############################################ # Suspicious queries logging # ############################################ ## Log queries for nonexistent zones ## These queries can reveal the presence of malware, broken/obsolete applications, ## and devices signaling their presence to 3rd parties. [nx_log] ## Path to the query log file (absolute, or relative to the same directory as the executable file) # file = 'nx.log' ## Query log format (currently supported: tsv and ltsv) format = 'tsv' ###################################################### # Pattern-based blocking (blacklists) # ###################################################### ## Blacklists are made of one pattern per line. Example of valid patterns: ## ## example.com ## =example.com ## *sex* ## ads.* ## 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. [blacklist] ## Path to the file of blocking rules (absolute, or relative to the same directory as the executable file) # blacklist_file = 'blacklist.txt' ## Optional path to a file logging blocked queries # log_file = 'blocked.log' ## Optional log format: tsv or ltsv (default: tsv) # log_format = 'tsv' ########################################################### # Pattern-based IP blocking (IP blacklists) # ########################################################### ## IP blacklists are made of one pattern per line. Example of valid patterns: ## ## 127.* ## fe80:abcd:* ## 192.168.1.4 [ip_blacklist] ## Path to the file of blocking rules (absolute, or relative to the same directory as the executable file) # blacklist_file = 'ip-blacklist.txt' ## Optional path to a file logging blocked queries # log_file = 'ip-blocked.log' ## Optional log format: tsv or ltsv (default: tsv) # log_format = 'tsv' ###################################################### # Pattern-based whitelisting (blacklists bypass) # ###################################################### ## Whitelists support the same patterns as blacklists ## If a name matches a whitelist 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] ## Path to the file of whitelisting rules (absolute, or relative to the same directory as the executable file) # whitelist_file = 'whitelist.txt' ## Optional path to a file logging whitelisted queries # log_file = 'whitelisted.log' ## Optional log format: tsv or ltsv (default: tsv) # log_format = 'tsv' ########################################## # Time access restrictions # ########################################## ## One or more weekly schedules can be defined here. ## Patterns in the name-based blocklist 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: ## *.youtube.* @time-to-sleep ## would block access to YouTube only during the days, and period of the days ## define by the 'time-to-sleep' schedule. ## ## {after='21:00', before= '7:00'} matches 0:00-7:00 and 21:00-0:00 ## {after= '9:00', before='18:00'} matches 9:00-18:00 [schedules] # [schedules.'time-to-sleep'] # mon = [{after='21:00', before='7:00'}] # tue = [{after='21:00', before='7:00'}] # wed = [{after='21:00', before='7:00'}] # thu = [{after='21:00', before='7:00'}] # fri = [{after='23:00', before='7:00'}] # sat = [{after='23:00', before='7:00'}] # sun = [{after='21:00', before='7:00'}] # [schedules.'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'}] ######################### # Servers # ######################### ## Remote lists of available servers ## Multiple sources can be used simultaneously, but every source ## requires a dedicated cache file. ## ## Refer to the documentation for URLs of public sources. ## ## A prefix can be prepended to server names in order to ## avoid collisions if different sources share the same for ## different servers. In that case, names listed in `server_names` ## must include the prefixes. ## ## If the `urls` property is missing, cache files and valid signatures ## must be already present; This doesn't prevent these cache files from ## expiring after `refresh_delay` hours. [sources] ## 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 = '' ## Anonymized DNS relays [sources.'relays'] urls = ['https://github.com/DNSCrypt/dnscrypt-resolvers/raw/master/v2/relays.md', 'https://download.dnscrypt.info/resolvers-list/v2/relays.md'] cache_file = 'relays.md' minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3' refresh_delay = 72 prefix = '' ## Quad9 over DNSCrypt - https://quad9.net/ # [sources.quad9-resolvers] # 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 # [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' ################################ # Anonymized DNS # ################################ [anonymized_dns] ## Routes are indirect ways to reach DNSCrypt servers. ## ## A route maps a server name ("server_name") to one or more relays that will be ## 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. ## ## The following example routes "example-server-1" via `anon-example-1` or `anon-example-2``, ## and "example-server-2" via the relay whose relay DNS stamp ## is "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM". ## ## !!! THESE ARE JUST EXAMPLES !!! ## ## Review the list of available relays from the "relays.md` file, and, for each ## server you want to use, define the relays you want connections to go through. ## ## Carefully choose relays and servers so that the are run by different entities. ## ## "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. # routes = [ # { server_name='example-server-1', via=['anon-example-1', 'anon-example-2'] }, # { server_name='example-server-2', via=['sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM'] } # ] ## Optional, local, static list of additional servers ## Mostly useful for testing your own servers. [static] # [static.'myserver'] # stamp = 'sdns:AQcAAAAAAAAAAAAQMi5kbnNjcnlwdC1jZXJ0Lg' dnscrypt-proxy-2.0.31/dnscrypt-proxy/dnsutils.go0000644000175000017500000001016013556612755020506 0ustar ericericpackage main import ( "encoding/binary" "net" "strings" "time" "github.com/miekg/dns" ) func TruncatedResponse(packet []byte) ([]byte, error) { srcMsg := new(dns.Msg) if err := srcMsg.Unpack(packet); err != nil { return nil, err } dstMsg := srcMsg dstMsg.Response = true dstMsg.Answer = make([]dns.RR, 0) dstMsg.Ns = make([]dns.RR, 0) dstMsg.Extra = make([]dns.RR, 0) dstMsg.Truncated = true return dstMsg.Pack() } func EmptyResponseFromMessage(srcMsg *dns.Msg) (*dns.Msg, error) { dstMsg := srcMsg dstMsg.Response = true dstMsg.Answer = make([]dns.RR, 0) dstMsg.Ns = make([]dns.RR, 0) dstMsg.Extra = make([]dns.RR, 0) return dstMsg, nil } func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ipv4 net.IP, ipv6 net.IP, ttl uint32) (*dns.Msg, error) { dstMsg, err := EmptyResponseFromMessage(srcMsg) if err != nil { return dstMsg, err } if refusedCode { dstMsg.Rcode = dns.RcodeRefused } else { dstMsg.Rcode = dns.RcodeSuccess questions := srcMsg.Question if len(questions) > 0 { question := questions[0] sendHInfoResponse := true if ipv4 != nil && question.Qtype == dns.TypeA { rr := new(dns.A) rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl} rr.A = ipv4.To4() if rr.A != nil { dstMsg.Answer = []dns.RR{rr} sendHInfoResponse = false } } else if ipv6 != nil && question.Qtype == dns.TypeAAAA { rr := new(dns.AAAA) rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl} rr.AAAA = ipv6.To16() if rr.AAAA != nil { dstMsg.Answer = []dns.RR{rr} sendHInfoResponse = false } } if sendHInfoResponse { hinfo := new(dns.HINFO) hinfo.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeHINFO, Class: dns.ClassINET, Ttl: 1} hinfo.Cpu = "This query has been locally blocked" hinfo.Os = "by dnscrypt-proxy" dstMsg.Answer = []dns.RR{hinfo} } } } return dstMsg, nil } func HasTCFlag(packet []byte) bool { return packet[2]&2 == 2 } func TransactionID(packet []byte) uint16 { return binary.BigEndian.Uint16(packet[0:2]) } func SetTransactionID(packet []byte, tid uint16) { binary.BigEndian.PutUint16(packet[0:2], tid) } func Rcode(packet []byte) uint8 { return packet[3] & 0xf } func NormalizeName(name *[]byte) { for i, c := range *name { if c >= 65 && c <= 90 { (*name)[i] = c + 32 } } } func StripTrailingDot(str string) string { if len(str) > 1 && strings.HasSuffix(str, ".") { str = str[:len(str)-1] } return str } 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) { return time.Duration(cacheNegMinTTL) * time.Second } var ttl uint32 if msg.Rcode == dns.RcodeSuccess { ttl = uint32(maxTTL) } else { ttl = uint32(cacheNegMaxTTL) } if len(msg.Answer) > 0 { for _, rr := range msg.Answer { if rr.Header().Ttl < ttl { ttl = rr.Header().Ttl } } } else { for _, rr := range msg.Ns { if rr.Header().Ttl < ttl { ttl = rr.Header().Ttl } } } if msg.Rcode == dns.RcodeSuccess { if ttl < minTTL { ttl = minTTL } } else { if ttl < cacheNegMinTTL { ttl = cacheNegMinTTL } } return time.Duration(ttl) * time.Second } func setMaxTTL(msg *dns.Msg, ttl uint32) { for _, rr := range msg.Answer { if ttl < rr.Header().Ttl { rr.Header().Ttl = ttl } } for _, rr := range msg.Ns { if ttl < rr.Header().Ttl { rr.Header().Ttl = ttl } } for _, rr := range msg.Extra { header := rr.Header() if header.Rrtype == dns.TypeOPT { continue } if ttl < rr.Header().Ttl { rr.Header().Ttl = ttl } } } func updateTTL(msg *dns.Msg, expiration time.Time) { until := time.Until(expiration) ttl := uint32(0) if until > 0 { ttl = uint32(until / time.Second) } for _, rr := range msg.Answer { rr.Header().Ttl = ttl } for _, rr := range msg.Ns { rr.Header().Ttl = ttl } for _, rr := range msg.Extra { if rr.Header().Rrtype != dns.TypeOPT { rr.Header().Ttl = ttl } } } dnscrypt-proxy-2.0.31/dnscrypt-proxy/common.go0000644000175000017500000000620613556612755020137 0ustar ericericpackage main import ( "bytes" "encoding/binary" "errors" "io/ioutil" "net" "os" "strconv" "strings" "unicode" ) type CryptoConstruction uint16 const ( UndefinedConstruction CryptoConstruction = iota XSalsa20Poly1305 XChacha20Poly1305 ) const ( ClientMagicLen = 8 ) const ( MaxHTTPBodyLength = 4000000 ) var ( CertMagic = [4]byte{0x44, 0x4e, 0x53, 0x43} ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38} MinDNSPacketSize = 12 + 5 MaxDNSPacketSize = 4096 MaxDNSUDPPacketSize = 4096 MaxDNSUDPSafePacketSize = 1252 InitialMinQuestionSize = 512 ) var ( FileDescriptors = make([]*os.File, 0) FileDescriptorNum = 0 ) func PrefixWithSize(packet []byte) ([]byte, error) { packetLen := len(packet) if packetLen > 0xffff { return packet, errors.New("Packet too large") } packet = append(append(packet, 0), 0) copy(packet[2:], packet[:len(packet)-2]) binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)-2)) return packet, nil } func ReadPrefixed(conn *net.Conn) ([]byte, error) { buf := make([]byte, 2+MaxDNSPacketSize) packetLength, pos := -1, 0 for { readnb, err := (*conn).Read(buf[pos:]) if err != nil { return buf, err } pos += readnb if pos >= 2 && packetLength < 0 { packetLength = int(binary.BigEndian.Uint16(buf[0:2])) if packetLength > MaxDNSPacketSize-1 { return buf, errors.New("Packet too large") } if packetLength < MinDNSPacketSize { return buf, errors.New("Packet too short") } } if packetLength >= 0 && pos >= 2+packetLength { return buf[2 : 2+packetLength], nil } } } func Min(a, b int) int { if a < b { return a } return b } func Max(a, b int) int { if a > b { return a } 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 { r[i], r[j] = r[j], r[i] } return string(r) } func StringTwoFields(str string) (string, string, bool) { if len(str) < 3 { return "", "", false } pos := strings.IndexFunc(str, unicode.IsSpace) if pos == -1 { return "", "", false } a, b := strings.TrimFunc(str[:pos], unicode.IsSpace), strings.TrimFunc(str[pos+1:], unicode.IsSpace) if len(a) == 0 || len(b) == 0 { return a, b, false } return a, b, true } func StringQuote(str string) string { str = strconv.QuoteToGraphic(str) return str[1 : len(str)-1] } func StringStripSpaces(str string) string { return strings.Map(func(r rune) rune { if unicode.IsSpace(r) { return -1 } return r }, str) } func ExtractHostAndPort(str string, defaultPort int) (host string, port int) { host, port = str, defaultPort if idx := strings.LastIndex(str, ":"); idx >= 0 && idx < len(str)-1 { if portX, err := strconv.Atoi(str[idx+1:]); err == nil { host, port = host[:idx], portX } } return } func ReadTextFile(filename string) (string, error) { bin, err := ioutil.ReadFile(filename) if err != nil { return "", err } bin = bytes.TrimPrefix(bin, []byte{0xef, 0xbb, 0xbf}) return string(bin), nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/privilege_linux.go0000644000175000017500000000543313556612755022055 0ustar ericericpackage main import ( "os" "os/exec" "os/user" "path/filepath" "runtime" "strconv" "syscall" "golang.org/x/sys/unix" "github.com/jedisct1/dlog" ) func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) { currentUser, err := user.Current() if err != nil && currentUser.Uid != "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) args := os.Args 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.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) if err != nil { dlog.Fatal(err) } gid, err := strconv.Atoi(userInfo.Gid) if err != nil { dlog.Fatal(err) } execPath, err := exec.LookPath(args[0]) if err != nil { dlog.Fatalf("Unable to get the path to the dnscrypt-proxy executable file: [%s]", err) } path, err := filepath.Abs(execPath) if err != nil { dlog.Fatal(err) } if err := ServiceManagerReadyNotify(); err != nil { dlog.Fatal(err) } args = append(args, "-child") dlog.Notice("Dropping privileges") runtime.LockOSThread() if _, _, rcode := syscall.RawSyscall(syscall.SYS_SETGROUPS, uintptr(0), uintptr(0), 0); rcode != 0 { dlog.Fatalf("Unable to drop additional groups: [%s]", rcode.Error()) } if _, _, rcode := syscall.RawSyscall(syscall.SYS_SETGID, uintptr(gid), 0, 0); rcode != 0 { dlog.Fatalf("Unable to drop group privileges: [%s]", rcode.Error()) } 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 { 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) } dnscrypt-proxy-2.0.31/dnscrypt-proxy/resolve.go0000644000175000017500000000243613556612755020327 0ustar ericericpackage main import ( "fmt" "net" "strings" ) const myResolverHost string = "resolver.dnscrypt.info" 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)) } fmt.Printf("Canonical name: ") cname, err := net.LookupCNAME(name) if err != nil { fmt.Println("-") } else { fmt.Println(cname) } fmt.Printf("IP addresses: ") addrs, err := net.LookupHost(name) if err != nil { fmt.Println("-") } else { fmt.Println(strings.Join(addrs, ", ")) } 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]) } fmt.Println("") } fmt.Println("") } dnscrypt-proxy-2.0.31/dnscrypt-proxy/example-forwarding-rules.txt0000644000175000017500000000113013556612755023773 0ustar ericeric################################## # Forwarding rules # ################################## ## This is used to route specific domain names to specific servers. ## The general format is: ## [:port] [, [:port]...] ## IPv6 addresses can be specified by enclosing the address in square brackets. ## In order to enable this feature, the "forwarding_rules" property needs to ## be set to this file name inside the main configuration file. ## 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 dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_block_ip.go0000644000175000017500000001100513556612755022000 0ustar ericericpackage main import ( "errors" "fmt" "net" "strings" "time" "unicode" "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 format string } func (plugin *PluginBlockIP) Name() string { return "block_ip" } func (plugin *PluginBlockIP) Description() string { return "Block responses containing specific IP addresses" } 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) if err != nil { return err } plugin.blockedPrefixes = iradix.New() plugin.blockedIPs = make(map[string]interface{}) for lineNo, line := range strings.Split(string(bin), "\n") { line = strings.TrimFunc(line, unicode.IsSpace) if len(line) == 0 || strings.HasPrefix(line, "#") { continue } ip := net.ParseIP(line) trailingStar := strings.HasSuffix(line, "*") if len(line) < 2 || (ip != nil && trailingStar) { dlog.Errorf("Suspicious IP blocking 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 IP blocking 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.blockedPrefixes, _, _ = plugin.blockedPrefixes.Insert([]byte(line), 0) } else { plugin.blockedIPs[line] = true } } 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.format = proxy.blockIPFormat return nil } func (plugin *PluginBlockIP) Drop() error { return nil } func (plugin *PluginBlockIP) Reload() error { return nil } func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) error { if pluginsState.sessionData["whitelisted"] != nil { return nil } answers := msg.Answer if len(answers) == 0 { return nil } reject, 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.blockedIPs[ipStr]; found { reject, reason = true, ipStr break } match, _, found := plugin.blockedPrefixes.Root().LongestPrefix([]byte(ipStr)) if found { if len(match) == len(ipStr) || (ipStr[len(match)] == '.' || ipStr[len(match)] == ':') { reject, reason = true, string(match)+"*" break } } } if reject { pluginsState.action = PluginsActionReject pluginsState.returnCode = PluginsReturnCodeReject if plugin.logger != nil { questions := msg.Question if len(questions) != 1 { return nil } qName := strings.ToLower(StripTrailingDot(questions[0].Name)) if len(qName) < 2 { return nil } 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\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 } dnscrypt-proxy-2.0.31/dnscrypt-proxy/privilege_windows.go0000644000175000017500000000014013556612755022376 0ustar ericericpackage main import "os" func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {} dnscrypt-proxy-2.0.31/dnscrypt-proxy/systemd_free.go0000644000175000017500000000013513556612755021333 0ustar ericeric// +build !linux package main func (proxy *Proxy) SystemDListeners() error { return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/sources.go0000644000175000017500000002014013556612755020323 0ustar ericericpackage main import ( "errors" "fmt" "io" "io/ioutil" "net/http" "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-minisign" ) type SourceFormat int const ( SourceFormatV2 = iota ) const ( MinSourcesUpdateDelay = time.Duration(24) * time.Hour ) type Source struct { urls []string format SourceFormat in string } func fetchFromCache(cacheFile string, refreshDelay time.Duration) (in string, expired bool, delayTillNextUpdate time.Duration, err error) { expired = false if refreshDelay < MinSourcesUpdateDelay { refreshDelay = MinSourcesUpdateDelay } fi, err := os.Stat(cacheFile) if err != nil { dlog.Debugf("Cache file [%s] not present", cacheFile) delayTillNextUpdate = time.Duration(0) return } elapsed := time.Since(fi.ModTime()) if elapsed < refreshDelay { dlog.Debugf("Cache file [%s] is still fresh", cacheFile) delayTillNextUpdate = refreshDelay - elapsed } else { dlog.Debugf("Cache file [%s] needs to be refreshed", cacheFile) delayTillNextUpdate = time.Duration(0) } var bin []byte bin, err = ioutil.ReadFile(cacheFile) if err != nil { delayTillNextUpdate = time.Duration(0) return } in = string(bin) if delayTillNextUpdate <= time.Duration(0) { expired = true } return } func fetchWithCache(xTransport *XTransport, urlStr string, cacheFile string, refreshDelay time.Duration) (in string, cached bool, delayTillNextUpdate time.Duration, err error) { cached = false expired := false in, expired, delayTillNextUpdate, err = fetchFromCache(cacheFile, refreshDelay) if err == nil && !expired { dlog.Debugf("Delay till next update: %v", delayTillNextUpdate) cached = true return } if expired { cached = true } if len(urlStr) == 0 { if !expired { err = fmt.Errorf("Cache file [%s] not present and no URL given to retrieve it", cacheFile) } return } var resp *http.Response dlog.Infof("Loading source information from URL [%s]", urlStr) url, err := url.Parse(urlStr) if err != nil { return } resp, _, err = xTransport.Get(url, "", 30*time.Second) if err == nil && resp != nil && (resp.StatusCode < 200 || resp.StatusCode > 299) { err = fmt.Errorf("Webserver returned code %d", resp.StatusCode) return } else if err != nil { return } else if resp == nil { err = errors.New("Webserver returned an error") return } var bin []byte bin, err = ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength)) resp.Body.Close() if err != nil { return } err = nil cached = false in = string(bin) delayTillNextUpdate = refreshDelay return } func AtomicFileWrite(file string, data []byte) error { return safefile.WriteFile(file, data, 0644) } type URLToPrefetch struct { url string cacheFile string when time.Time } func NewSource(xTransport *XTransport, urls []string, minisignKeyStr string, cacheFile string, formatStr string, refreshDelay time.Duration) (Source, []URLToPrefetch, error) { source := Source{urls: urls} if formatStr == "v2" { source.format = SourceFormatV2 } else { return source, []URLToPrefetch{}, fmt.Errorf("Unsupported source format: [%s]", formatStr) } minisignKey, err := minisign.NewPublicKey(minisignKeyStr) if err != nil { return source, []URLToPrefetch{}, err } now := time.Now() urlsToPrefetch := []URLToPrefetch{} sigCacheFile := cacheFile + ".minisig" var sigStr, in string var cached, sigCached bool var delayTillNextUpdate, sigDelayTillNextUpdate time.Duration var sigErr error var preloadURL string if len(urls) <= 0 { in, cached, delayTillNextUpdate, err = fetchWithCache(xTransport, "", cacheFile, refreshDelay) sigStr, sigCached, sigDelayTillNextUpdate, sigErr = fetchWithCache(xTransport, "", sigCacheFile, refreshDelay) } else { preloadURL = urls[0] for _, url := range urls { sigURL := url + ".minisig" in, cached, delayTillNextUpdate, err = fetchWithCache(xTransport, url, cacheFile, refreshDelay) sigStr, sigCached, sigDelayTillNextUpdate, sigErr = fetchWithCache(xTransport, sigURL, sigCacheFile, refreshDelay) if err == nil && sigErr == nil { preloadURL = url break } dlog.Infof("Loading from [%s] failed", url) } } if len(preloadURL) > 0 { url := preloadURL sigURL := url + ".minisig" urlsToPrefetch = append(urlsToPrefetch, URLToPrefetch{url: url, cacheFile: cacheFile, when: now.Add(delayTillNextUpdate)}) urlsToPrefetch = append(urlsToPrefetch, URLToPrefetch{url: sigURL, cacheFile: sigCacheFile, when: now.Add(sigDelayTillNextUpdate)}) } if sigErr != nil && err == nil { err = sigErr } if err != nil { return source, urlsToPrefetch, err } signature, err := minisign.DecodeSignature(sigStr) if err != nil { os.Remove(cacheFile) os.Remove(sigCacheFile) return source, urlsToPrefetch, err } res, err := minisignKey.Verify([]byte(in), signature) if err != nil || !res { os.Remove(cacheFile) os.Remove(sigCacheFile) return source, urlsToPrefetch, err } if !cached { if err = AtomicFileWrite(cacheFile, []byte(in)); err != nil { if absPath, err2 := filepath.Abs(cacheFile); err2 == nil { dlog.Warnf("%s: %s", absPath, err) } } } if !sigCached { if err = AtomicFileWrite(sigCacheFile, []byte(sigStr)); err != nil { if absPath, err2 := filepath.Abs(sigCacheFile); err2 == nil { dlog.Warnf("%s: %s", absPath, err) } } } dlog.Noticef("Source [%s] loaded", cacheFile) source.in = in return source, urlsToPrefetch, nil } func (source *Source) Parse(prefix string) ([]RegisteredServer, error) { if source.format == SourceFormatV2 { return source.parseV2(prefix) } dlog.Fatal("Unexpected source format") return []RegisteredServer{}, nil } func (source *Source) parseV2(prefix string) ([]RegisteredServer, error) { var registeredServers []RegisteredServer var stampErrs []string appendStampErr := func(format string, a ...interface{}) { stampErr := fmt.Sprintf(format, a...) stampErrs = append(stampErrs, stampErr) dlog.Warn(stampErr) } in := string(source.in) 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) 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) if len(name) == 0 { return registeredServers, fmt.Errorf("Invalid format for source at [%v]", source.urls) } subparts = subparts[1:] name = prefix + name var stampStr, description string 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 continue } else if len(subpart) == 0 || strings.HasPrefix(subpart, "//") { continue } if len(description) > 0 { description += "\n" } description += subpart } if len(stampStr) < 6 { appendStampErr("Missing stamp for server [%s]", name) continue } stamp, err := stamps.NewServerStampFromString(stampStr) if err != nil { appendStampErr("Invalid or unsupported stamp [%v]: %s", stampStr, err.Error()) continue } registeredServer := RegisteredServer{ name: name, stamp: stamp, description: description, } dlog.Debugf("Registered [%s] with stamp [%s]", name, stamp.String()) registeredServers = append(registeredServers, registeredServer) } if len(stampErrs) > 0 { return registeredServers, fmt.Errorf("%s", strings.Join(stampErrs, ", ")) } return registeredServers, nil } func PrefetchSourceURL(xTransport *XTransport, urlToPrefetch *URLToPrefetch) error { in, cached, delayTillNextUpdate, err := fetchWithCache(xTransport, urlToPrefetch.url, urlToPrefetch.cacheFile, MinSourcesUpdateDelay) if err == nil && !cached { AtomicFileWrite(urlToPrefetch.cacheFile, []byte(in)) } urlToPrefetch.when = time.Now().Add(delayTillNextUpdate) return err } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugins.go0000644000175000017500000002465413556612755020337 0ustar ericericpackage main import ( "errors" "net" "strings" "sync" "time" "github.com/jedisct1/dlog" "github.com/miekg/dns" ) type PluginsAction int const ( PluginsActionNone = 0 PluginsActionForward = 1 PluginsActionDrop = 2 PluginsActionReject = 3 PluginsActionSynth = 4 ) type PluginsGlobals struct { sync.RWMutex queryPlugins *[]Plugin responsePlugins *[]Plugin loggingPlugins *[]Plugin refusedCodeInResponses bool respondWithIPv4 net.IP respondWithIPv6 net.IP } type PluginsReturnCode int const ( PluginsReturnCodePass = iota PluginsReturnCodeForward PluginsReturnCodeDrop PluginsReturnCodeReject PluginsReturnCodeSynth PluginsReturnCodeParseError PluginsReturnCodeNXDomain PluginsReturnCodeResponseError PluginsReturnCodeServerError PluginsReturnCodeCloak PluginsReturnCodeServerTimeout ) var PluginsReturnCodeToString = map[PluginsReturnCode]string{ PluginsReturnCodePass: "PASS", PluginsReturnCodeForward: "FORWARD", PluginsReturnCodeDrop: "DROP", PluginsReturnCodeReject: "REJECT", PluginsReturnCodeSynth: "SYNTH", PluginsReturnCodeParseError: "PARSE_ERROR", PluginsReturnCodeNXDomain: "NXDOMAIN", PluginsReturnCodeResponseError: "RESPONSE_ERROR", PluginsReturnCodeServerError: "SERVER_ERROR", PluginsReturnCodeCloak: "CLOAK", PluginsReturnCodeServerTimeout: "SERVER_TIMEOUT", } type PluginsState struct { sessionData map[string]interface{} action PluginsAction maxUnencryptedUDPSafePayloadSize int originalMaxPayloadSize int maxPayloadSize int clientProto string clientAddr *net.Addr synthResponse *dns.Msg dnssec bool cacheSize int cacheNegMinTTL uint32 cacheNegMaxTTL uint32 cacheMinTTL uint32 cacheMaxTTL uint32 rejectTTL uint32 questionMsg *dns.Msg requestStart time.Time requestEnd time.Time cacheHit bool returnCode PluginsReturnCode serverName string } func (proxy *Proxy) InitPluginsGlobals() error { queryPlugins := &[]Plugin{} if len(proxy.queryMeta) != 0 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginQueryMeta))) } if len(proxy.whitelistNameFile) != 0 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginWhitelistName))) } *queryPlugins = append(*queryPlugins, Plugin(new(PluginFirefox))) if len(proxy.blockNameFile) != 0 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockName))) } if proxy.pluginBlockIPv6 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockIPv6))) } if len(proxy.cloakFile) != 0 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginCloak))) } *queryPlugins = append(*queryPlugins, Plugin(new(PluginGetSetPayloadSize))) if proxy.cache { *queryPlugins = append(*queryPlugins, Plugin(new(PluginCache))) } if len(proxy.forwardFile) != 0 { *queryPlugins = append(*queryPlugins, Plugin(new(PluginForward))) } responsePlugins := &[]Plugin{} if len(proxy.nxLogFile) != 0 { *responsePlugins = append(*responsePlugins, Plugin(new(PluginNxLog))) } if len(proxy.blockIPFile) != 0 { *responsePlugins = append(*responsePlugins, Plugin(new(PluginBlockIP))) } if proxy.cache { *responsePlugins = append(*responsePlugins, Plugin(new(PluginCacheResponse))) } loggingPlugins := &[]Plugin{} if len(proxy.queryLogFile) != 0 { *loggingPlugins = append(*loggingPlugins, Plugin(new(PluginQueryLog))) } for _, plugin := range *queryPlugins { if err := plugin.Init(proxy); err != nil { return err } } for _, plugin := range *responsePlugins { if err := plugin.Init(proxy); err != nil { return err } } for _, plugin := range *loggingPlugins { if err := plugin.Init(proxy); err != nil { return err } } proxy.pluginsGlobals.queryPlugins = queryPlugins proxy.pluginsGlobals.responsePlugins = responsePlugins proxy.pluginsGlobals.loggingPlugins = loggingPlugins parseBlockedQueryResponse(proxy.blockedQueryResponse, &proxy.pluginsGlobals) return nil } // blockedQueryResponse can be 'refused', 'hinfo' or IP responses 'a:IPv4,aaaa:IPv6 func parseBlockedQueryResponse(blockedResponse string, pluginsGlobals *PluginsGlobals) { blockedResponse = StringStripSpaces(strings.ToLower(blockedResponse)) if strings.HasPrefix(blockedResponse, "a:") { blockedIPStrings := strings.Split(blockedResponse, ",") (*pluginsGlobals).respondWithIPv4 = net.ParseIP(strings.TrimPrefix(blockedIPStrings[0], "a:")) if (*pluginsGlobals).respondWithIPv4 == nil { dlog.Notice("Error parsing IPv4 response given in blocked_query_response option, defaulting to `hinfo`") (*pluginsGlobals).refusedCodeInResponses = false return } if len(blockedIPStrings) > 1 { if strings.HasPrefix(blockedIPStrings[1], "aaaa:") { ipv6Response := strings.TrimPrefix(blockedIPStrings[1], "aaaa:") if strings.HasPrefix(ipv6Response, "[") { ipv6Response = strings.Trim(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") } } else { dlog.Noticef("Invalid IPv6 response given in blocked_query_response option [%s], the option should take the form 'a:,aaaa:'", blockedIPStrings[1]) } } if (*pluginsGlobals).respondWithIPv6 == nil { (*pluginsGlobals).respondWithIPv6 = (*pluginsGlobals).respondWithIPv4 } } else { switch blockedResponse { case "refused": (*pluginsGlobals).refusedCodeInResponses = true case "hinfo": (*pluginsGlobals).refusedCodeInResponses = false default: dlog.Noticef("Invalid blocked_query_response option [%s], defaulting to `hinfo`", blockedResponse) (*pluginsGlobals).refusedCodeInResponses = false } } } type Plugin interface { Name() string Description() string Init(proxy *Proxy) error Drop() error Reload() error Eval(pluginsState *PluginsState, msg *dns.Msg) error } func NewPluginsState(proxy *Proxy, clientProto string, clientAddr *net.Addr, start time.Time) PluginsState { return PluginsState{ action: PluginsActionForward, maxPayloadSize: MaxDNSUDPPacketSize - ResponseOverhead, clientProto: clientProto, clientAddr: clientAddr, cacheSize: proxy.cacheSize, cacheNegMinTTL: proxy.cacheNegMinTTL, cacheNegMaxTTL: proxy.cacheNegMaxTTL, cacheMinTTL: proxy.cacheMinTTL, cacheMaxTTL: proxy.cacheMaxTTL, rejectTTL: proxy.rejectTTL, questionMsg: nil, requestStart: start, maxUnencryptedUDPSafePayloadSize: MaxDNSUDPSafePacketSize, } } func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGlobals, packet []byte, serverName string) ([]byte, error) { if len(*pluginsGlobals.queryPlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 { return packet, nil } pluginsState.serverName = serverName pluginsState.action = PluginsActionForward msg := dns.Msg{} if err := msg.Unpack(packet); err != nil { return packet, err } if len(msg.Question) > 1 { return packet, errors.New("Unexpected number of questions") } pluginsState.questionMsg = &msg pluginsGlobals.RLock() defer pluginsGlobals.RUnlock() for _, plugin := range *pluginsGlobals.queryPlugins { if err := plugin.Eval(pluginsState, &msg); err != nil { pluginsState.action = PluginsActionDrop return packet, err } if pluginsState.action == PluginsActionReject { synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.rejectTTL) if err != nil { return nil, err } pluginsState.synthResponse = synth } if pluginsState.action != PluginsActionForward { break } } packet2, err := msg.PackBuffer(packet) if err != nil { return packet, err } return packet2, nil } func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGlobals, packet []byte, ttl *uint32) ([]byte, error) { if len(*pluginsGlobals.responsePlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 { return packet, nil } pluginsState.action = PluginsActionForward msg := dns.Msg{} if err := msg.Unpack(packet); err != nil { if len(packet) >= MinDNSPacketSize && HasTCFlag(packet) { err = nil } return packet, err } switch Rcode(packet) { case dns.RcodeSuccess: pluginsState.returnCode = PluginsReturnCodePass case dns.RcodeNameError: pluginsState.returnCode = PluginsReturnCodeNXDomain case dns.RcodeServerFailure: pluginsState.returnCode = PluginsReturnCodeServerError default: pluginsState.returnCode = PluginsReturnCodeResponseError } pluginsGlobals.RLock() defer pluginsGlobals.RUnlock() for _, plugin := range *pluginsGlobals.responsePlugins { if err := plugin.Eval(pluginsState, &msg); err != nil { pluginsState.action = PluginsActionDrop return packet, err } if pluginsState.action == PluginsActionReject { synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.rejectTTL) if err != nil { return nil, err } dlog.Infof("Blocking [%s]", synth.Question[0].Name) pluginsState.synthResponse = synth } if pluginsState.action != PluginsActionForward { break } } if ttl != nil { setMaxTTL(&msg, *ttl) } packet2, err := msg.PackBuffer(packet) if err != nil { return packet, err } return packet2, nil } func (pluginsState *PluginsState) ApplyLoggingPlugins(pluginsGlobals *PluginsGlobals) error { if len(*pluginsGlobals.loggingPlugins) == 0 { return nil } pluginsState.requestEnd = time.Now() questionMsg := pluginsState.questionMsg if questionMsg == nil || len(questionMsg.Question) > 1 { return errors.New("Unexpected number of questions") } pluginsGlobals.RLock() defer pluginsGlobals.RUnlock() for _, plugin := range *pluginsGlobals.loggingPlugins { if err := plugin.Eval(pluginsState, questionMsg); err != nil { return err } } return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/systemd_linux.go0000644000175000017500000000221013556612755021545 0ustar ericeric// +build !android package main import ( "fmt" "net" "github.com/coreos/go-systemd/activation" "github.com/jedisct1/dlog" ) func (proxy *Proxy) SystemDListeners() 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.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 { 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 { 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 } dnscrypt-proxy-2.0.31/dnscrypt-proxy/estimators.go0000644000175000017500000000256413556612755021044 0ustar ericericpackage main import ( "sync" "github.com/VividCortex/ewma" ) const ( SizeEstimatorEwmaDecay = 100.0 ) type QuestionSizeEstimator struct { sync.RWMutex minQuestionSize int ewma ewma.MovingAverage } func NewQuestionSizeEstimator() QuestionSizeEstimator { return QuestionSizeEstimator{minQuestionSize: InitialMinQuestionSize, ewma: ewma.NewMovingAverage(SizeEstimatorEwmaDecay)} } func (questionSizeEstimator *QuestionSizeEstimator) MinQuestionSize() int { questionSizeEstimator.RLock() minQuestionSize := questionSizeEstimator.minQuestionSize questionSizeEstimator.RUnlock() return minQuestionSize } func (questionSizeEstimator *QuestionSizeEstimator) blindAdjust() { questionSizeEstimator.Lock() questionSizeEstimator.minQuestionSize = Min(MaxDNSUDPPacketSize, questionSizeEstimator.minQuestionSize*2) questionSizeEstimator.ewma.Set(float64(questionSizeEstimator.minQuestionSize)) questionSizeEstimator.Unlock() } func (questionSizeEstimator *QuestionSizeEstimator) adjust(packetSize int) { questionSizeEstimator.Lock() questionSizeEstimator.ewma.Add(float64(packetSize)) ma, minQuestionSize := int(questionSizeEstimator.ewma.Value()), questionSizeEstimator.minQuestionSize if ma > InitialMinQuestionSize && ma < minQuestionSize/2 { questionSizeEstimator.minQuestionSize = Max(InitialMinQuestionSize, minQuestionSize/2) } questionSizeEstimator.Unlock() } dnscrypt-proxy-2.0.31/dnscrypt-proxy/time_ranges.go0000644000175000017500000000537613556612755021153 0ustar ericericpackage main import ( "fmt" "strconv" "strings" "time" ) type TimeRange struct { after int before int } type WeeklyRanges struct { ranges [7][]TimeRange } type TimeRangeStr struct { After string Before string } type WeeklyRangesStr struct { Sun, Mon, Tue, Wed, Thu, Fri, Sat []TimeRangeStr } func daySecsFromStr(str string) (int, error) { parts := strings.Split(str, ":") if len(parts) != 2 { return -1, fmt.Errorf("Syntax error in a time expression: [%s]", str) } hours, err := strconv.Atoi(parts[0]) if err != nil || hours < 0 || hours > 23 { return -1, fmt.Errorf("Syntax error in a time expression: [%s]", str) } minutes, err := strconv.Atoi(parts[1]) if err != nil || minutes < 0 || minutes > 59 { return -1, fmt.Errorf("Syntax error in a time expression: [%s]", str) } return (hours*60 + minutes) * 60, nil } func parseTimeRanges(timeRangesStr []TimeRangeStr) ([]TimeRange, error) { timeRanges := []TimeRange{} for _, timeRangeStr := range timeRangesStr { after, err := daySecsFromStr(timeRangeStr.After) if err != nil { return timeRanges, err } before, err := daySecsFromStr(timeRangeStr.Before) if err != nil { return timeRanges, err } if after == before { after, before = -1, 86402 } timeRanges = append(timeRanges, TimeRange{after: after, before: before}) } return timeRanges, nil } 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} for day, weeklyRangeStrX := range weeklyRangesStrX { timeRanges, err := parseTimeRanges(weeklyRangeStrX) if err != nil { return weeklyRanges, err } weeklyRanges.ranges[day] = timeRanges } return weeklyRanges, nil } func ParseAllWeeklyRanges(allWeeklyRangesStr map[string]WeeklyRangesStr) (*map[string]WeeklyRanges, error) { allWeeklyRanges := make(map[string]WeeklyRanges) for weeklyRangesName, weeklyRangesStr := range allWeeklyRangesStr { weeklyRanges, err := parseWeeklyRanges(weeklyRangesStr) if err != nil { return nil, err } allWeeklyRanges[weeklyRangesName] = weeklyRanges } return &allWeeklyRanges, nil } func (weeklyRanges *WeeklyRanges) Match() bool { now := time.Now().Local() day := now.Weekday() weeklyRange := weeklyRanges.ranges[day] if len(weeklyRange) == 0 { return false } hour, min, _ := now.Clock() nowX := (hour*60 + min) * 60 for _, timeRange := range weeklyRange { if timeRange.after > timeRange.before { if nowX >= timeRange.after || nowX <= timeRange.before { return true } } else if nowX >= timeRange.after && nowX <= timeRange.before { return true } } return false } dnscrypt-proxy-2.0.31/dnscrypt-proxy/example-cloaking-rules.txt0000644000175000017500000000230613556612755023426 0ustar ericeric################################ # Cloaking rules # ################################ # The following example rules force "safe" (without adult content) search # results from Google, Bing and YouTube. # # This has to be enabled with the `cloaking_rules` parameter in the main # configuration file www.google.* forcesafesearch.google.com www.bing.com strict.bing.com yandex.ru familysearch.yandex.ru =duckduckgo.com safe.duckduckgo.com www.youtube.com restrictmoderate.youtube.com m.youtube.com restrictmoderate.youtube.com youtubei.googleapis.com restrictmoderate.youtube.com youtube.googleapis.com restrictmoderate.youtube.com www.youtube-nocookie.com restrictmoderate.youtube.com # Multiple IP entries for the same name are supported. # In the following example, the same name maps both to IPv4 and IPv6 addresses: localhost 127.0.0.1 localhost ::1 # For load-balancing, multiple IP addresses of the same class can also be # provided using the same format, one pair per line. # ads.* 192.168.100.1 # ads.* 192.168.100.2 # ads.* ::1 dnscrypt-proxy-2.0.31/dnscrypt-proxy/systemd_android.go0000644000175000017500000000011313556612755022026 0ustar ericericpackage main func (proxy *Proxy) SystemDListeners() error { return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/netprobe_others.go0000644000175000017500000000147513556612755022054 0ustar ericeric// +build !windows package main import ( "net" "time" "github.com/jedisct1/dlog" ) func NetProbe(address string, timeout int) error { if len(address) <= 0 || timeout <= 0 { return nil } remoteUDPAddr, err := net.ResolveUDPAddr("udp", address) if err != nil { return err } retried := false if timeout < 0 { timeout = MaxTimeout } else { timeout = Min(MaxTimeout, timeout) } for tries := timeout; tries > 0; tries-- { pc, err := net.DialUDP("udp", nil, remoteUDPAddr) if err != nil { if !retried { retried = true dlog.Notice("Network not available yet -- waiting...") } dlog.Debug(err) time.Sleep(1 * time.Second) continue } pc.Close() dlog.Notice("Network connectivity detected") return nil } dlog.Error("Timeout while waiting for network connectivity") return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_querymeta.go0000644000175000017500000000154213556612755022237 0ustar ericericpackage main import ( "github.com/miekg/dns" ) type PluginQueryMeta struct { queryMetaRR *dns.TXT } func (plugin *PluginQueryMeta) Name() string { return "query_log" } func (plugin *PluginQueryMeta) Description() string { return "Log DNS queries." } 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.Txt = proxy.queryMeta plugin.queryMetaRR = queryMetaRR return nil } func (plugin *PluginQueryMeta) Drop() error { return nil } func (plugin *PluginQueryMeta) Reload() error { return nil } func (plugin *PluginQueryMeta) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) == 0 { return nil } msg.Extra = []dns.RR{plugin.queryMetaRR} return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/service_others.go0000644000175000017500000000023013556612755021662 0ustar ericeric// +build !linux,!windows package main func ServiceManagerStartNotify() error { return nil } func ServiceManagerReadyNotify() error { return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/pattern_matcher.go0000644000175000017500000001077013556612755022030 0ustar ericericpackage main import ( "fmt" "path/filepath" "strings" "github.com/k-sone/critbitgo" "github.com/jedisct1/dlog" ) type PatternType int const ( PatternTypeNone PatternType = iota PatternTypePrefix PatternTypeSuffix PatternTypeSubstring PatternTypePattern PatternTypeExact ) type PatternMatcher struct { blockedPrefixes *critbitgo.Trie blockedSuffixes *critbitgo.Trie blockedSubstrings []string blockedPatterns []string blockedExact map[string]interface{} indirectVals map[string]interface{} } func NewPatternPatcher() *PatternMatcher { patternMatcher := PatternMatcher{ blockedPrefixes: critbitgo.NewTrie(), blockedSuffixes: critbitgo.NewTrie(), blockedExact: make(map[string]interface{}), indirectVals: make(map[string]interface{}), } return &patternMatcher } func isGlobCandidate(str string) bool { for i, c := range str { if c == '?' || c == '[' { return true } else if c == '*' && i != 0 && i != len(str)-1 { return true } } return false } func (patternMatcher *PatternMatcher) Add(pattern string, val interface{}, position int) (PatternType, error) { leadingStar := strings.HasPrefix(pattern, "*") trailingStar := strings.HasSuffix(pattern, "*") exact := strings.HasPrefix(pattern, "=") patternType := PatternTypeNone if isGlobCandidate(pattern) { patternType = PatternTypePattern _, err := filepath.Match(pattern, "example.com") if len(pattern) < 2 || err != nil { return patternType, fmt.Errorf("Syntax error in block rules at pattern %d", position) } } else if leadingStar && trailingStar { patternType = PatternTypeSubstring if len(pattern) < 3 { return patternType, fmt.Errorf("Syntax error in block rules at pattern %d", position) } pattern = pattern[1 : len(pattern)-1] } else if trailingStar { patternType = PatternTypePrefix if len(pattern) < 2 { return patternType, fmt.Errorf("Syntax error in block rules at pattern %d", position) } pattern = pattern[:len(pattern)-1] } else if exact { patternType = PatternTypeExact if len(pattern) < 2 { return patternType, fmt.Errorf("Syntax error in block rules at pattern %d", position) } pattern = pattern[1:] } else { patternType = PatternTypeSuffix if leadingStar { pattern = pattern[1:] } pattern = strings.TrimPrefix(pattern, ".") } if len(pattern) == 0 { dlog.Errorf("Syntax error in block rule at line %d", position) } pattern = strings.ToLower(pattern) switch patternType { case PatternTypeSubstring: patternMatcher.blockedSubstrings = append(patternMatcher.blockedSubstrings, pattern) if val != nil { patternMatcher.indirectVals[pattern] = val } case PatternTypePattern: patternMatcher.blockedPatterns = append(patternMatcher.blockedPatterns, pattern) if val != nil { patternMatcher.indirectVals[pattern] = val } case PatternTypePrefix: patternMatcher.blockedPrefixes.Insert([]byte(pattern), val) case PatternTypeSuffix: patternMatcher.blockedSuffixes.Insert([]byte(StringReverse(pattern)), val) case PatternTypeExact: patternMatcher.blockedExact[pattern] = val default: dlog.Fatal("Unexpected block type") } return patternType, nil } func (patternMatcher *PatternMatcher) Eval(qName string) (reject bool, reason string, val interface{}) { if len(qName) < 2 { return false, "", nil } revQname := StringReverse(qName) if match, xval, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(revQname)); found { if len(match) == len(qName) || revQname[len(match)] == '.' { return true, "*." + StringReverse(string(match)), xval } if len(match) < len(revQname) && len(revQname) > 0 { if i := strings.LastIndex(revQname, "."); i > 0 { pName := revQname[:i] if match, _, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(pName)); found { if len(match) == len(pName) || pName[len(match)] == '.' { return true, "*." + StringReverse(string(match)), xval } } } } } if match, xval, found := patternMatcher.blockedPrefixes.LongestPrefix([]byte(qName)); found { return true, string(match) + "*", xval } for _, substring := range patternMatcher.blockedSubstrings { if strings.Contains(qName, substring) { return true, "*" + substring + "*", patternMatcher.indirectVals[substring] } } for _, pattern := range patternMatcher.blockedPatterns { if found, _ := filepath.Match(pattern, qName); found { return true, pattern, patternMatcher.indirectVals[pattern] } } if xval := patternMatcher.blockedExact[qName]; xval != nil { return true, qName, xval } return false, "", nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_get_set_payload_size.go0000644000175000017500000000336113556612755024421 0ustar ericericpackage main import "github.com/miekg/dns" type PluginGetSetPayloadSize struct{} func (plugin *PluginGetSetPayloadSize) Name() string { return "get_set_payload_size" } func (plugin *PluginGetSetPayloadSize) Description() string { return "Adjusts the maximum payload size advertised in queries sent to upstream servers." } func (plugin *PluginGetSetPayloadSize) Init(proxy *Proxy) error { return nil } func (plugin *PluginGetSetPayloadSize) Drop() error { return nil } func (plugin *PluginGetSetPayloadSize) Reload() error { return nil } func (plugin *PluginGetSetPayloadSize) Eval(pluginsState *PluginsState, msg *dns.Msg) error { pluginsState.originalMaxPayloadSize = 512 - ResponseOverhead edns0 := msg.IsEdns0() dnssec := false if edns0 != nil { pluginsState.maxUnencryptedUDPSafePayloadSize = int(edns0.UDPSize()) 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)) if pluginsState.maxPayloadSize > 512 { extra2 := []dns.RR{} for _, extra := range msg.Extra { if extra.Header().Rrtype != dns.TypeOPT { extra2 = append(extra2, extra) } else if xoptions := &extra.(*dns.OPT).Option; len(*xoptions) > 0 && options == nil { options = xoptions } } msg.Extra = extra2 msg.SetEdns0(uint16(pluginsState.maxPayloadSize), dnssec) if options != nil { for _, extra := range msg.Extra { if extra.Header().Rrtype == dns.TypeOPT { extra.(*dns.OPT).Option = *options break } } } } return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/privilege_others.go0000644000175000017500000000515613556612755022224 0ustar ericeric// +build !windows,!linux package main import ( "os" "os/exec" "os/user" "path/filepath" "runtime" "strconv" "golang.org/x/sys/unix" "github.com/jedisct1/dlog" ) func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) { currentUser, err := user.Current() if err != nil && currentUser.Uid != "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) args := os.Args 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.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) if err != nil { dlog.Fatal(err) } gid, err := strconv.Atoi(userInfo.Gid) if err != nil { dlog.Fatal(err) } execPath, err := exec.LookPath(args[0]) if err != nil { dlog.Fatalf("Unable to get the path to the dnscrypt-proxy executable file: [%s]", err) } path, err := filepath.Abs(execPath) if err != nil { dlog.Fatal(err) } if err := ServiceManagerReadyNotify(); err != nil { dlog.Fatal(err) } args = append(args, "-child") dlog.Notice("Dropping privileges") runtime.LockOSThread() if err := unix.Setgroups([]int{}); err != nil { dlog.Fatalf("Unable to drop additional groups: %s", err) } if err := unix.Setgid(gid); err != nil { dlog.Fatalf("Unable to drop group privileges: %s", err) } 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 { 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) } dnscrypt-proxy-2.0.31/dnscrypt-proxy/netprobe_windows.go0000644000175000017500000000234613556612755022240 0ustar ericericpackage main import ( "net" "time" "github.com/jedisct1/dlog" ) func NetProbe(address string, timeout int) error { if len(address) <= 0 || timeout == 0 { return nil } remoteUDPAddr, err := net.ResolveUDPAddr("udp", address) if err != nil { return err } retried := false if timeout < 0 { timeout = MaxTimeout } else { timeout = Min(MaxTimeout, timeout) } for tries := timeout; tries > 0; tries-- { 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" _, err = pc.Write([]byte{0}) } if err != nil { if !retried { retried = true dlog.Notice("Network not available yet -- waiting...") } dlog.Debug(err) time.Sleep(1 * time.Second) continue } pc.Close() dlog.Notice("Network connectivity detected") return nil } dlog.Error("Timeout while waiting for network connectivity") return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/example-whitelist.txt0000644000175000017500000000134713556612755022527 0ustar ericeric ########################### # 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 dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_nx_log.go0000644000175000017500000000401513556612755021507 0ustar ericericpackage main import ( "errors" "fmt" "net" "time" "github.com/jedisct1/dlog" "github.com/miekg/dns" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) type PluginNxLog struct { logger *lumberjack.Logger format string } func (plugin *PluginNxLog) Name() string { return "nx_log" } func (plugin *PluginNxLog) Description() string { return "Log DNS queries for nonexistent zones." } 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.format = proxy.nxLogFormat return nil } func (plugin *PluginNxLog) Drop() error { return nil } func (plugin *PluginNxLog) Reload() error { return nil } func (plugin *PluginNxLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error { if msg.Rcode != dns.RcodeNameError { return nil } questions := msg.Question if len(questions) == 0 { return nil } question := questions[0] qType, ok := dns.TypeToString[question.Qtype] if !ok { qType = string(qType) } var clientIPStr string if pluginsState.clientProto == "udp" { clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String() } else { clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String() } qName := StripTrailingDot(question.Name) 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), qType) } else if plugin.format == "ltsv" { line = fmt.Sprintf("time:%d\thost:%s\tmessage:%s\ttype:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), qType) } 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 } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_forward.go0000644000175000017500000000473613556612755021677 0ustar ericericpackage main import ( "fmt" "math/rand" "net" "strings" "unicode" "github.com/jedisct1/dlog" "github.com/miekg/dns" ) type PluginForwardEntry struct { domain string servers []string } type PluginForward struct { forwardMap []PluginForwardEntry } func (plugin *PluginForward) Name() string { return "forward" } func (plugin *PluginForward) Description() string { return "Route queries matching specific domains to a dedicated set of servers" } func (plugin *PluginForward) Init(proxy *Proxy) error { dlog.Noticef("Loading the set of forwarding rules from [%s]", proxy.forwardFile) bin, err := ReadTextFile(proxy.forwardFile) if err != nil { return err } for lineNo, line := range strings.Split(string(bin), "\n") { line = strings.TrimFunc(line, unicode.IsSpace) if len(line) == 0 || strings.HasPrefix(line, "#") { 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) } 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) } servers = append(servers, server) } if len(servers) == 0 { continue } plugin.forwardMap = append(plugin.forwardMap, PluginForwardEntry{ domain: domain, servers: servers, }) } return nil } func (plugin *PluginForward) Drop() error { return nil } func (plugin *PluginForward) Reload() error { return nil } func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) != 1 { return nil } question := strings.ToLower(StripTrailingDot(questions[0].Name)) questionLen := len(question) var servers []string for _, candidate := range plugin.forwardMap { candidateLen := len(candidate.domain) if candidateLen > questionLen { continue } if question[questionLen-candidateLen:] == candidate.domain && (candidateLen == questionLen || (question[questionLen-candidateLen-1] == '.')) { servers = candidate.servers break } } if len(servers) == 0 { return nil } server := servers[rand.Intn(len(servers))] pluginsState.serverName = server respMsg, err := dns.Exchange(msg, server) if err != nil { return err } pluginsState.synthResponse = respMsg pluginsState.action = PluginsActionSynth return nil } dnscrypt-proxy-2.0.31/dnscrypt-proxy/plugin_cloak.go0000644000175000017500000001137513556612755021321 0ustar ericericpackage main import ( "math/rand" "net" "strings" "sync" "time" "unicode" "github.com/jedisct1/dlog" "github.com/miekg/dns" ) type CloakedName struct { target string ipv4 []net.IP ipv6 []net.IP lastUpdate *time.Time lineNo int isIP bool } type PluginCloak struct { sync.RWMutex patternMatcher *PatternMatcher ttl uint32 } func (plugin *PluginCloak) Name() string { return "cloak" } func (plugin *PluginCloak) Description() string { return "Return a synthetic IP address or a flattened CNAME for specific names" } func (plugin *PluginCloak) Init(proxy *Proxy) error { dlog.Noticef("Loading the set of cloaking rules from [%s]", proxy.cloakFile) bin, err := ReadTextFile(proxy.cloakFile) if err != nil { return err } plugin.ttl = proxy.cloakTTL plugin.patternMatcher = NewPatternPatcher() cloakedNames := make(map[string]*CloakedName) for lineNo, line := range strings.Split(string(bin), "\n") { line = strings.TrimFunc(line, unicode.IsSpace) if len(line) == 0 || strings.HasPrefix(line, "#") { continue } var target string parts := strings.FieldsFunc(line, unicode.IsSpace) if len(parts) == 2 { line = strings.TrimFunc(parts[0], unicode.IsSpace) target = strings.TrimFunc(parts[1], unicode.IsSpace) } else if len(parts) > 2 { dlog.Errorf("Syntax error in cloaking rules at line %d -- Unexpected space character", 1+lineNo) continue } if len(line) == 0 || len(target) == 0 { dlog.Errorf("Syntax error in cloaking rules at line %d -- Missing name or target", 1+lineNo) continue } line = strings.ToLower(line) cloakedName, found := cloakedNames[line] if !found { cloakedName = &CloakedName{} } if ip := net.ParseIP(target); ip != nil { if ipv4 := ip.To4(); ipv4 != nil { cloakedName.ipv4 = append((*cloakedName).ipv4, ipv4) } else if ipv6 := ip.To16(); ipv6 != nil { cloakedName.ipv6 = append((*cloakedName).ipv6, ipv6) } else { dlog.Errorf("Invalid IP address in cloaking rule at line %d", 1+lineNo) continue } cloakedName.isIP = true } else { cloakedName.target = target } cloakedName.lineNo = lineNo + 1 cloakedNames[line] = cloakedName } for line, cloakedName := range cloakedNames { plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo) } return nil } func (plugin *PluginCloak) Drop() error { return nil } func (plugin *PluginCloak) Reload() error { return nil } func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error { questions := msg.Question if len(questions) != 1 { return nil } question := questions[0] if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) { return nil } qName := strings.ToLower(StripTrailingDot(questions[0].Name)) if len(qName) < 2 { return nil } now := time.Now() plugin.RLock() _, _, xcloakedName := plugin.patternMatcher.Eval(qName) if xcloakedName == nil { plugin.RUnlock() return nil } cloakedName := xcloakedName.(*CloakedName) ttl, expired := plugin.ttl, false if cloakedName.lastUpdate != nil { if elapsed := uint32(now.Sub(*cloakedName.lastUpdate).Seconds()); elapsed < ttl { ttl -= elapsed } else { expired = true } } if !cloakedName.isIP && ((cloakedName.ipv4 == nil && cloakedName.ipv6 == nil) || expired) { target := cloakedName.target plugin.RUnlock() foundIPs, err := net.LookupIP(target) if err != nil { return nil } plugin.Lock() cloakedName.lastUpdate = &now cloakedName.ipv4 = nil cloakedName.ipv6 = nil for _, foundIP := range foundIPs { if ipv4 := foundIP.To4(); ipv4 != nil { cloakedName.ipv4 = append(cloakedName.ipv4, foundIP) if len(cloakedName.ipv4) >= 16 { break } } else { cloakedName.ipv6 = append(cloakedName.ipv6, foundIP) if len(cloakedName.ipv6) >= 16 { break } } } 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, err := EmptyResponseFromMessage(msg) if err != nil { return err } 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} } pluginsState.synthResponse = synth pluginsState.action = PluginsActionSynth pluginsState.returnCode = PluginsReturnCodeCloak return nil }