Permalink
Browse files

Add asychronous DNS resolver based on c-ares library.

TODO: timer and multiple DNS servers.
  • Loading branch information...
1 parent df18a8c commit 415a31e98f7b3b7a5c84294b7acebb10b10bc833 @chenshuo committed Mar 8, 2012
Showing with 263 additions and 1 deletion.
  1. +2 −0 CMakeLists.txt
  2. +0 −1 TODO
  3. +3 −0 examples/CMakeLists.txt
  4. +7 −0 examples/cdns/CMakeLists.txt
  5. +150 −0 examples/cdns/Resolver.cc
  6. +74 −0 examples/cdns/Resolver.h
  7. +27 −0 examples/cdns/dns.cc
View
@@ -38,6 +38,8 @@ set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
find_package(Boost REQUIRED)
find_package(Protobuf)
find_path(PROTOBUF_PLUGIN google/protobuf/compiler/cpp/cpp_generator.h)
+find_path(CARES_INCLUDE_DIR ares.h)
+find_library(CARES_LIBRARY NAMES cares)
include_directories(${Boost_INCLUDE_DIRS})
View
@@ -1,6 +1,5 @@
Always processing timer before IO
Support string and line protocol
-Add Resolver
Add Benchmark
Support signal handling
@@ -14,6 +14,9 @@ add_subdirectory(filetransfer)
add_subdirectory(socks4a)
add_subdirectory(shorturl)
add_subdirectory(sudoku)
+if(CARES_INCLUDE_DIR AND CARES_LIBRARY)
+ add_subdirectory(cdns)
+endif()
add_subdirectory(asio/tutorial)
add_subdirectory(asio/chat)
@@ -0,0 +1,7 @@
+add_library(muduo_cdns Resolver.cc)
+target_link_libraries(muduo_cdns muduo_net)
+target_link_libraries(muduo_cdns cares)
+
+add_executable(cdns dns.cc)
+target_link_libraries(cdns muduo_cdns)
+
@@ -0,0 +1,150 @@
+#include <examples/cdns/Resolver.h>
+
+#include <muduo/net/Channel.h>
+#include <muduo/net/EventLoop.h>
+
+#include <boost/bind.hpp>
+
+#include <ares.h>
+#include <netdb.h>
+#include <arpa/inet.h> // inet_ntop
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+using namespace muduo;
+using namespace muduo::net;
+using namespace cdns;
+
+Resolver::Resolver(EventLoop* loop, Option opt)
+ : loop_(loop),
+ ctx_(NULL)
+{
+ static char lookups[] = "b";
+ struct ares_options options;
+ int optmask = ARES_OPT_FLAGS;
+ options.flags = ARES_FLAG_NOCHECKRESP;
+ options.flags |= ARES_FLAG_STAYOPEN;
+ options.flags |= ARES_FLAG_IGNTC; // UDP only
+ optmask |= ARES_OPT_SOCK_STATE_CB;
+ options.sock_state_cb = &Resolver::ares_sock_state_callback;
+ options.sock_state_cb_data = this;
+ if (opt == kDNSonly)
+ {
+ optmask |= ARES_OPT_LOOKUPS;
+ options.lookups = lookups;
+ }
+
+ int status = ares_init_options(&ctx_, &options, optmask);
+ if (status != ARES_SUCCESS)
+ {
+ assert(0);
+ }
+ ares_set_socket_callback(ctx_, &Resolver::ares_sock_create_callback, this);
+}
+
+Resolver::~Resolver()
+{
+ ares_destroy(ctx_);
+}
+
+bool Resolver::resolve(const StringPiece& hostname, const Callback& cb)
+{
+ loop_->assertInLoopThread();
+ printf("resolve %s\n", hostname.data());
+ QueryData* queryData = new QueryData(this, cb);
+ ares_gethostbyname(ctx_, hostname.data(), AF_INET,
+ &Resolver::ares_host_callback, queryData);
+ struct timeval tv;
+ struct timeval* tvp = ares_timeout(ctx_, NULL, &tv);
+ // FIXME timer
+ printf("timeout %ld.%06ld\n", tvp->tv_sec, tv.tv_usec);
+ return queryData != NULL;
+}
+
+void Resolver::onRead(int sockfd, Timestamp t)
+{
+ printf("onRead %d\n", sockfd);
+ ares_process_fd(ctx_, sockfd, ARES_SOCKET_BAD);
+}
+
+void Resolver::onQueryResult(int status, struct hostent* result, const Callback& callback)
+{
+ printf("onQueryResult %p %d\n", result, status);
+ struct sockaddr_in addr;
+ bzero(&addr, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ if (result)
+ {
+ printf("h_name %s\n", result->h_name);
+ for (char** alias = result->h_aliases; *alias != NULL; ++alias)
+ {
+ printf("alias: %s\n", *alias);
+ }
+ // printf("ttl %d\n", ttl);
+ // printf("h_length %d\n", result->h_length);
+ for (char** haddr = result->h_addr_list; *haddr != NULL; ++haddr)
+ {
+ char buf[32];
+ inet_ntop(AF_INET, *haddr, buf, sizeof buf);
+ printf(" %s\n", buf);
+ }
+ addr.sin_addr = *reinterpret_cast<in_addr*>(result->h_addr);
+ }
+ InetAddress inet(addr);
+ callback(inet);
+}
+
+void Resolver::onSockCreate(int sockfd, int type)
+{
+ loop_->assertInLoopThread();
+ assert(channels_.find(sockfd) == channels_.end());
+ Channel* channel = new Channel(loop_, sockfd);
+ channel->setReadCallback(boost::bind(&Resolver::onRead, this, sockfd, _1));
+ channel->enableReading();
+ channels_.insert(sockfd, channel);
+}
+
+void Resolver::onSockStateChange(int sockfd, bool read, bool write)
+{
+ loop_->assertInLoopThread();
+ ChannelList::iterator it = channels_.find(sockfd);
+ assert(it != channels_.end());
+ if (read)
+ {
+ // update
+ // if (write) { } else { }
+ }
+ else
+ {
+ // remove
+ it->second->disableAll();
+ loop_->removeChannel(it->second);
+ channels_.erase(it);
+ }
+}
+
+void Resolver::ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent)
+{
+ QueryData* query = static_cast<QueryData*>(data);
+
+ printf("ares_host_callback %p\n", query);
+ query->owner->onQueryResult(status, hostent, query->callback);
+ delete query;
+}
+
+int Resolver::ares_sock_create_callback(int sockfd, int type, void* data)
+{
+ printf("fd %d type %d\n", sockfd, type);
+ static_cast<Resolver*>(data)->onSockCreate(sockfd, type);
+ return 0;
+}
+
+void Resolver::ares_sock_state_callback(void* data, int sockfd, int read, int write)
+{
+ printf("sock_state %d read %d write %d\n", sockfd, read ,write);
+ static_cast<Resolver*>(data)->onSockStateChange(sockfd, read, write);
+}
+
@@ -0,0 +1,74 @@
+#ifndef MUDUO_EXAMPLES_CDNS_RESOLVER_H
+#define MUDUO_EXAMPLES_CDNS_RESOLVER_H
+
+#include <muduo/base/StringPiece.h>
+#include <muduo/base/Timestamp.h>
+#include <muduo/net/InetAddress.h>
+
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/ptr_container/ptr_map.hpp>
+
+extern "C"
+{
+ struct hostent;
+ struct ares_channeldata;
+ typedef struct ares_channeldata* ares_channel;
+}
+
+namespace muduo
+{
+namespace net
+{
+class Channel;
+class EventLoop;
+}
+}
+
+namespace cdns
+{
+
+class Resolver : boost::noncopyable
+{
+ public:
+ typedef boost::function<void(const muduo::net::InetAddress&)> Callback;
+ enum Option
+ {
+ kDNSandHostsFile,
+ kDNSonly,
+ };
+
+ Resolver(muduo::net::EventLoop* loop, Option opt = kDNSandHostsFile);
+ ~Resolver();
+
+ bool resolve(const muduo::StringPiece& hostname, const Callback& cb);
+
+ private:
+
+ struct QueryData
+ {
+ Resolver* owner;
+ Callback callback;
+ QueryData(Resolver* o, const Callback& cb)
+ : owner(o), callback(cb)
+ {
+ }
+ };
+
+ muduo::net::EventLoop* loop_;
+ ares_channel ctx_;
+ typedef boost::ptr_map<int, muduo::net::Channel> ChannelList;
+ ChannelList channels_;
+
+ void onRead(int sockfd, muduo::Timestamp t);
+ void onQueryResult(int status, struct hostent* result, const Callback& cb);
+ void onSockCreate(int sockfd, int type);
+ void onSockStateChange(int sockfd, bool read, bool write);
+
+ static void ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent);
+ static int ares_sock_create_callback(int sockfd, int type, void* data);
+ static void ares_sock_state_callback(void* data, int sockfd, int read, int write);
+};
+}
+
+#endif
View
@@ -0,0 +1,27 @@
+#include <examples/cdns/Resolver.h>
+#include <muduo/net/EventLoop.h>
+#include <stdio.h>
+
+using namespace muduo::net;
+using namespace cdns;
+
+EventLoop* g_loop;
+int count = 0;
+
+void resolveCallback(const InetAddress& addr)
+{
+ printf("resolveCallback %s\n", addr.toHostPort().c_str());
+ if (++count == 2)
+ g_loop->quit();
+}
+
+int main(int argc, char* argv[])
+{
+ EventLoop loop;
+ g_loop = &loop;
+ Resolver resolver(&loop, Resolver::kDNSonly);
+ resolver.resolve("www.example.com", resolveCallback);
+ resolver.resolve("www.chenshuo.com", resolveCallback);
+ // resolver.resolve("www.google.com", resolveCallback);
+ loop.loop();
+}

0 comments on commit 415a31e

Please sign in to comment.