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

Last change on this file since 609 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 16.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 <private/qunicodetables_p.h>
48#include <qcoreapplication.h>
49#include <qmetaobject.h>
50#include <qregexp.h>
51#include <private/qnativesocketengine_p.h>
52#include <qstringlist.h>
53#include <qthread.h>
54#include <qtimer.h>
55#include <qurl.h>
56
57#ifdef Q_OS_UNIX
58# include <unistd.h>
59#endif
60
61QT_BEGIN_NAMESPACE
62
63#ifndef QT_NO_THREAD
64Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
65#endif
66
67//#define QHOSTINFO_DEBUG
68
69/*!
70 \class QHostInfo
71 \brief The QHostInfo class provides static functions for host name lookups.
72
73 \reentrant
74 \inmodule QtNetwork
75 \ingroup network
76
77 QHostInfo uses the lookup mechanisms provided by the operating
78 system to find the IP address(es) associated with a host name,
79 or the host name associated with an IP address.
80 The class provides two static convenience functions: one that
81 works asynchronously and emits a signal once the host is found,
82 and one that blocks and returns a QHostInfo object.
83
84 To look up a host's IP addresses asynchronously, call lookupHost(),
85 which takes the host name or IP address, a receiver object, and a slot
86 signature as arguments and returns an ID. You can abort the
87 lookup by calling abortHostLookup() with the lookup ID.
88
89 Example:
90
91 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
92
93
94 The slot is invoked when the results are ready. (If you use
95 Qt for Embedded Linux and disabled multithreading support by defining
96 \c QT_NO_THREAD, lookupHost() will block until the lookup has
97 finished.) The results are stored in a QHostInfo object. Call
98 addresses() to get the list of IP addresses for the host, and
99 hostName() to get the host name that was looked up.
100
101 If the lookup failed, error() returns the type of error that
102 occurred. errorString() gives a human-readable description of the
103 lookup error.
104
105 If you want a blocking lookup, use the QHostInfo::fromName() function:
106
107 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1
108
109 QHostInfo supports Internationalized Domain Names (IDNs) through the
110 IDNA and Punycode standards.
111
112 To retrieve the name of the local host, use the static
113 QHostInfo::localHostName() function.
114
115 \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
116*/
117
118static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
119
120/*!
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.
126
127 The lookup is performed by a single function call, for example:
128
129 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2
130
131 The implementation of the slot prints basic information about the
132 addresses returned by the lookup, or reports an error if it failed:
133
134 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3
135
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
140 name. Example:
141
142 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
143
144 \note There is no guarantee on the order the signals will be emitted
145 if you start multiple requests with lookupHost().
146
147 \sa abortHostLookup(), addresses(), error(), fromName()
148*/
149int QHostInfo::lookupHost(const QString &name, QObject *receiver,
150 const char *member)
151{
152#if defined QHOSTINFO_DEBUG
153 qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
154 name.toLatin1().constData(), receiver, member ? member + 1 : 0);
155#endif
156 if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
157 qWarning("QHostInfo::lookupHost() called with no event dispatcher");
158 return -1;
159 }
160
161 qRegisterMetaType<QHostInfo>("QHostInfo");
162
163 int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
164
165 if (name.isEmpty()) {
166 QHostInfo hostInfo(id);
167 hostInfo.setError(QHostInfo::HostNotFound);
168 hostInfo.setErrorString(QObject::tr("No host name given"));
169 QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
170 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
171 receiver, member, Qt::QueuedConnection);
172 result.data()->emitResultsReady(hostInfo);
173 return id;
174 }
175
176#ifdef QT_NO_THREAD
177 QHostInfo hostInfo = QHostInfoAgent::fromName(name);
178 hostInfo.setLookupId(id);
179 QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
180 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
181 receiver, member, Qt::QueuedConnection);
182 result.data()->emitResultsReady(hostInfo);
183#else
184 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
185 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
186 theHostInfoLookupManager()->scheduleLookup(runnable);
187#endif
188
189 return id;
190}
191
192/*!
193 Aborts the host lookup with the ID \a id, as returned by lookupHost().
194
195 \sa lookupHost(), lookupId()
196*/
197void QHostInfo::abortHostLookup(int id)
198{
199#ifndef QT_NO_THREAD
200 theHostInfoLookupManager()->abortLookup(id);
201#else
202 // we cannot abort if it was non threaded.. the result signal has already been posted
203 Q_UNUSED(id);
204#endif
205}
206
207/*!
208 Looks up the IP address(es) for the given host \a name. The
209 function blocks during the lookup which means that execution of
210 the program is suspended until the results of the lookup are
211 ready. Returns the result of the lookup in a QHostInfo object.
212
213 If you pass a literal IP address to \a name instead of a host name,
214 QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
215 perform a \e reverse lookup). On success, the returned QHostInfo will
216 contain both the resolved domain name and IP addresses for the host name.
217
218 \sa lookupHost()
219*/
220QHostInfo QHostInfo::fromName(const QString &name)
221{
222#if defined QHOSTINFO_DEBUG
223 qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
224#endif
225
226 return QHostInfoAgent::fromName(name);
227}
228
229/*!
230 \enum QHostInfo::HostInfoError
231
232 This enum describes the various errors that can occur when trying
233 to resolve a host name.
234
235 \value NoError The lookup was successful.
236 \value HostNotFound No IP addresses were found for the host.
237 \value UnknownError An unknown error occurred.
238
239 \sa error(), setError()
240*/
241
242/*!
243 Constructs an empty host info object with lookup ID \a id.
244
245 \sa lookupId()
246*/
247QHostInfo::QHostInfo(int id)
248 : d(new QHostInfoPrivate)
249{
250 d->lookupId = id;
251}
252
253/*!
254 Constructs a copy of \a other.
255*/
256QHostInfo::QHostInfo(const QHostInfo &other)
257 : d(new QHostInfoPrivate(*other.d.data()))
258{
259}
260
261/*!
262 Assigns the data of the \a other object to this host info object,
263 and returns a reference to it.
264*/
265QHostInfo &QHostInfo::operator=(const QHostInfo &other)
266{
267 *d.data() = *other.d.data();
268 return *this;
269}
270
271/*!
272 Destroys the host info object.
273*/
274QHostInfo::~QHostInfo()
275{
276}
277
278/*!
279 Returns the list of IP addresses associated with hostName(). This
280 list may be empty.
281
282 Example:
283
284 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5
285
286 \sa hostName(), error()
287*/
288QList<QHostAddress> QHostInfo::addresses() const
289{
290 return d->addrs;
291}
292
293/*!
294 Sets the list of addresses in this QHostInfo to \a addresses.
295
296 \sa addresses()
297*/
298void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
299{
300 d->addrs = addresses;
301}
302
303/*!
304 Returns the name of the host whose IP addresses were looked up.
305
306 \sa localHostName()
307*/
308QString QHostInfo::hostName() const
309{
310 return d->hostName;
311}
312
313/*!
314 Sets the host name of this QHostInfo to \a hostName.
315
316 \sa hostName()
317*/
318void QHostInfo::setHostName(const QString &hostName)
319{
320 d->hostName = hostName;
321}
322
323/*!
324 Returns the type of error that occurred if the host name lookup
325 failed; otherwise returns NoError.
326
327 \sa setError(), errorString()
328*/
329QHostInfo::HostInfoError QHostInfo::error() const
330{
331 return d->err;
332}
333
334/*!
335 Sets the error type of this QHostInfo to \a error.
336
337 \sa error(), errorString()
338*/
339void QHostInfo::setError(HostInfoError error)
340{
341 d->err = error;
342}
343
344/*!
345 Returns the ID of this lookup.
346
347 \sa setLookupId(), abortHostLookup(), hostName()
348*/
349int QHostInfo::lookupId() const
350{
351 return d->lookupId;
352}
353
354/*!
355 Sets the ID of this lookup to \a id.
356
357 \sa lookupId(), lookupHost()
358*/
359void QHostInfo::setLookupId(int id)
360{
361 d->lookupId = id;
362}
363
364/*!
365 If the lookup failed, this function returns a human readable
366 description of the error; otherwise "Unknown error" is returned.
367
368 \sa setErrorString(), error()
369*/
370QString QHostInfo::errorString() const
371{
372 return d->errorStr;
373}
374
375/*!
376 Sets the human readable description of the error that occurred to \a str
377 if the lookup failed.
378
379 \sa errorString(), setError()
380*/
381void QHostInfo::setErrorString(const QString &str)
382{
383 d->errorStr = str;
384}
385
386/*!
387 \fn QString QHostInfo::localHostName()
388
389 Returns the host name of this machine.
390
391 \sa hostName()
392*/
393
394/*!
395 \fn QString QHostInfo::localDomainName()
396
397 Returns the DNS domain of this machine.
398
399 Note: DNS domains are not related to domain names found in
400 Windows networks.
401
402 \sa hostName()
403*/
404
405#ifndef QT_NO_THREAD
406QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
407{
408 setAutoDelete(true);
409}
410
411// the QHostInfoLookupManager will at some point call this via a QThreadPool
412void QHostInfoRunnable::run()
413{
414 QHostInfoLookupManager *manager = theHostInfoLookupManager();
415 // check aborted
416 if (manager->wasAborted(id)) {
417 manager->lookupFinished(this);
418 return;
419 }
420
421 // check cache
422 // FIXME
423
424 // if not in cache: OS lookup
425 QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
426
427 // save to cache
428 // FIXME
429
430 // check aborted again
431 if (manager->wasAborted(id)) {
432 manager->lookupFinished(this);
433 return;
434 }
435
436 // signal emission
437 hostInfo.setLookupId(id);
438 resultEmitter.emitResultsReady(hostInfo);
439
440 manager->lookupFinished(this);
441
442 // thread goes back to QThreadPool
443}
444
445QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
446{
447 moveToThread(QCoreApplicationPrivate::mainThread());
448 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
449}
450
451QHostInfoLookupManager::~QHostInfoLookupManager()
452{
453 wasDeleted = true;
454}
455
456void QHostInfoLookupManager::work()
457{
458 if (wasDeleted)
459 return;
460
461 // goals of this function:
462 // - launch new lookups via the thread pool
463 // - make sure only one lookup per host/IP is in progress
464
465 QMutexLocker locker(&mutex);
466
467 if (!finishedLookups.isEmpty()) {
468 // remove ID from aborted if it is in there
469 for (int i = 0; i < finishedLookups.length(); i++) {
470 abortedLookups.removeAll(finishedLookups.at(i)->id);
471 }
472
473 finishedLookups.clear();
474 }
475
476 if (!postponedLookups.isEmpty()) {
477 // try to start the postponed ones
478
479 QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
480 while (iterator.hasNext()) {
481 QHostInfoRunnable* postponed = iterator.next();
482
483 // check if none of the postponed hostnames is currently running
484 bool alreadyRunning = false;
485 for (int i = 0; i < currentLookups.length(); i++) {
486 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
487 alreadyRunning = true;
488 break;
489 }
490 }
491 if (!alreadyRunning) {
492 iterator.remove();
493 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
494 }
495 }
496 }
497
498 if (!scheduledLookups.isEmpty()) {
499 // try to start the new ones
500 QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
501 while (iterator.hasNext()) {
502 QHostInfoRunnable *scheduled = iterator.next();
503
504 // check if a lookup for this host is already running, then postpone
505 for (int i = 0; i < currentLookups.size(); i++) {
506 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
507 iterator.remove();
508 postponedLookups.append(scheduled);
509 scheduled = 0;
510 break;
511 }
512 }
513
514 if (scheduled && threadPool.tryStart(scheduled)) {
515 // runnable now running in new thread, track this in currentLookups
516 iterator.remove();
517 currentLookups.append(scheduled);
518 } else if (scheduled) {
519 // wanted to start, but could not because thread pool is busy
520 break;
521 } else {
522 // was postponed, continue iterating
523 continue;
524 }
525 };
526 }
527}
528
529// called by QHostInfo
530void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
531{
532 if (wasDeleted)
533 return;
534
535 QMutexLocker locker(&this->mutex);
536 scheduledLookups.enqueue(r);
537 work();
538}
539
540// called by QHostInfo
541void QHostInfoLookupManager::abortLookup(int id)
542{
543 if (wasDeleted)
544 return;
545
546 QMutexLocker locker(&this->mutex);
547 if (!abortedLookups.contains(id))
548 abortedLookups.append(id);
549}
550
551// called from QHostInfoRunnable
552bool QHostInfoLookupManager::wasAborted(int id)
553{
554 if (wasDeleted)
555 return true;
556
557 QMutexLocker locker(&this->mutex);
558 return abortedLookups.contains(id);
559}
560
561// called from QHostInfoRunnable
562void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
563{
564 if (wasDeleted)
565 return;
566
567 QMutexLocker locker(&this->mutex);
568 currentLookups.removeOne(r);
569 finishedLookups.append(r);
570 work();
571}
572
573#endif // QT_NO_THREAD
574
575QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.