summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Network/CAresDomainNameResolver.cpp')
-rw-r--r--Swiften/Network/CAresDomainNameResolver.cpp159
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);
+ }
+}
+
+}