nelexa / guzzle-doh-middleware
A Dns over Https (DoH) middleware for Guzzle >= 6.0
Requires
- php: ^7.1 | ^8.0
- ext-curl: *
- daverandom/libdns: ^2.0
- guzzlehttp/guzzle: ^6.3 | ^7.0
- psr/cache: ^1.0 | ^2.0 | ^3.0
- psr/log: ^1.0 | ^2.0 | ^3.0
- psr/simple-cache: ^1.0 | ^2.0 | ^3.0
Requires (Dev)
- phpunit/phpunit: ^7 | ^8.5.23 | ^9
- symfony/cache: >=4.4
- symfony/var-dumper: *
- vimeo/psalm: ^4.22
Suggests
- symfony/cache: Install the PSR-6 or PSR-16 caching implementation
This package is auto-updated.
Last update: 2024-11-06 10:54:10 UTC
README
guzzle-doh-middleware
A DNS over HTTPS (DoH) middleware for Guzzle.Goals
- Resolving domains, via DoH before sending HTTP requests.
- Bypassing blocked sites, through DNS packet spoofing.
- Support for caching DNS responses, via PSR-6 and PSR-16 compatible packages.
- Support for multiple DoH providers.
Install
composer require nelexa/guzzle-doh-middleware
Usage
use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use Nelexa\Doh\DohMiddleware; // Create default HandlerStack $stack = HandlerStack::create(); // Add this middleware to the top with `push` $stack->push(DohMiddleware::create(), 'doh'); // Initialize the client with the handler option $client = new Client(['handler' => $stack]);
Setup cache
It is very important to configure caching of DNS requests, so that you do not have to contact a DNS server to resolve domains for every HTTP request.
Install a PSR-6 or PSR-16 compatible caching package.
For example, you can install the popular symfony/cache package.
composer require symfony/cache
Example init PSR-6 redis cache
$cache = new \Symfony\Component\Cache\Adapter\RedisAdapter( \Symfony\Component\Cache\Adapter\RedisAdapter::createConnection() );
Example init PSR-16 redis cache
$cache = new \Symfony\Component\Cache\Psr16Cache( new \Symfony\Component\Cache\Adapter\RedisAdapter( \Symfony\Component\Cache\Adapter\RedisAdapter::createConnection() ) );
You can pass the configured cache as the first argument when creating middleware.
If you don't pass the argument or pass null
, the cache will only be stored for the lifetime of the PHP process.
$stack->push(DohMiddleware::create($cache), 'doh');
Setup DoH Servers
You can specify which DoH servers to use as a second argument when creating middleware. They will be chosen in random order.
The defaults are Cloudflare (for Mozilla) and Google.
Example:
$dohServers = [ 'https://mozilla.cloudflare-dns.com/dns-query', 'https://dns.google/dns-query', 'https://doh.cleanbrowsing.org/doh/security-filter', \Nelexa\Doh\DohServers::SERVER_ADGUARD_FAMILY, 'https://doh.opendns.com/dns-query', ]; $stack->push(DohMiddleware::create($cache, $dohServers), 'doh');
Setup Logger & Debug
You can add the PSR-3 compatible Logger as a 3rd argument when creating middleware.
use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('doh'); $logger->pushHandler(new StreamHandler('path/to/doh.log', Logger::DEBUG)); $stack->push(DohMiddleware::create( cache: $cache, logger: $logger ), 'doh');
For debugging and console output of all DoH requests to servers, you can pass true
as 4th parameter when creating middleware.
$stack->push(DohMiddleware::create( cache: $cache, debug: true, ), 'doh');
Example debug info
* Trying 104.16.248.249:443...
* TCP_NODELAY set
* connect to 104.16.248.249 port 443 failed: Connection refused
* Trying 104.16.249.249:443...
* TCP_NODELAY set
* Connected to mozilla.cloudflare-dns.com (104.16.249.249) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=cloudflare-dns.com
* start date: Oct 25 00:00:00 2021 GMT
* expire date: Oct 25 23:59:59 2022 GMT
* subjectAltName: host "mozilla.cloudflare-dns.com" matched cert's "*.cloudflare-dns.com"
* issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
* SSL certificate verify ok.
> GET /dns-query?dns=q80BAAABAAAAAAAACmdvb2dsZW1haWwBbAZnb29nbGUDY29tAAABAAE HTTP/1.1
Host: mozilla.cloudflare-dns.com
Accept: application/dns-udpwireformat, application/dns-message
User-Agent: DoH-Client
* old SSL session ID is stale, removing
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: cloudflare
< Date: Thu, 03 Mar 2022 09:58:15 GMT
< Content-Type: application/dns-message
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Content-Length: 105
< CF-RAY: 6e6183398ec716f0-DME
<
* Connection #0 to host mozilla.cloudflare-dns.com left intact
Request options
You can configure requests created and transmitted by the client using request options.
Option "doh"
- Summary
Set
false
to disable domain resolving, via DoH.- Types
-
- bool
- Default
true
- Constant
\Nelexa\Doh\DohMiddleware::OPTION_DOH_ENABLED
// Disable DoH for concrete request $client->request('GET', 'https://...', [ 'doh' => false, ]);
To disable DoH middleware by default, pass false
for the doh
option when creating the HTTP client.
$stack = HandlerStack::create(); $stack->push(DohMiddleware::create($cache), 'doh'); $client = new Client([ 'handler' => $stack, 'doh' => false, ]);
Option "doh_ttl"
- Summary
Forced setting of caching time for resolving results. If the option is not passed or
null
is passed, the caching time from the DNS server is used.- Types
-
- integer
- \DateInterval
- null
- Default
null
- Constant
\Nelexa\Doh\DohMiddleware::OPTION_DOH_TTL
$client->request('GET', 'https://...', [ 'doh_ttl' => \DateInterval::createFromDateString('1 hour'), ]);
Option "doh_shuffle"
- Summary
Set
true
to enable shuffling of ip addresses in random order when more than one ip address has been received as a result of domain resolving.- Types
-
- bool
- Default
false
- Constant
\Nelexa\Doh\DohMiddleware::OPTION_DOH_SHUFFLE
// Enable shuffle ip addresses $client->request('GET', 'https://...', [ 'doh_shuffle' => true, ]);
To enable ip mixing for all requests by default, pass true
for the ttl_shuffle
option when creating the HTTP client.
$stack = HandlerStack::create(); $stack->push(DohMiddleware::create($cache), 'doh'); $client = new Client([ 'handler' => $stack, 'doh_shuffle' => true, ]);
Symfony config DI
# config/services.yaml parameters: doh.servers: - 'https://mozilla.cloudflare-dns.com/dns-query', - 'https://dns.google/dns-query', - 'https://doh.opendns.com/dns-query' services: app.client.doh_middleware: factory: Nelexa\Doh\DohMiddleware::create class: Nelexa\Doh\DohMiddleware arguments: - '@cache.app' - '%doh.servers%' - '@logger' - '%kernel.debug%' app.client.handler_stack: factory: GuzzleHttp\HandlerStack::create class: GuzzleHttp\HandlerStack calls: - [ push, [ '@app.client.doh_middleware' ] ] app.client: class: GuzzleHttp\Client arguments: app.client: class: GuzzleHttp\Client arguments: - handler: '@app.client.handler_stack' doh: true # doh_ttl: 3600 # doh_shuffle: true # Aliases GuzzleHttp\Client: '@app.client'
Credits
Changelog
Changes are documented in the releases page.
License
The MIT License (MIT). Please see LICENSE for more information.