2
* Copyright (C) 2006 Justin Karneges
3
* Copyright (C) 2009-2010 Dennis Schridde
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
#include "irisnetplugin.h"
28
#include "irisnetglobal_p.h"
29
#include "addressresolver.h"
32
//#define NETNAMES_DEBUG
35
# define NNDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":")
41
//----------------------------------------------------------------------------
43
//----------------------------------------------------------------------------
44
class NameRecord::Private : public QSharedData
48
NameRecord::Type type;
53
int priority, weight, port;
54
QList<QByteArray> texts;
59
#define ENSURE_D { if(!d) d = new Private; }
61
NameRecord::NameRecord()
66
NameRecord::NameRecord(const QByteArray &owner, int ttl)
73
NameRecord::NameRecord(const NameRecord &from)
79
NameRecord::~NameRecord()
83
NameRecord & NameRecord::operator=(const NameRecord &from)
89
bool NameRecord::operator==(const NameRecord &o) {
90
if (isNull() != o.isNull() || owner() != o.owner() || ttl() != o.ttl() || type() != o.type()) {
95
case XMPP::NameRecord::A:
96
case XMPP::NameRecord::Aaaa:
97
return address() == o.address();
98
case XMPP::NameRecord::Mx:
99
return name() == o.name() && priority() == o.priority();
100
case XMPP::NameRecord::Srv:
101
return name() == o.name() && port() == o.port() && priority() == o.priority() && weight() == o.weight();
102
case XMPP::NameRecord::Cname:
103
case XMPP::NameRecord::Ptr:
104
case XMPP::NameRecord::Ns:
105
return name() == o.name();
106
case XMPP::NameRecord::Txt:
107
return texts() == o.texts();
108
case XMPP::NameRecord::Hinfo:
109
return cpu() == o.cpu() && os() == o.os();
110
case XMPP::NameRecord::Null:
111
return rawData() == o.rawData();
112
case XMPP::NameRecord::Any:
119
bool NameRecord::isNull() const
121
return (d ? false : true);
124
QByteArray NameRecord::owner() const
130
int NameRecord::ttl() const
136
NameRecord::Type NameRecord::type() const
142
QHostAddress NameRecord::address() const
148
QByteArray NameRecord::name() const
154
int NameRecord::priority() const
160
int NameRecord::weight() const
166
int NameRecord::port() const
172
QList<QByteArray> NameRecord::texts() const
178
QByteArray NameRecord::cpu() const
184
QByteArray NameRecord::os() const
190
QByteArray NameRecord::rawData() const
196
void NameRecord::setOwner(const QByteArray &name)
202
void NameRecord::setTtl(int seconds)
208
void NameRecord::setAddress(const QHostAddress &a)
211
if(a.protocol() == QAbstractSocket::IPv6Protocol)
212
d->type = NameRecord::Aaaa;
214
d->type = NameRecord::A;
218
void NameRecord::setMx(const QByteArray &name, int priority)
221
d->type = NameRecord::Mx;
223
d->priority = priority;
226
void NameRecord::setSrv(const QByteArray &name, int port, int priority, int weight)
229
d->type = NameRecord::Srv;
232
d->priority = priority;
236
void NameRecord::setCname(const QByteArray &name)
239
d->type = NameRecord::Cname;
243
void NameRecord::setPtr(const QByteArray &name)
246
d->type = NameRecord::Ptr;
250
void NameRecord::setTxt(const QList<QByteArray> &texts)
253
d->type = NameRecord::Txt;
257
void NameRecord::setHinfo(const QByteArray &cpu, const QByteArray &os)
260
d->type = NameRecord::Hinfo;
265
void NameRecord::setNs(const QByteArray &name)
268
d->type = NameRecord::Ns;
272
void NameRecord::setNull(const QByteArray &rawData)
275
d->type = NameRecord::Null;
276
d->rawData = rawData;
279
QDebug operator<<(QDebug dbg, XMPP::NameRecord::Type type)
281
dbg.nospace() << "XMPP::NameRecord::";
285
case XMPP::NameRecord::A:
286
dbg.nospace() << "A";
288
case XMPP::NameRecord::Aaaa:
289
dbg.nospace() << "Aaaa";
291
case XMPP::NameRecord::Mx:
292
dbg.nospace() << "Mx";
294
case XMPP::NameRecord::Srv:
295
dbg.nospace() << "Srv";
297
case XMPP::NameRecord::Cname:
298
dbg.nospace() << "Cname";
300
case XMPP::NameRecord::Ptr:
301
dbg.nospace() << "Ptr";
303
case XMPP::NameRecord::Txt:
304
dbg.nospace() << "Txt";
306
case XMPP::NameRecord::Hinfo:
307
dbg.nospace() << "Hinfo";
309
case XMPP::NameRecord::Ns:
310
dbg.nospace() << "Ns";
312
case XMPP::NameRecord::Null:
313
dbg.nospace() << "Null";
315
case XMPP::NameRecord::Any:
316
dbg.nospace() << "Any";
323
QDebug operator<<(QDebug dbg, const XMPP::NameRecord &record)
325
dbg.nospace() << "XMPP::NameRecord("
326
<< "owner=" << record.owner()
327
<< ", ttl=" << record.ttl()
328
<< ", type=" << record.type();
330
switch(record.type())
332
case XMPP::NameRecord::A:
333
case XMPP::NameRecord::Aaaa:
334
dbg.nospace() << ", address=" << record.address();
336
case XMPP::NameRecord::Mx:
338
<< ", name=" << record.name()
339
<< ", priority=" << record.priority();
341
case XMPP::NameRecord::Srv:
343
<< ", name=" << record.name()
344
<< ", port=" << record.port()
345
<< ", priority=" << record.priority()
346
<< ", weight=" << record.weight();
348
case XMPP::NameRecord::Cname:
349
case XMPP::NameRecord::Ptr:
350
case XMPP::NameRecord::Ns:
351
dbg.nospace() << ", name=" << record.name();
353
case XMPP::NameRecord::Txt:
354
dbg.nospace() << ", texts={" << record.texts() << "}";
356
case XMPP::NameRecord::Hinfo:
357
dbg.nospace() << ", cpu=" << record.cpu() << ", os=" << record.os();
359
case XMPP::NameRecord::Null:
360
dbg.nospace() << ", size=" << record.rawData().size();
362
case XMPP::NameRecord::Any:
363
dbg.nospace() << ", <unknown>";
369
dbg.nospace() << ")";
374
//----------------------------------------------------------------------------
376
//----------------------------------------------------------------------------
377
class ServiceInstance::Private : public QSharedData
380
QString instance, type, domain;
381
QMap<QString,QByteArray> attribs;
385
ServiceInstance::ServiceInstance()
390
ServiceInstance::ServiceInstance(const QString &instance, const QString &type, const QString &domain, const QMap<QString,QByteArray> &attribs)
393
d->instance = instance;
396
d->attribs = attribs;
398
// FIXME: escape the items
399
d->name = instance.toLatin1() + '.' + type.toLatin1() + '.' + domain.toLatin1();
402
ServiceInstance::ServiceInstance(const ServiceInstance &from)
408
ServiceInstance::~ServiceInstance()
412
ServiceInstance & ServiceInstance::operator=(const ServiceInstance &from)
418
QString ServiceInstance::instance() const
423
QString ServiceInstance::type() const
428
QString ServiceInstance::domain() const
433
QMap<QString,QByteArray> ServiceInstance::attributes() const
438
QByteArray ServiceInstance::name() const
443
//----------------------------------------------------------------------------
445
//----------------------------------------------------------------------------
448
Q_GLOBAL_STATIC(QMutex, nman_mutex)
449
static NameManager *g_nman = 0;
451
class NameResolver::Private
460
Private(NameResolver *_q) : q(_q)
465
class ServiceBrowser::Private
472
Private(ServiceBrowser *_q) : q(_q)
477
class ServiceResolver::Private : public QObject
481
Private(ServiceResolver *parent)
482
: q(parent), dns_sd_resolve_id(0), requestedProtocol(IPv6_IPv4), port(0), protocol(QAbstractSocket::IPv6Protocol)
486
/* DNS-SD interaction with NameManager */
487
ServiceResolver *q; //!< Pointing upwards, so NameManager can call its signals
488
int dns_sd_resolve_id; //!< DNS-SD lookup id, set by NameManager
491
Protocol requestedProtocol; //!< IP protocol requested by user
494
QString domain; //!< Domain we are currently looking up
495
QString host; //!< Hostname we are currently looking up
496
QHostAddress address; //!< IP address we are currently looking up
497
quint16 port; //!< Port we are currently looking up
498
QAbstractSocket::NetworkLayerProtocol protocol; //!< IP protocol we are currently looking up
500
XMPP::WeightedNameRecordList srvList; //!< List of resolved SRV names
501
QList<XMPP::NameRecord> hostList; //!< List or resolved hostnames for current SRV name
502
QList<XMPP::NameResolver*> resolverList; //!< NameResolvers currently in use, needed for cleanup
507
WeightedNameRecordList::WeightedNameRecordList()
508
: currentPriorityGroup(priorityGroups.end()) /* void current state */
511
WeightedNameRecordList::WeightedNameRecordList(const QList<XMPP::NameRecord> &list)
516
WeightedNameRecordList::~WeightedNameRecordList() {
519
bool WeightedNameRecordList::isEmpty() const {
520
return currentPriorityGroup == const_cast<WeightedNameRecordList *>(this)->priorityGroups.end();
523
XMPP::NameRecord WeightedNameRecordList::takeNext() {
524
/* Find the next useful priority group */
525
while (currentPriorityGroup != priorityGroups.end() && currentPriorityGroup->empty()) {
526
currentPriorityGroup++;
528
/* There are no priority groups left, return failure */
529
if (currentPriorityGroup == priorityGroups.end()) {
530
#ifdef NETNAMES_DEBUG
531
NNDEBUG << "No more SRV records left";
533
return XMPP::NameRecord();
536
/* Find the new total weight of this priority group */
538
foreach (const XMPP::NameRecord &record, *currentPriorityGroup) {
539
totalWeight += record.weight();
542
#ifdef NETNAMES_DEBUG
543
NNDEBUG << "Total weight:" << totalWeight;
546
/* Pick a random entry */
547
int randomWeight = qrand()/static_cast<float>(RAND_MAX)*totalWeight;
549
#ifdef NETNAMES_DEBUG
550
NNDEBUG << "Picked weight:" << randomWeight;
553
/* Iterate through the priority group until we found the randomly selected entry */
554
WeightedNameRecordPriorityGroup::iterator it(currentPriorityGroup->begin());
555
for (int currentWeight = it->weight(); currentWeight < randomWeight; currentWeight += (++it)->weight()) {}
556
Q_ASSERT(it != currentPriorityGroup->end());
558
/* We are going to delete the entry in the list, so save it */
559
XMPP::NameRecord result(*it);
561
#ifdef NETNAMES_DEBUG
562
NNDEBUG << "Picked record:" << result;
565
/* Delete the entry from list, to prevent it from being tried multiple times */
566
currentPriorityGroup->remove(it->weight(), *it);
567
if (currentPriorityGroup->isEmpty()) {
568
priorityGroups.erase(currentPriorityGroup++);
574
void WeightedNameRecordList::clear() {
575
priorityGroups.clear();
577
/* void current state */
578
currentPriorityGroup = priorityGroups.end();
581
void WeightedNameRecordList::append(const XMPP::WeightedNameRecordList &list) {
582
/* Copy over all records from all groups */
583
foreach (const WeightedNameRecordPriorityGroup &group, list.priorityGroups) {
584
foreach(const NameRecord& record, group) {
589
/* Reset to beginning */
590
currentPriorityGroup = priorityGroups.begin();
593
void WeightedNameRecordList::append(const QList<XMPP::NameRecord> &list) {
594
foreach (const XMPP::NameRecord &record, list) {
595
WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority()));
597
group.insert(record.weight(), record);
599
if (!priorityGroups.contains(record.priority())) {
600
priorityGroups.insert(record.priority(), group);
604
/* Reset to beginning */
605
currentPriorityGroup = priorityGroups.begin();
608
void WeightedNameRecordList::append(const XMPP::NameRecord &record) {
609
WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority()));
611
group.insert(record.weight(), record);
613
if (!priorityGroups.contains(record.priority())) {
614
priorityGroups.insert(record.priority(), group);
617
/* Reset to beginning */
618
currentPriorityGroup = priorityGroups.begin();
621
void WeightedNameRecordList::append(const QString &hostname, quint16 port) {
622
NameRecord record(hostname.toLocal8Bit(), std::numeric_limits<int>::max());
623
record.setSrv(hostname.toLocal8Bit(), port, std::numeric_limits<int>::max(), 0);
627
/* Reset to beginning */
628
currentPriorityGroup = priorityGroups.begin();
631
XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::WeightedNameRecordList &list) {
636
WeightedNameRecordList& WeightedNameRecordList::operator<<(const QList<NameRecord> &list) {
641
XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::NameRecord &record) {
647
QDebug operator<<(QDebug dbg, const XMPP::WeightedNameRecordList &list) {
648
dbg.nospace() << "XMPP::WeightedNameRecordList(\n";
650
/* operator(QDebug, QMap const&) has a bug which makes it crash when trying to print the dereferenced end() iterator */
651
if (!list.isEmpty()) {
652
dbg.nospace() << "current=" << *list.currentPriorityGroup << endl;
655
dbg.nospace() << "{";
657
foreach(int priority, list.priorityGroups.keys()) {
658
dbg.nospace() << "\t" << priority << "->" << list.priorityGroups.value(priority) << endl;
661
dbg.nospace() << "})";
666
class ServiceLocalPublisher::Private
669
ServiceLocalPublisher *q;
673
Private(ServiceLocalPublisher *_q) : q(_q)
678
class NameManager : public QObject
682
NameProvider *p_net, *p_local;
683
ServiceProvider *p_serv;
684
QHash<int,NameResolver::Private*> res_instances;
685
QHash<int,int> res_sub_instances;
687
QHash<int,ServiceBrowser::Private*> br_instances;
688
QHash<int,ServiceResolver::Private*> sres_instances;
689
QHash<int,ServiceLocalPublisher::Private*> slp_instances;
691
NameManager(QObject *parent = 0) : QObject(parent)
705
static NameManager *instance()
707
QMutexLocker locker(nman_mutex());
710
g_nman = new NameManager;
711
irisNetAddPostRoutine(NetNames::cleanup);
716
static void cleanup()
722
void resolve_start(NameResolver::Private *np, const QByteArray &name, int qType, bool longLived)
724
QMutexLocker locker(nman_mutex());
727
np->longLived = longLived;
731
QList<IrisNetProvider*> list = irisNetProviders();
732
for(int n = 0; n < list.count(); ++n)
734
IrisNetProvider *p = list[n];
735
c = p->createNameProviderInternet();
739
Q_ASSERT(c); // we have built-in support, so this should never fail
742
// use queued connections
743
qRegisterMetaType< QList<XMPP::NameRecord> >("QList<XMPP::NameRecord>");
744
qRegisterMetaType<XMPP::NameResolver::Error>("XMPP::NameResolver::Error");
745
connect(p_net, SIGNAL(resolve_resultsReady(int, const QList<XMPP::NameRecord> &)), SLOT(provider_resolve_resultsReady(int, const QList<XMPP::NameRecord> &)));
746
connect(p_net, SIGNAL(resolve_error(int, XMPP::NameResolver::Error)), SLOT(provider_resolve_error(int, XMPP::NameResolver::Error)));
747
connect(p_net, SIGNAL(resolve_useLocal(int, const QByteArray &)), SLOT(provider_resolve_useLocal(int, const QByteArray &)));
750
np->id = p_net->resolve_start(name, qType, longLived);
752
//printf("assigning %d to %p\n", req_id, np);
753
res_instances.insert(np->id, np);
756
void resolve_stop(NameResolver::Private *np)
758
// FIXME: stop sub instances?
759
p_net->resolve_stop(np->id);
763
void resolve_cleanup(NameResolver::Private *np)
765
// clean up any sub instances
767
QList<int> sub_instances_to_remove;
768
QHashIterator<int, int> it(res_sub_instances);
772
if(it.value() == np->id)
773
sub_instances_to_remove += it.key();
776
foreach(int res_sub_id, sub_instances_to_remove)
778
res_sub_instances.remove(res_sub_id);
779
p_local->resolve_stop(res_sub_id);
782
// clean up primary instance
784
res_instances.remove(np->id);
785
NameResolver *q = np->q;
790
void browse_start(ServiceBrowser::Private *np, const QString &type, const QString &domain)
792
QMutexLocker locker(nman_mutex());
796
ServiceProvider *c = 0;
797
QList<IrisNetProvider*> list = irisNetProviders();
798
for(int n = 0; n < list.count(); ++n)
800
IrisNetProvider *p = list[n];
801
c = p->createServiceProvider();
805
Q_ASSERT(c); // we have built-in support, so this should never fail
808
// use queued connections
809
qRegisterMetaType<XMPP::ServiceInstance>("XMPP::ServiceInstance");
810
qRegisterMetaType<XMPP::ServiceBrowser::Error>("XMPP::ServiceBrowser::Error");
812
connect(p_serv, SIGNAL(browse_instanceAvailable(int, const XMPP::ServiceInstance &)), SLOT(provider_browse_instanceAvailable(int, const XMPP::ServiceInstance &)), Qt::QueuedConnection);
813
connect(p_serv, SIGNAL(browse_instanceUnavailable(int, const XMPP::ServiceInstance &)), SLOT(provider_browse_instanceUnavailable(int, const XMPP::ServiceInstance &)), Qt::QueuedConnection);
814
connect(p_serv, SIGNAL(browse_error(int, XMPP::ServiceBrowser::Error)), SLOT(provider_browse_error(int, XMPP::ServiceBrowser::Error)), Qt::QueuedConnection);
819
np->id = p_serv->browse_start(type, domain);
821
br_instances.insert(np->id, np);
824
void resolve_instance_start(ServiceResolver::Private *np, const QByteArray &name)
826
QMutexLocker locker(nman_mutex());
830
ServiceProvider *c = 0;
831
QList<IrisNetProvider*> list = irisNetProviders();
832
for(int n = 0; n < list.count(); ++n)
834
IrisNetProvider *p = list[n];
835
c = p->createServiceProvider();
839
Q_ASSERT(c); // we have built-in support, so this should never fail
842
// use queued connections
843
qRegisterMetaType<QHostAddress>("QHostAddress");
844
qRegisterMetaType< QList<XMPP::ServiceProvider::ResolveResult> >("QList<XMPP::ServiceProvider::ResolveResult>");
845
connect(p_serv, SIGNAL(resolve_resultsReady(int, const QList<XMPP::ServiceProvider::ResolveResult> &)), SLOT(provider_resolve_resultsReady(int, const QList<XMPP::ServiceProvider::ResolveResult> &)), Qt::QueuedConnection);
848
/* store the id so we can stop it later */
849
np->dns_sd_resolve_id = p_serv->resolve_start(name);
851
sres_instances.insert(np->dns_sd_resolve_id, np);
854
void publish_start(ServiceLocalPublisher::Private *np, const QString &instance, const QString &type, int port, const QMap<QString,QByteArray> &attribs)
856
QMutexLocker locker(nman_mutex());
860
ServiceProvider *c = 0;
861
QList<IrisNetProvider*> list = irisNetProviders();
862
for(int n = 0; n < list.count(); ++n)
864
IrisNetProvider *p = list[n];
865
c = p->createServiceProvider();
869
Q_ASSERT(c); // we have built-in support, so this should never fail
872
// use queued connections
873
qRegisterMetaType<XMPP::ServiceLocalPublisher::Error>("XMPP::ServiceLocalPublisher::Error");
874
connect(p_serv, SIGNAL(publish_published(int)), SLOT(provider_publish_published(int)), Qt::QueuedConnection);
875
connect(p_serv, SIGNAL(publish_extra_published(int)), SLOT(provider_publish_extra_published(int)), Qt::QueuedConnection);
880
np->id = p_serv->publish_start(instance, type, port, attribs);
882
slp_instances.insert(np->id, np);
885
void publish_extra_start(ServiceLocalPublisher::Private *np, const NameRecord &rec)
887
np->id = p_serv->publish_extra_start(np->id, rec);
891
void provider_resolve_resultsReady(int id, const QList<XMPP::NameRecord> &results)
893
NameResolver::Private *np = res_instances.value(id);
894
NameResolver *q = np->q; // resolve_cleanup deletes np
897
emit q->resultsReady(results);
900
void provider_resolve_error(int id, XMPP::NameResolver::Error e)
902
NameResolver::Private *np = res_instances.value(id);
903
NameResolver *q = np->q; // resolve_cleanup deletes np
908
void provider_local_resolve_resultsReady(int id, const QList<XMPP::NameRecord> &results)
910
int par_id = res_sub_instances.value(id);
911
NameResolver::Private *np = res_instances.value(par_id);
913
res_sub_instances.remove(id);
914
p_net->resolve_localResultsReady(par_id, results);
917
void provider_local_resolve_error(int id, XMPP::NameResolver::Error e)
919
int par_id = res_sub_instances.value(id);
920
res_sub_instances.remove(id);
921
p_net->resolve_localError(par_id, e);
924
void provider_resolve_useLocal(int id, const QByteArray &name)
930
QList<IrisNetProvider*> list = irisNetProviders();
931
for(int n = 0; n < list.count(); ++n)
933
IrisNetProvider *p = list[n];
934
c = p->createNameProviderLocal();
938
Q_ASSERT(c); // we have built-in support, so this should never fail
939
// FIXME: not true, binding can fail
942
// use queued connections
943
qRegisterMetaType< QList<XMPP::NameRecord> >("QList<XMPP::NameRecord>");
944
qRegisterMetaType<XMPP::NameResolver::Error>("XMPP::NameResolver::Error");
945
connect(p_local, SIGNAL(resolve_resultsReady(int, const QList<XMPP::NameRecord> &)), SLOT(provider_local_resolve_resultsReady(int, const QList<XMPP::NameRecord> &)), Qt::QueuedConnection);
946
connect(p_local, SIGNAL(resolve_error(int, XMPP::NameResolver::Error)), SLOT(provider_local_resolve_error(int, XMPP::NameResolver::Error)), Qt::QueuedConnection);
949
NameResolver::Private *np = res_instances.value(id);
951
/*// transfer to local only
954
res_instances.remove(np->id);
956
np->id = p_local->resolve_start(name, np->type, true);
957
res_instances.insert(np->id, np);
962
int req_id = p_local->resolve_start(name, np->type, false);
964
res_sub_instances.insert(req_id, np->id);
967
int req_id = p_local->resolve_start(name, np->type, np->longLived);
968
res_sub_instances.insert(req_id, np->id);
971
void provider_browse_instanceAvailable(int id, const XMPP::ServiceInstance &i)
973
ServiceBrowser::Private *np = br_instances.value(id);
974
emit np->q->instanceAvailable(i);
977
void provider_browse_instanceUnavailable(int id, const XMPP::ServiceInstance &i)
979
ServiceBrowser::Private *np = br_instances.value(id);
980
emit np->q->instanceUnavailable(i);
983
void provider_browse_error(int id, XMPP::ServiceBrowser::Error e)
986
ServiceBrowser::Private *np = br_instances.value(id);
991
void provider_resolve_resultsReady(int id, const QList<XMPP::ServiceProvider::ResolveResult> &results)
993
ServiceResolver::Private *np = sres_instances.value(id);
994
emit np->q->resultReady(results[0].address, results[0].port);
997
void provider_publish_published(int id)
999
ServiceLocalPublisher::Private *np = slp_instances.value(id);
1000
emit np->q->published();
1003
void provider_publish_extra_published(int id)
1006
//ServiceLocalPublisher::Private *np = slp_instances.value(id);
1007
//emit np->q->published();
1011
//----------------------------------------------------------------------------
1013
//----------------------------------------------------------------------------
1016
#define JDNS_RTYPE_A 1
1017
#define JDNS_RTYPE_AAAA 28
1018
#define JDNS_RTYPE_MX 15
1019
#define JDNS_RTYPE_SRV 33
1020
#define JDNS_RTYPE_CNAME 5
1021
#define JDNS_RTYPE_PTR 12
1022
#define JDNS_RTYPE_TXT 16
1023
#define JDNS_RTYPE_HINFO 13
1024
#define JDNS_RTYPE_NS 2
1025
#define JDNS_RTYPE_ANY 255
1027
static int recordType2Rtype(NameRecord::Type type)
1031
case NameRecord::A: return JDNS_RTYPE_A;
1032
case NameRecord::Aaaa: return JDNS_RTYPE_AAAA;
1033
case NameRecord::Mx: return JDNS_RTYPE_MX;
1034
case NameRecord::Srv: return JDNS_RTYPE_SRV;
1035
case NameRecord::Cname: return JDNS_RTYPE_CNAME;
1036
case NameRecord::Ptr: return JDNS_RTYPE_PTR;
1037
case NameRecord::Txt: return JDNS_RTYPE_TXT;
1038
case NameRecord::Hinfo: return JDNS_RTYPE_HINFO;
1039
case NameRecord::Ns: return JDNS_RTYPE_NS;
1040
case NameRecord::Null: return 10;
1041
case NameRecord::Any: return JDNS_RTYPE_ANY;
1046
NameResolver::NameResolver(QObject *parent)
1052
NameResolver::~NameResolver()
1057
void NameResolver::start(const QByteArray &name, NameRecord::Type type, Mode mode)
1060
d = new Private(this);
1061
int qType = recordType2Rtype(type);
1063
qType = JDNS_RTYPE_A;
1064
NameManager::instance()->resolve_start(d, name, qType, mode == NameResolver::LongLived ? true : false);
1067
void NameResolver::stop()
1071
NameManager::instance()->resolve_stop(d);
1077
QDebug operator<<(QDebug dbg, XMPP::NameResolver::Error e)
1079
dbg.nospace() << "XMPP::NameResolver::";
1083
case XMPP::NameResolver::ErrorGeneric:
1084
dbg.nospace() << "ErrorGeneric";
1086
case XMPP::NameResolver::ErrorNoName:
1087
dbg.nospace() << "ErrorNoName";
1089
case XMPP::NameResolver::ErrorTimeout:
1090
dbg.nospace() << "ErrorTimeout";
1092
case XMPP::NameResolver::ErrorNoLocal:
1093
dbg.nospace() << "ErrorNoLocal";
1095
case XMPP::NameResolver::ErrorNoLongLived:
1096
dbg.nospace() << "ErrorNoLongLived";
1104
//----------------------------------------------------------------------------
1106
//----------------------------------------------------------------------------
1107
ServiceBrowser::ServiceBrowser(QObject *parent)
1110
d = new Private(this);
1113
ServiceBrowser::~ServiceBrowser()
1118
void ServiceBrowser::start(const QString &type, const QString &domain)
1120
NameManager::instance()->browse_start(d, type, domain);
1123
void ServiceBrowser::stop()
1128
//----------------------------------------------------------------------------
1130
//----------------------------------------------------------------------------
1131
ServiceResolver::ServiceResolver(QObject *parent)
1134
#ifdef NETNAMES_DEBUG
1138
d = new Private(this);
1141
ServiceResolver::~ServiceResolver()
1146
void ServiceResolver::clear_resolvers()
1148
#ifdef NETNAMES_DEBUG
1152
/* cleanup all resolvers */
1153
foreach (XMPP::NameResolver *resolver, d->resolverList) {
1154
cleanup_resolver(resolver);
1158
void ServiceResolver::cleanup_resolver(XMPP::NameResolver *resolver)
1160
#ifdef NETNAMES_DEBUG
1161
NNDEBUG << "r:" << resolver;
1166
do not just "delete", because we might have been called from a slot
1167
that was invoked by the resolver, and we do not want to create a mess
1170
disconnect(resolver);
1172
resolver->deleteLater();
1174
d->resolverList.removeAll(resolver);
1178
ServiceResolver::Protocol ServiceResolver::protocol() const {
1179
return d->requestedProtocol;
1182
void ServiceResolver::setProtocol(ServiceResolver::Protocol p) {
1183
d->requestedProtocol = p;
1187
void ServiceResolver::start(const QByteArray &name) {
1188
NameManager::instance()->resolve_instance_start(d, name);
1191
/* normal host lookup */
1192
void ServiceResolver::start(const QString &host, quint16 port)
1194
#ifdef NETNAMES_DEBUG
1195
NNDEBUG << "h:" << host << "p:" << port;
1198
/* clear host list */
1199
d->hostList.clear();
1201
d->protocol = (d->requestedProtocol == IPv6_IPv4 || d->requestedProtocol == IPv6 ? QAbstractSocket::IPv6Protocol : QAbstractSocket::IPv4Protocol);
1205
#ifdef NETNAMES_DEBUG
1206
NNDEBUG << "d->p:" << d->protocol;
1209
/* initiate the host lookup */
1210
XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A);
1211
XMPP::NameResolver *resolver = new XMPP::NameResolver;
1212
connect(resolver, SIGNAL(resultsReady(QList<XMPP::NameRecord>)), this, SLOT(handle_host_ready(QList<XMPP::NameRecord>)));
1213
connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_error(XMPP::NameResolver::Error)));
1214
resolver->start(host.toLocal8Bit(), querytype);
1215
d->resolverList << resolver;
1219
void ServiceResolver::start(const QString &service, const QString &transport, const QString &domain, int port)
1221
#ifdef NETNAMES_DEBUG
1222
NNDEBUG << "s:" << service << "t:" << transport << "d:" << domain << "p:" << port;
1225
QString srv_request("_" + service + "._" + transport + "." + domain + ".");
1227
/* clear SRV list */
1232
/* after we tried all SRV hosts, we shall connect directly (if requested) */
1233
if (port < std::numeric_limits<quint16>::max()) {
1234
d->srvList.append(domain.toLocal8Bit(), port);
1237
/* The only "valid" port above the valid port range is our specification of an invalid port */
1238
Q_ASSERT(port == std::numeric_limits<int>::max());
1241
/* initiate the SRV lookup */
1242
XMPP::NameResolver *resolver = new XMPP::NameResolver;
1243
connect(resolver, SIGNAL(resultsReady(QList<XMPP::NameRecord>)), this, SLOT(handle_srv_ready(QList<XMPP::NameRecord>)));
1244
connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_srv_error(XMPP::NameResolver::Error)));
1245
resolver->start(srv_request.toLocal8Bit(), XMPP::NameRecord::Srv);
1246
d->resolverList << resolver;
1249
/* SRV request resolved, now try to connect to the hosts */
1250
void ServiceResolver::handle_srv_ready(const QList<XMPP::NameRecord> &r)
1252
#ifdef NETNAMES_DEBUG
1253
NNDEBUG << "sl:" << r;
1256
/* cleanup resolver */
1257
cleanup_resolver(static_cast<XMPP::NameResolver*>(sender()));
1259
/* lookup srv pointers */
1264
/* failed the srv lookup, but we might have a fallback host in the srvList */
1265
void ServiceResolver::handle_srv_error(XMPP::NameResolver::Error e)
1267
#ifdef NETNAMES_DEBUG
1268
NNDEBUG << "e:" << e;
1273
/* cleanup resolver */
1274
cleanup_resolver(static_cast<XMPP::NameResolver*>(sender()));
1276
/* srvList already contains a failsafe host, try that */
1280
/* hosts resolved, now try to connect to them */
1281
void ServiceResolver::handle_host_ready(const QList<XMPP::NameRecord> &r)
1283
#ifdef NETNAMES_DEBUG
1284
NNDEBUG << "hl:" << r;
1287
/* cleanup resolver */
1288
cleanup_resolver(static_cast<XMPP::NameResolver*>(sender()));
1290
/* connect to host */
1295
/* failed to lookup the primary record (A or AAAA, depending on user choice) */
1296
void ServiceResolver::handle_host_error(XMPP::NameResolver::Error e)
1298
#ifdef NETNAMES_DEBUG
1299
NNDEBUG << "e:" << e;
1302
/* cleanup resolver */
1303
cleanup_resolver(static_cast<XMPP::NameResolver*>(sender()));
1305
/* try a fallback lookup if requested*/
1306
if (!lookup_host_fallback()) {
1307
/* no-fallback should behave the same as a failed fallback */
1308
handle_host_fallback_error(e);
1312
/* failed to lookup the fallback record (A or AAAA, depending on user choice) */
1313
void ServiceResolver::handle_host_fallback_error(XMPP::NameResolver::Error e)
1315
#ifdef NETNAMES_DEBUG
1316
NNDEBUG << "e:" << e;
1321
/* cleanup resolver */
1322
cleanup_resolver(static_cast<XMPP::NameResolver*>(sender()));
1324
/* lookup next SRV */
1328
/* check whether a fallback is needed in the current situation */
1329
bool ServiceResolver::check_protocol_fallback()
1331
return (d->requestedProtocol == IPv6_IPv4 && d->protocol == QAbstractSocket::IPv6Protocol)
1332
|| (d->requestedProtocol == IPv4_IPv6 && d->protocol == QAbstractSocket::IPv4Protocol);
1335
/* lookup the fallback host */
1336
bool ServiceResolver::lookup_host_fallback() {
1337
#ifdef NETNAMES_DEBUG
1341
/* if a fallback is desired, otherwise we must fail immediately */
1342
if (!check_protocol_fallback()) {
1346
d->protocol = (d->protocol == QAbstractSocket::IPv6Protocol ? QAbstractSocket::IPv4Protocol : QAbstractSocket::IPv6Protocol);
1348
#ifdef NETNAMES_DEBUG
1349
NNDEBUG << "d->p:" << d->protocol;
1352
/* initiate the fallback host lookup */
1353
XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A);
1354
XMPP::NameResolver *resolver = new XMPP::NameResolver;
1355
connect(resolver, SIGNAL(resultsReady(QList<XMPP::NameRecord>)), this, SLOT(handle_host_ready(QList<XMPP::NameRecord>)));
1356
connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_fallback_error(XMPP::NameResolver::Error)));
1357
resolver->start(d->host.toLocal8Bit(), querytype);
1358
d->resolverList << resolver;
1363
/* notify user about next host */
1364
bool ServiceResolver::try_next_host() {
1365
#ifdef NETNAMES_DEBUG
1366
NNDEBUG << "hl:" << d->hostList;
1369
/* if there is a host left for current protocol (AAAA or A) */
1370
if (!d->hostList.empty()) {
1371
XMPP::NameRecord record(d->hostList.takeFirst());
1372
/* emit found address and the port specified earlier */
1373
emit resultReady(record.address(), d->port);
1377
/* otherwise try the fallback protocol */
1378
return lookup_host_fallback();
1381
/* lookup the next SRV record in line */
1382
void ServiceResolver::try_next_srv()
1384
#ifdef NETNAMES_DEBUG
1385
NNDEBUG << "sl:" << d->srvList;
1388
/* if there are still hosts we did not try */
1389
if (!d->srvList.isEmpty()) {
1390
XMPP::NameRecord record(d->srvList.takeNext());
1391
/* lookup host by name and specify port for later use */
1392
start(record.name(), record.port());
1395
#ifdef NETNAMES_DEBUG
1396
NNDEBUG << "SRV list empty, failing";
1398
/* no more SRV hosts to try, fail */
1399
emit error(NoHostLeft);
1403
void ServiceResolver::tryNext() {
1404
/* if the host list cannot help, try the SRV list */
1405
if (!try_next_host()) {
1410
void ServiceResolver::stop() {
1414
bool ServiceResolver::hasPendingSrv() const
1416
return !d->srvList.isEmpty();
1420
//----------------------------------------------------------------------------
1421
// ServiceLocalPublisher
1422
//----------------------------------------------------------------------------
1423
ServiceLocalPublisher::ServiceLocalPublisher(QObject *parent)
1426
d = new Private(this);
1429
ServiceLocalPublisher::~ServiceLocalPublisher()
1434
void ServiceLocalPublisher::publish(const QString &instance, const QString &type, int port, const QMap<QString,QByteArray> &attributes)
1436
NameManager::instance()->publish_start(d, instance, type, port, attributes);
1439
void ServiceLocalPublisher::updateAttributes(const QMap<QString,QByteArray> &attributes)
1441
Q_UNUSED(attributes);
1444
void ServiceLocalPublisher::addRecord(const NameRecord &rec)
1446
NameManager::instance()->publish_extra_start(d, rec);
1449
void ServiceLocalPublisher::cancel()
1453
//----------------------------------------------------------------------------
1455
//----------------------------------------------------------------------------
1456
void NetNames::cleanup()
1458
NameManager::cleanup();
1461
QString NetNames::diagnosticText()
1467
QByteArray NetNames::idnaFromString(const QString &in)
1471
return QByteArray();
1474
QString NetNames::idnaToString(const QByteArray &in)
1481
QByteArray NetNames::escapeDomain(const QByteArray &in)
1485
return QByteArray();
1488
QByteArray NetNames::unescapeDomain(const QByteArray &in)
1492
return QByteArray();
1497
#include "netnames.moc"