21 #include <QCoreApplication>    35 #include <arpa/inet.h>    41                                         DNSServiceFlags      Flags,
    42                                         DNSServiceErrorType  Errorcode,
    48                                         DNSServiceFlags      Flags,
    49                                         uint32_t             InterfaceIndex,
    50                                         DNSServiceErrorType  ErrorCode,
    56                                         DNSServiceFlags      Flags,
    57                                         uint32_t             InterfaceIndex,
    58                                         DNSServiceErrorType  ErrorCode,
    60                                         const char          *HostTarget,
    63                                         const unsigned char *TxtRecord,
    75   : m_serviceType(Service),
    80     m_domain(QByteArray()),
    83     m_ipAddresses(QList<QHostAddress>()),
    87     m_socketNotifier(nullptr)
    90     LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Invalid TorcBonjourService object"));
   146                    const QByteArray &Name, 
const QByteArray &Type,
   147                    const QByteArray &Domain, uint32_t InterfaceIndex)
   169     if (FileDescriptor != -1 && Object)
   171         m_fd = FileDescriptor;
   172         m_socketNotifier = 
new QSocketNotifier(FileDescriptor, QSocketNotifier::Read, Object);
   196         LOG(VB_NETWORK, LOG_WARNING, QStringLiteral(
"Host lookup for '%1' is not finished - cancelling").arg(
m_host.constData()));
   206             LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Cancelling browse for '%1'")
   207                 .arg(
m_type.constData()));
   211             LOG(VB_GENERAL, LOG_INFO,
   212                 QStringLiteral(
"De-registering service '%1' on '%2'")
   217             LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Cancelling resolve for '%1'")
   218                 .arg(
m_type.constData()));
   234                      DNSServiceErrorType Errorcode,
   235                      const char *Name,   
const char *Type,
   236                      const char *Domain, 
void *Object)
   243     if (kDNSServiceErr_NoError != Errorcode)
   245         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Callback Error: %1").arg(Errorcode));
   249         LOG(VB_GENERAL, LOG_INFO,
   250             QStringLiteral(
"Service registration complete: name '%1' type '%2'")
   251             .arg(QString::fromUtf8(Name), QString::fromUtf8(Type)));
   255         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"BonjourRegisterCallback for unknown object."));
   263                            uint32_t InterfaceIndex,
   264                            DNSServiceErrorType ErrorCode, 
const char *Name,
   265                            const char *Type, 
const char *Domain, 
void *Object)
   270         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Received browse callback for unknown object"));
   274     if (ErrorCode != kDNSServiceErr_NoError)
   276         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Browse callback error"));
   281     if (Flags & kDNSServiceFlagsAdd)
   286     if (!(Flags & kDNSServiceFlagsMoreComing))
   296                             uint32_t InterfaceIndex, DNSServiceErrorType ErrorCode,
   297                             const char *Fullname, 
const char *HostTarget,
   298                             uint16_t Port, uint16_t TxtLen,
   299                             const unsigned char *TxtRecord, 
void *Object)
   302     (void) InterfaceIndex;
   307         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Received resolve callback for unknown object"));
   311     bonjour->
Resolve(Ref, ErrorCode, Fullname, HostTarget, Port, TxtLen, TxtRecord);
   351         QMetaObject::invokeMethod(gBonjour, 
"SuspendPriv", Qt::AutoConnection, Q_ARG(
bool, Suspend));
   371     QMap<QByteArray,QByteArray>::const_iterator it = Map.constBegin();
   372     for ( ; it != Map.constEnd(); ++it)
   374         QByteArray record(1, it.key().size() + it.value().size() + 1);
   375         record.append(it.key() + 
"=" + it.value());
   376         result.append(record);
   387     QMap<QByteArray,QByteArray> result;
   390     while (position < TxtRecord.size())
   392         int size = TxtRecord[position++];
   393         QList<QByteArray> records = TxtRecord.mid(position, size).split(
'=');
   396         if (records.size() == 2)
   397             result.insert(records[0], records[1]);
   406     m_serviceLock(QMutex::Recursive),
   407     m_services(QMap<quint32,TorcBonjourService>()),
   408     m_discoveredLock(QMutex::Recursive),
   409     m_discoveredServices(QMap<quint32,TorcBonjourService>())
   412     qputenv(
"AVAHI_COMPAT_NOWARN", 
"1");
   423     LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Closing Bonjour"));
   427         QMutexLocker locker(&m_serviceLock);
   436         QMutexLocker locker(&m_discoveredLock);
   439         m_discoveredServices.clear();
   464                               const QMap<QByteArray, QByteArray> &TxtRecords, quint32 Reference)
   466     QByteArray txt = MapToTxtRecord(TxtRecords);
   467     return Register(Port, Type, Name, txt, Reference);
   470 quint32 
TorcBonjour::Register(quint16 Port, 
const QByteArray &Type, 
const QByteArray &Name, 
const QByteArray &Txt, quint32 Reference)
   474         QMutexLocker locker(&m_serviceLock);
   479         quint32 reference = Reference;
   481         while (!reference || m_services.contains(reference))
   483         m_services.insert(reference, service);
   485         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Bonjour service registration deferred until Bonjour resumed"));
   489     DNSServiceRef dnssref = 
nullptr;
   490     DNSServiceErrorType result = kDNSServiceErr_NameConflict;
   494     while (tries < 20 && kDNSServiceErr_NameConflict == result)
   496         QByteArray name(Name);
   498             name.append(QStringLiteral(
" [%1]").arg(tries + 1));
   499         result = DNSServiceRegister(&dnssref, 0,
   500                                     0, (
const char*)name.constData(),
   501                                     (
const char*)Type.constData(),
   502                                     nullptr, 
nullptr, htons(Port), Txt.size(), (
void*)Txt.constData(),
   507     if (kDNSServiceErr_NoError != result)
   509         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Register error: %1").arg(result));
   511             DNSServiceRefDeallocate(dnssref);
   515         QMutexLocker locker(&m_serviceLock);
   516         quint32 reference = Reference;
   517         while (!reference || m_services.contains(reference))
   522         m_services.insert(reference, service);
   523         m_services[reference].SetFileDescriptor(DNSServiceRefSockFD(dnssref), 
this);
   527     LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to register service."));
   545     static QByteArray dummy(
"browser");
   549         QMutexLocker locker(&m_serviceLock);
   552         quint32 reference = Reference;
   553         while (!reference || m_services.contains(reference))
   555         m_services.insert(reference, service);
   557         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Bonjour browse request deferred until Bonjour resumed"));
   561     DNSServiceRef dnssref = 
nullptr;
   562     DNSServiceErrorType result = DNSServiceBrowse(&dnssref, 0,
   563                                                   kDNSServiceInterfaceIndexAny,
   565     if (kDNSServiceErr_NoError != result)
   567         LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Browse error: %1").arg(result));
   569             DNSServiceRefDeallocate(dnssref);
   573         QMutexLocker locker(&m_serviceLock);
   574         quint32 reference = Reference;
   575         while (!reference || m_services.contains(reference))
   578         m_services.insert(reference, service);
   579         m_services[reference].SetFileDescriptor(DNSServiceRefSockFD(dnssref), 
this);
   580         LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Browsing for '%1'").arg(Type.constData()));
   584     LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to browse for '%1'").arg(Type.constData()));
   603         QMutexLocker locker(&m_serviceLock);
   604         if (m_services.contains(Reference))
   606             type = m_services[Reference].m_type;
   607             m_services[Reference].Deregister();
   608             m_services.remove(Reference);
   614         LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Trying to de-register an unknown service."));
   620         QMutexLocker locker(&m_discoveredLock);
   621         QMutableMapIterator<quint32,TorcBonjourService> it(m_discoveredServices);
   625             if (it.value().m_type == type)
   627                 it.value().Deregister();
   644         QMutexLocker lock(&m_serviceLock);
   645         QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
   646         for ( ; it != m_services.constEnd(); ++it)
   648             if ((*it).m_fd == Socket)
   650                 DNSServiceErrorType res = DNSServiceProcessResult((*it).m_dnssRef);
   651                 if (kDNSServiceErr_NoError != res)
   652                     LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Read Error: %1").arg(res));
   660         QMutexLocker lock(&m_discoveredLock);
   661         QMap<quint32,TorcBonjourService>::const_iterator it = m_discoveredServices.constBegin();
   662         for ( ; it != m_discoveredServices.constEnd(); ++it)
   664             if ((*it).m_fd == Socket)
   666                 DNSServiceErrorType res = DNSServiceProcessResult((*it).m_dnssRef);
   667                 if (kDNSServiceErr_NoError != res)
   668                     LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Read Error: %1").arg(res));
   674     LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Read request on unknown socket"));
   686 void TorcBonjour::SuspendPriv(
bool Suspend)
   692             LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour already suspended"));
   696         LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Suspending bonjour activities"));
   701             QMutexLocker locker(&m_serviceLock);
   704             QMap<quint32,TorcBonjourService> services;
   705             QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
   706             for (; it != m_services.constEnd(); ++it)
   709                 service.
m_txt  = (*it).m_txt;
   710                 service.
m_port = (*it).m_port;
   711                 services.insert(it.key(), service);
   716             m_services = services;
   723             LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Cannot resume - not suspended"));
   727         LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Resuming bonjour activities"));
   730             QMutexLocker locker(&m_serviceLock);
   734             QMap<quint32,TorcBonjourService> services = m_services;
   737             QMap<quint32,TorcBonjourService>::const_iterator it = services.constBegin();
   738             for (; it != services.constEnd(); ++it)
   741                     (void)Register((*it).m_port, (*it).m_type, (*it).m_name, (*it).m_txt, it.key());
   743                     (
void)
Browse((*it).m_type, it.key());
   759         QMutexLocker locker(&m_serviceLock);
   761         QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
   762         for ( ; it != m_services.constEnd(); ++it)
   764             if ((*it).m_dnssRef == Reference)
   773             LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Browser result for unknown browser"));
   780         QMutexLocker locker(&m_discoveredLock);
   781         QMap<quint32,TorcBonjourService>::const_iterator it = m_discoveredServices.constBegin();
   782         for( ; it != m_discoveredServices.constEnd(); ++it)
   784             if ((*it).m_name == Service.
m_name &&
   785                 (*it).m_type == Service.
m_type &&
   786                 (*it).m_domain == Service.
m_domain &&
   789                 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Service '%1' already discovered - ignoring")
   790                     .arg(Service.
m_name.constData()));
   796         DNSServiceRef reference = 
nullptr;
   797         DNSServiceErrorType error =
   802         if (error != kDNSServiceErr_NoError)
   804             LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Service resolution call failed"));
   806                 DNSServiceRefDeallocate(reference);
   813             while (m_discoveredServices.contains(ref))
   816             m_discoveredServices.insert(ref, service);
   817             m_discoveredServices[ref].SetFileDescriptor(DNSServiceRefSockFD(reference), 
this);
   818             LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Resolving '%1'").arg(service.
m_name.constData()));
   831         QMutexLocker locker(&m_serviceLock);
   833         QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
   834         for ( ; it != m_services.constEnd(); ++it)
   836             if ((*it).m_dnssRef == Reference)
   845             LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Browser result for unknown browser"));
   852         QMutexLocker locker(&m_discoveredLock);
   853         QMutableMapIterator<quint32,TorcBonjourService> it(m_discoveredServices);
   857             if (it.value().m_type == Service.
m_type &&
   858                 it.value().m_name == Service.
m_name &&
   859                 it.value().m_domain == Service.
m_domain &&
   870                 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Service '%1' on '%2' went away")
   871                     .arg(it.value().m_type.constData(),
   872                          it.value().m_host.isEmpty() ? it.value().m_name.constData() : it.value().m_host.constData()));
   873                 it.value().Deregister();
   890              const char *HostTarget, uint16_t Port, uint16_t TxtLen,
   891              const unsigned char *TxtRecord)
   899         QMutexLocker locker(&m_discoveredLock);
   904                 if (ErrorType != kDNSServiceErr_NoError)
   906                     LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to resolve '%1' (Error %2)").arg(service.
m_name.constData()).arg(ErrorType));
   920                 uint16_t port = ntohs(Port);
   921                 service.
m_host = HostTarget;
   923                 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"%1 (%2) resolved to %3:%4")
   924                     .arg(service.
m_name.constData(), service.
m_type.constData(), HostTarget).arg(port));
   925                 QString name(HostTarget);
   926                 service.
m_lookupID = QHostInfo::lookupHost(name, 
this, SLOT(HostLookup(QHostInfo)));
   927                 service.
m_txt      = QByteArray((
const char *)TxtRecord, TxtLen);
   940 void TorcBonjour::HostLookup(
const QHostInfo &HostInfo)
   943     QMutexLocker locker(&m_discoveredLock);
   946         if (service.
m_lookupID == HostInfo.lookupId())
   951             if (HostInfo.error() != QHostInfo::NoError)
   953                 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Lookup failed for '%1' with error '%2'").arg(HostInfo.hostName(), HostInfo.errorString()));
   958             LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Service '%1' on '%2:%3' resolved to %4 address(es) on interface %5")
   959                 .arg(service.
m_type.constData(), service.
m_host.constData())
   964             QStringList addresses;
   968                 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Address: %1").arg(address.toString()));
   969                 addresses << address.toString();
   979             if (!addresses.isEmpty())
   986                 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"No valid addresses resolved for Service '%1' on '%2:%3'")
   987                     .arg(service.
m_type.constData(), service.
m_host.constData())
 
QList< QHostAddress > m_ipAddresses
Address resolution for a discovered service. 
uint32_t m_interfaceIndex
TorcLocalContext * gLocalContext
void Deregister(quint32 Reference)
Cancel a Bonjour service registration or browse request. 
static QByteArray MapToTxtRecord(const QMap< QByteArray, QByteArray > &Map)
Serialises a QMap into a Bonjour Txt record format. 
void socketReadyRead(int Socket)
Read from a socket. 
void RemoveBrowseResult(DNSServiceRef Reference, const TorcBonjourService &Service)
Handle removed services. 
QSocketNotifier * m_socketNotifier
quint32 Browse(const QByteArray &Type, quint32 Reference=0)
Search for a service advertised via Bonjour. 
bool IsResolved(void)
Returns true when the service has been fully resolved to an IP address and port. 
QMutex * gBonjourLock
Lock around access to gBonjour. 
#define TORC_BONJOUR_ADDRESSES
#define TORC_BONJOUR_NAME
void Deregister(void)
Release all resources associated with this service. 
TorcBonjour * gBonjour
Global TorcBonjour singleton. 
void AddBrowseResult(DNSServiceRef Reference, const TorcBonjourService &Service)
Handle newly discovered service. 
static QMap< QByteArray, QByteArray > TxtRecordToMap(const QByteArray &TxtRecord)
Extracts a QMap from a properly formatted Bonjour Txt record. 
static void TearDown(void)
Destroys the global TorcBonjour singleton. 
quint32 Register(quint16 Port, const QByteArray &Type, const QByteArray &Name, const QMap< QByteArray, QByteArray > &TxtRecords, quint32 Reference=0)
void Resolve(DNSServiceRef Reference, DNSServiceErrorType ErrorType, const char *Fullname, const char *HostTarget, uint16_t Port, uint16_t TxtLen, const unsigned char *TxtRecord)
Handle name resolution respones from Bonjour. 
#define TORC_BONJOUR_HOST
Wrapper around a DNS service reference, either advertised or discovered. 
static TorcBonjour * Instance(void)
Returns the global TorcBonjour singleton. 
A general purpose event object. 
A service being advertised by this application. 
#define LOG(_MASK_, _LEVEL_, _STRING_)
void AddObserver(QObject *Observer)
brief Register the given object to receive events. 
void Notify(const TorcEvent &Event)
Brief Send the given event to each registered listener/observer. 
#define TORC_BONJOUR_TYPE
#define TORC_BONJOUR_PORT
void DNSSD_API BonjourBrowseCallback(DNSServiceRef Ref, DNSServiceFlags Flags, uint32_t InterfaceIndex, DNSServiceErrorType ErrorCode, const char *Name, const char *Type, const char *Domain, void *Object)
TorcBonjourService & operator=(const TorcBonjourService &Other)
Advertises and searches for services via Bonjour (aka Zero Configuration Networking/Avahi) ...
void RemoveObserver(QObject *Observer)
brief Deregister the given object. 
An external service which we are actively trying to discover. 
void DNSSD_API BonjourRegisterCallback(DNSServiceRef Ref, DNSServiceFlags Flags, DNSServiceErrorType Errorcode, const char *Name, const char *Type, const char *Domain, void *Object)
void DNSSD_API BonjourResolveCallback(DNSServiceRef Ref, DNSServiceFlags Flags, uint32_t InterfaceIndex, DNSServiceErrorType ErrorCode, const char *Fullname, const char *HostTarget, uint16_t Port, uint16_t TxtLen, const unsigned char *TxtRecord, void *Object)
void SetFileDescriptor(int FileDescriptor, TorcBonjour *Object)
Sets the file descriptor and creates a QSocketNotifier to listen for socket events. 
ServiceType m_serviceType
static void Suspend(bool Suspend)
Suspends all Bonjour activities.