Skip to content

Add DNS over HTTPS (DoH) client and resolvers#36

Open
lohanidamodar wants to merge 6 commits into
mainfrom
claude/add-doh-support-0iirq
Open

Add DNS over HTTPS (DoH) client and resolvers#36
lohanidamodar wants to merge 6 commits into
mainfrom
claude/add-doh-support-0iirq

Conversation

@lohanidamodar
Copy link
Copy Markdown
Contributor

@lohanidamodar lohanidamodar commented Feb 3, 2026

Summary

This PR introduces DNS over HTTPS (DoH) support to the DNS library, implementing RFC 8484 for secure DNS queries over HTTPS. It includes a core DoH client, generic DoH resolver, and pre-configured resolvers for Cloudflare and Google's public DNS services.

Key Changes

  • DoHClient (src/DNS/DoHClient.php): Core implementation supporting both GET and POST methods for DNS queries over HTTPS

    • Validates endpoint URLs and HTTP methods in constructor
    • Implements RFC 8484 Section 4.1 specifications
    • Handles base64url encoding for GET requests
    • Validates DNS transaction IDs in responses
    • Comprehensive error handling for network and protocol errors
  • DoH Resolver (src/DNS/Resolver/DoH.php): Generic resolver implementing the Resolver interface

    • Wraps DoHClient for use in resolver chains
    • Supports custom endpoints and configurable timeouts
    • Provides access to underlying client instance
  • CloudflareDoH Resolver (src/DNS/Resolver/CloudflareDoH.php): Pre-configured resolver for Cloudflare's public DNS

    • Primary endpoint: https://cloudflare-dns.com/dns-query
    • Backup endpoint: https://one.one.one.one/dns-query
    • Supports both GET and POST methods
  • GoogleDoH Resolver (src/DNS/Resolver/GoogleDoH.php): Pre-configured resolver for Google's public DNS

    • Endpoint: https://dns.google/dns-query
    • Supports both GET and POST methods
  • Comprehensive Test Suite:

    • Unit tests for DoHClient validation and configuration
    • E2E tests for all resolvers covering A, AAAA, MX, TXT, and NS record types
    • Tests for both GET and POST methods
    • Tests for backup endpoint functionality

Implementation Details

  • Uses cURL for HTTPS communication with proper timeout and redirect handling
  • Implements base64url encoding (RFC 4648 Section 5) for GET method parameters
  • Validates HTTP 200 responses and handles errors gracefully
  • Verifies DNS transaction IDs match between request and response
  • Supports configurable timeouts and HTTP methods
  • All classes include comprehensive PHPDoc documentation

https://claude.ai/code/session_01W9EN976RGtJV3B3Lu2DYrB

Summary by CodeRabbit

  • New Features

    • DNS over HTTPS (DoH) support (GET/POST) compliant with RFC 8484
    • Cloudflare and Google DoH resolvers with primary and backup endpoints
    • Resolution support for A, AAAA, MX, TXT, and NS records
  • Tests

    • Added comprehensive unit and end-to-end tests for DoH functionality
  • Chores

    • Development environment DNS configured to use external resolvers (8.8.8.8, 1.1.1.1)

Implements DoH client and resolvers for secure DNS queries over HTTPS:
- DoHClient: Core client supporting both GET and POST methods per RFC 8484
- DoH resolver base class for custom DoH endpoints
- CloudflareDoH: Pre-configured Cloudflare DoH resolver
- GoogleDoH: Pre-configured Google DoH resolver

Includes comprehensive unit and E2E tests for all new functionality.

https://claude.ai/code/session_01W9EN976RGtJV3B3Lu2DYrB
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 3, 2026

Walkthrough

Adds DNS-over-HTTPS support. Introduces DoHClient (GET/POST, endpoint/method validation, request encoding, cURL calls, response decoding and transaction ID checking) and a DoH resolver that wraps it. Adds CloudflareDoH and GoogleDoH concrete resolvers with primary/backup endpoints. Adds unit tests for DoHClient constructor/validation and multiple end-to-end tests for DoH, CloudflareDoH, and GoogleDoH covering A/AAAA/MX/TXT/NS queries and GET/POST methods. Updates docker-compose DNS servers to 8.8.8.8 and 1.1.1.1.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately and concisely summarizes the main change—adding DNS over HTTPS (DoH) client and resolvers, which is exactly what the changeset implements across multiple new files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/add-doh-support-0iirq

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Update docker-compose.yml to use public DNS servers (8.8.8.8, 1.1.1.1)
instead of 127.0.0.1 to allow DoH E2E tests to resolve external hostnames
like cloudflare-dns.com and dns.google.

https://claude.ai/code/session_01W9EN976RGtJV3B3Lu2DYrB
* @param string $method HTTP method to use (GET or POST)
*/
public function __construct(
bool $useBackup = false,
Copy link
Copy Markdown
Contributor

@loks0n loks0n Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we expose useBackup?

Copy link
Copy Markdown
Contributor Author

@lohanidamodar lohanidamodar May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept it for symmetry with the existing UDP Resolver\Cloudflare (which also exposes bool $useBackup). Common usage is new Cloudflare() / new Cloudflare(useBackup: true), with the endpoint constants left as escape hatches for anyone who wants to bypass and pass a custom URL through Resolver\Http.

Comment thread src/DNS/DoHClient.php Outdated
Comment on lines +74 to +75
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => $this->timeout,
Copy link
Copy Markdown
Contributor

@loks0n loks0n Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be different fields, timeouts are really important to avoid worker exhaustion at our scale

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Split into $timeout (total request, default 5) and $connectTimeout (default 2), wired through Http\Client, Resolver\Http, and the Cloudflare/Google subclasses. Connect timeout maps to CURLOPT_CONNECTTIMEOUT, total to CURLOPT_TIMEOUT, so a slow upstream can no longer eat 5s of worker time before we even know it's unreachable.

Comment thread src/DNS/Resolver/DoH.php Outdated
* A resolver that forwards DNS queries to a DoH server over HTTPS.
* Implements RFC 8484 for DNS queries over HTTP/HTTPS.
*/
class DoH implements Resolver
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call it Http instead of DoH?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Class is now Utopia\DNS\Resolver\Http (file src/DNS/Resolver/Http.php).

Comment thread src/DNS/DoHClient.php Outdated
* Implements DNS queries over HTTPS as specified in RFC 8484.
* Supports both GET and POST methods for sending DNS queries.
*/
class DoHClient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we put this in Utopia\DNS\Http\Client

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Moved to Utopia\DNS\Http\Client at src/DNS/Http/Client.php.

Comment thread src/DNS/Resolver/CloudflareDoH.php Outdated
@@ -0,0 +1,46 @@
<?php

namespace Utopia\DNS\Resolver;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we but this in Utopia\DNS\Resolver\Http\Cloudflare?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Now at Utopia\DNS\Resolver\Http\Cloudflare (src/DNS/Resolver/Http/Cloudflare.php). Same restructure applied to GoogleDoHUtopia\DNS\Resolver\Http\Google.

@stnguyen90 stnguyen90 removed their request for review March 19, 2026 23:55
…imeouts

- Move DoHClient to Utopia\DNS\Http\Client
- Rename Resolver\DoH to Resolver\Http
- Move CloudflareDoH/GoogleDoH under Resolver\Http\{Cloudflare,Google}
- Split single $timeout into $timeout (total) and $connectTimeout (connect)
  for better worker exhaustion control under load

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR adds a complete DNS-over-HTTPS implementation (RFC 8484) including a core Client, a generic Http resolver, and pre-configured Cloudflare and Google subclasses. Previous review rounds addressed curl_init() failure handling, HTTPS-only redirect enforcement, duplicate Google backup endpoints, and HTTP scheme validation — all of which are correctly resolved in the current code.

  • src/DNS/Http/Client.php: Implements RFC 8484 GET and POST paths with proper base64url encoding, transaction ID validation, and cURL error handling; one remaining gap is that queryPost lacks CURLOPT_POSTREDIR, so a 301/302 redirect drops the POST body and silently converts the request to a parameterless GET.
  • src/DNS/Resolver/Http*: Clean resolver hierarchy; Cloudflare and Google subclasses expose distinct primary/IP-addressed backup endpoints and delegate all query logic to Client.
  • docker-compose.yml: DNS changed from 127.0.0.1 to 8.8.8.8/1.1.1.1 so the single test container can reach external DoH hosts; no other compose services are affected.

Confidence Score: 5/5

Safe to merge; the core DoH implementation is solid and all issues from prior review rounds have been fixed

All previously identified issues — curl handle failure guards, HTTPS-only redirects, Google backup endpoint deduplication, and HTTP scheme rejection — are correctly resolved in the current code. The one remaining gap (POST redirects silently becoming parameterless GET requests without CURLOPT_POSTREDIR) is a hardening concern for edge-case redirect scenarios that don't occur with Cloudflare or Google's current endpoints.

src/DNS/Http/Client.php — the queryPost redirect handling is the only area that could behave unexpectedly with a redirecting DoH endpoint

Important Files Changed

Filename Overview
src/DNS/Http/Client.php Core DoH client implementing RFC 8484 GET/POST; previous issues (curl_init guard, HTTPS redirect restriction, http:// endpoint rejection) all addressed — one gap remains: POST redirects may silently downgrade to GET
src/DNS/Resolver/Http.php Generic DoH resolver wrapping Client; clean implementation of the Resolver interface
src/DNS/Resolver/Http/Cloudflare.php Pre-configured Cloudflare resolver with distinct primary/backup endpoints; looks correct
src/DNS/Resolver/Http/Google.php Pre-configured Google resolver; ENDPOINT_BACKUP correctly set to IP-addressed 8.8.8.8 endpoint after prior review fix
docker-compose.yml DNS changed from 127.0.0.1 to 8.8.8.8/1.1.1.1 to allow the test container to resolve external DoH hostnames; no other services in the compose file are affected

Reviews (4): Last reviewed commit: "fix: reject non-HTTPS DoH endpoints" | Re-trigger Greptile

Comment thread src/DNS/Http/Client.php
Comment thread src/DNS/Http/Client.php
Comment thread src/DNS/Resolver/Http/Google.php Outdated
@lohanidamodar lohanidamodar requested a review from loks0n May 6, 2026 04:23
- Throw Exception when curl_init() returns false in queryPost/queryGet
  (prevents fatal TypeError on rare cURL init failures)
- Change Google ENDPOINT_BACKUP to https://8.8.8.8/dns-query so useBackup
  actually fails over to a distinct IP-addressed endpoint that survives
  a DNS resolution failure for dns.google

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/DNS/Http/Client.php
Add CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS in both queryPost and
queryGet so a malicious or misbehaving DoH server cannot redirect a
query to plaintext HTTP, which would defeat the security guarantee of
DNS-over-HTTPS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/DNS/Http/Client.php Outdated
FILTER_VALIDATE_URL accepts any scheme (http, ftp, ...). For DoH the
endpoint must be HTTPS, otherwise the encryption guarantee is silently
lost. Add an explicit https scheme check and a unit test for the http
rejection path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants