Torc  0.1
torcbonjour.cpp
Go to the documentation of this file.
1 /* Class TorcBonjour
2 *
3 * Copyright (C) Mark Kendall 2012-18
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 
20 // Qt
21 #include <QCoreApplication>
22 #include <QtEndian>
23 #include <QMap>
24 
25 // Torc
26 #include "torclogging.h"
27 #include "torcevent.h"
28 #include "torclocalcontext.h"
29 #include "torcadminthread.h"
30 #include "torcnetwork.h"
31 #include "torcbonjour.h"
32 
33 // Std
34 #include <stdlib.h>
35 #include <arpa/inet.h>
36 
37 TorcBonjour* gBonjour = nullptr;
38 QMutex* gBonjourLock = new QMutex(QMutex::Recursive);
39 
40 void DNSSD_API BonjourRegisterCallback (DNSServiceRef Ref,
41  DNSServiceFlags Flags,
42  DNSServiceErrorType Errorcode,
43  const char *Name,
44  const char *Type,
45  const char *Domain,
46  void *Object);
47 void DNSSD_API BonjourBrowseCallback (DNSServiceRef Ref,
48  DNSServiceFlags Flags,
49  uint32_t InterfaceIndex,
50  DNSServiceErrorType ErrorCode,
51  const char *Name,
52  const char *Type,
53  const char *Domain,
54  void *Object);
55 void DNSSD_API BonjourResolveCallback (DNSServiceRef Ref,
56  DNSServiceFlags Flags,
57  uint32_t InterfaceIndex,
58  DNSServiceErrorType ErrorCode,
59  const char *Fullname,
60  const char *HostTarget,
61  uint16_t Port,
62  uint16_t TxtLen,
63  const unsigned char *TxtRecord,
64  void *Object);
65 
75  : m_serviceType(Service),
76  m_dnssRef(nullptr),
77  m_name(QByteArray()),
78  m_type(QByteArray()),
79  m_txt(QByteArray()),
80  m_domain(QByteArray()),
81  m_interfaceIndex(0),
82  m_host(QByteArray()),
83  m_ipAddresses(QList<QHostAddress>()),
84  m_port(0),
85  m_lookupID(-1),
86  m_fd(-1),
87  m_socketNotifier(nullptr)
88 {
89  // the default constructor only exists to keep QMap happy - it should never be used
90  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Invalid TorcBonjourService object"));
91 }
92 
95  m_dnssRef(Other.m_dnssRef),
96  m_name(Other.m_name),
97  m_type(Other.m_type),
98  m_txt(Other.m_txt),
99  m_domain(Other.m_domain),
101  m_host(Other.m_host),
103  m_port(0), // NB
104  m_lookupID(-1), // NB
105  m_fd(-1), // NB
106  m_socketNotifier(nullptr) // NB
107 {
108 }
109 
111 {
113  m_dnssRef = Other.m_dnssRef;
114  m_name = Other.m_name;
115  m_type = Other.m_type;
116  m_txt = Other.m_txt;
117  m_domain = Other.m_domain;
119  m_host = Other.m_host;
121  m_port = 0; // NB
122  m_lookupID = -1; // NB
123  m_fd = -1; // NB
124  m_socketNotifier = nullptr; // NB
125  return *this;
126 }
127 
128 TorcBonjourService::TorcBonjourService(ServiceType BonjourType, DNSServiceRef DNSSRef, const QByteArray &Name, const QByteArray &Type)
129  : m_serviceType(BonjourType),
130  m_dnssRef(DNSSRef),
131  m_name(Name),
132  m_type(Type),
133  m_txt(QByteArray()),
134  m_domain(QByteArray()),
135  m_interfaceIndex(0),
136  m_host(QByteArray()),
137  m_ipAddresses(QList<QHostAddress>()),
138  m_port(0),
139  m_lookupID(-1),
140  m_fd(-1),
141  m_socketNotifier(nullptr)
142 {
143 }
144 
146  const QByteArray &Name, const QByteArray &Type,
147  const QByteArray &Domain, uint32_t InterfaceIndex)
148  : m_serviceType(BonjourType),
149  m_dnssRef(nullptr),
150  m_name(Name),
151  m_type(Type),
152  m_txt(QByteArray()),
153  m_domain(Domain),
154  m_interfaceIndex(InterfaceIndex),
155  m_host(QByteArray()),
156  m_ipAddresses(QList<QHostAddress>()),
157  m_port(0),
158  m_lookupID(-1),
159  m_fd(-1),
160  m_socketNotifier(nullptr)
161 {
162 }
163 
167 void TorcBonjourService::SetFileDescriptor(int FileDescriptor, TorcBonjour *Object)
168 {
169  if (FileDescriptor != -1 && Object)
170  {
171  m_fd = FileDescriptor;
172  m_socketNotifier = new QSocketNotifier(FileDescriptor, QSocketNotifier::Read, Object);
173  QObject::connect(m_socketNotifier, &QSocketNotifier::activated, Object, &TorcBonjour::socketReadyRead);
174  m_socketNotifier->setEnabled(true);
175  }
176 }
177 
182 {
183  return m_port && !m_ipAddresses.isEmpty();
184 }
185 
190 {
191  if (m_socketNotifier)
192  m_socketNotifier->setEnabled(false);
193 
194  if (m_lookupID != -1)
195  {
196  LOG(VB_NETWORK, LOG_WARNING, QStringLiteral("Host lookup for '%1' is not finished - cancelling").arg(m_host.constData()));
197  QHostInfo::abortHostLookup(m_lookupID);
198  m_lookupID = -1;
199  }
200 
201  if (m_dnssRef)
202  {
203  // Unregister
204  if (m_serviceType == Browse)
205  {
206  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Cancelling browse for '%1'")
207  .arg(m_type.constData()));
208  }
209  else if (m_serviceType == Service)
210  {
211  LOG(VB_GENERAL, LOG_INFO,
212  QStringLiteral("De-registering service '%1' on '%2'")
213  .arg(m_type.constData(), m_name.constData()));
214  }
215  else if (m_serviceType == Resolve)
216  {
217  LOG(VB_NETWORK, LOG_INFO, QStringLiteral("Cancelling resolve for '%1'")
218  .arg(m_type.constData()));
219  }
220 
221  DNSServiceRefDeallocate(m_dnssRef);
222  m_dnssRef = nullptr;
223  }
224 
225  if (m_socketNotifier)
226  m_socketNotifier->deleteLater();
227  m_socketNotifier = nullptr;
228 }
229 
233 void BonjourRegisterCallback(DNSServiceRef Ref, DNSServiceFlags Flags,
234  DNSServiceErrorType Errorcode,
235  const char *Name, const char *Type,
236  const char *Domain, void *Object)
237 {
238  (void)Ref;
239  (void)Flags;
240  (void)Domain;
241 
242  TorcBonjour *bonjour = static_cast<TorcBonjour*>(Object);
243  if (kDNSServiceErr_NoError != Errorcode)
244  {
245  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Callback Error: %1").arg(Errorcode));
246  }
247  else if (bonjour)
248  {
249  LOG(VB_GENERAL, LOG_INFO,
250  QStringLiteral("Service registration complete: name '%1' type '%2'")
251  .arg(QString::fromUtf8(Name), QString::fromUtf8(Type)));
252  }
253  else
254  {
255  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("BonjourRegisterCallback for unknown object."));
256  }
257 }
258 
262 void BonjourBrowseCallback(DNSServiceRef Ref, DNSServiceFlags Flags,
263  uint32_t InterfaceIndex,
264  DNSServiceErrorType ErrorCode, const char *Name,
265  const char *Type, const char *Domain, void *Object)
266 {
267  TorcBonjour *bonjour = static_cast<TorcBonjour*>(Object);
268  if (!bonjour)
269  {
270  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Received browse callback for unknown object"));
271  return;
272  }
273 
274  if (ErrorCode != kDNSServiceErr_NoError)
275  {
276  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Browse callback error"));
277  return;
278  }
279 
280  TorcBonjourService service(TorcBonjourService::Resolve, Name, Type, Domain, InterfaceIndex);
281  if (Flags & kDNSServiceFlagsAdd)
282  bonjour->AddBrowseResult(Ref, service);
283  else
284  bonjour->RemoveBrowseResult(Ref, service);
285 
286  if (!(Flags & kDNSServiceFlagsMoreComing))
287  {
288  // no idea
289  }
290 }
291 
295 void BonjourResolveCallback(DNSServiceRef Ref, DNSServiceFlags Flags,
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)
300 {
301  (void)Flags;
302  (void) InterfaceIndex;
303 
304  TorcBonjour *bonjour = static_cast<TorcBonjour*>(Object);
305  if (!bonjour)
306  {
307  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Received resolve callback for unknown object"));
308  return;
309  }
310 
311  bonjour->Resolve(Ref, ErrorCode, Fullname, HostTarget, Port, TxtLen, TxtRecord);
312 }
313 
335 {
336  QMutexLocker locker(gBonjourLock);
337  if (gBonjour)
338  return gBonjour;
339 
340  gBonjour = new TorcBonjour();
341  return gBonjour;
342 }
343 
347 void TorcBonjour::Suspend(bool Suspend)
348 {
349  QMutexLocker locker(gBonjourLock);
350  if (gBonjour)
351  QMetaObject::invokeMethod(gBonjour, "SuspendPriv", Qt::AutoConnection, Q_ARG(bool, Suspend));
352 }
353 
357 void TorcBonjour::TearDown(void)
358 {
359  QMutexLocker locker(gBonjourLock);
360  delete gBonjour;
361  gBonjour = nullptr;
362 }
363 
367 QByteArray TorcBonjour::MapToTxtRecord(const QMap<QByteArray, QByteArray> &Map)
368 {
369  QByteArray result;
370 
371  QMap<QByteArray,QByteArray>::const_iterator it = Map.constBegin();
372  for ( ; it != Map.constEnd(); ++it)
373  {
374  QByteArray record(1, it.key().size() + it.value().size() + 1);
375  record.append(it.key() + "=" + it.value());
376  result.append(record);
377  }
378 
379  return result;
380 }
381 
385 QMap<QByteArray,QByteArray> TorcBonjour::TxtRecordToMap(const QByteArray &TxtRecord)
386 {
387  QMap<QByteArray,QByteArray> result;
388 
389  int position = 0;
390  while (position < TxtRecord.size())
391  {
392  int size = TxtRecord[position++];
393  QList<QByteArray> records = TxtRecord.mid(position, size).split('=');
394  position += size;
395 
396  if (records.size() == 2)
397  result.insert(records[0], records[1]);
398  }
399 
400  return result;
401 }
402 
404  : QObject(nullptr),
405  m_suspended(false),
406  m_serviceLock(QMutex::Recursive),
407  m_services(QMap<quint32,TorcBonjourService>()),
408  m_discoveredLock(QMutex::Recursive),
409  m_discoveredServices(QMap<quint32,TorcBonjourService>())
410 {
411  // the Avahi compatability layer on *nix will spam us with warnings
412  qputenv("AVAHI_COMPAT_NOWARN", "1");
413 
414  // listen for network enabled/disabled events
415  gLocalContext->AddObserver(this);
416 }
417 
419 {
420  // stop listening for network events
422 
423  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Closing Bonjour"));
424 
425  // deregister any outstanding services (should be empty)
426  {
427  QMutexLocker locker(&m_serviceLock);
428  if (!m_suspended)
429  foreach(TorcBonjourService service, m_services)
430  service.Deregister();
431  m_services.clear();
432  }
433 
434  // deallocate resolve queries
435  {
436  QMutexLocker locker(&m_discoveredLock);
437  foreach(TorcBonjourService service, m_discoveredServices)
438  service.Deregister();
439  m_discoveredServices.clear();
440  }
441 }
442 
463 quint32 TorcBonjour::Register(quint16 Port, const QByteArray &Type, const QByteArray &Name,
464  const QMap<QByteArray, QByteArray> &TxtRecords, quint32 Reference)
465 {
466  QByteArray txt = MapToTxtRecord(TxtRecords);
467  return Register(Port, Type, Name, txt, Reference);
468 }
469 
470 quint32 TorcBonjour::Register(quint16 Port, const QByteArray &Type, const QByteArray &Name, const QByteArray &Txt, quint32 Reference)
471 {
472  if (m_suspended)
473  {
474  QMutexLocker locker(&m_serviceLock);
475 
476  TorcBonjourService service(TorcBonjourService::Service, nullptr, Name, Type);
477  service.m_txt = Txt;
478  service.m_port = Port;
479  quint32 reference = Reference;
480  // find a unique reference
481  while (!reference || m_services.contains(reference))
482  reference++;
483  m_services.insert(reference, service);
484 
485  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Bonjour service registration deferred until Bonjour resumed"));
486  return reference;
487  }
488 
489  DNSServiceRef dnssref = nullptr;
490  DNSServiceErrorType result = kDNSServiceErr_NameConflict;
491  int tries = 0;
492 
493  // the avahi compatability layer doesn't appear to be automatically renaming as it should
494  while (tries < 20 && kDNSServiceErr_NameConflict == result)
495  {
496  QByteArray name(Name);
497  if (tries > 0)
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(),
504  tries++;
505  }
506 
507  if (kDNSServiceErr_NoError != result)
508  {
509  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Register error: %1").arg(result));
510  if (dnssref)
511  DNSServiceRefDeallocate(dnssref);
512  }
513  else
514  {
515  QMutexLocker locker(&m_serviceLock);
516  quint32 reference = Reference;
517  while (!reference || m_services.contains(reference))
518  reference++;
519  TorcBonjourService service(TorcBonjourService::Service, dnssref, Name, Type);
520  service.m_txt = Txt;
521  service.m_port = Port;
522  m_services.insert(reference, service);
523  m_services[reference].SetFileDescriptor(DNSServiceRefSockFD(dnssref), this);
524  return reference;
525  }
526 
527  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to register service."));
528  return 0;
529 }
530 
543 quint32 TorcBonjour::Browse(const QByteArray &Type, quint32 Reference /*=0*/)
544 {
545  static QByteArray dummy("browser");
546 
547  if (m_suspended)
548  {
549  QMutexLocker locker(&m_serviceLock);
550 
551  TorcBonjourService service(TorcBonjourService::Browse, nullptr, dummy, Type);
552  quint32 reference = Reference;
553  while (!reference || m_services.contains(reference))
554  reference++;
555  m_services.insert(reference, service);
556 
557  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Bonjour browse request deferred until Bonjour resumed"));
558  return reference;
559  }
560 
561  DNSServiceRef dnssref = nullptr;
562  DNSServiceErrorType result = DNSServiceBrowse(&dnssref, 0,
563  kDNSServiceInterfaceIndexAny,
564  Type, nullptr, BonjourBrowseCallback, this);
565  if (kDNSServiceErr_NoError != result)
566  {
567  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Browse error: %1").arg(result));
568  if (dnssref)
569  DNSServiceRefDeallocate(dnssref);
570  }
571  else
572  {
573  QMutexLocker locker(&m_serviceLock);
574  quint32 reference = Reference;
575  while (!reference || m_services.contains(reference))
576  reference++;
577  TorcBonjourService service(TorcBonjourService::Browse, dnssref, dummy, Type);
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()));
581  return reference;
582  }
583 
584  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to browse for '%1'").arg(Type.constData()));
585  return 0;
586 }
587 
595 void TorcBonjour::Deregister(quint32 Reference)
596 {
597  if (!Reference)
598  return;
599 
600  QByteArray type;
601 
602  {
603  QMutexLocker locker(&m_serviceLock);
604  if (m_services.contains(Reference))
605  {
606  type = m_services[Reference].m_type;
607  m_services[Reference].Deregister();
608  m_services.remove(Reference);
609  }
610  }
611 
612  if (type.isEmpty())
613  {
614  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Trying to de-register an unknown service."));
615  return;
616  }
617 
618  // Remove any resolve requests associated with this type
619  {
620  QMutexLocker locker(&m_discoveredLock);
621  QMutableMapIterator<quint32,TorcBonjourService> it(m_discoveredServices);
622  while (it.hasNext())
623  {
624  it.next();
625  if (it.value().m_type == type)
626  {
627  it.value().Deregister();
628  it.remove();
629  }
630  }
631  }
632 }
633 
640 void TorcBonjour::socketReadyRead(int Socket)
641 {
642  {
643  // match Socket to an announced service
644  QMutexLocker lock(&m_serviceLock);
645  QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
646  for ( ; it != m_services.constEnd(); ++it)
647  {
648  if ((*it).m_fd == Socket)
649  {
650  DNSServiceErrorType res = DNSServiceProcessResult((*it).m_dnssRef);
651  if (kDNSServiceErr_NoError != res)
652  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Read Error: %1").arg(res));
653  return;
654  }
655  }
656  }
657 
658  {
659  // match Socket to a discovered service
660  QMutexLocker lock(&m_discoveredLock);
661  QMap<quint32,TorcBonjourService>::const_iterator it = m_discoveredServices.constBegin();
662  for ( ; it != m_discoveredServices.constEnd(); ++it)
663  {
664  if ((*it).m_fd == Socket)
665  {
666  DNSServiceErrorType res = DNSServiceProcessResult((*it).m_dnssRef);
667  if (kDNSServiceErr_NoError != res)
668  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Read Error: %1").arg(res));
669  return;
670  }
671  }
672  }
673 
674  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Read request on unknown socket"));
675 }
676 
686 void TorcBonjour::SuspendPriv(bool Suspend)
687 {
688  if (Suspend)
689  {
690  if (m_suspended)
691  {
692  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Bonjour already suspended"));
693  return;
694  }
695 
696  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Suspending bonjour activities"));
697 
698  m_suspended = true;
699 
700  {
701  QMutexLocker locker(&m_serviceLock);
702 
703  // close the services but retain the necessary details
704  QMap<quint32,TorcBonjourService> services;
705  QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
706  for (; it != m_services.constEnd(); ++it)
707  {
708  TorcBonjourService service((*it).m_serviceType, nullptr, (*it).m_name, (*it).m_type);
709  service.m_txt = (*it).m_txt;
710  service.m_port = (*it).m_port;
711  services.insert(it.key(), service);
712  }
713  foreach(TorcBonjourService service, m_services)
714  service.Deregister();
715  m_services.clear();
716  m_services = services;
717  }
718  }
719  else
720  {
721  if (!m_suspended)
722  {
723  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Cannot resume - not suspended"));
724  return;
725  }
726 
727  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Resuming bonjour activities"));
728 
729  {
730  QMutexLocker locker(&m_serviceLock);
731 
732  m_suspended = false;
733 
734  QMap<quint32,TorcBonjourService> services = m_services;
735  m_services.clear();
736 
737  QMap<quint32,TorcBonjourService>::const_iterator it = services.constBegin();
738  for (; it != services.constEnd(); ++it)
739  {
740  if ((*it).m_serviceType == TorcBonjourService::Service)
741  (void)Register((*it).m_port, (*it).m_type, (*it).m_name, (*it).m_txt, it.key());
742  else
743  (void)Browse((*it).m_type, it.key());
744  }
745  }
746  }
747 }
748 
755 void TorcBonjour::AddBrowseResult(DNSServiceRef Reference, const TorcBonjourService &Service)
756 {
757  {
758  // validate against known browsers
759  QMutexLocker locker(&m_serviceLock);
760  bool found = false;
761  QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
762  for ( ; it != m_services.constEnd(); ++it)
763  {
764  if ((*it).m_dnssRef == Reference)
765  {
766  found = true;
767  break;
768  }
769  }
770 
771  if (!found)
772  {
773  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Browser result for unknown browser"));
774  return;
775  }
776  }
777 
778  {
779  // have we already seen this service?
780  QMutexLocker locker(&m_discoveredLock);
781  QMap<quint32,TorcBonjourService>::const_iterator it = m_discoveredServices.constBegin();
782  for( ; it != m_discoveredServices.constEnd(); ++it)
783  {
784  if ((*it).m_name == Service.m_name &&
785  (*it).m_type == Service.m_type &&
786  (*it).m_domain == Service.m_domain &&
787  (*it).m_interfaceIndex == Service.m_interfaceIndex)
788  {
789  LOG(VB_NETWORK, LOG_INFO, QStringLiteral("Service '%1' already discovered - ignoring")
790  .arg(Service.m_name.constData()));
791  return;
792  }
793  }
794 
795  // kick off resolve
796  DNSServiceRef reference = nullptr;
797  DNSServiceErrorType error =
798  DNSServiceResolve(&reference, 0, Service.m_interfaceIndex,
799  Service.m_name.constData(), Service.m_type.constData(),
800  Service.m_domain.constData(), BonjourResolveCallback,
801  this);
802  if (error != kDNSServiceErr_NoError)
803  {
804  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Service resolution call failed"));
805  if (reference)
806  DNSServiceRefDeallocate(reference);
807  }
808  else
809  {
810  // add it to our list
811  TorcBonjourService service = Service;
812  quint32 ref = 1;
813  while (m_discoveredServices.contains(ref))
814  ref++;
815  service.m_dnssRef = reference;
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()));
819  }
820  }
821 }
822 
826 void TorcBonjour::RemoveBrowseResult(DNSServiceRef Reference,
828 {
829  {
830  // validate against known browsers
831  QMutexLocker locker(&m_serviceLock);
832  bool found = false;
833  QMap<quint32,TorcBonjourService>::const_iterator it = m_services.constBegin();
834  for ( ; it != m_services.constEnd(); ++it)
835  {
836  if ((*it).m_dnssRef == Reference)
837  {
838  found = true;
839  break;
840  }
841  }
842 
843  if (!found)
844  {
845  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Browser result for unknown browser"));
846  return;
847  }
848  }
849 
850  {
851  // validate against known services
852  QMutexLocker locker(&m_discoveredLock);
853  QMutableMapIterator<quint32,TorcBonjourService> it(m_discoveredServices);
854  while (it.hasNext())
855  {
856  it.next();
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 &&
860  it.value().m_interfaceIndex == Service.m_interfaceIndex)
861  {
862  QVariantMap data;
863  data.insert(TORC_BONJOUR_NAME, it.value().m_name.constData());
864  data.insert(TORC_BONJOUR_TYPE, it.value().m_type.constData());
865  data.insert(TORC_BONJOUR_TXT, it.value().m_txt);
866  data.insert(TORC_BONJOUR_HOST, it.value().m_host);
867  TorcEvent event(Torc::ServiceWentAway, data);
868  gLocalContext->Notify(event);
869 
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();
874  it.remove();
875  }
876  }
877  }
878 }
879 
889 void TorcBonjour::Resolve(DNSServiceRef Reference, DNSServiceErrorType ErrorType, const char *Fullname,
890  const char *HostTarget, uint16_t Port, uint16_t TxtLen,
891  const unsigned char *TxtRecord)
892 {
893  (void)Fullname;
894 
895  if (!Reference)
896  return;
897 
898  {
899  QMutexLocker locker(&m_discoveredLock);
900  foreach(TorcBonjourService service, m_discoveredServices)
901  {
902  if (service.m_dnssRef == Reference)
903  {
904  if (ErrorType != kDNSServiceErr_NoError)
905  {
906  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to resolve '%1' (Error %2)").arg(service.m_name.constData()).arg(ErrorType));
907  QVariantMap data;
908  data.insert(TORC_BONJOUR_NAME, service.m_name.constData());
909  data.insert(TORC_BONJOUR_TYPE, service.m_type.constData());
910  data.insert(TORC_BONJOUR_PORT, service.m_port);
911  data.insert(TORC_BONJOUR_TXT, service.m_txt);
912  data.insert(TORC_BONJOUR_HOST, service.m_host.constData());
913  // as per HostLookup below - resolve may have failed due to a network disruption and the service is no
914  // longer available. Signal removal - if it wasn't already known it won't matter.
915  TorcEvent event(Torc::ServiceWentAway, data);
916  gLocalContext->Notify(event);
917  return;
918  }
919 
920  uint16_t port = ntohs(Port);
921  service.m_host = HostTarget;
922  service.m_port = port;
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);
928  return;
929  }
930  }
931  }
932 }
933 
940 void TorcBonjour::HostLookup(const QHostInfo &HostInfo)
941 {
942  // search for the lookup id
943  QMutexLocker locker(&m_discoveredLock);
944  foreach(TorcBonjourService service, m_discoveredServices)
945  {
946  if (service.m_lookupID == HostInfo.lookupId())
947  {
948  service.m_lookupID = -1;
949 
950  // igore if errored
951  if (HostInfo.error() != QHostInfo::NoError)
952  {
953  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Lookup failed for '%1' with error '%2'").arg(HostInfo.hostName(), HostInfo.errorString()));
954  return;
955  }
956 
957  service.m_ipAddresses = HostInfo.addresses();
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())
960  .arg(service.m_port)
961  .arg(service.m_ipAddresses.size())
962  .arg(service.m_interfaceIndex));
963 
964  QStringList addresses;
965 
966  foreach (const QHostAddress &address, service.m_ipAddresses)
967  {
968  LOG(VB_NETWORK, LOG_INFO, QStringLiteral("Address: %1").arg(address.toString()));
969  addresses << address.toString();
970  }
971 
972  QVariantMap data;
973  data.insert(TORC_BONJOUR_NAME, service.m_name.constData());
974  data.insert(TORC_BONJOUR_TYPE, service.m_type.constData());
975  data.insert(TORC_BONJOUR_PORT, service.m_port);
976  data.insert(TORC_BONJOUR_ADDRESSES, addresses);
977  data.insert(TORC_BONJOUR_TXT, service.m_txt);
978  data.insert(TORC_BONJOUR_HOST, service.m_host.constData());
979  if (!addresses.isEmpty())
980  {
981  TorcEvent event(Torc::ServiceDiscovered, data);
982  gLocalContext->Notify(event);
983  }
984  else
985  {
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())
988  .arg(service.m_port));
989  // NB this is experimental!!!
990  TorcEvent event(Torc::ServiceWentAway, data);
991  gLocalContext->Notify(event);
992  }
993  }
994  }
995 }
QList< QHostAddress > m_ipAddresses
Definition: torcbonjour.h:58
Address resolution for a discovered service.
Definition: torcbonjour.h:31
uint32_t m_interfaceIndex
Definition: torcbonjour.h:56
TorcLocalContext * gLocalContext
DNSServiceRef m_dnssRef
Definition: torcbonjour.h:50
void Deregister(quint32 Reference)
Cancel a Bonjour service registration or browse request.
#define TORC_BONJOUR_TXT
Definition: torcbonjour.h:17
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
Definition: torcbonjour.h:62
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.
Definition: torcbonjour.cpp:38
QByteArray m_type
Definition: torcbonjour.h:53
#define TORC_BONJOUR_ADDRESSES
Definition: torcbonjour.h:16
#define TORC_BONJOUR_NAME
Definition: torcbonjour.h:18
QByteArray m_txt
Definition: torcbonjour.h:54
QByteArray m_name
Definition: torcbonjour.h:52
void Deregister(void)
Release all resources associated with this service.
TorcBonjour * gBonjour
Global TorcBonjour singleton.
Definition: torcbonjour.cpp:37
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
Definition: torcbonjour.h:15
QByteArray m_domain
Definition: torcbonjour.h:55
QByteArray m_host
Definition: torcbonjour.h:57
Wrapper around a DNS service reference, either advertised or discovered.
Definition: torcbonjour.h:24
static TorcBonjour * Instance(void)
Returns the global TorcBonjour singleton.
A general purpose event object.
Definition: torcevent.h:9
A service being advertised by this application.
Definition: torcbonjour.h:29
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
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
Definition: torcbonjour.h:19
#define TORC_BONJOUR_PORT
Definition: torcbonjour.h:20
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) ...
Definition: torcbonjour.h:65
void RemoveObserver(QObject *Observer)
brief Deregister the given object.
An external service which we are actively trying to discover.
Definition: torcbonjour.h:30
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
Definition: torcbonjour.h:48
static void Suspend(bool Suspend)
Suspends all Bonjour activities.