1 /*
2 * Copyright (C) 2007 Justin Karneges
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 *
19 */
20
21 #include "irisnetplugin.h"
22
23 #include <QtCore>
24 #include <QtNetwork>
25 #include "qdnssd.h"
26
27 // for ntohl
28 #ifdef Q_OS_WIN
29 # include <windows.h>
30 #else
31 # include <netinet/in.h>
32 #endif
33
nameToDottedString(const QByteArray & in)34 static QByteArray nameToDottedString(const QByteArray &in)
35 {
36 QByteArray out;
37 int at = 0;
38 while(at < in.size())
39 {
40 int len = in[at++];
41 if(len > 0)
42 out += in.mid(at, len);
43 out += '.';
44 at += len;
45 }
46 return out;
47 }
48
textsToAttribs(const QList<QByteArray> & texts)49 static QMap<QString,QByteArray> textsToAttribs(const QList<QByteArray> &texts)
50 {
51 QMap<QString,QByteArray> out;
52 foreach(const QByteArray &a, texts)
53 {
54 QString key;
55 QByteArray value;
56 int x = a.indexOf('=');
57 if(x != -1)
58 {
59 key = QString::fromLatin1(a.mid(0, x));
60 value = a.mid(x + 1);
61 }
62 else
63 {
64 key = QString::fromLatin1(a);
65 }
66
67 out.insert(key, value);
68 }
69 return out;
70 }
71
attribsToTxtRecord(const QMap<QString,QByteArray> & attribs)72 static QByteArray attribsToTxtRecord(const QMap<QString,QByteArray> &attribs)
73 {
74 QList<QByteArray> texts;
75 QMapIterator<QString,QByteArray> it(attribs);
76 while(it.hasNext())
77 {
78 it.next();
79 QByteArray line = it.key().toLatin1() + '=' + it.value();
80 texts += line;
81 }
82 return QDnsSd::createTxtRecord(texts);
83 }
84
85 // returns a list of 3 items, or an empty list on error
nameToInstanceParts(const QByteArray & name)86 static QList<QByteArray> nameToInstanceParts(const QByteArray &name)
87 {
88 // FIXME: improve this parsing... (what about escaping??)
89 int at = name.indexOf('.');
90 QByteArray sname = name.mid(0, at);
91 ++at;
92 int next = name.indexOf('.', at);
93 ++next;
94 next = name.indexOf('.', next);
95 QByteArray stype = name.mid(at, next - at);
96 at = next + 1;
97 QByteArray sdomain = name.mid(at);
98
99 QList<QByteArray> out;
100 out += sname;
101 out += stype;
102 out += sdomain;
103 return out;
104 }
105
importQDnsSdRecord(const QDnsSd::Record & in)106 static XMPP::NameRecord importQDnsSdRecord(const QDnsSd::Record &in)
107 {
108 XMPP::NameRecord out;
109 switch(in.rrtype)
110 {
111 case 1: // A
112 {
113 quint32 *p = (quint32 *)in.rdata.data();
114 out.setAddress(QHostAddress(ntohl(*p)));
115 }
116 break;
117
118 case 28: // AAAA
119 {
120 out.setAddress(QHostAddress((quint8 *)in.rdata.data()));
121 }
122 break;
123
124 case 12: // PTR
125 {
126 out.setPtr(nameToDottedString(in.rdata));
127 }
128 break;
129
130 case 10: // NULL
131 {
132 out.setNull(in.rdata);
133 }
134 break;
135
136 case 16: // TXT
137 {
138 QList<QByteArray> txtEntries = QDnsSd::parseTxtRecord(in.rdata);
139 if(txtEntries.isEmpty())
140 return out;
141 out.setTxt(txtEntries);
142 }
143 break;
144
145 default: // unsupported
146 {
147 return out;
148 }
149 }
150
151 out.setOwner(in.name);
152 out.setTtl(in.ttl);
153 return out;
154 }
155
156 namespace {
157
158 class QDnsSdDelegate
159 {
160 public:
~QDnsSdDelegate()161 virtual ~QDnsSdDelegate()
162 {
163 }
164
dns_queryResult(int id,const QDnsSd::QueryResult & result)165 virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result)
166 {
167 Q_UNUSED(id);
168 Q_UNUSED(result);
169 }
170
dns_browseResult(int id,const QDnsSd::BrowseResult & result)171 virtual void dns_browseResult(int id, const QDnsSd::BrowseResult &result)
172 {
173 Q_UNUSED(id);
174 Q_UNUSED(result);
175 }
176
dns_resolveResult(int id,const QDnsSd::ResolveResult & result)177 virtual void dns_resolveResult(int id, const QDnsSd::ResolveResult &result)
178 {
179 Q_UNUSED(id);
180 Q_UNUSED(result);
181 }
182
dns_regResult(int id,const QDnsSd::RegResult & result)183 virtual void dns_regResult(int id, const QDnsSd::RegResult &result)
184 {
185 Q_UNUSED(id);
186 Q_UNUSED(result);
187 }
188 };
189
190 class IdManager
191 {
192 private:
193 QSet<int> set;
194 int at;
195
bump_at()196 inline void bump_at()
197 {
198 if(at == 0x7fffffff)
199 at = 0;
200 else
201 ++at;
202 }
203
204 public:
IdManager()205 IdManager() :
206 at(0)
207 {
208 }
209
reserveId()210 int reserveId()
211 {
212 while(1)
213 {
214 if(!set.contains(at))
215 {
216 int id = at;
217 set.insert(id);
218 bump_at();
219 return id;
220 }
221
222 bump_at();
223 }
224 }
225
releaseId(int id)226 void releaseId(int id)
227 {
228 set.remove(id);
229 }
230 };
231
232 }
233
234 //----------------------------------------------------------------------------
235 // AppleProvider
236 //----------------------------------------------------------------------------
237 class AppleProvider : public XMPP::IrisNetProvider
238 {
239 Q_OBJECT
240 Q_INTERFACES(XMPP::IrisNetProvider);
241 public:
242 QDnsSd dns;
243 QHash<int,QDnsSdDelegate*> delegateById;
244
AppleProvider()245 AppleProvider() :
246 dns(this)
247 {
248 connect(&dns, SIGNAL(queryResult(int,QDnsSd::QueryResult)), SLOT(dns_queryResult(int,QDnsSd::QueryResult)));
249 connect(&dns, SIGNAL(browseResult(int,QDnsSd::BrowseResult)), SLOT(dns_browseResult(int,QDnsSd::BrowseResult)));
250 connect(&dns, SIGNAL(resolveResult(int,QDnsSd::ResolveResult)), SLOT(dns_resolveResult(int,QDnsSd::ResolveResult)));
251 connect(&dns, SIGNAL(regResult(int,QDnsSd::RegResult)), SLOT(dns_regResult(int,QDnsSd::RegResult)));
252 }
253
254 virtual XMPP::NameProvider *createNameProviderInternet();
255 virtual XMPP::NameProvider *createNameProviderLocal();
256 virtual XMPP::ServiceProvider *createServiceProvider();
257
query(QDnsSdDelegate * p,const QByteArray & name,int qType)258 int query(QDnsSdDelegate *p, const QByteArray &name, int qType)
259 {
260 int id = dns.query(name, qType);
261 delegateById[id] = p;
262 return id;
263 }
264
browse(QDnsSdDelegate * p,const QByteArray & serviceType,const QByteArray & domain)265 int browse(QDnsSdDelegate *p, const QByteArray &serviceType, const QByteArray &domain)
266 {
267 int id = dns.browse(serviceType, domain);
268 delegateById[id] = p;
269 return id;
270 }
271
resolve(QDnsSdDelegate * p,const QByteArray & serviceName,const QByteArray & serviceType,const QByteArray & domain)272 int resolve(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain)
273 {
274 int id = dns.resolve(serviceName, serviceType, domain);
275 delegateById[id] = p;
276 return id;
277 }
278
reg(QDnsSdDelegate * p,const QByteArray & serviceName,const QByteArray & serviceType,const QByteArray & domain,int port,const QByteArray & txtRecord)279 int reg(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord)
280 {
281 int id = dns.reg(serviceName, serviceType, domain, port, txtRecord);
282 delegateById[id] = p;
283 return id;
284 }
285
stop(int id)286 void stop(int id)
287 {
288 delegateById.remove(id);
289 dns.stop(id);
290 }
291
stop_all(QDnsSdDelegate * p)292 void stop_all(QDnsSdDelegate *p)
293 {
294 QList<int> ids;
295 QHashIterator<int,QDnsSdDelegate*> it(delegateById);
296 while(it.hasNext())
297 {
298 it.next();
299 if(it.value() == p)
300 ids += it.key();
301 }
302 foreach(int id, ids)
303 stop(id);
304 }
305
306 private slots:
dns_queryResult(int id,const QDnsSd::QueryResult & result)307 void dns_queryResult(int id, const QDnsSd::QueryResult &result)
308 {
309 delegateById[id]->dns_queryResult(id, result);
310 }
311
dns_browseResult(int id,const QDnsSd::BrowseResult & result)312 void dns_browseResult(int id, const QDnsSd::BrowseResult &result)
313 {
314 delegateById[id]->dns_browseResult(id, result);
315 }
316
dns_resolveResult(int id,const QDnsSd::ResolveResult & result)317 void dns_resolveResult(int id, const QDnsSd::ResolveResult &result)
318 {
319 delegateById[id]->dns_resolveResult(id, result);
320 }
321
dns_regResult(int id,const QDnsSd::RegResult & result)322 void dns_regResult(int id, const QDnsSd::RegResult &result)
323 {
324 delegateById[id]->dns_regResult(id, result);
325 }
326 };
327
328 //----------------------------------------------------------------------------
329 // AppleBrowseSession
330 //----------------------------------------------------------------------------
331 // only use this class for a single browse. if you want to browse again,
332 // create a new object.
333 class AppleBrowse : public QObject, public QDnsSdDelegate
334 {
335 Q_OBJECT
336 public:
337 AppleProvider *global;
338 int browse_id;
339 QList<XMPP::ServiceInstance> instances;
340 QHash<int,QByteArray> pendingByQueryId; // waiting for TXT
341
AppleBrowse(AppleProvider * _global,QObject * parent=0)342 AppleBrowse(AppleProvider *_global, QObject *parent = 0) :
343 QObject(parent),
344 global(_global),
345 browse_id(-1)
346 {
347 connect(this, SIGNAL(unavailable_p(XMPP::ServiceInstance)), SIGNAL(unavailable(XMPP::ServiceInstance)));
348 }
349
~AppleBrowse()350 ~AppleBrowse()
351 {
352 global->stop_all(this);
353 }
354
browse(const QString & type,const QString & domain)355 void browse(const QString &type, const QString &domain)
356 {
357 browse_id = global->browse(this, type.toUtf8(), domain.toUtf8());
358 }
359
360 signals:
361 void available(const XMPP::ServiceInstance &instance);
362 void unavailable(const XMPP::ServiceInstance &instance);
363 void error();
364
365 // emit delayed
366 void unavailable_p(const XMPP::ServiceInstance &instance);
367
368 protected:
dns_browseResult(int id,const QDnsSd::BrowseResult & result)369 virtual void dns_browseResult(int id, const QDnsSd::BrowseResult &result)
370 {
371 Q_UNUSED(id);
372
373 if(!result.success)
374 {
375 emit error();
376 return;
377 }
378
379 foreach(const QDnsSd::BrowseEntry &e, result.entries)
380 {
381 XMPP::ServiceInstance si(e.serviceName, e.serviceType, e.replyDomain, QMap<QString,QByteArray>());
382
383 if(e.added)
384 {
385 int query_id = global->query(this, si.name(), 16); // 16 == TXT
386 pendingByQueryId[query_id] = si.name();
387 }
388 else // removed
389 {
390 // emit these queued for SS. no worry of SR since
391 // the browse operation is not cancellable.
392 for(int n = 0; n < instances.count(); ++n)
393 {
394 const XMPP::ServiceInstance &i = instances[n];
395 if(i.name() == si.name())
396 {
397 emit unavailable_p(i);
398 instances.removeAt(n);
399 --n; // adjust position
400 }
401 }
402 }
403 }
404 }
405
dns_queryResult(int id,const QDnsSd::QueryResult & result)406 virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result)
407 {
408 if(!result.success)
409 {
410 // if we get here, then it means we received a browse
411 // entry, but could not fetch its TXT record. if
412 // that happens, cancel the query and drop the
413 // browse entry.
414 global->stop(id);
415 pendingByQueryId.remove(id);
416 return;
417 }
418
419 // qdnssd guarantees at least one answer
420 Q_ASSERT(!result.records.isEmpty());
421
422 // only the first entry matters, and it must be an added TXT
423 if(!result.records[0].added || result.records[0].rrtype != 16)
424 return;
425
426 // we only care about one answer
427 QByteArray name = pendingByQueryId[id];
428 QList<QByteArray> parts = nameToInstanceParts(name);
429 if(parts.isEmpty())
430 {
431 // TODO: error
432 Q_ASSERT(0);
433 }
434
435 global->stop(id);
436 pendingByQueryId.remove(id);
437
438 XMPP::NameRecord rec = importQDnsSdRecord(result.records[0]);
439
440 // bad answer?
441 if(rec.isNull())
442 return;
443
444 QMap<QString,QByteArray> attribs = textsToAttribs(rec.texts());
445 // FIXME: conversion/escaping?
446 XMPP::ServiceInstance si(QString::fromUtf8(parts[0]), QString::fromUtf8(parts[1]), QString::fromUtf8(parts[2]), attribs);
447
448 // does qdnssd guarantee we won't receive dups?
449 bool found = false;
450 foreach(const XMPP::ServiceInstance &i, instances)
451 {
452 if(i.name() == si.name())
453 {
454 found = true;
455 break;
456 }
457 }
458 Q_ASSERT(!found);
459
460 instances += si;
461 emit available(si);
462 }
463 };
464
465 //----------------------------------------------------------------------------
466 // AppleBrowseLookup
467 //----------------------------------------------------------------------------
468 // only use this class for a single lookup. if you want to lookup again,
469 // create a new object.
470 class AppleBrowseLookup : public QObject, public QDnsSdDelegate
471 {
472 Q_OBJECT
473 public:
474 AppleProvider *global;
475 int resolve_id;
476 XMPP::NameResolver nameResolverAaaa;
477 XMPP::NameResolver nameResolverA;
478 bool activeAaaa;
479 bool activeA;
480 QTimer waitTimer;
481 QByteArray host;
482 QHostAddress addr4;
483 QHostAddress addr6;
484 int port;
485
AppleBrowseLookup(AppleProvider * _global,QObject * parent=0)486 AppleBrowseLookup(AppleProvider *_global, QObject *parent = 0) :
487 QObject(parent),
488 global(_global),
489 resolve_id(-1),
490 nameResolverAaaa(this),
491 nameResolverA(this),
492 activeAaaa(false),
493 activeA(false),
494 waitTimer(this)
495 {
496 connect(&nameResolverAaaa, SIGNAL(resultsReady(QList<XMPP::NameRecord>)),
497 SLOT(nameAaaa_resultsReady(QList<XMPP::NameRecord>)));
498 connect(&nameResolverAaaa, SIGNAL(error(XMPP::NameResolver::Error)),
499 SLOT(nameAaaa_error(XMPP::NameResolver::Error)));
500
501 connect(&nameResolverA, SIGNAL(resultsReady(QList<XMPP::NameRecord>)),
502 SLOT(nameA_resultsReady(QList<XMPP::NameRecord>)));
503 connect(&nameResolverA, SIGNAL(error(XMPP::NameResolver::Error)),
504 SLOT(nameA_error(XMPP::NameResolver::Error)));
505
506 connect(&waitTimer, SIGNAL(timeout()), SLOT(waitTimer_timeout()));
507 waitTimer.setSingleShot(true);
508 }
509
~AppleBrowseLookup()510 ~AppleBrowseLookup()
511 {
512 global->stop_all(this);
513 }
514
resolve(const QByteArray & instance,const QByteArray & type,const QByteArray & domain)515 void resolve(const QByteArray &instance, const QByteArray &type, const QByteArray &domain)
516 {
517 resolve_id = global->resolve(this, instance, type, domain);
518 }
519
520 signals:
521 // emits at least 1 and at most 2
522 void finished(const QList<QHostAddress> &addrs, int port);
523 void error();
524
525 protected:
dns_resolveResult(int id,const QDnsSd::ResolveResult & result)526 void dns_resolveResult(int id, const QDnsSd::ResolveResult &result)
527 {
528 // there is only one response, so deregister
529 global->stop(id);
530
531 if(!result.success)
532 {
533 emit error();
534 return;
535 }
536
537 host = result.hostTarget;
538 port = result.port;
539
540 activeAaaa = true;
541 activeA = true;
542 nameResolverAaaa.start(host, XMPP::NameRecord::Aaaa);
543 nameResolverA.start(host, XMPP::NameRecord::A);
544 waitTimer.start(500); // 500ms cut-off time, take what we have and run
545 }
546
547 private slots:
nameAaaa_resultsReady(const QList<XMPP::NameRecord> & results)548 void nameAaaa_resultsReady(const QList<XMPP::NameRecord> &results)
549 {
550 // nameresolver guarantees at least one result, and we only
551 // care about the first
552 addr6 = results[0].address();
553 activeAaaa = false;
554 tryDone();
555 }
556
nameAaaa_error(XMPP::NameResolver::Error e)557 void nameAaaa_error(XMPP::NameResolver::Error e)
558 {
559 Q_UNUSED(e);
560 activeAaaa = false;
561 tryDone();
562 }
563
nameA_resultsReady(const QList<XMPP::NameRecord> & results)564 void nameA_resultsReady(const QList<XMPP::NameRecord> &results)
565 {
566 // nameresolver guarantees at least one result, and we only
567 // care about the first
568 addr4 = results[0].address();
569 activeA = false;
570 tryDone();
571 }
572
nameA_error(XMPP::NameResolver::Error e)573 void nameA_error(XMPP::NameResolver::Error e)
574 {
575 Q_UNUSED(e);
576 activeA = false;
577 tryDone();
578 }
579
waitTimer_timeout()580 void waitTimer_timeout()
581 {
582 tryDone();
583 }
584
585 private:
tryDone()586 void tryDone()
587 {
588 // we're done if both resolves are inactive and we have no
589 // results, or if the wait timer ends and we have at least
590 // one result
591
592 if(!activeAaaa && !activeA && addr6.isNull() && addr4.isNull())
593 {
594 nameResolverAaaa.stop();
595 nameResolverA.stop();
596 waitTimer.stop();
597
598 emit error();
599 return;
600 }
601
602 if(!waitTimer.isActive() && (!addr6.isNull() || !addr4.isNull()))
603 {
604 nameResolverAaaa.stop();
605 nameResolverA.stop();
606
607 QList<QHostAddress> out;
608 if(!addr4.isNull())
609 out += addr4;
610 if(!addr6.isNull())
611 out += addr6;
612 emit finished(out, port);
613 }
614 }
615 };
616
617 //----------------------------------------------------------------------------
618 // AppleNameProvider
619 //----------------------------------------------------------------------------
620 class AppleNameProvider : public XMPP::NameProvider, public QDnsSdDelegate
621 {
622 Q_OBJECT
623 public:
624 AppleProvider *global;
625
AppleNameProvider(AppleProvider * parent)626 AppleNameProvider(AppleProvider *parent) :
627 NameProvider(parent),
628 global(parent)
629 {
630 }
631
~AppleNameProvider()632 ~AppleNameProvider()
633 {
634 global->stop_all(this);
635 }
636
supportsLongLived() const637 virtual bool supportsLongLived() const
638 {
639 return true;
640 }
641
supportsRecordType(int type) const642 virtual bool supportsRecordType(int type) const
643 {
644 // all record types supported
645 Q_UNUSED(type);
646 return true;
647 }
648
resolve_start(const QByteArray & name,int qType,bool longLived)649 virtual int resolve_start(const QByteArray &name, int qType, bool longLived)
650 {
651 Q_UNUSED(longLived); // query is always long lived
652 return global->query(this, name, qType);
653 }
654
resolve_stop(int id)655 virtual void resolve_stop(int id)
656 {
657 global->stop(id);
658 }
659
660 protected:
dns_queryResult(int id,const QDnsSd::QueryResult & result)661 virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result)
662 {
663 if(!result.success)
664 {
665 emit resolve_error(id, XMPP::NameResolver::ErrorGeneric);
666 return;
667 }
668
669 QList<XMPP::NameRecord> results;
670 foreach(const QDnsSd::Record &rec, result.records)
671 {
672 XMPP::NameRecord nr = importQDnsSdRecord(rec);
673
674 // unsupported type
675 if(nr.isNull())
676 continue;
677
678 // if removed, ensure ttl is 0
679 if(!rec.added)
680 nr.setTtl(0);
681
682 results += nr;
683 }
684
685 emit resolve_resultsReady(id, results);
686 }
687 };
688
689 //----------------------------------------------------------------------------
690 // AppleServiceProvider
691 //----------------------------------------------------------------------------
692 class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate
693 {
694 Q_OBJECT
695 public:
696 class Browse
697 {
698 public:
699 AppleServiceProvider *parent;
700 int id;
701 AppleBrowse *browse;
702
Browse(AppleServiceProvider * _parent)703 Browse(AppleServiceProvider *_parent) :
704 parent(_parent),
705 id(-1),
706 browse(0)
707 {
708 }
709
~Browse()710 ~Browse()
711 {
712 delete browse;
713 parent->idManager.releaseId(id);
714 }
715 };
716
717 class Resolve
718 {
719 public:
720 AppleServiceProvider *parent;
721 int id;
722 AppleBrowseLookup *resolve;
723
Resolve(AppleServiceProvider * _parent)724 Resolve(AppleServiceProvider *_parent) :
725 parent(_parent),
726 id(-1),
727 resolve(0)
728 {
729 }
730
~Resolve()731 ~Resolve()
732 {
733 delete resolve;
734 parent->idManager.releaseId(id);
735 }
736 };
737
738 AppleProvider *global;
739 QList<Browse*> browseList;
740 QList<Resolve*> resolveList;
741 IdManager idManager;
742
AppleServiceProvider(AppleProvider * parent)743 AppleServiceProvider(AppleProvider *parent) :
744 ServiceProvider(parent),
745 global(parent)
746 {
747 }
748
~AppleServiceProvider()749 ~AppleServiceProvider()
750 {
751 qDeleteAll(resolveList);
752 qDeleteAll(browseList);
753 global->stop_all(this);
754 }
755
indexOfBrowseByBrowse(AppleBrowse * browse) const756 int indexOfBrowseByBrowse(AppleBrowse *browse) const
757 {
758 for(int n = 0; n < browseList.count(); ++n)
759 {
760 if(browseList[n]->browse == browse)
761 return n;
762 }
763 return -1;
764 }
765
indexOfBrowseById(int id) const766 int indexOfBrowseById(int id) const
767 {
768 for(int n = 0; n < browseList.count(); ++n)
769 {
770 if(browseList[n]->id == id)
771 return n;
772 }
773 return -1;
774 }
775
indexOfResolveByResolve(AppleBrowseLookup * resolve) const776 int indexOfResolveByResolve(AppleBrowseLookup *resolve) const
777 {
778 for(int n = 0; n < resolveList.count(); ++n)
779 {
780 if(resolveList[n]->resolve == resolve)
781 return n;
782 }
783 return -1;
784 }
785
indexOfResolveById(int id) const786 int indexOfResolveById(int id) const
787 {
788 for(int n = 0; n < resolveList.count(); ++n)
789 {
790 if(resolveList[n]->id == id)
791 return n;
792 }
793 return -1;
794 }
795
browse_start(const QString & type,const QString & domain)796 virtual int browse_start(const QString &type, const QString &domain)
797 {
798 Browse *b = new Browse(this);
799 b->id = idManager.reserveId();
800 b->browse = new AppleBrowse(global, this);
801 connect(b->browse, SIGNAL(available(XMPP::ServiceInstance)), SLOT(browse_available(XMPP::ServiceInstance)));
802 connect(b->browse, SIGNAL(unavailable(XMPP::ServiceInstance)), SLOT(browse_unavailable(XMPP::ServiceInstance)));
803 connect(b->browse, SIGNAL(error()), SLOT(browse_error()));
804 browseList += b;
805 b->browse->browse(type, domain);
806 return b->id;
807 }
808
browse_stop(int id)809 virtual void browse_stop(int id)
810 {
811 int at = indexOfBrowseById(id);
812 if(at == -1)
813 return;
814
815 Browse *b = browseList[at];
816 browseList.removeAt(at);
817 delete b;
818 }
819
resolve_start(const QByteArray & name)820 virtual int resolve_start(const QByteArray &name)
821 {
822 QList<QByteArray> parts = nameToInstanceParts(name);
823 if(parts.isEmpty())
824 {
825 // TODO: signal error rather than die
826 Q_ASSERT(0);
827 }
828
829 Resolve *r = new Resolve(this);
830 r->id = idManager.reserveId();
831 r->resolve = new AppleBrowseLookup(global, this);
832 connect(r->resolve, SIGNAL(finished(QList<QHostAddress>)), SLOT(resolve_finished(QList<QHostAddress>)));
833 connect(r->resolve, SIGNAL(error()), SLOT(resolve_error()));
834 resolveList += r;
835 r->resolve->resolve(parts[0], parts[1], parts[2]);
836 return r->id;
837 }
838
resolve_stop(int id)839 virtual void resolve_stop(int id)
840 {
841 int at = indexOfResolveById(id);
842 if(at == -1)
843 return;
844
845 Resolve *r = resolveList[at];
846 resolveList.removeAt(at);
847 delete r;
848 }
849
publish_start(const QString & instance,const QString & type,int port,const QMap<QString,QByteArray> & attributes)850 virtual int publish_start(const QString &instance, const QString &type, int port, const QMap<QString,QByteArray> &attributes)
851 {
852 QByteArray txtRecord = attribsToTxtRecord(attributes);
853 if(txtRecord.isEmpty())
854 {
855 // TODO: signal error rather than die
856 Q_ASSERT(0);
857 }
858
859 QString domain = "local";
860
861 // FIXME: conversion/escaping is probably wrong?
862 return global->reg(this, instance.toUtf8(), type.toUtf8(), domain.toUtf8(), port, txtRecord);
863 }
864
publish_update(int id,const QMap<QString,QByteArray> & attributes)865 virtual void publish_update(int id, const QMap<QString,QByteArray> &attributes)
866 {
867 // TODO: verify 'id' is valid. if not valid, then assert/return (don't do anything or signal error)
868
869 QByteArray txtRecord = attribsToTxtRecord(attributes);
870 if(txtRecord.isEmpty())
871 {
872 // TODO: signal error rather than die
873 Q_ASSERT(0);
874 }
875
876 if(global->dns.recordUpdateTxt(id, txtRecord, 4500))
877 {
878 // FIXME: SR
879 QMetaObject::invokeMethod(this, "publish_published", Qt::QueuedConnection, Q_ARG(int, id));
880 }
881 else
882 {
883 // TODO: unpublish
884
885 // FIXME: register meta type, SR
886 QMetaObject::invokeMethod(this, "publish_error", Qt::QueuedConnection,
887 Q_ARG(int, id),
888 Q_ARG(XMPP::ServiceLocalPublisher::Error, XMPP::ServiceLocalPublisher::ErrorGeneric));
889 }
890 }
891
publish_stop(int id)892 virtual void publish_stop(int id)
893 {
894 global->stop(id);
895 }
896
publish_extra_start(int pub_id,const XMPP::NameRecord & name)897 virtual int publish_extra_start(int pub_id, const XMPP::NameRecord &name)
898 {
899 // TODO
900 Q_UNUSED(pub_id);
901 Q_UNUSED(name);
902 return 0;
903 }
904
publish_extra_update(int id,const XMPP::NameRecord & name)905 virtual void publish_extra_update(int id, const XMPP::NameRecord &name)
906 {
907 // TODO
908 Q_UNUSED(id);
909 Q_UNUSED(name);
910 }
911
publish_extra_stop(int id)912 virtual void publish_extra_stop(int id)
913 {
914 // TODO
915 Q_UNUSED(id);
916 }
917
918 // called by AppleProvider
919
dns_regResult(int id,const QDnsSd::RegResult & result)920 void dns_regResult(int id, const QDnsSd::RegResult &result)
921 {
922 // TODO
923 Q_UNUSED(id);
924 Q_UNUSED(result);
925 }
926
927 private slots:
browse_available(const XMPP::ServiceInstance & instance)928 void browse_available(const XMPP::ServiceInstance &instance)
929 {
930 int at = indexOfBrowseByBrowse((AppleBrowse *)sender());
931 Q_ASSERT(at != -1);
932
933 emit browse_instanceAvailable(browseList[at]->id, instance);
934 }
935
browse_unavailable(const XMPP::ServiceInstance & instance)936 void browse_unavailable(const XMPP::ServiceInstance &instance)
937 {
938 int at = indexOfBrowseByBrowse((AppleBrowse *)sender());
939 Q_ASSERT(at != -1);
940
941 emit browse_instanceUnavailable(browseList[at]->id, instance);
942 }
943
browse_error()944 void browse_error()
945 {
946 int at = indexOfBrowseByBrowse((AppleBrowse *)sender());
947 Q_ASSERT(at != -1);
948
949 Browse *b = browseList[at];
950 browseList.removeAt(at);
951 int id = b->id;
952 delete b;
953
954 // FIXME: this looks weird, we should probably rename our
955 // local function
956 emit ServiceProvider::browse_error(id, XMPP::ServiceBrowser::ErrorGeneric);
957 }
958
resolve_finished(const QList<QHostAddress> & addrs,int port)959 void resolve_finished(const QList<QHostAddress> &addrs, int port)
960 {
961 int at = indexOfResolveByResolve((AppleBrowseLookup *)sender());
962 Q_ASSERT(at != -1);
963
964 Resolve *r = resolveList[at];
965 resolveList.removeAt(at);
966 int id = r->id;
967 delete r;
968
969 QList<ResolveResult> results;
970 foreach(const QHostAddress &addr, addrs)
971 {
972 ResolveResult r;
973 r.address = addr;
974 r.port = port;
975 results += r;
976 }
977
978 emit resolve_resultsReady(id, results);
979 }
980
resolve_error()981 void resolve_error()
982 {
983 int at = indexOfResolveByResolve((AppleBrowseLookup *)sender());
984 Q_ASSERT(at != -1);
985
986 Resolve *r = resolveList[at];
987 resolveList.removeAt(at);
988 int id = r->id;
989 delete r;
990
991 // FIXME: this looks weird, we should probably rename our
992 // local function
993 emit ServiceProvider::resolve_error(id, XMPP::ServiceResolver::ErrorGeneric);
994 }
995 };
996
997 // AppleProvider
createNameProviderInternet()998 XMPP::NameProvider *AppleProvider::createNameProviderInternet()
999 {
1000 return new AppleNameProvider(this);
1001 }
1002
createNameProviderLocal()1003 XMPP::NameProvider *AppleProvider::createNameProviderLocal()
1004 {
1005 return new AppleNameProvider(this);
1006 }
1007
createServiceProvider()1008 XMPP::ServiceProvider *AppleProvider::createServiceProvider()
1009 {
1010 return new AppleServiceProvider(this);
1011 }
1012
1013 #ifdef APPLEDNS_STATIC
irisnet_createAppleProvider()1014 XMPP::IrisNetProvider *irisnet_createAppleProvider()
1015 {
1016 return new AppleProvider;
1017 }
1018 #else
1019 Q_EXPORT_PLUGIN2(appledns, AppleProvider)
1020 #endif
1021
1022 #include "appledns.moc"
1023