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

« back to all changes in this revision

Viewing changes to iris/src/irisnet/corelib/netnames_jdns.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) 2005-2008  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 *
 
18
 */
 
19
 
 
20
#include "irisnetplugin.h"
 
21
 
 
22
#include "objectsession.h"
 
23
#include "jdnsshared.h"
 
24
#include "netinterface.h"
 
25
 
 
26
//#define JDNS_DEBUG
 
27
 
 
28
Q_DECLARE_METATYPE(XMPP::NameRecord)
 
29
Q_DECLARE_METATYPE(XMPP::NameResolver::Error)
 
30
Q_DECLARE_METATYPE(XMPP::ServiceBrowser::Error)
 
31
Q_DECLARE_METATYPE(XMPP::ServiceResolver::Error)
 
32
Q_DECLARE_METATYPE(XMPP::ServiceLocalPublisher::Error)
 
33
 
 
34
namespace XMPP {
 
35
 
 
36
static NameRecord importJDNSRecord(const QJDns::Record &in)
 
37
{
 
38
        NameRecord out;
 
39
        switch(in.type)
 
40
        {
 
41
                case QJDns::A:     out.setAddress(in.address); break;
 
42
                case QJDns::Aaaa:  out.setAddress(in.address); break;
 
43
                case QJDns::Mx:    out.setMx(in.name, in.priority); break;
 
44
                case QJDns::Srv:   out.setSrv(in.name, in.port, in.priority, in.weight); break;
 
45
                case QJDns::Cname: out.setCname(in.name); break;
 
46
                case QJDns::Ptr:   out.setPtr(in.name); break;
 
47
                case QJDns::Txt:   out.setTxt(in.texts); break;
 
48
                case QJDns::Hinfo: out.setHinfo(in.cpu, in.os); break;
 
49
                case QJDns::Ns:    out.setNs(in.name); break;
 
50
                case 10:           out.setNull(in.rdata); break;
 
51
                default:
 
52
                        return out;
 
53
        }
 
54
        out.setOwner(in.owner);
 
55
        out.setTtl(in.ttl);
 
56
        return out;
 
57
}
 
58
 
 
59
static QJDns::Record exportJDNSRecord(const NameRecord &in)
 
60
{
 
61
        QJDns::Record out;
 
62
        switch(in.type())
 
63
        {
 
64
                case NameRecord::A:
 
65
                        out.type = QJDns::A;
 
66
                        out.haveKnown = true;
 
67
                        out.address = in.address();
 
68
                        break;
 
69
                case NameRecord::Aaaa:
 
70
                        out.type = QJDns::Aaaa;
 
71
                        out.haveKnown = true;
 
72
                        out.address = in.address();
 
73
                        break;
 
74
                case NameRecord::Mx:
 
75
                        out.type = QJDns::Mx;
 
76
                        out.haveKnown = true;
 
77
                        out.name = in.name();
 
78
                        out.priority = in.priority();
 
79
                        break;
 
80
                case NameRecord::Srv:
 
81
                        out.type = QJDns::Srv;
 
82
                        out.haveKnown = true;
 
83
                        out.name = in.name();
 
84
                        out.port = in.port();
 
85
                        out.priority = in.priority();
 
86
                        out.weight = in.weight();
 
87
                        break;
 
88
                case NameRecord::Cname:
 
89
                        out.type = QJDns::Cname;
 
90
                        out.haveKnown = true;
 
91
                        out.name = in.name();
 
92
                        break;
 
93
                case NameRecord::Ptr:
 
94
                        out.type = QJDns::Ptr;
 
95
                        out.haveKnown = true;
 
96
                        out.name = in.name();
 
97
                        break;
 
98
                case NameRecord::Txt:
 
99
                        out.type = QJDns::Txt;
 
100
                        out.haveKnown = true;
 
101
                        out.texts = in.texts();
 
102
                        break;
 
103
                case NameRecord::Hinfo:
 
104
                        out.type = QJDns::Hinfo;
 
105
                        out.haveKnown = true;
 
106
                        out.cpu = in.cpu();
 
107
                        out.os = in.os();
 
108
                        break;
 
109
                case NameRecord::Ns:
 
110
                        out.type = QJDns::Ns;
 
111
                        out.haveKnown = true;
 
112
                        out.name = in.name();
 
113
                        break;
 
114
                case NameRecord::Null:
 
115
                        out.type = 10;
 
116
                        out.rdata = in.rawData();
 
117
                        break;
 
118
                default:
 
119
                        return out;
 
120
        }
 
121
        out.owner = in.owner();
 
122
        out.ttl = in.ttl();
 
123
        return out;
 
124
}
 
125
 
 
126
static bool validServiceType(const QByteArray &in)
 
127
{
 
128
        // can't be empty, or start/end with a dot
 
129
        if(in.isEmpty() || in[0] == '.' || in[in.length() - 1] == '.')
 
130
                return false;
 
131
 
 
132
        // must contain exactly one dot
 
133
        int dotcount = 0;
 
134
        for(int n = 0; n < in.length(); ++n)
 
135
        {
 
136
                if(in[n] == '.')
 
137
                {
 
138
                        ++dotcount;
 
139
 
 
140
                        // no need to count more than 2
 
141
                        if(dotcount >= 2)
 
142
                                break;
 
143
                }
 
144
        }
 
145
        if(dotcount != 1)
 
146
                return false;
 
147
 
 
148
        return true;
 
149
}
 
150
 
 
151
static QByteArray escapeDomainPart(const QByteArray &in)
 
152
{
 
153
        QByteArray out;
 
154
        for(int n = 0; n < in.length(); ++n)
 
155
        {
 
156
                if(in[n] == '\\')
 
157
                        out += "\\\\";
 
158
                else if(in[n] == '.')
 
159
                        out += "\\.";
 
160
                else
 
161
                        out += in[n];
 
162
        }
 
163
        return out;
 
164
}
 
165
 
 
166
static QByteArray unescapeDomainPart(const QByteArray &in)
 
167
{
 
168
        QByteArray out;
 
169
        for(int n = 0; n < in.length(); ++n)
 
170
        {
 
171
                if(in[n] == '\\')
 
172
                {
 
173
                        if(n + 1 >= in.length())
 
174
                                return QByteArray();
 
175
 
 
176
                        out += in[n + 1];
 
177
                }
 
178
                else
 
179
                        out += in[n];
 
180
        }
 
181
        return out;
 
182
}
 
183
 
 
184
class IdManager
 
185
{
 
186
private:
 
187
        QSet<int> set;
 
188
        int at;
 
189
 
 
190
        inline void bump_at()
 
191
        {
 
192
                if(at == 0x7fffffff)
 
193
                        at = 0;
 
194
                else
 
195
                        ++at;
 
196
        }
 
197
 
 
198
public:
 
199
        IdManager() :
 
200
                at(0)
 
201
        {
 
202
        }
 
203
 
 
204
        int reserveId()
 
205
        {
 
206
                while(1)
 
207
                {
 
208
                        if(!set.contains(at))
 
209
                        {
 
210
                                int id = at;
 
211
                                set.insert(id);
 
212
                                bump_at();
 
213
                                return id;
 
214
                        }
 
215
 
 
216
                        bump_at();
 
217
                }
 
218
        }
 
219
 
 
220
        void releaseId(int id)
 
221
        {
 
222
                set.remove(id);
 
223
        }
 
224
 
 
225
        void clear()
 
226
        {
 
227
                set.clear();
 
228
                at = 0;
 
229
        }
 
230
};
 
231
 
 
232
//----------------------------------------------------------------------------
 
233
// JDnsGlobal
 
234
//----------------------------------------------------------------------------
 
235
class JDnsGlobal : public QObject
 
236
{
 
237
        Q_OBJECT
 
238
 
 
239
public:
 
240
        JDnsSharedDebug db;
 
241
        JDnsShared *uni_net, *uni_local, *mul;
 
242
        QHostAddress mul_addr4, mul_addr6;
 
243
        NetInterfaceManager netman;
 
244
        QList<NetInterface*> ifaces;
 
245
        QTimer *updateTimer;
 
246
 
 
247
        JDnsGlobal() :
 
248
                netman(this)
 
249
        {
 
250
                uni_net = 0;
 
251
                uni_local = 0;
 
252
                mul = 0;
 
253
 
 
254
                qRegisterMetaType<NameRecord>();
 
255
                qRegisterMetaType<NameResolver::Error>();
 
256
                qRegisterMetaType<ServiceBrowser::Error>();
 
257
                qRegisterMetaType<ServiceResolver::Error>();
 
258
                qRegisterMetaType<ServiceLocalPublisher::Error>();
 
259
 
 
260
                connect(&db, SIGNAL(readyRead()), SLOT(jdns_debugReady()));
 
261
 
 
262
                updateTimer = new QTimer(this);
 
263
                connect(updateTimer, SIGNAL(timeout()), SLOT(doUpdateMulticastInterfaces()));
 
264
                updateTimer->setSingleShot(true);
 
265
        }
 
266
 
 
267
        ~JDnsGlobal()
 
268
        {
 
269
                updateTimer->disconnect(this);
 
270
                updateTimer->setParent(0);
 
271
                updateTimer->deleteLater();
 
272
 
 
273
                qDeleteAll(ifaces);
 
274
 
 
275
                QList<JDnsShared*> list;
 
276
                if(uni_net)
 
277
                        list += uni_net;
 
278
                if(uni_local)
 
279
                        list += uni_local;
 
280
                if(mul)
 
281
                        list += mul;
 
282
 
 
283
                // calls shutdown on the list, waits for shutdownFinished, deletes
 
284
                JDnsShared::waitForShutdown(list);
 
285
 
 
286
                // get final debug
 
287
                jdns_debugReady();
 
288
        }
 
289
 
 
290
        JDnsShared *ensure_uni_net()
 
291
        {
 
292
                if(!uni_net)
 
293
                {
 
294
                        uni_net = new JDnsShared(JDnsShared::UnicastInternet, this);
 
295
                        uni_net->setDebug(&db, "U");
 
296
                        bool ok4 = uni_net->addInterface(QHostAddress::Any);
 
297
                        bool ok6 = uni_net->addInterface(QHostAddress::AnyIPv6);
 
298
                        if(!ok4 && !ok6)
 
299
                        {
 
300
                                delete uni_net;
 
301
                                uni_net = 0;
 
302
                        }
 
303
                }
 
304
                return uni_net;
 
305
        }
 
306
 
 
307
        JDnsShared *ensure_uni_local()
 
308
        {
 
309
                if(!uni_local)
 
310
                {
 
311
                        uni_local = new JDnsShared(JDnsShared::UnicastLocal, this);
 
312
                        uni_local->setDebug(&db, "L");
 
313
                        bool ok4 = uni_local->addInterface(QHostAddress::Any);
 
314
                        bool ok6 = uni_local->addInterface(QHostAddress::AnyIPv6);
 
315
                        if(!ok4 && !ok6)
 
316
                        {
 
317
                                delete uni_local;
 
318
                                uni_local = 0;
 
319
                        }
 
320
                }
 
321
                return uni_local;
 
322
        }
 
323
 
 
324
        JDnsShared *ensure_mul()
 
325
        {
 
326
                if(!mul)
 
327
                {
 
328
                        mul = new JDnsShared(JDnsShared::Multicast, this);
 
329
                        mul->setDebug(&db, "M");
 
330
 
 
331
                        connect(&netman, SIGNAL(interfaceAvailable(const QString &)), SLOT(iface_available(const QString &)));
 
332
 
 
333
                        // get the current network interfaces.  this initial
 
334
                        //   fetching should not trigger any calls to
 
335
                        //   updateMulticastInterfaces().  only future
 
336
                        //   activity should do that.
 
337
                        foreach(const QString &id, netman.interfaces())
 
338
                        {
 
339
                                NetInterface *iface = new NetInterface(id, &netman);
 
340
                                connect(iface, SIGNAL(unavailable()), SLOT(iface_unavailable()));
 
341
                                ifaces += iface;
 
342
                        }
 
343
 
 
344
                        updateMulticastInterfaces(false);
 
345
                }
 
346
                return mul;
 
347
        }
 
348
 
 
349
        bool haveMulticast4() const
 
350
        {
 
351
                return !mul_addr4.isNull();
 
352
        }
 
353
 
 
354
        bool haveMulticast6() const
 
355
        {
 
356
                return !mul_addr6.isNull();
 
357
        }
 
358
 
 
359
signals:
 
360
        void interfacesChanged();
 
361
 
 
362
private slots:
 
363
        void jdns_debugReady()
 
364
        {
 
365
                QStringList lines = db.readDebugLines();
 
366
#ifdef JDNS_DEBUG
 
367
                for(int n = 0; n < lines.count(); ++n)
 
368
                        printf("jdns: %s\n", qPrintable(lines[n]));
 
369
#else
 
370
                Q_UNUSED(lines);
 
371
#endif
 
372
        }
 
373
 
 
374
        void iface_available(const QString &id)
 
375
        {
 
376
                NetInterface *iface = new NetInterface(id, &netman);
 
377
                connect(iface, SIGNAL(unavailable()), SLOT(iface_unavailable()));
 
378
                ifaces += iface;
 
379
 
 
380
                updateTimer->start(100);
 
381
        }
 
382
 
 
383
        void iface_unavailable()
 
384
        {
 
385
                NetInterface *iface = (NetInterface *)sender();
 
386
                ifaces.removeAll(iface);
 
387
                delete iface;
 
388
 
 
389
                updateTimer->start(100);
 
390
        }
 
391
 
 
392
        void doUpdateMulticastInterfaces()
 
393
        {
 
394
                updateMulticastInterfaces(true);
 
395
        }
 
396
 
 
397
private:
 
398
        void updateMulticastInterfaces(bool useSignals)
 
399
        {
 
400
                QHostAddress addr4 = QJDns::detectPrimaryMulticast(QHostAddress::Any);
 
401
                QHostAddress addr6 = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6);
 
402
 
 
403
                bool had4 = !mul_addr4.isNull();
 
404
                bool had6 = !mul_addr6.isNull();
 
405
 
 
406
                updateMulticastInterface(&mul_addr4, addr4);
 
407
                updateMulticastInterface(&mul_addr6, addr6);
 
408
 
 
409
                bool have4 = !mul_addr4.isNull();
 
410
                bool have6 = !mul_addr6.isNull();
 
411
 
 
412
                // did we gain/lose something?
 
413
                if(had4 != have4 || had6 != have6)
 
414
                {
 
415
                        if(useSignals)
 
416
                                emit interfacesChanged();
 
417
                }
 
418
        }
 
419
 
 
420
        void updateMulticastInterface(QHostAddress *curaddr, const QHostAddress &newaddr)
 
421
        {
 
422
                if(!(newaddr == *curaddr)) // QHostAddress doesn't have operator!=
 
423
                {
 
424
                        if(!curaddr->isNull())
 
425
                                mul->removeInterface(*curaddr);
 
426
                        *curaddr = newaddr;
 
427
                        if(!curaddr->isNull())
 
428
                        {
 
429
                                if(!mul->addInterface(*curaddr))
 
430
                                        *curaddr = QHostAddress();
 
431
                        }
 
432
                }
 
433
        }
 
434
};
 
435
 
 
436
//----------------------------------------------------------------------------
 
437
// JDnsNameProvider
 
438
//----------------------------------------------------------------------------
 
439
class JDnsNameProvider : public NameProvider
 
440
{
 
441
        Q_OBJECT
 
442
        Q_INTERFACES(XMPP::NameProvider)
 
443
 
 
444
public:
 
445
        enum Mode { Internet, Local };
 
446
 
 
447
        JDnsGlobal *global;
 
448
        Mode mode;
 
449
        IdManager idman;
 
450
        ObjectSession sess;
 
451
 
 
452
        class Item
 
453
        {
 
454
        public:
 
455
                int id;
 
456
                JDnsSharedRequest *req;
 
457
                int type;
 
458
                bool longLived;
 
459
                ObjectSession sess;
 
460
                bool localResult;
 
461
 
 
462
                Item(QObject *parent = 0) :
 
463
                        id(-1),
 
464
                        req(0),
 
465
                        sess(parent),
 
466
                        localResult(false)
 
467
                {
 
468
                }
 
469
 
 
470
                ~Item()
 
471
                {
 
472
                        delete req;
 
473
                }
 
474
        };
 
475
        QList<Item*> items;
 
476
 
 
477
        static JDnsNameProvider *create(JDnsGlobal *global, Mode mode, QObject *parent = 0)
 
478
        {
 
479
                if(mode == Internet)
 
480
                {
 
481
                        if(!global->ensure_uni_net())
 
482
                                return 0;
 
483
                }
 
484
                else
 
485
                {
 
486
                        if(!global->ensure_uni_local())
 
487
                                return 0;
 
488
                }
 
489
 
 
490
                return new JDnsNameProvider(global, mode, parent);
 
491
        }
 
492
 
 
493
        JDnsNameProvider(JDnsGlobal *_global, Mode _mode, QObject *parent = 0) :
 
494
                NameProvider(parent)
 
495
        {
 
496
                global = _global;
 
497
                mode = _mode;
 
498
        }
 
499
 
 
500
        ~JDnsNameProvider()
 
501
        {
 
502
                qDeleteAll(items);
 
503
        }
 
504
 
 
505
        Item *getItemById(int id)
 
506
        {
 
507
                for(int n = 0; n < items.count(); ++n)
 
508
                {
 
509
                        if(items[n]->id == id)
 
510
                                return items[n];
 
511
                }
 
512
 
 
513
                return 0;
 
514
        }
 
515
 
 
516
        Item *getItemByReq(JDnsSharedRequest *req)
 
517
        {
 
518
                for(int n = 0; n < items.count(); ++n)
 
519
                {
 
520
                        if(items[n]->req == req)
 
521
                                return items[n];
 
522
                }
 
523
 
 
524
                return 0;
 
525
        }
 
526
 
 
527
        void releaseItem(Item *i)
 
528
        {
 
529
                idman.releaseId(i->id);
 
530
                items.removeAll(i);
 
531
                delete i;
 
532
        }
 
533
 
 
534
        virtual bool supportsSingle() const
 
535
        {
 
536
                return true;
 
537
        }
 
538
 
 
539
        virtual bool supportsLongLived() const
 
540
        {
 
541
                if(mode == Local)
 
542
                        return true;  // we support long-lived local queries
 
543
                else
 
544
                        return false; // we do NOT support long-lived internet queries
 
545
        }
 
546
 
 
547
        virtual bool supportsRecordType(int type) const
 
548
        {
 
549
                // all record types supported
 
550
                Q_UNUSED(type);
 
551
                return true;
 
552
        }
 
553
 
 
554
        virtual int resolve_start(const QByteArray &name, int qType, bool longLived)
 
555
        {
 
556
                if(mode == Internet)
 
557
                {
 
558
                        // if query ends in .local, switch to local resolver
 
559
                        if(name.right(6) == ".local" || name.right(7) == ".local.")
 
560
                        {
 
561
                                Item *i = new Item(this);
 
562
                                i->id = idman.reserveId();
 
563
                                i->longLived = longLived;
 
564
                                items += i;
 
565
                                i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name));
 
566
                                return i->id;
 
567
                        }
 
568
 
 
569
                        // we don't support long-lived internet queries
 
570
                        if(longLived)
 
571
                        {
 
572
                                Item *i = new Item(this);
 
573
                                i->id = idman.reserveId();
 
574
                                items += i;
 
575
                                i->sess.defer(this, "do_error", Q_ARG(int, i->id),
 
576
                                        Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLongLived));
 
577
                                return i->id;
 
578
                        }
 
579
 
 
580
                        // perform the query
 
581
                        Item *i = new Item(this);
 
582
                        i->id = idman.reserveId();
 
583
                        i->req = new JDnsSharedRequest(global->uni_net);
 
584
                        connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
 
585
                        i->type = qType;
 
586
                        i->longLived = false;
 
587
                        items += i;
 
588
                        i->req->query(name, qType);
 
589
                        return i->id;
 
590
                }
 
591
                else
 
592
                {
 
593
                        Item *i = new Item(this);
 
594
                        i->id = idman.reserveId();
 
595
                        i->type = qType;
 
596
                        if(longLived)
 
597
                        {
 
598
                                if(!global->ensure_mul())
 
599
                                {
 
600
                                        items += i;
 
601
                                        i->sess.defer(this, "do_error", Q_ARG(int, i->id),
 
602
                                                Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLocal));
 
603
                                        return i->id;
 
604
                                }
 
605
 
 
606
                                i->req = new JDnsSharedRequest(global->mul);
 
607
                                i->longLived = true;
 
608
                        }
 
609
                        else
 
610
                        {
 
611
                                i->req = new JDnsSharedRequest(global->uni_local);
 
612
                                i->longLived = false;
 
613
                        }
 
614
                        connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
 
615
                        items += i;
 
616
                        i->req->query(name, qType);
 
617
                        return i->id;
 
618
                }
 
619
        }
 
620
 
 
621
        virtual void resolve_stop(int id)
 
622
        {
 
623
                Item *i = getItemById(id);
 
624
                Q_ASSERT(i);
 
625
 
 
626
                if(i->req)
 
627
                        i->req->cancel();
 
628
                releaseItem(i);
 
629
        }
 
630
 
 
631
        virtual void resolve_localResultsReady(int id, const QList<XMPP::NameRecord> &results)
 
632
        {
 
633
                Item *i = getItemById(id);
 
634
                Q_ASSERT(i);
 
635
                Q_ASSERT(!i->localResult);
 
636
 
 
637
                i->localResult = true;
 
638
                i->sess.defer(this, "do_local_ready", Q_ARG(int, id),
 
639
                        Q_ARG(QList<XMPP::NameRecord>, results));
 
640
        }
 
641
 
 
642
        virtual void resolve_localError(int id, XMPP::NameResolver::Error e)
 
643
        {
 
644
                Item *i = getItemById(id);
 
645
                Q_ASSERT(i);
 
646
                Q_ASSERT(!i->localResult);
 
647
 
 
648
                i->localResult = true;
 
649
                i->sess.defer(this, "do_local_error", Q_ARG(int, id),
 
650
                        Q_ARG(XMPP::NameResolver::Error, e));
 
651
        }
 
652
 
 
653
private slots:
 
654
        void req_resultsReady()
 
655
        {
 
656
                JDnsSharedRequest *req = (JDnsSharedRequest *)sender();
 
657
                Item *i = getItemByReq(req);
 
658
                Q_ASSERT(i);
 
659
 
 
660
                int id = i->id;
 
661
 
 
662
                if(req->success())
 
663
                {
 
664
                        QList<NameRecord> out;
 
665
                        foreach(const QJDns::Record &r, req->results())
 
666
                        {
 
667
                                // unless we are asking for all types, only
 
668
                                //   accept the type we asked for
 
669
                                if(i->type == QJDns::Any || r.type == i->type)
 
670
                                {
 
671
                                        NameRecord rec = importJDNSRecord(r);
 
672
                                        if(!rec.isNull())
 
673
                                                out += rec;
 
674
                                }
 
675
                        }
 
676
 
 
677
                        // don't report anything if long-lived gives no results
 
678
                        if(i->longLived && out.isEmpty())
 
679
                                return;
 
680
 
 
681
                        // only emit success if we have at least 1 result
 
682
                        if(!out.isEmpty())
 
683
                        {
 
684
                                if(!i->longLived)
 
685
                                        releaseItem(i);
 
686
                                emit resolve_resultsReady(id, out);
 
687
                        }
 
688
                        else
 
689
                        {
 
690
                                releaseItem(i);
 
691
                                emit resolve_error(id, NameResolver::ErrorGeneric);
 
692
                        }
 
693
                }
 
694
                else
 
695
                {
 
696
                        JDnsSharedRequest::Error e = req->error();
 
697
                        releaseItem(i);
 
698
 
 
699
                        NameResolver::Error error = NameResolver::ErrorGeneric;
 
700
                        if(e == JDnsSharedRequest::ErrorNXDomain)
 
701
                                error = NameResolver::ErrorNoName;
 
702
                        else if(e == JDnsSharedRequest::ErrorTimeout)
 
703
                                error = NameResolver::ErrorTimeout;
 
704
                        else // ErrorGeneric or ErrorNoNet
 
705
                                error = NameResolver::ErrorGeneric;
 
706
                        emit resolve_error(id, error);
 
707
                }
 
708
        }
 
709
 
 
710
        void do_error(int id, XMPP::NameResolver::Error e)
 
711
        {
 
712
                Item *i = getItemById(id);
 
713
                Q_ASSERT(i);
 
714
 
 
715
                releaseItem(i);
 
716
                emit resolve_error(id, e);
 
717
        }
 
718
 
 
719
        void do_local(int id, const QByteArray &name)
 
720
        {
 
721
                Item *i = getItemById(id);
 
722
                Q_ASSERT(i);
 
723
 
 
724
                // resolve_useLocal has two behaviors:
 
725
                // - if longlived, then it indicates a hand-off
 
726
                // - if non-longlived, then it indicates we want a subquery
 
727
 
 
728
                if(i->longLived)
 
729
                        releaseItem(i);
 
730
                emit resolve_useLocal(id, name);
 
731
        }
 
732
 
 
733
        void do_local_ready(int id, const QList<XMPP::NameRecord> &results)
 
734
        {
 
735
                Item *i = getItemById(id);
 
736
                Q_ASSERT(i);
 
737
 
 
738
                // only non-longlived queries come through here, so we're done
 
739
                releaseItem(i);
 
740
                emit resolve_resultsReady(id, results);
 
741
        }
 
742
 
 
743
        void do_local_error(int id, XMPP::NameResolver::Error e)
 
744
        {
 
745
                Item *i = getItemById(id);
 
746
                Q_ASSERT(i);
 
747
 
 
748
                releaseItem(i);
 
749
                emit resolve_error(id, e);
 
750
        }
 
751
};
 
752
 
 
753
//----------------------------------------------------------------------------
 
754
// JDnsBrowse
 
755
//----------------------------------------------------------------------------
 
756
class JDnsBrowse : public QObject
 
757
{
 
758
        Q_OBJECT
 
759
 
 
760
public:
 
761
        QByteArray type, typeAndDomain;
 
762
        JDnsSharedRequest req;
 
763
 
 
764
        JDnsBrowse(JDnsShared *_jdns, QObject *parent = 0) :
 
765
                QObject(parent),
 
766
                req(_jdns, this)
 
767
        {
 
768
                connect(&req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady()));
 
769
        }
 
770
 
 
771
        void start(const QByteArray &_type)
 
772
        {
 
773
                type = _type;
 
774
                Q_ASSERT(validServiceType(type));
 
775
                typeAndDomain = type + ".local.";
 
776
                req.query(typeAndDomain, QJDns::Ptr);
 
777
        }
 
778
 
 
779
signals:
 
780
        void available(const QByteArray &instance);
 
781
        void unavailable(const QByteArray &instance);
 
782
 
 
783
private:
 
784
        QByteArray parseInstanceName(const QByteArray &name)
 
785
        {
 
786
                // needs to be at least X + '.' + typeAndDomain
 
787
                if(name.length() < typeAndDomain.length() + 2)
 
788
                        return QByteArray();
 
789
 
 
790
                // index of the '.' character
 
791
                int at = name.length() - typeAndDomain.length() - 1;
 
792
 
 
793
                if(name[at] != '.')
 
794
                        return QByteArray();
 
795
                if(name.mid(at + 1) != typeAndDomain)
 
796
                        return QByteArray();
 
797
 
 
798
                QByteArray friendlyName = unescapeDomainPart(name.mid(0, at));
 
799
                if(friendlyName.isEmpty())
 
800
                        return QByteArray();
 
801
 
 
802
                return friendlyName;
 
803
        }
 
804
 
 
805
private slots:
 
806
        void jdns_resultsReady()
 
807
        {
 
808
                // ignore errors
 
809
                if(!req.success())
 
810
                        return;
 
811
 
 
812
                QJDns::Record rec = req.results().first();
 
813
 
 
814
                Q_ASSERT(rec.type == QJDns::Ptr);
 
815
 
 
816
                QByteArray name = rec.name;
 
817
                QByteArray instance = parseInstanceName(name);
 
818
                if(instance.isEmpty())
 
819
                        return;
 
820
 
 
821
                if(rec.ttl == 0)
 
822
                {
 
823
                        emit unavailable(instance);
 
824
                        return;
 
825
                }
 
826
 
 
827
                emit available(instance);
 
828
        }
 
829
};
 
830
 
 
831
//----------------------------------------------------------------------------
 
832
// JDnsServiceResolve
 
833
//----------------------------------------------------------------------------
 
834
 
 
835
// 5 second timeout waiting for both A and AAAA
 
836
// 8 second timeout waiting for at least one record
 
837
class JDnsServiceResolve : public QObject
 
838
{
 
839
        Q_OBJECT
 
840
 
 
841
public:
 
842
        enum SrvState
 
843
        {
 
844
                Srv               = 0,
 
845
                AddressWait       = 1,
 
846
                AddressFirstCome  = 2
 
847
        };
 
848
 
 
849
        JDnsSharedRequest reqtxt; // for TXT
 
850
        JDnsSharedRequest req;    // for SRV/A
 
851
        JDnsSharedRequest req6;   // for AAAA
 
852
        bool haveTxt;
 
853
        SrvState srvState;
 
854
        QTimer *opTimer;
 
855
 
 
856
        // out
 
857
        QList<QByteArray> attribs;
 
858
        QByteArray host;
 
859
        int port;
 
860
        bool have4, have6;
 
861
        QHostAddress addr4, addr6;
 
862
 
 
863
        JDnsServiceResolve(JDnsShared *_jdns, QObject *parent = 0) :
 
864
                QObject(parent),
 
865
                reqtxt(_jdns, this),
 
866
                req(_jdns, this),
 
867
                req6(_jdns, this)
 
868
        {
 
869
                connect(&reqtxt, SIGNAL(resultsReady()), SLOT(reqtxt_ready()));
 
870
                connect(&req, SIGNAL(resultsReady()), SLOT(req_ready()));
 
871
                connect(&req6, SIGNAL(resultsReady()), SLOT(req6_ready()));
 
872
 
 
873
                opTimer = new QTimer(this);
 
874
                connect(opTimer, SIGNAL(timeout()), SLOT(op_timeout()));
 
875
                opTimer->setSingleShot(true);
 
876
        }
 
877
 
 
878
        ~JDnsServiceResolve()
 
879
        {
 
880
                opTimer->disconnect(this);
 
881
                opTimer->setParent(0);
 
882
                opTimer->deleteLater();
 
883
        }
 
884
 
 
885
        void start(const QByteArray name)
 
886
        {
 
887
                haveTxt = false;
 
888
                srvState = Srv;
 
889
                have4 = false;
 
890
                have6 = false;
 
891
 
 
892
                opTimer->start(8000);
 
893
 
 
894
                reqtxt.query(name, QJDns::Txt);
 
895
                req.query(name, QJDns::Srv);
 
896
        }
 
897
 
 
898
signals:
 
899
        void finished();
 
900
        void error(JDnsSharedRequest::Error e);
 
901
 
 
902
private:
 
903
        void cleanup()
 
904
        {
 
905
                if(opTimer->isActive())
 
906
                        opTimer->stop();
 
907
                if(!haveTxt)
 
908
                        reqtxt.cancel();
 
909
                if(srvState == Srv || !have4)
 
910
                        req.cancel();
 
911
                if(srvState >= AddressWait && !have6)
 
912
                        req6.cancel();
 
913
        }
 
914
 
 
915
        bool tryDone()
 
916
        {
 
917
                // we're done when we have txt and addresses
 
918
                if(haveTxt && ( (have4 && have6) || (srvState == AddressFirstCome && (have4 || have6)) ))
 
919
                {
 
920
                        cleanup();
 
921
                        emit finished();
 
922
                        return true;
 
923
                }
 
924
 
 
925
                return false;
 
926
        }
 
927
 
 
928
private slots:
 
929
        void reqtxt_ready()
 
930
        {
 
931
                if(!reqtxt.success())
 
932
                {
 
933
                        cleanup();
 
934
                        emit error(reqtxt.error());
 
935
                        return;
 
936
                }
 
937
 
 
938
                QJDns::Record rec = reqtxt.results().first();
 
939
                reqtxt.cancel();
 
940
 
 
941
                Q_ASSERT(rec.type == QJDns::Txt);
 
942
 
 
943
                attribs.clear();
 
944
                if(!rec.texts.isEmpty())
 
945
                {
 
946
                        // if there is only 1 text, it needs to be
 
947
                        //   non-empty for us to care
 
948
                        if(rec.texts.count() != 1 || !rec.texts[0].isEmpty())
 
949
                                attribs = rec.texts;
 
950
                }
 
951
 
 
952
                haveTxt = true;
 
953
 
 
954
                tryDone();
 
955
        }
 
956
 
 
957
        void req_ready()
 
958
        {
 
959
                if(!req.success())
 
960
                {
 
961
                        cleanup();
 
962
                        emit error(req.error());
 
963
                        return;
 
964
                }
 
965
 
 
966
                QJDns::Record rec = req.results().first();
 
967
                req.cancel();
 
968
 
 
969
                if(srvState == Srv)
 
970
                {
 
971
                        // in Srv state, req is used for SRV records
 
972
 
 
973
                        Q_ASSERT(rec.type == QJDns::Srv);
 
974
 
 
975
                        host = rec.name;
 
976
                        port = rec.port;
 
977
 
 
978
                        srvState = AddressWait;
 
979
                        opTimer->start(5000);
 
980
 
 
981
                        req.query(host, QJDns::A);
 
982
                        req6.query(host, QJDns::Aaaa);
 
983
                }
 
984
                else
 
985
                {
 
986
                        // in the other states, req is used for A records
 
987
 
 
988
                        Q_ASSERT(rec.type == QJDns::A);
 
989
 
 
990
                        addr4 = rec.address;
 
991
                        have4 = true;
 
992
 
 
993
                        tryDone();
 
994
                }
 
995
        }
 
996
 
 
997
        void req6_ready()
 
998
        {
 
999
                if(!req6.success())
 
1000
                {
 
1001
                        cleanup();
 
1002
                        emit error(req6.error());
 
1003
                        return;
 
1004
                }
 
1005
 
 
1006
                QJDns::Record rec = req6.results().first();
 
1007
                req6.cancel();
 
1008
 
 
1009
                Q_ASSERT(rec.type == QJDns::Aaaa);
 
1010
 
 
1011
                addr6 = rec.address;
 
1012
                have6 = true;
 
1013
 
 
1014
                tryDone();
 
1015
        }
 
1016
 
 
1017
        void op_timeout()
 
1018
        {
 
1019
                if(srvState == Srv)
 
1020
                {
 
1021
                        // timeout getting SRV.  it is possible that we could
 
1022
                        //   have obtained the TXT record, but if SRV times
 
1023
                        //   out then we consider the whole job to have
 
1024
                        //   failed.
 
1025
                        cleanup();
 
1026
                        emit error(JDnsSharedRequest::ErrorTimeout);
 
1027
                }
 
1028
                else if(srvState == AddressWait)
 
1029
                {
 
1030
                        // timeout while waiting for both A and AAAA.  we now
 
1031
                        //   switch to the AddressFirstCome state, where an
 
1032
                        //   answer for either will do
 
1033
 
 
1034
                        srvState = AddressFirstCome;
 
1035
 
 
1036
                        // if we have at least one of these, we're done
 
1037
                        if(have4 || have6)
 
1038
                        {
 
1039
                                // well, almost.  we might still be waiting
 
1040
                                //   for the TXT record
 
1041
                                if(tryDone())
 
1042
                                        return;
 
1043
                        }
 
1044
 
 
1045
                        // if we are here, then it means we are missing TXT
 
1046
                        //   still, or we have neither A nor AAAA.
 
1047
 
 
1048
                        // wait 3 more seconds
 
1049
                        opTimer->start(3000);
 
1050
                }
 
1051
                else // AddressFirstCome
 
1052
                {
 
1053
                        // last chance!
 
1054
                        if(!tryDone())
 
1055
                        {
 
1056
                                cleanup();
 
1057
                                emit error(JDnsSharedRequest::ErrorTimeout);
 
1058
                        }
 
1059
                }
 
1060
        }
 
1061
};
 
1062
 
 
1063
//----------------------------------------------------------------------------
 
1064
// JDnsPublishAddresses
 
1065
//----------------------------------------------------------------------------
 
1066
 
 
1067
// helper class for JDnsPublishAddresses.  publishes A+PTR or AAAA+PTR pair.
 
1068
class JDnsPublishAddress : public QObject
 
1069
{
 
1070
        Q_OBJECT
 
1071
 
 
1072
public:
 
1073
        enum Type
 
1074
        {
 
1075
                IPv4,
 
1076
                IPv6
 
1077
        };
 
1078
 
 
1079
        Type type;
 
1080
        QByteArray host;
 
1081
        JDnsSharedRequest pub_addr;
 
1082
        JDnsSharedRequest pub_ptr;
 
1083
        bool success_;
 
1084
 
 
1085
        JDnsPublishAddress(JDnsShared *_jdns, QObject *parent = 0) :
 
1086
                QObject(parent),
 
1087
                pub_addr(_jdns, this),
 
1088
                pub_ptr(_jdns, this)
 
1089
        {
 
1090
                connect(&pub_addr, SIGNAL(resultsReady()), SLOT(pub_addr_ready()));
 
1091
                connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready()));
 
1092
        }
 
1093
 
 
1094
        void start(Type _type, const QByteArray &_host)
 
1095
        {
 
1096
                type = _type;
 
1097
                host = _host;
 
1098
                success_ = false;
 
1099
 
 
1100
                QJDns::Record rec;
 
1101
                if(type == IPv6)
 
1102
                        rec.type = QJDns::Aaaa;
 
1103
                else
 
1104
                        rec.type = QJDns::A;
 
1105
                rec.owner = host;
 
1106
                rec.ttl = 120;
 
1107
                rec.haveKnown = true;
 
1108
                rec.address = QHostAddress(); // null address, will be filled in
 
1109
                pub_addr.publish(QJDns::Unique, rec);
 
1110
        }
 
1111
 
 
1112
        void cancel()
 
1113
        {
 
1114
                pub_addr.cancel();
 
1115
                pub_ptr.cancel();
 
1116
        }
 
1117
 
 
1118
        bool success() const
 
1119
        {
 
1120
                return success_;
 
1121
        }
 
1122
 
 
1123
signals:
 
1124
        void resultsReady();
 
1125
 
 
1126
private slots:
 
1127
        void pub_addr_ready()
 
1128
        {
 
1129
                if(pub_addr.success())
 
1130
                {
 
1131
                        QJDns::Record rec;
 
1132
                        rec.type = QJDns::Ptr;
 
1133
                        if(type == IPv6)
 
1134
                                rec.owner = ".ip6.arpa.";
 
1135
                        else
 
1136
                                rec.owner = ".in-addr.arpa.";
 
1137
                        rec.ttl = 120;
 
1138
                        rec.haveKnown = true;
 
1139
                        rec.name = host;
 
1140
                        pub_ptr.publish(QJDns::Shared, rec);
 
1141
                }
 
1142
                else
 
1143
                {
 
1144
                        pub_ptr.cancel(); // needed if addr fails during or after ptr
 
1145
                        success_ = false;
 
1146
                        emit resultsReady();
 
1147
                }
 
1148
        }
 
1149
 
 
1150
        void pub_ptr_ready()
 
1151
        {
 
1152
                if(pub_ptr.success())
 
1153
                {
 
1154
                        success_ = true;
 
1155
                }
 
1156
                else
 
1157
                {
 
1158
                        pub_addr.cancel();
 
1159
                        success_ = false;
 
1160
                }
 
1161
 
 
1162
                emit resultsReady();
 
1163
        }
 
1164
};
 
1165
 
 
1166
// This class publishes A/AAAA records for the machine, using a derived
 
1167
//   hostname (it will use QHostInfo::localHostName(), but append a unique
 
1168
//   suffix if necessary).  If there is ever a record conflict, it will
 
1169
//   republish under a unique name.
 
1170
//
 
1171
// The hostName() signal is emitted when a hostname is successfully
 
1172
//   published as.  When there is a conflict, hostName() is emitted with
 
1173
//   an empty value, and will again be emitted with a non-empty value
 
1174
//   once the conflict is resolved.  A missing hostname is considered a
 
1175
//   temporary problem, and so other publish operations that depend on a
 
1176
//   hostname (SRV, etc) should block until a hostname is available.
 
1177
class JDnsPublishAddresses : public QObject
 
1178
{
 
1179
        Q_OBJECT
 
1180
 
 
1181
public:
 
1182
        bool started;
 
1183
        bool use6, use4;
 
1184
        JDnsPublishAddress pub6;
 
1185
        JDnsPublishAddress pub4;
 
1186
        int counter;
 
1187
        QByteArray host;
 
1188
        bool success;
 
1189
        bool have6, have4;
 
1190
        ObjectSession sess;
 
1191
 
 
1192
        JDnsPublishAddresses(JDnsShared *_jdns, QObject *parent = 0) :
 
1193
                QObject(parent),
 
1194
                started(false),
 
1195
                use6(false),
 
1196
                use4(false),
 
1197
                pub6(_jdns, this),
 
1198
                pub4(_jdns, this),
 
1199
                sess(this)
 
1200
        {
 
1201
                connect(&pub6, SIGNAL(resultsReady()), SLOT(pub6_ready()));
 
1202
                connect(&pub4, SIGNAL(resultsReady()), SLOT(pub4_ready()));
 
1203
        }
 
1204
 
 
1205
        void start()
 
1206
        {
 
1207
                counter = 1;
 
1208
                success = false;
 
1209
                have6 = false;
 
1210
                have4 = false;
 
1211
                started = true;
 
1212
                tryPublish();
 
1213
        }
 
1214
 
 
1215
        bool isStarted() const
 
1216
        {
 
1217
                return started;
 
1218
        }
 
1219
 
 
1220
        // comments in this method apply to setUseIPv4 as well.
 
1221
        void setUseIPv6(bool b)
 
1222
        {
 
1223
                if(b == use6)
 
1224
                        return;
 
1225
 
 
1226
                use6 = b;
 
1227
                if(!started)
 
1228
                        return;
 
1229
 
 
1230
                // a "deferred call to doDisable" and "publish operations"
 
1231
                //   are mutually exclusive.  thus, a deferred call is only
 
1232
                //   invoked when both publishes are canceled, and the
 
1233
                //   deferred call is canceled if any of the publishes are
 
1234
                //   reinstantiated.
 
1235
 
 
1236
                if(use6)
 
1237
                {
 
1238
                        if(use4)
 
1239
                        {
 
1240
                                // if the other is already active, then
 
1241
                                //   just activate this one without
 
1242
                                //   recomputing the hostname
 
1243
                                tryPublish6();
 
1244
                        }
 
1245
                        else
 
1246
                        {
 
1247
                                sess.reset();
 
1248
 
 
1249
                                // otherwise, recompute the hostname
 
1250
                                tryPublish();
 
1251
                        }
 
1252
                }
 
1253
                else
 
1254
                {
 
1255
                        pub6.cancel();
 
1256
                        have6 = false;
 
1257
                        if(!use4)
 
1258
                                sess.defer(this, "doDisable");
 
1259
                }
 
1260
        }
 
1261
 
 
1262
        void setUseIPv4(bool b)
 
1263
        {
 
1264
                if(b == use4)
 
1265
                        return;
 
1266
 
 
1267
                use4 = b;
 
1268
                if(!started)
 
1269
                        return;
 
1270
 
 
1271
                if(use4)
 
1272
                {
 
1273
                        if(use6)
 
1274
                        {
 
1275
                                tryPublish4();
 
1276
                        }
 
1277
                        else
 
1278
                        {
 
1279
                                sess.reset();
 
1280
                                tryPublish();
 
1281
                        }
 
1282
                }
 
1283
                else
 
1284
                {
 
1285
                        pub4.cancel();
 
1286
                        have4 = false;
 
1287
                        if(!use6)
 
1288
                                sess.defer(this, "doDisable");
 
1289
                }
 
1290
        }
 
1291
 
 
1292
signals:
 
1293
        void hostName(const QByteArray &str);
 
1294
 
 
1295
private:
 
1296
        void tryPublish()
 
1297
        {
 
1298
                QString me = QHostInfo::localHostName();
 
1299
 
 
1300
                // some hosts may already have ".local" in their name
 
1301
                if(me.endsWith(".local"))
 
1302
                        me.truncate(me.length() - 6);
 
1303
 
 
1304
                // prefix our hostname so we don't conflict with a system
 
1305
                //   mdns daemon
 
1306
                me.prepend("jdns-");
 
1307
 
 
1308
                if(counter > 1)
 
1309
                        me += QString("-%1").arg(counter);
 
1310
 
 
1311
                host = escapeDomainPart(me.toUtf8()) + ".local.";
 
1312
 
 
1313
                if(use6)
 
1314
                        tryPublish6();
 
1315
                if(use4)
 
1316
                        tryPublish4();
 
1317
        }
 
1318
 
 
1319
        void tryPublish6()
 
1320
        {
 
1321
                pub6.start(JDnsPublishAddress::IPv6, host);
 
1322
        }
 
1323
 
 
1324
        void tryPublish4()
 
1325
        {
 
1326
                pub4.start(JDnsPublishAddress::IPv4, host);
 
1327
        }
 
1328
 
 
1329
        void tryDone()
 
1330
        {
 
1331
                bool done = true;
 
1332
                if(use6 && !have6)
 
1333
                        done = false;
 
1334
                if(use4 && !have4)
 
1335
                        done = false;
 
1336
 
 
1337
                if(done)
 
1338
                {
 
1339
                        success = true;
 
1340
                        emit hostName(host);
 
1341
                }
 
1342
        }
 
1343
 
 
1344
        void handleFail()
 
1345
        {
 
1346
                // we get here if we fail to publish at all, or if we
 
1347
                //   successfully publish but then fail later on.  in the
 
1348
                //   latter case it means we "lost" our host records.
 
1349
 
 
1350
                bool lostHost = success; // as in earlier publish success
 
1351
                success = false;
 
1352
 
 
1353
                // if we lost a hostname with a suffix, or counter is
 
1354
                //   at 99, then start counter over at 1 (no suffix).
 
1355
                if((lostHost && counter > 1) || counter >= 99)
 
1356
                        counter = 1;
 
1357
                else
 
1358
                        ++counter;
 
1359
 
 
1360
                tryPublish();
 
1361
 
 
1362
                // only emit lost host signal once
 
1363
                if(lostHost)
 
1364
                        emit hostName(QByteArray());
 
1365
        }
 
1366
 
 
1367
private slots:
 
1368
        void doDisable()
 
1369
        {
 
1370
                bool lostHost = success;
 
1371
                success = false;
 
1372
 
 
1373
                if(lostHost)
 
1374
                        emit hostName(QByteArray());
 
1375
        }
 
1376
 
 
1377
        void pub6_ready()
 
1378
        {
 
1379
                if(pub6.success())
 
1380
                {
 
1381
                        have6 = true;
 
1382
                        tryDone();
 
1383
                }
 
1384
                else
 
1385
                {
 
1386
                        have6 = false;
 
1387
                        have4 = false;
 
1388
                        pub4.cancel();
 
1389
                        handleFail();
 
1390
                }
 
1391
        }
 
1392
 
 
1393
        void pub4_ready()
 
1394
        {
 
1395
                if(pub4.success())
 
1396
                {
 
1397
                        have4 = true;
 
1398
                        tryDone();
 
1399
                }
 
1400
                else
 
1401
                {
 
1402
                        have4 = false;
 
1403
                        have6 = false;
 
1404
                        pub6.cancel();
 
1405
                        handleFail();
 
1406
                }
 
1407
        }
 
1408
};
 
1409
 
 
1410
//----------------------------------------------------------------------------
 
1411
// JDnsPublish
 
1412
//----------------------------------------------------------------------------
 
1413
class JDnsPublish;
 
1414
 
 
1415
class JDnsPublishExtra : public QObject
 
1416
{
 
1417
        Q_OBJECT
 
1418
 
 
1419
public:
 
1420
        JDnsPublishExtra(JDnsPublish *_jdnsPub);
 
1421
        ~JDnsPublishExtra();
 
1422
 
 
1423
        void start(const QJDns::Record &_rec);
 
1424
        void update(const QJDns::Record &_rec);
 
1425
 
 
1426
signals:
 
1427
        void published();
 
1428
        void error(JDnsSharedRequest::Error e);
 
1429
 
 
1430
private:
 
1431
        friend class JDnsPublish;
 
1432
 
 
1433
        JDnsPublish *jdnsPub;
 
1434
        bool started;
 
1435
        JDnsSharedRequest pub;
 
1436
        QJDns::Record rec;
 
1437
        bool have;
 
1438
        bool need_update;
 
1439
};
 
1440
 
 
1441
// This class publishes SRV/TXT/PTR for a service.  if a hostName is not
 
1442
//   is not available (see JDnsPublishAddresses) then the publish action
 
1443
//   will be deferred until one is available.  SRV and TXT are published
 
1444
//   as unique records, and once they both succeed then the PTR record
 
1445
//   is published.  once the PTR succeeds, then published() is emitted.
 
1446
//   if a conflict occurs with any action, then the whole thing fails and
 
1447
//   error() is emitted.  if, at any time, the hostName is lost, then
 
1448
//   then the SRV operation is canceled, but no error is emitted.  when the
 
1449
//   hostName is regained, then the SRV record is republished.
 
1450
//
 
1451
// It's important to note that published() is only emitted once ever, even
 
1452
//   if a hostName change causes a republishing.  this way, hostName changes
 
1453
//   are completely transparent.
 
1454
class JDnsPublish : public QObject
 
1455
{
 
1456
        Q_OBJECT
 
1457
 
 
1458
public:
 
1459
        JDnsShared *jdns;
 
1460
        JDnsSharedRequest pub_srv;
 
1461
        JDnsSharedRequest pub_txt;
 
1462
        JDnsSharedRequest pub_ptr;
 
1463
 
 
1464
        bool have_srv, have_txt, have_ptr;
 
1465
        bool need_update_txt;
 
1466
 
 
1467
        QByteArray fullname;
 
1468
        QByteArray instance;
 
1469
        QByteArray type;
 
1470
        QByteArray host;
 
1471
        int port;
 
1472
        QList<QByteArray> attribs;
 
1473
 
 
1474
        QSet<JDnsPublishExtra*> extraList;
 
1475
 
 
1476
        JDnsPublish(JDnsShared *_jdns, QObject *parent = 0) :
 
1477
                QObject(parent),
 
1478
                jdns(_jdns),
 
1479
                pub_srv(_jdns, this),
 
1480
                pub_txt(_jdns, this),
 
1481
                pub_ptr(_jdns, this)
 
1482
        {
 
1483
                connect(&pub_srv, SIGNAL(resultsReady()), SLOT(pub_srv_ready()));
 
1484
                connect(&pub_txt, SIGNAL(resultsReady()), SLOT(pub_txt_ready()));
 
1485
                connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready()));
 
1486
        }
 
1487
 
 
1488
        ~JDnsPublish()
 
1489
        {
 
1490
                qDeleteAll(extraList);
 
1491
        }
 
1492
 
 
1493
        void start(const QString &_instance, const QByteArray &_type, const QByteArray &localHost, int _port, const QMap<QString,QByteArray> &attributes)
 
1494
        {
 
1495
                type = _type;
 
1496
                Q_ASSERT(validServiceType(type));
 
1497
 
 
1498
                instance = escapeDomainPart(_instance.toUtf8());
 
1499
                fullname = instance + '.' + type + ".local.";
 
1500
                host = localHost;
 
1501
                port = _port;
 
1502
                attribs = makeTxtList(attributes);
 
1503
 
 
1504
                have_srv = false;
 
1505
                have_txt = false;
 
1506
                have_ptr = false;
 
1507
                need_update_txt = false;
 
1508
 
 
1509
                // no host?  defer publishing till we have one
 
1510
                if(host.isEmpty())
 
1511
                        return;
 
1512
 
 
1513
                doPublish();
 
1514
        }
 
1515
 
 
1516
        void update(const QMap<QString,QByteArray> &attributes)
 
1517
        {
 
1518
                attribs = makeTxtList(attributes);
 
1519
 
 
1520
                // still publishing the initial txt?
 
1521
                if(!have_txt)
 
1522
                {
 
1523
                        // flag that we want to update once the publish
 
1524
                        //   succeeds.
 
1525
                        need_update_txt = true;
 
1526
                        return;
 
1527
                }
 
1528
 
 
1529
                // no SRV, but have TXT?  this means we lost SRV due to
 
1530
                //   a hostname change.
 
1531
                if(!have_srv)
 
1532
                {
 
1533
                        // in that case, revoke the TXT.  it'll get
 
1534
                        //   republished after SRV then.
 
1535
                        have_txt = false;
 
1536
                        pub_txt.cancel();
 
1537
                        return;
 
1538
                }
 
1539
 
 
1540
                doPublishTxt();
 
1541
        }
 
1542
 
 
1543
public slots:
 
1544
        // pass empty host if host lost
 
1545
        void hostChanged(const QByteArray &_host)
 
1546
        {
 
1547
                bool changed = (host != _host);
 
1548
 
 
1549
                if(changed)
 
1550
                {
 
1551
                        host = _host;
 
1552
 
 
1553
                        if(host.isEmpty())
 
1554
                        {
 
1555
                                // cancel srv record momentarily
 
1556
                                have_srv = false;
 
1557
                                pub_srv.cancel();
 
1558
                        }
 
1559
                        else
 
1560
                        {
 
1561
                                // we now have a host, publish
 
1562
                                doPublish();
 
1563
                        }
 
1564
                }
 
1565
        }
 
1566
 
 
1567
signals:
 
1568
        void published();
 
1569
        void error(JDnsSharedRequest::Error e);
 
1570
 
 
1571
private:
 
1572
        friend class JDnsPublishExtra;
 
1573
 
 
1574
        static QList<QByteArray> makeTxtList(const QMap<QString,QByteArray> &attributes)
 
1575
        {
 
1576
                QList<QByteArray> out;
 
1577
 
 
1578
                QMapIterator<QString,QByteArray> it(attributes);
 
1579
                while(it.hasNext())
 
1580
                {
 
1581
                        it.next();
 
1582
                        out += it.key().toLatin1() + '=' + it.value();
 
1583
                }
 
1584
                if(out.isEmpty())
 
1585
                        out += QByteArray();
 
1586
 
 
1587
                return out;
 
1588
        }
 
1589
 
 
1590
        void doPublish()
 
1591
        {
 
1592
                // SRV
 
1593
                QJDns::Record rec;
 
1594
                rec.type = QJDns::Srv;
 
1595
                rec.owner = fullname;
 
1596
                rec.ttl = 120;
 
1597
                rec.haveKnown = true;
 
1598
                rec.name = host;
 
1599
                rec.port = port;
 
1600
                rec.priority = 0;
 
1601
                rec.weight = 0;
 
1602
                pub_srv.publish(QJDns::Unique, rec);
 
1603
 
 
1604
                // if we're just republishing SRV after losing/regaining
 
1605
                //   our hostname, then TXT is already published
 
1606
                if(!have_txt)
 
1607
                        doPublishTxt();
 
1608
 
 
1609
                // publish extra records as needed
 
1610
                foreach(JDnsPublishExtra *extra, extraList)
 
1611
                {
 
1612
                        if(!extra->have)
 
1613
                                doPublishExtra(extra);
 
1614
                }
 
1615
        }
 
1616
 
 
1617
        void doPublishTxt()
 
1618
        {
 
1619
                // TXT
 
1620
                QJDns::Record rec;
 
1621
                rec.type = QJDns::Txt;
 
1622
                rec.owner = fullname;
 
1623
                rec.ttl = 4500;
 
1624
                rec.haveKnown = true;
 
1625
                rec.texts = attribs;
 
1626
 
 
1627
                if(!have_txt)
 
1628
                        pub_txt.publish(QJDns::Unique, rec);
 
1629
                else
 
1630
                        pub_txt.publishUpdate(rec);
 
1631
        }
 
1632
 
 
1633
        void tryDone()
 
1634
        {
 
1635
                if(have_srv && have_txt)
 
1636
                {
 
1637
                        // PTR
 
1638
                        QJDns::Record rec;
 
1639
                        rec.type = QJDns::Ptr;
 
1640
                        rec.owner = type + ".local.";
 
1641
                        rec.ttl = 4500;
 
1642
                        rec.haveKnown = true;
 
1643
                        rec.name = fullname;
 
1644
                        pub_ptr.publish(QJDns::Shared, rec);
 
1645
                }
 
1646
        }
 
1647
 
 
1648
        void cleanup()
 
1649
        {
 
1650
                foreach(JDnsPublishExtra *extra, extraList)
 
1651
                        cleanupExtra(extra);
 
1652
                qDeleteAll(extraList);
 
1653
                extraList.clear();
 
1654
 
 
1655
                have_srv = false;
 
1656
                have_txt = false;
 
1657
                have_ptr = false;
 
1658
                pub_srv.cancel();
 
1659
                pub_txt.cancel();
 
1660
                pub_ptr.cancel();
 
1661
        }
 
1662
 
 
1663
        void publishExtra(JDnsPublishExtra *extra)
 
1664
        {
 
1665
                Q_ASSERT(!extraList.contains(extra));
 
1666
 
 
1667
                connect(&extra->pub, SIGNAL(resultsReady()), SLOT(pub_extra_ready()));
 
1668
                extraList += extra;
 
1669
 
 
1670
                // defer publishing until SRV is ready
 
1671
                if(!have_srv)
 
1672
                        return;
 
1673
 
 
1674
                doPublishExtra(extra);
 
1675
        }
 
1676
 
 
1677
        void publishExtraUpdate(JDnsPublishExtra *extra)
 
1678
        {
 
1679
                if(!extra->have)
 
1680
                {
 
1681
                        extra->need_update = true;
 
1682
                        return;
 
1683
                }
 
1684
 
 
1685
                if(!have_srv)
 
1686
                {
 
1687
                        extra->have = false;
 
1688
                        extra->pub.cancel();
 
1689
                        return;
 
1690
                }
 
1691
 
 
1692
                doPublishExtra(extra);
 
1693
        }
 
1694
 
 
1695
        void unpublishExtra(JDnsPublishExtra *extra)
 
1696
        {
 
1697
                extraList.remove(extra);
 
1698
        }
 
1699
 
 
1700
        void doPublishExtra(JDnsPublishExtra *extra)
 
1701
        {
 
1702
                if(!extra->have)
 
1703
                        extra->pub.publish(QJDns::Unique, extra->rec);
 
1704
                else
 
1705
                        extra->pub.publishUpdate(extra->rec);
 
1706
        }
 
1707
 
 
1708
        void cleanupExtra(JDnsPublishExtra *extra)
 
1709
        {
 
1710
                extra->pub.cancel();
 
1711
                extra->disconnect(this);
 
1712
                extra->started = false;
 
1713
                extra->have = false;
 
1714
        }
 
1715
 
 
1716
private slots:
 
1717
        void pub_srv_ready()
 
1718
        {
 
1719
                if(pub_srv.success())
 
1720
                {
 
1721
                        have_srv = true;
 
1722
                        tryDone();
 
1723
                }
 
1724
                else
 
1725
                {
 
1726
                        JDnsSharedRequest::Error e = pub_srv.error();
 
1727
                        cleanup();
 
1728
                        emit error(e);
 
1729
                }
 
1730
        }
 
1731
 
 
1732
        void pub_txt_ready()
 
1733
        {
 
1734
                if(pub_txt.success())
 
1735
                {
 
1736
                        have_txt = true;
 
1737
 
 
1738
                        if(need_update_txt)
 
1739
                        {
 
1740
                                need_update_txt = false;
 
1741
                                doPublishTxt();
 
1742
                        }
 
1743
 
 
1744
                        tryDone();
 
1745
                }
 
1746
                else
 
1747
                {
 
1748
                        JDnsSharedRequest::Error e = pub_txt.error();
 
1749
                        cleanup();
 
1750
                        emit error(e);
 
1751
                }
 
1752
        }
 
1753
 
 
1754
        void pub_ptr_ready()
 
1755
        {
 
1756
                if(pub_ptr.success())
 
1757
                {
 
1758
                        have_ptr = true;
 
1759
                        emit published();
 
1760
                }
 
1761
                else
 
1762
                {
 
1763
                        JDnsSharedRequest::Error e = pub_ptr.error();
 
1764
                        cleanup();
 
1765
                        emit error(e);
 
1766
                }
 
1767
        }
 
1768
 
 
1769
        void pub_extra_ready()
 
1770
        {
 
1771
                JDnsSharedRequest *req = (JDnsSharedRequest *)sender();
 
1772
                JDnsPublishExtra *extra = 0;
 
1773
                foreach(JDnsPublishExtra *e, extraList)
 
1774
                {
 
1775
                        if(&e->pub == req)
 
1776
                        {
 
1777
                                extra = e;
 
1778
                                break;
 
1779
                        }
 
1780
                }
 
1781
                Q_ASSERT(extra);
 
1782
 
 
1783
                if(extra->pub.success())
 
1784
                {
 
1785
                        extra->have = true;
 
1786
 
 
1787
                        if(extra->need_update)
 
1788
                        {
 
1789
                                extra->need_update = false;
 
1790
                                doPublishExtra(extra);
 
1791
                        }
 
1792
 
 
1793
                        emit extra->published();
 
1794
                }
 
1795
                else
 
1796
                {
 
1797
                        JDnsSharedRequest::Error e = extra->pub.error();
 
1798
                        cleanupExtra(extra);
 
1799
                        emit extra->error(e);
 
1800
                }
 
1801
        }
 
1802
};
 
1803
 
 
1804
JDnsPublishExtra::JDnsPublishExtra(JDnsPublish *_jdnsPub) :
 
1805
        QObject(_jdnsPub),
 
1806
        jdnsPub(_jdnsPub),
 
1807
        started(false),
 
1808
        pub(_jdnsPub->jdns, this)
 
1809
{
 
1810
}
 
1811
 
 
1812
JDnsPublishExtra::~JDnsPublishExtra()
 
1813
{
 
1814
        if(started)
 
1815
                jdnsPub->unpublishExtra(this);
 
1816
}
 
1817
 
 
1818
void JDnsPublishExtra::start(const QJDns::Record &_rec)
 
1819
{
 
1820
        rec = _rec;
 
1821
        started = true;
 
1822
        have = false;
 
1823
        need_update = false;
 
1824
        jdnsPub->publishExtra(this);
 
1825
}
 
1826
 
 
1827
void JDnsPublishExtra::update(const QJDns::Record &_rec)
 
1828
{
 
1829
        rec = _rec;
 
1830
        jdnsPub->publishExtraUpdate(this);
 
1831
}
 
1832
 
 
1833
//----------------------------------------------------------------------------
 
1834
// JDnsServiceProvider
 
1835
//----------------------------------------------------------------------------
 
1836
class BrowseItem
 
1837
{
 
1838
public:
 
1839
        const int id;
 
1840
        JDnsBrowse * const browse;
 
1841
        ObjectSession *sess;
 
1842
 
 
1843
        BrowseItem(int _id, JDnsBrowse *_browse) :
 
1844
                id(_id),
 
1845
                browse(_browse),
 
1846
                sess(0)
 
1847
        {
 
1848
        }
 
1849
 
 
1850
        ~BrowseItem()
 
1851
        {
 
1852
                delete browse;
 
1853
                delete sess;
 
1854
        }
 
1855
};
 
1856
 
 
1857
class BrowseItemList
 
1858
{
 
1859
private:
 
1860
        QSet<BrowseItem*> items;
 
1861
        QHash<int,BrowseItem*> indexById;
 
1862
        QHash<JDnsBrowse*,BrowseItem*> indexByBrowse;
 
1863
        IdManager idman;
 
1864
 
 
1865
public:
 
1866
        ~BrowseItemList()
 
1867
        {
 
1868
                qDeleteAll(items);
 
1869
        }
 
1870
 
 
1871
        int reserveId()
 
1872
        {
 
1873
                return idman.reserveId();
 
1874
        }
 
1875
 
 
1876
        void insert(BrowseItem *item)
 
1877
        {
 
1878
                items.insert(item);
 
1879
                indexById.insert(item->id, item);
 
1880
                indexByBrowse.insert(item->browse, item);
 
1881
        }
 
1882
 
 
1883
        void remove(BrowseItem *item)
 
1884
        {
 
1885
                indexById.remove(item->id);
 
1886
                indexByBrowse.remove(item->browse);
 
1887
                items.remove(item);
 
1888
                if(item->id != -1)
 
1889
                        idman.releaseId(item->id);
 
1890
                delete item;
 
1891
        }
 
1892
 
 
1893
        BrowseItem *itemById(int id) const
 
1894
        {
 
1895
                return indexById.value(id);
 
1896
        }
 
1897
 
 
1898
        BrowseItem *itemByBrowse(JDnsBrowse *browse) const
 
1899
        {
 
1900
                return indexByBrowse.value(browse);
 
1901
        }
 
1902
};
 
1903
 
 
1904
class ResolveItem
 
1905
{
 
1906
public:
 
1907
        const int id;
 
1908
        JDnsServiceResolve * const resolve;
 
1909
        ObjectSession *sess;
 
1910
 
 
1911
        ResolveItem(int _id, JDnsServiceResolve *_resolve) :
 
1912
                id(_id),
 
1913
                resolve(_resolve),
 
1914
                sess(0)
 
1915
        {
 
1916
        }
 
1917
 
 
1918
        ~ResolveItem()
 
1919
        {
 
1920
                delete resolve;
 
1921
                delete sess;
 
1922
        }
 
1923
};
 
1924
 
 
1925
class ResolveItemList
 
1926
{
 
1927
private:
 
1928
        QSet<ResolveItem*> items;
 
1929
        QHash<int,ResolveItem*> indexById;
 
1930
        QHash<JDnsServiceResolve*,ResolveItem*> indexByResolve;
 
1931
        IdManager idman;
 
1932
 
 
1933
public:
 
1934
        ~ResolveItemList()
 
1935
        {
 
1936
                qDeleteAll(items);
 
1937
        }
 
1938
 
 
1939
        int reserveId()
 
1940
        {
 
1941
                return idman.reserveId();
 
1942
        }
 
1943
 
 
1944
        void insert(ResolveItem *item)
 
1945
        {
 
1946
                items.insert(item);
 
1947
                indexById.insert(item->id, item);
 
1948
                indexByResolve.insert(item->resolve, item);
 
1949
        }
 
1950
 
 
1951
        void remove(ResolveItem *item)
 
1952
        {
 
1953
                indexById.remove(item->id);
 
1954
                indexByResolve.remove(item->resolve);
 
1955
                items.remove(item);
 
1956
                if(item->id != -1)
 
1957
                        idman.releaseId(item->id);
 
1958
                delete item;
 
1959
        }
 
1960
 
 
1961
        ResolveItem *itemById(int id) const
 
1962
        {
 
1963
                return indexById.value(id);
 
1964
        }
 
1965
 
 
1966
        ResolveItem *itemByResolve(JDnsServiceResolve *resolve) const
 
1967
        {
 
1968
                return indexByResolve.value(resolve);
 
1969
        }
 
1970
};
 
1971
 
 
1972
class PublishItem
 
1973
{
 
1974
public:
 
1975
        const int id;
 
1976
        JDnsPublish * const publish;
 
1977
        ObjectSession *sess;
 
1978
 
 
1979
        PublishItem(int _id, JDnsPublish *_publish) :
 
1980
                id(_id),
 
1981
                publish(_publish),
 
1982
                sess(0)
 
1983
        {
 
1984
        }
 
1985
 
 
1986
        ~PublishItem()
 
1987
        {
 
1988
                delete publish;
 
1989
                delete sess;
 
1990
        }
 
1991
};
 
1992
 
 
1993
class PublishItemList
 
1994
{
 
1995
public:
 
1996
        QSet<PublishItem*> items;
 
1997
 
 
1998
private:
 
1999
        QHash<int,PublishItem*> indexById;
 
2000
        QHash<JDnsPublish*,PublishItem*> indexByPublish;
 
2001
        IdManager idman;
 
2002
 
 
2003
public:
 
2004
        ~PublishItemList()
 
2005
        {
 
2006
                qDeleteAll(items);
 
2007
        }
 
2008
 
 
2009
        int reserveId()
 
2010
        {
 
2011
                return idman.reserveId();
 
2012
        }
 
2013
 
 
2014
        void insert(PublishItem *item)
 
2015
        {
 
2016
                items.insert(item);
 
2017
                indexById.insert(item->id, item);
 
2018
                indexByPublish.insert(item->publish, item);
 
2019
        }
 
2020
 
 
2021
        void remove(PublishItem *item)
 
2022
        {
 
2023
                indexById.remove(item->id);
 
2024
                indexByPublish.remove(item->publish);
 
2025
                items.remove(item);
 
2026
                if(item->id != -1)
 
2027
                        idman.releaseId(item->id);
 
2028
                delete item;
 
2029
        }
 
2030
 
 
2031
        PublishItem *itemById(int id) const
 
2032
        {
 
2033
                return indexById.value(id);
 
2034
        }
 
2035
 
 
2036
        PublishItem *itemByPublish(JDnsPublish *publish) const
 
2037
        {
 
2038
                return indexByPublish.value(publish);
 
2039
        }
 
2040
};
 
2041
 
 
2042
class PublishExtraItem
 
2043
{
 
2044
public:
 
2045
        const int id;
 
2046
        JDnsPublishExtra * const publish;
 
2047
        ObjectSession *sess;
 
2048
 
 
2049
        PublishExtraItem(int _id, JDnsPublishExtra *_publish) :
 
2050
                id(_id),
 
2051
                publish(_publish),
 
2052
                sess(0)
 
2053
        {
 
2054
        }
 
2055
 
 
2056
        ~PublishExtraItem()
 
2057
        {
 
2058
                delete publish;
 
2059
                delete sess;
 
2060
        }
 
2061
};
 
2062
 
 
2063
class PublishExtraItemList
 
2064
{
 
2065
public:
 
2066
        QSet<PublishExtraItem*> items;
 
2067
 
 
2068
private:
 
2069
        QHash<int,PublishExtraItem*> indexById;
 
2070
        QHash<JDnsPublishExtra*,PublishExtraItem*> indexByPublish;
 
2071
        IdManager idman;
 
2072
 
 
2073
public:
 
2074
        ~PublishExtraItemList()
 
2075
        {
 
2076
                qDeleteAll(items);
 
2077
        }
 
2078
 
 
2079
        void clear()
 
2080
        {
 
2081
                qDeleteAll(items);
 
2082
                items.clear();
 
2083
                indexById.clear();
 
2084
                indexByPublish.clear();
 
2085
                idman.clear();
 
2086
        }
 
2087
 
 
2088
        int reserveId()
 
2089
        {
 
2090
                return idman.reserveId();
 
2091
        }
 
2092
 
 
2093
        void insert(PublishExtraItem *item)
 
2094
        {
 
2095
                items.insert(item);
 
2096
                indexById.insert(item->id, item);
 
2097
                indexByPublish.insert(item->publish, item);
 
2098
        }
 
2099
 
 
2100
        void remove(PublishExtraItem *item)
 
2101
        {
 
2102
                indexById.remove(item->id);
 
2103
                indexByPublish.remove(item->publish);
 
2104
                items.remove(item);
 
2105
                if(item->id != -1)
 
2106
                        idman.releaseId(item->id);
 
2107
                delete item;
 
2108
        }
 
2109
 
 
2110
        PublishExtraItem *itemById(int id) const
 
2111
        {
 
2112
                return indexById.value(id);
 
2113
        }
 
2114
 
 
2115
        PublishExtraItem *itemByPublish(JDnsPublishExtra *publish) const
 
2116
        {
 
2117
                return indexByPublish.value(publish);
 
2118
        }
 
2119
};
 
2120
 
 
2121
class JDnsServiceProvider : public ServiceProvider
 
2122
{
 
2123
        Q_OBJECT
 
2124
 
 
2125
public:
 
2126
        JDnsGlobal *global;
 
2127
 
 
2128
        // browse
 
2129
        BrowseItemList browseItemList;
 
2130
        QHash<QByteArray,ServiceInstance> items;
 
2131
 
 
2132
        // resolve
 
2133
        ResolveItemList resolveItemList;
 
2134
 
 
2135
        // publish
 
2136
        JDnsPublishAddresses *pub_addresses;
 
2137
        QByteArray localHost;
 
2138
        PublishItemList publishItemList;
 
2139
        PublishExtraItemList publishExtraItemList;
 
2140
 
 
2141
        static JDnsServiceProvider *create(JDnsGlobal *global, QObject *parent = 0)
 
2142
        {
 
2143
                return new JDnsServiceProvider(global, parent);
 
2144
        }
 
2145
 
 
2146
        JDnsServiceProvider(JDnsGlobal *_global, QObject *parent = 0) :
 
2147
                ServiceProvider(parent),
 
2148
                pub_addresses(0)
 
2149
        {
 
2150
                global = _global;
 
2151
                connect(global, SIGNAL(interfacesChanged()), SLOT(interfacesChanged()));
 
2152
        }
 
2153
 
 
2154
        ~JDnsServiceProvider()
 
2155
        {
 
2156
                // make sure extra items are deleted before normal ones
 
2157
                publishExtraItemList.clear();
 
2158
        }
 
2159
 
 
2160
        virtual int browse_start(const QString &_type, const QString &_domain)
 
2161
        {
 
2162
                QString domain;
 
2163
                if(_domain.isEmpty() || _domain == ".")
 
2164
                        domain = "local.";
 
2165
                else
 
2166
                        domain = _domain;
 
2167
 
 
2168
                if(domain[domain.length() - 1] != '.')
 
2169
                        domain += '.';
 
2170
 
 
2171
                Q_ASSERT(domain.length() >= 2 && domain[domain.length() - 1] == '.');
 
2172
 
 
2173
                int id = browseItemList.reserveId();
 
2174
 
 
2175
                // no support for non-local domains
 
2176
                if(domain != "local.")
 
2177
                {
 
2178
                        BrowseItem *i = new BrowseItem(id, 0);
 
2179
                        i->sess = new ObjectSession(this);
 
2180
                        browseItemList.insert(i);
 
2181
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2182
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoWide));
 
2183
                        return i->id;
 
2184
                }
 
2185
 
 
2186
                if(!global->ensure_mul())
 
2187
                {
 
2188
                        BrowseItem *i = new BrowseItem(id, 0);
 
2189
                        i->sess = new ObjectSession(this);
 
2190
                        browseItemList.insert(i);
 
2191
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2192
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoLocal));
 
2193
                        return i->id;
 
2194
                }
 
2195
 
 
2196
                QByteArray type = _type.toUtf8();
 
2197
                if(!validServiceType(type))
 
2198
                {
 
2199
                        BrowseItem *i = new BrowseItem(id, 0);
 
2200
                        i->sess = new ObjectSession(this);
 
2201
                        browseItemList.insert(i);
 
2202
                        i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id),
 
2203
                                Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorGeneric));
 
2204
                        return i->id;
 
2205
                }
 
2206
 
 
2207
                BrowseItem *i = new BrowseItem(id, new JDnsBrowse(global->mul, this));
 
2208
                connect(i->browse, SIGNAL(available(const QByteArray &)), SLOT(jb_available(const QByteArray &)));
 
2209
                connect(i->browse, SIGNAL(unavailable(const QByteArray &)), SLOT(jb_unavailable(const QByteArray &)));
 
2210
                browseItemList.insert(i);
 
2211
                i->browse->start(type);
 
2212
                return i->id;
 
2213
        }
 
2214
 
 
2215
        virtual void browse_stop(int id)
 
2216
        {
 
2217
                BrowseItem *i = browseItemList.itemById(id);
 
2218
                Q_ASSERT(i);
 
2219
 
 
2220
                browseItemList.remove(i);
 
2221
        }
 
2222
 
 
2223
        virtual int resolve_start(const QByteArray &name)
 
2224
        {
 
2225
                int id = resolveItemList.reserveId();
 
2226
 
 
2227
                if(!global->ensure_mul())
 
2228
                {
 
2229
                        ResolveItem *i = new ResolveItem(id, 0);
 
2230
                        i->sess = new ObjectSession(this);
 
2231
                        resolveItemList.insert(i);
 
2232
                        i->sess->defer(this, "do_resolve_error", Q_ARG(int, i->id),
 
2233
                                Q_ARG(XMPP::ServiceResolver::Error, ServiceResolver::ErrorNoLocal));
 
2234
                        return i->id;
 
2235
                }
 
2236
 
 
2237
                ResolveItem *i = new ResolveItem(id, new JDnsServiceResolve(global->mul, this));
 
2238
                connect(i->resolve, SIGNAL(finished()), SLOT(jr_finished()));
 
2239
                connect(i->resolve, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jr_error(JDnsSharedRequest::Error)));
 
2240
                resolveItemList.insert(i);
 
2241
                i->resolve->start(name);
 
2242
                return i->id;
 
2243
        }
 
2244
 
 
2245
        virtual void resolve_stop(int id)
 
2246
        {
 
2247
                ResolveItem *i = resolveItemList.itemById(id);
 
2248
                Q_ASSERT(i);
 
2249
 
 
2250
                resolveItemList.remove(i);
 
2251
        }
 
2252
 
 
2253
        virtual int publish_start(const QString &instance, const QString &_type, int port, const QMap<QString,QByteArray> &attributes)
 
2254
        {
 
2255
                int id = publishItemList.reserveId();
 
2256
 
 
2257
                if(!global->ensure_mul())
 
2258
                {
 
2259
                        PublishItem *i = new PublishItem(id, 0);
 
2260
                        i->sess = new ObjectSession(this);
 
2261
                        publishItemList.insert(i);
 
2262
                        i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id),
 
2263
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorNoLocal));
 
2264
                        return i->id;
 
2265
                }
 
2266
 
 
2267
                QByteArray type = _type.toUtf8();
 
2268
                if(!validServiceType(type))
 
2269
                {
 
2270
                        PublishItem *i = new PublishItem(id, 0);
 
2271
                        i->sess = new ObjectSession(this);
 
2272
                        publishItemList.insert(i);
 
2273
                        i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id),
 
2274
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2275
                        return i->id;
 
2276
                }
 
2277
 
 
2278
                // make sure A/AAAA records are published
 
2279
                if(!pub_addresses)
 
2280
                {
 
2281
                        pub_addresses = new JDnsPublishAddresses(global->mul, this);
 
2282
                        connect(pub_addresses, SIGNAL(hostName(const QByteArray &)), SLOT(pub_addresses_hostName(const QByteArray &)));
 
2283
                        pub_addresses->setUseIPv6(global->haveMulticast6());
 
2284
                        pub_addresses->setUseIPv4(global->haveMulticast4());
 
2285
                        pub_addresses->start();
 
2286
                }
 
2287
 
 
2288
                // it's okay to attempt to publish even if pub_addresses
 
2289
                //   hasn't succeeded yet.  JDnsPublish is smart enough to
 
2290
                //   defer the operation until a host is acquired.
 
2291
                PublishItem *i = new PublishItem(id, new JDnsPublish(global->mul, this));
 
2292
                connect(i->publish, SIGNAL(published()), SLOT(jp_published()));
 
2293
                connect(i->publish, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jp_error(JDnsSharedRequest::Error)));
 
2294
                publishItemList.insert(i);
 
2295
                i->publish->start(instance, type, localHost, port, attributes);
 
2296
                return i->id;
 
2297
        }
 
2298
 
 
2299
        virtual void publish_update(int id, const QMap<QString,QByteArray> &attributes)
 
2300
        {
 
2301
                PublishItem *i = publishItemList.itemById(id);
 
2302
                Q_ASSERT(i);
 
2303
 
 
2304
                // if we already have an error queued, do nothing
 
2305
                if(i->sess->isDeferred(this, "do_publish_error"))
 
2306
                        return;
 
2307
 
 
2308
                i->publish->update(attributes);
 
2309
        }
 
2310
 
 
2311
        virtual void publish_stop(int id)
 
2312
        {
 
2313
                PublishItem *i = publishItemList.itemById(id);
 
2314
                Q_ASSERT(i);
 
2315
 
 
2316
                cleanupExtra(i);
 
2317
                publishItemList.remove(i);
 
2318
        }
 
2319
 
 
2320
        virtual int publish_extra_start(int pub_id, const NameRecord &name)
 
2321
        {
 
2322
                PublishItem *pi = publishItemList.itemById(pub_id);
 
2323
                Q_ASSERT(pi);
 
2324
 
 
2325
                int id = publishItemList.reserveId();
 
2326
 
 
2327
                QJDns::Record rec = exportJDNSRecord(name);
 
2328
                if(rec.type == -1)
 
2329
                {
 
2330
                        PublishExtraItem *i = new PublishExtraItem(id, 0);
 
2331
                        i->sess = new ObjectSession(this);
 
2332
                        publishExtraItemList.insert(i);
 
2333
                        i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id),
 
2334
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2335
                        return i->id;
 
2336
                }
 
2337
 
 
2338
                // fill in owner if necessary
 
2339
                if(rec.owner.isEmpty())
 
2340
                        rec.owner = pi->publish->fullname;
 
2341
 
 
2342
                // fill in the ttl if necessary
 
2343
                if(rec.ttl == 0)
 
2344
                        rec.ttl = 4500;
 
2345
 
 
2346
                PublishExtraItem *i = new PublishExtraItem(id, new JDnsPublishExtra(pi->publish));
 
2347
                connect(i->publish, SIGNAL(published()), SLOT(jpe_published()));
 
2348
                connect(i->publish, SIGNAL(error(JDnsSharedRequest::Error)), SLOT(jpe_error(JDnsSharedRequest::Error)));
 
2349
                publishExtraItemList.insert(i);
 
2350
                i->publish->start(rec);
 
2351
                return i->id;
 
2352
        }
 
2353
 
 
2354
        virtual void publish_extra_update(int id, const NameRecord &name)
 
2355
        {
 
2356
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2357
                Q_ASSERT(i);
 
2358
 
 
2359
                // if we already have an error queued, do nothing
 
2360
                if(i->sess->isDeferred(this, "do_publish_extra_error"))
 
2361
                        return;
 
2362
 
 
2363
                QJDns::Record rec = exportJDNSRecord(name);
 
2364
                if(rec.type == -1)
 
2365
                {
 
2366
                        i->sess = new ObjectSession(this);
 
2367
                        i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id),
 
2368
                                Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric));
 
2369
                        return;
 
2370
                }
 
2371
 
 
2372
                // fill in owner if necessary
 
2373
                if(rec.owner.isEmpty())
 
2374
                        rec.owner = static_cast<JDnsPublish*>(i->publish->parent())->fullname;
 
2375
 
 
2376
                // fill in the ttl if necessary
 
2377
                if(rec.ttl == 0)
 
2378
                        rec.ttl = 4500;
 
2379
 
 
2380
                i->publish->update(rec);
 
2381
        }
 
2382
 
 
2383
        virtual void publish_extra_stop(int id)
 
2384
        {
 
2385
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2386
                Q_ASSERT(i);
 
2387
 
 
2388
                publishExtraItemList.remove(i);
 
2389
        }
 
2390
 
 
2391
private:
 
2392
        void cleanupExtra(PublishItem *pi)
 
2393
        {
 
2394
                // remove all extra publishes associated with this publish.
 
2395
                //   the association can be checked via QObject parenting.
 
2396
                QSet<PublishExtraItem*> remove;
 
2397
                foreach(PublishExtraItem *i, publishExtraItemList.items)
 
2398
                {
 
2399
                        if(static_cast<JDnsPublish*>(i->publish->parent()) == pi->publish)
 
2400
                                remove += i;
 
2401
                }
 
2402
 
 
2403
                foreach(PublishExtraItem *i, remove)
 
2404
                        publishExtraItemList.remove(i);
 
2405
        }
 
2406
 
 
2407
private slots:
 
2408
        void interfacesChanged()
 
2409
        {
 
2410
                if(pub_addresses)
 
2411
                {
 
2412
                        pub_addresses->setUseIPv6(global->haveMulticast6());
 
2413
                        pub_addresses->setUseIPv4(global->haveMulticast4());
 
2414
                }
 
2415
        }
 
2416
 
 
2417
        void jb_available(const QByteArray &instance)
 
2418
        {
 
2419
                JDnsBrowse *jb = (JDnsBrowse *)sender();
 
2420
                BrowseItem *i = browseItemList.itemByBrowse(jb);
 
2421
                Q_ASSERT(i);
 
2422
 
 
2423
                QByteArray name = instance + '.' + jb->typeAndDomain;
 
2424
                ServiceInstance si(QString::fromLatin1(instance), QString::fromLatin1(jb->type), "local.", QMap<QString,QByteArray>());
 
2425
                items.insert(name, si);
 
2426
 
 
2427
                emit browse_instanceAvailable(i->id, si);
 
2428
        }
 
2429
 
 
2430
        void jb_unavailable(const QByteArray &instance)
 
2431
        {
 
2432
                JDnsBrowse *jb = (JDnsBrowse *)sender();
 
2433
                BrowseItem *i = browseItemList.itemByBrowse(jb);
 
2434
                Q_ASSERT(i);
 
2435
 
 
2436
                QByteArray name = instance + '.' + jb->typeAndDomain;
 
2437
                Q_ASSERT(items.contains(name));
 
2438
 
 
2439
                ServiceInstance si = items.value(name);
 
2440
                items.remove(name);
 
2441
 
 
2442
                emit browse_instanceUnavailable(i->id, si);
 
2443
        }
 
2444
 
 
2445
        void do_browse_error(int id, XMPP::ServiceBrowser::Error e)
 
2446
        {
 
2447
                BrowseItem *i = browseItemList.itemById(id);
 
2448
                Q_ASSERT(i);
 
2449
 
 
2450
                browseItemList.remove(i);
 
2451
                emit browse_error(id, e);
 
2452
        }
 
2453
 
 
2454
        void jr_finished()
 
2455
        {
 
2456
                JDnsServiceResolve *jr = (JDnsServiceResolve *)sender();
 
2457
                ResolveItem *i = resolveItemList.itemByResolve(jr);
 
2458
                Q_ASSERT(i);
 
2459
 
 
2460
                // parse TXT list into attribute map
 
2461
                QMap<QString,QByteArray> attribs;
 
2462
                for(int n = 0; n < jr->attribs.count(); ++n)
 
2463
                {
 
2464
                        const QByteArray &a = jr->attribs[n];
 
2465
                        QString key;
 
2466
                        QByteArray value;
 
2467
                        int x = a.indexOf('=');
 
2468
                        if(x != -1)
 
2469
                        {
 
2470
                                key = QString::fromLatin1(a.mid(0, x));
 
2471
                                value = a.mid(x + 1);
 
2472
                        }
 
2473
                        else
 
2474
                        {
 
2475
                                key = QString::fromLatin1(a);
 
2476
                        }
 
2477
 
 
2478
                        attribs.insert(key, value);
 
2479
                }
 
2480
 
 
2481
                // one of these must be true
 
2482
                Q_ASSERT(jr->have4 || jr->have6);
 
2483
 
 
2484
                QList<ResolveResult> results;
 
2485
                if(jr->have6)
 
2486
                {
 
2487
                        ResolveResult r;
 
2488
                        r.attributes = attribs;
 
2489
                        r.address = jr->addr6;
 
2490
                        r.port = jr->port;
 
2491
                        r.hostName = jr->host;
 
2492
                        results += r;
 
2493
                }
 
2494
                if(jr->have4)
 
2495
                {
 
2496
                        ResolveResult r;
 
2497
                        r.attributes = attribs;
 
2498
                        r.address = jr->addr4;
 
2499
                        r.port = jr->port;
 
2500
                        r.hostName = jr->host;
 
2501
                        results += r;
 
2502
                }
 
2503
 
 
2504
                int id = i->id;
 
2505
                resolveItemList.remove(i);
 
2506
                emit resolve_resultsReady(id, results);
 
2507
        }
 
2508
 
 
2509
        void jr_error(JDnsSharedRequest::Error e)
 
2510
        {
 
2511
                JDnsServiceResolve *jr = (JDnsServiceResolve *)sender();
 
2512
                ResolveItem *i = resolveItemList.itemByResolve(jr);
 
2513
                Q_ASSERT(i);
 
2514
 
 
2515
                ServiceResolver::Error err;
 
2516
                if(e == JDnsSharedRequest::ErrorTimeout)
 
2517
                        err = ServiceResolver::ErrorTimeout;
 
2518
                else
 
2519
                        err = ServiceResolver::ErrorGeneric;
 
2520
 
 
2521
                int id = i->id;
 
2522
                resolveItemList.remove(i);
 
2523
                emit resolve_error(id, err);
 
2524
        }
 
2525
 
 
2526
        void do_resolve_error(int id, XMPP::ServiceResolver::Error e)
 
2527
        {
 
2528
                ResolveItem *i = resolveItemList.itemById(id);
 
2529
                Q_ASSERT(i);
 
2530
 
 
2531
                resolveItemList.remove(i);
 
2532
                emit resolve_error(id, e);
 
2533
        }
 
2534
 
 
2535
        void pub_addresses_hostName(const QByteArray &name)
 
2536
        {
 
2537
                // tell all active publishes about the change
 
2538
                foreach(PublishItem *item, publishItemList.items)
 
2539
                        item->publish->hostChanged(name);
 
2540
        }
 
2541
 
 
2542
        void jp_published()
 
2543
        {
 
2544
                JDnsPublish *jp = (JDnsPublish *)sender();
 
2545
                PublishItem *i = publishItemList.itemByPublish(jp);
 
2546
                Q_ASSERT(i);
 
2547
 
 
2548
                emit publish_published(i->id);
 
2549
        }
 
2550
 
 
2551
        void jp_error(JDnsSharedRequest::Error e)
 
2552
        {
 
2553
                JDnsPublish *jp = (JDnsPublish *)sender();
 
2554
                PublishItem *i = publishItemList.itemByPublish(jp);
 
2555
                Q_ASSERT(i);
 
2556
 
 
2557
                ServiceLocalPublisher::Error err;
 
2558
                if(e == JDnsSharedRequest::ErrorConflict)
 
2559
                        err = ServiceLocalPublisher::ErrorConflict;
 
2560
                else
 
2561
                        err = ServiceLocalPublisher::ErrorGeneric;
 
2562
 
 
2563
                int id = i->id;
 
2564
                cleanupExtra(i);
 
2565
                publishItemList.remove(i);
 
2566
                emit publish_error(id, err);
 
2567
        }
 
2568
 
 
2569
        void do_publish_error(int id, XMPP::ServiceLocalPublisher::Error e)
 
2570
        {
 
2571
                PublishItem *i = publishItemList.itemById(id);
 
2572
                Q_ASSERT(i);
 
2573
 
 
2574
                cleanupExtra(i);
 
2575
                publishItemList.remove(i);
 
2576
                emit publish_error(id, e);
 
2577
        }
 
2578
 
 
2579
        void jpe_published()
 
2580
        {
 
2581
                JDnsPublishExtra *jp = (JDnsPublishExtra *)sender();
 
2582
                PublishExtraItem *i = publishExtraItemList.itemByPublish(jp);
 
2583
                Q_ASSERT(i);
 
2584
 
 
2585
                emit publish_extra_published(i->id);
 
2586
        }
 
2587
 
 
2588
        void jpe_error(JDnsSharedRequest::Error e)
 
2589
        {
 
2590
                JDnsPublishExtra *jp = (JDnsPublishExtra *)sender();
 
2591
                PublishExtraItem *i = publishExtraItemList.itemByPublish(jp);
 
2592
                Q_ASSERT(i);
 
2593
 
 
2594
                ServiceLocalPublisher::Error err;
 
2595
                if(e == JDnsSharedRequest::ErrorConflict)
 
2596
                        err = ServiceLocalPublisher::ErrorConflict;
 
2597
                else
 
2598
                        err = ServiceLocalPublisher::ErrorGeneric;
 
2599
 
 
2600
                int id = i->id;
 
2601
                publishExtraItemList.remove(i);
 
2602
                emit publish_extra_error(id, err);
 
2603
        }
 
2604
 
 
2605
        void do_publish_extra_error(int id, XMPP::ServiceLocalPublisher::Error e)
 
2606
        {
 
2607
                PublishExtraItem *i = publishExtraItemList.itemById(id);
 
2608
                Q_ASSERT(i);
 
2609
 
 
2610
                publishExtraItemList.remove(i);
 
2611
                emit publish_extra_error(id, e);
 
2612
        }
 
2613
};
 
2614
 
 
2615
//----------------------------------------------------------------------------
 
2616
// JDnsProvider
 
2617
//----------------------------------------------------------------------------
 
2618
class JDnsProvider : public IrisNetProvider
 
2619
{
 
2620
        Q_OBJECT
 
2621
        Q_INTERFACES(XMPP::IrisNetProvider)
 
2622
 
 
2623
public:
 
2624
        JDnsGlobal *global;
 
2625
 
 
2626
        JDnsProvider()
 
2627
        {
 
2628
                global = 0;
 
2629
        }
 
2630
 
 
2631
        ~JDnsProvider()
 
2632
        {
 
2633
                delete global;
 
2634
        }
 
2635
 
 
2636
        void ensure_global()
 
2637
        {
 
2638
                if(!global)
 
2639
                        global = new JDnsGlobal;
 
2640
        }
 
2641
 
 
2642
        virtual NameProvider *createNameProviderInternet()
 
2643
        {
 
2644
                ensure_global();
 
2645
                return JDnsNameProvider::create(global, JDnsNameProvider::Internet);
 
2646
        }
 
2647
 
 
2648
        virtual NameProvider *createNameProviderLocal()
 
2649
        {
 
2650
                ensure_global();
 
2651
                return JDnsNameProvider::create(global, JDnsNameProvider::Local);
 
2652
        }
 
2653
 
 
2654
        virtual ServiceProvider *createServiceProvider()
 
2655
        {
 
2656
                ensure_global();
 
2657
                return JDnsServiceProvider::create(global);
 
2658
        }
 
2659
};
 
2660
 
 
2661
IrisNetProvider *irisnet_createJDnsProvider()
 
2662
{
 
2663
        return new JDnsProvider;
 
2664
}
 
2665
 
 
2666
}
 
2667
 
 
2668
#include "netnames_jdns.moc"