37 # include "mDNS/ArnMDns.hpp" 38 # include "mDNS/mDNSShared/dns_sd.h" 42 #include <QSocketNotifier> 52 bool ArnZeroConfLookup::_isForceQtDnsLookup(
false);
54 bool ArnZeroConfLookup::_isForceQtDnsLookup(
true);
60 class ArnZeroConfIntern
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(
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(
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);
82 QAtomicInt ArnZeroConfB::_idCount(1);
90 _notifier = arnNullptr;
93 _socketType = QAbstractSocket::TcpSocket;
99 qputenv(
"AVAHI_COMPAT_NOWARN",
"1");
109 void ArnZeroConfB::parseFullDomain(
const QByteArray& domainName)
111 ARN_RegExp rx(
"^((?:\\\\{2,2}|\\\\\\.|\\\\\\d{3,3}|[^\\\\\\.])+)\\.(.+\\._(?:tcp|udp))\\.(.+)");
112 if (rx.indexIn( QString::fromUtf8( domainName.constData())) != -1) {
116 QByteArray servName = rx.cap(1).toUtf8();
117 for (
int i= 0; i < servName.length(); ++i) {
118 if (servName.at(i) !=
'\\')
continue;
120 char c = servName.at(i + 1);
121 if ((c ==
'\\') || (c ==
'.'))
122 servName.remove(i, 1);
124 servName.replace(i, 4, QByteArray(1,
char(servName.mid(i + 1, 3).toInt())));
127 setServiceName( QString::fromUtf8( servName.constData(), servName.length()));
132 QByteArray ArnZeroConfB::escapedName(
const QByteArray& name)
135 const char* namep = name.constData();
136 int len = name.size();
138 for (
int i = 0; i < len; ++i) {
139 uchar c = uchar( *namep++);
142 retVal += QByteArray::number(c).rightJustified(3,
'0');
144 else if ((c ==
'.') || (c ==
'\\')) {
156 int ArnZeroConfB::getNextId()
158 return ArnZeroConfB::_idCount.fetchAndAddRelaxed(1);
162 QStringList ArnZeroConfB::subTypes()
const 164 return _serviceSubTypes;
168 void ArnZeroConfB::setSubTypes(
const QStringList& subTypes)
170 _serviceSubTypes = subTypes;
174 void ArnZeroConfB::addSubType(
const QString& subType)
176 if (!subType.isEmpty() && !_serviceSubTypes.contains( subType))
177 _serviceSubTypes += subType;
181 quint16 ArnZeroConfB::port()
const 187 void ArnZeroConfB::setPort( quint16 port)
199 QByteArray ArnZeroConfB::txtRecord()
const 205 void ArnZeroConfB::setTxtRecord(
const QByteArray& txt)
211 bool ArnZeroConfB::getTxtRecordMap(
XStringMap& xsm)
214 QByteArray txtRec = _txtRec;
216 if (txtRec.isEmpty())
break;
217 int parLen = quint8( txtRec.at(0));
218 if (parLen > (txtRec.size() - 1))
return false;
219 QByteArray par( txtRec.constData() + 1, parLen);
220 txtRec.remove(0, parLen + 1);
222 int eqPos = par.indexOf(
'=');
223 xsm.
add( par.mid(0, eqPos), par.mid( eqPos + 1));
230 void ArnZeroConfB::setTxtRecordMap(
const XStringMap& xsm)
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())));
242 QString ArnZeroConfB::serviceName()
const 248 void ArnZeroConfB::setServiceName(
const QString& name)
274 QStringList tp = type.split(
'.');
276 if ((tp.size() == 2) && (tp.at(0).size() > 0) && (tp.at(0)[0] ==
'_'))
277 _serviceType = tp.at(0).mid(1);
281 if (tp.size() >= 2) {
282 QString sockTypePart = tp.last();
283 if (sockTypePart ==
"_udp")
285 else if (sockTypePart ==
"_tcp")
307 QString ArnZeroConfB::host()
const 313 void ArnZeroConfB::setHost(
const QString& host)
319 QHostAddress ArnZeroConfB::hostAddr()
const 324 void ArnZeroConfB::setHostAddr(
const QHostAddress &hostAddr)
326 _hostAddr = hostAddr;
332 if (_serviceType.startsWith(
'_'))
return _serviceType;
334 QString ret =
"_" + _serviceType +
"._";
335 if (_socketType == QAbstractSocket::TcpSocket)
344 QByteArray ArnZeroConfB::escapedFullDomain()
const 346 char buffer[kDNSServiceMaxDomainName] =
"";
347 int err = DNSServiceConstructFullName(buffer,
348 _serviceName.toUtf8().constData(),
350 domain().toUtf8().constData());
357 void ArnZeroConfB::socketData()
360 DNSServiceProcessResult( _sdRef);
367 void ArnZeroConfRegister::init()
392 quint16 port, QObject* parent)
416 return _currentServiceName;
422 ArnZeroConfB::setServiceName( name);
423 _currentServiceName = name;
430 qWarning() <<
"ZeroConfRegister: Error register service while not in None state";
436 if (txtRec.isEmpty())
440 foreach (
const QString& subType, _serviceSubTypes) {
441 serviceTypes +=
",_" + escapedName( subType.toUtf8());
444 if (
Arn::debugZeroConf) qDebug() <<
"Register: serviceTypes=" << serviceTypes.constData() <<
"";
445 DNSServiceErrorType err;
446 err = DNSServiceRegister(&_sdRef,
447 noAutoRename ? kDNSServiceFlagsNoAutoRename : 0,
450 serviceTypes.constData(),
451 domain().toUtf8().constData(),
452 host().toUtf8().constData(),
453 qToBigEndian(
port()),
454 uint16_t( txtRec.length()),
456 ArnZeroConfIntern::registerServiceCallback,
458 if (err != kDNSServiceErr_NoError) {
465 _notifier =
new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read,
this);
466 connect( _notifier, SIGNAL(activated(
int)),
this, SLOT(socketData()));
475 qWarning() <<
"ZeroConfRegister release: unregistered service";
481 _notifier = arnNullptr;
483 DNSServiceRefDeallocate( _sdRef);
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)
496 if (
Arn::debugZeroConf) qDebug() <<
"Register callback: name=" << name <<
" regtype=" << regtype
497 <<
" domain=" << domain;
499 if (errCode == kDNSServiceErr_NoError) {
500 QString servName = QString::fromUtf8( name);
501 self->_currentServiceName = servName;
502 self->setDomain( QString::fromUtf8( domain));
504 emit
self->registered( servName);
508 emit
self->registrationError( errCode);
515 void ArnZeroConfResolve::init()
518 _operationTimer =
new QTimer(
this);
519 _operationTimer->setInterval(2000);
520 connect( _operationTimer, SIGNAL(timeout()),
this, SLOT(operationTimeout()));
580 _id = ArnZeroConfB::getNextId();
583 qWarning() <<
"ZeroConfResolv: Error resolve service when operation still in progress";
589 DNSServiceErrorType err;
590 err = DNSServiceResolve(&_sdRef,
591 (forceMulticast ? kDNSServiceFlagsForceMulticast : 0),
595 domain().toUtf8().constData(),
596 ArnZeroConfIntern::resolveServiceCallback,
598 if (err != kDNSServiceErr_NoError) {
605 _operationTimer->start();
608 _notifier =
new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read,
this);
609 connect( _notifier, SIGNAL(activated(
int)),
this, SLOT(socketData()));
617 _operationTimer->stop();
623 _notifier = arnNullptr;
625 DNSServiceRefDeallocate( _sdRef);
632 void ArnZeroConfResolve::operationTimeout()
639 void DNSSD_API ArnZeroConfIntern::resolveServiceCallback(
641 DNSServiceErrorType errCode,
const char* fullname,
const char* host, quint16 port,
642 quint16 txtLen,
const unsigned char* txt,
void* context)
649 self->_operationTimer->stop();
655 if (errCode == kDNSServiceErr_NoError) {
656 QString resHost = QString::fromUtf8( host);
657 if (resHost.endsWith(
'.'))
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);
666 emit
self->resolved( self->_id, fullname);
670 emit
self->resolveError( self->_id, errCode);
677 void ArnZeroConfLookup::init()
680 _operationTimer =
new QTimer(
this);
681 _operationTimer->setInterval(2000);
682 connect( _operationTimer, SIGNAL(timeout()),
this, SLOT(operationTimeout()));
731 _id = ArnZeroConfB::getNextId();
734 qWarning() <<
"ZeroConfLookup: Error lookup host when operation still in progress";
740 bool useQtLookup =
false;
741 useQtLookup |= !forceMulticast && !_host.endsWith(
".local");
742 useQtLookup |= _isForceQtDnsLookup;
743 #ifndef MDNS_HAVE_LOOKUP 749 int ipLookupId = QHostInfo::lookupHost( _host,
this, SLOT(onIpLookup(QHostInfo)));
750 if (
Arn::debugZeroConf) qDebug() <<
"ZeroConfLookup Qt DNS: host=" << _host <<
" lookupId=" << ipLookupId;
755 #ifdef MDNS_HAVE_LOOKUP 757 if (
Arn::debugZeroConf) qDebug() <<
"ZeroConfLookup mDNS: host=" << _host <<
" id=" << _id;
758 DNSServiceErrorType err;
759 err = DNSServiceGetAddrInfo(&_sdRef,
760 (forceMulticast ? kDNSServiceFlagsForceMulticast : 0),
762 kDNSServiceProtocol_IPv4,
763 _host.toUtf8().constData(),
764 ArnZeroConfIntern::lookupHostCallback,
766 if (err != kDNSServiceErr_NoError) {
773 _operationTimer->start();
776 _notifier =
new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read,
this);
777 connect( _notifier, SIGNAL(activated(
int)),
this, SLOT(socketData()));
778 #endif // MDNS_INTERN 780 #endif // MDNS_HAVE_LOOKUP 786 _operationTimer->stop();
792 _notifier = arnNullptr;
794 DNSServiceRefDeallocate( _sdRef);
802 void ArnZeroConfLookup::operationTimeout()
809 void ArnZeroConfLookup::onIpLookup(
const QHostInfo &host)
811 if (
host.error() == QHostInfo::NoError) {
813 foreach (
const QHostAddress &address,
host.addresses()) {
814 qDebug() <<
"Qt Lookup DNS callback, Found address:" << address.toString();
818 _hostAddr =
host.addresses().first();
820 <<
" ip=" << _hostAddr.toString();
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)
840 Q_UNUSED(interfaceIndex);
844 #ifdef MDNS_HAVE_LOOKUP 847 self->_operationTimer->stop();
853 <<
" errCode=" << errCode;
854 if (errCode == kDNSServiceErr_NoError) {
855 QHostAddress hostAddr( address);
856 self->_hostAddr = hostAddr;
858 <<
" ip=" << hostAddr.toString();
861 emit
self->lookuped( self->_id);
865 emit
self->lookupError( self->_id, errCode);
877 return _isForceQtDnsLookup;
889 void ArnZeroConfBrowser::init()
924 return _activeServiceNames.keys();
930 return _activeServiceNames.value( name, -1);
942 setSubTypes( subtype.isEmpty() ? QStringList() : QStringList( subtype));
948 QStringList subT = subTypes();
949 if (subT.isEmpty())
return QString();
959 _activeServiceNames.clear();
962 if (!_serviceSubTypes.isEmpty()) {
963 serviceTypes +=
",_" + escapedName( _serviceSubTypes.at(0).toUtf8());
967 DNSServiceErrorType err;
968 err = DNSServiceBrowse(&_sdRef,
971 serviceTypes.constData(),
972 domain().toUtf8().constData(),
973 ArnZeroConfIntern::browseServiceCallback,
981 _notifier =
new QSocketNotifier( DNSServiceRefSockFD( _sdRef), QSocketNotifier::Read,
this);
982 connect( _notifier, SIGNAL(activated(
int)),
this, SLOT(socketData()));
994 _notifier = arnNullptr;
996 DNSServiceRefDeallocate( _sdRef);
1002 void DNSSD_API ArnZeroConfIntern::browseServiceCallback(
1004 DNSServiceErrorType errCode,
const char* serviceName,
const char* regtype,
1005 const char* replyDomain,
void* 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)) {
1020 self->_activeServiceNames.insert( servName,
id);
1021 emit
self->serviceAdded(
id, servName, repDomain);
1024 id =
self->_activeServiceNames.value( servName);
1025 self->_activeServiceNames.remove( servName);
1026 emit
self->serviceRemoved(
id, servName, repDomain);
1028 emit
self->serviceChanged(
id, isAdded, servName, repDomain);
1032 emit
self->browseError( errCode);
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
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.
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.
QString serviceName() const
Returns the service name used for this resolv.
ArnZeroConfResolve(QObject *parent=arnNullptr)
Standard constructor of an ArnZeroConfResolv object.
Container class with string representation for serialized data.
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)
Base class for Zero Config.
const quint16 defaultTcpPort
void releaseService()
Release the service.
Registering service in progress.
Resolving service has finished sucessfully.
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.
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
void stopBrowse()
Stop browsing.
void resolve(bool forceMulticast=false)
Resolve the service.
void lookup(bool forceMulticast=false)
Lookup the host address.
struct _DNSServiceRef_t * DNSServiceRef
static void setForceQtDnsLookup(bool isForceQtDnsLookup)
Set Force using Qt for DNS lookup.
Lookup host has finished sucessfully.
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.
States of ZeroConfig, limited valid for each ArnZeroConfB subclass / These values must be synced with...
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.
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.
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
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.
void setSubType(const QString &subtype)
Set subtype (filter)
isAny(): Registering service in progress or has finished sucessfully
QString host() const
Returns the host name for this Lookup.