1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qhostinfo.h"
43 #include "qhostinfo_p.h"
45 #include "QtCore/qscopedpointer.h"
46 #include <qabstracteventdispatcher.h>
47 #include <qcoreapplication.h>
48 #include <qmetaobject.h>
49 #include <qstringlist.h>
52 #include <private/qnetworksession_p.h>
60 //#define QHOSTINFO_DEBUG
62 Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
66 \brief The QHostInfo class provides static functions for host name lookups.
72 QHostInfo uses the lookup mechanisms provided by the operating
73 system to find the IP address(es) associated with a host name,
74 or the host name associated with an IP address.
75 The class provides two static convenience functions: one that
76 works asynchronously and emits a signal once the host is found,
77 and one that blocks and returns a QHostInfo object.
79 To look up a host's IP addresses asynchronously, call lookupHost(),
80 which takes the host name or IP address, a receiver object, and a slot
81 signature as arguments and returns an ID. You can abort the
82 lookup by calling abortHostLookup() with the lookup ID.
86 \snippet code/src_network_kernel_qhostinfo.cpp 0
89 The slot is invoked when the results are ready. The results are
90 stored in a QHostInfo object. Call
91 addresses() to get the list of IP addresses for the host, and
92 hostName() to get the host name that was looked up.
94 If the lookup failed, error() returns the type of error that
95 occurred. errorString() gives a human-readable description of the
98 If you want a blocking lookup, use the QHostInfo::fromName() function:
100 \snippet code/src_network_kernel_qhostinfo.cpp 1
102 QHostInfo supports Internationalized Domain Names (IDNs) through the
103 IDNA and Punycode standards.
105 To retrieve the name of the local host, use the static
106 QHostInfo::localHostName() function.
108 \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
109 instead of one dedicated DNS thread. This improves performance,
110 but also changes the order of signal emissions when using lookupHost()
111 compared to previous versions of Qt.
112 \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
113 for performance improvements.
115 \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
118 static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
121 Looks up the IP address(es) associated with host name \a name, and
122 returns an ID for the lookup. When the result of the lookup is
123 ready, the slot or signal \a member in \a receiver is called with
124 a QHostInfo argument. The QHostInfo object can then be inspected
125 to get the results of the lookup.
127 The lookup is performed by a single function call, for example:
129 \snippet code/src_network_kernel_qhostinfo.cpp 2
131 The implementation of the slot prints basic information about the
132 addresses returned by the lookup, or reports an error if it failed:
134 \snippet code/src_network_kernel_qhostinfo.cpp 3
136 If you pass a literal IP address to \a name instead of a host name,
137 QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
138 perform a \e reverse lookup). On success, the resulting QHostInfo will
139 contain both the resolved domain name and IP addresses for the host
142 \snippet code/src_network_kernel_qhostinfo.cpp 4
144 \note There is no guarantee on the order the signals will be emitted
145 if you start multiple requests with lookupHost().
147 \sa abortHostLookup(), addresses(), error(), fromName()
149 int QHostInfo::lookupHost(const QString &name, QObject *receiver,
152 #if defined QHOSTINFO_DEBUG
153 qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
154 name.toLatin1().constData(), receiver, member ? member + 1 : 0);
157 if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
158 qWarning("QHostInfo::lookupHost() called with no event dispatcher");
162 qRegisterMetaType<QHostInfo>("QHostInfo");
164 int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
166 if (name.isEmpty()) {
167 QHostInfo hostInfo(id);
168 hostInfo.setError(QHostInfo::HostNotFound);
169 hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
170 QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
171 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
172 receiver, member, Qt::QueuedConnection);
173 result.data()->emitResultsReady(hostInfo);
177 QHostInfoLookupManager *manager = theHostInfoLookupManager();
180 // the application is still alive
181 if (manager->cache.isEnabled()) {
184 QHostInfo info = manager->cache.get(name, &valid);
186 info.setLookupId(id);
187 QHostInfoResult result;
188 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
189 result.emitResultsReady(info);
194 // cache is not enabled or it was not in the cache, do normal lookup
195 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
196 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
197 manager->scheduleLookup(runnable);
203 Aborts the host lookup with the ID \a id, as returned by lookupHost().
205 \sa lookupHost(), lookupId()
207 void QHostInfo::abortHostLookup(int id)
209 theHostInfoLookupManager()->abortLookup(id);
213 Looks up the IP address(es) for the given host \a name. The
214 function blocks during the lookup which means that execution of
215 the program is suspended until the results of the lookup are
216 ready. Returns the result of the lookup in a QHostInfo object.
218 If you pass a literal IP address to \a name instead of a host name,
219 QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
220 perform a \e reverse lookup). On success, the returned QHostInfo will
221 contain both the resolved domain name and IP addresses for the host name.
225 QHostInfo QHostInfo::fromName(const QString &name)
227 #if defined QHOSTINFO_DEBUG
228 qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
231 QHostInfo hostInfo = QHostInfoAgent::fromName(name);
232 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
233 manager->cache.put(name, hostInfo);
237 #ifndef QT_NO_BEARERMANAGEMENT
238 QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
240 #if defined QHOSTINFO_DEBUG
241 qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
244 QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
245 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
246 manager->cache.put(name, hostInfo);
251 #ifndef QT_NO_BEARERMANAGEMENT
252 QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
254 return QHostInfoAgent::fromName(hostName);
260 \enum QHostInfo::HostInfoError
262 This enum describes the various errors that can occur when trying
263 to resolve a host name.
265 \value NoError The lookup was successful.
266 \value HostNotFound No IP addresses were found for the host.
267 \value UnknownError An unknown error occurred.
269 \sa error(), setError()
273 Constructs an empty host info object with lookup ID \a id.
277 QHostInfo::QHostInfo(int id)
278 : d(new QHostInfoPrivate)
284 Constructs a copy of \a other.
286 QHostInfo::QHostInfo(const QHostInfo &other)
287 : d(new QHostInfoPrivate(*other.d.data()))
292 Assigns the data of the \a other object to this host info object,
293 and returns a reference to it.
295 QHostInfo &QHostInfo::operator=(const QHostInfo &other)
297 *d.data() = *other.d.data();
302 Destroys the host info object.
304 QHostInfo::~QHostInfo()
309 Returns the list of IP addresses associated with hostName(). This
314 \snippet code/src_network_kernel_qhostinfo.cpp 5
316 \sa hostName(), error()
318 QList<QHostAddress> QHostInfo::addresses() const
324 Sets the list of addresses in this QHostInfo to \a addresses.
328 void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
330 d->addrs = addresses;
334 Returns the name of the host whose IP addresses were looked up.
338 QString QHostInfo::hostName() const
344 Sets the host name of this QHostInfo to \a hostName.
348 void QHostInfo::setHostName(const QString &hostName)
350 d->hostName = hostName;
354 Returns the type of error that occurred if the host name lookup
355 failed; otherwise returns NoError.
357 \sa setError(), errorString()
359 QHostInfo::HostInfoError QHostInfo::error() const
365 Sets the error type of this QHostInfo to \a error.
367 \sa error(), errorString()
369 void QHostInfo::setError(HostInfoError error)
375 Returns the ID of this lookup.
377 \sa setLookupId(), abortHostLookup(), hostName()
379 int QHostInfo::lookupId() const
385 Sets the ID of this lookup to \a id.
387 \sa lookupId(), lookupHost()
389 void QHostInfo::setLookupId(int id)
395 If the lookup failed, this function returns a human readable
396 description of the error; otherwise "Unknown error" is returned.
398 \sa setErrorString(), error()
400 QString QHostInfo::errorString() const
406 Sets the human readable description of the error that occurred to \a str
407 if the lookup failed.
409 \sa errorString(), setError()
411 void QHostInfo::setErrorString(const QString &str)
417 \fn QString QHostInfo::localHostName()
419 Returns the host name of this machine.
425 \fn QString QHostInfo::localDomainName()
427 Returns the DNS domain of this machine.
429 Note: DNS domains are not related to domain names found in
435 QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
440 // the QHostInfoLookupManager will at some point call this via a QThreadPool
441 void QHostInfoRunnable::run()
443 QHostInfoLookupManager *manager = theHostInfoLookupManager();
445 if (manager->wasAborted(id)) {
446 manager->lookupFinished(this);
452 // QHostInfo::lookupHost already checks the cache. However we need to check
453 // it here too because it might have been cache saved by another QHostInfoRunnable
454 // in the meanwhile while this QHostInfoRunnable was scheduled but not running
455 if (manager->cache.isEnabled()) {
456 // check the cache first
458 hostInfo = manager->cache.get(toBeLookedUp, &valid);
460 // not in cache, we need to do the lookup and store the result in the cache
461 hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
462 manager->cache.put(toBeLookedUp, hostInfo);
465 // cache is not enabled, just do the lookup and continue
466 hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
469 // check aborted again
470 if (manager->wasAborted(id)) {
471 manager->lookupFinished(this);
476 hostInfo.setLookupId(id);
477 resultEmitter.emitResultsReady(hostInfo);
479 // now also iterate through the postponed ones
481 QMutexLocker locker(&manager->mutex);
482 QMutableListIterator<QHostInfoRunnable*> iterator(manager->postponedLookups);
483 while (iterator.hasNext()) {
484 QHostInfoRunnable* postponed = iterator.next();
485 if (toBeLookedUp == postponed->toBeLookedUp) {
488 hostInfo.setLookupId(postponed->id);
489 postponed->resultEmitter.emitResultsReady(hostInfo);
495 manager->lookupFinished(this);
497 // thread goes back to QThreadPool
500 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
502 moveToThread(QCoreApplicationPrivate::mainThread());
503 connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
504 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
507 QHostInfoLookupManager::~QHostInfoLookupManager()
511 // don't qDeleteAll currentLookups, the QThreadPool has ownership
515 void QHostInfoLookupManager::clear()
518 QMutexLocker locker(&mutex);
519 qDeleteAll(postponedLookups);
520 qDeleteAll(scheduledLookups);
521 qDeleteAll(finishedLookups);
522 postponedLookups.clear();
523 scheduledLookups.clear();
524 finishedLookups.clear();
527 threadPool.waitForDone();
531 void QHostInfoLookupManager::work()
536 // goals of this function:
537 // - launch new lookups via the thread pool
538 // - make sure only one lookup per host/IP is in progress
540 QMutexLocker locker(&mutex);
542 if (!finishedLookups.isEmpty()) {
543 // remove ID from aborted if it is in there
544 for (int i = 0; i < finishedLookups.length(); i++) {
545 abortedLookups.removeAll(finishedLookups.at(i)->id);
548 finishedLookups.clear();
551 if (!postponedLookups.isEmpty()) {
552 // try to start the postponed ones
554 QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
555 while (iterator.hasNext()) {
556 QHostInfoRunnable* postponed = iterator.next();
558 // check if none of the postponed hostnames is currently running
559 bool alreadyRunning = false;
560 for (int i = 0; i < currentLookups.length(); i++) {
561 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
562 alreadyRunning = true;
566 if (!alreadyRunning) {
568 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
573 if (!scheduledLookups.isEmpty()) {
574 // try to start the new ones
575 QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
576 while (iterator.hasNext()) {
577 QHostInfoRunnable *scheduled = iterator.next();
579 // check if a lookup for this host is already running, then postpone
580 for (int i = 0; i < currentLookups.size(); i++) {
581 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
583 postponedLookups.append(scheduled);
589 if (scheduled && currentLookups.size() < threadPool.maxThreadCount()) {
590 // runnable now running in new thread, track this in currentLookups
591 threadPool.start(scheduled);
593 currentLookups.append(scheduled);
595 // was postponed, continue iterating
602 // called by QHostInfo
603 void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
608 QMutexLocker locker(&this->mutex);
609 scheduledLookups.enqueue(r);
613 // called by QHostInfo
614 void QHostInfoLookupManager::abortLookup(int id)
619 QMutexLocker locker(&this->mutex);
621 // is postponed? delete and return
622 for (int i = 0; i < postponedLookups.length(); i++) {
623 if (postponedLookups.at(i)->id == id) {
624 delete postponedLookups.takeAt(i);
629 // is scheduled? delete and return
630 for (int i = 0; i < scheduledLookups.length(); i++) {
631 if (scheduledLookups.at(i)->id == id) {
632 delete scheduledLookups.takeAt(i);
637 if (!abortedLookups.contains(id))
638 abortedLookups.append(id);
641 // called from QHostInfoRunnable
642 bool QHostInfoLookupManager::wasAborted(int id)
647 QMutexLocker locker(&this->mutex);
648 return abortedLookups.contains(id);
651 // called from QHostInfoRunnable
652 void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
657 QMutexLocker locker(&this->mutex);
658 currentLookups.removeOne(r);
659 finishedLookups.append(r);
663 // This function returns immediately when we had a result in the cache, else it will later emit a signal
664 QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
670 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
671 if (manager && manager->cache.isEnabled()) {
672 QHostInfo info = manager->cache.get(name, valid);
678 // was not in cache, trigger lookup
679 *id = QHostInfo::lookupHost(name, receiver, member);
681 // return empty response, valid==false
685 void qt_qhostinfo_clear_cache()
687 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
693 void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
695 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
697 manager->cache.setEnabled(e);
701 // cache for 60 seconds
703 QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64)
705 #ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
710 bool QHostInfoCache::isEnabled()
715 // this function is currently only used for the auto tests
716 // and not usable by public API
717 void QHostInfoCache::setEnabled(bool e)
723 QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
725 QMutexLocker locker(&this->mutex);
728 if (cache.contains(name)) {
729 QHostInfoCacheElement *element = cache.object(name);
730 if (element->age.elapsed() < max_age*1000)
732 return element->info;
735 // if too old but not expired, trigger a new lookup
736 // to freshen our cache
742 void QHostInfoCache::put(const QString &name, const QHostInfo &info)
744 // if the lookup failed, don't cache
745 if (info.error() != QHostInfo::NoError)
748 QHostInfoCacheElement* element = new QHostInfoCacheElement();
749 element->info = info;
750 element->age = QElapsedTimer();
751 element->age.start();
753 QMutexLocker locker(&this->mutex);
754 cache.insert(name, element); // cache will take ownership
757 void QHostInfoCache::clear()
759 QMutexLocker locker(&this->mutex);
763 QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
765 return theHostInfoLookupManager();