1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qdnslookup.h"
41 #include "qdnslookup_p.h"
42 
43 #include <qcoreapplication.h>
44 #include <qdatetime.h>
45 #include <qrandom.h>
46 #include <qurl.h>
47 
48 #include <algorithm>
49 
50 QT_BEGIN_NAMESPACE
51 
52 #if QT_CONFIG(thread)
53 Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
54 #endif
55 
qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord & r1,const QDnsMailExchangeRecord & r2)56 static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
57 {
58     // Lower numbers are more preferred than higher ones.
59     return r1.preference() < r2.preference();
60 }
61 
62 /*
63     Sorts a list of QDnsMailExchangeRecord objects according to RFC 5321.
64 */
65 
qt_qdnsmailexchangerecord_sort(QList<QDnsMailExchangeRecord> & records)66 static void qt_qdnsmailexchangerecord_sort(QList<QDnsMailExchangeRecord> &records)
67 {
68     // If we have no more than one result, we are done.
69     if (records.size() <= 1)
70         return;
71 
72     // Order the records by preference.
73     std::sort(records.begin(), records.end(), qt_qdnsmailexchangerecord_less_than);
74 
75     int i = 0;
76     while (i < records.size()) {
77 
78         // Determine the slice of records with the current preference.
79         QList<QDnsMailExchangeRecord> slice;
80         const quint16 slicePreference = records.at(i).preference();
81         for (int j = i; j < records.size(); ++j) {
82             if (records.at(j).preference() != slicePreference)
83                 break;
84             slice << records.at(j);
85         }
86 
87         // Randomize the slice of records.
88         while (!slice.isEmpty()) {
89             const unsigned int pos = QRandomGenerator::global()->bounded(slice.size());
90             records[i++] = slice.takeAt(pos);
91         }
92     }
93 }
94 
qt_qdnsservicerecord_less_than(const QDnsServiceRecord & r1,const QDnsServiceRecord & r2)95 static bool qt_qdnsservicerecord_less_than(const QDnsServiceRecord &r1, const QDnsServiceRecord &r2)
96 {
97     // Order by priority, or if the priorities are equal,
98     // put zero weight records first.
99     return r1.priority() < r2.priority()
100        || (r1.priority() == r2.priority()
101         && r1.weight() == 0 && r2.weight() > 0);
102 }
103 
104 /*
105     Sorts a list of QDnsServiceRecord objects according to RFC 2782.
106 */
107 
qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> & records)108 static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
109 {
110     // If we have no more than one result, we are done.
111     if (records.size() <= 1)
112         return;
113 
114     // Order the records by priority, and for records with an equal
115     // priority, put records with a zero weight first.
116     std::sort(records.begin(), records.end(), qt_qdnsservicerecord_less_than);
117 
118     int i = 0;
119     while (i < records.size()) {
120 
121         // Determine the slice of records with the current priority.
122         QList<QDnsServiceRecord> slice;
123         const quint16 slicePriority = records.at(i).priority();
124         unsigned int sliceWeight = 0;
125         for (int j = i; j < records.size(); ++j) {
126             if (records.at(j).priority() != slicePriority)
127                 break;
128             sliceWeight += records.at(j).weight();
129             slice << records.at(j);
130         }
131 #ifdef QDNSLOOKUP_DEBUG
132         qDebug("qt_qdnsservicerecord_sort() : priority %i (size: %i, total weight: %i)",
133                slicePriority, slice.size(), sliceWeight);
134 #endif
135 
136         // Order the slice of records.
137         while (!slice.isEmpty()) {
138             const unsigned int weightThreshold = QRandomGenerator::global()->bounded(sliceWeight + 1);
139             unsigned int summedWeight = 0;
140             for (int j = 0; j < slice.size(); ++j) {
141                 summedWeight += slice.at(j).weight();
142                 if (summedWeight >= weightThreshold) {
143 #ifdef QDNSLOOKUP_DEBUG
144                     qDebug("qt_qdnsservicerecord_sort() : adding %s %i (weight: %i)",
145                            qPrintable(slice.at(j).target()), slice.at(j).port(),
146                            slice.at(j).weight());
147 #endif
148                     // Adjust the slice weight and take the current record.
149                     sliceWeight -= slice.at(j).weight();
150                     records[i++] = slice.takeAt(j);
151                     break;
152                 }
153             }
154         }
155     }
156 }
157 
158 const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
159     QT_TRANSLATE_NOOP("QDnsLookupRunnable", "IPv6 addresses for nameservers are currently not supported");
160 
161 /*!
162     \class QDnsLookup
163     \brief The QDnsLookup class represents a DNS lookup.
164     \since 5.0
165 
166     \inmodule QtNetwork
167     \ingroup network
168 
169     QDnsLookup uses the mechanisms provided by the operating system to perform
170     DNS lookups. To perform a lookup you need to specify a \l name and \l type
171     then invoke the \l{QDnsLookup::lookup()}{lookup()} slot. The
172     \l{QDnsLookup::finished()}{finished()} signal will be emitted upon
173     completion.
174 
175     For example, you can determine which servers an XMPP chat client should
176     connect to for a given domain with:
177 
178     \snippet code/src_network_kernel_qdnslookup.cpp 0
179 
180     Once the request finishes you can handle the results with:
181 
182     \snippet code/src_network_kernel_qdnslookup.cpp 1
183 
184     \note If you simply want to find the IP address(es) associated with a host
185     name, or the host name associated with an IP address you should use
186     QHostInfo instead.
187 */
188 
189 /*!
190     \enum QDnsLookup::Error
191 
192     Indicates all possible error conditions found during the
193     processing of the DNS lookup.
194 
195     \value NoError              no error condition.
196 
197     \value ResolverError        there was an error initializing the system's
198     DNS resolver.
199 
200     \value OperationCancelledError  the lookup was aborted using the abort()
201     method.
202 
203     \value InvalidRequestError  the requested DNS lookup was invalid.
204 
205     \value InvalidReplyError    the reply returned by the server was invalid.
206 
207     \value ServerFailureError   the server encountered an internal failure
208     while processing the request (SERVFAIL).
209 
210     \value ServerRefusedError   the server refused to process the request for
211     security or policy reasons (REFUSED).
212 
213     \value NotFoundError        the requested domain name does not exist
214     (NXDOMAIN).
215 */
216 
217 /*!
218     \enum QDnsLookup::Type
219 
220     Indicates the type of DNS lookup that was performed.
221 
222     \value A        IPv4 address records.
223 
224     \value AAAA     IPv6 address records.
225 
226     \value ANY      any records.
227 
228     \value CNAME    canonical name records.
229 
230     \value MX       mail exchange records.
231 
232     \value NS       name server records.
233 
234     \value PTR      pointer records.
235 
236     \value SRV      service records.
237 
238     \value TXT      text records.
239 */
240 
241 /*!
242     \fn void QDnsLookup::finished()
243 
244     This signal is emitted when the reply has finished processing.
245 */
246 
247 /*!
248     \fn void QDnsLookup::nameChanged(const QString &name)
249 
250     This signal is emitted when the lookup \l name changes.
251     \a name is the new lookup name.
252 */
253 
254 /*!
255     \fn void QDnsLookup::typeChanged(Type type)
256 
257     This signal is emitted when the lookup \l type changes.
258     \a type is the new lookup type.
259 */
260 
261 /*!
262     Constructs a QDnsLookup object and sets \a parent as the parent object.
263 
264     The \l type property will default to QDnsLookup::A.
265 */
266 
QDnsLookup(QObject * parent)267 QDnsLookup::QDnsLookup(QObject *parent)
268     : QObject(*new QDnsLookupPrivate, parent)
269 {
270     qRegisterMetaType<QDnsLookupReply>();
271 }
272 /*!
273     Constructs a QDnsLookup object for the given \a type and \a name and sets
274     \a parent as the parent object.
275 */
276 
QDnsLookup(Type type,const QString & name,QObject * parent)277 QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
278     : QObject(*new QDnsLookupPrivate, parent)
279 {
280     Q_D(QDnsLookup);
281     qRegisterMetaType<QDnsLookupReply>();
282     d->name = name;
283     d->type = type;
284 }
285 
286 /*!
287     \fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
288     \since 5.4
289     Constructs a QDnsLookup object for the given \a type, \a name and
290     \a nameserver and sets \a parent as the parent object.
291 */
292 
QDnsLookup(Type type,const QString & name,const QHostAddress & nameserver,QObject * parent)293 QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
294     : QObject(*new QDnsLookupPrivate, parent)
295 {
296     Q_D(QDnsLookup);
297     qRegisterMetaType<QDnsLookupReply>();
298     d->name = name;
299     d->type = type;
300     d->nameserver = nameserver;
301 }
302 
303 /*!
304     Destroys the QDnsLookup object.
305 
306     It is safe to delete a QDnsLookup object even if it is not finished, you
307     will simply never receive its results.
308 */
309 
~QDnsLookup()310 QDnsLookup::~QDnsLookup()
311 {
312 }
313 
314 /*!
315     \property QDnsLookup::error
316     \brief the type of error that occurred if the DNS lookup failed, or NoError.
317 */
318 
error() const319 QDnsLookup::Error QDnsLookup::error() const
320 {
321     return d_func()->reply.error;
322 }
323 
324 /*!
325     \property QDnsLookup::errorString
326     \brief a human-readable description of the error if the DNS lookup failed.
327 */
328 
errorString() const329 QString QDnsLookup::errorString() const
330 {
331     return d_func()->reply.errorString;
332 }
333 
334 /*!
335     Returns whether the reply has finished or was aborted.
336 */
337 
isFinished() const338 bool QDnsLookup::isFinished() const
339 {
340     return d_func()->isFinished;
341 }
342 
343 /*!
344     \property QDnsLookup::name
345     \brief the name to lookup.
346 
347     \note The name will be encoded using IDNA, which means it's unsuitable for
348     querying SRV records compatible with the DNS-SD specification.
349 */
350 
name() const351 QString QDnsLookup::name() const
352 {
353     return d_func()->name;
354 }
355 
setName(const QString & name)356 void QDnsLookup::setName(const QString &name)
357 {
358     Q_D(QDnsLookup);
359     if (name != d->name) {
360         d->name = name;
361         emit nameChanged(name);
362     }
363 }
364 
365 /*!
366     \property QDnsLookup::type
367     \brief the type of DNS lookup.
368 */
369 
type() const370 QDnsLookup::Type QDnsLookup::type() const
371 {
372     return d_func()->type;
373 }
374 
setType(Type type)375 void QDnsLookup::setType(Type type)
376 {
377     Q_D(QDnsLookup);
378     if (type != d->type) {
379         d->type = type;
380         emit typeChanged(type);
381     }
382 }
383 
384 /*!
385     \property QDnsLookup::nameserver
386     \brief the nameserver to use for DNS lookup.
387 */
388 
nameserver() const389 QHostAddress QDnsLookup::nameserver() const
390 {
391     return d_func()->nameserver;
392 }
393 
setNameserver(const QHostAddress & nameserver)394 void QDnsLookup::setNameserver(const QHostAddress &nameserver)
395 {
396     Q_D(QDnsLookup);
397     if (nameserver != d->nameserver) {
398         d->nameserver = nameserver;
399         emit nameserverChanged(nameserver);
400     }
401 }
402 
403 /*!
404     Returns the list of canonical name records associated with this lookup.
405 */
406 
canonicalNameRecords() const407 QList<QDnsDomainNameRecord> QDnsLookup::canonicalNameRecords() const
408 {
409     return d_func()->reply.canonicalNameRecords;
410 }
411 
412 /*!
413     Returns the list of host address records associated with this lookup.
414 */
415 
hostAddressRecords() const416 QList<QDnsHostAddressRecord> QDnsLookup::hostAddressRecords() const
417 {
418     return d_func()->reply.hostAddressRecords;
419 }
420 
421 /*!
422     Returns the list of mail exchange records associated with this lookup.
423 
424     The records are sorted according to
425     \l{http://www.rfc-editor.org/rfc/rfc5321.txt}{RFC 5321}, so if you use them
426     to connect to servers, you should try them in the order they are listed.
427 */
428 
mailExchangeRecords() const429 QList<QDnsMailExchangeRecord> QDnsLookup::mailExchangeRecords() const
430 {
431     return d_func()->reply.mailExchangeRecords;
432 }
433 
434 /*!
435     Returns the list of name server records associated with this lookup.
436 */
437 
nameServerRecords() const438 QList<QDnsDomainNameRecord> QDnsLookup::nameServerRecords() const
439 {
440     return d_func()->reply.nameServerRecords;
441 }
442 
443 /*!
444     Returns the list of pointer records associated with this lookup.
445 */
446 
pointerRecords() const447 QList<QDnsDomainNameRecord> QDnsLookup::pointerRecords() const
448 {
449     return d_func()->reply.pointerRecords;
450 }
451 
452 /*!
453     Returns the list of service records associated with this lookup.
454 
455     The records are sorted according to
456     \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}, so if you use them
457     to connect to servers, you should try them in the order they are listed.
458 */
459 
serviceRecords() const460 QList<QDnsServiceRecord> QDnsLookup::serviceRecords() const
461 {
462     return d_func()->reply.serviceRecords;
463 }
464 
465 /*!
466     Returns the list of text records associated with this lookup.
467 */
468 
textRecords() const469 QList<QDnsTextRecord> QDnsLookup::textRecords() const
470 {
471     return d_func()->reply.textRecords;
472 }
473 
474 /*!
475     Aborts the DNS lookup operation.
476 
477     If the lookup is already finished, does nothing.
478 */
479 
abort()480 void QDnsLookup::abort()
481 {
482     Q_D(QDnsLookup);
483     if (d->runnable) {
484         d->runnable = nullptr;
485         d->reply = QDnsLookupReply();
486         d->reply.error = QDnsLookup::OperationCancelledError;
487         d->reply.errorString = tr("Operation cancelled");
488         d->isFinished = true;
489         emit finished();
490     }
491 }
492 
493 /*!
494     Performs the DNS lookup.
495 
496     The \l{QDnsLookup::finished()}{finished()} signal is emitted upon completion.
497 */
498 
lookup()499 void QDnsLookup::lookup()
500 {
501     Q_D(QDnsLookup);
502     d->isFinished = false;
503     d->reply = QDnsLookupReply();
504     d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
505     connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
506             this, SLOT(_q_lookupFinished(QDnsLookupReply)),
507             Qt::BlockingQueuedConnection);
508 #if QT_CONFIG(thread)
509     theDnsLookupThreadPool()->start(d->runnable);
510 #endif
511 }
512 
513 /*!
514     \class QDnsDomainNameRecord
515     \brief The QDnsDomainNameRecord class stores information about a domain
516     name record.
517 
518     \inmodule QtNetwork
519     \ingroup network
520     \ingroup shared
521 
522     When performing a name server lookup, zero or more records will be returned.
523     Each record is represented by a QDnsDomainNameRecord instance.
524 
525     \sa QDnsLookup
526 */
527 
528 /*!
529     Constructs an empty domain name record object.
530 */
531 
QDnsDomainNameRecord()532 QDnsDomainNameRecord::QDnsDomainNameRecord()
533     : d(new QDnsDomainNameRecordPrivate)
534 {
535 }
536 
537 /*!
538     Constructs a copy of \a other.
539 */
540 
QDnsDomainNameRecord(const QDnsDomainNameRecord & other)541 QDnsDomainNameRecord::QDnsDomainNameRecord(const QDnsDomainNameRecord &other)
542     : d(other.d)
543 {
544 }
545 
546 /*!
547     Destroys a domain name record.
548 */
549 
~QDnsDomainNameRecord()550 QDnsDomainNameRecord::~QDnsDomainNameRecord()
551 {
552 }
553 
554 /*!
555     Returns the name for this record.
556 */
557 
name() const558 QString QDnsDomainNameRecord::name() const
559 {
560     return d->name;
561 }
562 
563 /*!
564     Returns the duration in seconds for which this record is valid.
565 */
566 
timeToLive() const567 quint32 QDnsDomainNameRecord::timeToLive() const
568 {
569     return d->timeToLive;
570 }
571 
572 /*!
573     Returns the value for this domain name record.
574 */
575 
value() const576 QString QDnsDomainNameRecord::value() const
577 {
578     return d->value;
579 }
580 
581 /*!
582     Assigns the data of the \a other object to this record object,
583     and returns a reference to it.
584 */
585 
operator =(const QDnsDomainNameRecord & other)586 QDnsDomainNameRecord &QDnsDomainNameRecord::operator=(const QDnsDomainNameRecord &other)
587 {
588     d = other.d;
589     return *this;
590 }
591 /*!
592     \fn void QDnsDomainNameRecord::swap(QDnsDomainNameRecord &other)
593 
594     Swaps this domain-name record instance with \a other. This
595     function is very fast and never fails.
596 */
597 
598 /*!
599     \class QDnsHostAddressRecord
600     \brief The QDnsHostAddressRecord class stores information about a host
601     address record.
602 
603     \inmodule QtNetwork
604     \ingroup network
605     \ingroup shared
606 
607     When performing an address lookup, zero or more records will be
608     returned. Each record is represented by a QDnsHostAddressRecord instance.
609 
610     \sa QDnsLookup
611 */
612 
613 /*!
614     Constructs an empty host address record object.
615 */
616 
QDnsHostAddressRecord()617 QDnsHostAddressRecord::QDnsHostAddressRecord()
618     : d(new QDnsHostAddressRecordPrivate)
619 {
620 }
621 
622 /*!
623     Constructs a copy of \a other.
624 */
625 
QDnsHostAddressRecord(const QDnsHostAddressRecord & other)626 QDnsHostAddressRecord::QDnsHostAddressRecord(const QDnsHostAddressRecord &other)
627     : d(other.d)
628 {
629 }
630 
631 /*!
632     Destroys a host address record.
633 */
634 
~QDnsHostAddressRecord()635 QDnsHostAddressRecord::~QDnsHostAddressRecord()
636 {
637 }
638 
639 /*!
640     Returns the name for this record.
641 */
642 
name() const643 QString QDnsHostAddressRecord::name() const
644 {
645     return d->name;
646 }
647 
648 /*!
649     Returns the duration in seconds for which this record is valid.
650 */
651 
timeToLive() const652 quint32 QDnsHostAddressRecord::timeToLive() const
653 {
654     return d->timeToLive;
655 }
656 
657 /*!
658     Returns the value for this host address record.
659 */
660 
value() const661 QHostAddress QDnsHostAddressRecord::value() const
662 {
663     return d->value;
664 }
665 
666 /*!
667     Assigns the data of the \a other object to this record object,
668     and returns a reference to it.
669 */
670 
operator =(const QDnsHostAddressRecord & other)671 QDnsHostAddressRecord &QDnsHostAddressRecord::operator=(const QDnsHostAddressRecord &other)
672 {
673     d = other.d;
674     return *this;
675 }
676 /*!
677     \fn void QDnsHostAddressRecord::swap(QDnsHostAddressRecord &other)
678 
679     Swaps this host address record instance with \a other. This
680     function is very fast and never fails.
681 */
682 
683 /*!
684     \class QDnsMailExchangeRecord
685     \brief The QDnsMailExchangeRecord class stores information about a DNS MX record.
686 
687     \inmodule QtNetwork
688     \ingroup network
689     \ingroup shared
690 
691     When performing a lookup on a service, zero or more records will be
692     returned. Each record is represented by a QDnsMailExchangeRecord instance.
693 
694     The meaning of the fields is defined in
695     \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
696 
697     \sa QDnsLookup
698 */
699 
700 /*!
701     Constructs an empty mail exchange record object.
702 */
703 
QDnsMailExchangeRecord()704 QDnsMailExchangeRecord::QDnsMailExchangeRecord()
705     : d(new QDnsMailExchangeRecordPrivate)
706 {
707 }
708 
709 /*!
710     Constructs a copy of \a other.
711 */
712 
QDnsMailExchangeRecord(const QDnsMailExchangeRecord & other)713 QDnsMailExchangeRecord::QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other)
714     : d(other.d)
715 {
716 }
717 
718 /*!
719     Destroys a mail exchange record.
720 */
721 
~QDnsMailExchangeRecord()722 QDnsMailExchangeRecord::~QDnsMailExchangeRecord()
723 {
724 }
725 
726 /*!
727     Returns the domain name of the mail exchange for this record.
728 */
729 
exchange() const730 QString QDnsMailExchangeRecord::exchange() const
731 {
732     return d->exchange;
733 }
734 
735 /*!
736     Returns the name for this record.
737 */
738 
name() const739 QString QDnsMailExchangeRecord::name() const
740 {
741     return d->name;
742 }
743 
744 /*!
745     Returns the preference for this record.
746 */
747 
preference() const748 quint16 QDnsMailExchangeRecord::preference() const
749 {
750     return d->preference;
751 }
752 
753 /*!
754     Returns the duration in seconds for which this record is valid.
755 */
756 
timeToLive() const757 quint32 QDnsMailExchangeRecord::timeToLive() const
758 {
759     return d->timeToLive;
760 }
761 
762 /*!
763     Assigns the data of the \a other object to this record object,
764     and returns a reference to it.
765 */
766 
operator =(const QDnsMailExchangeRecord & other)767 QDnsMailExchangeRecord &QDnsMailExchangeRecord::operator=(const QDnsMailExchangeRecord &other)
768 {
769     d = other.d;
770     return *this;
771 }
772 /*!
773     \fn void QDnsMailExchangeRecord::swap(QDnsMailExchangeRecord &other)
774 
775     Swaps this mail exchange record with \a other. This function is
776     very fast and never fails.
777 */
778 
779 /*!
780     \class QDnsServiceRecord
781     \brief The QDnsServiceRecord class stores information about a DNS SRV record.
782 
783     \inmodule QtNetwork
784     \ingroup network
785     \ingroup shared
786 
787     When performing a lookup on a service, zero or more records will be
788     returned. Each record is represented by a QDnsServiceRecord instance.
789 
790     The meaning of the fields is defined in
791     \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}.
792 
793     \sa QDnsLookup
794 */
795 
796 /*!
797     Constructs an empty service record object.
798 */
799 
QDnsServiceRecord()800 QDnsServiceRecord::QDnsServiceRecord()
801     : d(new QDnsServiceRecordPrivate)
802 {
803 }
804 
805 /*!
806     Constructs a copy of \a other.
807 */
808 
QDnsServiceRecord(const QDnsServiceRecord & other)809 QDnsServiceRecord::QDnsServiceRecord(const QDnsServiceRecord &other)
810     : d(other.d)
811 {
812 }
813 
814 /*!
815     Destroys a service record.
816 */
817 
~QDnsServiceRecord()818 QDnsServiceRecord::~QDnsServiceRecord()
819 {
820 }
821 
822 /*!
823     Returns the name for this record.
824 */
825 
name() const826 QString QDnsServiceRecord::name() const
827 {
828     return d->name;
829 }
830 
831 /*!
832     Returns the port on the target host for this service record.
833 */
834 
port() const835 quint16 QDnsServiceRecord::port() const
836 {
837     return d->port;
838 }
839 
840 /*!
841     Returns the priority for this service record.
842 
843     A client must attempt to contact the target host with the lowest-numbered
844     priority.
845 */
846 
priority() const847 quint16 QDnsServiceRecord::priority() const
848 {
849     return d->priority;
850 }
851 
852 /*!
853     Returns the domain name of the target host for this service record.
854 */
855 
target() const856 QString QDnsServiceRecord::target() const
857 {
858     return d->target;
859 }
860 
861 /*!
862     Returns the duration in seconds for which this record is valid.
863 */
864 
timeToLive() const865 quint32 QDnsServiceRecord::timeToLive() const
866 {
867     return d->timeToLive;
868 }
869 
870 /*!
871     Returns the weight for this service record.
872 
873     The weight field specifies a relative weight for entries with the same
874     priority. Entries with higher weights should be selected with a higher
875     probability.
876 */
877 
weight() const878 quint16 QDnsServiceRecord::weight() const
879 {
880     return d->weight;
881 }
882 
883 /*!
884     Assigns the data of the \a other object to this record object,
885     and returns a reference to it.
886 */
887 
operator =(const QDnsServiceRecord & other)888 QDnsServiceRecord &QDnsServiceRecord::operator=(const QDnsServiceRecord &other)
889 {
890     d = other.d;
891     return *this;
892 }
893 /*!
894     \fn void QDnsServiceRecord::swap(QDnsServiceRecord &other)
895 
896     Swaps this service record instance with \a other. This function is
897     very fast and never fails.
898 */
899 
900 /*!
901     \class QDnsTextRecord
902     \brief The QDnsTextRecord class stores information about a DNS TXT record.
903 
904     \inmodule QtNetwork
905     \ingroup network
906     \ingroup shared
907 
908     When performing a text lookup, zero or more records will be
909     returned. Each record is represented by a QDnsTextRecord instance.
910 
911     The meaning of the fields is defined in
912     \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
913 
914     \sa QDnsLookup
915 */
916 
917 /*!
918     Constructs an empty text record object.
919 */
920 
QDnsTextRecord()921 QDnsTextRecord::QDnsTextRecord()
922     : d(new QDnsTextRecordPrivate)
923 {
924 }
925 
926 /*!
927     Constructs a copy of \a other.
928 */
929 
QDnsTextRecord(const QDnsTextRecord & other)930 QDnsTextRecord::QDnsTextRecord(const QDnsTextRecord &other)
931     : d(other.d)
932 {
933 }
934 
935 /*!
936     Destroys a text record.
937 */
938 
~QDnsTextRecord()939 QDnsTextRecord::~QDnsTextRecord()
940 {
941 }
942 
943 /*!
944     Returns the name for this text record.
945 */
946 
name() const947 QString QDnsTextRecord::name() const
948 {
949     return d->name;
950 }
951 
952 /*!
953     Returns the duration in seconds for which this record is valid.
954 */
955 
timeToLive() const956 quint32 QDnsTextRecord::timeToLive() const
957 {
958     return d->timeToLive;
959 }
960 
961 /*!
962     Returns the values for this text record.
963 */
964 
values() const965 QList<QByteArray> QDnsTextRecord::values() const
966 {
967     return d->values;
968 }
969 
970 /*!
971     Assigns the data of the \a other object to this record object,
972     and returns a reference to it.
973 */
974 
operator =(const QDnsTextRecord & other)975 QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
976 {
977     d = other.d;
978     return *this;
979 }
980 /*!
981     \fn void QDnsTextRecord::swap(QDnsTextRecord &other)
982 
983     Swaps this text record instance with \a other. This function is
984     very fast and never fails.
985 */
986 
_q_lookupFinished(const QDnsLookupReply & _reply)987 void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
988 {
989     Q_Q(QDnsLookup);
990     if (runnable == q->sender()) {
991 #ifdef QDNSLOOKUP_DEBUG
992         qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
993 #endif
994         reply = _reply;
995         runnable = nullptr;
996         isFinished = true;
997         emit q->finished();
998     }
999 }
1000 
run()1001 void QDnsLookupRunnable::run()
1002 {
1003     QDnsLookupReply reply;
1004 
1005     // Validate input.
1006     if (requestName.isEmpty()) {
1007         reply.error = QDnsLookup::InvalidRequestError;
1008         reply.errorString = tr("Invalid domain name");
1009         emit finished(reply);
1010         return;
1011     }
1012 
1013     // Perform request.
1014     query(requestType, requestName, nameserver, &reply);
1015 
1016     // Sort results.
1017     qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
1018     qt_qdnsservicerecord_sort(reply.serviceRecords);
1019 
1020     emit finished(reply);
1021 }
1022 
1023 #if QT_CONFIG(thread)
QDnsLookupThreadPool()1024 QDnsLookupThreadPool::QDnsLookupThreadPool()
1025     : signalsConnected(false)
1026 {
1027     // Run up to 5 lookups in parallel.
1028     setMaxThreadCount(5);
1029 }
1030 
start(QRunnable * runnable)1031 void QDnsLookupThreadPool::start(QRunnable *runnable)
1032 {
1033     // Ensure threads complete at application destruction.
1034     if (!signalsConnected) {
1035         QMutexLocker signalsLocker(&signalsMutex);
1036         if (!signalsConnected) {
1037             QCoreApplication *app = QCoreApplication::instance();
1038             if (!app) {
1039                 qWarning("QDnsLookup requires a QCoreApplication");
1040                 delete runnable;
1041                 return;
1042             }
1043 
1044             moveToThread(app->thread());
1045             connect(app, SIGNAL(destroyed()),
1046                 SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
1047             signalsConnected = true;
1048         }
1049     }
1050 
1051     QThreadPool::start(runnable);
1052 }
1053 
_q_applicationDestroyed()1054 void QDnsLookupThreadPool::_q_applicationDestroyed()
1055 {
1056     waitForDone();
1057     signalsConnected = false;
1058 }
1059 #endif // QT_CONFIG(thread)
1060 QT_END_NAMESPACE
1061 
1062 #include "moc_qdnslookup.cpp"
1063