~ubuntu-branches/ubuntu/jaunty/psi/jaunty

« back to all changes in this revision

Viewing changes to cutestuff/network/ndns.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ndns.cpp - native DNS resolution
 
3
 * Copyright (C) 2001, 2002  Justin Karneges
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU Lesser General Public
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2.1 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Lesser General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Lesser General Public
 
16
 * License along with this library; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 *
 
19
 */
 
20
 
 
21
//! \class NDns ndns.h
 
22
//! \brief Simple DNS resolution using native system calls
 
23
//!
 
24
//! This class is to be used when Qt's QDns is not good enough.  Because QDns
 
25
//! does not use threads, it cannot make a system call asyncronously.  Thus,
 
26
//! QDns tries to imitate the behavior of each platform's native behavior, and
 
27
//! generally falls short.
 
28
//!
 
29
//! NDns uses a thread to make the system call happen in the background.  This
 
30
//! gives your program native DNS behavior, at the cost of requiring threads
 
31
//! to build.
 
32
//!
 
33
//! \code
 
34
//! #include "ndns.h"
 
35
//!
 
36
//! ...
 
37
//!
 
38
//! NDns dns;
 
39
//! dns.resolve("psi.affinix.com");
 
40
//!
 
41
//! // The class will emit the resultsReady() signal when the resolution
 
42
//! // is finished. You may then retrieve the results:
 
43
//!
 
44
//! uint ip_address = dns.result();
 
45
//!
 
46
//! // or if you want to get the IP address as a string:
 
47
//!
 
48
//! QString ip_address = dns.resultString();
 
49
//! \endcode
 
50
 
 
51
#include"ndns.h"
 
52
 
 
53
#include<qapplication.h>
 
54
#include<qsocketdevice.h>
 
55
#include<qptrlist.h>
 
56
#include<qeventloop.h>
 
57
 
 
58
#ifdef Q_OS_UNIX
 
59
#include<netdb.h>
 
60
#include<sys/types.h>
 
61
#include<netinet/in.h>
 
62
#include<arpa/inet.h>
 
63
#endif
 
64
 
 
65
#ifdef Q_OS_WIN32
 
66
#include<windows.h>
 
67
#endif
 
68
 
 
69
// CS_NAMESPACE_BEGIN
 
70
 
 
71
//! \if _hide_doc_
 
72
class NDnsWorkerEvent : public QCustomEvent
 
73
{
 
74
public:
 
75
        enum Type { WorkerEvent = QEvent::User + 100 };
 
76
        NDnsWorkerEvent(NDnsWorker *);
 
77
 
 
78
        NDnsWorker *worker;
 
79
};
 
80
 
 
81
class NDnsWorker : public QThread
 
82
{
 
83
public:
 
84
        NDnsWorker(QObject *, const QCString &);
 
85
 
 
86
        bool success;
 
87
        bool cancelled;
 
88
        QHostAddress addr;
 
89
 
 
90
protected:
 
91
        void run();
 
92
 
 
93
private:
 
94
        QCString host;
 
95
        QObject *par;
 
96
};
 
97
//! \endif
 
98
 
 
99
//----------------------------------------------------------------------------
 
100
// NDnsManager
 
101
//----------------------------------------------------------------------------
 
102
#ifndef HAVE_GETHOSTBYNAME_R
 
103
static QMutex *workerMutex = 0;
 
104
static QMutex *workerCancelled = 0;
 
105
#endif
 
106
static NDnsManager *man = 0;
 
107
bool winsock_init = false;
 
108
 
 
109
class NDnsManager::Item
 
110
{
 
111
public:
 
112
        NDns *ndns;
 
113
        NDnsWorker *worker;
 
114
};
 
115
 
 
116
class NDnsManager::Private
 
117
{
 
118
public:
 
119
        Item *find(const NDns *n)
 
120
        {
 
121
                QPtrListIterator<Item> it(list);
 
122
                for(Item *i; (i = it.current()); ++it) {
 
123
                        if(i->ndns == n)
 
124
                                return i;
 
125
                }
 
126
                return 0;
 
127
        }
 
128
 
 
129
        Item *find(const NDnsWorker *w)
 
130
        {
 
131
                QPtrListIterator<Item> it(list);
 
132
                for(Item *i; (i = it.current()); ++it) {
 
133
                        if(i->worker == w)
 
134
                                return i;
 
135
                }
 
136
                return 0;
 
137
        }
 
138
 
 
139
        QPtrList<Item> list;
 
140
};
 
141
 
 
142
NDnsManager::NDnsManager()
 
143
{
 
144
#ifndef HAVE_GETHOSTBYNAME_R
 
145
        workerMutex = new QMutex;
 
146
        workerCancelled = new QMutex;
 
147
#endif
 
148
 
 
149
#ifdef Q_OS_WIN32
 
150
        if(!winsock_init) {
 
151
                winsock_init = true;
 
152
                QSocketDevice *sd = new QSocketDevice;
 
153
                delete sd;
 
154
        }
 
155
#endif
 
156
 
 
157
        d = new Private;
 
158
        d->list.setAutoDelete(true);
 
159
 
 
160
        connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
 
161
}
 
162
 
 
163
NDnsManager::~NDnsManager()
 
164
{
 
165
        delete d;
 
166
 
 
167
#ifndef HAVE_GETHOSTBYNAME_R
 
168
        delete workerMutex;
 
169
        workerMutex = 0;
 
170
        delete workerCancelled;
 
171
        workerCancelled = 0;
 
172
#endif
 
173
}
 
174
 
 
175
void NDnsManager::resolve(NDns *self, const QString &name)
 
176
{
 
177
        Item *i = new Item;
 
178
        i->ndns = self;
 
179
        i->worker = new NDnsWorker(this, name.utf8());
 
180
        d->list.append(i);
 
181
 
 
182
        i->worker->start();
 
183
}
 
184
 
 
185
void NDnsManager::stop(NDns *self)
 
186
{
 
187
        Item *i = d->find(self);
 
188
        if(!i)
 
189
                return;
 
190
        // disassociate
 
191
        i->ndns = 0;
 
192
 
 
193
#ifndef HAVE_GETHOSTBYNAME_R
 
194
        // cancel
 
195
        workerCancelled->lock();
 
196
        i->worker->cancelled = true;
 
197
        workerCancelled->unlock();
 
198
#endif
 
199
}
 
200
 
 
201
bool NDnsManager::isBusy(const NDns *self) const
 
202
{
 
203
        Item *i = d->find(self);
 
204
        return (i ? true: false);
 
205
}
 
206
 
 
207
bool NDnsManager::event(QEvent *e)
 
208
{
 
209
        if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
 
210
                NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
 
211
                we->worker->wait(); // ensure that the thread is terminated
 
212
 
 
213
                Item *i = d->find(we->worker);
 
214
                if(!i) {
 
215
                        // should NOT happen
 
216
                        return true;
 
217
                }
 
218
                QHostAddress addr = i->worker->addr;
 
219
                NDns *ndns = i->ndns;
 
220
                delete i->worker;
 
221
                d->list.removeRef(i);
 
222
 
 
223
                // nuke manager if no longer needed (code that follows MUST BE SAFE!)
 
224
                tryDestroy();
 
225
 
 
226
                // requestor still around?
 
227
                if(ndns)
 
228
                        ndns->finished(addr);
 
229
                return true;
 
230
        }
 
231
        return false;
 
232
}
 
233
 
 
234
void NDnsManager::tryDestroy()
 
235
{
 
236
        if(d->list.isEmpty()) {
 
237
                man = 0;
 
238
                delete this;
 
239
        }
 
240
}
 
241
 
 
242
void NDnsManager::app_aboutToQuit()
 
243
{
 
244
        while(man) {
 
245
                QEventLoop *e = qApp->eventLoop();
 
246
                e->processEvents(QEventLoop::WaitForMore);
 
247
        }
 
248
}
 
249
 
 
250
 
 
251
//----------------------------------------------------------------------------
 
252
// NDns
 
253
//----------------------------------------------------------------------------
 
254
 
 
255
//! \fn void NDns::resultsReady()
 
256
//! This signal is emitted when the DNS resolution succeeds or fails.
 
257
 
 
258
//!
 
259
//! Constructs an NDns object with parent \a parent.
 
260
NDns::NDns(QObject *parent)
 
261
:QObject(parent)
 
262
{
 
263
}
 
264
 
 
265
//!
 
266
//! Destroys the object and frees allocated resources.
 
267
NDns::~NDns()
 
268
{
 
269
        stop();
 
270
}
 
271
 
 
272
//!
 
273
//! Resolves hostname \a host (eg. psi.affinix.com)
 
274
void NDns::resolve(const QString &host)
 
275
{
 
276
        stop();
 
277
        if(!man)
 
278
                man = new NDnsManager;
 
279
        man->resolve(this, host);
 
280
}
 
281
 
 
282
//!
 
283
//! Cancels the lookup action.
 
284
//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
 
285
void NDns::stop()
 
286
{
 
287
        if(man)
 
288
                man->stop(this);
 
289
}
 
290
 
 
291
//!
 
292
//! Returns the IP address as a 32-bit integer in host-byte-order.  This will be 0 if the lookup failed.
 
293
//! \sa resultsReady()
 
294
uint NDns::result() const
 
295
{
 
296
        return addr.ip4Addr();
 
297
}
 
298
 
 
299
//!
 
300
//! Returns the IP address as a string.  This will be an empty string if the lookup failed.
 
301
//! \sa resultsReady()
 
302
QString NDns::resultString() const
 
303
{
 
304
        return addr.toString();
 
305
}
 
306
 
 
307
//!
 
308
//! Returns TRUE if busy resolving a hostname.
 
309
bool NDns::isBusy() const
 
310
{
 
311
        if(!man)
 
312
                return false;
 
313
        return man->isBusy(this);
 
314
}
 
315
 
 
316
void NDns::finished(const QHostAddress &a)
 
317
{
 
318
        addr = a;
 
319
        resultsReady();
 
320
}
 
321
 
 
322
//----------------------------------------------------------------------------
 
323
// NDnsWorkerEvent
 
324
//----------------------------------------------------------------------------
 
325
NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
 
326
:QCustomEvent(WorkerEvent)
 
327
{
 
328
        worker = p;
 
329
}
 
330
 
 
331
//----------------------------------------------------------------------------
 
332
// NDnsWorker
 
333
//----------------------------------------------------------------------------
 
334
NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
 
335
{
 
336
        success = cancelled = false;
 
337
        par = _par;
 
338
        host = _host.copy(); // do we need this to avoid sharing across threads?
 
339
}
 
340
 
 
341
void NDnsWorker::run()
 
342
{
 
343
        hostent *h = 0;
 
344
 
 
345
#ifdef HAVE_GETHOSTBYNAME_R
 
346
        hostent buf;
 
347
        char char_buf[1024];
 
348
        int err;
 
349
        gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
 
350
#else
 
351
        // lock for gethostbyname
 
352
        QMutexLocker locker(workerMutex);
 
353
 
 
354
        // check for cancel
 
355
        workerCancelled->lock();
 
356
        bool cancel = cancelled;
 
357
        workerCancelled->unlock();
 
358
 
 
359
        if(!cancel)
 
360
                h = gethostbyname(host.data());
 
361
#endif
 
362
 
 
363
        if(!h) {
 
364
                success = false;
 
365
                QApplication::postEvent(par, new NDnsWorkerEvent(this));
 
366
                return;
 
367
        }
 
368
 
 
369
        in_addr a = *((struct in_addr *)h->h_addr_list[0]);
 
370
        addr.setAddress(ntohl(a.s_addr));
 
371
        success = true;
 
372
 
 
373
        QApplication::postEvent(par, new NDnsWorkerEvent(this));
 
374
}
 
375
 
 
376
// CS_NAMESPACE_END