~ubuntu-branches/ubuntu/utopic/psi/utopic

« back to all changes in this revision

Viewing changes to iris/irisnet/jdnsshared.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2006,2007  Justin Karneges
3
 
 *
4
 
 * This library is free software; you can redistribute it and/or
5
 
 * modify it under the terms of the GNU Lesser General Public
6
 
 * License as published by the Free Software Foundation; either
7
 
 * version 2.1 of the License, or (at your option) any later version.
8
 
 *
9
 
 * This library is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * Lesser General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this library; if not, write to the Free Software
16
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
 
 * 02110-1301  USA
18
 
 *
19
 
 */
20
 
 
21
 
// Note: JDnsShared supports multiple interfaces for multicast, but only one
22
 
//   for IPv4 and one for IPv6.  Sharing multiple interfaces of the same IP
23
 
//   version for multicast is unfortunately not possible without reworking
24
 
//   the jdns subsystem.
25
 
//
26
 
//   The reason for this limitation is that in order to do multi-interface
27
 
//   multicast, you have to do a single bind to Any, and then use special
28
 
//   functions to determine which interface a packet came from and to
29
 
//   specify which interface a packet should go out on.  Again this is just
30
 
//   not possible with the current system and the assumptions made by jdns.
31
 
 
32
 
// Note: When quering against multiple interfaces with multicast, it is
33
 
//   possible that different answers for a unique record may be reported
34
 
//   on each interface.  We don't do anything about this.
35
 
 
36
 
#include "jdnsshared.h"
37
 
 
38
 
namespace {
39
 
 
40
 
class SystemInfoCache
41
 
{
42
 
public:
43
 
        QJDns::SystemInfo info;
44
 
        QTime time;
45
 
};
46
 
 
47
 
}
48
 
 
49
 
Q_GLOBAL_STATIC(QMutex, jdnsshared_mutex)
50
 
Q_GLOBAL_STATIC(SystemInfoCache, jdnsshared_infocache)
51
 
 
52
 
static QJDns::SystemInfo get_sys_info()
53
 
{
54
 
        QMutexLocker locker(jdnsshared_mutex());
55
 
        SystemInfoCache *c = jdnsshared_infocache();
56
 
 
57
 
        // cache info for 1/2 second, enough to prevent re-reading of sys
58
 
        //   info 20 times because of all the different resolves
59
 
        if(c->time.isNull() || c->time.elapsed() >= 500)
60
 
        {
61
 
                c->info = QJDns::systemInfo();
62
 
                c->time.start();
63
 
        }
64
 
 
65
 
        return c->info;
66
 
}
67
 
 
68
 
static bool domainCompare(const QByteArray &a, const QByteArray &b)
69
 
{
70
 
        return (qstricmp(a.data(), b.data()) == 0) ? true: false;
71
 
}
72
 
 
73
 
// adapted from jdns_mdnsd.c, _a_match()
74
 
static bool matchRecordExceptTtl(const QJDns::Record &a, const QJDns::Record &b)
75
 
{
76
 
        if(a.type != b.type || !domainCompare(a.owner, b.owner))
77
 
                return false;
78
 
 
79
 
        if(a.type == QJDns::Srv)
80
 
        {
81
 
                if(domainCompare(a.name, b.name)
82
 
                        && a.port == b.port
83
 
                        && a.priority == b.priority
84
 
                        && a.weight == b.weight)
85
 
                {
86
 
                        return true;
87
 
                }
88
 
        }
89
 
        else if(a.type == QJDns::Ptr || a.type == QJDns::Ns || a.type == QJDns::Cname)
90
 
        {
91
 
                if(domainCompare(a.name, b.name))
92
 
                        return true;
93
 
        }
94
 
        else if(a.rdata == b.rdata)
95
 
                return true;
96
 
 
97
 
        return false;
98
 
}
99
 
 
100
 
//----------------------------------------------------------------------------
101
 
// Handle
102
 
//----------------------------------------------------------------------------
103
 
 
104
 
namespace {
105
 
 
106
 
// QJDns uses integer handle ids, but they are only unique within
107
 
//   the relevant QJDns instance.  Since we want our handles to be
108
 
//   unique across all instances, we'll make an instance/id pair.
109
 
class Handle
110
 
{
111
 
public:
112
 
        QJDns *jdns;
113
 
        int id;
114
 
 
115
 
        Handle() : jdns(0), id(-1)
116
 
        {
117
 
        }
118
 
 
119
 
        Handle(QJDns *_jdns, int _id) : jdns(_jdns), id(_id)
120
 
        {
121
 
        }
122
 
 
123
 
        bool operator==(const Handle &a) const
124
 
        {
125
 
                if(a.jdns == jdns && a.id == id)
126
 
                        return true;
127
 
                return false;
128
 
        }
129
 
 
130
 
        bool operator!=(const Handle &a) const
131
 
        {
132
 
                return !(operator==(a));
133
 
        }
134
 
};
135
 
 
136
 
}
137
 
 
138
 
inline uint qHash(const Handle &key)
139
 
{
140
 
        return ((uint)key.jdns) ^ key.id;
141
 
}
142
 
 
143
 
//----------------------------------------------------------------------------
144
 
// JDnsShutdown
145
 
//----------------------------------------------------------------------------
146
 
namespace {
147
 
 
148
 
class JDnsShutdownAgent : public QObject
149
 
{
150
 
        Q_OBJECT
151
 
public:
152
 
        void start()
153
 
        {
154
 
                QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection);
155
 
        }
156
 
 
157
 
signals:
158
 
        void started();
159
 
};
160
 
 
161
 
class JDnsShutdown : public QThread
162
 
{
163
 
        Q_OBJECT
164
 
public:
165
 
        QMutex m;
166
 
        QWaitCondition w;
167
 
        QList<JDnsShared*> list;
168
 
        JDnsShutdownAgent *agent;
169
 
        int phase;
170
 
 
171
 
        void waitForShutdown(const QList<JDnsShared*> _list)
172
 
        {
173
 
                list = _list;
174
 
                phase = 0;
175
 
 
176
 
                m.lock();
177
 
                start();
178
 
                w.wait(&m);
179
 
 
180
 
                foreach(JDnsShared *i, list)
181
 
                {
182
 
                        i->setParent(0);
183
 
                        i->moveToThread(this);
184
 
                }
185
 
 
186
 
                phase = 1;
187
 
                agent->start();
188
 
                wait();
189
 
        }
190
 
 
191
 
protected:
192
 
        virtual void run()
193
 
        {
194
 
                m.lock();
195
 
                agent = new JDnsShutdownAgent;
196
 
                connect(agent, SIGNAL(started()), SLOT(agent_started()), Qt::DirectConnection);
197
 
                agent->start();
198
 
                exec();
199
 
                delete agent;
200
 
        }
201
 
 
202
 
private slots:
203
 
        void agent_started()
204
 
        {
205
 
                if(phase == 0)
206
 
                {
207
 
                        w.wakeOne();
208
 
                        m.unlock();
209
 
                }
210
 
                else
211
 
                {
212
 
                        foreach(JDnsShared *i, list)
213
 
                        {
214
 
                                connect(i, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished()), Qt::DirectConnection);
215
 
                                i->shutdown();
216
 
                        }
217
 
                }
218
 
        }
219
 
 
220
 
        void jdns_shutdownFinished()
221
 
        {
222
 
                JDnsShared *i = (JDnsShared *)sender();
223
 
                delete i;
224
 
                list.removeAll(i);
225
 
                if(list.isEmpty())
226
 
                        quit();
227
 
        }
228
 
};
229
 
 
230
 
}
231
 
 
232
 
//----------------------------------------------------------------------------
233
 
// JDnsSharedDebug
234
 
//----------------------------------------------------------------------------
235
 
class JDnsSharedDebugPrivate : public QObject
236
 
{
237
 
        Q_OBJECT
238
 
public:
239
 
        JDnsSharedDebug *q;
240
 
        QMutex m;
241
 
        QStringList lines;
242
 
        bool dirty;
243
 
 
244
 
        JDnsSharedDebugPrivate(JDnsSharedDebug *_q) : QObject(_q), q(_q)
245
 
        {
246
 
                dirty = false;
247
 
        }
248
 
 
249
 
        void addDebug(const QString &name, const QStringList &_lines)
250
 
        {
251
 
                if(!_lines.isEmpty())
252
 
                {
253
 
                        QMutexLocker locker(&m);
254
 
                        for(int n = 0; n < _lines.count(); ++n)
255
 
                                lines += name + ": " + _lines[n];
256
 
                        if(!dirty)
257
 
                        {
258
 
                                dirty = true;
259
 
                                QMetaObject::invokeMethod(this, "doUpdate", Qt::QueuedConnection);
260
 
                        }
261
 
                }
262
 
        }
263
 
 
264
 
private slots:
265
 
        void doUpdate()
266
 
        {
267
 
                {
268
 
                        QMutexLocker locker(&m);
269
 
                        if(!dirty)
270
 
                                return;
271
 
                }
272
 
                emit q->readyRead();
273
 
        }
274
 
};
275
 
 
276
 
JDnsSharedDebug::JDnsSharedDebug(QObject *parent)
277
 
:QObject(parent)
278
 
{
279
 
        d = new JDnsSharedDebugPrivate(this);
280
 
}
281
 
 
282
 
JDnsSharedDebug::~JDnsSharedDebug()
283
 
{
284
 
        delete d;
285
 
}
286
 
 
287
 
QStringList JDnsSharedDebug::readDebugLines()
288
 
{
289
 
        QMutexLocker locker(&d->m);
290
 
        QStringList tmplines = d->lines;
291
 
        d->lines.clear();
292
 
        d->dirty = false;
293
 
        return tmplines;
294
 
}
295
 
 
296
 
//----------------------------------------------------------------------------
297
 
// JDnsSharedRequest
298
 
//----------------------------------------------------------------------------
299
 
class JDnsSharedPrivate : public QObject
300
 
{
301
 
        Q_OBJECT
302
 
public:
303
 
        class Instance
304
 
        {
305
 
        public:
306
 
                QJDns *jdns;
307
 
                QHostAddress addr;
308
 
                int index;
309
 
 
310
 
                Instance() : jdns(0)
311
 
                {
312
 
                }
313
 
        };
314
 
 
315
 
        JDnsShared *q;
316
 
        JDnsShared::Mode mode;
317
 
        bool shutting_down;
318
 
        JDnsSharedDebug *db;
319
 
        QString dbname;
320
 
 
321
 
        QList<Instance*> instances;
322
 
        QHash<QJDns*,Instance*> instanceForQJDns;
323
 
 
324
 
        QSet<JDnsSharedRequest*> requests;
325
 
        QHash<Handle,JDnsSharedRequest*> requestForHandle;
326
 
 
327
 
        JDnsSharedPrivate(JDnsShared *_q) : QObject(_q), q(_q)
328
 
        {
329
 
        }
330
 
 
331
 
        JDnsSharedRequest *findRequest(QJDns *jdns, int id) const
332
 
        {
333
 
                Handle h(jdns, id);
334
 
                return requestForHandle.value(h);
335
 
        }
336
 
 
337
 
        void jdns_link(QJDns *jdns)
338
 
        {
339
 
                connect(jdns, SIGNAL(resultsReady(int, const QJDns::Response &)), SLOT(jdns_resultsReady(int, const QJDns::Response &)));
340
 
                connect(jdns, SIGNAL(published(int)), SLOT(jdns_published(int)));
341
 
                connect(jdns, SIGNAL(error(int, QJDns::Error)), SLOT(jdns_error(int, QJDns::Error)));
342
 
                connect(jdns, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished()));
343
 
                connect(jdns, SIGNAL(debugLinesReady()), SLOT(jdns_debugLinesReady()));
344
 
        }
345
 
 
346
 
        int getNewIndex() const
347
 
        {
348
 
                // find lowest unused value
349
 
                for(int n = 0;; ++n)
350
 
                {
351
 
                        bool found = false;
352
 
                        foreach(Instance *i, instances)
353
 
                        {
354
 
                                if(i->index == n)
355
 
                                {
356
 
                                        found = true;
357
 
                                        break;
358
 
                                }
359
 
                        }
360
 
                        if(!found)
361
 
                                return n;
362
 
                }
363
 
        }
364
 
 
365
 
        void addDebug(int index, const QString &line)
366
 
        {
367
 
                if(db)
368
 
                        db->d->addDebug(dbname + QString::number(index), QStringList() << line);
369
 
        }
370
 
 
371
 
        void doDebug(QJDns *jdns, int index)
372
 
        {
373
 
                QStringList lines = jdns->debugLines();
374
 
                if(db)
375
 
                        db->d->addDebug(dbname + QString::number(index), lines);
376
 
        }
377
 
 
378
 
        QJDns::Record manipulateRecord(const QJDns::Record &in)
379
 
        {
380
 
                // Note: since our implementation only allows 1 ipv4 and 1 ipv6
381
 
                //   interface to exist, it is safe to publish both kinds of
382
 
                //   records on both interfaces, with the same values.  For
383
 
                //   example, an A record can be published on both interfaces,
384
 
                //   with the value set to the ipv4 interface.  If we supported
385
 
                //   multiple ipv4 interfaces, then this wouldn't work, because
386
 
                //   we wouldn't know which value to use for the A record when
387
 
                //   publishing on the ipv6 interface.
388
 
 
389
 
                // publishing our own IP address?  null address means the user
390
 
                //   wants us to fill in the blank with our address.
391
 
                if((in.type == QJDns::Aaaa || in.type == QJDns::A) && in.address.isNull())
392
 
                {
393
 
                        QJDns::Record out = in;
394
 
                        if(in.type == QJDns::Aaaa)
395
 
                        {
396
 
                                // are we operating on ipv6?
397
 
                                foreach(Instance *i, instances)
398
 
                                {
399
 
                                        if(i->addr.protocol() == QAbstractSocket::IPv6Protocol)
400
 
                                        {
401
 
                                                out.address = i->addr;
402
 
                                                break;
403
 
                                        }
404
 
                                }
405
 
                        }
406
 
                        else // A
407
 
                        {
408
 
                                // are we operating on ipv4?
409
 
                                foreach(Instance *i, instances)
410
 
                                {
411
 
                                        if(i->addr.protocol() == QAbstractSocket::IPv4Protocol)
412
 
                                        {
413
 
                                                out.address = i->addr;
414
 
                                                break;
415
 
                                        }
416
 
                                }
417
 
                        }
418
 
                        return out;
419
 
                }
420
 
                return in;
421
 
        }
422
 
 
423
 
        bool addInterface(const QHostAddress &addr);
424
 
        void removeInterface(const QHostAddress &addr);
425
 
 
426
 
        void queryStart(JDnsSharedRequest *obj, const QByteArray &name, int qType);
427
 
        void queryCancel(JDnsSharedRequest *obj);
428
 
        void publishStart(JDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record);
429
 
        void publishUpdate(JDnsSharedRequest *obj, const QJDns::Record &record);
430
 
        void publishCancel(JDnsSharedRequest *obj);
431
 
 
432
 
public slots:
433
 
        void late_shutdown()
434
 
        {
435
 
                shutting_down = false;
436
 
                emit q->shutdownFinished();
437
 
        }
438
 
 
439
 
private slots:
440
 
        void jdns_resultsReady(int id, const QJDns::Response &results);
441
 
        void jdns_published(int id);
442
 
        void jdns_error(int id, QJDns::Error e);
443
 
        void jdns_shutdownFinished();
444
 
        void jdns_debugLinesReady();
445
 
};
446
 
 
447
 
class JDnsSharedRequestPrivate : public QObject
448
 
{
449
 
        Q_OBJECT
450
 
public:
451
 
        JDnsSharedRequest *q;
452
 
        JDnsSharedPrivate *jsp;
453
 
 
454
 
        // current action
455
 
        JDnsSharedRequest::Type type;
456
 
        QByteArray name;
457
 
        int qType;
458
 
        QJDns::PublishMode pubmode;
459
 
        QJDns::Record pubrecord;
460
 
 
461
 
        // a single request might have to perform multiple QJDns operations
462
 
        QList<Handle> handles;
463
 
 
464
 
        // keep a list of handles that successfully publish
465
 
        QList<Handle> published;
466
 
 
467
 
        // use to weed out dups for multicast
468
 
        QList<QJDns::Record> queryCache;
469
 
 
470
 
        bool success;
471
 
        JDnsSharedRequest::Error error;
472
 
        QList<QJDns::Record> results;
473
 
        QTimer lateTimer;
474
 
 
475
 
        JDnsSharedRequestPrivate(JDnsSharedRequest *_q) : QObject(_q), q(_q), lateTimer(this)
476
 
        {
477
 
                connect(&lateTimer, SIGNAL(timeout()), SLOT(lateTimer_timeout()));
478
 
        }
479
 
 
480
 
        void resetSession()
481
 
        {
482
 
                name = QByteArray();
483
 
                pubrecord = QJDns::Record();
484
 
                handles.clear();
485
 
                published.clear();
486
 
                queryCache.clear();
487
 
        }
488
 
 
489
 
private slots:
490
 
        void lateTimer_timeout()
491
 
        {
492
 
                emit q->resultsReady();
493
 
        }
494
 
};
495
 
 
496
 
JDnsSharedRequest::JDnsSharedRequest(JDnsShared *jdnsShared, QObject *parent)
497
 
:QObject(parent)
498
 
{
499
 
        d = new JDnsSharedRequestPrivate(this);
500
 
        d->jsp = jdnsShared->d;
501
 
}
502
 
 
503
 
JDnsSharedRequest::~JDnsSharedRequest()
504
 
{
505
 
        cancel();
506
 
        delete d;
507
 
}
508
 
 
509
 
JDnsSharedRequest::Type JDnsSharedRequest::type()
510
 
{
511
 
        return d->type;
512
 
}
513
 
 
514
 
void JDnsSharedRequest::query(const QByteArray &name, int type)
515
 
{
516
 
        cancel();
517
 
        d->jsp->queryStart(this, name, type);
518
 
}
519
 
 
520
 
void JDnsSharedRequest::publish(QJDns::PublishMode m, const QJDns::Record &record)
521
 
{
522
 
        cancel();
523
 
        d->jsp->publishStart(this, m, record);
524
 
}
525
 
 
526
 
void JDnsSharedRequest::publishUpdate(const QJDns::Record &record)
527
 
{
528
 
        // only allowed to update if we have an active publish
529
 
        if(!d->handles.isEmpty() && d->type == Publish)
530
 
                d->jsp->publishUpdate(this, record);
531
 
}
532
 
 
533
 
void JDnsSharedRequest::cancel()
534
 
{
535
 
        d->lateTimer.stop();
536
 
        if(!d->handles.isEmpty())
537
 
        {
538
 
                if(d->type == Query)
539
 
                        d->jsp->queryCancel(this);
540
 
                else
541
 
                        d->jsp->publishCancel(this);
542
 
        }
543
 
        d->resetSession();
544
 
}
545
 
 
546
 
bool JDnsSharedRequest::success() const
547
 
{
548
 
        return d->success;
549
 
}
550
 
 
551
 
JDnsSharedRequest::Error JDnsSharedRequest::error() const
552
 
{
553
 
        return d->error;
554
 
}
555
 
 
556
 
QList<QJDns::Record> JDnsSharedRequest::results() const
557
 
{
558
 
        return d->results;
559
 
}
560
 
 
561
 
//----------------------------------------------------------------------------
562
 
// JDnsShared
563
 
//----------------------------------------------------------------------------
564
 
JDnsShared::JDnsShared(Mode mode, QObject *parent)
565
 
:QObject(parent)
566
 
{
567
 
        d = new JDnsSharedPrivate(this);
568
 
        d->mode = mode;
569
 
        d->shutting_down = false;
570
 
        d->db = 0;
571
 
}
572
 
 
573
 
JDnsShared::~JDnsShared()
574
 
{
575
 
        foreach(JDnsSharedPrivate::Instance *i, d->instances)
576
 
        {
577
 
                delete i->jdns;
578
 
                delete i;
579
 
        }
580
 
        delete d;
581
 
}
582
 
 
583
 
void JDnsShared::setDebug(JDnsSharedDebug *db, const QString &name)
584
 
{
585
 
        d->db = db;
586
 
        d->dbname = name;
587
 
}
588
 
 
589
 
bool JDnsShared::addInterface(const QHostAddress &addr)
590
 
{
591
 
        return d->addInterface(addr);
592
 
}
593
 
 
594
 
void JDnsShared::removeInterface(const QHostAddress &addr)
595
 
{
596
 
        d->removeInterface(addr);
597
 
}
598
 
 
599
 
void JDnsShared::shutdown()
600
 
{
601
 
        d->shutting_down = true;
602
 
        if(!d->instances.isEmpty())
603
 
        {
604
 
                foreach(JDnsSharedPrivate::Instance *i, d->instances)
605
 
                        i->jdns->shutdown();
606
 
        }
607
 
        else
608
 
                QMetaObject::invokeMethod(d, "late_shutdown", Qt::QueuedConnection);
609
 
}
610
 
 
611
 
QList<QByteArray> JDnsShared::domains()
612
 
{
613
 
        return get_sys_info().domains;
614
 
}
615
 
 
616
 
void JDnsShared::waitForShutdown(const QList<JDnsShared*> instances)
617
 
{
618
 
        JDnsShutdown s;
619
 
        s.waitForShutdown(instances);
620
 
}
621
 
 
622
 
bool JDnsSharedPrivate::addInterface(const QHostAddress &addr)
623
 
{
624
 
        if(shutting_down)
625
 
                return false;
626
 
 
627
 
        // make sure we don't have this one already
628
 
        foreach(Instance *i, instances)
629
 
        {
630
 
                if(i->addr == addr)
631
 
                        return false;
632
 
        }
633
 
 
634
 
        int index = getNewIndex();
635
 
        addDebug(index, QString("attempting to use interface %1").arg(addr.toString()));
636
 
 
637
 
        QJDns *jdns;
638
 
 
639
 
        if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal)
640
 
        {
641
 
                jdns = new QJDns(this);
642
 
                jdns_link(jdns);
643
 
                if(!jdns->init(QJDns::Unicast, addr))
644
 
                {
645
 
                        doDebug(jdns, index);
646
 
                        delete jdns;
647
 
                        return false;
648
 
                }
649
 
 
650
 
                if(mode == JDnsShared::UnicastLocal)
651
 
                {
652
 
                        QJDns::NameServer host;
653
 
                        host.address = QHostAddress("224.0.0.251");
654
 
                        host.port = 5353;
655
 
                        jdns->setNameServers(QList<QJDns::NameServer>() << host);
656
 
                }
657
 
        }
658
 
        else // Multicast
659
 
        {
660
 
                // only one multicast interface allowed per IP protocol version.
661
 
                // this is because we bind to INADDR_ANY.
662
 
 
663
 
                bool have_v6 = false;
664
 
                bool have_v4 = false;
665
 
                foreach(Instance *i, instances)
666
 
                {
667
 
                        if(i->addr.protocol() == QAbstractSocket::IPv6Protocol)
668
 
                                have_v6 = true;
669
 
                        else
670
 
                                have_v4 = true;
671
 
                }
672
 
 
673
 
                bool is_v6 = (addr.protocol() == QAbstractSocket::IPv6Protocol) ? true : false;
674
 
 
675
 
                if(is_v6 && have_v6)
676
 
                {
677
 
                        addDebug(index, "already have an ipv6 interface");
678
 
                        return false;
679
 
                }
680
 
 
681
 
                if(!is_v6 && have_v4)
682
 
                {
683
 
                        addDebug(index, "already have an ipv4 interface");
684
 
                        return false;
685
 
                }
686
 
 
687
 
                QHostAddress actualBindAddress;
688
 
                if(is_v6)
689
 
                        actualBindAddress = QHostAddress::AnyIPv6;
690
 
                else
691
 
                        actualBindAddress = QHostAddress::Any;
692
 
 
693
 
                jdns = new QJDns(this);
694
 
                jdns_link(jdns);
695
 
                if(!jdns->init(QJDns::Multicast, actualBindAddress))
696
 
                {
697
 
                        doDebug(jdns, index);
698
 
                        delete jdns;
699
 
                        return false;
700
 
                }
701
 
        }
702
 
 
703
 
        Instance *i = new Instance;
704
 
        i->jdns = jdns;
705
 
        i->addr = addr;
706
 
        i->index = index;
707
 
        instances += i;
708
 
        instanceForQJDns.insert(i->jdns, i);
709
 
 
710
 
        addDebug(index, "interface ready");
711
 
 
712
 
        if(mode == JDnsShared::Multicast)
713
 
        {
714
 
                // extend active requests to this interface
715
 
                foreach(JDnsSharedRequest *obj, requests)
716
 
                {
717
 
                        if(obj->d->type == JDnsSharedRequest::Query)
718
 
                        {
719
 
                                Handle h(i->jdns, i->jdns->queryStart(obj->d->name, obj->d->qType));
720
 
                                obj->d->handles += h;
721
 
                                requestForHandle.insert(h, obj);
722
 
                        }
723
 
                        else // Publish
724
 
                        {
725
 
                                Handle h(i->jdns, i->jdns->publishStart(obj->d->pubmode, obj->d->pubrecord));
726
 
                                obj->d->handles += h;
727
 
                                requestForHandle.insert(h, obj);
728
 
                        }
729
 
                }
730
 
        }
731
 
 
732
 
        return true;
733
 
}
734
 
 
735
 
void JDnsSharedPrivate::removeInterface(const QHostAddress &addr)
736
 
{
737
 
        Instance *i = 0;
738
 
        for(int n = 0; n < instances.count(); ++n)
739
 
        {
740
 
                if(instances[n]->addr == addr)
741
 
                {
742
 
                        i = instances[n];
743
 
                        break;
744
 
                }
745
 
        }
746
 
        if(!i)
747
 
                return;
748
 
 
749
 
        int index = i->index;
750
 
 
751
 
        // we don't cancel operations or shutdown jdns, we simply
752
 
        //   delete our references.  this is because if the interface
753
 
        //   is gone, then we have nothing to send on anyway.
754
 
 
755
 
        foreach(JDnsSharedRequest *obj, requests)
756
 
        {
757
 
                for(int n = 0; n < obj->d->handles.count(); ++n)
758
 
                {
759
 
                        Handle h = obj->d->handles[n];
760
 
                        if(h.jdns == i->jdns)
761
 
                        {
762
 
                                // see above, no need to cancel the operation
763
 
                                obj->d->handles.removeAt(n);
764
 
                                requestForHandle.remove(h);
765
 
                                break;
766
 
                        }
767
 
                }
768
 
 
769
 
                // remove published reference
770
 
                if(obj->d->type == JDnsSharedRequest::Publish)
771
 
                {
772
 
                        for(int n = 0; n < obj->d->published.count(); ++n)
773
 
                        {
774
 
                                Handle h = obj->d->published[n];
775
 
                                if(h.jdns == i->jdns)
776
 
                                {
777
 
                                        obj->d->published.removeAt(n);
778
 
                                        break;
779
 
                                }
780
 
                        }
781
 
                }
782
 
        }
783
 
 
784
 
        // see above, no need to shutdown jdns
785
 
        instanceForQJDns.remove(i->jdns);
786
 
        instances.removeAll(i);
787
 
        delete i->jdns;
788
 
        delete i;
789
 
 
790
 
        // if that was the last interface to be removed, then there should
791
 
        //   be no more handles left.  let's take action with these
792
 
        //   handleless requests.
793
 
        foreach(JDnsSharedRequest *obj, requests)
794
 
        {
795
 
                if(obj->d->handles.isEmpty())
796
 
                {
797
 
                        if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal)
798
 
                        {
799
 
                                // for unicast, we'll invalidate with ErrorNoNet
800
 
                                obj->d->success = false;
801
 
                                obj->d->error = JDnsSharedRequest::ErrorNoNet;
802
 
                                obj->d->lateTimer.start();
803
 
                        }
804
 
                        else // Multicast
805
 
                        {
806
 
                                // for multicast, we'll keep all requests alive.
807
 
                                //   activity will resume when an interface is
808
 
                                //   added.
809
 
                        }
810
 
                }
811
 
        }
812
 
 
813
 
        addDebug(index, QString("removing from %1").arg(addr.toString()));
814
 
}
815
 
 
816
 
void JDnsSharedPrivate::queryStart(JDnsSharedRequest *obj, const QByteArray &name, int qType)
817
 
{
818
 
        obj->d->type = JDnsSharedRequest::Query;
819
 
        obj->d->success = false;
820
 
        obj->d->results.clear();
821
 
        obj->d->name = name;
822
 
        obj->d->qType = qType;
823
 
 
824
 
        // is the input an IP address and the qType is an address record?
825
 
        if(qType == QJDns::Aaaa || qType == QJDns::A)
826
 
        {
827
 
                QHostAddress addr;
828
 
                if(addr.setAddress(QString::fromLocal8Bit(name)))
829
 
                {
830
 
                        if(qType == QJDns::Aaaa && addr.protocol() == QAbstractSocket::IPv6Protocol)
831
 
                        {
832
 
                                QJDns::Record rec;
833
 
                                rec.owner = name;
834
 
                                rec.type = QJDns::Aaaa;
835
 
                                rec.ttl = 120;
836
 
                                rec.haveKnown = true;
837
 
                                rec.address = addr;
838
 
                                obj->d->success = true;
839
 
                                obj->d->results = QList<QJDns::Record>() << rec;
840
 
                                obj->d->lateTimer.start();
841
 
                                return;
842
 
                        }
843
 
                        else if(qType == QJDns::A && addr.protocol() == QAbstractSocket::IPv4Protocol)
844
 
                        {
845
 
                                QJDns::Record rec;
846
 
                                rec.owner = name;
847
 
                                rec.type = QJDns::A;
848
 
                                rec.ttl = 120;
849
 
                                rec.haveKnown = true;
850
 
                                rec.address = addr;
851
 
                                obj->d->success = true;
852
 
                                obj->d->results = QList<QJDns::Record>() << rec;
853
 
                                obj->d->lateTimer.start();
854
 
                                return;
855
 
                        }
856
 
                }
857
 
        }
858
 
 
859
 
        QJDns::SystemInfo sysInfo = get_sys_info();
860
 
 
861
 
        // is the input name a known host and the qType is an address record?
862
 
        if(qType == QJDns::Aaaa || qType == QJDns::A)
863
 
        {
864
 
                QByteArray lname = name.toLower();
865
 
                QList<QJDns::DnsHost> known = sysInfo.hosts;
866
 
                foreach(QJDns::DnsHost host, known)
867
 
                {
868
 
                        if(((qType == QJDns::Aaaa && host.address.protocol() == QAbstractSocket::IPv6Protocol)
869
 
                                || (qType == QJDns::A && host.address.protocol() == QAbstractSocket::IPv4Protocol))
870
 
                                && host.name.toLower() == lname)
871
 
                        {
872
 
                                QJDns::Record rec;
873
 
                                rec.owner = name;
874
 
                                rec.type = qType;
875
 
                                rec.ttl = 120;
876
 
                                rec.haveKnown = true;
877
 
                                rec.address = host.address;
878
 
                                obj->d->success = true;
879
 
                                obj->d->results = QList<QJDns::Record>() << rec;
880
 
                                obj->d->lateTimer.start();
881
 
                                return;
882
 
                        }
883
 
                }
884
 
        }
885
 
 
886
 
        // if we have no QJDns instances to operate on, then error
887
 
        if(instances.isEmpty())
888
 
        {
889
 
                obj->d->error = JDnsSharedRequest::ErrorNoNet;
890
 
                obj->d->lateTimer.start();
891
 
                return;
892
 
        }
893
 
 
894
 
        if(mode == JDnsShared::UnicastInternet)
895
 
        {
896
 
                // get latest nameservers, split into ipv6/v4, apply to jdns instances
897
 
                QList<QJDns::NameServer> ns_v6;
898
 
                QList<QJDns::NameServer> ns_v4;
899
 
                {
900
 
                        QList<QJDns::NameServer> nameServers = sysInfo.nameServers;
901
 
                        foreach(QJDns::NameServer ns, nameServers)
902
 
                        {
903
 
                                if(ns.address.protocol() == QAbstractSocket::IPv6Protocol)
904
 
                                        ns_v6 += ns;
905
 
                                else
906
 
                                        ns_v4 += ns;
907
 
                        }
908
 
                }
909
 
                foreach(Instance *i, instances)
910
 
                {
911
 
                        if(i->addr.protocol() == QAbstractSocket::IPv6Protocol)
912
 
                                i->jdns->setNameServers(ns_v6);
913
 
                        else
914
 
                                i->jdns->setNameServers(ns_v4);
915
 
                }
916
 
        }
917
 
 
918
 
        // keep track of this request
919
 
        requests += obj;
920
 
 
921
 
        // query on all jdns instances
922
 
        foreach(Instance *i, instances)
923
 
        {
924
 
                Handle h(i->jdns, i->jdns->queryStart(name, qType));
925
 
                obj->d->handles += h;
926
 
 
927
 
                // keep track of this handle for this request
928
 
                requestForHandle.insert(h, obj);
929
 
        }
930
 
}
931
 
 
932
 
void JDnsSharedPrivate::queryCancel(JDnsSharedRequest *obj)
933
 
{
934
 
        if(!requests.contains(obj))
935
 
                return;
936
 
 
937
 
        foreach(Handle h, obj->d->handles)
938
 
        {
939
 
                h.jdns->queryCancel(h.id);
940
 
                requestForHandle.remove(h);
941
 
        }
942
 
 
943
 
        obj->d->handles.clear();
944
 
        requests.remove(obj);
945
 
}
946
 
 
947
 
void JDnsSharedPrivate::publishStart(JDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record)
948
 
{
949
 
        obj->d->type = JDnsSharedRequest::Publish;
950
 
        obj->d->success = false;
951
 
        obj->d->results.clear();
952
 
        obj->d->pubmode = m;
953
 
        obj->d->pubrecord = manipulateRecord(record);
954
 
 
955
 
        // if we have no QJDns instances to operate on, then error
956
 
        if(instances.isEmpty())
957
 
        {
958
 
                obj->d->error = JDnsSharedRequest::ErrorNoNet;
959
 
                obj->d->lateTimer.start();
960
 
                return;
961
 
        }
962
 
 
963
 
        // keep track of this request
964
 
        requests += obj;
965
 
 
966
 
        // attempt to publish on all jdns instances
967
 
        foreach(JDnsSharedPrivate::Instance *i, instances)
968
 
        {
969
 
                Handle h(i->jdns, i->jdns->publishStart(m, obj->d->pubrecord));
970
 
                obj->d->handles += h;
971
 
 
972
 
                // keep track of this handle for this request
973
 
                requestForHandle.insert(h, obj);
974
 
        }
975
 
}
976
 
 
977
 
void JDnsSharedPrivate::publishUpdate(JDnsSharedRequest *obj, const QJDns::Record &record)
978
 
{
979
 
        if(!requests.contains(obj))
980
 
                return;
981
 
 
982
 
        obj->d->pubrecord = manipulateRecord(record);
983
 
 
984
 
        // publish update on all handles for this request
985
 
        foreach(Handle h, obj->d->handles)
986
 
                h.jdns->publishUpdate(h.id, obj->d->pubrecord);
987
 
}
988
 
 
989
 
void JDnsSharedPrivate::publishCancel(JDnsSharedRequest *obj)
990
 
{
991
 
        if(!requests.contains(obj))
992
 
                return;
993
 
 
994
 
        foreach(Handle h, obj->d->handles)
995
 
        {
996
 
                h.jdns->publishCancel(h.id);
997
 
                requestForHandle.remove(h);
998
 
        }
999
 
 
1000
 
        obj->d->handles.clear();
1001
 
        obj->d->published.clear();
1002
 
        requests.remove(obj);
1003
 
}
1004
 
 
1005
 
void JDnsSharedPrivate::jdns_resultsReady(int id, const QJDns::Response &results)
1006
 
{
1007
 
        QJDns *jdns = (QJDns *)sender();
1008
 
        JDnsSharedRequest *obj = findRequest(jdns, id);
1009
 
 
1010
 
        obj->d->success = true;
1011
 
        obj->d->results = results.answerRecords;
1012
 
 
1013
 
        if(mode == JDnsShared::UnicastInternet || mode == JDnsShared::UnicastLocal)
1014
 
        {
1015
 
                // only one response, so "cancel" it
1016
 
                for(int n = 0; n < obj->d->handles.count(); ++n)
1017
 
                {
1018
 
                        Handle h = obj->d->handles[n];
1019
 
                        if(h.jdns == jdns && h.id == id)
1020
 
                        {
1021
 
                                obj->d->handles.removeAt(n);
1022
 
                                requestForHandle.remove(h);
1023
 
                                break;
1024
 
                        }
1025
 
                }
1026
 
 
1027
 
                // cancel related handles
1028
 
                foreach(Handle h, obj->d->handles)
1029
 
                {
1030
 
                        h.jdns->queryCancel(h.id);
1031
 
                        requestForHandle.remove(h);
1032
 
                }
1033
 
 
1034
 
                obj->d->handles.clear();
1035
 
                requests.remove(obj);
1036
 
        }
1037
 
        else // Multicast
1038
 
        {
1039
 
                // check our cache to see how we should report these results
1040
 
                for(int n = 0; n < obj->d->results.count(); ++n)
1041
 
                {
1042
 
                        QJDns::Record &r = obj->d->results[n];
1043
 
 
1044
 
                        // do we have this answer already in our cache?
1045
 
                        QJDns::Record *c = 0;
1046
 
                        int c_at = -1;
1047
 
                        for(int k = 0; k < obj->d->queryCache.count(); ++k)
1048
 
                        {
1049
 
                                QJDns::Record &tmp = obj->d->queryCache[k];
1050
 
                                if(matchRecordExceptTtl(r, tmp))
1051
 
                                {
1052
 
                                        c = &tmp;
1053
 
                                        c_at = k;
1054
 
                                        break;
1055
 
                                }
1056
 
                        }
1057
 
 
1058
 
                        // don't report duplicates or unknown removals
1059
 
                        if((c && r.ttl != 0) || (!c && r.ttl == 0))
1060
 
                        {
1061
 
                                obj->d->results.removeAt(n);
1062
 
                                --n; // adjust position
1063
 
                                continue;
1064
 
                        }
1065
 
 
1066
 
                        // if we have it, and it is removed, remove from cache
1067
 
                        if(c && r.ttl == 0)
1068
 
                        {
1069
 
                                obj->d->queryCache.removeAt(c_at);
1070
 
                        }
1071
 
                        // otherwise, if we don't have it, add it to the cache
1072
 
                        else if(!c)
1073
 
                        {
1074
 
                                obj->d->queryCache += r;
1075
 
                        }
1076
 
                }
1077
 
 
1078
 
                if(obj->d->results.isEmpty())
1079
 
                        return;
1080
 
        }
1081
 
 
1082
 
        emit obj->resultsReady();
1083
 
}
1084
 
 
1085
 
void JDnsSharedPrivate::jdns_published(int id)
1086
 
{
1087
 
        QJDns *jdns = (QJDns *)sender();
1088
 
        JDnsSharedRequest *obj = findRequest(jdns, id);
1089
 
 
1090
 
        // find handle
1091
 
        Handle handle;
1092
 
        for(int n = 0; n < obj->d->handles.count(); ++n)
1093
 
        {
1094
 
                Handle h = obj->d->handles[n];
1095
 
                if(h.jdns == jdns && h.id == id)
1096
 
                {
1097
 
                        handle = h;
1098
 
                        break;
1099
 
                }
1100
 
        }
1101
 
 
1102
 
        obj->d->published += handle;
1103
 
 
1104
 
        // if this publish has already been considered successful, then
1105
 
        //   a publish has succeeded on a new interface and there's no
1106
 
        //   need to report success for this request again
1107
 
        if(obj->d->success)
1108
 
                return;
1109
 
 
1110
 
        // all handles published?
1111
 
        if(obj->d->published.count() == obj->d->handles.count())
1112
 
        {
1113
 
                obj->d->success = true;
1114
 
                emit obj->resultsReady();
1115
 
        }
1116
 
}
1117
 
 
1118
 
void JDnsSharedPrivate::jdns_error(int id, QJDns::Error e)
1119
 
{
1120
 
        QJDns *jdns = (QJDns *)sender();
1121
 
        JDnsSharedRequest *obj = findRequest(jdns, id);
1122
 
 
1123
 
        // "cancel" it
1124
 
        for(int n = 0; n < obj->d->handles.count(); ++n)
1125
 
        {
1126
 
                Handle h = obj->d->handles[n];
1127
 
                if(h.jdns == jdns && h.id == id)
1128
 
                {
1129
 
                        obj->d->handles.removeAt(n);
1130
 
                        requestForHandle.remove(h);
1131
 
                        break;
1132
 
                }
1133
 
        }
1134
 
 
1135
 
        if(obj->d->type == JDnsSharedRequest::Query)
1136
 
        {
1137
 
                // ignore the error if it is not the last error
1138
 
                if(!obj->d->handles.isEmpty())
1139
 
                        return;
1140
 
 
1141
 
                requests.remove(obj);
1142
 
 
1143
 
                obj->d->success = false;
1144
 
                JDnsSharedRequest::Error x = JDnsSharedRequest::ErrorGeneric;
1145
 
                if(e == QJDns::ErrorNXDomain)
1146
 
                        x = JDnsSharedRequest::ErrorNXDomain;
1147
 
                else if(e == QJDns::ErrorTimeout)
1148
 
                        x = JDnsSharedRequest::ErrorTimeout;
1149
 
                else // ErrorGeneric
1150
 
                        x = JDnsSharedRequest::ErrorGeneric;
1151
 
                obj->d->error = x;
1152
 
                emit obj->resultsReady();
1153
 
        }
1154
 
        else // Publish
1155
 
        {
1156
 
                // cancel related handles
1157
 
                foreach(Handle h, obj->d->handles)
1158
 
                {
1159
 
                        h.jdns->publishCancel(h.id);
1160
 
                        requestForHandle.remove(h);
1161
 
                }
1162
 
 
1163
 
                obj->d->handles.clear();
1164
 
                obj->d->published.clear();
1165
 
                requests.remove(obj);
1166
 
 
1167
 
                obj->d->success = false;
1168
 
                JDnsSharedRequest::Error x = JDnsSharedRequest::ErrorGeneric;
1169
 
                if(e == QJDns::ErrorConflict)
1170
 
                        x = JDnsSharedRequest::ErrorConflict;
1171
 
                else // ErrorGeneric
1172
 
                        x = JDnsSharedRequest::ErrorGeneric;
1173
 
                obj->d->error = x;
1174
 
                emit obj->resultsReady();
1175
 
        }
1176
 
}
1177
 
 
1178
 
void JDnsSharedPrivate::jdns_shutdownFinished()
1179
 
{
1180
 
        QJDns *jdns = (QJDns *)sender();
1181
 
 
1182
 
        addDebug(instanceForQJDns.value(jdns)->index, "jdns_shutdownFinished, removing interface");
1183
 
 
1184
 
        Instance *instance = instanceForQJDns.value(jdns);
1185
 
        delete instance->jdns;
1186
 
        delete instance;
1187
 
        instanceForQJDns.remove(jdns);
1188
 
        instances.removeAll(instance);
1189
 
 
1190
 
        if(instances.isEmpty())
1191
 
                late_shutdown();
1192
 
}
1193
 
 
1194
 
void JDnsSharedPrivate::jdns_debugLinesReady()
1195
 
{
1196
 
        QJDns *jdns = (QJDns *)sender();
1197
 
 
1198
 
        doDebug(jdns, instanceForQJDns.value(jdns)->index);
1199
 
}
1200
 
 
1201
 
#include "jdnsshared.moc"