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

KDE's Doxygen guidelines are available online.