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)
292 || QT_CONFIG(harmony_dnsresolver) || defined(Q_OS_WIN)
294 case QDnsLookup::Standard:
295# if QT_CONFIG(android_dnsresolver)
296 return qt_androidDnsResolverAvailable();
300 case QDnsLookup::DnsOverTls:
302 if (QSslSocket::supportsSsl())
314
315
316
317
318
319
320quint16 QDnsLookup::defaultPortForProtocol(Protocol protocol)
noexcept
323 case QDnsLookup::Standard:
325 case QDnsLookup::DnsOverTls:
326 return DnsOverTlsPort;
332
333
334
335
338
339
340
341
342
345
346
347
348
349
352
353
354
355
357QDnsLookup::QDnsLookup(QObject *parent)
358 : QObject(*
new QDnsLookupPrivate, parent)
363
364
365
367QDnsLookup::QDnsLookup(Type type,
const QString &name, QObject *parent)
368 : QObject(*
new QDnsLookupPrivate, parent)
376
377
378
379
380
381
382
384QDnsLookup::QDnsLookup(Type type,
const QString &name,
const QHostAddress &nameserver, QObject *parent)
385 : QDnsLookup(type, name, nameserver, 0, parent)
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405QDnsLookup::QDnsLookup(Type type,
const QString &name,
const QHostAddress &nameserver, quint16 port, QObject *parent)
406 : QObject(*
new QDnsLookupPrivate, parent)
412 d->nameserver = nameserver;
416
417
418
419
420
421
422
423
424
425
426
427QDnsLookup::QDnsLookup(Type type,
const QString &name, Protocol protocol,
428 const QHostAddress &nameserver, quint16 port, QObject *parent)
429 : QObject(*
new QDnsLookupPrivate, parent)
434 d->nameserver = nameserver;
436 d->protocol = protocol;
440
441
442
443
444
446QDnsLookup::~QDnsLookup()
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467bool QDnsLookup::isAuthenticData()
const
469 return d_func()->reply.authenticData;
473
474
475
477QDnsLookup::Error QDnsLookup::error()
const
479 return d_func()->reply.error;
483
484
485
487QString QDnsLookup::errorString()
const
489 return d_func()->reply.errorString;
493
494
496bool QDnsLookup::isFinished()
const
498 return d_func()->isFinished;
502
503
504
505
506
507
508
509
510
511
513QString QDnsLookup::name()
const
515 return d_func()->name;
518void QDnsLookup::setName(
const QString &name)
524QBindable<QString> QDnsLookup::bindableName()
531
532
533
535QDnsLookup::Type QDnsLookup::type()
const
537 return d_func()->type;
540void QDnsLookup::setType(Type type)
546QBindable<QDnsLookup::Type> QDnsLookup::bindableType()
553
554
555
557QHostAddress QDnsLookup::nameserver()
const
559 return d_func()->nameserver;
562void QDnsLookup::setNameserver(
const QHostAddress &nameserver)
565 d->nameserver = nameserver;
568QBindable<QHostAddress> QDnsLookup::bindableNameserver()
571 return &d->nameserver;
575
576
577
578
579
580
581
582
583
585quint16 QDnsLookup::nameserverPort()
const
587 return d_func()->port;
590void QDnsLookup::setNameserverPort(quint16 nameserverPort)
593 d->port = nameserverPort;
596QBindable<quint16> QDnsLookup::bindableNameserverPort()
603
604
605
606
607
608
609QDnsLookup::Protocol QDnsLookup::nameserverProtocol()
const
611 return d_func()->protocol;
614void QDnsLookup::setNameserverProtocol(Protocol protocol)
616 d_func()->protocol = protocol;
619QBindable<QDnsLookup::Protocol> QDnsLookup::bindableNameserverProtocol()
621 return &d_func()->protocol;
625
626
627
628
629
630
631
632
633
635void QDnsLookup::setNameserver(Protocol protocol,
const QHostAddress &nameserver, quint16 port)
637 Qt::beginPropertyUpdateGroup();
638 setNameserver(nameserver);
639 setNameserverPort(port);
640 setNameserverProtocol(protocol);
641 Qt::endPropertyUpdateGroup();
645
646
648QList<QDnsDomainNameRecord> QDnsLookup::canonicalNameRecords()
const
650 return d_func()->reply.canonicalNameRecords;
654
655
657QList<QDnsHostAddressRecord> QDnsLookup::hostAddressRecords()
const
659 return d_func()->reply.hostAddressRecords;
663
664
665
666
667
668
670QList<QDnsMailExchangeRecord> QDnsLookup::mailExchangeRecords()
const
672 return d_func()->reply.mailExchangeRecords;
676
677
679QList<QDnsDomainNameRecord> QDnsLookup::nameServerRecords()
const
681 return d_func()->reply.nameServerRecords;
685
686
688QList<QDnsDomainNameRecord> QDnsLookup::pointerRecords()
const
690 return d_func()->reply.pointerRecords;
694
695
696
697
698
699
701QList<QDnsServiceRecord> QDnsLookup::serviceRecords()
const
703 return d_func()->reply.serviceRecords;
707
708
710QList<QDnsTextRecord> QDnsLookup::textRecords()
const
712 return d_func()->reply.textRecords;
716
717
718
719
720
721
722
723
724
725QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords()
const
727 return d_func()->reply.tlsAssociationRecords;
732
733
734
735
736
737void QDnsLookup::setSslConfiguration(
const QSslConfiguration &sslConfiguration)
740 d->sslConfiguration.emplace(sslConfiguration);
744
745
746
747
748QSslConfiguration QDnsLookup::sslConfiguration()
const
750 const Q_D(QDnsLookup);
751 return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
756
757
758
759
761void QDnsLookup::abort()
765 d->runnable =
nullptr;
766 d->reply = QDnsLookupReply();
767 d->reply.error = QDnsLookup::OperationCancelledError;
768 d->reply.errorString = tr(
"Operation cancelled");
769 d->isFinished =
true;
775
776
777
778
780void QDnsLookup::lookup()
783 d->isFinished =
false;
784 d->reply = QDnsLookupReply();
785 if (!QCoreApplication::instanceExists()) {
787 qWarning(
"QDnsLookup requires a QCoreApplication");
791 auto l = [
this](
const QDnsLookupReply &reply) {
793 if (d->runnable == sender()) {
794#ifdef QDNSLOOKUP_DEBUG
795 qDebug(
"DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
798 d->sslConfiguration = std::move(reply.sslConfiguration);
801 d->runnable =
nullptr;
802 d->isFinished =
true;
807 d->runnable =
new QDnsLookupRunnable(d);
808 connect(d->runnable, &QDnsLookupRunnable::finished,
this, l,
809 Qt::BlockingQueuedConnection);
810 theDnsLookupThreadPool->start(d->runnable);
814
815
816
817
818
819
820
821
822
823
824
825
826
829
830
832QDnsDomainNameRecord::QDnsDomainNameRecord()
833 : d(
new QDnsDomainNameRecordPrivate)
838
839
841QDnsDomainNameRecord::QDnsDomainNameRecord(
const QDnsDomainNameRecord &other)
847
848
850QDnsDomainNameRecord::~QDnsDomainNameRecord()
855
856
858QString QDnsDomainNameRecord::name()
const
864
865
867quint32 QDnsDomainNameRecord::timeToLive()
const
869 return d->timeToLive;
873
874
876QString QDnsDomainNameRecord::value()
const
882
883
884
886QDnsDomainNameRecord &QDnsDomainNameRecord::operator=(
const QDnsDomainNameRecord &other)
893
894
895
898
899
900
901
902
903
904
905
906
907
908
909
910
913
914
916QDnsHostAddressRecord::QDnsHostAddressRecord()
917 : d(
new QDnsHostAddressRecordPrivate)
922
923
925QDnsHostAddressRecord::QDnsHostAddressRecord(
const QDnsHostAddressRecord &other)
931
932
934QDnsHostAddressRecord::~QDnsHostAddressRecord()
939
940
942QString QDnsHostAddressRecord::name()
const
948
949
951quint32 QDnsHostAddressRecord::timeToLive()
const
953 return d->timeToLive;
957
958
960QHostAddress QDnsHostAddressRecord::value()
const
966
967
968
970QDnsHostAddressRecord &QDnsHostAddressRecord::operator=(
const QDnsHostAddressRecord &other)
977
978
979
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
999
1000
1002QDnsMailExchangeRecord::QDnsMailExchangeRecord()
1003 : d(
new QDnsMailExchangeRecordPrivate)
1008
1009
1011QDnsMailExchangeRecord::QDnsMailExchangeRecord(
const QDnsMailExchangeRecord &other)
1017
1018
1020QDnsMailExchangeRecord::~QDnsMailExchangeRecord()
1025
1026
1028QString QDnsMailExchangeRecord::exchange()
const
1034
1035
1037QString QDnsMailExchangeRecord::name()
const
1043
1044
1046quint16 QDnsMailExchangeRecord::preference()
const
1048 return d->preference;
1052
1053
1055quint32 QDnsMailExchangeRecord::timeToLive()
const
1057 return d->timeToLive;
1061
1062
1063
1065QDnsMailExchangeRecord &QDnsMailExchangeRecord::operator=(
const QDnsMailExchangeRecord &other)
1071
1072
1073
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1093
1094
1096QDnsServiceRecord::QDnsServiceRecord()
1097 : d(
new QDnsServiceRecordPrivate)
1102
1103
1105QDnsServiceRecord::QDnsServiceRecord(
const QDnsServiceRecord &other)
1111
1112
1114QDnsServiceRecord::~QDnsServiceRecord()
1119
1120
1122QString QDnsServiceRecord::name()
const
1128
1129
1131quint16 QDnsServiceRecord::port()
const
1137
1138
1139
1140
1141
1143quint16 QDnsServiceRecord::priority()
const
1149
1150
1152QString QDnsServiceRecord::target()
const
1158
1159
1161quint32 QDnsServiceRecord::timeToLive()
const
1163 return d->timeToLive;
1167
1168
1169
1170
1171
1172
1174quint16 QDnsServiceRecord::weight()
const
1180
1181
1182
1184QDnsServiceRecord &QDnsServiceRecord::operator=(
const QDnsServiceRecord &other)
1190
1191
1192
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1212
1213
1215QDnsTextRecord::QDnsTextRecord()
1216 : d(
new QDnsTextRecordPrivate)
1221
1222
1224QDnsTextRecord::QDnsTextRecord(
const QDnsTextRecord &other)
1230
1231
1233QDnsTextRecord::~QDnsTextRecord()
1238
1239
1241QString QDnsTextRecord::name()
const
1247
1248
1250quint32 QDnsTextRecord::timeToLive()
const
1252 return d->timeToLive;
1256
1257
1259QList<QByteArray> QDnsTextRecord::values()
const
1265
1266
1267
1269QDnsTextRecord &QDnsTextRecord::operator=(
const QDnsTextRecord &other)
1275
1276
1277
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1296QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
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
1349
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
1384
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
1417
1420
1421
1422QDnsTlsAssociationRecord::QDnsTlsAssociationRecord()
1423 : d(
new QDnsTlsAssociationRecordPrivate)
1428
1429
1433
1434
1439
1440
1444
1445
1452
1453
1456 return d->timeToLive;
1460
1461
1468
1469
1476
1477
1480 return d->matchType;
1484
1485
1486
1487
1488
1489
1490
1498 QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
1499 if (label.isEmpty())
1500 return QDnsLookupRunnable::EncodedLabel(1, rootDomain);
1502 QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
1504 return encodedLabel;
1506 return std::move(encodedLabel).toLatin1();
1518 port = QDnsLookup::defaultPortForProtocol(protocol);
1520 sslConfiguration = d->sslConfiguration;
1529 if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
1530 reply.error = QDnsLookup::InvalidRequestError;
1531 reply.errorString = QDnsLookup::tr(
"Invalid domain name");
1537 qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
1538 qt_qdnsservicerecord_sort(reply.serviceRecords);
1541 emit finished(reply);
1544 switch (reply.error) {
1545 case QDnsLookup::NoError:
1546 case QDnsLookup::OperationCancelledError:
1547 case QDnsLookup::NotFoundError:
1548 case QDnsLookup::ServerFailureError:
1549 case QDnsLookup::ServerRefusedError:
1550 case QDnsLookup::TimeoutError:
1553 case QDnsLookup::ResolverError:
1554 case QDnsLookup::InvalidRequestError:
1555 case QDnsLookup::InvalidReplyError:
1557 <<
"DNS lookup failed (" << reply.error <<
"): "
1558 << qUtf16Printable(reply.errorString)
1559 <<
"; request was " <<
this;
1566 d << r->requestName.left(MaxDomainNameLength);
1567 if (r->requestName.size() > MaxDomainNameLength)
1568 d <<
"... (truncated)";
1569 d <<
" type " << r->requestType;
1570 if (!r->nameserver.isNull()) {
1571 d <<
" to nameserver " << qUtf16Printable(r->nameserver.toString())
1572 <<
" port " << (r->port ? r->port : QDnsLookup::defaultPortForProtocol(r->protocol));
1573 switch (r->protocol) {
1574 case QDnsLookup::Standard:
1576 case QDnsLookup::DnsOverTls:
1584static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
1585static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
1586static constexpr quint8 DnsAuthenticDataBit = 0x20;
1588static int makeReplyErrorFromSocket(QDnsLookupReply *reply,
const QAbstractSocket *socket)
1590 QDnsLookup::Error error = [&] {
1591 switch (socket->error()) {
1592 case QAbstractSocket::SocketTimeoutError:
1593 case QAbstractSocket::ProxyConnectionTimeoutError:
1594 return QDnsLookup::TimeoutError;
1596 return QDnsLookup::ResolverError;
1599 reply->setError(error, socket->errorString());
1603bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<
unsigned char> query,
1604 ReplyBuffer &response)
1607 socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
1609# if QT_CONFIG(networkproxy)
1610 socket.setProtocolTag(
"domain-s"_L1);
1614 query[3] |= DnsAuthenticDataBit;
1617 quint16 size = qToBigEndian<quint16>(query.size());
1618 QDeadlineTimer timeout(DnsOverTlsTimeout);
1620 socket.connectToHostEncrypted(nameserver.toString(), port);
1621 socket.write(
reinterpret_cast<
const char *>(&size),
sizeof(size));
1622 socket.write(
reinterpret_cast<
const char *>(query.data()), query.size());
1623 if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
1626 reply->sslConfiguration = socket.sslConfiguration();
1629 auto waitForBytes = [&](
void *buffer,
int count) {
1630 int remaining = timeout.remainingTime();
1631 while (remaining >= 0 && socket.bytesAvailable() < count) {
1632 if (!socket.waitForReadyRead(remaining))
1635 return socket.read(
static_cast<
char *>(buffer), count) == count;
1637 if (!waitForBytes(&size,
sizeof(size)))
1643 size = qFromBigEndian(size);
1644 response.resize(size);
1645 if (waitForBytes(response.data(), size)) {
1648 reply->authenticData = response[3] & DnsAuthenticDataBit;
1654 return makeReplyErrorFromSocket(reply, &socket);
1658 ReplyBuffer &response)
1662 reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr(
"SSL/TLS support not present"));
1669#include "moc_qdnslookup.cpp"
1670#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,...)