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 ;
}