9#include <qapplicationstatic.h>
10#include <qcoreapplication.h>
13#include <qloggingcategory.h>
19# include <qsslsocket.h>
26using namespace Qt::StringLiterals;
31struct QDnsLookupThreadPool : QThreadPool
33 QDnsLookupThreadPool()
46 return r1.preference() < r2.preference();
50
51
56 if (records.size() <= 1)
60 std::sort(records.begin(), records.end(), qt_qdnsmailexchangerecord_less_than);
63 while (i < records.size()) {
66 QList<QDnsMailExchangeRecord> slice;
67 const quint16 slicePreference = records.at(i).preference();
68 for (
int j = i; j < records.size(); ++j) {
69 if (records.at(j).preference() != slicePreference)
71 slice << records.at(j);
75 while (!slice.isEmpty()) {
76 const unsigned int pos = QRandomGenerator::global()->bounded(slice.size());
77 records[i++] = slice.takeAt(pos);
86 return r1.priority() < r2.priority()
87 || (r1.priority() == r2.priority()
88 && r1.weight() == 0 && r2.weight() > 0);
92
93
98 if (records.size() <= 1)
103 std::sort(records.begin(), records.end(), qt_qdnsservicerecord_less_than);
106 while (i < records.size()) {
109 QList<QDnsServiceRecord> slice;
110 const quint16 slicePriority = records.at(i).priority();
111 unsigned int sliceWeight = 0;
112 for (
int j = i; j < records.size(); ++j) {
113 if (records.at(j).priority() != slicePriority)
115 sliceWeight += records.at(j).weight();
116 slice << records.at(j);
118#ifdef QDNSLOOKUP_DEBUG
119 qDebug(
"qt_qdnsservicerecord_sort() : priority %i (size: %i, total weight: %i)",
120 slicePriority, slice.size(), sliceWeight);
124 while (!slice.isEmpty()) {
125 const unsigned int weightThreshold = QRandomGenerator::global()->bounded(sliceWeight + 1);
126 unsigned int summedWeight = 0;
127 for (
int j = 0; j < slice.size(); ++j) {
128 summedWeight += slice.at(j).weight();
129 if (summedWeight >= weightThreshold) {
130#ifdef QDNSLOOKUP_DEBUG
131 qDebug(
"qt_qdnsservicerecord_sort() : adding %s %i (weight: %i)",
132 qPrintable(slice.at(j).target()), slice.at(j).port(),
133 slice.at(j).weight());
136 sliceWeight -= slice.at(j).weight();
137 records[i++] = slice.takeAt(j);
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
241
242
243
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
267
268
269
270
271
272
273
274
275
276
277
278
279
280
283
284
285
286
287
288
289bool QDnsLookup::isProtocolSupported(Protocol protocol)
291#if QT_CONFIG(libresolv) || QT_CONFIG(android_dnsresolver) || defined(Q_OS_WIN)
293 case QDnsLookup::Standard:
294# if QT_CONFIG(android_dnsresolver)
295 return qt_androidDnsResolverAvailable();
299 case QDnsLookup::DnsOverTls:
301 if (QSslSocket::supportsSsl())
313
314
315
316
317
318
319quint16 QDnsLookup::defaultPortForProtocol(Protocol protocol)
noexcept
322 case QDnsLookup::Standard:
324 case QDnsLookup::DnsOverTls:
325 return DnsOverTlsPort;
331
332
333
334
337
338
339
340
341
344
345
346
347
348
351
352
353
354
356QDnsLookup::QDnsLookup(QObject *parent)
357 : QObject(*
new QDnsLookupPrivate, parent)
362
363
364
366QDnsLookup::QDnsLookup(Type type,
const QString &name, QObject *parent)
367 : QObject(*
new QDnsLookupPrivate, parent)
375
376
377
378
379
380
381
383QDnsLookup::QDnsLookup(Type type,
const QString &name,
const QHostAddress &nameserver, QObject *parent)
384 : QDnsLookup(type, name, nameserver, 0, parent)
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404QDnsLookup::QDnsLookup(Type type,
const QString &name,
const QHostAddress &nameserver, quint16 port, QObject *parent)
405 : QObject(*
new QDnsLookupPrivate, parent)
411 d->nameserver = nameserver;
415
416
417
418
419
420
421
422
423
424
425
426QDnsLookup::QDnsLookup(Type type,
const QString &name, Protocol protocol,
427 const QHostAddress &nameserver, quint16 port, QObject *parent)
428 : QObject(*
new QDnsLookupPrivate, parent)
433 d->nameserver = nameserver;
435 d->protocol = protocol;
439
440
441
442
443
445QDnsLookup::~QDnsLookup()
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466bool QDnsLookup::isAuthenticData()
const
468 return d_func()->reply.authenticData;
472
473
474
476QDnsLookup::Error QDnsLookup::error()
const
478 return d_func()->reply.error;
482
483
484
486QString QDnsLookup::errorString()
const
488 return d_func()->reply.errorString;
492
493
495bool QDnsLookup::isFinished()
const
497 return d_func()->isFinished;
501
502
503
504
505
506
507
508
509
510
512QString QDnsLookup::name()
const
514 return d_func()->name;
517void QDnsLookup::setName(
const QString &name)
523QBindable<QString> QDnsLookup::bindableName()
530
531
532
534QDnsLookup::Type QDnsLookup::type()
const
536 return d_func()->type;
539void QDnsLookup::setType(Type type)
545QBindable<QDnsLookup::Type> QDnsLookup::bindableType()
552
553
554
556QHostAddress QDnsLookup::nameserver()
const
558 return d_func()->nameserver;
561void QDnsLookup::setNameserver(
const QHostAddress &nameserver)
564 d->nameserver = nameserver;
567QBindable<QHostAddress> QDnsLookup::bindableNameserver()
570 return &d->nameserver;
574
575
576
577
578
579
580
581
582
584quint16 QDnsLookup::nameserverPort()
const
586 return d_func()->port;
589void QDnsLookup::setNameserverPort(quint16 nameserverPort)
592 d->port = nameserverPort;
595QBindable<quint16> QDnsLookup::bindableNameserverPort()
602
603
604
605
606
607
608QDnsLookup::Protocol QDnsLookup::nameserverProtocol()
const
610 return d_func()->protocol;
613void QDnsLookup::setNameserverProtocol(Protocol protocol)
615 d_func()->protocol = protocol;
618QBindable<QDnsLookup::Protocol> QDnsLookup::bindableNameserverProtocol()
620 return &d_func()->protocol;
624
625
626
627
628
629
630
631
632
634void QDnsLookup::setNameserver(Protocol protocol,
const QHostAddress &nameserver, quint16 port)
636 Qt::beginPropertyUpdateGroup();
637 setNameserver(nameserver);
638 setNameserverPort(port);
639 setNameserverProtocol(protocol);
640 Qt::endPropertyUpdateGroup();
644
645
647QList<QDnsDomainNameRecord> QDnsLookup::canonicalNameRecords()
const
649 return d_func()->reply.canonicalNameRecords;
653
654
656QList<QDnsHostAddressRecord> QDnsLookup::hostAddressRecords()
const
658 return d_func()->reply.hostAddressRecords;
662
663
664
665
666
667
669QList<QDnsMailExchangeRecord> QDnsLookup::mailExchangeRecords()
const
671 return d_func()->reply.mailExchangeRecords;
675
676
678QList<QDnsDomainNameRecord> QDnsLookup::nameServerRecords()
const
680 return d_func()->reply.nameServerRecords;
684
685
687QList<QDnsDomainNameRecord> QDnsLookup::pointerRecords()
const
689 return d_func()->reply.pointerRecords;
693
694
695
696
697
698
700QList<QDnsServiceRecord> QDnsLookup::serviceRecords()
const
702 return d_func()->reply.serviceRecords;
706
707
709QList<QDnsTextRecord> QDnsLookup::textRecords()
const
711 return d_func()->reply.textRecords;
715
716
717
718
719
720
721
722
723
724QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords()
const
726 return d_func()->reply.tlsAssociationRecords;
731
732
733
734
735
736void QDnsLookup::setSslConfiguration(
const QSslConfiguration &sslConfiguration)
739 d->sslConfiguration.emplace(sslConfiguration);
743
744
745
746
747QSslConfiguration QDnsLookup::sslConfiguration()
const
749 const Q_D(QDnsLookup);
750 return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
755
756
757
758
760void QDnsLookup::abort()
764 d->runnable =
nullptr;
765 d->reply = QDnsLookupReply();
766 d->reply.error = QDnsLookup::OperationCancelledError;
767 d->reply.errorString = tr(
"Operation cancelled");
768 d->isFinished =
true;
774
775
776
777
779void QDnsLookup::lookup()
782 d->isFinished =
false;
783 d->reply = QDnsLookupReply();
784 if (!QCoreApplication::instanceExists()) {
786 qWarning(
"QDnsLookup requires a QCoreApplication");
790 auto l = [
this](
const QDnsLookupReply &reply) {
792 if (d->runnable == sender()) {
793#ifdef QDNSLOOKUP_DEBUG
794 qDebug(
"DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
797 d->sslConfiguration = std::move(reply.sslConfiguration);
800 d->runnable =
nullptr;
801 d->isFinished =
true;
806 d->runnable =
new QDnsLookupRunnable(d);
807 connect(d->runnable, &QDnsLookupRunnable::finished,
this, l,
808 Qt::BlockingQueuedConnection);
809 theDnsLookupThreadPool->start(d->runnable);
813
814
815
816
817
818
819
820
821
822
823
824
825
828
829
831QDnsDomainNameRecord::QDnsDomainNameRecord()
832 : d(
new QDnsDomainNameRecordPrivate)
837
838
840QDnsDomainNameRecord::QDnsDomainNameRecord(
const QDnsDomainNameRecord &other)
846
847
849QDnsDomainNameRecord::~QDnsDomainNameRecord()
854
855
857QString QDnsDomainNameRecord::name()
const
863
864
866quint32 QDnsDomainNameRecord::timeToLive()
const
868 return d->timeToLive;
872
873
875QString QDnsDomainNameRecord::value()
const
881
882
883
885QDnsDomainNameRecord &QDnsDomainNameRecord::operator=(
const QDnsDomainNameRecord &other)
892
893
894
897
898
899
900
901
902
903
904
905
906
907
908
909
912
913
915QDnsHostAddressRecord::QDnsHostAddressRecord()
916 : d(
new QDnsHostAddressRecordPrivate)
921
922
924QDnsHostAddressRecord::QDnsHostAddressRecord(
const QDnsHostAddressRecord &other)
930
931
933QDnsHostAddressRecord::~QDnsHostAddressRecord()
938
939
941QString QDnsHostAddressRecord::name()
const
947
948
950quint32 QDnsHostAddressRecord::timeToLive()
const
952 return d->timeToLive;
956
957
959QHostAddress QDnsHostAddressRecord::value()
const
965
966
967
969QDnsHostAddressRecord &QDnsHostAddressRecord::operator=(
const QDnsHostAddressRecord &other)
976
977
978
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
998
999
1001QDnsMailExchangeRecord::QDnsMailExchangeRecord()
1002 : d(
new QDnsMailExchangeRecordPrivate)
1007
1008
1010QDnsMailExchangeRecord::QDnsMailExchangeRecord(
const QDnsMailExchangeRecord &other)
1016
1017
1019QDnsMailExchangeRecord::~QDnsMailExchangeRecord()
1024
1025
1027QString QDnsMailExchangeRecord::exchange()
const
1033
1034
1036QString QDnsMailExchangeRecord::name()
const
1042
1043
1045quint16 QDnsMailExchangeRecord::preference()
const
1047 return d->preference;
1051
1052
1054quint32 QDnsMailExchangeRecord::timeToLive()
const
1056 return d->timeToLive;
1060
1061
1062
1064QDnsMailExchangeRecord &QDnsMailExchangeRecord::operator=(
const QDnsMailExchangeRecord &other)
1070
1071
1072
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1092
1093
1095QDnsServiceRecord::QDnsServiceRecord()
1096 : d(
new QDnsServiceRecordPrivate)
1101
1102
1104QDnsServiceRecord::QDnsServiceRecord(
const QDnsServiceRecord &other)
1110
1111
1113QDnsServiceRecord::~QDnsServiceRecord()
1118
1119
1121QString QDnsServiceRecord::name()
const
1127
1128
1130quint16 QDnsServiceRecord::port()
const
1136
1137
1138
1139
1140
1142quint16 QDnsServiceRecord::priority()
const
1148
1149
1151QString QDnsServiceRecord::target()
const
1157
1158
1160quint32 QDnsServiceRecord::timeToLive()
const
1162 return d->timeToLive;
1166
1167
1168
1169
1170
1171
1173quint16 QDnsServiceRecord::weight()
const
1179
1180
1181
1183QDnsServiceRecord &QDnsServiceRecord::operator=(
const QDnsServiceRecord &other)
1189
1190
1191
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1211
1212
1214QDnsTextRecord::QDnsTextRecord()
1215 : d(
new QDnsTextRecordPrivate)
1220
1221
1223QDnsTextRecord::QDnsTextRecord(
const QDnsTextRecord &other)
1229
1230
1232QDnsTextRecord::~QDnsTextRecord()
1237
1238
1240QString QDnsTextRecord::name()
const
1246
1247
1249quint32 QDnsTextRecord::timeToLive()
const
1251 return d->timeToLive;
1255
1256
1258QList<QByteArray> QDnsTextRecord::values()
const
1264
1265
1266
1268QDnsTextRecord &QDnsTextRecord::operator=(
const QDnsTextRecord &other)
1274
1275
1276
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1295QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1419
1420
1421QDnsTlsAssociationRecord::QDnsTlsAssociationRecord()
1422 : d(
new QDnsTlsAssociationRecordPrivate)
1427
1428
1432
1433
1438
1439
1443
1444
1451
1452
1455 return d->timeToLive;
1459
1460
1467
1468
1475
1476
1479 return d->matchType;
1483
1484
1485
1486
1487
1488
1489
1497 QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
1498 if (label.isEmpty())
1499 return QDnsLookupRunnable::EncodedLabel(1, rootDomain);
1501 QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
1503 return encodedLabel;
1505 return std::move(encodedLabel).toLatin1();
1517 port = QDnsLookup::defaultPortForProtocol(protocol);
1519 sslConfiguration = d->sslConfiguration;
1528 if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
1529 reply.error = QDnsLookup::InvalidRequestError;
1530 reply.errorString = QDnsLookup::tr(
"Invalid domain name");
1536 qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
1537 qt_qdnsservicerecord_sort(reply.serviceRecords);
1540 emit finished(reply);
1543 switch (reply.error) {
1544 case QDnsLookup::NoError:
1545 case QDnsLookup::OperationCancelledError:
1546 case QDnsLookup::NotFoundError:
1547 case QDnsLookup::ServerFailureError:
1548 case QDnsLookup::ServerRefusedError:
1549 case QDnsLookup::TimeoutError:
1552 case QDnsLookup::ResolverError:
1553 case QDnsLookup::InvalidRequestError:
1554 case QDnsLookup::InvalidReplyError:
1556 <<
"DNS lookup failed (" << reply.error <<
"): "
1557 << qUtf16Printable(reply.errorString)
1558 <<
"; request was " <<
this;
1565 d << r->requestName.left(MaxDomainNameLength);
1566 if (r->requestName.size() > MaxDomainNameLength)
1567 d <<
"... (truncated)";
1568 d <<
" type " << r->requestType;
1569 if (!r->nameserver.isNull()) {
1570 d <<
" to nameserver " << qUtf16Printable(r->nameserver.toString())
1571 <<
" port " << (r->port ? r->port : QDnsLookup::defaultPortForProtocol(r->protocol));
1572 switch (r->protocol) {
1573 case QDnsLookup::Standard:
1575 case QDnsLookup::DnsOverTls:
1583static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
1584static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
1585static constexpr quint8 DnsAuthenticDataBit = 0x20;
1587static int makeReplyErrorFromSocket(QDnsLookupReply *reply,
const QAbstractSocket *socket)
1589 QDnsLookup::Error error = [&] {
1590 switch (socket->error()) {
1591 case QAbstractSocket::SocketTimeoutError:
1592 case QAbstractSocket::ProxyConnectionTimeoutError:
1593 return QDnsLookup::TimeoutError;
1595 return QDnsLookup::ResolverError;
1598 reply->setError(error, socket->errorString());
1602bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<
unsigned char> query,
1603 ReplyBuffer &response)
1606 socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
1608# if QT_CONFIG(networkproxy)
1609 socket.setProtocolTag(
"domain-s"_L1);
1613 query[3] |= DnsAuthenticDataBit;
1616 quint16 size = qToBigEndian<quint16>(query.size());
1617 QDeadlineTimer timeout(DnsOverTlsTimeout);
1619 socket.connectToHostEncrypted(nameserver.toString(), port);
1620 socket.write(
reinterpret_cast<
const char *>(&size),
sizeof(size));
1621 socket.write(
reinterpret_cast<
const char *>(query.data()), query.size());
1622 if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
1625 reply->sslConfiguration = socket.sslConfiguration();
1628 auto waitForBytes = [&](
void *buffer,
int count) {
1629 int remaining = timeout.remainingTime();
1630 while (remaining >= 0 && socket.bytesAvailable() < count) {
1631 if (!socket.waitForReadyRead(remaining))
1634 return socket.read(
static_cast<
char *>(buffer), count) == count;
1636 if (!waitForBytes(&size,
sizeof(size)))
1642 size = qFromBigEndian(size);
1643 response.resize(size);
1644 if (waitForBytes(response.data(), size)) {
1647 reply->authenticData = response[3] & DnsAuthenticDataBit;
1653 return makeReplyErrorFromSocket(reply, &socket);
1657 ReplyBuffer &response)
1661 reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr(
"SSL/TLS support not present"));
1668#include "moc_qdnslookup.cpp"
1669#include "moc_qdnslookup_p.cpp"
QDnsLookupRunnable(const QDnsLookupPrivate *d)
void run() override
Implement this pure virtual function in your subclass.
bool sendDnsOverTls(QDnsLookupReply *reply, QSpan< unsigned char > query, ReplyBuffer &response)
The QDnsTlsAssociationRecord class stores information about a DNS TLSA record.
Q_NETWORK_EXPORT ~QDnsTlsAssociationRecord()
Destroys this TLS Association record object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Combined button and popup list for selecting options.
#define Q_APPLICATION_STATIC(TYPE, NAME,...)
static void qt_qdnsmailexchangerecord_sort(QList< QDnsMailExchangeRecord > &records)
static void qt_qdnsservicerecord_sort(QList< QDnsServiceRecord > &records)
static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
static bool qt_qdnsservicerecord_less_than(const QDnsServiceRecord &r1, const QDnsServiceRecord &r2)
static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)