KIO

hostinfo.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Roland Harnau <tau@gmx.eu>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "hostinfo.h"
8
9#include <QCache>
10#include <QFutureWatcher>
11#include <QHash>
12#include <QHostInfo>
13#include <QList>
14#include <QMetaType>
15#include <QPair>
16#include <QSemaphore>
17#include <QThread>
18#include <QTime>
19#include <QTimer>
20#include <QtConcurrentRun>
21
22#ifdef Q_OS_UNIX
23#include <QFileInfo>
24#include <arpa/nameser.h>
25#include <netinet/in.h>
26#include <resolv.h> // for _PATH_RESCONF
27#ifndef _PATH_RESCONF
28#define _PATH_RESCONF "/etc/resolv.conf"
29#endif
30#endif
31
32static constexpr int TTL = 300;
33
34namespace KIO
35{
36class HostInfoAgentPrivate : public QObject
37{
39
40 class Query;
41
42public:
43 explicit HostInfoAgentPrivate(int cacheSize = 100);
44 ~HostInfoAgentPrivate() override
45 {
46 }
47 void lookupHost(const QString &hostName, QObject *receiver, const char *member);
48 QHostInfo lookupCachedHostInfoFor(const QString &hostName);
49 void cacheLookup(const QHostInfo &);
50private Q_SLOTS:
51 void queryFinished(const QHostInfo &info, Query *sender);
52
53private:
54 class Result;
55
56 QHash<QString, Query *> openQueries;
57 struct HostCacheInfo {
58 QHostInfo hostInfo;
59 QTime time;
60 };
62 QDateTime resolvConfMTime;
63};
64
65class HostInfoAgentPrivate::Result : public QObject
66{
69 void result(const QHostInfo &);
70
71private:
72 friend class HostInfoAgentPrivate;
73};
74
75class HostInfoAgentPrivate::Query : public QObject
76{
78public:
79 Query()
80 : m_watcher()
81 , m_hostName()
82 {
83 connect(&m_watcher, &QFutureWatcher<QHostInfo>::finished, this, &Query::relayFinished);
84 }
85 void start(const QString &hostName)
86 {
87 m_hostName = hostName;
89 m_watcher.setFuture(future);
90 }
91 QString hostName() const
92 {
93 return m_hostName;
94 }
96 void result(const QHostInfo &);
97private Q_SLOTS:
98 void relayFinished()
99 {
100 Q_EMIT result(m_watcher.result());
101 }
102
103private:
105 QString m_hostName;
106};
107
108class NameLookupThreadRequest
109{
110public:
111 NameLookupThreadRequest(const QString &hostName)
112 : m_hostName(hostName)
113 {
114 }
115
116 QSemaphore *semaphore()
117 {
118 return &m_semaphore;
119 }
120
121 QHostInfo result() const
122 {
123 return m_hostInfo;
124 }
125
126 void setResult(const QHostInfo &hostInfo)
127 {
128 m_hostInfo = hostInfo;
129 }
130
131 QString hostName() const
132 {
133 return m_hostName;
134 }
135
136 int lookupId() const
137 {
138 return m_lookupId;
139 }
140
141 void setLookupId(int id)
142 {
143 m_lookupId = id;
144 }
145
146private:
147 Q_DISABLE_COPY(NameLookupThreadRequest)
148 QString m_hostName;
149 QSemaphore m_semaphore;
150 QHostInfo m_hostInfo;
151 int m_lookupId;
152};
153}
154
155Q_DECLARE_METATYPE(std::shared_ptr<KIO::NameLookupThreadRequest>)
156
157namespace KIO
158{
159class NameLookUpThreadWorker : public QObject
160{
162public Q_SLOTS:
163 void lookupHost(const std::shared_ptr<KIO::NameLookupThreadRequest> &request)
164 {
165 const QString hostName = request->hostName();
166 const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo)));
167 request->setLookupId(lookupId);
168 m_lookups.insert(lookupId, request);
169 }
170
171 void abortLookup(const std::shared_ptr<KIO::NameLookupThreadRequest> &request)
172 {
173 QHostInfo::abortHostLookup(request->lookupId());
174 m_lookups.remove(request->lookupId());
175 }
176
177 void lookupFinished(const QHostInfo &hostInfo)
178 {
179 auto it = m_lookups.find(hostInfo.lookupId());
180 if (it != m_lookups.end()) {
181 (*it)->setResult(hostInfo);
182 (*it)->semaphore()->release();
183 m_lookups.erase(it);
184 }
185 }
186
187private:
189};
190
191class NameLookUpThread : public QThread
192{
194public:
195 NameLookUpThread()
196 : m_worker(nullptr)
197 {
198 qRegisterMetaType<std::shared_ptr<NameLookupThreadRequest>>();
199 start();
200 }
201
202 ~NameLookUpThread() override
203 {
204 quit();
205 wait();
206 }
207
208 NameLookUpThreadWorker *worker()
209 {
210 return m_worker;
211 }
212
213 QSemaphore *semaphore()
214 {
215 return &m_semaphore;
216 }
217
218 void run() override
219 {
220 NameLookUpThreadWorker worker;
221 m_worker = &worker;
222 m_semaphore.release();
223 exec();
224 }
225
226private:
227 NameLookUpThreadWorker *m_worker;
228 QSemaphore m_semaphore;
229};
230}
231
232using namespace KIO;
233
234Q_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate)
235Q_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread)
236
237void HostInfo::lookupHost(const QString &hostName, QObject *receiver, const char *member)
238{
239 hostInfoAgentPrivate()->lookupHost(hostName, receiver, member);
240}
241
242QHostInfo HostInfo::lookupHost(const QString &hostName, unsigned long timeout)
243{
244 // Do not perform a reverse lookup here...
245 QHostAddress address(hostName);
246 QHostInfo hostInfo;
247 if (!address.isNull()) {
248 QList<QHostAddress> addressList;
249 addressList << address;
250 hostInfo.setAddresses(addressList);
251 return hostInfo;
252 }
253
254 // Look up the name in the KIO DNS cache...
255 hostInfo = HostInfo::lookupCachedHostInfoFor(hostName);
256 if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
257 return hostInfo;
258 }
259
260 // Failing all of the above, do the lookup...
261 std::shared_ptr<NameLookupThreadRequest> request = std::make_shared<NameLookupThreadRequest>(hostName);
262 nameLookUpThread()->semaphore()->acquire();
263 nameLookUpThread()->semaphore()->release();
264 NameLookUpThreadWorker *worker = nameLookUpThread()->worker();
265 auto lookupFunc = [worker, request]() {
266 worker->lookupHost(request);
267 };
269 if (request->semaphore()->tryAcquire(1, timeout)) {
270 hostInfo = request->result();
271 if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
272 HostInfo::cacheLookup(hostInfo); // cache the look up...
273 }
274 } else {
275 auto abortFunc = [worker, request]() {
276 worker->abortLookup(request);
277 };
279 }
280
281 // qDebug() << "Name look up succeeded for" << hostName;
282 return hostInfo;
283}
284
286{
287 return hostInfoAgentPrivate()->lookupCachedHostInfoFor(hostName);
288}
289
291{
292 hostInfoAgentPrivate()->cacheLookup(info);
293}
294
295HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize)
296 : openQueries()
297 , dnsCache(cacheSize)
298{
299 qRegisterMetaType<QHostInfo>();
300}
301
302void HostInfoAgentPrivate::lookupHost(const QString &hostName, QObject *receiver, const char *member)
303{
304#ifdef _PATH_RESCONF
305 QFileInfo resolvConf(QFile::decodeName(_PATH_RESCONF));
306 QDateTime currentMTime = resolvConf.lastModified();
307 if (resolvConf.exists() && currentMTime != resolvConfMTime) {
308 // /etc/resolv.conf has been modified
309 // clear our cache
310 resolvConfMTime = currentMTime;
311 dnsCache.clear();
312 }
313#endif
314
315 if (HostCacheInfo *info = dnsCache.object(hostName)) {
316 if (QTime::currentTime() <= info->time.addSecs(TTL)) {
317 Result result;
318 if (receiver) {
319 QObject::connect(&result, SIGNAL(result(QHostInfo)), receiver, member);
320 Q_EMIT result.result(info->hostInfo);
321 }
322 return;
323 }
324 dnsCache.remove(hostName);
325 }
326
327 if (Query *query = openQueries.value(hostName)) {
328 if (receiver) {
329 connect(query, SIGNAL(result(QHostInfo)), receiver, member);
330 }
331 return;
332 }
333
334 Query *query = new Query();
335 openQueries.insert(hostName, query);
336 connect(query, &Query::result, this, [this, query](const QHostInfo &info) {
337 queryFinished(info, query);
338 });
339 if (receiver) {
340 connect(query, SIGNAL(result(QHostInfo)), receiver, member);
341 }
342 query->start(hostName);
343}
344
345QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString &hostName)
346{
347 HostCacheInfo *info = dnsCache.object(hostName);
348 if (info && info->time.addSecs(TTL) >= QTime::currentTime()) {
349 return info->hostInfo;
350 }
351
352 // not found in dnsCache
353 QHostInfo hostInfo;
355 return hostInfo;
356}
357
358void HostInfoAgentPrivate::cacheLookup(const QHostInfo &info)
359{
360 if (info.hostName().isEmpty()) {
361 return;
362 }
363
364 if (info.error() != QHostInfo::NoError) {
365 return;
366 }
367
368 dnsCache.insert(info.hostName(), new HostCacheInfo{info, QTime::currentTime()});
369}
370
371void HostInfoAgentPrivate::queryFinished(const QHostInfo &info, Query *sender)
372{
373 const auto host = sender->hostName();
374 openQueries.remove(host);
375 if (info.error() == QHostInfo::NoError) {
376 dnsCache.insert(host, new HostCacheInfo{info, QTime::currentTime()});
377 }
379}
380
381#include "hostinfo.moc"
Q_SCRIPTABLE Q_NOREPLY void start()
std::optional< QSqlQuery > query(const QString &queryStatement)
KIOCORE_EXPORT QHostInfo lookupCachedHostInfoFor(const QString &hostName)
Definition hostinfo.cpp:285
KIOCORE_EXPORT void cacheLookup(const QHostInfo &info)
Definition hostinfo.cpp:290
void lookupHost(const QString &hostName, QObject *receiver, const char *member)
Definition hostinfo.cpp:237
A namespace for KIO globals.
void clear()
bool insert(const Key &key, T *object, qsizetype cost)
T * object(const Key &key) const const
bool remove(const Key &key)
QString decodeName(const QByteArray &localFileName)
T result() const const
void setFuture(const QFuture< T > &future)
iterator insert(const Key &key, const T &value)
bool remove(const Key &key)
T value(const Key &key) const const
void abortHostLookup(int id)
HostInfoError error() const const
QHostInfo fromName(const QString &name)
QString hostName() const const
int lookupHost(const QString &name, Functor &&functor)
int lookupId() const const
void setAddresses(const QList< QHostAddress > &addresses)
void setError(HostInfoError error)
iterator end()
iterator erase(const_iterator first, const_iterator last)
iterator find(const Key &key)
iterator insert(const Key &key, const T &value)
size_type remove(const Key &key)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QObject * sender() const const
void release(int n)
bool isEmpty() const const
QueuedConnection
QFuture< T > run(Function function,...)
int exec()
void quit()
bool wait(QDeadlineTimer deadline)
QTime addSecs(int s) const const
QTime currentTime()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:53:29 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.