// Copyright (C) 2012 Jeremy Lainé // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include #include "qdnslookup_p.h" #include #include #include #include #include #include #ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH // MinGW headers are missing almost all of this typedef struct Qt_DnsAddr { CHAR MaxSa[32]; DWORD DnsAddrUserDword[8]; } DNS_ADDR, *PDNS_ADDR; typedef struct Qt_DnsAddrArray { DWORD MaxCount; DWORD AddrCount; DWORD Tag; WORD Family; WORD WordReserved; DWORD Flags; DWORD MatchFlag; DWORD Reserved1; DWORD Reserved2; DNS_ADDR AddrArray[]; } DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY; typedef struct Qt_DNS_QUERY_REQUEST { ULONG Version; PCWSTR QueryName; WORD QueryType; ULONG64 QueryOptions; PDNS_ADDR_ARRAY pDnsServerList; ULONG InterfaceIndex; PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback; PVOID pQueryContext; } DNS_QUERY_REQUEST, *PDNS_QUERY_REQUEST; typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it extern "C" { DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest, PDNS_QUERY_RESULT pQueryResults, PDNS_QUERY_CANCEL pCancelHandle); } #endif QT_BEGIN_NAMESPACE void QDnsLookupRunnable::query(QDnsLookupReply *reply) { // Perform DNS query. const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size()); alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)]; DNS_QUERY_REQUEST request = {}; request.Version = 1; request.QueryName = reinterpret_cast(requestNameUtf16.constData()); request.QueryType = requestType; request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN; if (!nameserver.isNull()) { memset(dnsAddresses, 0, sizeof(dnsAddresses)); request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY; auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1]; auto sa = new (addr[0].MaxSa) sockaddr; request.pDnsServerList->MaxCount = sizeof(dnsAddresses); request.pDnsServerList->AddrCount = 1; // ### setting port 53 seems to cause some systems to fail setSockaddr(sa, nameserver, 0); request.pDnsServerList->Family = sa->sa_family; } DNS_QUERY_RESULT results = {}; results.Version = 1; const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr); switch (status) { case ERROR_SUCCESS: break; case DNS_ERROR_RCODE_FORMAT_ERROR: reply->error = QDnsLookup::InvalidRequestError; reply->errorString = tr("Server could not process query"); return; case DNS_ERROR_RCODE_SERVER_FAILURE: case DNS_ERROR_RCODE_NOT_IMPLEMENTED: reply->error = QDnsLookup::ServerFailureError; reply->errorString = tr("Server failure"); return; case DNS_ERROR_RCODE_NAME_ERROR: reply->error = QDnsLookup::NotFoundError; reply->errorString = tr("Non existent domain"); return; case DNS_ERROR_RCODE_REFUSED: reply->error = QDnsLookup::ServerRefusedError; reply->errorString = tr("Server refused to answer"); return; default: reply->error = QDnsLookup::InvalidReplyError; reply->errorString = QSystemError(status, QSystemError::NativeError).toString(); return; } // Extract results. for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) { const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() ); if (ptr->wType == QDnsLookup::A) { QDnsHostAddressRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; record.d->value = QHostAddress(ntohl(ptr->Data.A.IpAddress)); reply->hostAddressRecords.append(record); } else if (ptr->wType == QDnsLookup::AAAA) { Q_IPV6ADDR addr; memcpy(&addr, &ptr->Data.AAAA.Ip6Address, sizeof(Q_IPV6ADDR)); QDnsHostAddressRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; record.d->value = QHostAddress(addr); reply->hostAddressRecords.append(record); } else if (ptr->wType == QDnsLookup::CNAME) { QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1()); reply->canonicalNameRecords.append(record); } else if (ptr->wType == QDnsLookup::MX) { QDnsMailExchangeRecord record; record.d->name = name; record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1()); record.d->preference = ptr->Data.Mx.wPreference; record.d->timeToLive = ptr->dwTtl; reply->mailExchangeRecords.append(record); } else if (ptr->wType == QDnsLookup::NS) { QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1()); reply->nameServerRecords.append(record); } else if (ptr->wType == QDnsLookup::PTR) { QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1()); reply->pointerRecords.append(record); } else if (ptr->wType == QDnsLookup::SRV) { QDnsServiceRecord record; record.d->name = name; record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1()); record.d->port = ptr->Data.Srv.wPort; record.d->priority = ptr->Data.Srv.wPriority; record.d->timeToLive = ptr->dwTtl; record.d->weight = ptr->Data.Srv.wWeight; reply->serviceRecords.append(record); } else if (ptr->wType == QDnsLookup::TXT) { QDnsTextRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) { record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();; } reply->textRecords.append(record); } } DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList); } QT_END_NAMESPACE