diff options
Diffstat (limited to 'Swiften/Network/CAresDomainNameResolver.cpp')
-rw-r--r-- | Swiften/Network/CAresDomainNameResolver.cpp | 159 |
1 files changed, 159 insertions, 0 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); + } +} + +} |