diff options
Diffstat (limited to 'dnssd')
-rw-r--r-- | dnssd/Makefile.am | 4 | ||||
-rw-r--r-- | dnssd/README | 7 | ||||
-rw-r--r-- | dnssd/configure.in.in | 49 | ||||
-rw-r--r-- | dnssd/domainbrowser.cpp | 75 | ||||
-rw-r--r-- | dnssd/publicservice.cpp | 192 | ||||
-rw-r--r-- | dnssd/publicservice.h | 5 | ||||
-rw-r--r-- | dnssd/query.cpp | 132 | ||||
-rw-r--r-- | dnssd/remoteservice.cpp | 105 | ||||
-rw-r--r-- | dnssd/responder.cpp | 75 | ||||
-rw-r--r-- | dnssd/responder.h | 34 | ||||
-rw-r--r-- | dnssd/sdevent.h | 9 | ||||
-rw-r--r-- | dnssd/servicebase.cpp | 2 | ||||
-rw-r--r-- | dnssd/servicebrowser.cpp | 25 |
13 files changed, 391 insertions, 323 deletions
diff --git a/dnssd/Makefile.am b/dnssd/Makefile.am index 5c9edb9c4..268e72a5d 100644 --- a/dnssd/Makefile.am +++ b/dnssd/Makefile.am @@ -1,5 +1,5 @@ # set the include path for X, qt and KDE -INCLUDES = -I$(top_srcdir) $(all_includes) +INCLUDES = -I$(top_srcdir) $(all_includes) $(AVAHI_CFLAGS) # these are the headers for your project noinst_HEADERS = sdevent.h @@ -14,7 +14,7 @@ libkdnssd_la_SOURCES = remoteservice.cpp responder.cpp servicebase.cpp \ dnssdincludedir = $(includedir)/dnssd dnssdinclude_HEADERS = domainbrowser.h query.h remoteservice.h \ publicservice.h servicebase.h servicebrowser.h settings.h -libkdnssd_la_LIBADD = ../kdecore/libkdecore.la $(LIB_DNSSD) +libkdnssd_la_LIBADD = ../kdecore/libkdecore.la $(AVAHI_LIBS) $(LIB_QT) libkdnssd_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -version-info 1:0 #kde_kcfg_DATA = kcm_kdnssd.kcfg diff --git a/dnssd/README b/dnssd/README index fd085df95..c797b2939 100644 --- a/dnssd/README +++ b/dnssd/README @@ -1,12 +1,11 @@ Checklist to ensure that zeroconf will work: -1) Install Apple's mdnsd, at least version 85 -2) kdelibs (and kdebase for ksysguard) should be configured and compiled with dns_sd sdk (part -of mdnsd installation) present - config.h should contain '#define HAVE_DNSSD 1' +1) Install Avahi, at least version 0.3 +2) compile kdnssd-avahi and install it to replace 'stub' libkdnssd.so provided by kdelibs 3) check /etc/nsswitch.conf and ensure that there is 'mdns' before 'dns' in line starting with 'host:'. It should be something like: host: files mdns dns -4) ensure that mdnsd is being started in initscripts +4) ensure that avahi-daemon is being started in initscripts 5) for testing: use kpf kicker applet to publish a directory, then open 'zeroconf:/' URL in konqueror. You should be able to see a local webserver with that published dir. diff --git a/dnssd/configure.in.in b/dnssd/configure.in.in index 022ffe871..4eac2441a 100644 --- a/dnssd/configure.in.in +++ b/dnssd/configure.in.in @@ -1,38 +1,17 @@ #MIN_CONFIG -AC_ARG_ENABLE(dnssd, [ --disable-dnssd don't require libdns_sd (browsing and publishing DNS-SD services will not be possible) ], with_dnssd=$enableval, with_dnssd=yes) -if test "$with_dnssd" = "yes"; then -AC_MSG_CHECKING(for DNS-SD support) -save_dnssdtest_LIBS="$LIBS" -save_dnssdtest_LDFLAGS="$LDFLAGS" -save_dnssdtest_CPPFLAGS="$CPPFLAGS" -LDFLAGS="$all_libraries $LDFLAGS" -CPPFLAGS="$CPPFLAGS $all_includes" -case $host_os in - darwin*) LIBS="" ;; - *) LIBS="-ldns_sd" ;; -esac -have_libdns_sd="no" -AC_TRY_LINK( [ - #include <dns_sd.h> - ],[ - DNSServiceRefDeallocate( (DNSServiceRef) 0); - TXTRecordDeallocate( (TXTRecordRef*) 0); - ],[ - AC_DEFINE(HAVE_DNSSD,1,[Define if dns-sd is available]) - case $host_os in - darwin*) LIB_DNSSD="" ;; - *) LIB_DNSSD="-ldns_sd" ;; - esac - have_libdns_sd="yes" - AC_MSG_RESULT(yes) - ],[ - AC_MSG_RESULT(no) - LIB_DNSSD="" -]) -CPPFLAGS=$save_dnssdtest_CPPFLAGS -LDFLAGS=$save_dnssdtest_LDFLAGS -LIBS=$save_dnssdtest_LIBS +# Check for pkg-config manually first, as if its not installed the +# PKG_PROG_PKG_CONFIG macro won't be defined. +m4_pattern_allow(PKG_CONFIG_MIN_VERSION) +AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no) + +if test x"$have_pkg_config" == xno; then + AC_MSG_ERROR(pkg-config is required to install this program) fi -AC_SUBST(LIB_DNSSD) -AM_CONDITIONAL(HAVE_DNSSD, test "$have_libdns_sd" = "yes") + +PKG_PROG_PKG_CONFIG + +PKG_CHECK_MODULES( AVAHI, [ avahi-qt3 >= 0.4 , avahi-client >= 0.4 ]) +AC_SUBST(AVAHI_CFLAGS) +AC_SUBST(AVAHI_LIBS) +PKG_CHECK_EXISTS( [ avahi-client >= 0.6], AC_DEFINE(AVAHI_API_0_6,1,[Avahi API 0.6] ) ) diff --git a/dnssd/domainbrowser.cpp b/dnssd/domainbrowser.cpp index cad3307dd..294ce2576 100644 --- a/dnssd/domainbrowser.cpp +++ b/dnssd/domainbrowser.cpp @@ -27,46 +27,56 @@ #include "query.h" #include "servicebrowser.h" #include <kapplication.h> +#ifdef AVAHI_API_0_6 +#include <avahi-client/lookup.h> +#endif namespace DNSSD { -#ifdef HAVE_DNSSD -void domain_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, - const char *replyDomain, void *context); +#ifdef AVAHI_API_0_6 +void domains_callback(AvahiDomainBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* replyDomain, + AvahiLookupResultFlags, void* context); +#else +void domains_callback(AvahiDomainBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* replyDomain, + void* context); #endif -class DomainBrowserPrivate : public Responder + +class DomainBrowserPrivate { public: - DomainBrowserPrivate(DomainBrowser* owner) : Responder(), m_browseLAN(false), m_started(false), m_owner(owner) {} + DomainBrowserPrivate(DomainBrowser* owner) : m_browseLAN(false), m_started(false), + m_browser(0), m_owner(owner) {} + ~DomainBrowserPrivate() { if (m_browser) avahi_domain_browser_free(m_browser); } QStringList m_domains; virtual void customEvent(QCustomEvent* event); bool m_browseLAN; bool m_started; + AvahiDomainBrowser* m_browser; DomainBrowser* m_owner; }; void DomainBrowserPrivate::customEvent(QCustomEvent* event) { - if (event->type()==QEvent::User+SD_ERROR) stop(); if (event->type()==QEvent::User+SD_ADDREMOVE) { AddRemoveEvent *aev = static_cast<AddRemoveEvent*>(event); if (aev->m_op==AddRemoveEvent::Add) m_owner->gotNewDomain(aev->m_domain); else m_owner->gotRemoveDomain(aev->m_domain); } } + DomainBrowser::DomainBrowser(QObject *parent) : QObject(parent) { d = new DomainBrowserPrivate(this); - d->m_domains = Configuration::domainList(); + d->m_domains = Configuration::domainList(); if (Configuration::browseLocal()) { d->m_domains+="local."; d->m_browseLAN=true; } - connect(KApplication::kApplication(),SIGNAL(kipcMessage(int,int)),this, - SLOT(domainListChanged(int,int))); + connect(KApplication::kApplication(),SIGNAL(kipcMessage(int,int)),this, + SLOT(domainListChanged(int,int))); } DomainBrowser::DomainBrowser(const QStringList& domains, bool recursive, QObject *parent) : QObject(parent) @@ -88,14 +98,15 @@ void DomainBrowser::startBrowse() if (d->m_started) return; d->m_started=true; if (ServiceBrowser::isAvailable()!=ServiceBrowser::Working) return; - QStringList::const_iterator itEnd = d->m_domains.end(); + QStringList::const_iterator itEnd = d->m_domains.end(); for (QStringList::const_iterator it=d->m_domains.begin(); it!=itEnd; ++it ) emit domainAdded(*it); -#ifdef HAVE_DNSSD - if (d->m_browseLAN) { - DNSServiceRef ref; - if (DNSServiceEnumerateDomains(&ref,kDNSServiceFlagsBrowseDomains,0,domain_callback, - reinterpret_cast<void*>(d))==kDNSServiceErr_NoError) d->setRef(ref); - } + if (d->m_browseLAN) +#ifdef AVAHI_API_0_6 + d->m_browser = avahi_domain_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + "local.", AVAHI_DOMAIN_BROWSER_BROWSE, (AvahiLookupFlags)0, domains_callback, this); +#else + d->m_browser = avahi_domain_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + "local.", AVAHI_DOMAIN_BROWSER_BROWSE, domains_callback, this); #endif } @@ -115,9 +126,14 @@ void DomainBrowser::gotRemoveDomain(const QString& domain) void DomainBrowser::domainListChanged(int message,int) { if (message!=KIPCDomainsChanged) return; + bool was_started = d->m_started; - if (d->isRunning()) d->stop(); // LAN query + if (d->m_browser) { + avahi_domain_browser_free(d->m_browser); // LAN query + d->m_browser=0; + } d->m_started = false; + // remove all domains and resolvers if (was_started) { QStringList::const_iterator itEnd = d->m_domains.end(); @@ -129,7 +145,7 @@ void DomainBrowser::domainListChanged(int message,int) Configuration::self()->readConfig(); d->m_browseLAN = Configuration::browseLocal(); d->m_domains = Configuration::domainList(); - if (Configuration::browseLocal()) d->m_domains+="local."; + if (Configuration::browseLocal()) d->m_domains+="local"; // this will emit domainAdded() for every domain if necessary if (was_started) startBrowse(); } @@ -147,22 +163,21 @@ bool DomainBrowser::isRunning() const void DomainBrowser::virtual_hook(int, void*) {} -#ifdef HAVE_DNSSD -void domain_callback(DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, - const char *replyDomain, void *context) +#ifdef AVAHI_API_0_6 +void domains_callback(AvahiDomainBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* replyDomain, + AvahiLookupResultFlags,void* context) +#else +void domains_callback(AvahiDomainBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* replyDomain, + void* context) +#endif { QObject *obj = reinterpret_cast<QObject*>(context); - if (errorCode != kDNSServiceErr_NoError) { - ErrorEvent err; - QApplication::sendEvent(obj, &err); - } else { - AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : + AddRemoveEvent* arev=new AddRemoveEvent((event==AVAHI_BROWSER_NEW) ? AddRemoveEvent::Add : AddRemoveEvent::Remove, QString::null, QString::null, - DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing)); - QApplication::sendEvent(obj, &arev); - } + DNSToDomain(replyDomain)); + QApplication::postEvent(obj, arev); } -#endif + } #include "domainbrowser.moc" diff --git a/dnssd/publicservice.cpp b/dnssd/publicservice.cpp index 7ba749c53..ad1ebdbb6 100644 --- a/dnssd/publicservice.cpp +++ b/dnssd/publicservice.cpp @@ -30,6 +30,12 @@ #include <network/ksocketaddress.h> #include <kurl.h> #include <unistd.h> +#include <avahi-client/client.h> +#ifdef AVAHI_API_0_6 +#include <avahi-client/publish.h> +#endif +#include <avahi-common/alternative.h> +#include <avahi-common/strlst.h> #include "sdevent.h" #include "responder.h" #include "servicebrowser.h" @@ -38,16 +44,23 @@ namespace DNSSD { static unsigned long publicIP(); -#ifdef HAVE_DNSSD -void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, - const char*, const char*, void *context); -#endif -class PublicServicePrivate : public Responder + +void publish_callback (AvahiEntryGroup*, AvahiEntryGroupState s, void *context); + +class PublicServicePrivate { public: - PublicServicePrivate() : m_published(false) + PublicServicePrivate() : m_published(false), m_running(false), m_collision(false), m_group(false) {} bool m_published; + bool m_running; + bool m_collision; + AvahiEntryGroup* m_group; + void commit() + { + if (!m_collision) avahi_entry_group_commit(m_group); + } + }; PublicService::PublicService(const QString& name, const QString& type, unsigned int port, @@ -55,6 +68,10 @@ PublicService::PublicService(const QString& name, const QString& type, unsigned : QObject(), ServiceBase(name, type, QString::null, domain, port) { d = new PublicServicePrivate; + if (Responder::self().client()) { + d->m_group = avahi_entry_group_new(Responder::self().client(), publish_callback,this); + connect(&Responder::self(),SIGNAL(stateChanged(AvahiClientState)),this,SLOT(clientState(AvahiClientState))); + } if (domain.isNull()) if (Configuration::publishType()==Configuration::EnumPublishType::LAN) m_domain="local."; else m_domain=Configuration::publishDomain(); @@ -63,114 +80,148 @@ PublicService::PublicService(const QString& name, const QString& type, unsigned PublicService::~PublicService() { - stop(); + if (d->m_group) avahi_entry_group_free(d->m_group); delete d; } +void PublicService::tryApply() +{ + if (fillEntryGroup()) d->commit(); + else { + stop(); + emit published(false); + } +} + void PublicService::setServiceName(const QString& serviceName) { m_serviceName = serviceName; - if (d->isRunning()) { - stop(); - publishAsync(); - } + if (d->m_running) { + avahi_entry_group_reset(d->m_group); + tryApply(); + } } void PublicService::setDomain(const QString& domain) { m_domain = domain; - if (d->isRunning()) { - stop(); - publishAsync(); - } + if (d->m_running) { + avahi_entry_group_reset(d->m_group); + tryApply(); + } } void PublicService::setType(const QString& type) { m_type = type; - if (d->isRunning()) { - stop(); - publishAsync(); - } + if (d->m_running) { + avahi_entry_group_reset(d->m_group); + tryApply(); + } } void PublicService::setPort(unsigned short port) { m_port = port; - if (d->isRunning()) { - stop(); - publishAsync(); - } + if (d->m_running) { + avahi_entry_group_reset(d->m_group); + tryApply(); + } } -bool PublicService::isPublished() const +void PublicService::setTextData(const QMap<QString,QString>& textData) { - return d->m_published; + m_textData = textData; + if (d->m_running) { + avahi_entry_group_reset(d->m_group); + tryApply(); + } } -void PublicService::setTextData(const QMap<QString,QString>& textData) +bool PublicService::isPublished() const { - m_textData = textData; - if (d->isRunning()) { - stop(); - publishAsync(); - } + return d->m_published; } bool PublicService::publish() { publishAsync(); - while (d->isRunning() && !d->m_published) d->process(); + while (d->m_running && !d->m_published) Responder::self().process(); return d->m_published; } void PublicService::stop() { - d->stop(); - d->m_published = false; + if (d->m_group) avahi_entry_group_reset(d->m_group); + d->m_published = false; +} +bool PublicService::fillEntryGroup() +{ + AvahiStringList *s=0; + QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); + for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) + s = avahi_string_list_add_pair(s, it.key().utf8(),it.data().utf8()); +#ifdef AVAHI_API_0_6 + bool res = (!avahi_entry_group_add_service_strlst(d->m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags)0, + m_serviceName.isNull() ? avahi_client_get_host_name(Responder::self().client()) : m_serviceName.utf8().data(), + m_type.ascii(),domainToDNS(m_domain),m_hostName.utf8(),m_port,s)); +#else + bool res = (!avahi_entry_group_add_service_strlst(d->m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + m_serviceName.isNull() ? avahi_client_get_host_name(Responder::self().client()) : m_serviceName.utf8().data(), + m_type.ascii(),m_domain.utf8(),m_hostName.utf8(),m_port,s)); +#endif + avahi_string_list_free(s); + return res; } +void PublicService::clientState(AvahiClientState s) +{ + if (!d->m_running) return; + switch (s) { +#ifdef AVAHI_API_0_6 + case AVAHI_CLIENT_FAILURE: +#else + case AVAHI_CLIENT_S_INVALID: + case AVAHI_CLIENT_DISCONNECTED: +#endif + stop(); + emit published(false); + break; + case AVAHI_CLIENT_S_REGISTERING: + case AVAHI_CLIENT_S_COLLISION: + avahi_entry_group_reset(d->m_group); + d->m_collision=true; + break; + case AVAHI_CLIENT_S_RUNNING: + if (d->m_collision) { + d->m_collision=false; + tryApply(); + } + } +} + void PublicService::publishAsync() { - if (d->isRunning()) stop(); -#ifdef HAVE_DNSSD - if (ServiceBrowser::isAvailable()==ServiceBrowser::Working) { - TXTRecordRef txt; - TXTRecordCreate(&txt,0,0); - QMap<QString,QString>::ConstIterator itEnd = m_textData.end(); - for (QMap<QString,QString>::ConstIterator it = m_textData.begin(); it!=itEnd ; ++it) { - QCString value = it.data().utf8(); - if (TXTRecordSetValue(&txt,it.key().utf8(),value.length(),value)!=kDNSServiceErr_NoError) { - TXTRecordDeallocate(&txt); - emit published(false); - return; - } - } - DNSServiceRef ref; - if (DNSServiceRegister(&ref,0,0,m_serviceName.utf8(),m_type.ascii(),domainToDNS(m_domain),NULL, - htons(m_port),TXTRecordGetLength(&txt),TXTRecordGetBytesPtr(&txt),publish_callback, - reinterpret_cast<void*>(this)) == kDNSServiceErr_NoError) d->setRef(ref); - TXTRecordDeallocate(&txt); + if (d->m_running) stop(); + + if (!d->m_group) { + emit published(false); + return; } -#endif - if (!d->isRunning()) emit published(false); + AvahiClientState s=Responder::self().state(); + d->m_running=true; + d->m_collision=true; // make it look like server is getting out of collision to force registering + clientState(s); } -#ifdef HAVE_DNSSD -void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, - const char*, const char*, void *context) +void publish_callback (AvahiEntryGroup*, AvahiEntryGroupState s, void *context) { QObject *obj = reinterpret_cast<QObject*>(context); - if (errorCode != kDNSServiceErr_NoError) { - ErrorEvent err; - QApplication::sendEvent(obj, &err); - } else { - PublishEvent pev(QString::fromUtf8(name)); - QApplication::sendEvent(obj, &pev); - } + if (s!=AVAHI_ENTRY_GROUP_ESTABLISHED && s!=AVAHI_ENTRY_GROUP_COLLISION) return; + PublishEvent* pev=new PublishEvent(s==AVAHI_ENTRY_GROUP_ESTABLISHED); + QApplication::postEvent(obj, pev); } -#endif const KURL PublicService::toInvitation(const QString& host) { @@ -194,14 +245,13 @@ const KURL PublicService::toInvitation(const QString& host) void PublicService::customEvent(QCustomEvent* event) { - if (event->type()==QEvent::User+SD_ERROR) { - stop(); - emit published(false); - } if (event->type()==QEvent::User+SD_PUBLISH) { + if (!static_cast<PublishEvent*>(event)->m_ok) { + setServiceName(QString::fromUtf8(avahi_alternative_service_name(m_serviceName.utf8()))); + return; + } d->m_published=true; emit published(true); - m_serviceName = static_cast<PublishEvent*>(event)->m_name; } } diff --git a/dnssd/publicservice.h b/dnssd/publicservice.h index 480f071ee..ff090d354 100644 --- a/dnssd/publicservice.h +++ b/dnssd/publicservice.h @@ -23,6 +23,7 @@ #include <qobject.h> #include <dnssd/servicebase.h> +#include <avahi-client/client.h> class KURL; namespace DNSSD @@ -137,6 +138,10 @@ signals: void published(bool); private: PublicServicePrivate *d; + bool fillEntryGroup(); + void tryApply(); +private slots: + void clientState(AvahiClientState); protected: virtual void customEvent(QCustomEvent* event); diff --git a/dnssd/query.cpp b/dnssd/query.cpp index bcb9c69f5..27b297816 100644 --- a/dnssd/query.cpp +++ b/dnssd/query.cpp @@ -22,26 +22,46 @@ #include "responder.h" #include "remoteservice.h" #include "sdevent.h" -#include <kdebug.h> +#include <qdatetime.h> #include <qapplication.h> #include <qtimer.h> -#define TIMEOUT_WAN 2000 +#include <avahi-client/client.h> +#ifdef AVAHI_API_0_6 +#include <avahi-client/lookup.h> +#endif + #define TIMEOUT_LAN 200 namespace DNSSD { -#ifdef HAVE_DNSSD -void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, - const char *serviceName, const char *regtype, const char *replyDomain, void *context); +#ifdef AVAHI_API_0_6 + +void services_callback(AvahiServiceBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* name, + const char* regtype, const char* domain, AvahiLookupResultFlags, void* context); +void types_callback(AvahiServiceTypeBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* regtype, + const char* replyDomain, AvahiLookupResultFlags, void* context); +#else +void services_callback(AvahiServiceBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* name, + const char* regtype, const char* domain, void* context); +void types_callback(AvahiServiceTypeBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* regtype, + const char* replyDomain, void* context); +void domains_callback(AvahiDomainBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* replyDomain, + void* context); #endif -class QueryPrivate : public Responder + +enum BrowserType { Types, Services }; + +class QueryPrivate { public: - QueryPrivate(const QString& type, const QString& domain) : Responder(), m_finished(false), - m_domain(domain), m_type(type) - {}; + QueryPrivate(const QString& type, const QString& domain) : m_finished(false), m_browser(0), + m_running(false), m_domain(domain), m_type(type) {} + bool m_finished; + BrowserType m_browserType; + void* m_browser; + bool m_running; QString m_domain; QTimer timeout; QString m_type; @@ -56,12 +76,18 @@ Query::Query(const QString& type, const QString& domain) Query::~Query() { + if (d->m_browser) { + switch (d->m_browserType) { + case Services: avahi_service_browser_free((AvahiServiceBrowser*)d->m_browser); break; + case Types: avahi_service_type_browser_free((AvahiServiceTypeBrowser*)d->m_browser); break; + } + } delete d; } bool Query::isRunning() const { - return d->isRunning(); + return d->m_running; } bool Query::isFinished() const @@ -76,16 +102,31 @@ const QString& Query::domain() const void Query::startQuery() { - if (d->isRunning()) return; + if (d->m_running) return; d->m_finished = false; -#ifdef HAVE_DNSSD - DNSServiceRef ref; - if (DNSServiceBrowse(&ref,0,0, d->m_type.ascii(), - domainToDNS(d->m_domain),query_callback,reinterpret_cast<void*>(this)) - == kDNSServiceErr_NoError) d->setRef(ref); + if (d->m_type=="_services._dns-sd._udp") { + d->m_browserType = Types; +#ifdef AVAHI_API_0_6 + d->m_browser = avahi_service_type_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + domainToDNS(d->m_domain), (AvahiLookupFlags)0, types_callback, this); +#else + d->m_browser = avahi_service_type_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + d->m_domain.utf8(), types_callback, this); #endif - if (!d->isRunning()) emit finished(); - else d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN,true); + } else { + d->m_browserType = Services; +#ifdef AVAHI_API_0_6 + d->m_browser = avahi_service_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + d->m_type.ascii(),domainToDNS(d->m_domain), (AvahiLookupFlags)0, services_callback,this); +#else + d->m_browser = avahi_service_browser_new(Responder::self().client(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + d->m_type.ascii(),d->m_domain.utf8(),services_callback,this); +#endif + } + if (d->m_browser) { + d->m_running=true; + d->timeout.start(TIMEOUT_LAN,true); + } else emit finished(); } void Query::virtual_hook(int, void*) { @@ -93,24 +134,15 @@ void Query::virtual_hook(int, void*) void Query::customEvent(QCustomEvent* event) { - if (event->type()==QEvent::User+SD_ERROR) { - d->stop(); - d->m_finished=false; - emit finished(); - } if (event->type()==QEvent::User+SD_ADDREMOVE) { - RemoteService* svr; + d->timeout.start(TIMEOUT_LAN,true); + d->m_finished=false; AddRemoveEvent *aev = static_cast<AddRemoveEvent*>(event); // m_type has useless trailing dot - QString type=aev->m_type.left(aev->m_type.length()-1); - // label is badly splitted here - _http _tcp.local. . - rely on decode() - if (d->m_type=="_services._dns-sd._udp") svr = new RemoteService(aev->m_name+"."+ - type+"."+aev->m_domain); - else svr = new RemoteService(aev->m_name, type, aev->m_domain); + RemoteService* svr = new RemoteService(aev->m_name, + aev->m_type,aev->m_domain); if (aev->m_op==AddRemoveEvent::Add) emit serviceAdded(svr); else emit serviceRemoved(svr); - d->m_finished = aev->m_last; - if (d->m_finished) emit finished(); } } @@ -119,22 +151,36 @@ void Query::timeout() d->m_finished=true; emit finished(); } -#ifdef HAVE_DNSSD -void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode, - const char *serviceName, const char *regtype, const char *replyDomain, - void *context) + +#ifdef AVAHI_API_0_6 +void services_callback (AvahiServiceBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, + const char* serviceName, const char* regtype, const char* replyDomain, AvahiLookupResultFlags, void* context) +#else +void services_callback (AvahiServiceBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, + const char* serviceName, const char* regtype, const char* replyDomain, void* context) +#endif { QObject *obj = reinterpret_cast<QObject*>(context); - if (errorCode != kDNSServiceErr_NoError) { - ErrorEvent err; - QApplication::sendEvent(obj, &err); - } else { - AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : + AddRemoveEvent* arev = new AddRemoveEvent((event==AVAHI_BROWSER_NEW) ? AddRemoveEvent::Add : AddRemoveEvent::Remove, QString::fromUtf8(serviceName), regtype, - DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing)); - QApplication::sendEvent(obj, &arev); - } + DNSToDomain(replyDomain)); + QApplication::postEvent(obj, arev); } + +#ifdef AVAHI_API_0_6 +void types_callback(AvahiServiceTypeBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* regtype, + const char* replyDomain, AvahiLookupResultFlags, void* context) +#else +void types_callback(AvahiServiceTypeBrowser*, AvahiIfIndex, AvahiProtocol, AvahiBrowserEvent event, const char* regtype, + const char* replyDomain, void* context) #endif +{ + QObject *obj = reinterpret_cast<QObject*>(context); + AddRemoveEvent* arev = new AddRemoveEvent((event==AVAHI_BROWSER_NEW) ? AddRemoveEvent::Add : + AddRemoveEvent::Remove, QString::null, regtype, + DNSToDomain(replyDomain)); + QApplication::postEvent(obj, arev); +} + } #include "query.moc" diff --git a/dnssd/remoteservice.cpp b/dnssd/remoteservice.cpp index 9d3e4abb6..9fa1a3fe2 100644 --- a/dnssd/remoteservice.cpp +++ b/dnssd/remoteservice.cpp @@ -27,33 +27,39 @@ #include <sys/types.h> #endif #include <netinet/in.h> +#include <avahi-client/client.h> +#include <avahi-common/strlst.h> +#ifdef AVAHI_API_0_6 +#include <avahi-client/lookup.h> +#endif #include "remoteservice.h" #include "responder.h" #include "sdevent.h" -#include <kdebug.h> namespace DNSSD { -#ifdef HAVE_DNSSD -void resolve_callback ( DNSServiceRef, - DNSServiceFlags, - uint32_t, - DNSServiceErrorType errorCode, - const char*, - const char *hosttarget, - uint16_t port, - uint16_t txtLen, - const unsigned char *txtRecord, - void *context - ); - +#ifdef AVAHI_API_0_6 +void resolve_callback(AvahiServiceResolver*, AvahiIfIndex, AvahiProtocol proto, AvahiResolverEvent e, + const char* name, const char* type, const char* domain, const char* hostname, const AvahiAddress* a, + uint16_t port, AvahiStringList* txt, AvahiLookupResultFlags, void* context); +#else +void resolve_callback(AvahiServiceResolver*, AvahiIfIndex, AvahiProtocol proto, AvahiResolverEvent e, + const char* name, const char* type, const char* domain, const char* hostname, const AvahiAddress* a, + uint16_t port, AvahiStringList* txt, void* context); #endif + class RemoteServicePrivate : public Responder { public: - RemoteServicePrivate() : Responder(), m_resolved(false) - {}; + RemoteServicePrivate() : m_resolved(false), m_running(false), m_resolver(0) {} bool m_resolved; + bool m_running; + AvahiServiceResolver* m_resolver; + void stop() { + m_running = false; + if (m_resolver) avahi_service_resolver_free(m_resolver); + m_resolver=0; + } }; RemoteService::RemoteService(const QString& label) @@ -83,29 +89,33 @@ RemoteService::RemoteService(const KURL& url) RemoteService::~RemoteService() { + if (d->m_resolver) avahi_service_resolver_free(d->m_resolver); delete d; } bool RemoteService::resolve() { resolveAsync(); - while (d->isRunning() && !d->m_resolved) d->process(); + while (d->m_running && !d->m_resolved) Responder::self().process(); d->stop(); return d->m_resolved; } void RemoteService::resolveAsync() { - if (d->isRunning()) return; + if (d->m_running) return; d->m_resolved = false; - kdDebug() << this << ":Starting resolve of : " << m_serviceName << " " << m_type << " " << m_domain << "\n"; -#ifdef HAVE_DNSSD - DNSServiceRef ref; - if (DNSServiceResolve(&ref,0,0,m_serviceName.utf8(), m_type.ascii(), - domainToDNS(m_domain),(DNSServiceResolveReply)resolve_callback,reinterpret_cast<void*>(this)) - == kDNSServiceErr_NoError) d->setRef(ref); + // FIXME: first protocol should be set? +#ifdef AVAHI_API_0_6 + d->m_resolver = avahi_service_resolver_new(Responder::self().client(),AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + m_serviceName.utf8(), m_type.ascii(), domainToDNS(m_domain), AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_NO_ADDRESS, + resolve_callback, this); +#else + d->m_resolver = avahi_service_resolver_new(Responder::self().client(),AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + m_serviceName.utf8(), m_type.ascii(), m_domain.utf8(), AVAHI_PROTO_UNSPEC, resolve_callback, this); #endif - if (!d->isRunning()) emit resolved(false); + if (d->m_resolver) d->m_running=true; + else emit resolved(false); } bool RemoteService::isResolved() const @@ -154,42 +164,33 @@ QDataStream & operator>> (QDataStream & s, RemoteService & a) return s; } - -#ifdef HAVE_DNSSD -void resolve_callback ( DNSServiceRef, - DNSServiceFlags, - uint32_t, - DNSServiceErrorType errorCode, - const char*, - const char *hosttarget, - uint16_t port, - uint16_t txtLen, - const unsigned char *txtRecord, - void *context - ) +#ifdef AVAHI_API_0_6 +void resolve_callback(AvahiServiceResolver*, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent e, + const char*, const char*, const char*, const char* hostname, const AvahiAddress*, + uint16_t port, AvahiStringList* txt, AvahiLookupResultFlags, void* context) +#else +void resolve_callback(AvahiServiceResolver*, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent e, + const char*, const char*, const char*, const char* hostname, const AvahiAddress*, + uint16_t port, AvahiStringList* txt, void* context) +#endif { QObject *obj = reinterpret_cast<QObject*>(context); - if (errorCode != kDNSServiceErr_NoError) { + if (e != AVAHI_RESOLVER_FOUND) { ErrorEvent err; QApplication::sendEvent(obj, &err); return; } - char key[256]; - int index=0; - unsigned char valueLen; - kdDebug() << "Resolve callback\n"; QMap<QString,QString> map; - const void *voidValue = 0; - while (TXTRecordGetItemAtIndex(txtLen,txtRecord,index++,256,key,&valueLen, - &voidValue) == kDNSServiceErr_NoError) - { - if (voidValue) map[QString::fromUtf8(key)]=QString::fromUtf8((const char*)voidValue,valueLen); - else map[QString::fromUtf8(key)]=QString::null; - } - ResolveEvent rev(DNSToDomain(hosttarget),ntohs(port),map); + while (txt) { + char *key, *value; + size_t size; + if (avahi_string_list_get_pair(txt,&key,&value,&size)) break; + map[QString::fromUtf8(key)]=(value) ? QString::fromUtf8(value) : QString::null; + txt = txt->next; + } + ResolveEvent rev(DNSToDomain(hostname),port,map); QApplication::sendEvent(obj, &rev); } -#endif } diff --git a/dnssd/responder.cpp b/dnssd/responder.cpp index 3721d3aae..43be68b28 100644 --- a/dnssd/responder.cpp +++ b/dnssd/responder.cpp @@ -20,62 +20,61 @@ #include "responder.h" #include <qapplication.h> +#include <qeventloop.h> +#include <kstaticdeleter.h> #include <kidna.h> +#include <kdebug.h> +#include <avahi-qt3/qt-watch.h> -// dns_sd.h API should care about proper encoding of non-latin1 characters -// but for now it does not -#define IDN_BROKEN_IN_MDNSRESPONDER namespace DNSSD { -Responder::Responder(DNSServiceRef ref,QObject *parent, const char *name) - : QObject(parent, name), m_ref(0), m_socket(0) +static KStaticDeleter<Responder> responder_sd; +Responder* Responder::m_self = 0; + +void client_callback(AvahiClient *, AvahiClientState s, void* u) { - setRef(ref); + Responder *r = reinterpret_cast<Responder*>(u); + emit (r->stateChanged(s)); } - -void Responder::setRef(DNSServiceRef ref) + + +Responder::Responder() { - if (m_socket || m_ref) stop(); - m_running = false; - m_ref = ref; - if (m_ref == 0 ) return; -#ifdef HAVE_DNSSD - int fd = DNSServiceRefSockFD(ref); - if (fd == -1) return; - m_socket = new QSocketNotifier(fd,QSocketNotifier::Read,this); - connect(m_socket,SIGNAL(activated(int)),this,SLOT(process())); - m_running = true; + int error; + const AvahiPoll* poll = avahi_qt_poll_get(); +#ifdef AVAHI_API_0_6 + m_client = avahi_client_new(poll, AVAHI_CLIENT_IGNORE_USER_CONFIG,client_callback, this, &error); +#else + m_client = avahi_client_new(poll, client_callback, this, &error); #endif + if (!m_client) kdWarning() << "Failed to create avahi client" << endl; } + Responder::~Responder() { - stop(); + if (m_client) avahi_client_free(m_client); } -void Responder::stop() +Responder& Responder::self() { - if (m_socket) delete m_socket; - m_socket = 0; -#ifdef HAVE_DNSSD - if (m_ref) DNSServiceRefDeallocate(m_ref); -#endif - m_ref = 0; - m_running = false; -} - + if (!m_self) responder_sd.setObject(m_self, new Responder); + return *m_self; +} void Responder::process() { -#ifdef HAVE_DNSSD - if ( DNSServiceProcessResult(m_ref) != kDNSServiceErr_NoError) stop(); -#endif + qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); } -bool Responder::isRunning() const +AvahiClientState Responder::state() const { - return m_running; +#ifdef AVAHI_API_0_6 + return (m_client) ? (avahi_client_get_state(m_client)) : AVAHI_CLIENT_FAILURE; +#else + return (m_client) ? (avahi_client_get_state(m_client)) : AVAHI_CLIENT_DISCONNECTED; +#endif } bool domainIsLocal(const QString& domain) @@ -85,22 +84,14 @@ bool domainIsLocal(const QString& domain) QCString domainToDNS(const QString &domain) { -#ifdef IDN_BROKEN_IN_MDNSRESPONDER if (domainIsLocal(domain)) return domain.utf8(); else return KIDNA::toAsciiCString(domain); -#else - return domain.utf8(); -#endif } QString DNSToDomain(const char* domain) { -#ifdef IDN_BROKEN_IN_MDNSRESPONDER if (domainIsLocal(domain)) return QString::fromUtf8(domain); else return KIDNA::toUnicode(domain); -#else - return QString::fromUtf8(domain); -#endif } diff --git a/dnssd/responder.h b/dnssd/responder.h index 6e36d3357..32ccb7a0b 100644 --- a/dnssd/responder.h +++ b/dnssd/responder.h @@ -25,12 +25,7 @@ #include <qsocketnotifier.h> #include <qsignal.h> #include <config.h> -#ifdef HAVE_DNSSD -#include <dns_sd.h> -#else -#define DNSServiceRef void* -#endif - +#include <avahi-client/client.h> namespace DNSSD { @@ -38,30 +33,28 @@ namespace DNSSD This class should not be used directly. @author Jakub Stachowski -@short Internal class wrapping dns_sd.h interface +@short Internal class wrapping avahi client */ class Responder : public QObject { Q_OBJECT public: - Responder(DNSServiceRef ref=0,QObject *parent = 0, const char *name = 0); + Responder(); ~Responder(); - /** - Returns true if it is possible to use mDNS service publishing and discovery. - It needs mDNSResponder running. - */ - bool isRunning() const; - void setRef(DNSServiceRef ref); - void stop(); -public slots: + static Responder& self(); + AvahiClientState state() const; + AvahiClient* client() const { return m_client; } void process(); -protected: - DNSServiceRef m_ref; - bool m_running; - QSocketNotifier *m_socket; +signals: + void stateChanged(AvahiClientState); +private: + AvahiClient* m_client; + static Responder* m_self; + friend void client_callback(AvahiClient*, AvahiClientState, void*); + }; /* Utils functions */ @@ -71,6 +64,7 @@ bool domainIsLocal(const QString& domain); QCString domainToDNS(const QString &domain); QString DNSToDomain(const char* domain); + } #endif diff --git a/dnssd/sdevent.h b/dnssd/sdevent.h index 5104839a7..8530e730f 100644 --- a/dnssd/sdevent.h +++ b/dnssd/sdevent.h @@ -41,24 +41,23 @@ class AddRemoveEvent : public QCustomEvent public: enum Operation { Add, Remove }; AddRemoveEvent(Operation op,const QString& name,const QString& type, - const QString& domain, bool last) : QCustomEvent(QEvent::User+SD_ADDREMOVE), - m_op(op), m_name(name), m_type(type), m_domain(domain), m_last(last) + const QString& domain) : QCustomEvent(QEvent::User+SD_ADDREMOVE), + m_op(op), m_name(name), m_type(type), m_domain(domain) {} const Operation m_op; const QString m_name; const QString m_type; const QString m_domain; - const bool m_last; }; class PublishEvent : public QCustomEvent { public: - PublishEvent(const QString& name) : QCustomEvent(QEvent::User+SD_PUBLISH), m_name(name) + PublishEvent(bool ok) : QCustomEvent(QEvent::User+SD_PUBLISH), m_ok(ok) {} - const QString m_name; + bool m_ok; }; class ResolveEvent : public QCustomEvent diff --git a/dnssd/servicebase.cpp b/dnssd/servicebase.cpp index ef2943b95..7fd1ef802 100644 --- a/dnssd/servicebase.cpp +++ b/dnssd/servicebase.cpp @@ -34,7 +34,7 @@ ServiceBase::~ServiceBase() QString ServiceBase::encode() { - return m_serviceName.replace("\\","\\\\").replace(".","\\.") + QString(".") + m_type + + return m_serviceName.replace(".","\\.").replace("\\","\\\\") + QString(".") + m_type + QString(".") + m_domain; } diff --git a/dnssd/servicebrowser.cpp b/dnssd/servicebrowser.cpp index 017dc8102..542580c1b 100644 --- a/dnssd/servicebrowser.cpp +++ b/dnssd/servicebrowser.cpp @@ -23,14 +23,11 @@ #include <qstringlist.h> #include <qfile.h> #include "domainbrowser.h" +#include "responder.h" #include "query.h" #include "servicebrowser.h" +#include <avahi-client/client.h> #include <config.h> -#ifdef HAVE_DNSSD -#include <dns_sd.h> -#endif - -#define MDNSD_PID "/var/run/mdnsd.pid" namespace DNSSD { @@ -85,20 +82,12 @@ ServiceBrowser::ServiceBrowser(const QString& type,const QString& domain,int fla const ServiceBrowser::State ServiceBrowser::isAvailable() { -#ifdef HAVE_DNSSD - QFile f(MDNSD_PID); - if (!f.open(IO_ReadOnly)) return Stopped; // no pidfile - QString line; - if (f.readLine(line,16)<1) return Stopped; - unsigned int pid = line.toUInt(); - if (pid==0) return Stopped; // not a pid - return (kill(pid,0)==0 || errno==EPERM) ? Working : Stopped; - // signal 0 only checks if process is running, mdnsd is probably owned by 'nobody' so we will - // get EPERM, if mdnsd is not running error will be ESRCH - + AvahiClientState s = Responder::self().state(); +#ifdef AVAHI_API_0_6 + return (s==AVAHI_CLIENT_FAILURE) ? Stopped : Working; #else - return Unsupported; -#endif + return (s==AVAHI_CLIENT_S_INVALID || s==AVAHI_CLIENT_DISCONNECTED) ? Stopped : Working; +#endif } ServiceBrowser::~ ServiceBrowser() { |