2
* Copyright (C) 2005 Justin Karneges
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.
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.
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
20
#include "irisnetplugin.h"
22
#include "jdnsshared.h"
23
#include "netinterface.h"
27
NameRecord importJDNSRecord(const QJDns::Record &in)
32
case QJDns::A: out.setAddress(in.address); break;
33
case QJDns::Aaaa: out.setAddress(in.address); break;
34
case QJDns::Mx: out.setMx(in.name, in.priority); break;
35
case QJDns::Srv: out.setSrv(in.name, in.port, in.priority, in.weight); break;
36
case QJDns::Cname: out.setCname(in.name); break;
37
case QJDns::Ptr: out.setPtr(in.name); break;
38
case QJDns::Txt: out.setTxt(in.texts); break;
39
case QJDns::Hinfo: out.setHinfo(in.cpu, in.os); break;
40
case QJDns::Ns: out.setNs(in.name); break;
41
case 10: out.setNull(in.rdata); break;
45
out.setOwner(in.owner);
50
QJDns::Record exportJDNSRecord(const NameRecord &in)
58
out.address = in.address();
60
case NameRecord::Aaaa:
61
out.type = QJDns::Aaaa;
63
out.address = in.address();
69
out.priority = in.priority();
72
out.type = QJDns::Srv;
76
out.priority = in.priority();
77
out.weight = in.weight();
79
case NameRecord::Cname:
80
out.type = QJDns::Cname;
85
out.type = QJDns::Ptr;
90
out.type = QJDns::Txt;
92
out.texts = in.texts();
94
case NameRecord::Hinfo:
95
out.type = QJDns::Hinfo;
101
out.type = QJDns::Ns;
102
out.haveKnown = true;
103
out.name = in.name();
105
case NameRecord::Null:
107
out.rdata = in.rawData();
112
out.owner = in.owner();
117
//----------------------------------------------------------------------------
119
//----------------------------------------------------------------------------
120
class JDnsGlobal : public QObject
125
JDnsShared *uni_net, *uni_local, *mul;
126
QHostAddress mul_addr4, mul_addr6;
134
connect(&db, SIGNAL(readyRead()), SLOT(jdns_debugReady()));
139
QList<JDnsShared*> list;
147
// calls shutdown on the list, waits for shutdownFinished, deletes
148
JDnsShared::waitForShutdown(list);
154
JDnsShared *ensure_uni_net()
158
uni_net = new JDnsShared(JDnsShared::UnicastInternet, this);
159
uni_net->setDebug(&db, "U");
160
bool ok4 = uni_net->addInterface(QHostAddress::Any);
161
bool ok6 = uni_net->addInterface(QHostAddress::AnyIPv6);
171
JDnsShared *ensure_uni_local()
175
uni_local = new JDnsShared(JDnsShared::UnicastLocal, this);
176
uni_local->setDebug(&db, "L");
177
bool ok4 = uni_local->addInterface(QHostAddress::Any);
178
bool ok6 = uni_local->addInterface(QHostAddress::AnyIPv6);
188
JDnsShared *ensure_mul()
192
mul = new JDnsShared(JDnsShared::Multicast, this);
193
mul->setDebug(&db, "M");
195
updateMulticastInterfaces();
201
void debug(const QStringList &lines);
204
// TODO: call this when the network changes
205
void updateMulticastInterfaces()
207
QHostAddress addr4 = QJDns::detectPrimaryMulticast(QHostAddress::Any);
208
QHostAddress addr6 = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6);
210
if(!(addr4 == mul_addr4))
212
if(!mul_addr4.isNull())
213
mul->removeInterface(mul_addr4);
215
if(!mul_addr4.isNull())
217
if(!mul->addInterface(mul_addr4))
218
mul_addr4 = QHostAddress();
222
if(!(addr6 == mul_addr6))
224
if(!mul_addr6.isNull())
225
mul->removeInterface(mul_addr6);
227
if(!mul_addr6.isNull())
229
if(!mul->addInterface(mul_addr6))
230
mul_addr6 = QHostAddress();
236
void jdns_debugReady()
238
QStringList lines = db.readDebugLines();
240
//for(int n = 0; n < lines.count(); ++n)
241
// printf("jdns: %s\n", qPrintable(lines[n]));
246
//----------------------------------------------------------------------------
248
//----------------------------------------------------------------------------
249
static int next_id = 1;
250
class JDnsNameProvider : public NameProvider
253
Q_INTERFACES(XMPP::NameProvider);
255
enum Mode { Internet, Local };
263
JDnsSharedRequest *req;
281
static JDnsNameProvider *create(JDnsGlobal *global, Mode mode, QObject *parent = 0)
285
if(!global->ensure_uni_net())
290
if(!global->ensure_uni_local())
294
return new JDnsNameProvider(global, mode, parent);
297
JDnsNameProvider(JDnsGlobal *_global, Mode _mode, QObject *parent = 0) : NameProvider(parent)
307
virtual int resolve_start(const QByteArray &name, int qType, bool longLived)
311
if(name.right(6) == ".local" || name.right(7) == ".local.")
316
i->longLived = longLived;
318
QMetaObject::invokeMethod(this, "do_local", Qt::QueuedConnection, Q_ARG(int, i->id));
327
QMetaObject::invokeMethod(this, "do_error", Qt::QueuedConnection, Q_ARG(int, i->id));
332
i->req = new JDnsSharedRequest(global->uni_net);
333
connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
334
i->longLived = false;
337
i->req->query(name, qType);
345
if(!global->ensure_mul())
350
QMetaObject::invokeMethod(this, "do_nolocal", Qt::QueuedConnection, Q_ARG(int, i->id));
354
i->req = new JDnsSharedRequest(global->mul);
359
i->req = new JDnsSharedRequest(global->uni_local);
360
i->longLived = false;
362
connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
365
i->req->query(name, qType);
370
virtual void resolve_stop(int id)
373
for(int n = 0; n < items.count(); ++n)
375
if(items[n]->id == id)
390
virtual void resolve_localResultsReady(int id, const QList<XMPP::NameRecord> &results)
393
for(int n = 0; n < items.count(); ++n)
395
if(items[n]->id == id)
404
// not long-lived, so delete it (long-lived doesn't get looped through here)
408
QMetaObject::invokeMethod(this, "resolve_resultsReady", Qt::QueuedConnection,
409
Q_ARG(int, id), Q_ARG(QList<XMPP::NameRecord>, results));
412
virtual void resolve_localError(int id, XMPP::NameResolver::Error e)
415
for(int n = 0; n < items.count(); ++n)
417
if(items[n]->id == id)
429
QMetaObject::invokeMethod(this, "resolve_error", Qt::QueuedConnection,
430
Q_ARG(int, id), Q_ARG(XMPP::NameResolver::Error, e));
434
void req_resultsReady()
436
JDnsSharedRequest *req = (JDnsSharedRequest *)sender();
439
for(int n = 0; n < items.count(); ++n)
441
if(items[n]->req == req)
454
QList<NameRecord> out;
455
QList<QJDns::Record> results = req->results();
456
for(int n = 0; n < results.count(); ++n)
457
out += importJDNSRecord(results[n]);
463
emit resolve_resultsReady(id, out);
467
JDnsSharedRequest::Error e = req->error();
471
NameResolver::Error error = NameResolver::ErrorGeneric;
472
if(e == JDnsSharedRequest::ErrorNXDomain)
473
error = NameResolver::ErrorNoName;
474
else if(e == JDnsSharedRequest::ErrorTimeout)
475
error = NameResolver::ErrorTimeout;
476
else // ErrorGeneric or ErrorNoNet
477
error = NameResolver::ErrorGeneric;
478
emit resolve_error(id, error);
482
void do_local(int id)
485
for(int n = 0; n < items.count(); ++n)
487
if(items[n]->id == id)
496
QByteArray name = i->name;
497
if(i->longLived) // longlived is a handoff, so delete our instance
503
emit resolve_useLocal(id, name);
506
void do_error(int id)
509
for(int n = 0; n < items.count(); ++n)
511
if(items[n]->id == id)
523
emit resolve_error(id, NameResolver::ErrorNoLongLived);
526
void do_nolocal(int id)
529
for(int n = 0; n < items.count(); ++n)
531
if(items[n]->id == id)
543
emit resolve_error(id, NameResolver::ErrorNoLocal);
547
//----------------------------------------------------------------------------
548
// JDnsServiceProvider
549
//----------------------------------------------------------------------------
550
class JDnsBrowseLookup : public QObject
555
JDnsSharedRequest *req;
557
bool success; // TODO: use this variable
564
QList<QByteArray> attribs;
567
JDnsBrowseLookup(JDnsShared *_jdns)
578
void start(const QByteArray &_name)
585
req = new JDnsSharedRequest(jdns);
586
connect(req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady()));
587
req->query(name, QJDns::Srv);
590
void start2(const QByteArray &_name)
597
req = new JDnsSharedRequest(jdns);
598
connect(req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady()));
599
req->query(name, QJDns::Srv);
606
void jdns_resultsReady()
610
QJDns::Record rec = req->results().first();
616
//printf(" Server: [%s] port=%d\n", srvhost.data(), srvport);
620
req->query(srvhost, QJDns::A); // TODO: ipv6?
622
req->query(name, QJDns::Txt);
628
QJDns::Record rec = req->results().first();
635
//printf("resolve done\n");
640
QJDns::Record rec = req->results().first();
643
if(!rec.texts.isEmpty())
645
if(rec.texts.count() != 1 || !rec.texts[0].isEmpty())
649
/*if(attribs.isEmpty())
651
printf(" No attributes\n");
655
printf(" Attributes:\n", attribs.count());
656
for(int n = 0; n < attribs.count(); ++n)
657
printf(" [%s]\n", attribs[n].data());
663
//printf("Instance Available!\n");
676
QList<QByteArray> attribs;
679
class JDnsBrowse : public QObject
686
JDnsSharedRequest *req;
688
QList<JDnsBrowseLookup*> lookups;
690
JDnsBrowse(JDnsShared *_jdns)
702
void start(const QByteArray &_type)
705
req = new JDnsSharedRequest(jdns);
706
connect(req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady()));
707
req->query(type + ".local.", QJDns::Ptr);
711
void available(const JDnsBrowseInfo &i);
712
void unavailable(const QByteArray &instance);
715
void jdns_resultsReady()
717
QJDns::Record rec = req->results().first();
718
QByteArray name = rec.name;
720
// FIXME: this is wrong, it should search backwards
721
int x = name.indexOf('.');
722
QByteArray instance = name.mid(0, x);
727
JDnsBrowseLookup *bl = 0;
728
for(int n = 0; n < lookups.count(); ++n)
730
if(lookups[n]->name == name)
738
lookups.removeAll(bl);
742
//printf("Instance Gone: [%s]\n", instance.data());
743
emit unavailable(instance);
747
//printf("Instance Found: [%s]\n", instance.data());
749
//printf("Lookup starting\n");
750
JDnsBrowseLookup *bl = new JDnsBrowseLookup(jdns);
751
connect(bl, SIGNAL(resultsReady()), SLOT(bl_resultsReady()));
753
bl->instance = instance;
757
void bl_resultsReady()
759
JDnsBrowseLookup *bl = (JDnsBrowseLookup *)sender();
763
i.instance = bl->instance;
764
i.srvhost = bl->srvhost;
765
i.srvport = bl->srvport;
766
i.attribs = bl->attribs;
768
lookups.removeAll(bl);
771
//printf("Lookup finished\n");
776
/*void JDnsShared::net_available(const QString &id)
778
NetInterface *iface = new NetInterface(id);
779
connect(iface, SIGNAL(unavailable()), SLOT(net_unavailable()));
782
QList<QHostAddress> addrlist = iface->addresses();
784
if(!instances.isEmpty())
787
// prefer using just ipv4
789
for(int n = 0; n < addrlist.count(); ++n)
791
if(addrlist[n].protocol() == QAbstractSocket::IPv4Protocol)
801
addr = QHostAddress("192.168.1.150");
804
void JDnsShared::net_unavailable()
809
class JDnsServiceProvider : public ServiceProvider
815
QList<JDnsBrowse*> list;
816
QHash<QByteArray,ServiceInstance> items;
818
QList<JDnsSharedRequest*> pubitems;
819
QByteArray _servname;
821
static JDnsServiceProvider *create(JDnsGlobal *global, QObject *parent = 0)
823
JDnsServiceProvider *p = new JDnsServiceProvider(global, parent);
827
JDnsServiceProvider(JDnsGlobal *_global, QObject *parent = 0) : ServiceProvider(parent)
832
~JDnsServiceProvider()
834
qDeleteAll(pubitems);
837
virtual int browse_start(const QString &type, const QString &domain)
839
// no support for non-local domains
840
if(!domain.isEmpty() && (domain != ".local." && domain != ".local" && domain != "."))
845
if(!global->ensure_mul())
850
JDnsBrowse *b = new JDnsBrowse(global->mul);
851
connect(b, SIGNAL(available(const JDnsBrowseInfo &)), SLOT(jb_available(const JDnsBrowseInfo &)));
852
connect(b, SIGNAL(unavailable(const QByteArray &)), SLOT(jb_unavailable(const QByteArray &)));
853
b->start(type.toLatin1());
858
virtual void browse_stop(int id)
864
virtual int resolve_start(const QByteArray &name)
866
if(!global->ensure_mul())
871
JDnsBrowseLookup *bl = new JDnsBrowseLookup(global->mul);
872
connect(bl, SIGNAL(finished()), SLOT(bl_finished()));
878
virtual void resolve_stop(int id)
884
virtual int publish_start(const QString &instance, const QString &type, int port, const QMap<QString,QByteArray> &attributes)
886
if(!global->ensure_mul())
891
QString me = QHostInfo::localHostName();
892
//QHostInfo hi = QHostInfo::fromName(me);
893
QByteArray melocal = me.toLatin1() + ".local.";
894
QByteArray servname = instance.toLatin1() + '.' + type.toLatin1() + ".local.";
896
JDnsSharedRequest *req = new JDnsSharedRequest(global->mul);
901
rec.haveKnown = true;
902
rec.address = QHostAddress(); // null address, will be filled in
903
req->publish(QJDns::Unique, rec);
906
/*JDnsSharedRequest *req = new JDnsSharedRequest(global->mul);
908
rec.type = QJDns::Aaaa;
911
rec.haveKnown = true;
912
rec.address = QHostAddress(); // null address, will be filled in
913
req->publish(QJDns::Unique, rec);
916
req = new JDnsSharedRequest(global->mul);
917
rec = QJDns::Record();
918
rec.type = QJDns::Srv;
919
rec.owner = servname;
921
rec.haveKnown = true;
926
req->publish(QJDns::Unique, rec);
929
req = new JDnsSharedRequest(global->mul);
930
rec = QJDns::Record();
931
rec.type = QJDns::Txt;
932
rec.owner = servname;
934
rec.haveKnown = true;
935
QMapIterator<QString,QByteArray> it(attributes);
939
rec.texts += it.key().toLatin1() + '=' + it.value();
941
if(rec.texts.isEmpty())
942
rec.texts += QByteArray();
943
req->publish(QJDns::Unique, rec);
946
req = new JDnsSharedRequest(global->mul);
947
rec = QJDns::Record();
948
rec.type = QJDns::Ptr;
949
rec.owner = type.toLatin1() + ".local.";
951
rec.haveKnown = true;
953
req->publish(QJDns::Shared, rec);
956
_servname = servname;
958
QMetaObject::invokeMethod(this, "publish_published", Qt::QueuedConnection, Q_ARG(int, 1));
963
virtual int publish_update(const QMap<QString,QByteArray> &attributes)
966
Q_UNUSED(attributes);
970
virtual void publish_cancel(int id)
976
virtual int publish_extra_start(int pub_id, const NameRecord &name)
981
JDnsSharedRequest *req = new JDnsSharedRequest(global->mul);
984
rec.owner = _servname;
986
rec.rdata = name.rawData();
987
req->publish(QJDns::Unique, rec);
990
QMetaObject::invokeMethod(this, "publish_extra_published", Qt::QueuedConnection, Q_ARG(int, 2));
995
virtual int publish_extra_update(int id, const NameRecord &name)
1003
virtual void publish_extra_cancel(int id)
1010
void jb_available(const JDnsBrowseInfo &i)
1012
//printf("jb_available: [%s]\n", i.instance.data());
1013
JDnsBrowse *b = (JDnsBrowse *)sender();
1014
QMap<QString,QByteArray> map;
1015
for(int n = 0; n < i.attribs.count(); ++n)
1017
const QByteArray &a = i.attribs[n];
1020
int x = a.indexOf('=');
1023
key = QString::fromLatin1(a.mid(0, x));
1024
value = a.mid(x + 1);
1028
key = QString::fromLatin1(a);
1031
map.insert(key, value);
1033
ServiceInstance si(QString::fromLatin1(i.instance), QString::fromLatin1(b->type), "local.", map);
1034
items.insert(i.name, si);
1035
emit browse_instanceAvailable(1, si);
1038
void jb_unavailable(const QByteArray &instance)
1040
//printf("jb_unavailable: [%s]\n", instance.data());
1041
JDnsBrowse *b = (JDnsBrowse *)sender();
1042
QByteArray name = instance + '.' + b->type + ".local.";
1043
if(!items.contains(name))
1046
ServiceInstance si = items.value(name);
1048
emit browse_instanceUnavailable(1, si);
1053
JDnsBrowseLookup *bl = (JDnsBrowseLookup *)sender();
1054
QHostAddress addr = bl->addr;
1055
int port = bl->srvport;
1057
emit resolve_resultsReady(1, addr, port);
1060
//connect(&jdns, SIGNAL(published(int)), SLOT(jdns_published(int)));
1062
/*virtual int publish_start(NameLocalPublisher::Mode pmode, const NameRecord &name)
1067
QJDns::Response response;
1068
QJDns::Record record = exportJDNSRecord(name);
1069
response.answerRecords += record;
1070
QJDns::PublishMode m = (pmode == NameLocalPublisher::Unique ? QJDns::Unique : QJDns::Shared);
1071
return jdns.publishStart(m, record.owner, record.type, response);
1074
virtual void publish_update(int id, const NameRecord &name)
1076
QJDns::Response response;
1077
QJDns::Record record = exportJDNSRecord(name);
1078
response.answerRecords += record;
1079
return jdns.publishUpdate(id, response);
1082
virtual void publish_stop(int id)
1084
jdns.publishCancel(id);
1087
//else if(e == QJDns::ErrorConflict)
1088
// error = NameResolver::ErrorConflict;
1090
//if(mode == Multicast)
1091
// jdns.queryCancel(id);
1094
//----------------------------------------------------------------------------
1096
//----------------------------------------------------------------------------
1097
class JDnsProvider : public IrisNetProvider
1100
Q_INTERFACES(XMPP::IrisNetProvider);
1114
virtual NameProvider *createNameProviderInternet()
1117
global = new JDnsGlobal;
1118
return JDnsNameProvider::create(global, JDnsNameProvider::Internet);
1121
virtual NameProvider *createNameProviderLocal()
1124
global = new JDnsGlobal;
1125
return JDnsNameProvider::create(global, JDnsNameProvider::Local);
1128
virtual ServiceProvider *createServiceProvider()
1131
global = new JDnsGlobal;
1132
return JDnsServiceProvider::create(global);
1136
IrisNetProvider *irisnet_createJDnsProvider()
1138
return new JDnsProvider;
1143
#include "netnames_jdns.moc"