| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- <?php
- declare(strict_types=1);
- if (!isset($argv[1], $argv[2], $argv[3])) {
- $error_json = [
- "valid" => false,
- "error_message" => "record, rtype, and priority arguments are required",
- ];
- echo json_encode(
- $error_json,
- JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
- );
- exit(1);
- }
- $record = $argv[1];
- $rtype = $argv[2];
- $priority = $argv[3];
- $known_types = [
- "A",
- "AAAA",
- "NS",
- "CNAME",
- "MX",
- "TXT",
- "SRV",
- "DNSKEY",
- "KEY",
- "IPSECKEY",
- "PTR",
- "SPF",
- "TLSA",
- "CAA",
- "DS",
- ];
- $valid = true;
- $error_message = null;
- $cleaned_record = null;
- $new_priority = null;
- $validateInt = static function ($value, $min, $max) {
- return filter_var($value, FILTER_VALIDATE_INT, [
- "options" => ["min_range" => $min, "max_range" => $max],
- ]);
- };
- $validateDomain = static function ($value) {
- return filter_var(rtrim($value, "."), FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
- };
- $validateHex = static function ($value) {
- return preg_match('/^[A-Fa-f0-9]+$/', $value) === 1;
- };
- $validateBase64 = static function ($value) {
- if ($value === "" || preg_match("/[^A-Za-z0-9+\/=]/", $value)) {
- return false;
- }
- return base64_decode($value, true) !== false;
- };
- $validatePrintableAscii = static function ($value) {
- return !preg_match('/[^\x20-\x7E]/', $value);
- };
- $validateSrvTarget = static function ($value) use ($validateDomain) {
- return $value === "." || $validateDomain($value);
- };
- if (!in_array($rtype, $known_types, true)) {
- $valid = false;
- $error_message = "unknown record type for validation: $rtype";
- } elseif ($rtype === "A") {
- $valid = filter_var($record, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
- if (!$valid) {
- $error_message = "invalid A record format";
- }
- } elseif ($rtype === "AAAA") {
- $valid = filter_var($record, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
- if (!$valid) {
- $error_message = "invalid AAAA record format";
- }
- } elseif ($rtype === "NS" || $rtype === "CNAME" || $rtype === "PTR") {
- $valid = $validateDomain($record);
- if (!$valid) {
- $error_message = "invalid $rtype record format";
- }
- } elseif ($rtype === "MX") {
- $valid = $validateDomain($record);
- if (!$valid) {
- $error_message = "invalid MX record format";
- } else {
- if ($priority === "") {
- $valid = false;
- $error_message = "MX record priority is required";
- } else {
- $valid = $validateInt($priority, 0, 65535);
- if ($valid === false) {
- $error_message = "invalid MX record priority format (must be between 0 and 65535)";
- }
- }
- }
- } elseif ($rtype === "SRV") {
- $parts = preg_split("/\s+/", trim($record));
- $parts_count = count($parts);
- $target = null;
- $port = null;
- $weight = null;
- $priority_to_use = $priority;
- if ($parts_count === 4) {
- [$priority_from_value, $weight, $port, $target] = $parts;
- $priority_to_use = $priority_from_value;
- } elseif ($parts_count === 3) {
- if ($validateSrvTarget($parts[0])) {
- [$target, $port, $weight] = $parts;
- } elseif ($validateSrvTarget($parts[2])) {
- [$weight, $port, $target] = $parts;
- } else {
- $valid = false;
- $error_message = "invalid SRV record format (expected target with port and weight)";
- }
- } else {
- $valid = false;
- $error_message =
- "invalid SRV record format (must contain priority weight port target or target port weight)";
- }
- if ($valid !== false) {
- $priority_validated = $validateInt($priority_to_use, 0, 65535);
- $weight_validated = $validateInt($weight, 0, 65535);
- $port_validated = $validateInt($port, 0, 65535);
- $target_validated = $validateSrvTarget($target ?? "");
- if ($priority_validated === false) {
- $valid = false;
- $error_message = "invalid SRV record priority format (must be between 0 and 65535)";
- } elseif ($weight_validated === false) {
- $valid = false;
- $error_message = "invalid SRV record weight format (must be between 0 and 65535)";
- } elseif ($port_validated === false) {
- $valid = false;
- $error_message = "invalid SRV record port format (must be between 0 and 65535)";
- } elseif (!$target_validated) {
- $valid = false;
- $error_message = "invalid SRV record target format";
- } else {
- $new_priority = $priority_validated;
- $cleaned_record = $weight_validated . " " . $port_validated . " " . $target;
- }
- }
- } elseif ($rtype === "TXT" || $rtype === "SPF") {
- if ($record === "") {
- $valid = false;
- $error_message = "$rtype record cannot be empty";
- } elseif (strlen($record) > 65535) {
- $valid = false;
- $error_message = "$rtype record exceeds maximum length";
- } elseif (!$validatePrintableAscii($record)) {
- $valid = false;
- $error_message = "$rtype record contains non-ASCII characters";
- }
- } elseif ($rtype === "DNSKEY" || $rtype === "KEY") {
- $parts = preg_split("/\s+/", trim($record));
- if (count($parts) < 3) {
- $valid = false;
- $error_message = "invalid $rtype record format (expected flags protocol algorithm [public-key])";
- } else {
- [$flags, $protocol, $algorithm] = array_slice($parts, 0, 3);
- $public_key = implode(" ", array_slice($parts, 3));
- $flags_valid = $validateInt($flags, 0, 65535);
- $protocol_valid = $validateInt($protocol, 0, 255);
- $algorithm_valid = $validateInt($algorithm, 0, 255);
- if ($rtype === "DNSKEY" && $protocol !== "3") {
- $protocol_valid = false;
- }
- if ($flags_valid === false || $protocol_valid === false || $algorithm_valid === false) {
- $valid = false;
- $error_message = "invalid $rtype numeric fields";
- } elseif ($rtype === "KEY" && $algorithm === "0") {
- if ($public_key !== "") {
- $valid = false;
- $error_message = "invalid KEY public key for algorithm 0 (must be empty)";
- }
- } elseif ($public_key === "" || !$validateBase64($public_key)) {
- $valid = false;
- $error_message = "invalid $rtype public key (must be base64)";
- }
- }
- } elseif ($rtype === "IPSECKEY") {
- $parts = preg_split("/\s+/", trim($record));
- if (count($parts) < 4) {
- $valid = false;
- $error_message =
- "invalid IPSECKEY record format (expected precedence gateway-type algorithm gateway [public-key])";
- } else {
- [$precedence, $gateway_type, $algorithm, $gateway] = array_slice($parts, 0, 4);
- $public_key = implode(" ", array_slice($parts, 4));
- $precedence_valid = $validateInt($precedence, 0, 255);
- $gateway_type_valid = $validateInt($gateway_type, 0, 3);
- $algorithm_valid = $validateInt($algorithm, 0, 255);
- if (
- $precedence_valid === false ||
- $gateway_type_valid === false ||
- $algorithm_valid === false
- ) {
- $valid = false;
- $error_message = "invalid IPSECKEY numeric fields";
- } else {
- if ($gateway_type === "0") {
- if ($gateway !== "." && $gateway !== "") {
- $valid = false;
- $error_message = "invalid IPSECKEY gateway for type 0";
- }
- } elseif ($gateway_type === "1") {
- if (!filter_var($gateway, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
- $valid = false;
- $error_message = "invalid IPSECKEY IPv4 gateway";
- }
- } elseif ($gateway_type === "2") {
- if (!filter_var($gateway, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
- $valid = false;
- $error_message = "invalid IPSECKEY IPv6 gateway";
- }
- } else {
- if (!$validateDomain($gateway)) {
- $valid = false;
- $error_message = "invalid IPSECKEY domain gateway";
- }
- }
- if ($valid !== false) {
- if ($algorithm === "0") {
- if ($public_key !== "") {
- $valid = false;
- $error_message =
- "invalid IPSECKEY public key for algorithm 0 (must be empty)";
- }
- } elseif ($public_key === "" || !$validateBase64($public_key)) {
- $valid = false;
- $error_message = "invalid IPSECKEY public key (must be base64)";
- }
- }
- }
- }
- } elseif ($rtype === "TLSA") {
- $parts = preg_split("/\s+/", trim($record));
- if (count($parts) < 4) {
- $valid = false;
- $error_message = "invalid TLSA record format (expected usage selector matching-type data)";
- } else {
- [$usage, $selector, $matching_type] = array_slice($parts, 0, 3);
- $data = implode(" ", array_slice($parts, 3));
- $usage_valid = $validateInt($usage, 0, 3);
- $selector_valid = $validateInt($selector, 0, 1);
- $matching_valid = $validateInt($matching_type, 0, 2);
- $data_length = strlen($data);
- if ($usage_valid === false || $selector_valid === false || $matching_valid === false) {
- $valid = false;
- $error_message = "invalid TLSA numeric fields";
- } elseif ($data === "" || !$validateHex($data) || $data_length % 2 !== 0) {
- $valid = false;
- $error_message = "invalid TLSA data";
- } elseif ($matching_valid === 1 && $data_length !== 64) {
- $valid = false;
- $error_message = "invalid TLSA data length for matching type 1";
- } elseif ($matching_valid === 2 && $data_length !== 128) {
- $valid = false;
- $error_message = "invalid TLSA data length for matching type 2";
- }
- }
- } elseif ($rtype === "CAA") {
- $parts = preg_split("/\s+/", trim($record), 3);
- if (count($parts) < 3) {
- $valid = false;
- $error_message = "invalid CAA record format (expected flag tag value)";
- } else {
- [$flag, $tag, $value] = $parts;
- $flag_valid = $validateInt($flag, 0, 255);
- $tag_valid = preg_match('/^[A-Za-z0-9-]{1,63}$/', $tag);
- if ($flag_valid === false || $tag_valid === 0) {
- $valid = false;
- $error_message = "invalid CAA flag or tag";
- } elseif ($value === "") {
- $valid = false;
- $error_message = "invalid CAA value";
- }
- }
- } elseif ($rtype === "DS") {
- $parts = preg_split("/\s+/", trim($record));
- if (count($parts) < 4) {
- $valid = false;
- $error_message = "invalid DS record format (expected keytag algorithm digest-type digest)";
- } else {
- [$key_tag, $algorithm, $digest_type] = array_slice($parts, 0, 3);
- $digest = implode(" ", array_slice($parts, 3));
- $key_tag_valid = $validateInt($key_tag, 0, 65535);
- $algorithm_valid = $validateInt($algorithm, 0, 255);
- $digest_type_valid = $validateInt($digest_type, 0, 255);
- $digest_lengths = [1 => 40, 2 => 64, 3 => 64, 4 => 96];
- $digest_length = strlen($digest);
- if (
- $key_tag_valid === false ||
- $algorithm_valid === false ||
- $digest_type_valid === false
- ) {
- $valid = false;
- $error_message = "invalid DS numeric fields";
- } elseif ($digest === "" || !$validateHex($digest) || $digest_length % 2 !== 0) {
- $valid = false;
- $error_message = "invalid DS digest";
- } elseif (
- array_key_exists((int) $digest_type, $digest_lengths) &&
- $digest_length !== $digest_lengths[(int) $digest_type]
- ) {
- $valid = false;
- $error_message = "invalid DS digest length for type $digest_type";
- }
- }
- } else {
- $valid = false;
- $error_message = "validation not implemented for record type: $rtype";
- }
- $json = ["valid" => $valid !== false];
- if ($error_message !== null) {
- $json["error_message"] = $error_message;
- }
- if ($cleaned_record !== null) {
- $json["cleaned_record"] = $cleaned_record;
- }
- if ($new_priority !== null) {
- $json["new_priority"] = $new_priority;
- }
- echo json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|