Введение
В большинстве случаев Qt резолвит доменные имена автоматически и незаметно — например, при вызове QTcpSocket::connectToHost() с именем хоста вместо IP-адреса. Но иногда нужен явный контроль над DNS-резолвингом: получить все IP-адреса домена, проверить MX или TXT-записи, реализовать собственную балансировку между несколькими адресами. Для этого в Qt есть класс QDnsLookup, который выполняет асинхронные DNS-запросы напрямую.
Концепция
QDnsLookup поддерживает основные типы записей: A/AAAA (адреса), MX (почтовые серверы), TXT, CNAME, NS, PTR (обратный резолвинг). Запрос выполняется асинхронно: задаются тип записи через setType() и имя через setName(), после чего вызывается lookup(); результат приходит в сигнале finished(), а сами данные читаются через методы вроде hostAddressRecords() или mailExchangeRecords() в зависимости от типа запроса.
В отличие от блокирующих функций типа gethostbyname, QDnsLookup не блокирует поток выполнения и интегрируется в event loop приложения, что делает его уместным даже в однопоточных GUI-приложениях.
Пример кода: QDnsLookup
#include <QCoreApplication>
#include <QDnsLookup>
#include <QDebug>
class DnsResolver : public QObject
{
Q_OBJECT
public:
explicit DnsResolver(QObject *parent = nullptr) : QObject(parent) {}
void resolveAddresses(const QString &hostname)
{
auto *lookup = new QDnsLookup(QDnsLookup::A, hostname, this);
connect(lookup, &QDnsLookup::finished, this, [this, lookup]() {
onAddressLookupFinished(lookup);
});
lookup->lookup();
}
void resolveMxRecords(const QString &domain)
{
auto *lookup = new QDnsLookup(QDnsLookup::MX, domain, this);
connect(lookup, &QDnsLookup::finished, this, [this, lookup]() {
onMxLookupFinished(lookup);
});
lookup->lookup();
}
private:
void onAddressLookupFinished(QDnsLookup *lookup)
{
lookup->deleteLater();
if (lookup->error() != QDnsLookup::NoError) {
qWarning() << "Ошибка DNS-резолвинга:" << lookup->errorString();
return;
}
for (const QDnsHostAddressRecord &record : lookup->hostAddressRecords()) {
qDebug() << "IP-адрес:" << record.value().toString()
<< "TTL:" << record.timeToLive();
}
}
void onMxLookupFinished(QDnsLookup *lookup)
{
lookup->deleteLater();
if (lookup->error() != QDnsLookup::NoError) {
qWarning() << "Ошибка резолвинга MX:" << lookup->errorString();
return;
}
for (const QDnsMailExchangeRecord &record : lookup->mailExchangeRecords()) {
qDebug() << "Почтовый сервер:" << record.exchange()
<< "приоритет:" << record.preference();
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
DnsResolver resolver;
resolver.resolveAddresses("example.com");
resolver.resolveMxRecords("example.com");
return app.exec();
}
#include "main.moc"
Пояснения к коду
Каждый запрос создаётся как отдельный объект QDnsLookup с указанным типом записи и именем — конструктор сразу принимает оба параметра для удобства. После вызова lookup() остаётся ждать сигнал finished(), в обработчике которого важно сначала проверить error(), а затем читать результаты соответствующим геттером — hostAddressRecords() для A-записей, mailExchangeRecords() для MX и так далее по аналогии для остальных типов.
Объект lookup создаётся с this как родителем, но всё равно явно удаляется через deleteLater() в обработчике — это страховка против накопления объектов, если резолвер используется многократно в течение жизни приложения.
Подводные камни
- TTL записей не учитывается автоматически. Если приложение кеширует результат резолвинга самостоятельно (а не полагается на ОС), нужно вручную отслеживать
timeToLive()и повторно резолвить адрес по истечении TTL — иначе можно работать с устаревшим IP после смены инфраструктуры на сервере. - DNS-резолвинг может занимать заметное время при недоступном или медленном DNS-сервере — стоит предусмотреть таймаут на уровне приложения (Qt не предоставляет встроенный таймаут для
QDnsLookup, нужно реализовывать черезQTimerвручную). - Несколько A-записей для одного домена — обычная ситуация для балансировки нагрузки; код, рассчитывающий на единственный адрес (
hostAddressRecords().first()), может постоянно использовать только первый сервер в списке и не давать отказоустойчивости. - На некоторых платформах поведение
QDnsLookupзависит от системного резолвера — в редких случаях (например, при специфичных корпоративных DNS-настройках через VPN) результат может отличаться отnslookup/digв терминале, что усложняет диагностику.