source: trunk/src/network/kernel/qhostinfo.cpp@ 810

Last change on this file since 810 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

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