Last active
March 10, 2026 23:43
-
-
Save masakielastic/ff79ec78e844678f46529ff8edb0bbef to your computer and use it in GitHub Desktop.
Minimal ReactPHP example for querying and parsing HTTPS DNS records (type 65)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| Minimal ReactPHP example for querying HTTPS DNS Resource Records (type 65). | |
| This script demonstrates how to: | |
| 1. Query HTTPS DNS records using ReactPHP DNS | |
| 2. Retrieve raw RDATA from the DNS response | |
| 3. Parse SVCB/HTTPS wire format | |
| 4. Extract ALPN values such as "h2" and "h3" | |
| HTTPS DNS records are defined in RFC 9460 and are used by modern | |
| web clients for service discovery, including HTTP/3 support. | |
| Example DNS query: | |
| dig HTTPS google.com | |
| Example result: | |
| google.com. IN HTTPS 1 . alpn="h2,h3" | |
| Repository / reference implementation: | |
| https://gist.github.com/masakielastic/524e2f123d7f7fefee43c4f9a28d92cf | |
| Requirements: | |
| - PHP | |
| - react/event-loop | |
| - react/dns | |
| */ | |
| require __DIR__ . '/vendor/autoload.php'; | |
| use React\EventLoop\Loop; | |
| use React\Dns\Resolver\Factory; | |
| $config = React\Dns\Config\Config::loadSystemConfigBlocking(); | |
| if (!$config->nameservers) { | |
| $config->nameservers[] = '8.8.8.8'; | |
| } | |
| $factory = new Factory(); | |
| $resolver = $factory->create($config); | |
| $resolver->resolveAll('google.com', 65)->then( | |
| function (array $records): void { | |
| $data = $records[0]; | |
| $alpns = parseAlpn($data); | |
| var_dump($alpns); | |
| }, | |
| function (Throwable $e): void { | |
| echo $e->getMessage() . PHP_EOL; | |
| } | |
| ); | |
| Loop::run(); | |
| function parseHttpsRR(string $data): array | |
| { | |
| $offset = 0; | |
| $len = strlen($data); | |
| // priority | |
| $priority = unpack('n', substr($data, $offset, 2))[1]; | |
| $offset += 2; | |
| // target name | |
| $labelLen = ord($data[$offset]); | |
| $offset++; | |
| if ($labelLen === 0) { | |
| $target = '.'; | |
| } else { | |
| $target = substr($data, $offset, $labelLen); | |
| $offset += $labelLen; | |
| } | |
| $params = []; | |
| while ($offset < $len) { | |
| $key = unpack('n', substr($data, $offset, 2))[1]; | |
| $offset += 2; | |
| $paramLen = unpack('n', substr($data, $offset, 2))[1]; | |
| $offset += 2; | |
| $value = substr($data, $offset, $paramLen); | |
| $offset += $paramLen; | |
| $params[$key] = $value; | |
| } | |
| return [ | |
| 'priority' => $priority, | |
| 'target' => $target, | |
| 'params' => $params | |
| ]; | |
| } | |
| function parseAlpn(string $value): array | |
| { | |
| $offset = 0; | |
| $alpns = []; | |
| while ($offset < strlen($value)) { | |
| $l = ord($value[$offset]); | |
| $offset++; | |
| $alpns[] = substr($value, $offset, $l); | |
| $offset += $l; | |
| } | |
| return $alpns; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment