QDnsLookup: implement DNS-over-TLS
For the libresolv (Unix) implementation, we already had the packet prepared by res_nmkquery(). This commit moves the res_nsend() to a separate function so QDnsLookupRunnable::query() can be more concise. On the Windows side, this commit creates a separate function for the DoT case, because we now need to use two other functions from WinDNS so we can create a query and parse the reply. The rest is just QSslSocket. Change-Id: I455fe22ef4ad4b2f9b01fffd17c805a3cb0466eb Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
9724b039ca
commit
f2f00b2a46
@ -8,14 +8,22 @@
|
|||||||
#include <qapplicationstatic.h>
|
#include <qapplicationstatic.h>
|
||||||
#include <qcoreapplication.h>
|
#include <qcoreapplication.h>
|
||||||
#include <qdatetime.h>
|
#include <qdatetime.h>
|
||||||
|
#include <qendian.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qrandom.h>
|
#include <qrandom.h>
|
||||||
|
#include <qspan.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
# include <qsslsocket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
|
static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -261,6 +269,10 @@ bool QDnsLookup::isProtocolSupported(Protocol protocol)
|
|||||||
case QDnsLookup::Standard:
|
case QDnsLookup::Standard:
|
||||||
return true;
|
return true;
|
||||||
case QDnsLookup::DnsOverTls:
|
case QDnsLookup::DnsOverTls:
|
||||||
|
# if QT_CONFIG(ssl)
|
||||||
|
if (QSslSocket::supportsSsl())
|
||||||
|
return true;
|
||||||
|
# endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -658,7 +670,8 @@ QList<QDnsTextRecord> QDnsLookup::textRecords() const
|
|||||||
*/
|
*/
|
||||||
void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
|
void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
|
||||||
{
|
{
|
||||||
Q_UNUSED(sslConfiguration)
|
Q_D(QDnsLookup);
|
||||||
|
d->sslConfiguration.emplace(sslConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -668,7 +681,8 @@ void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
|
|||||||
*/
|
*/
|
||||||
QSslConfiguration QDnsLookup::sslConfiguration() const
|
QSslConfiguration QDnsLookup::sslConfiguration() const
|
||||||
{
|
{
|
||||||
return {};
|
const Q_D(QDnsLookup);
|
||||||
|
return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1291,6 +1305,82 @@ inline QDebug operator<<(QDebug &d, QDnsLookupRunnable *r)
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
|
||||||
|
static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
|
||||||
|
|
||||||
|
static int makeReplyErrorFromSocket(QDnsLookupReply *reply, const QAbstractSocket *socket)
|
||||||
|
{
|
||||||
|
QDnsLookup::Error error = [&] {
|
||||||
|
switch (socket->error()) {
|
||||||
|
case QAbstractSocket::SocketTimeoutError:
|
||||||
|
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||||
|
return QDnsLookup::TimeoutError;
|
||||||
|
default:
|
||||||
|
return QDnsLookup::ResolverError;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
reply->setError(error, socket->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
|
||||||
|
ReplyBuffer &response)
|
||||||
|
{
|
||||||
|
QSslSocket socket;
|
||||||
|
socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
|
||||||
|
|
||||||
|
# if QT_CONFIG(networkproxy)
|
||||||
|
socket.setProtocolTag("domain-s"_L1);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
do {
|
||||||
|
quint16 size = qToBigEndian<quint16>(query.size());
|
||||||
|
QDeadlineTimer timeout(DnsOverTlsTimeout);
|
||||||
|
|
||||||
|
socket.connectToHostEncrypted(nameserver.toString(), port);
|
||||||
|
socket.write(reinterpret_cast<const char *>(&size), sizeof(size));
|
||||||
|
socket.write(reinterpret_cast<const char *>(query.data()), query.size());
|
||||||
|
if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
reply->sslConfiguration = socket.sslConfiguration();
|
||||||
|
|
||||||
|
// accumulate reply
|
||||||
|
auto waitForBytes = [&](void *buffer, int count) {
|
||||||
|
int remaining = timeout.remainingTime();
|
||||||
|
while (remaining >= 0 && socket.bytesAvailable() < count) {
|
||||||
|
if (!socket.waitForReadyRead(remaining))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return socket.read(static_cast<char *>(buffer), count) == count;
|
||||||
|
};
|
||||||
|
if (!waitForBytes(&size, sizeof(size)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// note: strictly speaking, we're allocating memory based on untrusted data
|
||||||
|
// but in practice, due to limited range of the data type (16 bits),
|
||||||
|
// the maximum allocation is small.
|
||||||
|
size = qFromBigEndian(size);
|
||||||
|
response.resize(size);
|
||||||
|
if (waitForBytes(response.data(), size))
|
||||||
|
return true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
// handle errors
|
||||||
|
return makeReplyErrorFromSocket(reply, &socket);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
|
||||||
|
ReplyBuffer &response)
|
||||||
|
{
|
||||||
|
Q_UNUSED(query)
|
||||||
|
Q_UNUSED(response)
|
||||||
|
reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("SSL/TLS support not present"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#include "moc_qdnslookup.cpp"
|
#include "moc_qdnslookup.cpp"
|
||||||
|
@ -201,9 +201,13 @@ public:
|
|||||||
#else
|
#else
|
||||||
using EncodedLabel = QByteArray;
|
using EncodedLabel = QByteArray;
|
||||||
#endif
|
#endif
|
||||||
|
// minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
|
||||||
|
static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
|
||||||
|
using ReplyBuffer = QVarLengthArray<unsigned char, ReplyBufferSize>;
|
||||||
|
|
||||||
QDnsLookupRunnable(const QDnsLookupPrivate *d);
|
QDnsLookupRunnable(const QDnsLookupPrivate *d);
|
||||||
void run() override;
|
void run() override;
|
||||||
|
bool sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query, ReplyBuffer &response);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(const QDnsLookupReply &reply);
|
void finished(const QDnsLookupReply &reply);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <qendian.h>
|
#include <qendian.h>
|
||||||
#include <qscopedpointer.h>
|
#include <qscopedpointer.h>
|
||||||
|
#include <qspan.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
#include <qvarlengtharray.h>
|
#include <qvarlengtharray.h>
|
||||||
#include <private/qnativesocketengine_p.h> // for setSockAddr
|
#include <private/qnativesocketengine_p.h> // for setSockAddr
|
||||||
@ -32,15 +33,13 @@ QT_REQUIRE_CONFIG(libresolv);
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
using ReplyBuffer = QDnsLookupRunnable::ReplyBuffer;
|
||||||
// minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
|
|
||||||
static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
|
|
||||||
|
|
||||||
// https://www.rfc-editor.org/rfc/rfc6891
|
// https://www.rfc-editor.org/rfc/rfc6891
|
||||||
static constexpr unsigned char Edns0Record[] = {
|
static constexpr unsigned char Edns0Record[] = {
|
||||||
0x00, // root label
|
0x00, // root label
|
||||||
T_OPT >> 8, T_OPT & 0xff, // type OPT
|
T_OPT >> 8, T_OPT & 0xff, // type OPT
|
||||||
ReplyBufferSize >> 8, ReplyBufferSize & 0xff, // payload size
|
ReplyBuffer::PreallocatedSize >> 8, ReplyBuffer::PreallocatedSize & 0xff, // payload size
|
||||||
NOERROR, // extended rcode
|
NOERROR, // extended rcode
|
||||||
0, // version
|
0, // version
|
||||||
0x00, 0x00, // flags
|
0x00, 0x00, // flags
|
||||||
@ -153,40 +152,19 @@ prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_r
|
|||||||
return queryLength + sizeof(Edns0Record);
|
return queryLength + sizeof(Edns0Record);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
static int sendStandardDns(QDnsLookupReply *reply, res_state state, QSpan<unsigned char> qbuffer,
|
||||||
|
ReplyBuffer &buffer, const QHostAddress &nameserver, quint16 port)
|
||||||
{
|
{
|
||||||
if (protocol != QDnsLookup::Standard)
|
|
||||||
return reply->setError(QDnsLookup::ResolverError,
|
|
||||||
QDnsLookup::tr("DNS over TLS not implemented"));
|
|
||||||
|
|
||||||
// Initialize state.
|
|
||||||
std::remove_pointer_t<res_state> state = {};
|
|
||||||
if (res_ninit(&state) < 0) {
|
|
||||||
int error = errno;
|
|
||||||
qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
|
|
||||||
return reply->makeResolverSystemError(error);
|
|
||||||
}
|
|
||||||
auto guard = qScopeGuard([&] { res_nclose(&state); });
|
|
||||||
|
|
||||||
//Check if a nameserver was set. If so, use it
|
//Check if a nameserver was set. If so, use it
|
||||||
if (!applyNameServer(&state, nameserver, port))
|
if (!applyNameServer(state, nameserver, port)) {
|
||||||
return reply->setError(QDnsLookup::ResolverError,
|
reply->setError(QDnsLookup::ResolverError,
|
||||||
QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
|
QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
|
||||||
#ifdef QDNSLOOKUP_DEBUG
|
return -1;
|
||||||
state.options |= RES_DEBUG;
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Prepare the DNS query.
|
|
||||||
QueryBuffer qbuffer;
|
|
||||||
int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
|
|
||||||
if (Q_UNLIKELY(queryLength < 0))
|
|
||||||
return reply->makeResolverSystemError();
|
|
||||||
|
|
||||||
// Perform DNS query.
|
|
||||||
QVarLengthArray<unsigned char, ReplyBufferSize> buffer(ReplyBufferSize);
|
|
||||||
auto attemptToSend = [&]() {
|
auto attemptToSend = [&]() {
|
||||||
std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
|
std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
|
||||||
int responseLength = res_nsend(&state, qbuffer.data(), queryLength, buffer.data(), buffer.size());
|
int responseLength = res_nsend(state, qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
|
||||||
if (responseLength >= 0)
|
if (responseLength >= 0)
|
||||||
return responseLength; // success
|
return responseLength; // success
|
||||||
|
|
||||||
@ -206,10 +184,10 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// strictly use UDP, we'll deal with truncated replies ourselves
|
// strictly use UDP, we'll deal with truncated replies ourselves
|
||||||
state.options |= RES_IGNTC;
|
state->options |= RES_IGNTC;
|
||||||
int responseLength = attemptToSend();
|
int responseLength = attemptToSend();
|
||||||
if (responseLength < 0)
|
if (responseLength < 0)
|
||||||
return;
|
return responseLength;
|
||||||
|
|
||||||
// check if we need to use the virtual circuit (TCP)
|
// check if we need to use the virtual circuit (TCP)
|
||||||
auto header = reinterpret_cast<HEADER *>(buffer.data());
|
auto header = reinterpret_cast<HEADER *>(buffer.data());
|
||||||
@ -220,17 +198,56 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
|||||||
|
|
||||||
// remove the EDNS record in the query
|
// remove the EDNS record in the query
|
||||||
reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
|
reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
|
||||||
queryLength -= sizeof(Edns0Record);
|
qbuffer = qbuffer.first(qbuffer.size() - sizeof(Edns0Record));
|
||||||
|
|
||||||
// send using the virtual circuit
|
// send using the virtual circuit
|
||||||
state.options |= RES_USEVC;
|
state->options |= RES_USEVC;
|
||||||
responseLength = attemptToSend();
|
responseLength = attemptToSend();
|
||||||
if (Q_UNLIKELY(responseLength > buffer.size())) {
|
if (Q_UNLIKELY(responseLength > buffer.size())) {
|
||||||
// Ok, we give up.
|
// Ok, we give up.
|
||||||
return reply->setError(QDnsLookup::ResolverError,
|
reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
|
||||||
QDnsLookup::tr("Reply was too large"));
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return responseLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
||||||
|
{
|
||||||
|
// Initialize state.
|
||||||
|
std::remove_pointer_t<res_state> state = {};
|
||||||
|
if (res_ninit(&state) < 0) {
|
||||||
|
int error = errno;
|
||||||
|
qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
|
||||||
|
return reply->makeResolverSystemError(error);
|
||||||
|
}
|
||||||
|
auto guard = qScopeGuard([&] { res_nclose(&state); });
|
||||||
|
|
||||||
|
#ifdef QDNSLOOKUP_DEBUG
|
||||||
|
state.options |= RES_DEBUG;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Prepare the DNS query.
|
||||||
|
QueryBuffer qbuffer;
|
||||||
|
int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
|
||||||
|
if (Q_UNLIKELY(queryLength < 0))
|
||||||
|
return reply->makeResolverSystemError();
|
||||||
|
|
||||||
|
// Perform DNS query.
|
||||||
|
ReplyBuffer buffer(ReplyBufferSize);
|
||||||
|
int responseLength = -1;
|
||||||
|
switch (protocol) {
|
||||||
|
case QDnsLookup::Standard:
|
||||||
|
responseLength = sendStandardDns(reply, &state, qbuffer, buffer, nameserver, port);
|
||||||
|
break;
|
||||||
|
case QDnsLookup::DnsOverTls:
|
||||||
|
if (!sendDnsOverTls(reply, qbuffer, buffer))
|
||||||
|
return;
|
||||||
|
responseLength = buffer.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (responseLength < 0)
|
if (responseLength < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -239,6 +256,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
|||||||
return reply->makeInvalidReplyError();
|
return reply->makeInvalidReplyError();
|
||||||
|
|
||||||
// Parse the reply.
|
// Parse the reply.
|
||||||
|
auto header = reinterpret_cast<HEADER *>(buffer.data());
|
||||||
if (header->rcode)
|
if (header->rcode)
|
||||||
return reply->makeDnsRcodeError(header->rcode);
|
return reply->makeDnsRcodeError(header->rcode);
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include "qdnslookup_p.h"
|
#include "qdnslookup_p.h"
|
||||||
|
|
||||||
#include <qurl.h>
|
#include <qendian.h>
|
||||||
#include <private/qnativesocketengine_p.h>
|
#include <private/qnativesocketengine_p.h>
|
||||||
#include <private/qsystemerror_p.h>
|
#include <private/qsystemerror_p.h>
|
||||||
|
#include <qurl.h>
|
||||||
|
#include <qspan.h>
|
||||||
|
|
||||||
#include <qt_windows.h>
|
#include <qt_windows.h>
|
||||||
#include <windns.h>
|
#include <windns.h>
|
||||||
@ -63,6 +65,58 @@ DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply,
|
||||||
|
PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
|
||||||
|
{
|
||||||
|
// WinDNS wants MTU - IP Header - UDP header for some reason, in spite
|
||||||
|
// of never needing that much
|
||||||
|
QVarLengthArray<unsigned char, 1472> query(1472);
|
||||||
|
|
||||||
|
auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
|
||||||
|
DWORD dnsBufferSize = query.size();
|
||||||
|
WORD xid = 0;
|
||||||
|
bool recursionDesired = true;
|
||||||
|
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
|
||||||
|
// MinGW winheaders incorrectly declare the third parameter as LPWSTR
|
||||||
|
if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
|
||||||
|
const_cast<LPWSTR>(request->QueryName), request->QueryType,
|
||||||
|
xid, recursionDesired)) {
|
||||||
|
// let's try reallocating
|
||||||
|
query.resize(dnsBufferSize);
|
||||||
|
if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
|
||||||
|
const_cast<LPWSTR>(request->QueryName), request->QueryType,
|
||||||
|
xid, recursionDesired)) {
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set AD bit: we want to trust this server
|
||||||
|
dnsBuffer->MessageHead.AuthenticatedData = true;
|
||||||
|
|
||||||
|
QDnsLookupRunnable::ReplyBuffer replyBuffer;
|
||||||
|
if (!self->sendDnsOverTls(reply, { query.data(), dnsBufferSize }, replyBuffer))
|
||||||
|
return DNS_STATUS(-1); // error set in reply
|
||||||
|
|
||||||
|
// interpret the RCODE in the reply
|
||||||
|
auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
|
||||||
|
DNS_HEADER *header = &response->MessageHead;
|
||||||
|
if (!header->IsResponse)
|
||||||
|
return DNS_ERROR_BAD_PACKET; // not a reply
|
||||||
|
|
||||||
|
// Convert the byte order for the 16-bit quantities in the header, so
|
||||||
|
// DnsExtractRecordsFromMessage can parse the contents.
|
||||||
|
//header->Xid = qFromBigEndian(header->Xid);
|
||||||
|
header->QuestionCount = qFromBigEndian(header->QuestionCount);
|
||||||
|
header->AnswerCount = qFromBigEndian(header->AnswerCount);
|
||||||
|
header->NameServerCount = qFromBigEndian(header->NameServerCount);
|
||||||
|
header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
|
||||||
|
|
||||||
|
results->QueryOptions = request->QueryOptions;
|
||||||
|
return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
|
||||||
|
}
|
||||||
|
|
||||||
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
||||||
{
|
{
|
||||||
// Perform DNS query.
|
// Perform DNS query.
|
||||||
@ -93,10 +147,12 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
|||||||
status = DnsQueryEx(&request, &results, nullptr);
|
status = DnsQueryEx(&request, &results, nullptr);
|
||||||
break;
|
break;
|
||||||
case QDnsLookup::DnsOverTls:
|
case QDnsLookup::DnsOverTls:
|
||||||
return reply->setError(QDnsLookup::ResolverError,
|
status = sendAlternate(this, reply, &request, &results);
|
||||||
QDnsLookup::tr("DNS over TLS not implemented"));
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status == DNS_STATUS(-1))
|
||||||
|
return; // error already set in reply
|
||||||
if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
|
if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
|
||||||
return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
|
return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
|
||||||
else if (status == ERROR_TIMEOUT)
|
else if (status == ERROR_TIMEOUT)
|
||||||
|
@ -13,6 +13,13 @@
|
|||||||
#include <QtNetwork/QNetworkDatagram>
|
#include <QtNetwork/QNetworkDatagram>
|
||||||
#include <QtNetwork/QUdpSocket>
|
#include <QtNetwork/QUdpSocket>
|
||||||
|
|
||||||
|
#if QT_CONFIG(networkproxy)
|
||||||
|
# include <QtNetwork/QNetworkProxyFactory>
|
||||||
|
#endif
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
# include <QtNetwork/QSslSocket>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
# include <QtCore/QFile>
|
# include <QtCore/QFile>
|
||||||
#else
|
#else
|
||||||
@ -135,9 +142,57 @@ static QList<QHostAddress> globalPublicNameservers(QDnsLookup::Protocol proto)
|
|||||||
//"9.9.9.9", "2620:fe::9",
|
//"9.9.9.9", "2620:fe::9",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto udpSendAndReceive = [](const QHostAddress &addr, QByteArray &data) {
|
||||||
|
QUdpSocket socket;
|
||||||
|
socket.connectToHost(addr, 53);
|
||||||
|
if (socket.waitForConnected(1))
|
||||||
|
socket.write(data);
|
||||||
|
|
||||||
|
if (!socket.waitForReadyRead(1000))
|
||||||
|
return socket.errorString();
|
||||||
|
|
||||||
|
QNetworkDatagram dgram = socket.receiveDatagram();
|
||||||
|
if (!dgram.isValid())
|
||||||
|
return socket.errorString();
|
||||||
|
|
||||||
|
data = dgram.data();
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto tlsSendAndReceive = [](const QHostAddress &addr, QByteArray &data) {
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
QSslSocket socket;
|
||||||
|
QDeadlineTimer timeout(2000);
|
||||||
|
socket.connectToHostEncrypted(addr.toString(), 853);
|
||||||
|
if (!socket.waitForEncrypted(2000))
|
||||||
|
return socket.errorString();
|
||||||
|
|
||||||
|
quint16 size = qToBigEndian<quint16>(data.size());
|
||||||
|
socket.write(reinterpret_cast<char *>(&size), sizeof(size));
|
||||||
|
socket.write(data);
|
||||||
|
|
||||||
|
if (!socket.waitForReadyRead(timeout.remainingTime()))
|
||||||
|
return socket.errorString();
|
||||||
|
if (socket.bytesAvailable() < 2)
|
||||||
|
return u"protocol error"_s;
|
||||||
|
|
||||||
|
socket.read(reinterpret_cast<char *>(&size), sizeof(size));
|
||||||
|
size = qFromBigEndian(size);
|
||||||
|
|
||||||
|
while (socket.bytesAvailable() < size) {
|
||||||
|
int remaining = timeout.remainingTime();
|
||||||
|
if (remaining < 0 || !socket.waitForReadyRead(remaining))
|
||||||
|
return socket.errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
data = socket.readAll();
|
||||||
|
return QString();
|
||||||
|
#else
|
||||||
|
return u"SSL/TLS support not compiled in"_s;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
QList<QHostAddress> result;
|
QList<QHostAddress> result;
|
||||||
if (proto != QDnsLookup::Standard)
|
|
||||||
return result;
|
|
||||||
QRandomGenerator &rng = *QRandomGenerator::system();
|
QRandomGenerator &rng = *QRandomGenerator::system();
|
||||||
for (auto name : candidates) {
|
for (auto name : candidates) {
|
||||||
// check the candidates for reachability
|
// check the candidates for reachability
|
||||||
@ -147,23 +202,18 @@ static QList<QHostAddress> globalPublicNameservers(QDnsLookup::Protocol proto)
|
|||||||
char *ptr = data.data();
|
char *ptr = data.data();
|
||||||
qToBigEndian(id, ptr);
|
qToBigEndian(id, ptr);
|
||||||
|
|
||||||
QUdpSocket socket;
|
QString errorString = [&] {
|
||||||
socket.connectToHost(addr, 53);
|
switch (proto) {
|
||||||
if (socket.waitForConnected(1))
|
case QDnsLookup::Standard: return udpSendAndReceive(addr, data);
|
||||||
socket.write(data);
|
case QDnsLookup::DnsOverTls: return tlsSendAndReceive(addr, data);
|
||||||
|
}
|
||||||
if (!socket.waitForReadyRead(1000)) {
|
Q_UNREACHABLE();
|
||||||
qDebug() << addr << "discarded:" << socket.errorString();
|
}();
|
||||||
|
if (!errorString.isEmpty()) {
|
||||||
|
qDebug() << addr << "discarded:" << errorString;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkDatagram dgram = socket.receiveDatagram();
|
|
||||||
if (!dgram.isValid()) {
|
|
||||||
qDebug() << addr << "discarded:" << socket.errorString();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = dgram.data();
|
|
||||||
ptr = data.data();
|
ptr = data.data();
|
||||||
if (data.size() < HeaderSize) {
|
if (data.size() < HeaderSize) {
|
||||||
qDebug() << addr << "discarded: reply too small";
|
qDebug() << addr << "discarded: reply too small";
|
||||||
@ -190,6 +240,11 @@ void tst_QDnsLookup::initTestCase()
|
|||||||
{
|
{
|
||||||
if (qgetenv("QTEST_ENVIRONMENT") == "ci")
|
if (qgetenv("QTEST_ENVIRONMENT") == "ci")
|
||||||
dnsServersMustWork = true;
|
dnsServersMustWork = true;
|
||||||
|
|
||||||
|
#if QT_CONFIG(networkproxy)
|
||||||
|
// for DNS-over-TLS
|
||||||
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString tst_QDnsLookup::domainName(const QString &input)
|
QString tst_QDnsLookup::domainName(const QString &input)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user