2
* Copyright (C) 2007,2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
30
# include <netinet/in.h>
37
// safeobj stuff, from qca
39
void releaseAndDeleteLater(QObject *owner, QObject *obj)
41
obj->disconnect(owner);
46
class SafeTimer : public QObject
50
SafeTimer(QObject *parent = 0) :
54
connect(t, SIGNAL(timeout()), SIGNAL(timeout()));
59
releaseAndDeleteLater(this, t);
62
int interval() const { return t->interval(); }
63
bool isActive() const { return t->isActive(); }
64
bool isSingleShot() const { return t->isSingleShot(); }
65
void setInterval(int msec) { t->setInterval(msec); }
66
void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); }
67
int timerId() const { return t->timerId(); }
70
void start(int msec) { t->start(msec); }
71
void start() { t->start(); }
72
void stop() { t->stop(); }
81
class SafeSocketNotifier : public QObject
85
SafeSocketNotifier(int socket, QSocketNotifier::Type type,
86
QObject *parent = 0) :
89
sn = new QSocketNotifier(socket, type, this);
90
connect(sn, SIGNAL(activated(int)), SIGNAL(activated(int)));
95
sn->setEnabled(false);
96
releaseAndDeleteLater(this, sn);
99
bool isEnabled() const { return sn->isEnabled(); }
100
int socket() const { return sn->socket(); }
101
QSocketNotifier::Type type() const { return sn->type(); }
104
void setEnabled(bool enable) { sn->setEnabled(enable); }
107
void activated(int socket);
113
// DNSServiceRef must be allocated by the user and initialized by the
114
// API. Additionally, it is unclear from the API whether or not
115
// DNSServiceRef can be copied (it is an opaque data structure).
116
// What we'll do is allocate DNSServiceRef on the heap, allowing us
117
// to maintain a pointer which /can/ be copied. Also, we'll keep
118
// a flag to indicate whether the allocated DNSServiceRef has been
130
_p = (DNSServiceRef *)malloc(sizeof(DNSServiceRef));
136
DNSServiceRefDeallocate(*_p);
140
DNSServiceRef *data()
145
void setInitialized()
159
_p = (DNSRecordRef *)malloc(sizeof(DNSRecordRef));
179
inline void bump_at()
197
if(!set.contains(at))
209
void releaseId(int id)
217
//----------------------------------------------------------------------------
219
//----------------------------------------------------------------------------
220
class QDnsSd::Private : public QObject
234
SubRecord(Private *self) :
244
_self->idManager.releaseId(_id);
264
SafeSocketNotifier *_sn_read;
265
SafeTimer *_errorTrigger;
268
LowLevelError _lowLevelError;
269
QList<QDnsSd::Record> _queryRecords;
270
QList<QDnsSd::BrowseEntry> _browseEntries;
271
QByteArray _resolveFullName;
272
QByteArray _resolveHost;
274
QByteArray _resolveTxtRecord;
275
QByteArray _regDomain;
278
QList<SubRecord*> _subRecords;
280
Request(Private *self) :
293
qDeleteAll(_subRecords);
295
delete _errorTrigger;
298
_self->idManager.releaseId(_id);
301
int subRecordIndexById(int rec_id) const
303
for(int n = 0; n < _subRecords.count(); ++n)
305
if(_subRecords[n]->_id == rec_id)
312
QHash<int,Request*> _requestsById;
313
QHash<SafeSocketNotifier*,Request*> _requestsBySocket;
314
QHash<SafeTimer*,Request*> _requestsByTimer;
315
QHash<int,Request*> _requestsByRecId;
317
Private(QDnsSd *_q) :
325
qDeleteAll(_requestsById);
328
void setDelayedError(Request *req, const LowLevelError &lowLevelError)
333
req->_lowLevelError = lowLevelError;
335
req->_errorTrigger = new SafeTimer(this);
336
connect(req->_errorTrigger, SIGNAL(timeout()), SLOT(doError()));
337
req->_errorTrigger->setSingleShot(true);
339
_requestsByTimer.insert(req->_errorTrigger, req);
341
req->_errorTrigger->start();
344
void removeRequest(Request *req)
346
foreach(const SubRecord *srec, req->_subRecords)
347
_requestsByRecId.remove(srec->_id);
348
if(req->_errorTrigger)
349
_requestsByTimer.remove(req->_errorTrigger);
351
_requestsBySocket.remove(req->_sn_read);
352
_requestsById.remove(req->_id);
356
int regIdForRecId(int rec_id) const
358
Request *req = _requestsByRecId.value(rec_id);
364
int query(const QByteArray &name, int qType)
366
int id = idManager.reserveId();
368
Request *req = new Request(this);
369
req->_type = Request::Query;
371
req->_sdref = new ServiceRef;
373
DNSServiceErrorType err = DNSServiceQueryRecord(
374
req->_sdref->data(), kDNSServiceFlagsLongLivedQuery,
375
0, name.constData(), qType, kDNSServiceClass_IN,
376
cb_queryRecordReply, req);
377
if(err != kDNSServiceErr_NoError)
379
setDelayedError(req, LowLevelError(
380
"DNSServiceQueryRecord", err));
384
req->_sdref->setInitialized();
386
int sockfd = DNSServiceRefSockFD(*(req->_sdref->data()));
389
setDelayedError(req, LowLevelError(
390
"DNSServiceRefSockFD", -1));
394
req->_sockfd = sockfd;
395
req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this);
396
connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated()));
397
_requestsById.insert(id, req);
398
_requestsBySocket.insert(req->_sn_read, req);
403
int browse(const QByteArray &serviceType, const QByteArray &domain)
405
int id = idManager.reserveId();
407
Request *req = new Request(this);
408
req->_type = Request::Browse;
410
req->_sdref = new ServiceRef;
412
DNSServiceErrorType err = DNSServiceBrowse(
413
req->_sdref->data(), 0, 0, serviceType.constData(),
414
!domain.isEmpty() ? domain.constData() : NULL,
415
cb_browseReply, req);
416
if(err != kDNSServiceErr_NoError)
418
setDelayedError(req, LowLevelError(
419
"DNSServiceBrowse", err));
423
req->_sdref->setInitialized();
425
int sockfd = DNSServiceRefSockFD(*(req->_sdref->data()));
428
setDelayedError(req, LowLevelError(
429
"DNSServiceRefSockFD", -1));
433
req->_sockfd = sockfd;
434
req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this);
435
connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated()));
436
_requestsById.insert(id, req);
437
_requestsBySocket.insert(req->_sn_read, req);
442
int resolve(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain)
444
int id = idManager.reserveId();
446
Request *req = new Request(this);
447
req->_type = Request::Resolve;
449
req->_sdref = new ServiceRef;
451
DNSServiceErrorType err = DNSServiceResolve(
452
req->_sdref->data(), 0, 0, serviceName.constData(),
453
serviceType.constData(), domain.constData(),
454
(DNSServiceResolveReply)cb_resolveReply, req);
455
if(err != kDNSServiceErr_NoError)
457
setDelayedError(req, LowLevelError(
458
"DNSServiceResolve", err));
462
req->_sdref->setInitialized();
464
int sockfd = DNSServiceRefSockFD(*(req->_sdref->data()));
467
setDelayedError(req, LowLevelError(
468
"DNSServiceRefSockFD", -1));
472
req->_sockfd = sockfd;
473
req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this);
474
connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated()));
475
_requestsById.insert(id, req);
476
_requestsBySocket.insert(req->_sn_read, req);
481
int reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord)
483
int id = idManager.reserveId();
485
Request *req = new Request(this);
486
req->_type = Request::Reg;
489
if(port < 1 || port > 0xffff)
491
setDelayedError(req, LowLevelError());
495
uint16_t sport = port;
496
sport = htons(sport);
498
req->_sdref = new ServiceRef;
500
DNSServiceErrorType err = DNSServiceRegister(
501
req->_sdref->data(), kDNSServiceFlagsNoAutoRename, 0,
502
serviceName.constData(), serviceType.constData(),
503
domain.constData(), NULL, sport, txtRecord.size(),
504
txtRecord.data(), cb_regReply, req);
505
if(err != kDNSServiceErr_NoError)
507
setDelayedError(req, LowLevelError(
508
"DNSServiceRegister", err));
512
req->_sdref->setInitialized();
514
int sockfd = DNSServiceRefSockFD(*(req->_sdref->data()));
517
setDelayedError(req, LowLevelError(
518
"DNSServiceRefSockFD", -1));
522
req->_sockfd = sockfd;
523
req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this);
524
connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated()));
525
_requestsById.insert(id, req);
526
_requestsBySocket.insert(req->_sn_read, req);
531
int recordAdd(int reg_id, const Record &rec, LowLevelError *lowLevelError)
533
Request *req = _requestsById.value(reg_id);
537
*lowLevelError = LowLevelError();
541
RecordRef *recordRef = new RecordRef;
543
DNSServiceErrorType err = DNSServiceAddRecord(
544
*(req->_sdref->data()), recordRef->data(), 0,
545
rec.rrtype, rec.rdata.size(), rec.rdata.data(),
547
if(err != kDNSServiceErr_NoError)
550
*lowLevelError = LowLevelError("DNSServiceAddRecord", err);
555
int id = idManager.reserveId();
556
SubRecord *srec = new SubRecord(this);
558
srec->_sdref = recordRef;
559
req->_subRecords += srec;
560
_requestsByRecId.insert(id, req);
565
bool recordUpdate(int reg_id, int rec_id, const Record &rec, LowLevelError *lowLevelError)
567
Request *req = _requestsById.value(reg_id);
571
*lowLevelError = LowLevelError();
578
int at = req->subRecordIndexById(rec_id);
582
*lowLevelError = LowLevelError();
585
srec = req->_subRecords[at];
588
DNSServiceErrorType err = DNSServiceUpdateRecord(
589
*(req->_sdref->data()),
590
srec ? *(srec->_sdref->data()) : NULL, 0,
591
rec.rdata.size(), rec.rdata.data(), rec.ttl);
592
if(err != kDNSServiceErr_NoError)
595
*lowLevelError = LowLevelError("DNSServiceUpdateRecord", err);
602
void recordRemove(int rec_id)
604
Request *req = _requestsByRecId.value(rec_id);
609
int at = req->subRecordIndexById(rec_id);
611
SubRecord *srec = req->_subRecords[at];
612
DNSServiceRemoveRecord(*(req->_sdref->data()), *(srec->_sdref->data()), 0);
613
_requestsByRecId.remove(srec->_id);
614
req->_subRecords.removeAt(at);
620
Request *req = _requestsById.value(id);
628
SafeSocketNotifier *sn_read = (SafeSocketNotifier *)sender();
629
Request *req = _requestsBySocket.value(sn_read);
635
DNSServiceErrorType err = DNSServiceProcessResult(*(req->_sdref->data()));
637
// do error if the above function returns an error, or if we
638
// collected an error during a callback
639
if(err != kDNSServiceErr_NoError || !req->_lowLevelError.func.isEmpty())
641
LowLevelError lowLevelError;
642
if(err != kDNSServiceErr_NoError)
643
lowLevelError = LowLevelError("DNSServiceProcessResult", err);
645
lowLevelError = req->_lowLevelError;
647
// reg conflict indicated via callback
648
bool regConflict = false;
649
if(req->_type == Request::Reg && !req->_lowLevelError.func.isEmpty())
650
regConflict = req->_regConflict;
654
if(req->_type == Request::Query)
656
QDnsSd::QueryResult r;
658
r.lowLevelError = lowLevelError;
659
emit q->queryResult(id, r);
661
else if(req->_type == Request::Browse)
663
QDnsSd::BrowseResult r;
665
r.lowLevelError = lowLevelError;
666
emit q->browseResult(id, r);
668
else if(req->_type == Request::Resolve)
670
QDnsSd::ResolveResult r;
672
r.lowLevelError = lowLevelError;
673
emit q->resolveResult(id, r);
680
r.errorCode = QDnsSd::RegResult::ErrorConflict;
682
r.errorCode = QDnsSd::RegResult::ErrorGeneric;
683
r.lowLevelError = lowLevelError;
684
emit q->regResult(id, r);
692
if(req->_type == Request::Query)
696
QDnsSd::QueryResult r;
698
r.records = req->_queryRecords;
699
req->_queryRecords.clear();
700
req->_doSignal = false;
701
emit q->queryResult(id, r);
704
else if(req->_type == Request::Browse)
708
QDnsSd::BrowseResult r;
710
r.entries = req->_browseEntries;
711
req->_browseEntries.clear();
712
req->_doSignal = false;
713
emit q->browseResult(id, r);
716
else if(req->_type == Request::Resolve)
720
QDnsSd::ResolveResult r;
722
r.fullName = req->_resolveFullName;
723
r.hostTarget = req->_resolveHost;
724
r.port = req->_resolvePort;
725
r.txtRecord = req->_resolveTxtRecord;
726
req->_doSignal = false;
728
// there is only one response
731
emit q->resolveResult(id, r);
740
r.domain = req->_regDomain;
741
req->_doSignal = false;
743
emit q->regResult(id, r);
750
SafeTimer *t = (SafeTimer *)sender();
751
Request *req = _requestsByTimer.value(t);
756
int type = req->_type;
759
if(type == Request::Query)
761
QDnsSd::QueryResult r;
763
r.lowLevelError = req->_lowLevelError;
764
emit q->queryResult(id, r);
766
else if(type == Request::Browse)
768
QDnsSd::BrowseResult r;
770
r.lowLevelError = req->_lowLevelError;
771
emit q->browseResult(id, r);
773
else if(type == Request::Resolve)
775
QDnsSd::ResolveResult r;
777
r.lowLevelError = req->_lowLevelError;
778
emit q->resolveResult(id, r);
784
r.errorCode = QDnsSd::RegResult::ErrorGeneric;
785
r.lowLevelError = req->_lowLevelError;
786
emit q->regResult(id, r);
791
static void cb_queryRecordReply(DNSServiceRef ref,
792
DNSServiceFlags flags, uint32_t interfaceIndex,
793
DNSServiceErrorType errorCode, const char *fullname,
794
uint16_t rrtype, uint16_t rrclass, uint16_t rdlen,
795
const void *rdata, uint32_t ttl, void *context)
798
Q_UNUSED(interfaceIndex);
801
Request *req = static_cast<Request *>(context);
802
req->_self->handle_queryRecordReply(req, flags, errorCode,
803
fullname, rrtype, rdlen, (const char *)rdata, ttl);
806
static void cb_browseReply(DNSServiceRef ref,
807
DNSServiceFlags flags, uint32_t interfaceIndex,
808
DNSServiceErrorType errorCode, const char *serviceName,
809
const char *regtype, const char *replyDomain, void *context)
812
Q_UNUSED(interfaceIndex);
814
Request *req = static_cast<Request *>(context);
815
req->_self->handle_browseReply(req, flags, errorCode,
816
serviceName, regtype, replyDomain);
819
static void cb_resolveReply(DNSServiceRef ref,
820
DNSServiceFlags flags, uint32_t interfaceIndex,
821
DNSServiceErrorType errorCode, const char *fullname,
822
const char *hosttarget, uint16_t port, uint16_t txtLen,
823
const unsigned char *txtRecord, void *context)
827
Q_UNUSED(interfaceIndex);
829
Request *req = static_cast<Request *>(context);
830
req->_self->handle_resolveReply(req, errorCode, fullname,
831
hosttarget, port, txtLen, txtRecord);
834
static void cb_regReply(DNSServiceRef ref,
835
DNSServiceFlags flags, DNSServiceErrorType errorCode,
836
const char *name, const char *regtype, const char *domain,
842
Request *req = static_cast<Request *>(context);
843
req->_self->handle_regReply(req, errorCode, name, regtype,
847
void handle_queryRecordReply(Request *req, DNSServiceFlags flags,
848
DNSServiceErrorType errorCode, const char *fullname,
849
uint16_t rrtype, uint16_t rdlen, const char *rdata,
852
if(errorCode != kDNSServiceErr_NoError)
854
req->_doSignal = true;
855
req->_lowLevelError = LowLevelError("DNSServiceQueryRecordReply", errorCode);
860
rec.added = (flags & kDNSServiceFlagsAdd) ? true: false;
861
rec.name = QByteArray(fullname);
863
rec.rdata = QByteArray(rdata, rdlen);
865
req->_queryRecords += rec;
867
if(!(flags & kDNSServiceFlagsMoreComing))
868
req->_doSignal = true;
871
void handle_browseReply(Request *req, DNSServiceFlags flags,
872
DNSServiceErrorType errorCode, const char *serviceName,
873
const char *regtype, const char *replyDomain)
875
if(errorCode != kDNSServiceErr_NoError)
877
req->_doSignal = true;
878
req->_lowLevelError = LowLevelError("DNSServiceBrowseReply", errorCode);
882
QDnsSd::BrowseEntry e;
883
e.added = (flags & kDNSServiceFlagsAdd) ? true: false;
884
e.serviceName = QByteArray(serviceName);
885
e.serviceType = QByteArray(regtype);
886
e.replyDomain = QByteArray(replyDomain);
887
req->_browseEntries += e;
889
if(!(flags & kDNSServiceFlagsMoreComing))
890
req->_doSignal = true;
893
void handle_resolveReply(Request *req, DNSServiceErrorType errorCode,
894
const char *fullname, const char *hosttarget, uint16_t port,
895
uint16_t txtLen, const unsigned char *txtRecord)
897
if(errorCode != kDNSServiceErr_NoError)
899
req->_doSignal = true;
900
req->_lowLevelError = LowLevelError("DNSServiceResolveReply", errorCode);
904
req->_resolveFullName = QByteArray(fullname);
905
req->_resolveHost = QByteArray(hosttarget);
906
req->_resolvePort = ntohs(port);
907
req->_resolveTxtRecord = QByteArray((const char *)txtRecord, txtLen);
909
req->_doSignal = true;
912
void handle_regReply(Request *req, DNSServiceErrorType errorCode,
913
const char *name, const char *regtype, const char *domain)
918
if(errorCode != kDNSServiceErr_NoError)
920
req->_doSignal = true;
921
req->_lowLevelError = LowLevelError("DNSServiceRegisterReply", errorCode);
923
if(errorCode == kDNSServiceErr_NameConflict)
924
req->_regConflict = true;
926
req->_regConflict = false;
930
req->_regDomain = QByteArray(domain);
931
req->_doSignal = true;
935
QDnsSd::QDnsSd(QObject *parent) :
938
d = new Private(this);
946
int QDnsSd::query(const QByteArray &name, int qType)
948
return d->query(name, qType);
951
int QDnsSd::browse(const QByteArray &serviceType, const QByteArray &domain)
953
return d->browse(serviceType, domain);
956
int QDnsSd::resolve(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain)
958
return d->resolve(serviceName, serviceType, domain);
961
int QDnsSd::reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord)
963
return d->reg(serviceName, serviceType, domain, port, txtRecord);
966
int QDnsSd::recordAdd(int reg_id, const Record &rec, LowLevelError *lowLevelError)
968
return d->recordAdd(reg_id, rec, lowLevelError);
971
bool QDnsSd::recordUpdate(int rec_id, const Record &rec, LowLevelError *lowLevelError)
973
int reg_id = d->regIdForRecId(rec_id);
977
return d->recordUpdate(reg_id, rec_id, rec, lowLevelError);
980
bool QDnsSd::recordUpdateTxt(int reg_id, const QByteArray &txtRecord, quint32 ttl, LowLevelError *lowLevelError)
983
rec.rrtype = kDNSServiceType_TXT;
984
rec.rdata = txtRecord;
986
return d->recordUpdate(reg_id, -1, rec, lowLevelError);
989
void QDnsSd::recordRemove(int rec_id)
991
d->recordRemove(rec_id);
994
void QDnsSd::stop(int id)
999
QByteArray QDnsSd::createTxtRecord(const QList<QByteArray> &strings)
1001
// split into var/val and validate
1002
QList<QByteArray> vars;
1003
QList<QByteArray> vals; // null = no value, empty = empty value
1004
foreach(const QByteArray &i, strings)
1008
int n = i.indexOf('=');
1017
for(int n = 0; n < var.size(); ++n)
1019
unsigned char c = var[n];
1020
if(c < 0x20 || c > 0x7e)
1021
return QByteArray();
1029
QByteArray buf(256, 0);
1030
TXTRecordCreate(&ref, buf.size(), buf.data());
1031
for(int n = 0; n < vars.count(); ++n)
1033
int valueSize = vals[n].size();
1035
if(!vals[n].isNull())
1036
value = vals[n].data();
1040
DNSServiceErrorType err = TXTRecordSetValue(&ref,
1041
vars[n].data(), valueSize, value);
1042
if(err != kDNSServiceErr_NoError)
1044
TXTRecordDeallocate(&ref);
1045
return QByteArray();
1048
QByteArray out((const char *)TXTRecordGetBytesPtr(&ref), TXTRecordGetLength(&ref));
1049
TXTRecordDeallocate(&ref);
1053
QList<QByteArray> QDnsSd::parseTxtRecord(const QByteArray &txtRecord)
1055
QList<QByteArray> out;
1056
int count = TXTRecordGetCount(txtRecord.size(), txtRecord.data());
1057
for(int n = 0; n < count; ++n)
1059
QByteArray keyBuf(256, 0);
1062
DNSServiceErrorType err = TXTRecordGetItemAtIndex(
1063
txtRecord.size(), txtRecord.data(), n, keyBuf.size(),
1064
keyBuf.data(), &valueLen, &value);
1065
if(err != kDNSServiceErr_NoError)
1066
return QList<QByteArray>();
1068
keyBuf.resize(qstrlen(keyBuf.data()));
1070
QByteArray entry = keyBuf;
1074
entry += QByteArray((const char *)value, valueLen);
1081
#include "qdnssd.moc"