diff options
Diffstat (limited to 'Swiften/LinkLocal/DNSSD')
38 files changed, 1520 insertions, 0 deletions
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h new file mode 100644 index 0000000..229f97e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h @@ -0,0 +1,64 @@ +#pragma once + +#include <boost/bind.hpp> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiBrowseQuery : public DNSSDBrowseQuery, public AvahiQuery { + public: + AvahiBrowseQuery(boost::shared_ptr<AvahiQuerier> q) : AvahiQuery(q) { + } + + void startBrowsing() { + std::cout << "Start browsing" << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + std::cout << "Creating browser" << std::endl; + AvahiServiceBrowser* browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, (AvahiLookupFlags) 0, &handleServiceDiscoveredStatic, this); + if (!browser) { + std::cout << "Error" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + std::cout << "Unlocking" << std::endl; + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + std::cout << "Browse started" << std::endl; + } + + void stopBrowsing() { + // TODO + } + + private: + static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) { + static_cast<AvahiBrowseQuery*>(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags); + } + + void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) { + switch (event) { + case AVAHI_BROWSER_FAILURE: + std::cout << "Service browse error" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + break; + case AVAHI_BROWSER_NEW: { + DNSSDServiceID service(name, domain, type, interfaceIndex); + std::cout << "Service discovered " << name << " " << type << " " << domain << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); + break; + } + case AVAHI_BROWSER_REMOVE: { + std::cout << "Service went away " << name << " " << type << " " << domain << std::endl; + DNSSDServiceID service(name, domain, type, interfaceIndex); + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); + break; + } + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp new file mode 100644 index 0000000..c4bfcb4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp @@ -0,0 +1,60 @@ +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" + +#include <iostream> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h" + +namespace Swift { + +AvahiQuerier::AvahiQuerier() : client(NULL), threadedPoll(NULL) { +} + +AvahiQuerier::~AvahiQuerier() { +} + +boost::shared_ptr<DNSSDBrowseQuery> AvahiQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new AvahiBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> AvahiQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new AvahiRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> AvahiQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new AvahiResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> AvahiQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void AvahiQuerier::start() { + std::cout << "Starrting querier" << std::endl; + assert(!threadedPoll); + threadedPoll = avahi_threaded_poll_new(); + int error; + assert(!client); + client = avahi_client_new( + avahi_threaded_poll_get(threadedPoll), + static_cast<AvahiClientFlags>(0), NULL, this, &error); // TODO + if (!client) { + // TODO + std::cerr << "Avahi Error: " << avahi_strerror(error) << std::endl; + return; + } + std::cout << "Starrting event loop" << std::endl; + avahi_threaded_poll_start(threadedPoll); +} + +void AvahiQuerier::stop() { + assert(threadedPoll); + avahi_threaded_poll_stop(threadedPoll); + assert(client); + avahi_client_free(client); + avahi_threaded_poll_free(threadedPoll); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h new file mode 100644 index 0000000..ffb8441 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h @@ -0,0 +1,47 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-common/thread-watch.h> +#include <avahi-common/watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" + +namespace Swift { + class ByteArray; + + class AvahiQuerier : + public DNSSDQuerier, + public boost::enable_shared_from_this<AvahiQuerier> { + public: + AvahiQuerier(); + ~AvahiQuerier(); + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void start(); + void stop(); + + AvahiThreadedPoll* getThreadedPoll() const { + return threadedPoll; + } + + AvahiClient* getClient() const { + return client; + } + + private: + AvahiClient* client; + AvahiThreadedPoll* threadedPoll; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp new file mode 100644 index 0000000..6b7c7f9 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp @@ -0,0 +1,13 @@ +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" + +namespace Swift { + +AvahiQuery::AvahiQuery(boost::shared_ptr<AvahiQuerier> q) : querier(q) { +} + +AvahiQuery::~AvahiQuery() { +} + +} + diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h new file mode 100644 index 0000000..847f3f7 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h @@ -0,0 +1,22 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiQuery : + public EventOwner, + public boost::enable_shared_from_this<AvahiQuery> { + public: + AvahiQuery(boost::shared_ptr<AvahiQuerier>); + virtual ~AvahiQuery(); + + protected: + boost::shared_ptr<AvahiQuerier> querier; + }; +} + diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h new file mode 100644 index 0000000..f42950e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h @@ -0,0 +1,123 @@ +#pragma once + +#include <avahi-client/publish.h> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiRegisterQuery : public DNSSDRegisterQuery, public AvahiQuery { + public: + AvahiRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), name(name), port(port), txtRecord(txtRecord), group(0) { + } + + void registerService() { + std::cout << "Registering service " << name << ":" << port << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + if (!group) { + std::cout << "Creating entry group" << std::endl; + group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this); + if (!group) { + std::cout << "Error ceating entry group" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + + doRegisterService(); + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + void unregisterService() { + } + + void updateServiceInfo(const ByteArray& txtRecord) { + this->txtRecord = txtRecord; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + assert(group); + avahi_entry_group_reset(group); + doRegisterService(); + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + private: + void doRegisterService() { + AvahiStringList* txtList; + avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList); + + int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtList); + if (result < 0) { + std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + result = avahi_entry_group_commit(group); + if (result < 0) { + std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; + } + } + + static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + static_cast<AvahiRegisterQuery*>(userdata)->handleEntryGroupChange(g, state); + } + + void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) { + std::cout << "ENtry group callback: " << state << std::endl; + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + // Domain is a hack! + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this()); + std::cout << "Entry group established" << std::endl; + break; + case AVAHI_ENTRY_GROUP_COLLISION : { + std::cout << "Entry group collision" << std::endl; + /*char *n; + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n;*/ + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl; + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + + /* + DNSServiceErrorType result = DNSServiceRegister( + &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, + txtRecord.getSize(), txtRecord.getData(), + &AvahiRegisterQuery::handleServiceRegisteredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + }*/ + //MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + +/* + static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { + static_cast<AvahiRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain); + } + + void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + else { + } + } + */ + + private: + String name; + int port; + ByteArray txtRecord; + AvahiEntryGroup* group; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h new file mode 100644 index 0000000..ee0e837 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" +#include "Swiften/Network/HostAddress.h" + +#include <netinet/in.h> + +namespace Swift { + class AvahiQuerier; + + class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery { + public: + AvahiResolveHostnameQuery(const String& hostname, int, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), hostname(hostname) { + std::cout << "Resolving hostname " << hostname << std::endl; + } + + void run() { + MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this()); + } + + void finish() { + } + + private: + HostAddress hostAddress; + String hostname; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h new file mode 100644 index 0000000..8577837 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h @@ -0,0 +1,73 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiResolveServiceQuery : public DNSSDResolveServiceQuery, public AvahiQuery { + public: + AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), service(service), resolver(NULL) { + } + + void start() { + std::cout << "Resolving " << service.getName() << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + assert(!resolver); + resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().getUTF8Data(), service.getType().getUTF8Data(), service.getDomain().getUTF8Data(), AVAHI_PROTO_UNSPEC, (AvahiLookupFlags) 0, handleServiceResolvedStatic, this); + if (!resolver) { + std::cout << "Error starting resolver" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + void stop() { + avahi_threaded_poll_lock(querier->getThreadedPoll()); + avahi_service_resolver_free(resolver); + resolver = NULL; + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + private: + static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* context) { + static_cast<AvahiResolveServiceQuery*>(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags); + } + + void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) { + std::cout << "Resolve finished" << std::endl; + switch(event) { + case AVAHI_RESOLVER_FAILURE: + std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + break; + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(a, sizeof(a), address); + + ByteArray txtRecord; + txtRecord.resize(1024); + avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize()); + + // FIXME: Probably not accurate + String fullname = String(name) + "." + String(type) + "." + String(domain) + "."; + std::cout << "Result: " << fullname << "->" << String(a) << ":" << port << std::endl; + MainEventLoop::postEvent( + boost::bind( + boost::ref(onServiceResolved), + Result(fullname, String(a), port, txtRecord)), + shared_from_this()); + break; + } + } + } + + private: + DNSSDServiceID service; + AvahiServiceResolver* resolver; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h new file mode 100644 index 0000000..2dec2fb --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourBrowseQuery : public DNSSDBrowseQuery, public BonjourQuery { + public: + BonjourBrowseQuery(boost::shared_ptr<BonjourQuerier> q) : BonjourQuery(q) { + DNSServiceErrorType result = DNSServiceBrowse( + &sdRef, 0, 0, "_presence._tcp", 0, + &BonjourBrowseQuery::handleServiceDiscoveredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void startBrowsing() { + if (!sdRef) { + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + else { + run(); + } + } + + void stopBrowsing() { + finish(); + } + + private: + static void handleServiceDiscoveredStatic(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain, void *context) { + static_cast<BonjourBrowseQuery*>(context)->handleServiceDiscovered(flags, interfaceIndex, errorCode, name, type, domain); + } + + void handleServiceDiscovered(DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + else { + //std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl; + DNSSDServiceID service(name, domain, type, interfaceIndex); + if (flags & kDNSServiceFlagsAdd) { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); + } + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp new file mode 100644 index 0000000..9c9e64e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp @@ -0,0 +1,129 @@ +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" + +#include <unistd.h> +#include <sys/socket.h> +#include <fcntl.h> + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + +BonjourQuerier::BonjourQuerier() : stopRequested(false), thread(0) { + int fds[2]; + int result = pipe(fds); + assert(result == 0); + interruptSelectReadSocket = fds[0]; + fcntl(interruptSelectReadSocket, F_SETFL, fcntl(interruptSelectReadSocket, F_GETFL)|O_NONBLOCK); + interruptSelectWriteSocket = fds[1]; +} + +BonjourQuerier::~BonjourQuerier() { + assert(!thread); +} + +boost::shared_ptr<DNSSDBrowseQuery> BonjourQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new BonjourBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> BonjourQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new BonjourRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> BonjourQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new BonjourResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> BonjourQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new BonjourResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void BonjourQuerier::addRunningQuery(boost::shared_ptr<BonjourQuery> query) { + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + runningQueries.push_back(query); + } + runningQueriesAvailableEvent.notify_one(); + interruptSelect(); +} + +void BonjourQuerier::removeRunningQuery(boost::shared_ptr<BonjourQuery> query) { + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + runningQueries.erase(std::remove( + runningQueries.begin(), runningQueries.end(), query), runningQueries.end()); + } +} + +void BonjourQuerier::interruptSelect() { + char c = 0; + write(interruptSelectWriteSocket, &c, 1); +} + +void BonjourQuerier::start() { + assert(!thread); + thread = new boost::thread(boost::bind(&BonjourQuerier::run, shared_from_this())); +} + +void BonjourQuerier::stop() { + if (thread) { + stopRequested = true; + assert(runningQueries.empty()); + runningQueriesAvailableEvent.notify_one(); + interruptSelect(); + thread->join(); + delete thread; + thread = NULL; + stopRequested = false; + } +} + +void BonjourQuerier::run() { + while (!stopRequested) { + fd_set fdSet; + int maxSocket; + { + boost::unique_lock<boost::mutex> lock(runningQueriesMutex); + if (runningQueries.empty()) { + runningQueriesAvailableEvent.wait(lock); + if (runningQueries.empty()) { + continue; + } + } + + // Run all running queries + FD_ZERO(&fdSet); + maxSocket = interruptSelectReadSocket; + FD_SET(interruptSelectReadSocket, &fdSet); + + foreach(const boost::shared_ptr<BonjourQuery>& query, runningQueries) { + int socketID = query->getSocketID(); + maxSocket = std::max(maxSocket, socketID); + FD_SET(socketID, &fdSet); + } + } + + if (select(maxSocket+1, &fdSet, NULL, NULL, 0) <= 0) { + continue; + } + + if (FD_ISSET(interruptSelectReadSocket, &fdSet)) { + char dummy; + while (read(interruptSelectReadSocket, &dummy, 1) > 0) {} + } + + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + foreach(boost::shared_ptr<BonjourQuery> query, runningQueries) { + if (FD_ISSET(query->getSocketID(), &fdSet)) { + query->processResult(); + } + } + } + } +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h new file mode 100644 index 0000000..d12f94f --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h @@ -0,0 +1,50 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <list> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" + +namespace Swift { + class ByteArray; + + class BonjourQuerier : + public DNSSDQuerier, + public boost::enable_shared_from_this<BonjourQuerier> { + public: + BonjourQuerier(); + ~BonjourQuerier(); + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void start(); + void stop(); + + private: + friend class BonjourQuery; + + void addRunningQuery(boost::shared_ptr<BonjourQuery>); + void removeRunningQuery(boost::shared_ptr<BonjourQuery>); + void interruptSelect(); + void run(); + + private: + bool stopRequested; + boost::thread* thread; + boost::mutex runningQueriesMutex; + std::list< boost::shared_ptr<BonjourQuery> > runningQueries; + int interruptSelectReadSocket; + int interruptSelectWriteSocket; + boost::condition_variable runningQueriesAvailableEvent; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp new file mode 100644 index 0000000..c1c481b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp @@ -0,0 +1,31 @@ +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" + +namespace Swift { + +BonjourQuery::BonjourQuery(boost::shared_ptr<BonjourQuerier> q) : querier(q), sdRef(0) { +} + +BonjourQuery::~BonjourQuery() { + DNSServiceRefDeallocate(sdRef); +} + +void BonjourQuery::processResult() { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + DNSServiceProcessResult(sdRef); +} + +int BonjourQuery::getSocketID() const { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + return DNSServiceRefSockFD(sdRef); +} + +void BonjourQuery::run() { + querier->addRunningQuery(shared_from_this()); +} + +void BonjourQuery::finish() { + querier->removeRunningQuery(shared_from_this()); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h new file mode 100644 index 0000000..bdb91a4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include <dns_sd.h> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread/mutex.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourQuery : + public EventOwner, + public boost::enable_shared_from_this<BonjourQuery> { + public: + BonjourQuery(boost::shared_ptr<BonjourQuerier>); + virtual ~BonjourQuery(); + + void processResult(); + int getSocketID() const; + + protected: + void run(); + void finish(); + + protected: + boost::shared_ptr<BonjourQuerier> querier; + mutable boost::mutex sdRefMutex; + DNSServiceRef sdRef; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h new file mode 100644 index 0000000..ddc2788 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourRegisterQuery : public DNSSDRegisterQuery, public BonjourQuery { + public: + BonjourRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceRegister( + &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, + txtRecord.getSize(), txtRecord.getData(), + &BonjourRegisterQuery::handleServiceRegisteredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void registerService() { + if (sdRef) { + run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + + void unregisterService() { + finish(); + } + + void updateServiceInfo(const ByteArray& txtRecord) { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + DNSServiceUpdateRecord(sdRef, NULL, NULL, txtRecord.getSize(), txtRecord.getData(), 0); + } + + private: + static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { + static_cast<BonjourRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain); + } + + void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, domain, regtype, 0))), shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h new file mode 100644 index 0000000..7b5f19a --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" +#include "Swiften/Network/HostAddress.h" + +#include <netinet/in.h> + +namespace Swift { + class BonjourQuerier; + + class BonjourResolveHostnameQuery : public DNSSDResolveHostnameQuery, public BonjourQuery { + public: + BonjourResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceGetAddrInfo( + &sdRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, + hostname.getUTF8Data(), + &BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + //void DNSSDResolveHostnameQuery::run() { + void run() { + if (sdRef) { + BonjourQuery::run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this()); + } + } + + void finish() { + BonjourQuery::finish(); + } + + private: + static void handleHostnameResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char*, const struct sockaddr *address, uint32_t, void *context) { + static_cast<BonjourResolveHostnameQuery*>(context)->handleHostnameResolved(errorCode, address); + } + + void handleHostnameResolved(DNSServiceErrorType errorCode, const struct sockaddr *rawAddress) { + if (errorCode) { + MainEventLoop::postEvent( + boost::bind(boost::ref(onHostnameResolved), + boost::optional<HostAddress>()), + shared_from_this()); + } + else { + assert(rawAddress->sa_family == AF_INET); + const sockaddr_in* sa = reinterpret_cast<const sockaddr_in*>(rawAddress); + uint32_t address = ntohl(sa->sin_addr.s_addr); + MainEventLoop::postEvent(boost::bind( + boost::ref(onHostnameResolved), + HostAddress(reinterpret_cast<unsigned char*>(&address), 4)), + shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h new file mode 100644 index 0000000..1c38179 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h @@ -0,0 +1,58 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourResolveServiceQuery : public DNSSDResolveServiceQuery, public BonjourQuery { + public: + BonjourResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceResolve( + &sdRef, 0, service.getNetworkInterfaceID(), + service.getName().getUTF8Data(), service.getType().getUTF8Data(), + service.getDomain().getUTF8Data(), + &BonjourResolveServiceQuery::handleServiceResolvedStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void start() { + if (sdRef) { + run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + } + + void stop() { + finish(); + } + + private: + static void handleServiceResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) { + static_cast<BonjourResolveServiceQuery*>(context)->handleServiceResolved(errorCode, fullname, hosttarget, port, txtLen, txtRecord); + } + + void handleServiceResolved(DNSServiceErrorType errorCode, const char* fullName, const char* host, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + else { + //std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl; + MainEventLoop::postEvent( + boost::bind( + boost::ref(onServiceResolved), + Result(String(fullName), String(host), port, + ByteArray(reinterpret_cast<const char*>(txtRecord), txtLen))), + shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp new file mode 100644 index 0000000..1dbff2c --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" + +namespace Swift { + +DNSSDBrowseQuery::~DNSSDBrowseQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h new file mode 100644 index 0000000..9b30858 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h @@ -0,0 +1,19 @@ +#pragma once + +#include <boost/signal.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + class DNSSDBrowseQuery { + public: + virtual ~DNSSDBrowseQuery(); + + virtual void startBrowsing() = 0; + virtual void stopBrowsing() = 0; + + boost::signal<void (const DNSSDServiceID&)> onServiceAdded; + boost::signal<void (const DNSSDServiceID&)> onServiceRemoved; + boost::signal<void ()> onError; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp new file mode 100644 index 0000000..cc8d6ef --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" + +namespace Swift { + +DNSSDQuerier::~DNSSDQuerier() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h new file mode 100644 index 0000000..efcc140 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h @@ -0,0 +1,29 @@ +#pragma once + +#include <boost/shared_ptr.hpp> + +namespace Swift { + class String; + class ByteArray; + class DNSSDServiceID; + class DNSSDBrowseQuery; + class DNSSDRegisterQuery; + class DNSSDResolveServiceQuery; + class DNSSDResolveHostnameQuery; + + class DNSSDQuerier { + public: + virtual ~DNSSDQuerier(); + + virtual void start() = 0; + virtual void stop() = 0; + + virtual boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery() = 0; + virtual boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info) = 0; + virtual boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&) = 0; + virtual boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex) = 0; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp new file mode 100644 index 0000000..bbb8692 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" + +namespace Swift { + +DNSSDRegisterQuery::~DNSSDRegisterQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h new file mode 100644 index 0000000..4a04fa9 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h @@ -0,0 +1,21 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + class ByteArray; + + class DNSSDRegisterQuery { + public: + virtual ~DNSSDRegisterQuery(); + + virtual void registerService() = 0; + virtual void unregisterService() = 0; + virtual void updateServiceInfo(const ByteArray& info) = 0; + + boost::signal<void (boost::optional<DNSSDServiceID>)> onRegisterFinished; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp new file mode 100644 index 0000000..e247e39 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" + +namespace Swift { + +DNSSDResolveHostnameQuery::~DNSSDResolveHostnameQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h new file mode 100644 index 0000000..1b9f291 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h @@ -0,0 +1,18 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class DNSSDResolveHostnameQuery { + public: + virtual ~DNSSDResolveHostnameQuery(); + + virtual void run() = 0; + virtual void finish() = 0; + + boost::signal<void (const boost::optional<HostAddress>&)> onHostnameResolved; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp new file mode 100644 index 0000000..67a5d66 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" + +namespace Swift { + +DNSSDResolveServiceQuery::~DNSSDResolveServiceQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h new file mode 100644 index 0000000..217e396 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h @@ -0,0 +1,28 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/Base/ByteArray.h" + +namespace Swift { + class DNSSDResolveServiceQuery { + public: + struct Result { + Result(const String& fullName, const String& host, int port, const ByteArray& info) : + fullName(fullName), host(host), port(port), info(info) {} + String fullName; + String host; + int port; + ByteArray info; + }; + + virtual ~DNSSDResolveServiceQuery(); + + virtual void start() = 0; + virtual void stop() = 0; + + boost::signal<void (const boost::optional<Result>&)> onServiceResolved; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp new file mode 100644 index 0000000..b360ee5 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp @@ -0,0 +1,7 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + +const char* DNSSDServiceID::PresenceServiceType = "_presence._tcp"; + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h new file mode 100644 index 0000000..ba7828b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Swiften/Base/String.h" + +namespace Swift { + class DNSSDServiceID { + public: + static const char* PresenceServiceType; + + DNSSDServiceID( + const String& name, + const String& domain, + const String& type = PresenceServiceType, + int networkInterface = 0) : + name(name), + domain(domain), + type(type), + networkInterface(networkInterface) { + } + + bool operator==(const DNSSDServiceID& o) const { + return name == o.name && domain == o.domain && type == o.type && (networkInterface != 0 && o.networkInterface != 0 ? networkInterface == o.networkInterface : true); + } + + bool operator<(const DNSSDServiceID& o) const { + if (o.name == name) { + if (o.domain == domain) { + if (o.type == type) { + return networkInterface < o.networkInterface; + } + else { + return type < o.type; + } + } + else { + return domain < o.domain; + } + } + else { + return o.name < name; + } + } + + const String& getName() const { + return name; + } + + const String& getDomain() const { + return domain; + } + + const String& getType() const { + return type; + } + + int getNetworkInterfaceID() const { + return networkInterface; + } + + private: + String name; + String domain; + String type; + int networkInterface; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h new file mode 100644 index 0000000..5a0b93b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDBrowseQuery : public DNSSDBrowseQuery, public FakeDNSSDQuery { + public: + FakeDNSSDBrowseQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier) { + } + + void startBrowsing() { + run(); + } + + void stopBrowsing() { + finish(); + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp new file mode 100644 index 0000000..c26f8ee --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp @@ -0,0 +1,130 @@ +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" + +#include <boost/bind.hpp> + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + +FakeDNSSDQuerier::FakeDNSSDQuerier(const String& domain) : domain(domain) { +} + +FakeDNSSDQuerier::~FakeDNSSDQuerier() { + if (!runningQueries.empty()) { + std::cerr << "FakeDNSSDQuerier: Running queries not empty at destruction time" << std::endl; + } +} + +boost::shared_ptr<DNSSDBrowseQuery> FakeDNSSDQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new FakeDNSSDBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> FakeDNSSDQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new FakeDNSSDRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> FakeDNSSDQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new FakeDNSSDResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> FakeDNSSDQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new FakeDNSSDResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) { + runningQueries.push_back(query); + if (boost::shared_ptr<FakeDNSSDBrowseQuery> browseQuery = boost::dynamic_pointer_cast<FakeDNSSDBrowseQuery>(query)) { + foreach(const DNSSDServiceID& service, services) { + MainEventLoop::postEvent(boost::bind(boost::ref(browseQuery->onServiceAdded), service), shared_from_this()); + } + } + else if (boost::shared_ptr<FakeDNSSDResolveServiceQuery> resolveQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveServiceQuery>(query)) { + for(ServiceInfoMap::const_iterator i = serviceInfo.begin(); i != serviceInfo.end(); ++i) { + if (i->first == resolveQuery->service) { + MainEventLoop::postEvent(boost::bind(boost::ref(resolveQuery->onServiceResolved), i->second), shared_from_this()); + } + } + } + else if (boost::shared_ptr<FakeDNSSDRegisterQuery> registerQuery = boost::dynamic_pointer_cast<FakeDNSSDRegisterQuery>(query)) { + DNSSDServiceID service(registerQuery->name, domain); + MainEventLoop::postEvent(boost::bind(boost::ref(registerQuery->onRegisterFinished), service), shared_from_this()); + } + else if (boost::shared_ptr<FakeDNSSDResolveHostnameQuery> resolveHostnameQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveHostnameQuery>(query)) { + std::map<String,boost::optional<HostAddress> >::const_iterator i = addresses.find(resolveHostnameQuery->hostname); + if (i != addresses.end()) { + MainEventLoop::postEvent( + boost::bind( + boost::ref(resolveHostnameQuery->onHostnameResolved), i->second), + shared_from_this()); + } + } +} + +void FakeDNSSDQuerier::removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) { + runningQueries.erase(std::remove( + runningQueries.begin(), runningQueries.end(), query), runningQueries.end()); +} + +void FakeDNSSDQuerier::addService(const DNSSDServiceID& id) { + services.insert(id); + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceAdded), id), shared_from_this()); + } +} + +void FakeDNSSDQuerier::removeService(const DNSSDServiceID& id) { + services.erase(id); + serviceInfo.erase(id); + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceRemoved), id), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) { + std::pair<ServiceInfoMap::iterator, bool> r = serviceInfo.insert(std::make_pair(id, info)); + if (!r.second) { + r.first->second = info; + } + foreach(const boost::shared_ptr<FakeDNSSDResolveServiceQuery>& query, getQueries<FakeDNSSDResolveServiceQuery>()) { + if (query->service == id) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceResolved), info), shared_from_this()); + } + } +} + +bool FakeDNSSDQuerier::isServiceRegistered(const String& name, int port, const ByteArray& info) { + foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) { + if (query->name == name && query->port == port && query->info == info) { + return true; + } + } + return false; +} + +void FakeDNSSDQuerier::setBrowseError() { + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::ref(query->onError), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setRegisterError() { + foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setAddress(const String& hostname, boost::optional<HostAddress> address) { + addresses[hostname] = address; + foreach(const boost::shared_ptr<FakeDNSSDResolveHostnameQuery>& query, getQueries<FakeDNSSDResolveHostnameQuery>()) { + if (query->hostname == hostname) { + MainEventLoop::postEvent(boost::bind( + boost::ref(query->onHostnameResolved), address), shared_from_this()); + } + } +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h new file mode 100644 index 0000000..22bca0c --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h @@ -0,0 +1,71 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <list> +#include <set> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Base/String.h" +#include "Swiften/EventLoop/EventOwner.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class ByteArray; + class FakeDNSSDQuery; + class FakeDNSSDBrowseQuery; + + class FakeDNSSDQuerier : + public DNSSDQuerier, + public EventOwner, + public boost::enable_shared_from_this<FakeDNSSDQuerier> { + public: + FakeDNSSDQuerier(const String& domain); + ~FakeDNSSDQuerier(); + + void start() {} + void stop() {} + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void addRunningQuery(boost::shared_ptr<FakeDNSSDQuery>); + void removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery>); + + void addService(const DNSSDServiceID& id); + void removeService(const DNSSDServiceID& id); + void setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info); + bool isServiceRegistered(const String& name, int port, const ByteArray& info); + void setAddress(const String& hostname, boost::optional<HostAddress> address); + + void setBrowseError(); + void setRegisterError(); + + private: + template<typename T> + std::vector< boost::shared_ptr<T> > getQueries() const { + std::vector< boost::shared_ptr<T> > result; + foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, runningQueries) { + if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) { + result.push_back(resultQuery); + } + } + return result; + } + + private: + String domain; + std::list< boost::shared_ptr<FakeDNSSDQuery> > runningQueries; + std::set<DNSSDServiceID> services; + typedef std::map<DNSSDServiceID,DNSSDResolveServiceQuery::Result> ServiceInfoMap; + ServiceInfoMap serviceInfo; + std::map<String, boost::optional<HostAddress> > addresses; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp new file mode 100644 index 0000000..ced7850 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp @@ -0,0 +1,20 @@ +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" + +namespace Swift { + +FakeDNSSDQuery::FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : querier(querier) { +} + +FakeDNSSDQuery::~FakeDNSSDQuery() { +} + +void FakeDNSSDQuery::run() { + querier->addRunningQuery(shared_from_this()); +} + +void FakeDNSSDQuery::finish() { + querier->removeRunningQuery(shared_from_this()); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h new file mode 100644 index 0000000..9fca1d4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h @@ -0,0 +1,25 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDQuery : + public EventOwner, + public boost::enable_shared_from_this<FakeDNSSDQuery> { + public: + FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier>); + virtual ~FakeDNSSDQuery(); + + protected: + void run(); + void finish(); + + protected: + boost::shared_ptr<FakeDNSSDQuerier> querier; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h new file mode 100644 index 0000000..82ec623 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Base/String.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDRegisterQuery : public DNSSDRegisterQuery, public FakeDNSSDQuery { + public: + FakeDNSSDRegisterQuery(const String& name, int port, const ByteArray& info, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), name(name), port(port), info(info) { + } + + void registerService() { + run(); + } + + void updateServiceInfo(const ByteArray& i) { + info = i; + } + + void unregisterService() { + finish(); + } + + String name; + int port; + ByteArray info; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h new file mode 100644 index 0000000..5ed42af --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDResolveHostnameQuery : public DNSSDResolveHostnameQuery, public FakeDNSSDQuery { + public: + FakeDNSSDResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), hostname(hostname), interfaceIndex(interfaceIndex) { + } + + void run() { + FakeDNSSDQuery::run(); + } + + void finish() { + FakeDNSSDQuery::finish(); + } + + String hostname; + int interfaceIndex; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h new file mode 100644 index 0000000..60d35e5 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDResolveServiceQuery : public DNSSDResolveServiceQuery, public FakeDNSSDQuery { + public: + FakeDNSSDResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), service(service) { + } + + void start() { + run(); + } + + void stop() { + finish(); + } + + DNSSDServiceID service; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp new file mode 100644 index 0000000..7b06350 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp @@ -0,0 +1,22 @@ +#include "Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h" + +#if defined(HAVE_BONJOUR) +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" +#elif defined(HAVE_AVAHI) +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" +#endif + + +namespace Swift { + +boost::shared_ptr<DNSSDQuerier> PlatformDNSSDQuerierFactory::createQuerier() { +#if defined(HAVE_BONJOUR) + return boost::shared_ptr<DNSSDQuerier>(new BonjourQuerier()); +#elif defined(HAVE_AVAHI) + return boost::shared_ptr<DNSSDQuerier>(new AvahiQuerier()); +#else + return boost::shared_ptr<DNSSDQuerier>(); +#endif +} + +} diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h new file mode 100644 index 0000000..b52814b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include <boost/shared_ptr.hpp> + +namespace Swift { + class DNSSDQuerier; + + class PlatformDNSSDQuerierFactory { + public: + boost::shared_ptr<DNSSDQuerier> createQuerier(); + }; +} |