1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qhostinfo.h"
43 #include "qhostinfo_p.h"
44 
45 #include "QtCore/qscopedpointer.h"
46 #include <qabstracteventdispatcher.h>
47 #include <qcoreapplication.h>
48 #include <qmetaobject.h>
49 #include <qstringlist.h>
50 #include <qthread.h>
51 #include <qurl.h>
52 #include <private/qnetworksession_p.h>
53 
54 #ifdef Q_OS_UNIX
55 #  include <unistd.h>
56 #endif
57 
58 QT_BEGIN_NAMESPACE
59 
60 //#define QHOSTINFO_DEBUG
61 
62 #ifndef Q_OS_SYMBIAN
63 Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
64 #else
65 Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager)
66 #endif
67 
68 /*!
69     \class QHostInfo
70     \brief The QHostInfo class provides static functions for host name lookups.
71 
72     \reentrant
73     \inmodule QtNetwork
74     \ingroup network
75 
76     QHostInfo uses the lookup mechanisms provided by the operating
77     system to find the IP address(es) associated with a host name,
78     or the host name associated with an IP address.
79     The class provides two static convenience functions: one that
80     works asynchronously and emits a signal once the host is found,
81     and one that blocks and returns a QHostInfo object.
82 
83     To look up a host's IP addresses asynchronously, call lookupHost(),
84     which takes the host name or IP address, a receiver object, and a slot
85     signature as arguments and returns an ID. You can abort the
86     lookup by calling abortHostLookup() with the lookup ID.
87 
88     Example:
89 
90     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
91 
92 
93     The slot is invoked when the results are ready. The results are
94     stored in a QHostInfo object. Call
95     addresses() to get the list of IP addresses for the host, and
96     hostName() to get the host name that was looked up.
97 
98     If the lookup failed, error() returns the type of error that
99     occurred. errorString() gives a human-readable description of the
100     lookup error.
101 
102     If you want a blocking lookup, use the QHostInfo::fromName() function:
103 
104     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1
105 
106     QHostInfo supports Internationalized Domain Names (IDNs) through the
107     IDNA and Punycode standards.
108 
109     To retrieve the name of the local host, use the static
110     QHostInfo::localHostName() function.
111 
112     \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
113     instead of one dedicated DNS thread. This improves performance,
114     but also changes the order of signal emissions when using lookupHost()
115     compared to previous versions of Qt.
116     \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
117     for performance improvements.
118 
119     \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
120 */
121 
122 static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
123 
124 /*!
125     Looks up the IP address(es) associated with host name \a name, and
126     returns an ID for the lookup. When the result of the lookup is
127     ready, the slot or signal \a member in \a receiver is called with
128     a QHostInfo argument. The QHostInfo object can then be inspected
129     to get the results of the lookup.
130 
131     The lookup is performed by a single function call, for example:
132 
133     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2
134 
135     The implementation of the slot prints basic information about the
136     addresses returned by the lookup, or reports an error if it failed:
137 
138     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3
139 
140     If you pass a literal IP address to \a name instead of a host name,
141     QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
142     perform a \e reverse lookup). On success, the resulting QHostInfo will
143     contain both the resolved domain name and IP addresses for the host
144     name. Example:
145 
146     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
147 
148     \note There is no guarantee on the order the signals will be emitted
149     if you start multiple requests with lookupHost().
150 
151     \sa abortHostLookup(), addresses(), error(), fromName()
152 */
lookupHost(const QString & name,QObject * receiver,const char * member)153 int QHostInfo::lookupHost(const QString &name, QObject *receiver,
154                           const char *member)
155 {
156 #if defined QHOSTINFO_DEBUG
157     qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
158            name.toLatin1().constData(), receiver, member ? member + 1 : 0);
159 #endif
160 
161     if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
162         qWarning("QHostInfo::lookupHost() called with no event dispatcher");
163         return -1;
164     }
165 
166     qRegisterMetaType<QHostInfo>("QHostInfo");
167 
168     int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
169 
170     if (name.isEmpty()) {
171         if (!receiver)
172             return -1;
173 
174         QHostInfo hostInfo(id);
175         hostInfo.setError(QHostInfo::HostNotFound);
176         hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
177         QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
178         QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
179                          receiver, member, Qt::QueuedConnection);
180         result.data()->emitResultsReady(hostInfo);
181         return id;
182     }
183 
184 #ifndef Q_OS_SYMBIAN
185     QHostInfoLookupManager *manager = theHostInfoLookupManager();
186 
187     if (manager) {
188         // the application is still alive
189         if (manager->cache.isEnabled()) {
190             // check cache first
191             bool valid = false;
192             QHostInfo info = manager->cache.get(name, &valid);
193             if (valid) {
194                 if (!receiver)
195                     return -1;
196 
197                 info.setLookupId(id);
198                 QHostInfoResult result;
199                 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
200                 result.emitResultsReady(info);
201                 return id;
202             }
203         }
204 
205         // cache is not enabled or it was not in the cache, do normal lookup
206         QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
207         if (receiver)
208             QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
209         manager->scheduleLookup(runnable);
210     }
211 #else
212     QSymbianHostInfoLookupManager *manager = theHostInfoLookupManager();
213 
214     if (manager) {
215         // the application is still alive
216         if (manager->cache.isEnabled()) {
217             // check cache first
218             bool valid = false;
219             QHostInfo info = manager->cache.get(name, &valid);
220             if (valid) {
221                 info.setLookupId(id);
222                 QHostInfoResult result;
223                 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
224                 result.emitResultsReady(info);
225                 return id;
226             }
227         }
228 
229         // cache is not enabled or it was not in the cache, do normal lookup
230 #ifndef QT_NO_BEARERMANAGEMENT
231         QSharedPointer<QNetworkSession> networkSession;
232         QVariant v(receiver->property("_q_networksession"));
233         if (v.isValid())
234             networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
235 #endif
236 
237         QSymbianHostResolver *symbianResolver = 0;
238         QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id, networkSession));
239         QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
240         manager->scheduleLookup(symbianResolver);
241     }
242 #endif
243 
244     return id;
245 }
246 
247 /*!
248     Aborts the host lookup with the ID \a id, as returned by lookupHost().
249 
250     \sa lookupHost(), lookupId()
251 */
abortHostLookup(int id)252 void QHostInfo::abortHostLookup(int id)
253 {
254     theHostInfoLookupManager()->abortLookup(id);
255 }
256 
257 /*!
258     Looks up the IP address(es) for the given host \a name. The
259     function blocks during the lookup which means that execution of
260     the program is suspended until the results of the lookup are
261     ready. Returns the result of the lookup in a QHostInfo object.
262 
263     If you pass a literal IP address to \a name instead of a host name,
264     QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
265     perform a \e reverse lookup). On success, the returned QHostInfo will
266     contain both the resolved domain name and IP addresses for the host name.
267 
268     \sa lookupHost()
269 */
fromName(const QString & name)270 QHostInfo QHostInfo::fromName(const QString &name)
271 {
272 #if defined QHOSTINFO_DEBUG
273     qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
274 #endif
275 
276     QHostInfo hostInfo = QHostInfoAgent::fromName(name);
277     QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
278     manager->cache.put(name, hostInfo);
279     return hostInfo;
280 }
281 
282 #ifndef QT_NO_BEARERMANAGEMENT
fromName(const QString & name,QSharedPointer<QNetworkSession> session)283 QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
284 {
285 #if defined QHOSTINFO_DEBUG
286     qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
287 #endif
288 
289     QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
290     QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
291     manager->cache.put(name, hostInfo);
292     return hostInfo;
293 }
294 #endif
295 
296 #ifndef Q_OS_SYMBIAN
297 #ifndef QT_NO_BEARERMANAGEMENT
298 // This function has a special implementation for symbian right now in qhostinfo_symbian.cpp but not on other OS.
fromName(const QString & hostName,QSharedPointer<QNetworkSession>)299 QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
300 {
301     return QHostInfoAgent::fromName(hostName);
302 }
303 #endif
304 #endif
305 
306 
307 /*!
308     \enum QHostInfo::HostInfoError
309 
310     This enum describes the various errors that can occur when trying
311     to resolve a host name.
312 
313     \value NoError The lookup was successful.
314     \value HostNotFound No IP addresses were found for the host.
315     \value UnknownError An unknown error occurred.
316 
317     \sa error(), setError()
318 */
319 
320 /*!
321     Constructs an empty host info object with lookup ID \a id.
322 
323     \sa lookupId()
324 */
QHostInfo(int id)325 QHostInfo::QHostInfo(int id)
326     : d(new QHostInfoPrivate)
327 {
328     d->lookupId = id;
329 }
330 
331 /*!
332     Constructs a copy of \a other.
333 */
QHostInfo(const QHostInfo & other)334 QHostInfo::QHostInfo(const QHostInfo &other)
335     : d(new QHostInfoPrivate(*other.d.data()))
336 {
337 }
338 
339 /*!
340     Assigns the data of the \a other object to this host info object,
341     and returns a reference to it.
342 */
operator =(const QHostInfo & other)343 QHostInfo &QHostInfo::operator=(const QHostInfo &other)
344 {
345     *d.data() = *other.d.data();
346     return *this;
347 }
348 
349 /*!
350     Destroys the host info object.
351 */
~QHostInfo()352 QHostInfo::~QHostInfo()
353 {
354 }
355 
356 /*!
357     Returns the list of IP addresses associated with hostName(). This
358     list may be empty.
359 
360     Example:
361 
362     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5
363 
364     \sa hostName(), error()
365 */
addresses() const366 QList<QHostAddress> QHostInfo::addresses() const
367 {
368     return d->addrs;
369 }
370 
371 /*!
372     Sets the list of addresses in this QHostInfo to \a addresses.
373 
374     \sa addresses()
375 */
setAddresses(const QList<QHostAddress> & addresses)376 void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
377 {
378     d->addrs = addresses;
379 }
380 
381 /*!
382     Returns the name of the host whose IP addresses were looked up.
383 
384     \sa localHostName()
385 */
hostName() const386 QString QHostInfo::hostName() const
387 {
388     return d->hostName;
389 }
390 
391 /*!
392     Sets the host name of this QHostInfo to \a hostName.
393 
394     \sa hostName()
395 */
setHostName(const QString & hostName)396 void QHostInfo::setHostName(const QString &hostName)
397 {
398     d->hostName = hostName;
399 }
400 
401 /*!
402     Returns the type of error that occurred if the host name lookup
403     failed; otherwise returns NoError.
404 
405     \sa setError(), errorString()
406 */
error() const407 QHostInfo::HostInfoError QHostInfo::error() const
408 {
409     return d->err;
410 }
411 
412 /*!
413     Sets the error type of this QHostInfo to \a error.
414 
415     \sa error(), errorString()
416 */
setError(HostInfoError error)417 void QHostInfo::setError(HostInfoError error)
418 {
419     d->err = error;
420 }
421 
422 /*!
423     Returns the ID of this lookup.
424 
425     \sa setLookupId(), abortHostLookup(), hostName()
426 */
lookupId() const427 int QHostInfo::lookupId() const
428 {
429     return d->lookupId;
430 }
431 
432 /*!
433     Sets the ID of this lookup to \a id.
434 
435     \sa lookupId(), lookupHost()
436 */
setLookupId(int id)437 void QHostInfo::setLookupId(int id)
438 {
439     d->lookupId = id;
440 }
441 
442 /*!
443     If the lookup failed, this function returns a human readable
444     description of the error; otherwise "Unknown error" is returned.
445 
446     \sa setErrorString(), error()
447 */
errorString() const448 QString QHostInfo::errorString() const
449 {
450     return d->errorStr;
451 }
452 
453 /*!
454     Sets the human readable description of the error that occurred to \a str
455     if the lookup failed.
456 
457     \sa errorString(), setError()
458 */
setErrorString(const QString & str)459 void QHostInfo::setErrorString(const QString &str)
460 {
461     d->errorStr = str;
462 }
463 
464 /*!
465     \fn QString QHostInfo::localHostName()
466 
467     Returns the host name of this machine.
468 
469     \sa hostName()
470 */
471 
472 /*!
473     \fn QString QHostInfo::localDomainName()
474 
475     Returns the DNS domain of this machine.
476 
477     Note: DNS domains are not related to domain names found in
478     Windows networks.
479 
480     \sa hostName()
481 */
482 
483 #ifndef Q_OS_SYMBIAN
QHostInfoRunnable(QString hn,int i)484 QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
485 {
486     setAutoDelete(true);
487 }
488 
489 // the QHostInfoLookupManager will at some point call this via a QThreadPool
run()490 void QHostInfoRunnable::run()
491 {
492     QHostInfoLookupManager *manager = theHostInfoLookupManager();
493     // check aborted
494     if (manager->wasAborted(id)) {
495         manager->lookupFinished(this);
496         return;
497     }
498 
499     QHostInfo hostInfo;
500 
501     // QHostInfo::lookupHost already checks the cache. However we need to check
502     // it here too because it might have been cache saved by another QHostInfoRunnable
503     // in the meanwhile while this QHostInfoRunnable was scheduled but not running
504     if (manager->cache.isEnabled()) {
505         // check the cache first
506         bool valid = false;
507         hostInfo = manager->cache.get(toBeLookedUp, &valid);
508         if (!valid) {
509             // not in cache, we need to do the lookup and store the result in the cache
510             hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
511             manager->cache.put(toBeLookedUp, hostInfo);
512         }
513     } else {
514         // cache is not enabled, just do the lookup and continue
515         hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
516     }
517 
518     // check aborted again
519     if (manager->wasAborted(id)) {
520         manager->lookupFinished(this);
521         return;
522     }
523 
524     // signal emission
525     hostInfo.setLookupId(id);
526     resultEmitter.emitResultsReady(hostInfo);
527 
528     // now also iterate through the postponed ones
529     {
530         QMutexLocker locker(&manager->mutex);
531         QMutableListIterator<QHostInfoRunnable*> iterator(manager->postponedLookups);
532         while (iterator.hasNext()) {
533             QHostInfoRunnable* postponed = iterator.next();
534             if (toBeLookedUp == postponed->toBeLookedUp) {
535                 // we can now emit
536                 iterator.remove();
537                 hostInfo.setLookupId(postponed->id);
538                 postponed->resultEmitter.emitResultsReady(hostInfo);
539                 delete postponed;
540             }
541         }
542     }
543 
544     manager->lookupFinished(this);
545 
546     // thread goes back to QThreadPool
547 }
548 
QHostInfoLookupManager()549 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
550 {
551     moveToThread(QCoreApplicationPrivate::mainThread());
552     connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
553     threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
554 }
555 
~QHostInfoLookupManager()556 QHostInfoLookupManager::~QHostInfoLookupManager()
557 {
558     wasDeleted = true;
559 
560     // don't qDeleteAll currentLookups, the QThreadPool has ownership
561     clear();
562 }
563 
clear()564 void QHostInfoLookupManager::clear()
565 {
566     {
567         QMutexLocker locker(&mutex);
568         qDeleteAll(postponedLookups);
569         qDeleteAll(scheduledLookups);
570         qDeleteAll(finishedLookups);
571         postponedLookups.clear();
572         scheduledLookups.clear();
573         finishedLookups.clear();
574     }
575 
576     threadPool.waitForDone();
577     cache.clear();
578 }
579 
work()580 void QHostInfoLookupManager::work()
581 {
582     if (wasDeleted)
583         return;
584 
585     // goals of this function:
586     //  - launch new lookups via the thread pool
587     //  - make sure only one lookup per host/IP is in progress
588 
589     QMutexLocker locker(&mutex);
590 
591     if (!finishedLookups.isEmpty()) {
592         // remove ID from aborted if it is in there
593         for (int i = 0; i < finishedLookups.length(); i++) {
594            abortedLookups.removeAll(finishedLookups.at(i)->id);
595         }
596 
597         finishedLookups.clear();
598     }
599 
600     if (!postponedLookups.isEmpty()) {
601         // try to start the postponed ones
602 
603         QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
604         while (iterator.hasNext()) {
605             QHostInfoRunnable* postponed = iterator.next();
606 
607             // check if none of the postponed hostnames is currently running
608             bool alreadyRunning = false;
609             for (int i = 0; i < currentLookups.length(); i++) {
610                 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
611                     alreadyRunning = true;
612                     break;
613                 }
614             }
615             if (!alreadyRunning) {
616                 iterator.remove();
617                 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
618             }
619         }
620     }
621 
622     if (!scheduledLookups.isEmpty()) {
623         // try to start the new ones
624         QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
625         while (iterator.hasNext()) {
626             QHostInfoRunnable *scheduled = iterator.next();
627 
628             // check if a lookup for this host is already running, then postpone
629             for (int i = 0; i < currentLookups.size(); i++) {
630                 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
631                     iterator.remove();
632                     postponedLookups.append(scheduled);
633                     scheduled = 0;
634                     break;
635                 }
636             }
637 
638             if (scheduled && currentLookups.size() < threadPool.maxThreadCount()) {
639                 // runnable now running in new thread, track this in currentLookups
640                 threadPool.start(scheduled);
641                 iterator.remove();
642                 currentLookups.append(scheduled);
643             } else {
644                 // was postponed, continue iterating
645                 continue;
646             }
647         };
648     }
649 }
650 
651 // called by QHostInfo
scheduleLookup(QHostInfoRunnable * r)652 void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
653 {
654     if (wasDeleted)
655         return;
656 
657     QMutexLocker locker(&this->mutex);
658     scheduledLookups.enqueue(r);
659     work();
660 }
661 
662 // called by QHostInfo
abortLookup(int id)663 void QHostInfoLookupManager::abortLookup(int id)
664 {
665     if (wasDeleted)
666         return;
667 
668     QMutexLocker locker(&this->mutex);
669 
670     // is postponed? delete and return
671     for (int i = 0; i < postponedLookups.length(); i++) {
672         if (postponedLookups.at(i)->id == id) {
673             delete postponedLookups.takeAt(i);
674             return;
675         }
676     }
677 
678     // is scheduled? delete and return
679     for (int i = 0; i < scheduledLookups.length(); i++) {
680         if (scheduledLookups.at(i)->id == id) {
681             delete scheduledLookups.takeAt(i);
682             return;
683         }
684     }
685 
686     if (!abortedLookups.contains(id))
687         abortedLookups.append(id);
688 }
689 
690 // called from QHostInfoRunnable
wasAborted(int id)691 bool QHostInfoLookupManager::wasAborted(int id)
692 {
693     if (wasDeleted)
694         return true;
695 
696     QMutexLocker locker(&this->mutex);
697     return abortedLookups.contains(id);
698 }
699 
700 // called from QHostInfoRunnable
lookupFinished(QHostInfoRunnable * r)701 void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
702 {
703     if (wasDeleted)
704         return;
705 
706     QMutexLocker locker(&this->mutex);
707     currentLookups.removeOne(r);
708     finishedLookups.append(r);
709     work();
710 }
711 #endif
712 
713 // This function returns immediately when we had a result in the cache, else it will later emit a signal
qt_qhostinfo_lookup(const QString & name,QObject * receiver,const char * member,bool * valid,int * id)714 QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
715 {
716     *valid = false;
717     *id = -1;
718 
719     // check cache
720     QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
721     if (manager && manager->cache.isEnabled()) {
722         QHostInfo info = manager->cache.get(name, valid);
723         if (*valid) {
724             return info;
725         }
726     }
727 
728     // was not in cache, trigger lookup
729     *id = QHostInfo::lookupHost(name, receiver, member);
730 
731     // return empty response, valid==false
732     return QHostInfo();
733 }
734 
qt_qhostinfo_clear_cache()735 void qt_qhostinfo_clear_cache()
736 {
737     QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
738     if (manager) {
739         manager->clear();
740     }
741 }
742 
qt_qhostinfo_enable_cache(bool e)743 void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
744 {
745     QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
746     if (manager) {
747         manager->cache.setEnabled(e);
748     }
749 }
750 
751 // cache for 60 seconds
752 // cache 128 items
QHostInfoCache()753 QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
754 {
755 #ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
756     enabled = false;
757 #endif
758 }
759 
isEnabled()760 bool QHostInfoCache::isEnabled()
761 {
762     return enabled;
763 }
764 
765 // this function is currently only used for the auto tests
766 // and not usable by public API
setEnabled(bool e)767 void QHostInfoCache::setEnabled(bool e)
768 {
769     enabled = e;
770 }
771 
772 
get(const QString & name,bool * valid)773 QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
774 {
775     QMutexLocker locker(&this->mutex);
776 
777     *valid = false;
778     if (cache.contains(name)) {
779         QHostInfoCacheElement *element = cache.object(name);
780         if (element->age.elapsed() < max_age*1000)
781             *valid = true;
782         return element->info;
783 
784         // FIXME idea:
785         // if too old but not expired, trigger a new lookup
786         // to freshen our cache
787     }
788 
789     return QHostInfo();
790 }
791 
put(const QString & name,const QHostInfo & info)792 void QHostInfoCache::put(const QString &name, const QHostInfo &info)
793 {
794     // if the lookup failed, don't cache
795     if (info.error() != QHostInfo::NoError)
796         return;
797 
798     QHostInfoCacheElement* element = new QHostInfoCacheElement();
799     element->info = info;
800     element->age = QElapsedTimer();
801     element->age.start();
802 
803     QMutexLocker locker(&this->mutex);
804     cache.insert(name, element); // cache will take ownership
805 }
806 
clear()807 void QHostInfoCache::clear()
808 {
809     QMutexLocker locker(&this->mutex);
810     cache.clear();
811 }
812 
globalInstance()813 QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
814 {
815     return theHostInfoLookupManager();
816 }
817 
818 QT_END_NAMESPACE
819