Использование DNS в PHP с примерами кода
Основы работы с DNS в PHP
Какое решение является самым универсальным для получения DNS записей
Функция dns_get_record позволяет запросить любой тип DNS записи (A, AAAA, MX, CNAME, TXT, NS, SOA, SRV и другие). Она возвращает массив ассоциативных массивов с детальным описанием каждой записи.
$domain = 'example.com';
$records = dns_get_record($domain, DNS_ALL);
print_r($records);
Array
(
[0] => Array
(
[host] => example.com
[class] => IN
[ttl] => 14400
[type] => A
[ip] => 93.184.216.34
)
[1] => Array
(
[host] => example.com
[class] => IN
[ttl] => 14400
[type] => AAAA
[ipv6] => 2606:2800:220:1:248:1893:25c8:1946
)
)
Второй аргумент - битовая маска типов записей (например, DNS_A, DNS_MX или DNS_ALL для всех).
Типичные проблемы
- Функция может вернуть пустой массив, если домен не существует или нет записей. Нужно проверять результат.
- На некоторых хостингах может быть отключена (из-за safe_mode или ограничений). Решение - использовать альтернативные методы (curl к DNS через HTTPS).
- Тайм-аут по умолчанию - 5 секунд. Можно изменить через ini_set('default_socket_timeout', 10);
Как быстро получить IP-адрес домена (только A запись)
gethostbyname возвращает один IPv4 адрес в виде строки. Подходит для простых проверок.
$ip = gethostbyname('example.com');
echo $ip;
93.184.216.34
Если требуется массив всех IP адресов для балансировки, используется gethostbynamel.
$ips = gethostbynamel('google.com');
print_r($ips);
Array
(
[0] => 142.250.185.142
[1] => 142.250.185.138
[2] => 142.250.185.110
...
)
Ограничения и ошибки
- Не возвращает записи AAAA (IPv6). Для IPv6 нужно использовать dns_get_record.
- При недоступности DNS возвращает исходный домен (неоправданное поведение). Проверяется через filter_var с FILTER_VALIDATE_IP.
Как проверить существование почтовой записи MX
Функция checkdnsrr проверяет, существуют ли записи указанного типа. Возвращает true/false.
$hasMX = checkdnsrr('example.com', 'MX');
if ($hasMX) {
echo "MX записи найдены";
} else {
echo "MX записей нет";
}
Нюансы
Для получения подробной информации о записях MX (приоритет и адрес) лучше использовать dns_get_record с типом DNS_MX.
Как выполнить обратный DNS запрос (PTR)
Функция gethostbyaddr возвращает доменное имя по IP-адресу.
$hostname = gethostbyaddr('93.184.216.34');
echo $hostname;
93.184.216.34
Если PTR запись отсутствует, возвращается исходный IP.
Проблемы надежности
Обратные запросы могут быть медленными, если DNS сервер не отвечает. Рекомендуется устанавливать тайм-аут через default_socket_timeout.
Как использовать DNS через HTTPS (DoH) для обхода блокировок
Если встроенные функции заблокированы или нужен сторонний провайдер, применяется curl к API Cloudflare или Google.
$url = 'https://dns.google/resolve?name=example.com&type=A';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (isset($data['Answer'])) {
foreach ($data['Answer'] as $answer) {
echo $answer['data'] . "\n";
}
}
93.184.216.34
Возможные трудности
- Требуется расширение curl и разрешение на внешние соединения.
- У разных провайдеров разный формат ответа. Необходимо адаптировать код под конкретный API.
Расширенные примеры работы с DNS в PHP
Пример 1. Получение всех типов записей для домена с обработкой ошибок
function getDnsRecordsSafe($domain, $timeout = 5) {
ini_set('default_socket_timeout', $timeout);
$records = @dns_get_record($domain, DNS_A | DNS_AAAA | DNS_MX | DNS_CNAME | DNS_TXT | DNS_NS | DNS_SOA | DNS_SRV);
if ($records === false) {
throw new Exception("DNS запрос не удался для домена: $domain");
}
return $records;
}
try {
$result = getDnsRecordsSafe('github.com');
foreach ($result as $rec) {
$type = $rec['type'];
$host = $rec['host'];
$data = '';
switch ($type) {
case 'A':
$data = $rec['ip'];
break;
case 'AAAA':
$data = $rec['ipv6'];
break;
case 'MX':
$data = $rec['target'] . ' (priority ' . $rec['pri'] . ')';
break;
case 'CNAME':
$data = $rec['target'];
break;
case 'TXT':
$data = $rec['txt'];
break;
case 'NS':
$data = $rec['target'];
break;
default:
$data = json_encode($rec);
}
echo "$type: $host -> $data\n";
}
} catch (Exception $e) {
echo "Ошибка: " . $e->getMessage();
}
A: github.com -> 20.27.177.113 AAAA: github.com -> (пусто, нет записи) MX: github.com -> alt1.aspmx.l.google.com (priority 1) TXT: github.com -> "v=spf1 include:spf.github.com ~all" NS: github.com -> ns1.p16.dynect.net ...
Пример 2. Кэширование DNS запросов с помощью файла или Memcached
function getDnsWithCache($domain, $type = DNS_A, $cacheFile = '/tmp/dns_cache.json', $ttl = 86400) {
$cache = [];
if (file_exists($cacheFile)) {
$cache = json_decode(file_get_contents($cacheFile), true);
}
$key = md5($domain . $type);
if (isset($cache[$key]) && (time() - $cache[$key]['time'] < $ttl)) {
return $cache[$key]['data'];
}
$records = dns_get_record($domain, $type);
$cache[$key] = ['time' => time(), 'data' => $records];
file_put_contents($cacheFile, json_encode($cache));
return $records;
}
$mx = getDnsWithCache('yandex.ru', DNS_MX);
print_r($mx);
Array
(
[0] => Array
(
[host] => yandex.ru
[class] => IN
[ttl] => 3600
[type] => MX
[pri] => 10
[target] => mx.yandex.ru
)
)
Пример 3. Рекурсивный поиск CNAME до конечного A/AAAA
function resolveCanonical($domain, $depth = 10) {
if ($depth <= 0) return $domain;
$rec = dns_get_record($domain, DNS_CNAME);
if (!empty($rec) && $rec[0]['type'] == 'CNAME') {
return resolveCanonical($rec[0]['target'], $depth - 1);
}
return $domain;
}
$final = resolveCanonical('www.google.com');
echo $final; // скорее всего google.com
google.com
Пример 4. Использование DNS over HTTPS (DoH) с несколькими провайдерами
function dohQuery($domain, $type = 'A', $provider = 'cloudflare') {
$providers = [
'cloudflare' => 'https://cloudflare-dns.com/dns-query?name=' . $domain . '&type=' . $type,
'google' => 'https://dns.google/resolve?name=' . $domain . '&type=' . $type,
];
$url = $providers[$provider] ?? $providers['cloudflare'];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Accept: application/dns-json'],
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) return null;
$data = json_decode($response, true);
$answers = [];
if (isset($data['Answer'])) {
foreach ($data['Answer'] as $ans) {
if ($ans['type'] === 1 && $type === 'A') $answers[] = $ans['data'];
if ($ans['type'] === 28 && $type === 'AAAA') $answers[] = $ans['data'];
}
}
return $answers;
}
$ipv4 = dohQuery('example.com', 'A', 'google');
print_r($ipv4);
Array
(
[0] => 93.184.216.34
)
Пример 5. Получение SRV записей для SIP или XMPP
$domain = '_sip._tcp.example.com';
$records = dns_get_record($domain, DNS_SRV);
if ($records) {
foreach ($records as $srv) {
echo "Priority: {$srv['pri']}, Weight: {$srv['weight']}, Port: {$srv['port']}, Target: {$srv['target']}\n";
}
} else {
echo "SRV записи не найдены";
}
Priority: 10, Weight: 60, Port: 5060, Target: sip.example.com Priority: 20, Weight: 40, Port: 5061, Target: sip2.example.com
Пример 6. Обработка ошибок при тайм-ауте с пользовательским исключением
function dnsWithTimeout($domain, $timeout = 2) {
$originalTimeout = ini_get('default_socket_timeout');
ini_set('default_socket_timeout', $timeout);
$records = @dns_get_record($domain, DNS_A);
ini_set('default_socket_timeout', $originalTimeout);
if ($records === false || empty($records)) {
throw new RuntimeException("DNS не ответил за $timeout секунд или запись отсутствует");
}
return $records;
}
try {
$a = dnsWithTimeout('nonexistent-domain-xyz.test', 1);
} catch (RuntimeException $e) {
echo "Проблема: " . $e->getMessage();
}
Проблема: DNS не ответил за 1 секунд или запись отсутствует