ArnLib  4.0.x
Active Registry Network
ArnZeroConf.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // GNU Lesser General Public License Usage
11 // This file may be used under the terms of the GNU Lesser General Public
12 // License version 2.1 as published by the Free Software Foundation and
13 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
14 // file. In addition, as a special exception, you may use the rights described
15 // in the Nokia Qt LGPL Exception version 1.1, included in the file
16 // LGPL_EXCEPTION.txt in this package.
17 //
18 // GNU General Public License Usage
19 // Alternatively, this file may be used under the terms of the GNU General Public
20 // License version 3.0 as published by the Free Software Foundation and appearing
21 // in the file LICENSE_GPL.txt included in the packaging of this file.
22 //
23 // Other Usage
24 // Alternatively, this file may be used in accordance with the terms and conditions
25 // contained in a signed written agreement between you and Michael Wiklund.
26 //
27 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
28 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
29 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
30 //
31 
32 #include "ArnInc/ArnZeroConf.hpp"
33 #include "ArnInc/Arn.hpp"
34 #include "ArnInc/ArnLib.hpp"
35 #include "ArnInc/ArnCompat.hpp"
36 #ifdef MDNS_INTERN
37 # include "mDNS/ArnMDns.hpp"
38 # include "mDNS/mDNSShared/dns_sd.h"
39 #else
40 # include <dns_sd.h>
41 #endif
42 #include <QSocketNotifier>
43 #include <QHostInfo>
44 #include <QTimer>
45 #include <QtEndian>
46 #include <QDebug>
47 
48 using Arn::XStringMap;
49 
50 
51 #ifdef Q_OS_WIN
52 bool ArnZeroConfLookup::_isForceQtDnsLookup( false); // Windows has normally no mDNS DNS lookup
53 #else
54 bool ArnZeroConfLookup::_isForceQtDnsLookup( true); // Deafult expect platform to support mDNS DNS lookup
55 #endif
56 
57 
60 class ArnZeroConfIntern
61 {
62 public:
63  static void DNSSD_API registerServiceCallback(
64  DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errCode,
65  const char* name, const char* regtype, const char* domain, void* context);
66  static void DNSSD_API resolveServiceCallback(
67  DNSServiceRef sdRef, DNSServiceFlags flags, quint32 iface,
68  DNSServiceErrorType errCode, const char* fullname, const char* host,
69  quint16 port, quint16 txtLen, const unsigned char* txt, void* context);
70  static void DNSSD_API browseServiceCallback(
71  DNSServiceRef sdRef, DNSServiceFlags flags, quint32 iface,
72  DNSServiceErrorType errCode, const char* serviceName, const char* regtype,
73  const char* replyDomain, void* context);
74  static void DNSSD_API lookupHostCallback(
75  DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
76  DNSServiceErrorType errCode, const char *hostname,
77  const struct sockaddr *address, uint32_t ttl, void *context);
78 };
80 
81 
82 QAtomicInt ArnZeroConfB::_idCount(1);
83 
84 
85 ArnZeroConfB::ArnZeroConfB( QObject* parent)
86  : QObject( parent)
87 {
88  _port = Arn::defaultTcpPort;
89  _iface = 0;
90  _notifier = arnNullptr;
91  _state = ArnZeroConf::State::None;
92  _serviceType = "arn";
93  _socketType = QAbstractSocket::TcpSocket;
94  _domain = "local."; // Default
95  _sdRef = arnNullptr;
96 
97 #ifndef MDNS_INTERN
98  // In case using Avahi, stop stupid warnings about libdns_sd
99  qputenv("AVAHI_COMPAT_NOWARN", "1");
100 #endif
101 }
102 
103 
105 {
106 }
107 
108 
109 void ArnZeroConfB::parseFullDomain( const QByteArray& domainName)
110 {
111  ARN_RegExp rx("^((?:\\\\{2,2}|\\\\\\.|\\\\\\d{3,3}|[^\\\\\\.])+)\\.(.+\\._(?:tcp|udp))\\.(.+)");
112  if (rx.indexIn( QString::fromUtf8( domainName.constData())) != -1) {
113  setServiceType(rx.cap(2));
114  setDomain(rx.cap(3));
115 
116  QByteArray servName = rx.cap(1).toUtf8();
117  for (int i= 0; i < servName.length(); ++i) {
118  if (servName.at(i) != '\\') continue;
119 
120  char c = servName.at(i + 1);
121  if ((c == '\\') || (c == '.'))
122  servName.remove(i, 1);
123  else if (isdigit(c))
124  servName.replace(i, 4, QByteArray(1, char(servName.mid(i + 1, 3).toInt())));
125  }
126 
127  setServiceName( QString::fromUtf8( servName.constData(), servName.length()));
128  }
129 }
130 
131 
132 QByteArray ArnZeroConfB::escapedName( const QByteArray& name)
133 {
134  QByteArray retVal;
135  const char* namep = name.constData();
136  int len = name.size();
137 
138  for (int i = 0; i < len; ++i) {
139  uchar c = uchar( *namep++);
140  if (c <= ' ') {
141  retVal += '\\';
142  retVal += QByteArray::number(c).rightJustified(3, '0');
143  }
144  else if ((c == '.') || (c == '\\')) {
145  retVal += '\\';
146  retVal += char(c);
147  }
148  else
149  retVal += char(c);
150  }
151 
152  return retVal;
153 }
154 
155 
156 int ArnZeroConfB::getNextId()
157 {
158  return ArnZeroConfB::_idCount.fetchAndAddRelaxed(1);
159 }
160 
161 
162 QStringList ArnZeroConfB::subTypes() const
163 {
164  return _serviceSubTypes;
165 }
166 
167 
168 void ArnZeroConfB::setSubTypes( const QStringList& subTypes)
169 {
170  _serviceSubTypes = subTypes;
171 }
172 
173 
174 void ArnZeroConfB::addSubType( const QString& subType)
175 {
176  if (!subType.isEmpty() && !_serviceSubTypes.contains( subType))
177  _serviceSubTypes += subType;
178 }
179 
180 
181 quint16 ArnZeroConfB::port() const
182 {
183  return _port;
184 }
185 
186 
187 void ArnZeroConfB::setPort( quint16 port)
188 {
189  _port = port;
190 }
191 
192 
194 {
195  return _state;
196 }
197 
198 
199 QByteArray ArnZeroConfB::txtRecord() const
200 {
201  return _txtRec;
202 }
203 
204 
205 void ArnZeroConfB::setTxtRecord( const QByteArray& txt)
206 {
207  _txtRec = txt;
208 }
209 
210 
211 bool ArnZeroConfB::getTxtRecordMap( XStringMap& xsm)
212 {
213  xsm.clear();
214  QByteArray txtRec = _txtRec;
215  forever {
216  if (txtRec.isEmpty()) break; // No more params
217  int parLen = quint8( txtRec.at(0));
218  if (parLen > (txtRec.size() - 1)) return false; // Too long parameter length
219  QByteArray par( txtRec.constData() + 1, parLen);
220  txtRec.remove(0, parLen + 1);
221 
222  int eqPos = par.indexOf('=');
223  xsm.add( par.mid(0, eqPos), par.mid( eqPos + 1));
224  }
225  // qDebug() << "getTxtRecordMap:" << xsm.toXString();
226  return true;
227 }
228 
229 
230 void ArnZeroConfB::setTxtRecordMap( const XStringMap& xsm)
231 {
232  // qDebug() << "setTxtRecordMap:" << xsm.toXString();
233  _txtRec.clear();
234  for (int i = 0; i < xsm.size(); ++i) {
235  QByteArray txtPar = xsm.key(i) + "=" + xsm.value(i);
236  txtPar.insert(0, char( quint8( txtPar.length())));
237  _txtRec += txtPar;
238  }
239 }
240 
241 
242 QString ArnZeroConfB::serviceName() const
243 {
244  return _serviceName;
245 }
246 
247 
248 void ArnZeroConfB::setServiceName( const QString& name)
249 {
250  _serviceName = name;
251 }
252 
253 
254 QAbstractSocket::SocketType ArnZeroConfB::socketType() const
255 {
256  return _socketType;
257 }
258 
259 
260 void ArnZeroConfB::setSocketType( QAbstractSocket::SocketType type)
261 {
262  _socketType = type;
263 }
264 
265 
267 {
268  return _serviceType;
269 }
270 
271 
272 void ArnZeroConfB::setServiceType( const QString& type)
273 {
274  QStringList tp = type.split('.');
275 
276  if ((tp.size() == 2) && (tp.at(0).size() > 0) && (tp.at(0)[0] == '_'))
277  _serviceType = tp.at(0).mid(1);
278  else
279  _serviceType = type;
280 
281  if (tp.size() >= 2) {
282  QString sockTypePart = tp.last();
283  if (sockTypePart == "_udp")
284  setSocketType( QAbstractSocket::UdpSocket);
285  else if (sockTypePart == "_tcp")
286  setSocketType( QAbstractSocket::TcpSocket);
287  else
288  setSocketType( QAbstractSocket::UnknownSocketType);
289  }
290  else
291  setSocketType( QAbstractSocket::TcpSocket);
292 }
293 
294 
295 QString ArnZeroConfB::domain() const
296 {
297  return _domain;
298 }
299 
300 
301 void ArnZeroConfB::setDomain( const QString& domain)
302 {
303  _domain = domain;
304 }
305 
306 
307 QString ArnZeroConfB::host() const
308 {
309  return _host;
310 }
311 
312 
313 void ArnZeroConfB::setHost( const QString& host)
314 {
315  _host = host;
316 }
317 
318 
319 QHostAddress ArnZeroConfB::hostAddr() const
320 {
321  return _hostAddr;
322 }
323 
324 void ArnZeroConfB::setHostAddr( const QHostAddress &hostAddr)
325 {
326  _hostAddr = hostAddr;
327 }
328 
329 
331 {
332  if (_serviceType.startsWith('_')) return _serviceType; // Non standard type
333 
334  QString ret = "_" + _serviceType + "._";
335  if (_socketType == QAbstractSocket::TcpSocket)
336  ret += "tcp";
337  else
338  ret += "udp";
339 
340  return ret;
341 }
342 
343 
344 QByteArray ArnZeroConfB::escapedFullDomain() const
345 {
346  char buffer[kDNSServiceMaxDomainName] = "";
347  int err = DNSServiceConstructFullName(buffer,
348  _serviceName.toUtf8().constData(),
349  fullServiceType().toUtf8().constData(),
350  domain().toUtf8().constData());
351  if (err)
352  return QByteArray(); // error
353  return buffer;
354 }
355 
356 
357 void ArnZeroConfB::socketData()
358 {
359 #ifndef MDNS_INTERN
360  DNSServiceProcessResult( _sdRef);
361 #endif
362 }
363 
364 
366 
367 void ArnZeroConfRegister::init()
368 {
369 #ifdef MDNS_INTERN
370  ArnMDns::attach();
371 #endif
372 }
373 
374 
376  : ArnZeroConfB( parent)
377 {
378  init();
379 }
380 
381 
382 ArnZeroConfRegister::ArnZeroConfRegister( const QString& serviceName, QObject* parent)
383  : ArnZeroConfB( parent)
384 {
386 
387  init();
388 }
389 
390 
391 ArnZeroConfRegister::ArnZeroConfRegister( const QString& serviceName, const QString& serviceType,
392  quint16 port, QObject* parent)
393  : ArnZeroConfB( parent)
394 {
397  setPort( port);
398 
399  init();
400 }
401 
402 
404 {
406  releaseService();
407 
408 #ifdef MDNS_INTERN
409  ArnMDns::detach();
410 #endif
411 }
412 
413 
415 {
416  return _currentServiceName;
417 }
418 
419 
420 void ArnZeroConfRegister::setServiceName( const QString& name)
421 {
422  ArnZeroConfB::setServiceName( name);
423  _currentServiceName = name;
424 }
425 
426 
427 void ArnZeroConfRegister::registerService( bool noAutoRename)
428 {
429  if (state() != ArnZeroConf::State::None) {
430  qWarning() << "ZeroConfRegister: Error register service while not in None state";
431  emit registrationError(0);
432  return;
433  }
434 
435  QByteArray txtRec = txtRecord();
436  if (txtRec.isEmpty())
437  txtRec.fill(0, 1);
438 
439  QByteArray serviceTypes = fullServiceType().toUtf8();
440  foreach (const QString& subType, _serviceSubTypes) {
441  serviceTypes += ",_" + escapedName( subType.toUtf8());
442  }
443 
444  if (Arn::debugZeroConf) qDebug() << "Register: serviceTypes=" << serviceTypes.constData() << "";
445  DNSServiceErrorType err;
446  err = DNSServiceRegister(&_sdRef,
447  noAutoRename ? kDNSServiceFlagsNoAutoRename : 0,
448  uint32_t(_iface),
449  serviceName().toUtf8().constData(),
450  serviceTypes.constData(),
451  domain().toUtf8().constData(),
452  host().toUtf8().constData(),
453  qToBigEndian( port()),
454  uint16_t( txtRec.length()),
455  txtRec.constData(),
456  ArnZeroConfIntern::registerServiceCallback,
457  this);
458  if (err != kDNSServiceErr_NoError) {
459  _state = ArnZeroConf::State::None;
460  emit registrationError(err);
461  }
462  else {
464 #ifndef MDNS_INTERN
465  _notifier = new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read, this);
466  connect( _notifier, SIGNAL(activated(int)), this, SLOT(socketData()));
467 #endif
468  }
469 }
470 
471 
473 {
474  if (!state().isAny( ArnZeroConf::State::Register)) {
475  qWarning() << "ZeroConfRegister release: unregistered service";
476  }
477  else {
478 #ifndef MDNS_INTERN
479  if (_notifier) // Should always be non Null ...
480  delete _notifier;
481  _notifier = arnNullptr;
482 #endif
483  DNSServiceRefDeallocate( _sdRef);
484  _state = ArnZeroConf::State::None;
485  }
486 }
487 
488 
489 void DNSSD_API ArnZeroConfIntern::registerServiceCallback(
490  DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errCode,
491  const char* name, const char* regtype, const char* domain, void* context)
492 {
493  Q_UNUSED(sdRef);
494  Q_UNUSED(flags);
495  Q_UNUSED(regtype);
496  if (Arn::debugZeroConf) qDebug() << "Register callback: name=" << name << " regtype=" << regtype
497  << " domain=" << domain;
498  ArnZeroConfRegister* self = reinterpret_cast<ArnZeroConfRegister*>(context);
499  if (errCode == kDNSServiceErr_NoError) {
500  QString servName = QString::fromUtf8( name);
501  self->_currentServiceName = servName;
502  self->setDomain( QString::fromUtf8( domain));
503  self->_state = ArnZeroConf::State::Registered;
504  emit self->registered( servName);
505  }
506  else {
507  self->_state = ArnZeroConf::State::None;
508  emit self->registrationError( errCode);
509  }
510 }
511 
512 
514 
515 void ArnZeroConfResolve::init()
516 {
517  _id = -1;
518  _operationTimer = new QTimer( this);
519  _operationTimer->setInterval(2000);
520  connect( _operationTimer, SIGNAL(timeout()), this, SLOT(operationTimeout()));
521 
522 #ifdef MDNS_INTERN
523  ArnMDns::attach();
524 #endif
525 }
526 
527 
529  : ArnZeroConfB( parent)
530 {
531  init();
532 }
533 
534 
535 ArnZeroConfResolve::ArnZeroConfResolve(const QString& serviceName, QObject* parent)
536  : ArnZeroConfB( parent)
537 {
539 
540  init();
541 }
542 
543 
544 ArnZeroConfResolve::ArnZeroConfResolve( const QString& serviceName, const QString& serviceType,
545  QObject* parent)
546  : ArnZeroConfB( parent)
547 {
550 
551  init();
552 }
553 
554 
556 {
557  releaseResolve();
558 
559 #ifdef MDNS_INTERN
560  ArnMDns::detach();
561 #endif
562 }
563 
564 
566 {
567  return _id;
568 }
569 
570 
572 {
573  _id = id;
574 }
575 
576 
577 void ArnZeroConfResolve::resolve( bool forceMulticast)
578 {
579  if (_id < 0) // No valid id set, get one
580  _id = ArnZeroConfB::getNextId();
581 
582  if (state().isAny( ArnZeroConf::State::InProgress)) {
583  qWarning() << "ZeroConfResolv: Error resolve service when operation still in progress";
585  return;
586  }
587  releaseResolve();
588 
589  DNSServiceErrorType err;
590  err = DNSServiceResolve(&_sdRef,
591  (forceMulticast ? kDNSServiceFlagsForceMulticast : 0),
592  uint32_t(_iface),
593  serviceName().toUtf8().constData(),
594  fullServiceType().toUtf8().constData(),
595  domain().toUtf8().constData(),
596  ArnZeroConfIntern::resolveServiceCallback,
597  this);
598  if (err != kDNSServiceErr_NoError) {
599  _sdRef = arnNullptr;
600  _state.set( ArnZeroConf::State::Resolve, false);
601  emit resolveError( _id, err);
602  }
603  else {
604  _state.set( ArnZeroConf::State::Resolving);
605  _operationTimer->start();
606 
607 #ifndef MDNS_INTERN
608  _notifier = new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read, this);
609  connect( _notifier, SIGNAL(activated(int)), this, SLOT(socketData()));
610 #endif
611  }
612 }
613 
614 
616 {
617  _operationTimer->stop();
618 
619  if (_sdRef) {
620 #ifndef MDNS_INTERN
621  if (_notifier) // Should always be non Null ...
622  delete _notifier;
623  _notifier = arnNullptr;
624 #endif
625  DNSServiceRefDeallocate( _sdRef);
626  _sdRef = arnNullptr;
627  }
628  _state.set( ArnZeroConf::State::Resolve, false);
629 }
630 
631 
632 void ArnZeroConfResolve::operationTimeout()
633 {
634  releaseResolve();
636 }
637 
638 
639 void DNSSD_API ArnZeroConfIntern::resolveServiceCallback(
640  DNSServiceRef sdRef, DNSServiceFlags flags, quint32 iface,
641  DNSServiceErrorType errCode, const char* fullname, const char* host, quint16 port,
642  quint16 txtLen, const unsigned char* txt, void* context)
643 {
644  Q_UNUSED(sdRef);
645  Q_UNUSED(flags);
646  ArnZeroConfResolve* self = reinterpret_cast<ArnZeroConfResolve*>(context);
647  Q_ASSERT(self);
648 
649  self->_operationTimer->stop();
650 
651  if (self->_id < 0) // No valid id set, get one
652  self->_id = ArnZeroConfBrowser::getNextId();
653 
654  if (Arn::debugZeroConf) qDebug() << "Resolve callback errCode=" << errCode;
655  if (errCode == kDNSServiceErr_NoError) {
656  QString resHost = QString::fromUtf8( host);
657  if (resHost.endsWith('.')) // MW: Remove strangely added "."
658  resHost.resize( resHost.size() - 1);
659  self->parseFullDomain( fullname);
660  self->setHost( resHost);
661  self->setPort( qFromBigEndian( port));
662  self->setTxtRecord( txtLen > 0 ? QByteArray((const char*) txt, txtLen) : QByteArray());
663  self->_iface = int( iface);
664  self->_state.set( ArnZeroConf::State::Resolving, false);
665  self->_state.set( ArnZeroConf::State::Resolved);
666  emit self->resolved( self->_id, fullname);
667  }
668  else {
669  self->_state.set( ArnZeroConf::State::Resolve, false);
670  emit self->resolveError( self->_id, errCode);
671  }
672 }
673 
674 
676 
677 void ArnZeroConfLookup::init()
678 {
679  _id = -1;
680  _operationTimer = new QTimer( this);
681  _operationTimer->setInterval(2000);
682  connect( _operationTimer, SIGNAL(timeout()), this, SLOT(operationTimeout()));
683 
684 #ifdef MDNS_INTERN
685  ArnMDns::attach();
686 #endif
687 }
688 
689 
691  : ArnZeroConfB( parent)
692 {
693  init();
694 }
695 
696 
697 ArnZeroConfLookup::ArnZeroConfLookup( const QString& hostName, QObject* parent)
698  : ArnZeroConfB( parent)
699 {
700  setHost( hostName);
701 
702  init();
703 }
704 
705 
707 {
708  releaseLookup();
709 
710 #ifdef MDNS_INTERN
711  ArnMDns::detach();
712 #endif
713 }
714 
715 
717 {
718  return _id;
719 }
720 
721 
723 {
724  _id = id;
725 }
726 
727 
728 void ArnZeroConfLookup::lookup( bool forceMulticast)
729 {
730  if (_id < 0) // No valid id set, get one
731  _id = ArnZeroConfB::getNextId();
732 
733  if (state().isAny( ArnZeroConf::State::InProgress)) {
734  qWarning() << "ZeroConfLookup: Error lookup host when operation still in progress";
736  return;
737  }
738  releaseLookup();
739 
740  bool useQtLookup = false; // Default
741  useQtLookup |= !forceMulticast && !_host.endsWith(".local");
742  useQtLookup |= _isForceQtDnsLookup;
743 #ifndef MDNS_HAVE_LOOKUP
744  useQtLookup = true;
745 #endif
746 
747  // Qt DNS lookup
748  if (useQtLookup) {
749  int ipLookupId = QHostInfo::lookupHost( _host, this, SLOT(onIpLookup(QHostInfo)));
750  if (Arn::debugZeroConf) qDebug() << "ZeroConfLookup Qt DNS: host=" << _host << " lookupId=" << ipLookupId;
751  _state.set( ArnZeroConf::State::LookingUp);
752  return;
753  }
754 
755 #ifdef MDNS_HAVE_LOOKUP
756  // Multicast DNS lookup
757  if (Arn::debugZeroConf) qDebug() << "ZeroConfLookup mDNS: host=" << _host << " id=" << _id;
758  DNSServiceErrorType err;
759  err = DNSServiceGetAddrInfo(&_sdRef,
760  (forceMulticast ? kDNSServiceFlagsForceMulticast : 0),
761  uint32_t(_iface),
762  kDNSServiceProtocol_IPv4,
763  _host.toUtf8().constData(),
764  ArnZeroConfIntern::lookupHostCallback,
765  this);
766  if (err != kDNSServiceErr_NoError) {
767  _sdRef = arnNullptr;
768  _state.set( ArnZeroConf::State::Lookup, false);
769  emit lookupError( _id, err);
770  }
771  else {
772  _state.set( ArnZeroConf::State::LookingUp);
773  _operationTimer->start();
774 
775 #ifndef MDNS_INTERN
776  _notifier = new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read, this);
777  connect( _notifier, SIGNAL(activated(int)), this, SLOT(socketData()));
778 #endif // MDNS_INTERN
779  }
780 #endif // MDNS_HAVE_LOOKUP
781 }
782 
783 
785 {
786  _operationTimer->stop();
787 
788  if (_sdRef) {
789 #ifndef MDNS_INTERN
790  if (_notifier) // Should always be non Null ...
791  delete _notifier;
792  _notifier = arnNullptr;
793 #endif
794  DNSServiceRefDeallocate( _sdRef);
795  _sdRef = arnNullptr;
796 
797  }
798  _state.set( ArnZeroConf::State::Lookup, false);
799 }
800 
801 
802 void ArnZeroConfLookup::operationTimeout()
803 {
804  releaseLookup();
806 }
807 
808 
809 void ArnZeroConfLookup::onIpLookup( const QHostInfo &host)
810 {
811  if (host.error() == QHostInfo::NoError) {
812  if (Arn::debugZeroConf) {
813  foreach (const QHostAddress &address, host.addresses()) {
814  qDebug() << "Qt Lookup DNS callback, Found address:" << address.toString();
815  }
816  }
817 
818  _hostAddr = host.addresses().first();
819  if (Arn::debugZeroConf) qDebug() << "Qt Lookup DNS callback: hostName=" << _host
820  << " ip=" << _hostAddr.toString();
821  _state.set( ArnZeroConf::State::LookingUp, false);
822  _state.set( ArnZeroConf::State::Lookuped);
823  emit lookuped( _id);
824  }
825  else {
826  if (Arn::debugZeroConf) qDebug() << "ZeroConfLookup uDNS failed:" << host.errorString();
827  _state.set( ArnZeroConf::State::Lookup, false);
829  }
830 }
831 
832 
833 void DNSSD_API ArnZeroConfIntern::lookupHostCallback(
834  DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
835  DNSServiceErrorType errCode, const char *hostname, const sockaddr *address,
836  uint32_t ttl, void *context)
837 {
838  Q_UNUSED(sdRef);
839  Q_UNUSED(flags);
840  Q_UNUSED(interfaceIndex);
841  Q_UNUSED(hostname);
842  Q_UNUSED(ttl);
843 
844 #ifdef MDNS_HAVE_LOOKUP
845  ArnZeroConfLookup* self = reinterpret_cast<ArnZeroConfLookup*>(context);
846  Q_ASSERT(self);
847  self->_operationTimer->stop();
848 
849  if (self->_id < 0) // No valid id set, get one
850  self->_id = ArnZeroConfBrowser::getNextId();
851 
852  if (Arn::debugZeroConf) qDebug() << "Resolve Lookup callback hostName=" << hostname
853  << " errCode=" << errCode;
854  if (errCode == kDNSServiceErr_NoError) {
855  QHostAddress hostAddr( address);
856  self->_hostAddr = hostAddr;
857  if (Arn::debugZeroConf) qDebug() << "Resolve Lookup callback hostName=" << hostname
858  << " ip=" << hostAddr.toString();
859  self->_state.set( ArnZeroConf::State::LookingUp, false);
860  self->_state.set( ArnZeroConf::State::Lookuped);
861  emit self->lookuped( self->_id);
862  }
863  else {
864  self->_state.set( ArnZeroConf::State::Lookup, false);
865  emit self->lookupError( self->_id, errCode);
866  }
867 #else
868  Q_UNUSED(errCode)
869  Q_UNUSED(address)
870  Q_UNUSED(context);
871 #endif
872 }
873 
874 
876 {
877  return _isForceQtDnsLookup;
878 }
879 
880 
881 void ArnZeroConfLookup::setForceQtDnsLookup( bool isForceQtDnsLookup)
882 {
883  _isForceQtDnsLookup = isForceQtDnsLookup;
884 }
885 
886 
888 
889 void ArnZeroConfBrowser::init()
890 {
891 #ifdef MDNS_INTERN
892  ArnMDns::attach();
893 #endif
894 }
895 
896 
898  : ArnZeroConfB( parent)
899 {
900  init();
901 }
902 
903 
904 ArnZeroConfBrowser::ArnZeroConfBrowser( const QString& serviceType, QObject* parent)
905  : ArnZeroConfB( parent)
906 {
908  init();
909 }
910 
911 
913  if(isBrowsing())
914  stopBrowse();
915 
916 #ifdef MDNS_INTERN
917  ArnMDns::detach();
918 #endif
919 }
920 
921 
923 {
924  return _activeServiceNames.keys();
925 }
926 
927 
928 int ArnZeroConfBrowser::serviceNameToId( const QString& name)
929 {
930  return _activeServiceNames.value( name, -1);
931 }
932 
933 
935 {
936  return _state.is( ArnZeroConf::State::Browsing);
937 }
938 
939 
940 void ArnZeroConfBrowser::setSubType( const QString& subtype)
941 {
942  setSubTypes( subtype.isEmpty() ? QStringList() : QStringList( subtype));
943 }
944 
945 
947 {
948  QStringList subT = subTypes();
949  if (subT.isEmpty()) return QString();
950  return subT.at(0);
951 }
952 
953 
954 void ArnZeroConfBrowser::browse( bool enable)
955 {
956  if (!enable) return stopBrowse();
957  if (state() != ArnZeroConf::State::None) return; // Already browsing
958 
959  _activeServiceNames.clear();
960 
961  QByteArray serviceTypes = fullServiceType().toUtf8();
962  if (!_serviceSubTypes.isEmpty()) {
963  serviceTypes += ",_" + escapedName( _serviceSubTypes.at(0).toUtf8());
964  }
965  if (Arn::debugZeroConf) qDebug() << "ZeroConf Browse: types=" << serviceTypes;
966 
967  DNSServiceErrorType err;
968  err = DNSServiceBrowse(&_sdRef,
969  0,
970  uint32_t(_iface),
971  serviceTypes.constData(),
972  domain().toUtf8().constData(),
973  ArnZeroConfIntern::browseServiceCallback,
974  this);
975  if (err) {
976  emit browseError(err);
977  }
978  else {
980 #ifndef MDNS_INTERN
981  _notifier = new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read, this);
982  connect( _notifier, SIGNAL(activated(int)), this, SLOT(socketData()));
983 #endif
984  }
985 }
986 
987 
989 {
991 #ifndef MDNS_INTERN
992  if (_notifier) // Should always be non Null ...
993  delete _notifier;
994  _notifier = arnNullptr;
995 #endif
996  DNSServiceRefDeallocate( _sdRef);
997  }
998  _state = ArnZeroConf::State::None;
999 }
1000 
1001 
1002 void DNSSD_API ArnZeroConfIntern::browseServiceCallback(
1003  DNSServiceRef sdRef, DNSServiceFlags flags, quint32 iface,
1004  DNSServiceErrorType errCode, const char* serviceName, const char* regtype,
1005  const char* replyDomain, void* context)
1006 {
1007  Q_UNUSED(sdRef);
1008  Q_UNUSED(iface);
1009  Q_UNUSED(regtype);
1010 
1011  ArnZeroConfBrowser* self = reinterpret_cast<ArnZeroConfBrowser*>(context);
1012  if (errCode == kDNSServiceErr_NoError) {
1013  bool isAdded = (flags & kDNSServiceFlagsAdd) != 0;
1014  QString servName = QString::fromUtf8( serviceName);
1015  QString repDomain = QString::fromUtf8( replyDomain);
1016  if (isAdded != self->_activeServiceNames.contains( servName)) { // Not multiple add or remove, ok
1017  int id = 0;
1018  if (isAdded) {
1020  self->_activeServiceNames.insert( servName, id);
1021  emit self->serviceAdded( id, servName, repDomain);
1022  }
1023  else {
1024  id = self->_activeServiceNames.value( servName);
1025  self->_activeServiceNames.remove( servName);
1026  emit self->serviceRemoved( id, servName, repDomain);
1027  }
1028  emit self->serviceChanged( id, isAdded, servName, repDomain);
1029  }
1030  }
1031  else {
1032  emit self->browseError( errCode);
1033  }
1034 }
ArnZeroConfBrowser(QObject *parent=arnNullptr)
Standard constructor of an ArnZeroConfBrowser object.
virtual ~ArnZeroConfResolve()
Destructor of an ArnZeroConfResolv object.
void lookuped(int id)
Indicate successfull lookup of host.
QString serviceName() const
Returns the service name for this Zero Config.
Registering a ZeroConfig service.
QString serviceType() const
Returns the service type for this Zero Config.
void setId(int id)
Sets the id number for this this lookup.
isAny(): Operation in progress
Definition: ArnZeroConf.hpp:96
void browse(bool enable=true)
Change state of browsing.
void resolveError(int id, int code)
Indicate unsuccessfull resolve of service.
ArnZeroConfRegister(QObject *parent=arnNullptr)
Standard constructor of an ArnZeroConfRegister object.
Lookup a host.
void setServiceName(const QString &name)
Set the service name for this Zero Config.
QString currentServiceName() const
Returns the current service name for this Zero Config.
void setHost(const QString &host)
Set the host name for this Lookup.
ArnZeroConfLookup(QObject *parent=arnNullptr)
Standard constructor of an ArnZeroConfLookup object.
static int getNextId()
Return the next id number for zero config objects.
QByteArray txtRecord() const
Return the Txt Record for this Zero Config.
void setPort(quint16 port)
Sets the port number for connecting to the service.
void registerService(bool noAutoRename=false)
Register the service.
virtual ~ArnZeroConfB()
QString serviceName() const
Returns the service name used for this resolv.
bool debugZeroConf
Definition: ArnLib.cpp:49
ArnZeroConfResolve(QObject *parent=arnNullptr)
Standard constructor of an ArnZeroConfResolv object.
Container class with string representation for serialized data.
Definition: XStringMap.hpp:107
virtual ~ArnZeroConfBrowser()
Destructor of an ArnZeroConfBrowser object.
virtual ~ArnZeroConfRegister()
Destructor of an ArnZeroConfRegister object.
void setServiceName(const QString &name)
Set the service name used for this resolv.
Browsing for ZeroConfig services.
QString subType()
Return current subtype (filter)
QAbstractSocket::SocketType socketType() const
Returns the socket type for this Zero Config.
ArnZeroConfB(QObject *parent=arnNullptr)
Definition: ArnZeroConf.cpp:85
Base class for Zero Config.
const quint16 defaultTcpPort
Definition: Arn.hpp:50
void releaseService()
Release the service.
#define ARN_RegExp
Definition: ArnCompat.hpp:70
Registering service in progress.
Definition: ArnZeroConf.hpp:76
Resolving service has finished sucessfully.
Definition: ArnZeroConf.hpp:86
void lookupError(int id, int code)
Indicate unsuccessfull lookup of host.
void releaseLookup()
Release the lookup.
static bool isForceQtDnsLookup()
Return Force using Qt for DNS lookup.
Inactive state.
Definition: ArnZeroConf.hpp:74
QByteArray value(int i, const char *def=arnNullptr) const
ArnZeroConf::State state() const
Returns the current state of the service.
void setServiceType(const QString &type)
Returns the service type for this Zero Config.
void clear(bool freeMem=false)
isAny(): Resolving service in progress or has finished sucessfully
Definition: ArnZeroConf.hpp:88
void stopBrowse()
Stop browsing.
Lookup host in progress.
Definition: ArnZeroConf.hpp:90
void resolve(bool forceMulticast=false)
Resolve the service.
void lookup(bool forceMulticast=false)
Lookup the host address.
struct _DNSServiceRef_t * DNSServiceRef
Definition: ArnZeroConf.hpp:45
static void setForceQtDnsLookup(bool isForceQtDnsLookup)
Set Force using Qt for DNS lookup.
Operation timeout.
Definition: ArnZeroConf.hpp:62
Lookup host has finished sucessfully.
Definition: ArnZeroConf.hpp:92
int id() const
Returns the id number for this resolv.
void releaseResolve()
Release the resolving.
void setId(int id)
Sets the id number for this this resolv.
int serviceNameToId(const QString &name)
Return the id for a service by its service name.
Bad request sequence.
Definition: ArnZeroConf.hpp:60
States of ZeroConfig, limited valid for each ArnZeroConfB subclass / These values must be synced with...
Definition: ArnZeroConf.hpp:71
QByteArray key(int i, const char *def=arnNullptr) const
Resolv a ZeroConfig service.
quint16 port() const
Returns the port number for connecting to the service.
void setSocketType(QAbstractSocket::SocketType type)
Sets the socket type for this Zero Config.
void registrationError(int code)
Indicate unsuccessfull registration of service.
Resolving service in progress.
Definition: ArnZeroConf.hpp:84
QString host() const
Returns the host name for this Zero Config.
void browseError(int errorCode)
Indicate unsuccessfull browsing.
XStringMap & add(const char *key, const QByteArray &val)
bool isBrowsing() const
Return the status of the browsing.
Browsing for service in progress.
Definition: ArnZeroConf.hpp:82
QString fullServiceType() const
Returns the full service type for this Zero Config.
int id() const
Returns the id number for this lookup.
isAny(): Lookup host in progress or has finished sucessfully
Definition: ArnZeroConf.hpp:94
int size() const
Definition: XStringMap.hpp:121
Unicast DNS lookup fail.
Definition: ArnZeroConf.hpp:64
QString domain() const
Returns the domain for this Zero Config.
void setDomain(const QString &domain)
Sets the domain for this Zero Config.
QStringList activeServiceNames() const
Return current list of active service names.
virtual ~ArnZeroConfLookup()
Destructor of an ArnZeroConfLookup object.
Registering service has finished sucessfully.
Definition: ArnZeroConf.hpp:78
void setSubType(const QString &subtype)
Set subtype (filter)
isAny(): Registering service in progress or has finished sucessfully
Definition: ArnZeroConf.hpp:80
QString host() const
Returns the host name for this Lookup.