summaryrefslogtreecommitdiffstats
path: root/src/network/kernel
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-01-23 18:25:39 +0100
committerQt by Nokia <qt-info@nokia.com>2012-01-30 16:39:46 +0100
commite54dc7c2b5b9aa14989f26a718eb99d7516af4a0 (patch)
tree248372c55bede52c63ab0cc367cdfc8c350ba619 /src/network/kernel
parentd9468a975210ecb58ff199e931f47df5b99b267f (diff)
Add support for DNS lookups using native APIs
The QDnsLookup class provides asynchronous APIs for performing DNS lookups. For now, the following lookups are supported: - A and AAAA - CNAME as defined per RFC 1035 - MX as defined per RFC 1035 - NS as defined per RFC 1035 - PTR as defined per RFC 1035 - SRV as defined per RFC 2782 - TXT as defined per RFC 1035 Task-number: QTBUG-10481 Change-Id: I46c1741ec23615863eeca3a1231d5e3f8942495e Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/network/kernel')
-rw-r--r--src/network/kernel/kernel.pri9
-rw-r--r--src/network/kernel/qdnslookup.cpp988
-rw-r--r--src/network/kernel/qdnslookup.h235
-rw-r--r--src/network/kernel/qdnslookup_p.h213
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp324
-rw-r--r--src/network/kernel/qdnslookup_win.cpp177
6 files changed, 1943 insertions, 3 deletions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index d6e099701d..ea937da518 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -5,6 +5,8 @@ INCLUDEPATH += $$PWD
HEADERS += kernel/qauthenticator.h \
kernel/qauthenticator_p.h \
+ kernel/qdnslookup.h \
+ kernel/qdnslookup_p.h \
kernel/qhostaddress.h \
kernel/qhostinfo.h \
kernel/qhostinfo_p.h \
@@ -14,15 +16,16 @@ HEADERS += kernel/qauthenticator.h \
kernel/qnetworkinterface_p.h
SOURCES += kernel/qauthenticator.cpp \
+ kernel/qdnslookup.cpp \
kernel/qhostaddress.cpp \
kernel/qhostinfo.cpp \
kernel/qurlinfo.cpp \
kernel/qnetworkproxy.cpp \
kernel/qnetworkinterface.cpp
-unix:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
-win32:SOURCES += kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp
-integrity:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
+unix:SOURCES += kernel/qdnslookup_unix.cpp kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
+win32:SOURCES += kernel/qdnslookup_win.cpp kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp
+integrity:SOURCES += kernel/qdnslookup_unix.cpp kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
mac:LIBS_PRIVATE += -framework SystemConfiguration -framework CoreFoundation
mac:SOURCES += kernel/qnetworkproxy_mac.cpp
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
new file mode 100644
index 0000000000..f4b143a5fc
--- /dev/null
+++ b/src/network/kernel/qdnslookup.cpp
@@ -0,0 +1,988 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup.h"
+#include "qdnslookup_p.h"
+
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qthreadstorage.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
+Q_GLOBAL_STATIC(QThreadStorage<bool *>, theDnsLookupSeedStorage);
+
+static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
+{
+ // Lower numbers are more preferred than higher ones.
+ return r1.preference() < r2.preference();
+}
+
+/*!
+ Sorts a list of QDnsMailExchangeRecord objects according to RFC 5321.
+*/
+
+static void qt_qdnsmailexchangerecord_sort(QList<QDnsMailExchangeRecord> &records)
+{
+ // If we have no more than one result, we are done.
+ if (records.size() <= 1)
+ return;
+
+ // Order the records by preference.
+ qSort(records.begin(), records.end(), qt_qdnsmailexchangerecord_less_than);
+
+ int i = 0;
+ while (i < records.size()) {
+
+ // Determine the slice of records with the current preference.
+ QList<QDnsMailExchangeRecord> slice;
+ const quint16 slicePreference = records[i].preference();
+ for (int j = i; j < records.size(); ++j) {
+ if (records[j].preference() != slicePreference)
+ break;
+ slice << records[j];
+ }
+
+ // Randomize the slice of records.
+ while (!slice.isEmpty()) {
+ const unsigned int pos = qrand() % slice.size();
+ records[i++] = slice.takeAt(pos);
+ }
+ }
+}
+
+static bool qt_qdnsservicerecord_less_than(const QDnsServiceRecord &r1, const QDnsServiceRecord &r2)
+{
+ // Order by priority, or if the priorities are equal,
+ // put zero weight records first.
+ return r1.priority() < r2.priority()
+ || (r1.priority() == r2.priority()
+ && r1.weight() == 0 && r2.weight() > 0);
+}
+
+/*!
+ Sorts a list of QDnsServiceRecord objects according to RFC 2782.
+*/
+
+static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
+{
+ // If we have no more than one result, we are done.
+ if (records.size() <= 1)
+ return;
+
+ // Order the records by priority, and for records with an equal
+ // priority, put records with a zero weight first.
+ qSort(records.begin(), records.end(), qt_qdnsservicerecord_less_than);
+
+ int i = 0;
+ while (i < records.size()) {
+
+ // Determine the slice of records with the current priority.
+ QList<QDnsServiceRecord> slice;
+ const quint16 slicePriority = records[i].priority();
+ unsigned int sliceWeight = 0;
+ for (int j = i; j < records.size(); ++j) {
+ if (records[j].priority() != slicePriority)
+ break;
+ sliceWeight += records[j].weight();
+ slice << records[j];
+ }
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("qt_qdnsservicerecord_sort() : priority %i (size: %i, total weight: %i)",
+ slicePriority, slice.size(), sliceWeight);
+#endif
+
+ // Order the slice of records.
+ while (!slice.isEmpty()) {
+ const unsigned int weightThreshold = qrand() % (sliceWeight + 1);
+ unsigned int summedWeight = 0;
+ for (int j = 0; j < slice.size(); ++j) {
+ summedWeight += slice[j].weight();
+ if (summedWeight >= weightThreshold) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("qt_qdnsservicerecord_sort() : adding %s %i (weight: %i)",
+ qPrintable(slice[j].target()), slice[j].port(),
+ slice[j].weight());
+#endif
+ // Adjust the slice weight and take the current record.
+ sliceWeight -= slice[j].weight();
+ records[i++] = slice.takeAt(j);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \class QDnsLookup
+ \brief The QDnsLookup class represents a DNS lookup.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ QDnsLookup uses the mechanisms provided by the operating system to perform
+ DNS lookups. To perform a lookup you need to specify a \l name and \l type
+ then invoke the \l{QDnsLookup::lookup()}{lookup()} slot. The
+ \l{QDnsLookup::finished()}{finished()} signal will be emitted upon
+ completion.
+
+ For example, you can determine which servers an XMPP chat client should
+ connect to for a given domain with:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qdnslookup.cpp 0
+
+ Once the request finishes you can handle the results with:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qdnslookup.cpp 1
+
+ \note If you simply want to find the IP address(es) associated with a host
+ name, or the host name associated with an IP address you should use
+ QHostInfo instead.
+*/
+
+/*!
+ \enum QDnsLookup::Error
+
+ Indicates all possible error conditions found during the
+ processing of the DNS lookup.
+
+ \value NoError no error condition.
+
+ \value ResolverError there was an error initializing the system's
+ DNS resolver.
+
+ \value OperationCancelledError the lookup was aborted using the abort()
+ method.
+
+ \value InvalidRequestError the requested DNS lookup was invalid.
+
+ \value InvalidReplyError the reply returned by the server was invalid.
+
+ \value ServerFailureError the server encountered an internal failure
+ while processing the request (SERVFAIL).
+
+ \value ServerRefusedError the server refused to process the request for
+ security or policy reasons (REFUSED).
+
+ \value NotFoundError the requested domain name does not exist
+ (NXDOMAIN).
+*/
+
+/*!
+ \enum QDnsLookup::Type
+
+ Indicates the type of DNS lookup that was performed.
+
+ \value A IPv4 address records.
+
+ \value AAAA IPv6 address records.
+
+ \value ANY any records.
+
+ \value CNAME canonical name records.
+
+ \value MX mail exchange records.
+
+ \value NS name server records.
+
+ \value PTR pointer records.
+
+ \value SRV service records.
+
+ \value TXT text records.
+*/
+
+/*!
+ \fn void QDnsLookup::finished()
+
+ This signal is emitted when the reply has finished processing.
+*/
+
+/*!
+ \fn void QDnsLookup::nameChanged(const QString &name)
+
+ This signal is emitted when the lookup \l name changes.
+ \a name is the new lookup name.
+*/
+
+/*!
+ \fn void QDnsLookup::typeChanged(Type type)
+
+ This signal is emitted when the lookup \l type changes.
+ \a type is the new lookup type.
+*/
+
+/*!
+ Constructs a QDnsLookup object and sets \a parent as the parent object.
+
+ The \l type property will default to QDnsLookup::A.
+*/
+
+QDnsLookup::QDnsLookup(QObject *parent)
+ : QObject(*new QDnsLookupPrivate, parent)
+{
+ qRegisterMetaType<QDnsLookupReply>();
+}
+/*!
+ Constructs a QDnsLookup object for the given \a type and \a name and sets
+ \a parent as the parent object.
+*/
+
+QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
+ : QObject(*new QDnsLookupPrivate, parent)
+{
+ Q_D(QDnsLookup);
+ qRegisterMetaType<QDnsLookupReply>();
+ d->name = name;
+ d->type = type;
+}
+
+/*!
+ Destroys the QDnsLookup object.
+
+ It is safe to delete a QDnsLookup object even if it is not finished, you
+ will simply never receive its results.
+*/
+
+QDnsLookup::~QDnsLookup()
+{
+}
+
+/*!
+ \property QDnsLookup::error
+ \brief the type of error that occurred if the DNS lookup failed, or NoError.
+*/
+
+QDnsLookup::Error QDnsLookup::error() const
+{
+ return d_func()->reply.error;
+}
+
+/*!
+ \property QDnsLookup::errorString
+ \brief a human-readable description of the error if the DNS lookup failed.
+*/
+
+QString QDnsLookup::errorString() const
+{
+ return d_func()->reply.errorString;
+}
+
+/*!
+ \property QDnsLookup::finished
+ \brief whether the reply has finished or was aborted.
+*/
+
+bool QDnsLookup::isFinished() const
+{
+ return d_func()->isFinished;
+}
+
+/*!
+ \property QDnsLookup::name
+ \brief the name to lookup.
+
+ \note The name will be encoded using IDNA, which means it's unsuitable for
+ querying SRV records compatible with the DNS-SD specification.
+*/
+
+QString QDnsLookup::name() const
+{
+ return d_func()->name;
+}
+
+void QDnsLookup::setName(const QString &name)
+{
+ Q_D(QDnsLookup);
+ if (name != d->name) {
+ d->name = name;
+ emit nameChanged(name);
+ }
+}
+
+/*!
+ \property QDnsLookup::type
+ \brief the type of DNS lookup.
+*/
+
+QDnsLookup::Type QDnsLookup::type() const
+{
+ return d_func()->type;
+}
+
+void QDnsLookup::setType(Type type)
+{
+ Q_D(QDnsLookup);
+ if (type != d->type) {
+ d->type = type;
+ emit typeChanged(type);
+ }
+}
+
+/*!
+ Returns the list of canonical name records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::canonicalNameRecords() const
+{
+ return d_func()->reply.canonicalNameRecords;
+}
+
+/*!
+ Returns the list of host address records associated with this lookup.
+*/
+
+QList<QDnsHostAddressRecord> QDnsLookup::hostAddressRecords() const
+{
+ return d_func()->reply.hostAddressRecords;
+}
+
+/*!
+ Returns the list of mail exchange records associated with this lookup.
+
+ The records are sorted according to
+ \l{http://www.rfc-editor.org/rfc/rfc5321.txt}{RFC 5321}, so if you use them
+ to connect to servers, you should try them in the order they are listed.
+*/
+
+QList<QDnsMailExchangeRecord> QDnsLookup::mailExchangeRecords() const
+{
+ return d_func()->reply.mailExchangeRecords;
+}
+
+/*!
+ Returns the list of name server records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::nameServerRecords() const
+{
+ return d_func()->reply.nameServerRecords;
+}
+
+/*!
+ Returns the list of pointer records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::pointerRecords() const
+{
+ return d_func()->reply.pointerRecords;
+}
+
+/*!
+ Returns the list of service records associated with this lookup.
+
+ The records are sorted according to
+ \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}, so if you use them
+ to connect to servers, you should try them in the order they are listed.
+*/
+
+QList<QDnsServiceRecord> QDnsLookup::serviceRecords() const
+{
+ return d_func()->reply.serviceRecords;
+}
+
+/*!
+ Returns the list of text records associated with this lookup.
+*/
+
+QList<QDnsTextRecord> QDnsLookup::textRecords() const
+{
+ return d_func()->reply.textRecords;
+}
+
+/*!
+ Aborts the DNS lookup operation.
+
+ If the lookup is already finished, does nothing.
+*/
+
+void QDnsLookup::abort()
+{
+ Q_D(QDnsLookup);
+ if (d->runnable) {
+ d->runnable = 0;
+ d->reply = QDnsLookupReply();
+ d->reply.error = QDnsLookup::OperationCancelledError;
+ d->reply.errorString = tr("Operation cancelled");
+ d->isFinished = true;
+ emit finished();
+ }
+}
+
+/*!
+ Performs the DNS lookup.
+
+ The \l{QDnsLookup::finished()}{finished()} signal is emitted upon completion.
+*/
+
+void QDnsLookup::lookup()
+{
+ Q_D(QDnsLookup);
+ d->isFinished = false;
+ d->reply = QDnsLookupReply();
+ d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name));
+ connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
+ this, SLOT(_q_lookupFinished(QDnsLookupReply)),
+ Qt::BlockingQueuedConnection);
+ theDnsLookupThreadPool()->start(d->runnable);
+}
+
+/*!
+ \class QDnsDomainNameRecord
+ \brief The QDnsDomainNameRecord class stores information about a domain
+ name record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a name server lookup, zero or more records will be returned.
+ Each record is represented by a QDnsDomainNameRecord instance.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty domain name record object.
+*/
+
+QDnsDomainNameRecord::QDnsDomainNameRecord()
+ : d(new QDnsDomainNameRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsDomainNameRecord::QDnsDomainNameRecord(const QDnsDomainNameRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a domain name record.
+*/
+
+QDnsDomainNameRecord::~QDnsDomainNameRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsDomainNameRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsDomainNameRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the value for this domain name record.
+*/
+
+QString QDnsDomainNameRecord::value() const
+{
+ return d->value;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsDomainNameRecord &QDnsDomainNameRecord::operator=(const QDnsDomainNameRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsHostAddressRecord
+ \brief The QDnsHostAddressRecord class stores information about a host
+ address record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing an address lookup, zero or more records will be
+ returned. Each record is represented by a QDnsHostAddressRecord instance.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty host address record object.
+*/
+
+QDnsHostAddressRecord::QDnsHostAddressRecord()
+ : d(new QDnsHostAddressRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsHostAddressRecord::QDnsHostAddressRecord(const QDnsHostAddressRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a host address record.
+*/
+
+QDnsHostAddressRecord::~QDnsHostAddressRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsHostAddressRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsHostAddressRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the value for this host address record.
+*/
+
+QHostAddress QDnsHostAddressRecord::value() const
+{
+ return d->value;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsHostAddressRecord &QDnsHostAddressRecord::operator=(const QDnsHostAddressRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsMailExchangeRecord
+ \brief The QDnsMailExchangeRecord class stores information about a DNS MX record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a lookup on a service, zero or more records will be
+ returned. Each record is represented by a QDnsMailExchangeRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty mail exchange record object.
+*/
+
+QDnsMailExchangeRecord::QDnsMailExchangeRecord()
+ : d(new QDnsMailExchangeRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsMailExchangeRecord::QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a mail exchange record.
+*/
+
+QDnsMailExchangeRecord::~QDnsMailExchangeRecord()
+{
+}
+
+/*!
+ Returns the domain name of the mail exchange for this record.
+*/
+
+QString QDnsMailExchangeRecord::exchange() const
+{
+ return d->exchange;
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsMailExchangeRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the preference for this record.
+*/
+
+quint16 QDnsMailExchangeRecord::preference() const
+{
+ return d->preference;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsMailExchangeRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsMailExchangeRecord &QDnsMailExchangeRecord::operator=(const QDnsMailExchangeRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsServiceRecord
+ \brief The QDnsServiceRecord class stores information about a DNS SRV record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a lookup on a service, zero or more records will be
+ returned. Each record is represented by a QDnsServiceRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty service record object.
+*/
+
+QDnsServiceRecord::QDnsServiceRecord()
+ : d(new QDnsServiceRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsServiceRecord::QDnsServiceRecord(const QDnsServiceRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a service record.
+*/
+
+QDnsServiceRecord::~QDnsServiceRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsServiceRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the port on the target host for this service record.
+*/
+
+quint16 QDnsServiceRecord::port() const
+{
+ return d->port;
+}
+
+/*!
+ Returns the priority for this service record.
+
+ A client must attempt to contact the target host with the lowest-numbered
+ priority.
+*/
+
+quint16 QDnsServiceRecord::priority() const
+{
+ return d->priority;
+}
+
+/*!
+ Returns the domain name of the target host for this service record.
+*/
+
+QString QDnsServiceRecord::target() const
+{
+ return d->target;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsServiceRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the weight for this service record.
+
+ The weight field specifies a relative weight for entries with the same
+ priority. Entries with higher weights should be selected with a higher
+ probability.
+*/
+
+quint16 QDnsServiceRecord::weight() const
+{
+ return d->weight;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsServiceRecord &QDnsServiceRecord::operator=(const QDnsServiceRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsTextRecord
+ \brief The QDnsTextRecord class stores information about a DNS TXT record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a text lookup, zero or more records will be
+ returned. Each record is represented by a QDnsTextRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty text record object.
+*/
+
+QDnsTextRecord::QDnsTextRecord()
+ : d(new QDnsTextRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsTextRecord::QDnsTextRecord(const QDnsTextRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a text record.
+*/
+
+QDnsTextRecord::~QDnsTextRecord()
+{
+}
+
+/*!
+ Returns the name for this text record.
+*/
+
+QString QDnsTextRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsTextRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the values for this text record.
+*/
+
+QList<QByteArray> QDnsTextRecord::values() const
+{
+ return d->values;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
+{
+ Q_Q(QDnsLookup);
+ if (runnable == q->sender()) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
+#endif
+ reply = _reply;
+ runnable = 0;
+ isFinished = true;
+ emit q->finished();
+ }
+}
+
+void QDnsLookupRunnable::run()
+{
+ QDnsLookupReply reply;
+
+ // Validate input.
+ if (requestName.isEmpty()) {
+ reply.error = QDnsLookup::InvalidRequestError;
+ reply.errorString = tr("Invalid domain name");
+ emit finished(reply);
+ return;
+ }
+
+ // Perform request.
+ query(requestType, requestName, &reply);
+
+ // Sort results.
+ if (!theDnsLookupSeedStorage()->hasLocalData()) {
+ qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
+ theDnsLookupSeedStorage()->setLocalData(new bool(true));
+ }
+ qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
+ qt_qdnsservicerecord_sort(reply.serviceRecords);
+
+ emit finished(reply);
+}
+
+QDnsLookupThreadPool::QDnsLookupThreadPool()
+ : signalsConnected(false)
+{
+ // Run up to 5 lookups in parallel.
+ setMaxThreadCount(5);
+}
+
+void QDnsLookupThreadPool::start(QRunnable *runnable)
+{
+ // Ensure threads complete at application destruction.
+ if (!signalsConnected) {
+ QMutexLocker signalsLocker(&signalsMutex);
+ if (!signalsConnected) {
+ QCoreApplication *app = QCoreApplication::instance();
+ if (!app) {
+ qWarning("QDnsLookup requires a QCoreApplication");
+ delete runnable;
+ return;
+ }
+
+ moveToThread(app->thread());
+ connect(app, SIGNAL(destroyed()),
+ SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
+ signalsConnected = true;
+ }
+ }
+
+ QThreadPool::start(runnable);
+}
+
+void QDnsLookupThreadPool::_q_applicationDestroyed()
+{
+ waitForDone();
+ signalsConnected = false;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qdnslookup.cpp"
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
new file mode 100644
index 0000000000..198b19d8a8
--- /dev/null
+++ b/src/network/kernel/qdnslookup.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDNSLOOKUP_H
+#define QDNSLOOKUP_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHostAddress;
+class QDnsLookupPrivate;
+class QDnsDomainNameRecordPrivate;
+class QDnsHostAddressRecordPrivate;
+class QDnsMailExchangeRecordPrivate;
+class QDnsServiceRecordPrivate;
+class QDnsTextRecordPrivate;
+
+class Q_NETWORK_EXPORT QDnsDomainNameRecord
+{
+public:
+ QDnsDomainNameRecord();
+ QDnsDomainNameRecord(const QDnsDomainNameRecord &other);
+ ~QDnsDomainNameRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QString value() const;
+
+ QDnsDomainNameRecord &operator=(const QDnsDomainNameRecord &other);
+
+private:
+ QSharedDataPointer<QDnsDomainNameRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class Q_NETWORK_EXPORT QDnsHostAddressRecord
+{
+public:
+ QDnsHostAddressRecord();
+ QDnsHostAddressRecord(const QDnsHostAddressRecord &other);
+ ~QDnsHostAddressRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QHostAddress value() const;
+
+ QDnsHostAddressRecord &operator=(const QDnsHostAddressRecord &other);
+
+private:
+ QSharedDataPointer<QDnsHostAddressRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class Q_NETWORK_EXPORT QDnsMailExchangeRecord
+{
+public:
+ QDnsMailExchangeRecord();
+ QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other);
+ ~QDnsMailExchangeRecord();
+
+ QString exchange() const;
+ QString name() const;
+ quint16 preference() const;
+ quint32 timeToLive() const;
+
+ QDnsMailExchangeRecord &operator=(const QDnsMailExchangeRecord &other);
+
+private:
+ QSharedDataPointer<QDnsMailExchangeRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class Q_NETWORK_EXPORT QDnsServiceRecord
+{
+public:
+ QDnsServiceRecord();
+ QDnsServiceRecord(const QDnsServiceRecord &other);
+ ~QDnsServiceRecord();
+
+ QString name() const;
+ quint16 port() const;
+ quint16 priority() const;
+ QString target() const;
+ quint32 timeToLive() const;
+ quint16 weight() const;
+
+ QDnsServiceRecord &operator=(const QDnsServiceRecord &other);
+
+private:
+ QSharedDataPointer<QDnsServiceRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class Q_NETWORK_EXPORT QDnsTextRecord
+{
+public:
+ QDnsTextRecord();
+ QDnsTextRecord(const QDnsTextRecord &other);
+ ~QDnsTextRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QList<QByteArray> values() const;
+
+ QDnsTextRecord &operator=(const QDnsTextRecord &other);
+
+private:
+ QSharedDataPointer<QDnsTextRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class Q_NETWORK_EXPORT QDnsLookup : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(Error Type)
+ Q_PROPERTY(Error error READ error NOTIFY finished)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
+
+public:
+ enum Error
+ {
+ NoError = 0,
+ ResolverError,
+ OperationCancelledError,
+ InvalidRequestError,
+ InvalidReplyError,
+ ServerFailureError,
+ ServerRefusedError,
+ NotFoundError
+ };
+
+ enum Type
+ {
+ A = 1,
+ AAAA = 28,
+ ANY = 255,
+ CNAME = 5,
+ MX = 15,
+ NS = 2,
+ PTR = 12,
+ SRV = 33,
+ TXT = 16
+ };
+
+ QDnsLookup(QObject *parent = 0);
+ QDnsLookup(Type type, const QString &name, QObject *parent = 0);
+ ~QDnsLookup();
+
+ Error error() const;
+ QString errorString() const;
+ bool isFinished() const;
+
+ QString name() const;
+ void setName(const QString &name);
+
+ Type type() const;
+ void setType(QDnsLookup::Type);
+
+ QList<QDnsDomainNameRecord> canonicalNameRecords() const;
+ QList<QDnsHostAddressRecord> hostAddressRecords() const;
+ QList<QDnsMailExchangeRecord> mailExchangeRecords() const;
+ QList<QDnsDomainNameRecord> nameServerRecords() const;
+ QList<QDnsDomainNameRecord> pointerRecords() const;
+ QList<QDnsServiceRecord> serviceRecords() const;
+ QList<QDnsTextRecord> textRecords() const;
+
+
+public Q_SLOTS:
+ void abort();
+ void lookup();
+
+Q_SIGNALS:
+ void finished();
+ void nameChanged(const QString &name);
+ void typeChanged(Type type);
+
+private:
+ Q_DECLARE_PRIVATE(QDnsLookup)
+ Q_PRIVATE_SLOT(d_func(), void _q_lookupFinished(const QDnsLookupReply &reply))
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDNSLOOKUP_H
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
new file mode 100644
index 0000000000..8515620a93
--- /dev/null
+++ b/src/network/kernel/qdnslookup_p.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDNSLOOKUP_P_H
+#define QDNSLOOKUP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QDnsLookup class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qmutex.h"
+#include "QtCore/qrunnable.h"
+#include "QtCore/qsharedpointer.h"
+#include "QtCore/qthreadpool.h"
+#include "QtNetwork/qdnslookup.h"
+#include "QtNetwork/qhostaddress.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QDNSLOOKUP_DEBUG
+
+class QDnsLookupRunnable;
+
+class QDnsLookupReply
+{
+public:
+ QDnsLookupReply()
+ : error(QDnsLookup::NoError)
+ { }
+
+ QDnsLookup::Error error;
+ QString errorString;
+
+ QList<QDnsDomainNameRecord> canonicalNameRecords;
+ QList<QDnsHostAddressRecord> hostAddressRecords;
+ QList<QDnsMailExchangeRecord> mailExchangeRecords;
+ QList<QDnsDomainNameRecord> nameServerRecords;
+ QList<QDnsDomainNameRecord> pointerRecords;
+ QList<QDnsServiceRecord> serviceRecords;
+ QList<QDnsTextRecord> textRecords;
+};
+
+class QDnsLookupPrivate : public QObjectPrivate
+{
+public:
+ QDnsLookupPrivate()
+ : isFinished(false)
+ , type(QDnsLookup::A)
+ , runnable(0)
+ { }
+
+ void _q_lookupFinished(const QDnsLookupReply &reply);
+
+ bool isFinished;
+ QString name;
+ QDnsLookup::Type type;
+ QDnsLookupReply reply;
+ QDnsLookupRunnable *runnable;
+
+ Q_DECLARE_PUBLIC(QDnsLookup)
+};
+
+class QDnsLookupRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name)
+ : requestType(type)
+ , requestName(name)
+ { }
+ void run();
+
+signals:
+ void finished(const QDnsLookupReply &reply);
+
+private:
+ static void query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply);
+ QDnsLookup::Type requestType;
+ QByteArray requestName;
+};
+
+class QDnsLookupThreadPool : public QThreadPool
+{
+ Q_OBJECT
+
+public:
+ QDnsLookupThreadPool();
+ void start(QRunnable *runnable);
+
+private slots:
+ void _q_applicationDestroyed();
+
+private:
+ QMutex signalsMutex;
+ bool signalsConnected;
+};
+
+class QDnsRecordPrivate : public QSharedData
+{
+public:
+ QDnsRecordPrivate()
+ : timeToLive(0)
+ { }
+
+ QString name;
+ quint32 timeToLive;
+};
+
+class QDnsDomainNameRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsDomainNameRecordPrivate()
+ { }
+
+ QString value;
+};
+
+class QDnsHostAddressRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsHostAddressRecordPrivate()
+ { }
+
+ QHostAddress value;
+};
+
+class QDnsMailExchangeRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsMailExchangeRecordPrivate()
+ : preference(0)
+ { }
+
+ QString exchange;
+ quint16 preference;
+};
+
+class QDnsServiceRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsServiceRecordPrivate()
+ : port(0),
+ priority(0),
+ weight(0)
+ { }
+
+ QString target;
+ quint16 port;
+ quint16 priority;
+ quint16 weight;
+};
+
+class QDnsTextRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsTextRecordPrivate()
+ { }
+
+ QList<QByteArray> values;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QDnsLookupReply)
+
+#endif // QDNSLOOKUP_P_H
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
new file mode 100644
index 0000000000..21a7135106
--- /dev/null
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <qlibrary.h>
+#include <qscopedpointer.h>
+#include <qurl.h>
+#include <private/qmutexpool_p.h>
+
+#include <sys/types.h>
+#include <netdb.h>
+#if defined(Q_OS_MAC)
+#include <arpa/nameser.h>
+#include <arpa/nameser_compat.h>
+#endif
+#include <resolv.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
+static dn_expand_proto local_dn_expand = 0;
+typedef void (*res_nclose_proto)(res_state);
+static res_nclose_proto local_res_nclose = 0;
+typedef int (*res_ninit_proto)(res_state);
+static res_ninit_proto local_res_ninit = 0;
+typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
+static res_nquery_proto local_res_nquery = 0;
+
+// Custom deleter to close resolver state.
+
+struct QDnsLookupStateDeleter
+{
+ static inline void cleanup(struct __res_state *pointer)
+ {
+ local_res_nclose(pointer);
+ }
+};
+
+static void resolveLibrary()
+{
+ QLibrary lib(QLatin1String("resolv"));
+ if (!lib.load())
+ return;
+
+ local_dn_expand = dn_expand_proto(lib.resolve("__dn_expand"));
+ if (!local_dn_expand)
+ local_dn_expand = dn_expand_proto(lib.resolve("dn_expand"));
+
+ local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
+ if (!local_res_nclose)
+ local_res_nclose = res_nclose_proto(lib.resolve("res_9_nclose"));
+ if (!local_res_nclose)
+ local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
+
+ local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
+ if (!local_res_ninit)
+ local_res_ninit = res_ninit_proto(lib.resolve("res_9_ninit"));
+ if (!local_res_ninit)
+ local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
+
+ local_res_nquery = res_nquery_proto(lib.resolve("__res_nquery"));
+ if (!local_res_nquery)
+ local_res_nquery = res_nquery_proto(lib.resolve("res_9_nquery"));
+ if (!local_res_nquery)
+ local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
+}
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ // Load dn_expand, res_ninit and res_nquery on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_ninit));
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ // If dn_expand, res_ninit or res_nquery is missing, fail.
+ if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Resolver functions not found");
+ return;
+ }
+
+ // Initialize state.
+ struct __res_state state;
+ memset(&state, 0, sizeof(state));
+ if (local_res_ninit(&state) < 0) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Resolver initialization failed");
+ return;
+ }
+#ifdef QDNSLOOKUP_DEBUG
+ state.options |= RES_DEBUG;
+#endif
+ QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
+
+ // Perform DNS query.
+ unsigned char response[PACKETSZ];
+ memset(response, 0, sizeof(response));
+ const int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, response, sizeof(response));
+
+ // Check the response header.
+ HEADER *header = (HEADER*)response;
+ const int answerCount = ntohs(header->ancount);
+ switch (header->rcode) {
+ case NOERROR:
+ break;
+ case FORMERR:
+ reply->error = QDnsLookup::InvalidRequestError;
+ reply->errorString = tr("Server could not process query");
+ return;
+ case SERVFAIL:
+ reply->error = QDnsLookup::ServerFailureError;
+ reply->errorString = tr("Server failure");
+ return;
+ case NXDOMAIN:
+ reply->error = QDnsLookup::NotFoundError;
+ reply->errorString = tr("Non existent domain");
+ return;
+ case REFUSED:
+ reply->error = QDnsLookup::ServerRefusedError;
+ reply->errorString = tr("Server refused to answer");
+ return;
+ default:
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+
+ // Check the reply is valid.
+ if (responseLength < int(sizeof(HEADER))) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ char host[PACKETSZ], answer[PACKETSZ];
+ unsigned char *p = response + sizeof(HEADER);
+ int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Could not expand domain name");
+ return;
+ }
+ p += status + 4;
+
+ // Extract results.
+ int answerIndex = 0;
+ while ((p < response + responseLength) && (answerIndex < answerCount)) {
+ status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Could not expand domain name");
+ return;
+ }
+ const QString name = QUrl::fromAce(host);
+
+ p += status;
+ const quint16 type = (p[0] << 8) | p[1];
+ p += 2; // RR type
+ p += 2; // RR class
+ const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ const quint16 size = (p[0] << 8) | p[1];
+ p += 2;
+
+ if (type == QDnsLookup::A) {
+ if (size != 4) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid IPv4 address record");
+ return;
+ }
+ const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QHostAddress(addr);
+ reply->hostAddressRecords.append(record);
+ } else if (type == QDnsLookup::AAAA) {
+ if (size != 16) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid IPv6 address record");
+ return;
+ }
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QHostAddress(p);
+ reply->hostAddressRecords.append(record);
+ } else if (type == QDnsLookup::CNAME) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid canonical name record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->canonicalNameRecords.append(record);
+ } else if (type == QDnsLookup::NS) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid name server record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->nameServerRecords.append(record);
+ } else if (type == QDnsLookup::PTR) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid pointer record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->pointerRecords.append(record);
+ } else if (type == QDnsLookup::MX) {
+ const quint16 preference = (p[0] << 8) | p[1];
+ status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid mail exchange record");
+ return;
+ }
+ QDnsMailExchangeRecord record;
+ record.d->exchange = QUrl::fromAce(answer);
+ record.d->name = name;
+ record.d->preference = preference;
+ record.d->timeToLive = ttl;
+ reply->mailExchangeRecords.append(record);
+ } else if (type == QDnsLookup::SRV) {
+ const quint16 priority = (p[0] << 8) | p[1];
+ const quint16 weight = (p[2] << 8) | p[3];
+ const quint16 port = (p[4] << 8) | p[5];
+ status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid service record");
+ return;
+ }
+ QDnsServiceRecord record;
+ record.d->name = name;
+ record.d->target = QUrl::fromAce(answer);
+ record.d->port = port;
+ record.d->priority = priority;
+ record.d->timeToLive = ttl;
+ record.d->weight = weight;
+ reply->serviceRecords.append(record);
+ } else if (type == QDnsLookup::TXT) {
+ unsigned char *txt = p;
+ QDnsTextRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ while (txt < p + size) {
+ const unsigned char length = *txt;
+ txt++;
+ if (txt + length > p + size) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid text record");
+ return;
+ }
+ record.d->values << QByteArray((char*)txt, length);
+ txt += length;
+ }
+ reply->textRecords.append(record);
+ }
+ p += size;
+ answerIndex++;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
new file mode 100644
index 0000000000..97a82de43c
--- /dev/null
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <qurl.h>
+#include <private/qmutexpool_p.h>
+#include <private/qsystemlibrary_p.h>
+
+#include <windows.h>
+#include <windns.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef DNS_STATUS (*dns_query_utf8_proto)(PCSTR,WORD,DWORD,PIP4_ARRAY,PDNS_RECORD*,PVOID*);
+static dns_query_utf8_proto local_dns_query_utf8 = 0;
+typedef void (*dns_record_list_free_proto)(PDNS_RECORD,DNS_FREE_TYPE);
+static dns_record_list_free_proto local_dns_record_list_free = 0;
+
+static void resolveLibrary()
+{
+ local_dns_query_utf8 = (dns_query_utf8_proto) QSystemLibrary::resolve(QLatin1String("dnsapi"), "DnsQuery_UTF8");
+ local_dns_record_list_free = (dns_record_list_free_proto) QSystemLibrary::resolve(QLatin1String("dnsapi"), "DnsRecordListFree");
+}
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ // Load DnsQuery_UTF8 and DnsRecordListFree on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&local_dns_query_utf8));
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ // If DnsQuery_UTF8 or DnsRecordListFree is missing, fail.
+ if (!local_dns_query_utf8 || !local_dns_record_list_free) {
+ reply->error = QDnsLookup::ResolverError,
+ reply->errorString = tr("Resolver functions not found");
+ return;
+ }
+
+ // Perform DNS query.
+ PDNS_RECORD dns_records;
+ const DNS_STATUS status = local_dns_query_utf8(requestName, requestType, DNS_QUERY_STANDARD, NULL, &dns_records, NULL);
+ 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:
+ 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 = tr("Invalid reply received");
+ return;
+ }
+
+ // Extract results.
+ for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) {
+ const QString name = QUrl::fromAce((char*)ptr->pName);
+ 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((char*)ptr->Data.Cname.pNameHost);
+ reply->canonicalNameRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::MX) {
+ QDnsMailExchangeRecord record;
+ record.d->name = name;
+ record.d->exchange = QUrl::fromAce((char*)ptr->Data.Mx.pNameExchange);
+ 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((char*)ptr->Data.Ns.pNameHost);
+ 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((char*)ptr->Data.Ptr.pNameHost);
+ reply->pointerRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::SRV) {
+ QDnsServiceRecord record;
+ record.d->name = name;
+ record.d->target = QUrl::fromAce((char*)ptr->Data.Srv.pNameTarget);
+ 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 << QByteArray((char*)ptr->Data.Txt.pStringArray[i]);
+ }
+ reply->textRecords.append(record);
+ }
+ }
+
+ local_dns_record_list_free(dns_records, DnsFreeRecordList);
+}
+
+QT_END_NAMESPACE