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

KDE's Doxygen guidelines are available online.