2015-11-21 07:22:36 -05:00
/**************************************************************************************************
---------------------------------------------------------------------------------------------------
Copyright (C) 2015 Jonathan Bagg
This file is part of QtZeroConf.
QtZeroConf is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QtZeroConf is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with QtZeroConf. If not, see <http://www.gnu.org/licenses/>.
---------------------------------------------------------------------------------------------------
2017-10-23 20:12:00 +02:00
Project name : QtZeroConf
File name : bonjour.cpp
Created : 20 July 2015
Author(s) : Jonathan Bagg
2015-11-21 07:22:36 -05:00
---------------------------------------------------------------------------------------------------
2017-10-23 20:12:00 +02:00
Wrapper for Apple's Bonjour library for use on Windows, MACs and iOS
2015-11-21 07:22:36 -05:00
---------------------------------------------------------------------------------------------------
**************************************************************************************************/
#include "qzeroconf.h"
#include "bonjour_p.h"
2019-04-04 18:48:14 -04:00
void Resolver :: resolverReady ()
{
DNSServiceErrorType err = DNSServiceProcessResult ( DNSresolverRef );
if ( err != kDNSServiceErr_NoError )
cleanUp ();
}
void Resolver :: addressReady ()
{
DNSServiceErrorType err = DNSServiceProcessResult ( DNSaddressRef );
if ( err != kDNSServiceErr_NoError )
cleanUp ();
}
void Resolver :: cleanUp ()
{
DNSServiceRefDeallocate ( DNSresolverRef );
DNSServiceRefDeallocate ( DNSaddressRef );
2020-07-08 18:59:09 -04:00
// the QSocketNotifiers resolverNotifier and addressNotifier get deleted when the QSharedPointer gets deleted along with the Resolver. No need to clear them here.
2019-04-04 18:48:14 -04:00
QString key = zcs -> name () + QString :: number ( zcs -> interfaceIndex ());
ref -> resolvers . remove ( key );
delete this ;
}
2015-11-21 07:22:36 -05:00
QZeroConfPrivate :: QZeroConfPrivate ( QZeroConf * parent )
{
pub = parent ;
}
void QZeroConfPrivate :: bsRead ()
{
DNSServiceErrorType err = DNSServiceProcessResult ( dnssRef );
if ( err != kDNSServiceErr_NoError ) {
cleanUp ( dnssRef );
emit pub -> error ( QZeroConf :: serviceRegistrationFailed );
}
}
void QZeroConfPrivate :: browserRead ()
{
DNSServiceErrorType err = DNSServiceProcessResult ( browser );
if ( err != kDNSServiceErr_NoError ) {
cleanUp ( browser );
emit pub -> error ( QZeroConf :: browserFailed );
}
}
2019-04-04 18:48:14 -04:00
void QZeroConfPrivate :: resolve ( QZeroConfService zcs )
2015-11-21 07:22:36 -05:00
{
DNSServiceErrorType err ;
2019-04-04 18:48:14 -04:00
Resolver * resolver = new Resolver ;
QString key = zcs -> name () + QString :: number ( zcs -> interfaceIndex ());
resolvers . insert ( key , resolver );
resolver -> ref = this ;
resolver -> zcs = zcs ;
2015-11-21 07:22:36 -05:00
2020-03-09 23:22:57 +01:00
err = DNSServiceResolve ( & resolver -> DNSresolverRef , kDNSServiceFlagsTimeout , zcs -> interfaceIndex (), zcs -> name (). toUtf8 (), zcs -> type (). toUtf8 (), zcs -> domain (). toUtf8 (), static_cast < DNSServiceResolveReply > ( resolverCallback ), resolver );
2015-11-21 07:22:36 -05:00
if ( err == kDNSServiceErr_NoError ) {
2019-04-04 18:48:14 -04:00
int sockfd = DNSServiceRefSockFD ( resolver -> DNSresolverRef );
2015-11-21 07:22:36 -05:00
if ( sockfd == - 1 ) {
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
}
else {
2020-07-08 18:59:09 -04:00
resolver -> resolverNotifier = QSharedPointer < QSocketNotifier >:: create ( sockfd , QSocketNotifier :: Read );
connect ( resolver -> resolverNotifier . data (), & QSocketNotifier :: activated , resolver , & Resolver :: resolverReady );
2015-11-21 07:22:36 -05:00
}
}
else {
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
}
}
void DNSSD_API QZeroConfPrivate :: registerCallback ( DNSServiceRef , DNSServiceFlags , DNSServiceErrorType errorCode , const char * , const char * , const char * , void * userdata )
{
QZeroConfPrivate * ref = static_cast < QZeroConfPrivate *> ( userdata );
if ( errorCode == kDNSServiceErr_NoError ) {
emit ref -> pub -> servicePublished ();
}
else {
ref -> cleanUp ( ref -> dnssRef );
emit ref -> pub -> error ( QZeroConf :: serviceRegistrationFailed );
}
}
void DNSSD_API QZeroConfPrivate :: browseCallback ( DNSServiceRef , DNSServiceFlags flags ,
quint32 interfaceIndex , DNSServiceErrorType err , const char * name ,
const char * type , const char * domain , void * userdata )
{
QString key ;
2017-10-23 20:12:00 +02:00
QZeroConfService zcs ;
2015-11-21 07:22:36 -05:00
QZeroConfPrivate * ref = static_cast < QZeroConfPrivate *> ( userdata );
//qDebug() << name;
if ( err == kDNSServiceErr_NoError ) {
key = name + QString :: number ( interfaceIndex );
if ( flags & kDNSServiceFlagsAdd ) {
if ( ! ref -> pub -> services . contains ( key )) {
2020-07-08 18:59:09 -04:00
zcs = QZeroConfService :: create ();
2019-03-05 21:19:32 -05:00
zcs -> m_name = name ;
zcs -> m_type = type ;
zcs -> m_domain = domain ;
zcs -> m_interfaceIndex = interfaceIndex ;
2019-04-04 18:48:14 -04:00
ref -> resolve ( zcs );
2015-11-21 07:22:36 -05:00
}
}
else if ( ref -> pub -> services . contains ( key )) {
zcs = ref -> pub -> services [ key ];
ref -> pub -> services . remove ( key );
2019-04-04 18:48:14 -04:00
if ( ref -> resolvers . contains ( key ))
ref -> resolvers [ key ] -> cleanUp ();
2015-11-21 07:22:36 -05:00
emit ref -> pub -> serviceRemoved ( zcs );
}
}
else {
ref -> cleanUp ( ref -> browser );
emit ref -> pub -> error ( QZeroConf :: browserFailed );
}
}
void DNSSD_API QZeroConfPrivate :: resolverCallback ( DNSServiceRef , DNSServiceFlags ,
quint32 interfaceIndex , DNSServiceErrorType err , const char * ,
const char * hostName , quint16 port , quint16 txtLen ,
2020-03-09 23:22:57 +01:00
const unsigned char * txtRecord , void * userdata )
2015-11-21 07:22:36 -05:00
{
2019-04-04 18:48:14 -04:00
Resolver * resolver = static_cast < Resolver *> ( userdata );
2015-11-21 07:22:36 -05:00
if ( err != kDNSServiceErr_NoError ) {
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
return ;
}
2020-07-01 15:13:04 +02:00
uchar recLen ;
2017-03-15 20:32:28 -04:00
while ( txtLen > 0 ) // add txt records
{
recLen = txtRecord [ 0 ];
txtRecord ++ ;
2020-03-09 23:22:57 +01:00
QByteArray avahiText ( reinterpret_cast < const char *> ( txtRecord ), recLen );
2022-04-07 13:48:38 +02:00
const int pos = avahiText . indexOf ( '=' );
2022-02-11 16:13:49 +01:00
if ( pos < 0 )
resolver -> zcs -> m_txt [ avahiText ] = "" ;
2017-03-15 20:32:28 -04:00
else
2022-02-11 16:13:49 +01:00
resolver -> zcs -> m_txt [ avahiText . left ( pos )] = avahiText . mid ( pos + 1 , - 1 );
2017-03-15 20:32:28 -04:00
txtLen -= recLen + 1 ;
txtRecord += recLen ;
}
2019-04-04 18:48:14 -04:00
resolver -> zcs -> m_host = hostName ;
resolver -> zcs -> m_port = qFromBigEndian < quint16 > ( port );
if ( resolver -> DNSaddressRef ) {
DNSServiceRefDeallocate ( resolver -> DNSaddressRef );
resolver -> DNSaddressRef = nullptr ;
}
2020-03-09 23:22:57 +01:00
err = DNSServiceGetAddrInfo ( & resolver -> DNSaddressRef , kDNSServiceFlagsForceMulticast , interfaceIndex , resolver -> ref -> protocol , hostName , static_cast < DNSServiceGetAddrInfoReply > ( addressReply ), resolver );
2015-11-21 07:22:36 -05:00
if ( err == kDNSServiceErr_NoError ) {
2019-04-04 18:48:14 -04:00
int sockfd = DNSServiceRefSockFD ( resolver -> DNSaddressRef );
2015-11-21 07:22:36 -05:00
if ( sockfd == - 1 ) {
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
}
else {
2022-09-01 07:30:17 +02:00
// Fix "multiple socket notifiers for same socket" warning
resolver -> addressNotifier . clear ();
2020-07-08 18:59:09 -04:00
resolver -> addressNotifier = QSharedPointer < QSocketNotifier >:: create ( sockfd , QSocketNotifier :: Read );
connect ( resolver -> addressNotifier . data (), & QSocketNotifier :: activated , resolver , & Resolver :: addressReady );
2015-11-21 07:22:36 -05:00
}
}
else {
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
}
}
void DNSSD_API QZeroConfPrivate :: addressReply ( DNSServiceRef sdRef ,
DNSServiceFlags flags , quint32 interfaceIndex ,
DNSServiceErrorType err , const char * hostName ,
const struct sockaddr * address , quint32 ttl , void * userdata )
{
2020-03-09 23:22:57 +01:00
Q_UNUSED ( interfaceIndex )
Q_UNUSED ( sdRef )
Q_UNUSED ( ttl )
Q_UNUSED ( hostName )
2015-11-21 07:22:36 -05:00
2019-04-04 18:48:14 -04:00
Resolver * resolver = static_cast < Resolver *> ( userdata );
2015-11-21 07:22:36 -05:00
if ( err == kDNSServiceErr_NoError ) {
if (( flags & kDNSServiceFlagsAdd ) != 0 ) {
QHostAddress hAddress ( address );
2019-04-04 18:48:14 -04:00
resolver -> zcs -> setIp ( hAddress );
2015-11-21 07:22:36 -05:00
2019-04-04 18:48:14 -04:00
QString key = resolver -> zcs -> name () + QString :: number ( interfaceIndex );
if ( ! resolver -> ref -> pub -> services . contains ( key )) {
resolver -> ref -> pub -> services . insert ( key , resolver -> zcs );
emit resolver -> ref -> pub -> serviceAdded ( resolver -> zcs );
2015-11-21 07:22:36 -05:00
}
else
2019-04-04 18:48:14 -04:00
emit resolver -> ref -> pub -> serviceUpdated ( resolver -> zcs );
2015-11-21 07:22:36 -05:00
}
}
else
2019-04-04 18:48:14 -04:00
resolver -> cleanUp ();
2015-11-21 07:22:36 -05:00
}
void QZeroConfPrivate :: cleanUp ( DNSServiceRef toClean )
{
if ( ! toClean )
return ;
else if ( toClean == browser ) {
2020-03-09 23:22:57 +01:00
browser = nullptr ;
2020-07-08 18:59:09 -04:00
browserNotifier . clear ();
2017-10-19 20:51:49 +02:00
QMap < QString , QZeroConfService >:: iterator i ;
2017-10-09 21:23:00 -04:00
for ( i = pub -> services . begin (); i != pub -> services . end (); i ++ ) {
2019-04-04 18:48:14 -04:00
QString key = ( * i ) -> name () + QString :: number (( * i ) -> interfaceIndex ());
resolvers [ key ] -> cleanUp ();
2017-10-09 21:23:00 -04:00
emit pub -> serviceRemoved ( * i );
}
2015-11-21 07:22:36 -05:00
pub -> services . clear ();
}
else if ( toClean == dnssRef ) {
2020-03-09 23:22:57 +01:00
dnssRef = nullptr ;
2020-07-08 18:59:09 -04:00
serviceNotifier . clear ();
2015-11-21 07:22:36 -05:00
}
DNSServiceRefDeallocate ( toClean );
}
2017-10-06 15:55:46 +02:00
QZeroConf :: QZeroConf ( QObject * parent ) : QObject ( parent )
2015-11-21 07:22:36 -05:00
{
pri = new QZeroConfPrivate ( this );
2017-11-02 20:27:37 -04:00
qRegisterMetaType < QZeroConfService > ( "QZeroConfService" );
2015-11-21 07:22:36 -05:00
}
QZeroConf ::~ QZeroConf ()
{
pri -> cleanUp ( pri -> dnssRef );
pri -> cleanUp ( pri -> browser );
delete pri ;
}
2022-10-28 13:12:56 -04:00
void QZeroConf :: startServicePublish ( const char * name , const char * type , const char * domain , quint16 port , quint32 interface )
2015-11-21 07:22:36 -05:00
{
DNSServiceErrorType err ;
if ( pri -> dnssRef ) {
emit error ( QZeroConf :: serviceRegistrationFailed );
return ;
}
2022-10-28 13:12:56 -04:00
err = DNSServiceRegister ( & pri -> dnssRef , 0 , interface ,
2015-11-21 07:22:36 -05:00
name ,
type ,
domain ,
2020-03-09 23:22:57 +01:00
nullptr ,
2017-03-10 20:08:54 -05:00
qFromBigEndian < quint16 > ( port ),
2020-03-09 23:22:57 +01:00
static_cast < uint16_t > ( pri -> txt . size ()), pri -> txt . data (),
static_cast < DNSServiceRegisterReply > ( QZeroConfPrivate :: registerCallback ), pri );
2015-11-21 07:22:36 -05:00
if ( err == kDNSServiceErr_NoError ) {
int sockfd = DNSServiceRefSockFD ( pri -> dnssRef );
if ( sockfd == - 1 ) {
pri -> cleanUp ( pri -> dnssRef );
emit error ( QZeroConf :: serviceRegistrationFailed );
}
else {
2020-07-08 18:59:09 -04:00
pri -> serviceNotifier = QSharedPointer < QSocketNotifier >:: create ( sockfd , QSocketNotifier :: Read , this );
connect ( pri -> serviceNotifier . data (), & QSocketNotifier :: activated , pri , & QZeroConfPrivate :: bsRead );
2015-11-21 07:22:36 -05:00
}
}
else {
pri -> cleanUp ( pri -> dnssRef );
emit error ( QZeroConf :: serviceRegistrationFailed );
}
}
void QZeroConf :: stopServicePublish ( void )
{
pri -> cleanUp ( pri -> dnssRef );
}
2017-10-09 21:25:58 -04:00
bool QZeroConf :: publishExists ( void )
{
if ( pri -> dnssRef )
return true ;
else
return false ;
}
2017-03-10 20:08:54 -05:00
void QZeroConf :: addServiceTxtRecord ( QString nameOnly )
{
2020-03-09 23:22:57 +01:00
pri -> txt . append ( static_cast < char > ( nameOnly . size ()));
2017-03-10 20:08:54 -05:00
pri -> txt . append ( nameOnly . toUtf8 ());
}
void QZeroConf :: addServiceTxtRecord ( QString name , QString value )
{
name . append ( "=" );
name . append ( value );
addServiceTxtRecord ( name );
}
void QZeroConf :: clearServiceTxtRecords ()
{
pri -> txt . clear ();
}
2015-11-21 07:22:36 -05:00
void QZeroConf :: startBrowser ( QString type , QAbstractSocket :: NetworkLayerProtocol protocol )
{
DNSServiceErrorType err ;
if ( pri -> browser ) {
emit error ( QZeroConf :: browserFailed );
return ;
}
switch ( protocol ) {
case QAbstractSocket :: IPv4Protocol : pri -> protocol = kDNSServiceProtocol_IPv4 ; break ;
case QAbstractSocket :: IPv6Protocol : pri -> protocol = kDNSServiceProtocol_IPv6 ; break ;
2019-03-05 21:26:41 -05:00
default :
qDebug ( "QZeroConf::startBrowser() - unsupported protocol, using IPv4" );
pri -> protocol = kDNSServiceProtocol_IPv4 ;
break ;
2020-03-09 23:22:57 +01:00
}
2015-11-21 07:22:36 -05:00
2020-03-09 23:22:57 +01:00
err = DNSServiceBrowse ( & pri -> browser , 0 , 0 , type . toUtf8 (), nullptr , static_cast < DNSServiceBrowseReply > ( QZeroConfPrivate :: browseCallback ), pri );
2015-11-21 07:22:36 -05:00
if ( err == kDNSServiceErr_NoError ) {
int sockfd = DNSServiceRefSockFD ( pri -> browser );
if ( sockfd == - 1 ) {
pri -> cleanUp ( pri -> browser );
emit error ( QZeroConf :: browserFailed );
}
else {
2020-07-08 18:59:09 -04:00
pri -> browserNotifier = QSharedPointer < QSocketNotifier >:: create ( sockfd , QSocketNotifier :: Read , this );
connect ( pri -> browserNotifier . data (), & QSocketNotifier :: activated , pri , & QZeroConfPrivate :: browserRead );
2015-11-21 07:22:36 -05:00
}
}
else {
pri -> cleanUp ( pri -> browser );
emit error ( QZeroConf :: browserFailed );
}
}
void QZeroConf :: stopBrowser ( void )
{
pri -> cleanUp ( pri -> browser );
}
2017-10-09 21:25:58 -04:00
bool QZeroConf :: browserExists ( void )
{
if ( pri -> browser )
return true ;
else
return false ;
}