KIO
hostinfo.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright 2008 Roland Harnau <tau@gmx.eu> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Lesser General Public 00006 License as published by the Free Software Foundation; either 00007 version 2.1 of the License, or (at your option) version 3, or any 00008 later version accepted by the membership of KDE e.V. (or its 00009 successor approved by the membership of KDE e.V.), which shall 00010 act as a proxy defined in Section 6 of version 3 of the license. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License along with this library. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include "hostinfo_p.h" 00022 00023 #include <kglobal.h> 00024 #include <QtCore/QString> 00025 #include <QtCore/QHash> 00026 #include <QtCore/QCache> 00027 #include <QtCore/QMetaType> 00028 #include <QtCore/QTime> 00029 #include <QtCore/QTimer> 00030 #include <QtCore/QList> 00031 #include <QtCore/QPair> 00032 #include <QtCore/QThread> 00033 #include <QtCore/QFutureWatcher> 00034 #include <QtCore/QSemaphore> 00035 #include <QtCore/QSharedPointer> 00036 #include <QtCore/QtConcurrentRun> 00037 #include <QtNetwork/QHostInfo> 00038 #include "kdebug.h" 00039 00040 #ifdef Q_OS_UNIX 00041 # include <QtCore/QFileInfo> 00042 # include <netinet/in.h> 00043 # include <arpa/nameser.h> 00044 # include <resolv.h> // for _PATH_RESCONF 00045 # ifndef _PATH_RESCONF 00046 # define _PATH_RESCONF "/etc/resolv.conf" 00047 # endif 00048 #endif 00049 00050 #define TTL 300 00051 00052 static int dummyHostInfoMetaType = qRegisterMetaType<QHostInfo>("QHostInfo"); 00053 00054 namespace KIO 00055 { 00056 class HostInfoAgentPrivate : public QObject 00057 { 00058 Q_OBJECT 00059 public: 00060 HostInfoAgentPrivate(int cacheSize = 100); 00061 virtual ~HostInfoAgentPrivate() {}; 00062 void lookupHost(const QString& hostName, QObject* receiver, const char* member); 00063 QHostInfo lookupCachedHostInfoFor(const QString& hostName); 00064 void cacheLookup(const QHostInfo&); 00065 void setCacheSize(int s) { dnsCache.setMaxCost(s); } 00066 void setTTL(int _ttl) { ttl = _ttl; } 00067 private slots: 00068 void queryFinished(const QHostInfo&); 00069 private: 00070 class Result; 00071 class Query; 00072 00073 QHash<QString, Query*> openQueries; 00074 QCache<QString, QPair<QHostInfo, QTime> > dnsCache; 00075 time_t resolvConfMTime; 00076 int ttl; 00077 }; 00078 00079 class HostInfoAgentPrivate::Result : public QObject 00080 { 00081 Q_OBJECT 00082 signals: 00083 void result(QHostInfo); 00084 private: 00085 friend class HostInfoAgentPrivate; 00086 }; 00087 00088 class HostInfoAgentPrivate::Query : public QObject 00089 { 00090 Q_OBJECT 00091 public: 00092 Query(): m_watcher(), m_hostName() 00093 { 00094 connect(&m_watcher, SIGNAL(finished()), this, SLOT(relayFinished())); 00095 } 00096 void start(const QString& hostName) 00097 { 00098 m_hostName = hostName; 00099 QFuture<QHostInfo> future = QtConcurrent::run(&QHostInfo::fromName, hostName); 00100 m_watcher.setFuture(future); 00101 } 00102 QString hostName() const 00103 { 00104 return m_hostName; 00105 } 00106 signals: 00107 void result(QHostInfo); 00108 private slots: 00109 void relayFinished() 00110 { 00111 emit result(m_watcher.result()); 00112 } 00113 private: 00114 QFutureWatcher<QHostInfo> m_watcher; 00115 QString m_hostName; 00116 }; 00117 00118 class NameLookupThreadRequest 00119 { 00120 public: 00121 NameLookupThreadRequest(const QString& hostName) : m_hostName(hostName) 00122 { 00123 } 00124 00125 QSemaphore *semaphore() 00126 { 00127 return &m_semaphore; 00128 } 00129 00130 QHostInfo result() const 00131 { 00132 return m_hostInfo; 00133 } 00134 00135 void setResult(const QHostInfo& hostInfo) 00136 { 00137 m_hostInfo = hostInfo; 00138 } 00139 00140 QString hostName() const 00141 { 00142 return m_hostName; 00143 } 00144 00145 int lookupId() const 00146 { 00147 return m_lookupId; 00148 } 00149 00150 void setLookupId(int id) 00151 { 00152 m_lookupId = id; 00153 } 00154 00155 private: 00156 Q_DISABLE_COPY(NameLookupThreadRequest); 00157 QString m_hostName; 00158 QSemaphore m_semaphore; 00159 QHostInfo m_hostInfo; 00160 int m_lookupId; 00161 }; 00162 00163 class NameLookUpThreadWorker : public QObject 00164 { 00165 Q_OBJECT 00166 public slots: 00167 void lookupHost(const QSharedPointer<NameLookupThreadRequest>& request) 00168 { 00169 const QString hostName = request->hostName(); 00170 const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo))); 00171 request->setLookupId(lookupId); 00172 m_lookups.insert(lookupId, request); 00173 } 00174 00175 void abortLookup(const QSharedPointer<NameLookupThreadRequest>& request) 00176 { 00177 QHostInfo::abortHostLookup(request->lookupId()); 00178 m_lookups.remove(request->lookupId()); 00179 } 00180 00181 void lookupFinished(const QHostInfo &hostInfo) 00182 { 00183 QMap<int, QSharedPointer<NameLookupThreadRequest> >::iterator it = m_lookups.find(hostInfo.lookupId()); 00184 if (it != m_lookups.end()) { 00185 (*it)->setResult(hostInfo); 00186 (*it)->semaphore()->release(); 00187 m_lookups.erase(it); 00188 } 00189 } 00190 00191 private: 00192 QMap<int, QSharedPointer<NameLookupThreadRequest> > m_lookups; 00193 }; 00194 00195 class NameLookUpThread : public QThread 00196 { 00197 Q_OBJECT 00198 public: 00199 NameLookUpThread () : m_worker(0) 00200 { 00201 qRegisterMetaType< QSharedPointer<NameLookupThreadRequest> > ("QSharedPointer<NameLookupThreadRequest>"); 00202 start(); 00203 } 00204 00205 ~NameLookUpThread () 00206 { 00207 quit(); 00208 wait(); 00209 } 00210 00211 NameLookUpThreadWorker *worker() 00212 { 00213 return m_worker; 00214 } 00215 00216 QSemaphore *semaphore() 00217 { 00218 return &m_semaphore; 00219 } 00220 00221 void run() 00222 { 00223 NameLookUpThreadWorker worker; 00224 m_worker = &worker; 00225 m_semaphore.release(); 00226 exec(); 00227 } 00228 00229 private: 00230 NameLookUpThreadWorker *m_worker; 00231 QSemaphore m_semaphore; 00232 }; 00233 } 00234 00235 using namespace KIO; 00236 00237 K_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate) 00238 K_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread) 00239 00240 void HostInfo::lookupHost(const QString& hostName, QObject* receiver, 00241 const char* member) 00242 { 00243 hostInfoAgentPrivate->lookupHost(hostName, receiver, member); 00244 } 00245 00246 QHostInfo HostInfo::lookupHost(const QString& hostName, unsigned long timeout) 00247 { 00248 // Do not perform a reverse lookup here... 00249 QHostAddress address (hostName); 00250 QHostInfo hostInfo; 00251 if (!address.isNull()) { 00252 QList<QHostAddress> addressList; 00253 addressList << address; 00254 hostInfo.setAddresses(addressList); 00255 return hostInfo; 00256 } 00257 00258 // Look up the name in the KIO/KHTML DNS cache... 00259 hostInfo = HostInfo::lookupCachedHostInfoFor(hostName); 00260 if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) { 00261 return hostInfo; 00262 } 00263 00264 // Failing all of the above, do the lookup... 00265 QSharedPointer<NameLookupThreadRequest> request = QSharedPointer<NameLookupThreadRequest>(new NameLookupThreadRequest(hostName)); 00266 nameLookUpThread->semaphore()->acquire(); 00267 nameLookUpThread->semaphore()->release(); 00268 QMetaObject::invokeMethod(nameLookUpThread->worker(), "lookupHost", Qt::QueuedConnection, Q_ARG(QSharedPointer<NameLookupThreadRequest>, request)); 00269 if (request->semaphore()->tryAcquire(1, timeout)) { 00270 hostInfo = request->result(); 00271 if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) { 00272 HostInfo::cacheLookup(hostInfo); // cache the look up... 00273 } 00274 } else { 00275 QMetaObject::invokeMethod(nameLookUpThread->worker(), "abortLookup", Qt::QueuedConnection, Q_ARG(QSharedPointer<NameLookupThreadRequest>, request)); 00276 } 00277 00278 //kDebug(7022) << "Name look up succeeded for" << hostName; 00279 return hostInfo; 00280 } 00281 00282 QHostInfo HostInfo::lookupCachedHostInfoFor(const QString& hostName) 00283 { 00284 return hostInfoAgentPrivate->lookupCachedHostInfoFor(hostName); 00285 } 00286 00287 void HostInfo::cacheLookup(const QHostInfo& info) 00288 { 00289 hostInfoAgentPrivate->cacheLookup(info); 00290 } 00291 00292 void HostInfo::prefetchHost(const QString& hostName) 00293 { 00294 hostInfoAgentPrivate->lookupHost(hostName, 0, 0); 00295 } 00296 00297 void HostInfo::setCacheSize(int s) 00298 { 00299 hostInfoAgentPrivate->setCacheSize(s); 00300 } 00301 00302 void HostInfo::setTTL(int ttl) 00303 { 00304 hostInfoAgentPrivate->setTTL(ttl); 00305 } 00306 00307 HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize) 00308 : openQueries(), 00309 dnsCache(cacheSize), 00310 resolvConfMTime(0), 00311 ttl(TTL) 00312 {} 00313 00314 void HostInfoAgentPrivate::lookupHost(const QString& hostName, 00315 QObject* receiver, const char* member) 00316 { 00317 #ifdef _PATH_RESCONF 00318 QFileInfo resolvConf(QFile::decodeName(_PATH_RESCONF)); 00319 time_t currentMTime = resolvConf.lastModified().toTime_t(); 00320 if (resolvConf.exists() && currentMTime != resolvConfMTime) { 00321 // /etc/resolv.conf has been modified 00322 // clear our cache 00323 resolvConfMTime = currentMTime; 00324 dnsCache.clear(); 00325 } 00326 #endif 00327 00328 if (QPair<QHostInfo, QTime>* info = dnsCache.object(hostName)) { 00329 if (QTime::currentTime() <= info->second.addSecs(ttl)) { 00330 Result result; 00331 if (receiver) { 00332 QObject::connect(&result, SIGNAL(result(QHostInfo)),receiver, member); 00333 emit result.result(info->first); 00334 } 00335 return; 00336 } 00337 dnsCache.remove(hostName); 00338 } 00339 00340 if (Query* query = openQueries.value(hostName)) { 00341 if (receiver) { 00342 connect(query, SIGNAL(result(QHostInfo)), receiver, member); 00343 } 00344 return; 00345 } 00346 00347 Query* query = new Query(); 00348 openQueries.insert(hostName, query); 00349 connect(query, SIGNAL(result(QHostInfo)), this, SLOT(queryFinished(QHostInfo))); 00350 if (receiver) { 00351 connect(query, SIGNAL(result(QHostInfo)), receiver, member); 00352 } 00353 query->start(hostName); 00354 } 00355 00356 QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString& hostName) 00357 { 00358 QPair<QHostInfo, QTime>* info = dnsCache.object(hostName); 00359 if (info && info->second.addSecs(ttl) >= QTime::currentTime()) 00360 return info->first; 00361 00362 return QHostInfo(); 00363 } 00364 00365 void HostInfoAgentPrivate::cacheLookup(const QHostInfo& info) 00366 { 00367 if (info.hostName().isEmpty()) 00368 return; 00369 00370 if (info.error() != QHostInfo::NoError) 00371 return; 00372 00373 dnsCache.insert(info.hostName(), new QPair<QHostInfo, QTime>(info, QTime::currentTime())); 00374 } 00375 00376 void HostInfoAgentPrivate::queryFinished(const QHostInfo& info) 00377 { 00378 Query* query = static_cast<Query* >(sender()); 00379 openQueries.remove(query->hostName()); 00380 if (info.error() == QHostInfo::NoError) { 00381 dnsCache.insert(query->hostName(), 00382 new QPair<QHostInfo, QTime>(info, QTime::currentTime())); 00383 } 00384 query->deleteLater(); 00385 } 00386 00387 #include "hostinfo.moc"
KDE 4.7 API Reference