diff options
author | Remko Tronçon <git@el-tramo.be> | 2009-12-04 21:49:44 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2009-12-04 23:52:36 (GMT) |
commit | 897ad55ffee76c9e84ffb174d700f6182a3e7d0b (patch) | |
tree | f31e6bd601cf5f1483a3f4ad5e79a492d3a9ca3e /Swiften/Network | |
parent | c8802e3bef6b73d349a711b187c72aea0a3dd964 (diff) | |
download | swift-897ad55ffee76c9e84ffb174d700f6182a3e7d0b.zip swift-897ad55ffee76c9e84ffb174d700f6182a3e7d0b.tar.bz2 |
Implemented CAresDomainNameResolver.
Diffstat (limited to 'Swiften/Network')
-rw-r--r-- | Swiften/Network/CAresDomainNameResolver.cpp | 159 | ||||
-rw-r--r-- | Swiften/Network/CAresDomainNameResolver.h | 34 | ||||
-rw-r--r-- | Swiften/Network/DomainNameResolver.cpp | 14 | ||||
-rw-r--r-- | Swiften/Network/DomainNameResolver.h | 5 | ||||
-rw-r--r-- | Swiften/Network/DomainNameServiceQuery.h | 6 | ||||
-rw-r--r-- | Swiften/Network/PlatformDomainNameResolver.cpp | 12 | ||||
-rw-r--r-- | Swiften/Network/PlatformDomainNameServiceQuery.cpp | 10 | ||||
-rw-r--r-- | Swiften/Network/SConscript | 4 |
8 files changed, 222 insertions, 22 deletions
diff --git a/Swiften/Network/CAresDomainNameResolver.cpp b/Swiften/Network/CAresDomainNameResolver.cpp new file mode 100644 index 0000000..6daba3d --- /dev/null +++ b/Swiften/Network/CAresDomainNameResolver.cpp @@ -0,0 +1,159 @@ +// TODO: Check the second param of postEvent. We sometimes omit it. Same +// goes for the PlatformDomainNameResolver. + +#include "Swiften/Network/CAresDomainNameResolver.h" + +#include <netdb.h> +#include <arpa/inet.h> +#include <algorithm> + +#include "Swiften/Network/DomainNameServiceQuery.h" +#include "Swiften/Network/DomainNameAddressQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + +class CAresQuery : public boost::enable_shared_from_this<CAresQuery>, public EventOwner { + public: + CAresQuery(const String& query, int dnsclass, int type, CAresDomainNameResolver* resolver) : query(query), dnsclass(dnsclass), type(type), resolver(resolver) { + } + + virtual ~CAresQuery() { + } + + void addToQueue() { + resolver->addToQueue(shared_from_this()); + } + + void doRun(ares_channel* channel) { + ares_query(*channel, query.getUTF8Data(), dnsclass, type, &CAresQuery::handleResult, this); + } + + static void handleResult(void* arg, int status, int timeouts, unsigned char* buffer, int len) { + reinterpret_cast<CAresQuery*>(arg)->handleResult(status, timeouts, buffer, len); + } + + virtual void handleResult(int status, int, unsigned char* buffer, int len) = 0; + + private: + String query; + int dnsclass; + int type; + CAresDomainNameResolver* resolver; +}; + +class CAresDomainNameServiceQuery : public DomainNameServiceQuery, public CAresQuery { + public: + CAresDomainNameServiceQuery(const String& service, CAresDomainNameResolver* resolver) : CAresQuery(service, 1, 33, resolver) { + } + + virtual void run() { + addToQueue(); + } + + void handleResult(int status, int, unsigned char* buffer, int len) { + if (status == ARES_SUCCESS) { + std::vector<DomainNameServiceQuery::Result> records; + ares_srv_reply* rawRecords; + if (ares_parse_srv_reply(buffer, len, &rawRecords) == ARES_SUCCESS) { + for( ; rawRecords != NULL; rawRecords = rawRecords->next) { + DomainNameServiceQuery::Result record; + record.priority = rawRecords->priority; + record.weight = rawRecords->weight; + record.port = rawRecords->port; + record.hostname = String(rawRecords->host); + records.push_back(record); + } + } + std::sort(records.begin(), records.end(), ResultPriorityComparator()); + MainEventLoop::postEvent(boost::bind(boost::ref(onResult), records)); + } + else if (status != ARES_EDESTRUCTION) { + MainEventLoop::postEvent(boost::bind(boost::ref(onResult), std::vector<DomainNameServiceQuery::Result>()), shared_from_this()); + } + } +}; + +class CAresDomainNameAddressQuery : public DomainNameAddressQuery, public CAresQuery { + public: + CAresDomainNameAddressQuery(const String& host, CAresDomainNameResolver* resolver) : CAresQuery(host, 1, 1, resolver) { + } + + virtual void run() { + addToQueue(); + } + + void handleResult(int status, int, unsigned char* buffer, int len) { + if (status == ARES_SUCCESS) { + struct hostent* hosts; + if (ares_parse_a_reply(buffer, len, &hosts, NULL, NULL) == ARES_SUCCESS) { + // Check whether the different fields are what we expect them to be + struct in_addr addr; + addr.s_addr = *(unsigned int*)hosts->h_addr_list[0]; + HostAddress result(inet_ntoa(addr)); + MainEventLoop::postEvent(boost::bind(boost::ref(onResult), result, boost::optional<DomainNameResolveError>()), boost::dynamic_pointer_cast<CAresDomainNameAddressQuery>(shared_from_this())); + ares_free_hostent(hosts); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this()); + } + } + else if (status != ARES_EDESTRUCTION) { + MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this()); + } + } +}; + +CAresDomainNameResolver::CAresDomainNameResolver() : stopRequested(false) { + ares_init(&channel); + thread = new boost::thread(boost::bind(&CAresDomainNameResolver::run, this)); +} + +CAresDomainNameResolver::~CAresDomainNameResolver() { + stopRequested = true; + thread->join(); + ares_destroy(channel); +} + +boost::shared_ptr<DomainNameServiceQuery> CAresDomainNameResolver::createServiceQuery(const String& name) { + return boost::shared_ptr<DomainNameServiceQuery>(new CAresDomainNameServiceQuery(getNormalized(name), this)); +} + +boost::shared_ptr<DomainNameAddressQuery> CAresDomainNameResolver::createAddressQuery(const String& name) { + return boost::shared_ptr<DomainNameAddressQuery>(new CAresDomainNameAddressQuery(getNormalized(name), this)); +} + +void CAresDomainNameResolver::addToQueue(boost::shared_ptr<CAresQuery> query) { + boost::lock_guard<boost::mutex> lock(pendingQueriesMutex); + pendingQueries.push_back(query); +} + +void CAresDomainNameResolver::run() { + fd_set readers, writers; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + while(!stopRequested) { + { + boost::unique_lock<boost::mutex> lock(pendingQueriesMutex); + foreach(const boost::shared_ptr<CAresQuery>& query, pendingQueries) { + query->doRun(&channel); + } + pendingQueries.clear(); + } + FD_ZERO(&readers); + FD_ZERO(&writers); + int nfds = ares_fds(channel, &readers, &writers); + //if (nfds) { + // break; + //} + struct timeval tv; + struct timeval* tvp = ares_timeout(channel, &timeout, &tv); + select(nfds, &readers, &writers, NULL, tvp); + ares_process(channel, &readers, &writers); + } +} + +} diff --git a/Swiften/Network/CAresDomainNameResolver.h b/Swiften/Network/CAresDomainNameResolver.h new file mode 100644 index 0000000..0cdd163 --- /dev/null +++ b/Swiften/Network/CAresDomainNameResolver.h @@ -0,0 +1,34 @@ +#pragma once + +#include <ares.h> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <list> + +#include "Swiften/Network/DomainNameResolver.h" + +namespace Swift { + class CAresQuery; + + class CAresDomainNameResolver : public DomainNameResolver { + public: + CAresDomainNameResolver(); + ~CAresDomainNameResolver(); + + virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name); + virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name); + + private: + friend class CAresQuery; + + void run(); + void addToQueue(boost::shared_ptr<CAresQuery>); + + private: + bool stopRequested; + ares_channel channel; + boost::thread* thread; + boost::mutex pendingQueriesMutex; + std::list< boost::shared_ptr<CAresQuery> > pendingQueries; + }; +} diff --git a/Swiften/Network/DomainNameResolver.cpp b/Swiften/Network/DomainNameResolver.cpp index 907dfc9..63ed881 100644 --- a/Swiften/Network/DomainNameResolver.cpp +++ b/Swiften/Network/DomainNameResolver.cpp @@ -1,8 +1,22 @@ #include "Swiften/Network/DomainNameResolver.h" +#include <idna.h> + namespace Swift { DomainNameResolver::~DomainNameResolver() { } +String DomainNameResolver::getNormalized(const String& domain) { + char* output; + if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) { + String result(output); + free(output); + return result; + } + else { + return domain; + } +} + } diff --git a/Swiften/Network/DomainNameResolver.h b/Swiften/Network/DomainNameResolver.h index b99ace3..d3dab26 100644 --- a/Swiften/Network/DomainNameResolver.h +++ b/Swiften/Network/DomainNameResolver.h @@ -1,17 +1,22 @@ #pragma once #include <boost/shared_ptr.hpp> +#include "Swiften/Base/String.h" + namespace Swift { class DomainNameServiceQuery; class DomainNameAddressQuery; class String; class DomainNameResolver { public: virtual ~DomainNameResolver(); virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name) = 0; virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name) = 0; + + protected: + static String getNormalized(const String& domain); }; } diff --git a/Swiften/Network/DomainNameServiceQuery.h b/Swiften/Network/DomainNameServiceQuery.h index 3c08749..57e48d3 100644 --- a/Swiften/Network/DomainNameServiceQuery.h +++ b/Swiften/Network/DomainNameServiceQuery.h @@ -1,27 +1,33 @@ #pragma once #include <boost/signals.hpp> #include <boost/optional.hpp> #include <vector> #include "Swiften/Base/String.h" #include "Swiften/Network/DomainNameResolveError.h" namespace Swift { class DomainNameServiceQuery { public: struct Result { Result(const String& hostname = "", int port = -1, int priority = -1, int weight = -1) : hostname(hostname), port(port), priority(priority), weight(weight) {} String hostname; int port; int priority; int weight; }; + struct ResultPriorityComparator { + bool operator()(const DomainNameServiceQuery::Result& a, const DomainNameServiceQuery::Result& b) const { + return a.priority < b.priority; + } + }; + virtual ~DomainNameServiceQuery(); virtual void run() = 0; boost::signal<void (const std::vector<Result>&)> onResult; }; } diff --git a/Swiften/Network/PlatformDomainNameResolver.cpp b/Swiften/Network/PlatformDomainNameResolver.cpp index 755b177..5ffa2fb 100644 --- a/Swiften/Network/PlatformDomainNameResolver.cpp +++ b/Swiften/Network/PlatformDomainNameResolver.cpp @@ -1,106 +1,94 @@ #include "Swiften/Network/PlatformDomainNameResolver.h" // Putting this early on, because some system types conflict with thread #include "Swiften/Network/PlatformDomainNameServiceQuery.h" #include <string> #include <vector> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/thread.hpp> #include <boost/enable_shared_from_this.hpp> -#include <idna.h> #include <algorithm> #include "Swiften/Base/String.h" #include "Swiften/Network/HostAddress.h" #include "Swiften/EventLoop/MainEventLoop.h" #include "Swiften/Network/HostAddressPort.h" #include "Swiften/Network/DomainNameAddressQuery.h" using namespace Swift; namespace { struct AddressQuery : public DomainNameAddressQuery, public boost::enable_shared_from_this<AddressQuery>, public EventOwner { AddressQuery(const String& host) : hostname(host), thread(NULL), safeToJoin(false) {} ~AddressQuery() { if (safeToJoin) { thread->join(); } else { // FIXME: UGLYYYYY } delete thread; } void run() { safeToJoin = false; thread = new boost::thread(boost::bind(&AddressQuery::doRun, shared_from_this())); } void doRun() { std::cout << "PlatformDomainNameResolver::doRun()" << std::endl; boost::asio::ip::tcp::resolver resolver(ioService); boost::asio::ip::tcp::resolver::query query(hostname.getUTF8String(), "5222"); try { std::cout << "PlatformDomainNameResolver::doRun(): Resolving" << std::endl; boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query); std::cout << "PlatformDomainNameResolver::doRun(): Resolved" << std::endl; if (endpointIterator == boost::asio::ip::tcp::resolver::iterator()) { std::cout << "PlatformDomainNameResolver::doRun(): Error 1" << std::endl; emitError(); } else { boost::asio::ip::address address = (*endpointIterator).endpoint().address(); HostAddress result = (address.is_v4() ? HostAddress(&address.to_v4().to_bytes()[0], 4) : HostAddress(&address.to_v6().to_bytes()[0], 16)); std::cout << "PlatformDomainNameResolver::doRun(): Success" << std::endl; MainEventLoop::postEvent( boost::bind(boost::ref(onResult), result, boost::optional<DomainNameResolveError>()), shared_from_this()); } } catch (...) { std::cout << "PlatformDomainNameResolver::doRun(): Error 2" << std::endl; emitError(); } safeToJoin = true; } void emitError() { MainEventLoop::postEvent(boost::bind(boost::ref(onResult), HostAddress(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), shared_from_this()); } boost::asio::io_service ioService; String hostname; boost::thread* thread; bool safeToJoin; }; - String getNormalized(const String& domain) { - char* output; - if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) { - String result(output); - free(output); - return result; - } - else { - return domain; - } - } } namespace Swift { PlatformDomainNameResolver::PlatformDomainNameResolver() { } boost::shared_ptr<DomainNameServiceQuery> PlatformDomainNameResolver::createServiceQuery(const String& name) { return boost::shared_ptr<DomainNameServiceQuery>(new PlatformDomainNameServiceQuery(getNormalized(name))); } boost::shared_ptr<DomainNameAddressQuery> PlatformDomainNameResolver::createAddressQuery(const String& name) { return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(getNormalized(name))); } } diff --git a/Swiften/Network/PlatformDomainNameServiceQuery.cpp b/Swiften/Network/PlatformDomainNameServiceQuery.cpp index d6c87dc..659f397 100644 --- a/Swiften/Network/PlatformDomainNameServiceQuery.cpp +++ b/Swiften/Network/PlatformDomainNameServiceQuery.cpp @@ -1,80 +1,72 @@ #include "Swiften/Network/PlatformDomainNameServiceQuery.h" #include "Swiften/Base/Platform.h" #include <stdlib.h> #ifdef SWIFTEN_PLATFORM_WINDOWS #undef UNICODE #include <windows.h> #include <windns.h> #ifndef DNS_TYPE_SRV #define DNS_TYPE_SRV 33 #endif #else #include <arpa/nameser.h> #include <arpa/nameser_compat.h> #include <resolv.h> #endif #include <boost/bind.hpp> #include "Swiften/Base/ByteArray.h" #include "Swiften/EventLoop/MainEventLoop.h" #include "Swiften/Base/foreach.h" using namespace Swift; -namespace { - struct SRVRecordPriorityComparator { - bool operator()(const DomainNameServiceQuery::Result& a, const DomainNameServiceQuery::Result& b) const { - return a.priority < b.priority; - } - }; -} - namespace Swift { PlatformDomainNameServiceQuery::PlatformDomainNameServiceQuery(const String& service) : thread(NULL), service(service), safeToJoin(true) { } PlatformDomainNameServiceQuery::~PlatformDomainNameServiceQuery() { if (safeToJoin) { thread->join(); } else { // FIXME: UGLYYYYY } delete thread; } void PlatformDomainNameServiceQuery::run() { safeToJoin = false; thread = new boost::thread(boost::bind(&PlatformDomainNameServiceQuery::doRun, shared_from_this())); } void PlatformDomainNameServiceQuery::doRun() { std::vector<DomainNameServiceQuery::Result> records; #if defined(SWIFTEN_PLATFORM_WINDOWS) DNS_RECORD* responses; // FIXME: This conversion doesn't work if unicode is deffed above if (DnsQuery(service.getUTF8Data(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) { emitError(); return; } DNS_RECORD* currentEntry = responses; while (currentEntry) { if (currentEntry->wType == DNS_TYPE_SRV) { DomainNameServiceQuery::Result record; record.priority = currentEntry->Data.SRV.wPriority; record.weight = currentEntry->Data.SRV.wWeight; record.port = currentEntry->Data.SRV.wPort; // The pNameTarget is actually a PCWSTR, so I would have expected this // conversion to not work at all, but it does. // Actually, it doesn't. Fix this and remove explicit cast // Remove unicode undef above as well record.hostname = String((const char*) currentEntry->Data.SRV.pNameTarget); records.push_back(record); } currentEntry = currentEntry->pNext; } @@ -120,59 +112,59 @@ void PlatformDomainNameServiceQuery::doRun() { int entryLength = dn_skipname(currentEntry, messageEnd); currentEntry += entryLength; currentEntry += NS_RRFIXEDSZ; // Priority if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.priority = ns_get16(currentEntry); currentEntry += 2; // Weight if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.weight = ns_get16(currentEntry); currentEntry += 2; // Port if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.port = ns_get16(currentEntry); currentEntry += 2; // Hostname if (currentEntry >= messageEnd) { emitError(); return; } ByteArray entry; entry.resize(NS_MAXDNAME); entryLength = dn_expand(messageStart, messageEnd, currentEntry, entry.getData(), entry.getSize()); if (entryLength < 0) { emitError(); return; } record.hostname = String(entry.getData()); records.push_back(record); currentEntry += entryLength; answersCount--; } #endif safeToJoin = true; - std::sort(records.begin(), records.end(), SRVRecordPriorityComparator()); + std::sort(records.begin(), records.end(), ResultPriorityComparator()); std::cout << "Sending out " << records.size() << " SRV results " << std::endl; MainEventLoop::postEvent(boost::bind(boost::ref(onResult), records)); } void PlatformDomainNameServiceQuery::emitError() { safeToJoin = true; MainEventLoop::postEvent(boost::bind(boost::ref(onResult), std::vector<DomainNameServiceQuery::Result>()), shared_from_this()); } } diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript index 9aa8139..d63b673 100644 --- a/Swiften/Network/SConscript +++ b/Swiften/Network/SConscript @@ -1,27 +1,29 @@ Import("swiften_env") myenv = swiften_env.Clone() -myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"]) +myenv.MergeFlags(myenv["LIBIDN_FLAGS"]) +myenv.MergeFlags(myenv["CARES_FLAGS"]) objects = myenv.StaticObject([ "BoostConnection.cpp", "BoostConnectionFactory.cpp", "BoostConnectionServer.cpp", "MainBoostIOServiceThread.cpp", "BoostIOServiceThread.cpp", "ConnectionFactory.cpp", "ConnectionServer.cpp", "Connector.cpp", "TimerFactory.cpp", "BoostTimerFactory.cpp", "DomainNameResolver.cpp", "DomainNameAddressQuery.cpp", "DomainNameServiceQuery.cpp", "PlatformDomainNameResolver.cpp", "PlatformDomainNameServiceQuery.cpp", + "CAresDomainNameResolver.cpp", "StaticDomainNameResolver.cpp", "HostAddress.cpp", "Timer.cpp", "BoostTimer.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) |