KIO

hostinfo.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Roland Harnau <[email protected]>
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 <QSharedPointer>
18 #include <QThread>
19 #include <QTime>
20 #include <QTimer>
21 #include <QtConcurrentRun>
22 
23 #ifdef Q_OS_UNIX
24 #include <QFileInfo>
25 #include <arpa/nameser.h>
26 #include <netinet/in.h>
27 #include <resolv.h> // for _PATH_RESCONF
28 #ifndef _PATH_RESCONF
29 #define _PATH_RESCONF "/etc/resolv.conf"
30 #endif
31 #endif
32 
33 #define TTL 300
34 
35 namespace KIO
36 {
37 class HostInfoAgentPrivate : public QObject
38 {
39  Q_OBJECT
40 public:
41  explicit HostInfoAgentPrivate(int cacheSize = 100);
42  ~HostInfoAgentPrivate() override
43  {
44  }
45  void lookupHost(const QString &hostName, QObject *receiver, const char *member);
46  QHostInfo lookupCachedHostInfoFor(const QString &hostName);
47  void cacheLookup(const QHostInfo &);
48  void setCacheSize(int s)
49  {
50  dnsCache.setMaxCost(s);
51  }
52  void setTTL(int _ttl)
53  {
54  ttl = _ttl;
55  }
56 private Q_SLOTS:
57  void queryFinished(const QHostInfo &);
58 
59 private:
60  class Result;
61  class Query;
62 
63  QHash<QString, Query *> openQueries;
65  QDateTime resolvConfMTime;
66  int ttl;
67 };
68 
69 class HostInfoAgentPrivate::Result : public QObject
70 {
71  Q_OBJECT
72 Q_SIGNALS:
73  void result(const QHostInfo &);
74 
75 private:
76  friend class HostInfoAgentPrivate;
77 };
78 
79 class HostInfoAgentPrivate::Query : public QObject
80 {
81  Q_OBJECT
82 public:
83  Query()
84  : m_watcher()
85  , m_hostName()
86  {
87  connect(&m_watcher, &QFutureWatcher<QHostInfo>::finished, this, &Query::relayFinished);
88  }
89  void start(const QString &hostName)
90  {
91  m_hostName = hostName;
93  m_watcher.setFuture(future);
94  }
95  QString hostName() const
96  {
97  return m_hostName;
98  }
99 Q_SIGNALS:
100  void result(const QHostInfo &);
101 private Q_SLOTS:
102  void relayFinished()
103  {
104  Q_EMIT result(m_watcher.result());
105  }
106 
107 private:
108  QFutureWatcher<QHostInfo> m_watcher;
109  QString m_hostName;
110 };
111 
112 class NameLookupThreadRequest
113 {
114 public:
115  NameLookupThreadRequest(const QString &hostName)
116  : m_hostName(hostName)
117  {
118  }
119 
120  QSemaphore *semaphore()
121  {
122  return &m_semaphore;
123  }
124 
125  QHostInfo result() const
126  {
127  return m_hostInfo;
128  }
129 
130  void setResult(const QHostInfo &hostInfo)
131  {
132  m_hostInfo = hostInfo;
133  }
134 
135  QString hostName() const
136  {
137  return m_hostName;
138  }
139 
140  int lookupId() const
141  {
142  return m_lookupId;
143  }
144 
145  void setLookupId(int id)
146  {
147  m_lookupId = id;
148  }
149 
150 private:
151  Q_DISABLE_COPY(NameLookupThreadRequest)
152  QString m_hostName;
153  QSemaphore m_semaphore;
154  QHostInfo m_hostInfo;
155  int m_lookupId;
156 };
157 }
158 
159 Q_DECLARE_METATYPE(QSharedPointer<KIO::NameLookupThreadRequest>)
160 
161 namespace KIO
162 {
163 class NameLookUpThreadWorker : public QObject
164 {
165  Q_OBJECT
166 public Q_SLOTS:
168  {
169  const QString hostName = request->hostName();
170  const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo)));
171  request->setLookupId(lookupId);
172  m_lookups.insert(lookupId, request);
173  }
174 
175  void abortLookup(const QSharedPointer<KIO::NameLookupThreadRequest> &request)
176  {
177  QHostInfo::abortHostLookup(request->lookupId());
178  m_lookups.remove(request->lookupId());
179  }
180 
181  void lookupFinished(const QHostInfo &hostInfo)
182  {
183  QMap<int, QSharedPointer<NameLookupThreadRequest>>::iterator it = m_lookups.find(hostInfo.lookupId());
184  if (it != m_lookups.end()) {
185  (*it)->setResult(hostInfo);
186  (*it)->semaphore()->release();
187  m_lookups.erase(it);
188  }
189  }
190 
191 private:
193 };
194 
195 class NameLookUpThread : public QThread
196 {
197  Q_OBJECT
198 public:
199  NameLookUpThread()
200  : m_worker(nullptr)
201  {
202  qRegisterMetaType<QSharedPointer<NameLookupThreadRequest>>();
203  start();
204  }
205 
206  ~NameLookUpThread() override
207  {
208  quit();
209  wait();
210  }
211 
212  NameLookUpThreadWorker *worker()
213  {
214  return m_worker;
215  }
216 
217  QSemaphore *semaphore()
218  {
219  return &m_semaphore;
220  }
221 
222  void run() override
223  {
224  NameLookUpThreadWorker worker;
225  m_worker = &worker;
226  m_semaphore.release();
227  exec();
228  }
229 
230 private:
231  NameLookUpThreadWorker *m_worker;
232  QSemaphore m_semaphore;
233 };
234 }
235 
236 using namespace KIO;
237 
238 Q_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate)
239 Q_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread)
240 
241 void HostInfo::lookupHost(const QString &hostName, QObject *receiver, const char *member)
242 {
243  hostInfoAgentPrivate()->lookupHost(hostName, receiver, member);
244 }
245 
246 QHostInfo HostInfo::lookupHost(const QString &hostName, unsigned long timeout)
247 {
248  // Do not perform a reverse lookup here...
249  QHostAddress address(hostName);
250  QHostInfo hostInfo;
251  if (!address.isNull()) {
252  QList<QHostAddress> addressList;
253  addressList << address;
254  hostInfo.setAddresses(addressList);
255  return hostInfo;
256  }
257 
258  // Look up the name in the KIO/KHTML DNS cache...
259  hostInfo = HostInfo::lookupCachedHostInfoFor(hostName);
260  if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
261  return hostInfo;
262  }
263 
264  // Failing all of the above, do the lookup...
265  QSharedPointer<NameLookupThreadRequest> request = QSharedPointer<NameLookupThreadRequest>(new NameLookupThreadRequest(hostName));
266  nameLookUpThread()->semaphore()->acquire();
267  nameLookUpThread()->semaphore()->release();
268  QMetaObject::invokeMethod(nameLookUpThread()->worker(), "lookupHost", Qt::QueuedConnection, Q_ARG(QSharedPointer<KIO::NameLookupThreadRequest>, request));
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  QMetaObject::invokeMethod(nameLookUpThread()->worker(),
276  "abortLookup",
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 
295 void HostInfo::prefetchHost(const QString &hostName)
296 {
297  hostInfoAgentPrivate()->lookupHost(hostName, nullptr, nullptr);
298 }
299 
301 {
302  hostInfoAgentPrivate()->setCacheSize(s);
303 }
304 
305 void HostInfo::setTTL(int ttl)
306 {
307  hostInfoAgentPrivate()->setTTL(ttl);
308 }
309 
310 HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize)
311  : openQueries()
312  , dnsCache(cacheSize)
313  , ttl(TTL)
314 {
315  qRegisterMetaType<QHostInfo>();
316 }
317 
318 void HostInfoAgentPrivate::lookupHost(const QString &hostName, QObject *receiver, const char *member)
319 {
320 #ifdef _PATH_RESCONF
321  QFileInfo resolvConf(QFile::decodeName(_PATH_RESCONF));
322  QDateTime currentMTime = resolvConf.lastModified();
323  if (resolvConf.exists() && currentMTime != resolvConfMTime) {
324  // /etc/resolv.conf has been modified
325  // clear our cache
326  resolvConfMTime = currentMTime;
327  dnsCache.clear();
328  }
329 #endif
330 
331  if (QPair<QHostInfo, QTime> *info = dnsCache.object(hostName)) {
332  if (QTime::currentTime() <= info->second.addSecs(ttl)) {
333  Result result;
334  if (receiver) {
335  QObject::connect(&result, SIGNAL(result(QHostInfo)), receiver, member);
336  Q_EMIT result.result(info->first);
337  }
338  return;
339  }
340  dnsCache.remove(hostName);
341  }
342 
343  if (Query *query = openQueries.value(hostName)) {
344  if (receiver) {
345  connect(query, SIGNAL(result(QHostInfo)), receiver, member);
346  }
347  return;
348  }
349 
350  Query *query = new Query();
351  openQueries.insert(hostName, query);
352  connect(query, &Query::result, this, &HostInfoAgentPrivate::queryFinished);
353  if (receiver) {
354  connect(query, SIGNAL(result(QHostInfo)), receiver, member);
355  }
356  query->start(hostName);
357 }
358 
359 QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString &hostName)
360 {
361  QPair<QHostInfo, QTime> *info = dnsCache.object(hostName);
362  if (info && info->second.addSecs(ttl) >= QTime::currentTime()) {
363  return info->first;
364  }
365 
366  // not found in dnsCache
367  QHostInfo hostInfo;
369  return hostInfo;
370 }
371 
372 void HostInfoAgentPrivate::cacheLookup(const QHostInfo &info)
373 {
374  if (info.hostName().isEmpty()) {
375  return;
376  }
377 
378  if (info.error() != QHostInfo::NoError) {
379  return;
380  }
381 
382  dnsCache.insert(info.hostName(), new QPair<QHostInfo, QTime>(info, QTime::currentTime()));
383 }
384 
385 void HostInfoAgentPrivate::queryFinished(const QHostInfo &info)
386 {
387  Query *query = static_cast<Query *>(sender());
388  openQueries.remove(query->hostName());
389  if (info.error() == QHostInfo::NoError) {
390  dnsCache.insert(query->hostName(), new QPair<QHostInfo, QTime>(info, QTime::currentTime()));
391  }
392  query->deleteLater();
393 }
394 
395 #include "hostinfo.moc"
A namespace for KIO globals.
void setAddresses(const QList< QHostAddress > &addresses)
KIOCORE_EXPORT void lookupHost(const QString &hostName, QObject *receiver, const char *member)
Definition: hostinfo.cpp:241
int second() const const
Q_OBJECTQ_OBJECT
bool isEmpty() const const
KIOCORE_EXPORT void setTTL(int ttl)
Definition: hostinfo.cpp:305
QMap::iterator end()
QDateTime lastModified() const const
bool exists() const const
KIOCORE_EXPORT void cacheLookup(const QHostInfo &info)
Definition: hostinfo.cpp:290
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QHostInfo fromName(const QString &name)
QTime currentTime()
int lookupHost(const QString &name, QObject *receiver, const char *member)
KIOCORE_EXPORT QHostInfo lookupCachedHostInfoFor(const QString &hostName)
Definition: hostinfo.cpp:285
void release(int n)
KIOCORE_EXPORT void prefetchHost(const QString &hostName)
Definition: hostinfo.cpp:295
void abortHostLookup(int id)
Q_SLOTSQ_SLOTS
bool isNull() const const
QueuedConnection
QHostInfo::HostInfoError error() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QFuture< T > run(Function function,...)
KIOCORE_EXPORT void setCacheSize(int s)
Definition: hostinfo.cpp:300
int lookupId() const const
QMap::iterator find(const Key &key)
QString decodeName(const QByteArray &localFileName)
void setError(QHostInfo::HostInfoError error)
const QList< QKeySequence > & quit()
QString hostName() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Apr 13 2021 23:00:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.