diff options
author | Sami Lempinen <sami.lempinen@nokia.com> | 2011-04-28 08:10:57 (GMT) |
---|---|---|
committer | Sami Lempinen <sami.lempinen@nokia.com> | 2011-04-28 08:10:57 (GMT) |
commit | 95de3f34d9dba4cd95f1f3d32b35c4a4d97e70d9 (patch) | |
tree | 7be35a9028b5c83b792190fb954127e9f558baf5 /src/network/kernel | |
parent | 9d6530b9774de482b0b3a29720f7f756e986f5c7 (diff) | |
parent | 8e615d9b07f6146b5cb6b56c4cd2e32376a8b429 (diff) | |
download | Qt-95de3f34d9dba4cd95f1f3d32b35c4a4d97e70d9.zip Qt-95de3f34d9dba4cd95f1f3d32b35c4a4d97e70d9.tar.gz Qt-95de3f34d9dba4cd95f1f3d32b35c4a4d97e70d9.tar.bz2 |
Merge remote-tracking branch 'qt/4.8'
Diffstat (limited to 'src/network/kernel')
-rw-r--r-- | src/network/kernel/kernel.pri | 2 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 84 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_p.h | 131 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_symbian.cpp | 600 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_unix.cpp | 13 | ||||
-rw-r--r-- | src/network/kernel/qnetworkinterface_symbian.cpp | 140 |
6 files changed, 892 insertions, 78 deletions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index bd3e6ec..bb98305 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -20,7 +20,7 @@ SOURCES += kernel/qauthenticator.cpp \ kernel/qnetworkproxy.cpp \ kernel/qnetworkinterface.cpp -symbian: SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_symbian.cpp +symbian: SOURCES += kernel/qhostinfo_symbian.cpp kernel/qnetworkinterface_symbian.cpp unix:!symbian: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 diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 5ec6041..c86f510 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -49,6 +49,7 @@ #include <qstringlist.h> #include <qthread.h> #include <qurl.h> +#include <private/qnetworksession_p.h> #ifdef Q_OS_UNIX # include <unistd.h> @@ -56,10 +57,14 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) - //#define QHOSTINFO_DEBUG +#ifndef Q_OS_SYMBIAN +Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) +#else +Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager) +#endif + /*! \class QHostInfo \brief The QHostInfo class provides static functions for host name lookups. @@ -152,6 +157,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)", name.toLatin1().constData(), receiver, member ? member + 1 : 0); #endif + if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { qWarning("QHostInfo::lookupHost() called with no event dispatcher"); return -1; @@ -172,7 +178,9 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, return id; } +#ifndef Q_OS_SYMBIAN QHostInfoLookupManager *manager = theHostInfoLookupManager(); + if (manager) { // the application is still alive if (manager->cache.isEnabled()) { @@ -187,11 +195,45 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, return id; } } + // cache is not enabled or it was not in the cache, do normal lookup QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); manager->scheduleLookup(runnable); } +#else + QSymbianHostInfoLookupManager *manager = theHostInfoLookupManager(); + + if (manager) { + // the application is still alive + if (manager->cache.isEnabled()) { + // check cache first + bool valid = false; + QHostInfo info = manager->cache.get(name, &valid); + if (valid) { + info.setLookupId(id); + QHostInfoResult result; + QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + result.emitResultsReady(info); + return id; + } + } + + // cache is not enabled or it was not in the cache, do normal lookup +#ifndef QT_NO_BEARERMANAGEMENT + QSharedPointer<QNetworkSession> networkSession; + QVariant v(receiver->property("_q_networksession")); + if (v.isValid()) + networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v); +#endif + + QSymbianHostResolver *symbianResolver = 0; + QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id, networkSession)); + QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + manager->scheduleLookup(symbianResolver); + } +#endif + return id; } @@ -225,10 +267,33 @@ QHostInfo QHostInfo::fromName(const QString &name) #endif QHostInfo hostInfo = QHostInfoAgent::fromName(name); - QHostInfoLookupManager *manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); + manager->cache.put(name, hostInfo); + return hostInfo; +} + +#ifndef QT_NO_BEARERMANAGEMENT +QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session) +{ +#if defined QHOSTINFO_DEBUG + qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data()); +#endif + + QHostInfo hostInfo = QHostInfoAgent::fromName(name, session); + QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); manager->cache.put(name, hostInfo); return hostInfo; } +#endif + +#ifndef Q_OS_SYMBIAN +// This function has a special implementation for symbian right now in qhostinfo_symbian.cpp but not on other OS. +QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>) +{ + return QHostInfoAgent::fromName(hostName); +} +#endif + /*! \enum QHostInfo::HostInfoError @@ -406,6 +471,7 @@ void QHostInfo::setErrorString(const QString &str) \sa hostName() */ +#ifndef Q_OS_SYMBIAN QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i) { setAutoDelete(true); @@ -632,6 +698,7 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r) finishedLookups.append(r); work(); } +#endif // This function returns immediately when we had a result in the cache, else it will later emit a signal QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id) @@ -640,7 +707,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *id = -1; // check cache - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager && manager->cache.isEnabled()) { QHostInfo info = manager->cache.get(name, valid); if (*valid) { @@ -657,7 +724,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char void qt_qhostinfo_clear_cache() { - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager) { manager->clear(); } @@ -665,7 +732,7 @@ void qt_qhostinfo_clear_cache() void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e) { - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager(); if (manager) { manager->cache.setEnabled(e); } @@ -733,4 +800,9 @@ void QHostInfoCache::clear() cache.clear(); } +QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance() +{ + return theHostInfoLookupManager(); +} + QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index b568ec2..8da0692 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -69,9 +69,19 @@ #include <QElapsedTimer> #include <QCache> +#include <QNetworkSession> +#include <QSharedPointer> + +#ifdef Q_OS_SYMBIAN +// Symbian Headers +#include <es_sock.h> +#include <in_sock.h> +#endif + QT_BEGIN_NAMESPACE + class QHostInfoResult : public QObject { Q_OBJECT @@ -91,6 +101,12 @@ class QHostInfoAgent : public QObject Q_OBJECT public: static QHostInfo fromName(const QString &hostName); + static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession); + +#ifdef Q_OS_SYMBIAN + static int lookupHost(const QString &name, QObject *receiver, const char *member); + static void abortHostLookup(int lookupId); +#endif }; class QHostInfoPrivate @@ -102,6 +118,10 @@ public: lookupId(0) { } +#ifndef QT_NO_BEARERMANAGEMENT + //not a public API yet + static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession); +#endif QHostInfo::HostInfoError err; QString errorStr; @@ -151,7 +171,25 @@ public: QHostInfoResult resultEmitter; }; -class QHostInfoLookupManager : public QObject + +class QAbstractHostInfoLookupManager : public QObject +{ + Q_OBJECT + +public: + ~QAbstractHostInfoLookupManager() {} + virtual void clear() = 0; + + QHostInfoCache cache; + +protected: + QAbstractHostInfoLookupManager() {} + static QAbstractHostInfoLookupManager* globalInstance(); + +}; + +#ifndef Q_OS_SYMBIAN +class QHostInfoLookupManager : public QAbstractHostInfoLookupManager { Q_OBJECT public: @@ -169,8 +207,6 @@ public: void lookupFinished(QHostInfoRunnable *r); bool wasAborted(int id); - QHostInfoCache cache; - friend class QHostInfoRunnable; protected: QList<QHostInfoRunnable*> currentLookups; // in progress @@ -189,6 +225,95 @@ private slots: void waitForThreadPoolDone() { threadPool.waitForDone(); } }; +#else + +class QSymbianHostResolver : public CActive +{ +public: + QSymbianHostResolver(const QString &hostName, int id, QSharedPointer<QNetworkSession> networkSession); + ~QSymbianHostResolver(); + + void requestHostLookup(); + void abortHostLookup(); + int id(); + + void returnResults(); + + QHostInfoResult resultEmitter; + +private: + void DoCancel(); + void RunL(); + void run(); + TInt RunError(TInt aError); + + void processNameResult(); + void nextNameResult(); + void processAddressResult(); + +private: + int iId; + + const QString iHostName; + QString iEncodedHostName; + TPtrC iHostNamePtr; + + RSocketServ& iSocketServ; + RHostResolver iHostResolver; + QSharedPointer<QNetworkSession> iNetworkSession; + + TNameEntry iNameResult; + TInetAddr IpAdd; + + QHostAddress iAddress; + + QHostInfo iResults; + + QList<QHostAddress> iHostAddresses; + + enum { + EIdle, + EGetByName, + EGetByAddress, + ECompleteFromCache, + EError + } iState; +}; + +class QSymbianHostInfoLookupManager : public QAbstractHostInfoLookupManager +{ + Q_OBJECT +public: + QSymbianHostInfoLookupManager(); + ~QSymbianHostInfoLookupManager(); + + static QSymbianHostInfoLookupManager* globalInstance(); + + int id(); + void clear(); + + // called from QHostInfo + void scheduleLookup(QSymbianHostResolver *r); + void abortLookup(int id); + + // called from QSymbianHostResolver + void lookupFinished(QSymbianHostResolver *r); + +private: + void runNextLookup(); + + // this is true for single threaded use, with multiple threads the max is ((number of threads) + KMaxConcurrentLookups - 1) + static const int KMaxConcurrentLookups = 5; + + QList<QSymbianHostResolver*> iCurrentLookups; + QList<QSymbianHostResolver*> iScheduledLookups; + + QMutex mutex; +}; +#endif + + + QT_END_NAMESPACE #endif // QHOSTINFO_P_H diff --git a/src/network/kernel/qhostinfo_symbian.cpp b/src/network/kernel/qhostinfo_symbian.cpp new file mode 100644 index 0000000..2a8de1d --- /dev/null +++ b/src/network/kernel/qhostinfo_symbian.cpp @@ -0,0 +1,600 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QHOSTINFO_DEBUG + +// Qt Headers +#include <QByteArray> +#include <QUrl> +#include <QList> + +#include "qplatformdefs.h" + +#include "qhostinfo_p.h" +#include <private/qcore_symbian_p.h> +#include <private/qsystemerror_p.h> +#include <private/qnetworksession_p.h> + +// Header does not exist in the S60 5.0 SDK +//#include <networking/dnd_err.h> +const TInt KErrDndNameNotFound = -5120; // Returned when no data found for GetByName +const TInt KErrDndAddrNotFound = -5121; // Returned when no data found for GetByAddr + +QT_BEGIN_NAMESPACE + +static void setError_helper(QHostInfo &info, TInt symbianError) +{ + switch (symbianError) { + case KErrDndNameNotFound: + case KErrDndAddrNotFound: + case KErrNotFound: + case KErrEof: + // various "no more results" error codes + info.setError(QHostInfo::HostNotFound); + info.setErrorString(QObject::tr("Host not found")); + break; + default: + // Unknown error + info.setError(QHostInfo::UnknownError); + info.setErrorString(QSystemError(symbianError, QSystemError::NativeError).toString()); + break; + } +} + +QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession) +{ + QHostInfo results; + + // Connect to ESOCK + RSocketServ socketServ(qt_symbianGetSocketServer()); + RHostResolver hostResolver; + + + int err; + if (networkSession) + err = QNetworkSessionPrivate::nativeOpenHostResolver(*networkSession, hostResolver, KAfInet, KProtocolInetUdp); + else + err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp); + if (err) { + setError_helper(results, err); + return results; + } + + TNameEntry nameResult; + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::fromName(%s) looking up...", + hostName.toLatin1().constData()); +#endif + + QHostAddress address; + if (address.setAddress(hostName)) { + // Reverse lookup +#if defined(QHOSTINFO_DEBUG) + qDebug("(reverse lookup)"); +#endif + TInetAddr IpAdd; + IpAdd.Input(qt_QString2TPtrC(hostName)); + + // Synchronous request. nameResult returns Host Name. + err = hostResolver.GetByAddress(IpAdd, nameResult); + if (err) { + //for behavioural compatibility with Qt 4.7 and unix/windows + //backends: don't report error, return ip address as host name + results.setHostName(address.toString()); + } else { + results.setHostName(qt_TDesC2QString(nameResult().iName)); + } + results.setAddresses(QList<QHostAddress>() << address); + return results; + } + + // IDN support + QByteArray aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? + QCoreApplication::translate("QHostInfoAgent", "No host name given") : + QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); + return results; + } + + + // Call RHostResolver::GetByAddress, and place all IPv4 addresses at the start and + // the IPv6 addresses at the end of the address list in results. + + // Synchronous request. + err = hostResolver.GetByName(qt_QString2TPtrC(QString::fromLatin1(aceHostname)), nameResult); + if (err) { + setError_helper(results, err); + return results; + } + + QList<QHostAddress> hostAddresses; + + TInetAddr hostAdd = nameResult().iAddr; + // 39 is the maximum length of an IPv6 address. + TBuf<39> ipAddr; + + // Fill ipAddr with the IP address from hostAdd + hostAdd.Output(ipAddr); + if (ipAddr.Length() > 0) + hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr))); + + // Check if there's more than one IP address linkd to this name + while (hostResolver.Next(nameResult) == KErrNone) { + hostAdd = nameResult().iAddr; + hostAdd.Output(ipAddr); + + // Ensure that record is valid (not an alias and with length greater than 0) + if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) { + hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr))); + } + } + + hostResolver.Close(); + + results.setAddresses(hostAddresses); + return results; +} + +QHostInfo QHostInfoAgent::fromName(const QString &hostName) +{ + // null shared pointer + QSharedPointer<QNetworkSession> networkSession; + return fromName(hostName, networkSession); +} + +QString QHostInfo::localHostName() +{ + // Connect to ESOCK + RSocketServ socketServ(qt_symbianGetSocketServer()); + RHostResolver hostResolver; + + // RConnection not required to get the host name + int err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp); + if (err) + return QString(); + + THostName hostName; + err = hostResolver.GetHostName(hostName); + if (err) + return QString(); + + hostResolver.Close(); + + return qt_TDesC2QString(hostName); +} + +QString QHostInfo::localDomainName() +{ + // This concept does not exist on Symbian OS because the device can be on + // multiple networks with multiple "local domain" names. + // For now, return a null string. + return QString(); +} + + +QSymbianHostResolver::QSymbianHostResolver(const QString &hostName, int identifier, QSharedPointer<QNetworkSession> networkSession) + : CActive(CActive::EPriorityStandard), iHostName(hostName), + iSocketServ(qt_symbianGetSocketServer()), iNetworkSession(networkSession), iResults(identifier) +{ + CActiveScheduler::Add(this); +} + +QSymbianHostResolver::~QSymbianHostResolver() +{ +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::~QSymbianHostResolver" << id(); +#endif + Cancel(); + iHostResolver.Close(); +} + +// Async equivalent to QHostInfoAgent::fromName() +void QSymbianHostResolver::requestHostLookup() +{ + +#if defined(QHOSTINFO_DEBUG) + qDebug("QSymbianHostResolver::requestHostLookup(%s) looking up... (id = %d)", + iHostName.toLatin1().constData(), id()); +#endif + + QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance(); + if (manager->cache.isEnabled()) { + //check if name has been put in the cache while this request was queued + bool valid; + QHostInfo cachedResult = manager->cache.get(iHostName, &valid); + if (valid) { +#if defined(QHOSTINFO_DEBUG) + qDebug("...found in cache"); +#endif + iResults = cachedResult; + iState = ECompleteFromCache; + SetActive(); + TRequestStatus* stat = &iStatus; + User::RequestComplete(stat, KErrNone); + return; + } + } + + int err; + if (iNetworkSession) { + err = QNetworkSessionPrivate::nativeOpenHostResolver(*iNetworkSession, iHostResolver, KAfInet, KProtocolInetUdp); +#if defined(QHOSTINFO_DEBUG) + qDebug("using resolver from session (err = %d)", err); +#endif + } else { + err = iHostResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp); +#if defined(QHOSTINFO_DEBUG) + qDebug("using default resolver (err = %d)", err); +#endif + } + if (err) { + setError_helper(iResults, err); + } else { + + if (iAddress.setAddress(iHostName)) { + // Reverse lookup + IpAdd.Input(qt_QString2TPtrC(iHostName)); + + // Asynchronous request. + iHostResolver.GetByAddress(IpAdd, iNameResult, iStatus); // <---- ASYNC + iState = EGetByAddress; + + } else { + + // IDN support + QByteArray aceHostname = QUrl::toAce(iHostName); + iResults.setHostName(iHostName); + if (aceHostname.isEmpty()) { + iResults.setError(QHostInfo::HostNotFound); + iResults.setErrorString(iHostName.isEmpty() ? + QCoreApplication::translate("QHostInfoAgent", "No host name given") : + QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); + + err = KErrArgument; + } else { + iEncodedHostName = QString::fromLatin1(aceHostname); + iHostNamePtr.Set(qt_QString2TPtrC(iEncodedHostName)); + + // Asynchronous request. + iHostResolver.GetByName(iHostNamePtr, iNameResult, iStatus); + iState = EGetByName; + } + } + } + SetActive(); + if (err) { + iHostResolver.Close(); + + //self complete so that RunL can inform manager without causing recursion + iState = EError; + TRequestStatus* stat = &iStatus; + User::RequestComplete(stat, err); + } +} + +void QSymbianHostResolver::abortHostLookup() +{ + if (resultEmitter.thread() == QThread::currentThread()) { +#ifdef QHOSTINFO_DEBUG + qDebug("QSymbianHostResolver::abortHostLookup - deleting %d", id()); +#endif + //normal case, abort from same thread it was started + delete this; //will cancel outstanding request + } else { +#ifdef QHOSTINFO_DEBUG + qDebug("QSymbianHostResolver::abortHostLookup - detaching %d", id()); +#endif + //abort from different thread, carry on but don't report the results + resultEmitter.disconnect(); + } +} + +void QSymbianHostResolver::DoCancel() +{ +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostResolver::DoCancel" << QThread::currentThreadId() << id() << (int)iState << this; +#endif + if (iState == EGetByAddress || iState == EGetByName) { + //these states have made an async request to host resolver + iHostResolver.Cancel(); + } else { + //for the self completing states there is nothing to cancel + Q_ASSERT(iState == EError || iState == ECompleteFromCache); + } +} + +void QSymbianHostResolver::RunL() +{ + QT_TRYCATCH_LEAVING(run()); +} + +void QSymbianHostResolver::run() +{ + switch (iState) { + case EGetByName: + processNameResult(); + break; + case EGetByAddress: + processAddressResult(); + break; + case ECompleteFromCache: + case EError: + returnResults(); + break; + default: + qWarning("QSymbianHostResolver internal error, bad state in run()"); + iResults.setError(QHostInfo::UnknownError); + iResults.setErrorString(QSystemError(KErrCorrupt,QSystemError::NativeError).toString()); + returnResults(); + } +} + +void QSymbianHostResolver::returnResults() +{ +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostResolver::returnResults" << iResults.error() << iResults.errorString(); + foreach (QHostAddress addr, iResults.addresses()) + qDebug() << addr; +#endif + iState = EIdle; + + QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance(); + if (manager->cache.isEnabled()) { + manager->cache.put(iHostName, iResults); + } + manager->lookupFinished(this); + + resultEmitter.emitResultsReady(iResults); + + delete this; +} + +TInt QSymbianHostResolver::RunError(TInt aError) +{ + QT_TRY { + iState = EIdle; + + QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance(); + manager->lookupFinished(this); + + setError_helper(iResults, aError); + + resultEmitter.emitResultsReady(iResults); + } + QT_CATCH(...) {} + + delete this; + + return KErrNone; +} + +void QSymbianHostResolver::processNameResult() +{ + if (iStatus.Int() == KErrNone) { + TInetAddr hostAdd = iNameResult().iAddr; + // 39 is the maximum length of an IPv6 address. + TBuf<39> ipAddr; + + hostAdd.Output(ipAddr); + + // Ensure that record is valid (not an alias and with length greater than 0) + if (!(iNameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) { + iHostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr))); + } + + iState = EGetByName; + iHostResolver.Next(iNameResult, iStatus); + SetActive(); + } + else { + // No more addresses, so return the results (or an error if there aren't any). +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostResolver::processNameResult with err=" << iStatus.Int() << "count=" << iHostAddresses.count(); +#endif + if (iHostAddresses.count() > 0) { + iResults.setAddresses(iHostAddresses); + } else { + iState = EError; + setError_helper(iResults, iStatus.Int()); + } + returnResults(); + } +} + +void QSymbianHostResolver::processAddressResult() +{ + TInt err = iStatus.Int(); + + if (err < 0) { + //For behavioural compatibility with Qt 4.7, don't report errors on reverse lookup, + //return the address as a string (same as unix/windows backends) + iResults.setHostName(iAddress.toString()); + } else { + iResults.setHostName(qt_TDesC2QString(iNameResult().iName)); + } + iResults.setAddresses(QList<QHostAddress>() << iAddress); + returnResults(); +} + + +int QSymbianHostResolver::id() +{ + return iResults.lookupId(); +} + +QSymbianHostInfoLookupManager::QSymbianHostInfoLookupManager() +{ +} + +QSymbianHostInfoLookupManager::~QSymbianHostInfoLookupManager() +{ +} + +void QSymbianHostInfoLookupManager::clear() +{ + QMutexLocker locker(&mutex); +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::clear" << QThread::currentThreadId(); +#endif + foreach (QSymbianHostResolver *hr, iCurrentLookups) + hr->abortHostLookup(); + iCurrentLookups.clear(); + qDeleteAll(iScheduledLookups); + cache.clear(); +} + +void QSymbianHostInfoLookupManager::lookupFinished(QSymbianHostResolver *r) +{ + QMutexLocker locker(&mutex); + +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::lookupFinished" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count(); +#endif + // remove finished lookup from array and destroy + TInt count = iCurrentLookups.count(); + for (TInt i = 0; i < count; i++) { + if (iCurrentLookups[i]->id() == r->id()) { + iCurrentLookups.removeAt(i); + break; + } + } + + runNextLookup(); +} + +void QSymbianHostInfoLookupManager::runNextLookup() +{ +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::runNextLookup" << QThread::currentThreadId() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count(); +#endif + // check to see if there are any scheduled lookups + for (int i=0; i<iScheduledLookups.count(); i++) { + QSymbianHostResolver* hostResolver = iScheduledLookups.at(i); + if (hostResolver->resultEmitter.thread() == QThread::currentThread()) { + // if so, move one to the current lookups and run it + iCurrentLookups.append(hostResolver); + iScheduledLookups.removeAt(i); + hostResolver->requestHostLookup(); + // if spare capacity, try to start another one + if (iCurrentLookups.count() >= KMaxConcurrentLookups) + break; + i--; //compensate for removeAt + } + } +} + +// called from QHostInfo +void QSymbianHostInfoLookupManager::scheduleLookup(QSymbianHostResolver* r) +{ + QMutexLocker locker(&mutex); + +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::scheduleLookup" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count(); +#endif + // Check to see if we have space on the current lookups pool. + bool defer = false; + if (iCurrentLookups.count() >= KMaxConcurrentLookups) { + // busy, defer unless there are no request in this thread + // at least one active request per thread with queued requests is needed + for (int i=0; i < iCurrentLookups.count();i++) { + if (iCurrentLookups.at(i)->resultEmitter.thread() == QThread::currentThread()) { + defer = true; + break; + } + } + } + if (defer) { + // If no, schedule for later. + iScheduledLookups.append(r); +#if defined(QHOSTINFO_DEBUG) + qDebug(" - scheduled"); +#endif + return; + } else { + // If yes, add it to the current lookups. + iCurrentLookups.append(r); + + // ... and trigger the async call. + r->requestHostLookup(); + } +} + +void QSymbianHostInfoLookupManager::abortLookup(int id) +{ + QMutexLocker locker(&mutex); + +#if defined(QHOSTINFO_DEBUG) + qDebug() << "QSymbianHostInfoLookupManager::abortLookup" << QThread::currentThreadId() << id << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count(); +#endif + int i = 0; + // Find the aborted lookup by ID. + // First in the current lookups. + for (i = 0; i < iCurrentLookups.count(); i++) { + if (id == iCurrentLookups[i]->id()) { + QSymbianHostResolver* r = iCurrentLookups.at(i); + iCurrentLookups.removeAt(i); + r->abortHostLookup(); + runNextLookup(); + return; + } + } + // Then in the scheduled lookups. + for (i = 0; i < iScheduledLookups.count(); i++) { + if (id == iScheduledLookups[i]->id()) { + QSymbianHostResolver* r = iScheduledLookups.at(i); + iScheduledLookups.removeAt(i); + delete r; + return; + } + } +} + +QSymbianHostInfoLookupManager* QSymbianHostInfoLookupManager::globalInstance() +{ + return static_cast<QSymbianHostInfoLookupManager*> + (QAbstractHostInfoLookupManager::globalInstance()); +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 22f6e0d..8fc6bf6 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -147,7 +147,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) if (address.setAddress(hostName)) { // Reverse lookup // Reverse lookups using getnameinfo are broken on darwin, use gethostbyaddr instead. -#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN) && !defined (Q_OS_SYMBIAN) +#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN) sockaddr_in sa4; #ifndef QT_NO_IPV6 sockaddr_in6 sa6; @@ -208,23 +208,12 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) #ifdef Q_ADDRCONFIG hints.ai_flags = Q_ADDRCONFIG; #endif -#ifdef Q_OS_SYMBIAN -# ifdef QHOSTINFO_DEBUG - qDebug() << "Setting flags: 'hints.ai_flags &= AI_V4MAPPED | AI_ALL'"; -# endif -#endif int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); # ifdef Q_ADDRCONFIG if (result == EAI_BADFLAGS) { // if the lookup failed with AI_ADDRCONFIG set, try again without it hints.ai_flags = 0; -#ifdef Q_OS_SYMBIAN -# ifdef QHOSTINFO_DEBUG - qDebug() << "Setting flags: 'hints.ai_flags &= AI_V4MAPPED | AI_ALL'"; -# endif - hints.ai_flags &= AI_V4MAPPED | AI_ALL; -#endif result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); } # endif diff --git a/src/network/kernel/qnetworkinterface_symbian.cpp b/src/network/kernel/qnetworkinterface_symbian.cpp index 8e5db3c..7767f54 100644 --- a/src/network/kernel/qnetworkinterface_symbian.cpp +++ b/src/network/kernel/qnetworkinterface_symbian.cpp @@ -67,22 +67,29 @@ static QNetworkInterface::InterfaceFlags convertFlags(const TSoInetInterfaceInfo return flags; } +//TODO: share this, at least QHostInfo needs to do the same thing +static QHostAddress qt_QHostAddressFromTInetAddr(const TInetAddr& addr) +{ + //TODO: do we want to call v4 mapped addresses v4 or v6 outside of this file? + if (addr.IsV4Mapped() || addr.Family() == KAfInet) { + //convert v4 host address + return QHostAddress(addr.Address()); + } else { + //convert v6 host address + return QHostAddress((quint8 *)(addr.Ip6Address().u.iAddr8)); + } +} + static QList<QNetworkInterfacePrivate *> interfaceListing() { TInt err(KErrNone); QList<QNetworkInterfacePrivate *> interfaces; - - // Connect to Native socket server - RSocketServ socketServ; - err = socketServ.Connect(); - if (err) - return interfaces; + QList<QHostAddress> addressesWithEstimatedNetmasks; // Open dummy socket for interface queries RSocket socket; - err = socket.Open(socketServ, _L("udp")); + err = socket.Open(qt_symbianGetSocketServer(), _L("udp")); if (err) { - socketServ.Close(); return interfaces; } @@ -90,7 +97,6 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() err = socket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); if (err) { socket.Close(); - socketServ.Close(); return interfaces; } @@ -98,8 +104,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() TPckgBuf<TSoInetInterfaceInfo> infoPckg; TSoInetInterfaceInfo &info = infoPckg(); while (socket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, infoPckg) == KErrNone) { - // Do not include IPv6 addresses because netmask and broadcast address cannot be determined correctly - if (info.iName != KNullDesC && info.iAddress.IsV4Mapped()) { + if (info.iName != KNullDesC) { TName address; QNetworkAddressEntry entry; QNetworkInterfacePrivate *iface = 0; @@ -121,40 +126,58 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() } // Get the address of the interface - info.iAddress.Output(address); - entry.setIp(QHostAddress(qt_TDesC2QString(address))); + entry.setIp(qt_QHostAddressFromTInetAddr(info.iAddress)); + +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "address is" << info.iAddress.Family() << entry.ip(); + qDebug() << "netmask is" << info.iNetMask.Family() << qt_QHostAddressFromTInetAddr( info.iNetMask ); +#endif // Get the interface netmask - // For some reason netmask is always 0.0.0.0 - // info.iNetMask.Output(address); - // entry.setNetmask( QHostAddress( qt_TDesC2QString( address ) ) ); - - // Workaround: Let Symbian determine netmask based on IP address class - // TODO: Works only for IPv4 - Task: 259128 Implement IPv6 support - TInetAddr netmask; - netmask.NetMask(info.iAddress); - netmask.Output(address); - entry.setNetmask(QHostAddress(qt_TDesC2QString(address))); - - // Get the interface broadcast address - if (iface->flags & QNetworkInterface::CanBroadcast) { - // For some reason broadcast address is always 0.0.0.0 - // info.iBrdAddr.Output(address); - // entry.setBroadcast( QHostAddress( qt_TDesC2QString( address ) ) ); - - // Workaround: Let Symbian determine broadcast address based on IP address - // TODO: Works only for IPv4 - Task: 259128 Implement IPv6 support - TInetAddr broadcast; - broadcast.NetBroadcast(info.iAddress); - broadcast.Output(address); - entry.setBroadcast(QHostAddress(qt_TDesC2QString(address))); + if (info.iNetMask.IsUnspecified()) { + // For some reason netmask is always 0.0.0.0 for IPv4 interfaces + // and loopback interfaces (which we statically know) + if (info.iAddress.IsV4Mapped()) { + if (info.iFeatures & KIfIsLoopback) { + entry.setPrefixLength(32); + } else { + // Workaround: Let Symbian determine netmask based on IP address class (IPv4 only API) + TInetAddr netmask; + netmask.NetMask(info.iAddress); + entry.setNetmask(QHostAddress(netmask.Address())); //binary convert v4 address + addressesWithEstimatedNetmasks << entry.ip(); +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "address class determined netmask" << entry.netmask(); +#endif + } + } else { + // For IPv6 interfaces + if (info.iFeatures & KIfIsLoopback) { + entry.setPrefixLength(128); + } else if (info.iNetMask.IsUnspecified()) { + //Don't see this error for IPv6, but try to handle it if it happens + entry.setPrefixLength(64); //most common +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "total guess netmask" << entry.netmask(); +#endif + addressesWithEstimatedNetmasks << entry.ip(); + } + } + } else { + //Expected code path for IPv6 non loopback interfaces (IPv4 could come here if symbian is fixed) + entry.setNetmask(qt_QHostAddressFromTInetAddr(info.iNetMask)); +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "reported netmask" << entry.netmask(); +#endif } + // broadcast address is determined from the netmask in postProcess() + // Add new entry to interface address entries iface->addressEntries << entry; #if defined(QNETWORKINTERFACE_DEBUG) - printf("\n Found network interface %s, interface flags:\n\ + qDebug("\n Found network interface %s, interface flags:\n\ IsUp = %d, IsRunning = %d, CanBroadcast = %d,\n\ IsLoopBack = %d, IsPointToPoint = %d, CanMulticast = %d, \n\ ip = %s, netmask = %s, broadcast = %s,\n\ @@ -168,15 +191,20 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() } } + // if we didn't have to guess any netmasks, then we're done. + if (addressesWithEstimatedNetmasks.isEmpty()) { + socket.Close(); + return interfaces; + } + // we will try to use routing info to detect more precisely - // netmask and then ::postProcess() should calculate + // estimated netmasks and then ::postProcess() should calculate // broadcast addresses // use dummy socket to start enumerating routes err = socket.SetOpt(KSoInetEnumRoutes, KSolInetRtCtrl); if (err) { socket.Close(); - socketServ.Close(); // return what we have // up to this moment return interfaces; @@ -185,16 +213,21 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() TSoInetRouteInfo routeInfo; TPckg<TSoInetRouteInfo> routeInfoPkg(routeInfo); while (socket.GetOpt(KSoInetNextRoute, KSolInetRtCtrl, routeInfoPkg) == KErrNone) { - TName address; - // get interface address - routeInfo.iIfAddr.Output(address); - QHostAddress ifAddr(qt_TDesC2QString(address)); + QHostAddress ifAddr(qt_QHostAddressFromTInetAddr(routeInfo.iIfAddr)); if (ifAddr.isNull()) continue; + if (!addressesWithEstimatedNetmasks.contains(ifAddr)) { +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "skipping route from" << ifAddr << "because it wasn't an estimated netmask"; +#endif + continue; + } - routeInfo.iDstAddr.Output(address); - QHostAddress destination(qt_TDesC2QString(address)); + QHostAddress destination(qt_QHostAddressFromTInetAddr(routeInfo.iDstAddr)); +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << "route from" << ifAddr << "to" << destination; +#endif if (destination.isNull() || destination != ifAddr) continue; @@ -205,17 +238,13 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() QNetworkAddressEntry entry = iface->addressEntries.at(eindex); if (entry.ip() != ifAddr) { continue; - } else if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol) { - // skip if not IPv4 address (e.g. IPv6) - // as results not reliable on Symbian - continue; - } else { - routeInfo.iNetMask.Output(address); - QHostAddress netmask(qt_TDesC2QString(address)); + } else if (!routeInfo.iNetMask.IsUnspecified()) { + //the route may also return 0.0.0.0 netmask, in which case don't use it. + QHostAddress netmask(qt_QHostAddressFromTInetAddr(routeInfo.iNetMask)); entry.setNetmask(netmask); - // NULL boradcast address for - // ::postProcess to have effect - entry.setBroadcast(QHostAddress()); +#if defined(QNETWORKINTERFACE_DEBUG) + qDebug() << " - route netmask" << routeInfo.iNetMask.Family() << netmask << " (using route determined netmask)"; +#endif iface->addressEntries.replace(eindex, entry); } } @@ -223,7 +252,6 @@ static QList<QNetworkInterfacePrivate *> interfaceListing() } socket.Close(); - socketServ.Close(); return interfaces; } |