// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. // $Id$ #include #include // for some IPC/network system calls #include #include #include #include #include #include #include #include #include #include #include using namespace asio; using asio::ip::udp; using asio::ip::tcp; using namespace std; using namespace isc::dns; namespace asiolink { class IOServiceImpl { private: IOServiceImpl(const IOService& source); IOServiceImpl& operator=(const IOService& source); public: /// \brief The constructor IOServiceImpl() : io_service_() {}; /// \brief The destructor. ~IOServiceImpl() {}; //@} /// \brief Start the underlying event loop. /// /// This method does not return control to the caller until /// the \c stop() method is called via some handler. void run() { io_service_.run(); }; /// \brief Run the underlying event loop for a single event. /// /// This method return control to the caller as soon as the /// first handler has completed. (If no handlers are ready when /// it is run, it will block until one is.) void run_one() { io_service_.run_one();} ; /// \brief Stop the underlying event loop. /// /// This will return the control to the caller of the \c run() method. void stop() { io_service_.stop();} ; /// \brief Return the native \c io_service object used in this wrapper. /// /// This is a short term work around to support other BIND 10 modules /// that share the same \c io_service with the authoritative server. /// It will eventually be removed once the wrapper interface is /// generalized. asio::io_service& get_io_service() { return io_service_; }; private: asio::io_service io_service_; }; IOService::IOService() { io_impl_ = new IOServiceImpl(); } IOService::~IOService() { delete io_impl_; } void IOService::run() { io_impl_->run(); } void IOService::run_one() { io_impl_->run_one(); } void IOService::stop() { io_impl_->stop(); } asio::io_service& IOService::get_io_service() { return (io_impl_->get_io_service()); } class DNSServiceImpl { public: DNSServiceImpl(IOService& io_service, const char& port, const ip::address* v4addr, const ip::address* v6addr, SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer); //asio::io_service io_service_; void stop(); typedef boost::shared_ptr UDPServerPtr; typedef boost::shared_ptr TCPServerPtr; UDPServerPtr udp4_server_; UDPServerPtr udp6_server_; TCPServerPtr tcp4_server_; TCPServerPtr tcp6_server_; }; DNSServiceImpl::DNSServiceImpl(IOService& io_service_, const char& port, const ip::address* const v4addr, const ip::address* const v6addr, SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) : udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()), tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr()) { uint16_t portnum; try { // XXX: SunStudio with stlport4 doesn't reject some invalid // representation such as "-1" by lexical_cast, so // we convert it into a signed integer of a larger size and perform // range check ourselves. const int32_t portnum32 = boost::lexical_cast(&port); if (portnum32 < 0 || portnum32 > 65535) { isc_throw(IOError, "Invalid port number '" << &port); } portnum = portnum32; } catch (const boost::bad_lexical_cast& ex) { isc_throw(IOError, "Invalid port number '" << &port << "': " << ex.what()); } try { if (v4addr != NULL) { udp4_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(), *v4addr, portnum, checkin, lookup, answer)); (*udp4_server_)(); tcp4_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(), *v4addr, portnum, checkin, lookup, answer)); (*tcp4_server_)(); } if (v6addr != NULL) { udp6_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(), *v6addr, portnum, checkin, lookup, answer)); (*udp6_server_)(); tcp6_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(), *v6addr, portnum, checkin, lookup, answer)); (*tcp6_server_)(); } } catch (const asio::system_error& err) { // We need to catch and convert any ASIO level exceptions. // This can happen for unavailable address, binding a privilege port // without the privilege, etc. isc_throw(IOError, "Failed to initialize network servers: " << err.what()); } } DNSService::DNSService(IOService& io_service, const char& port, const char& address, SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) : impl_(NULL), io_service_(io_service) { error_code err; const ip::address addr = ip::address::from_string(&address, err); if (err) { isc_throw(IOError, "Invalid IP address '" << &address << "': " << err.message()); } impl_ = new DNSServiceImpl(io_service, port, addr.is_v4() ? &addr : NULL, addr.is_v6() ? &addr : NULL, checkin, lookup, answer); } DNSService::DNSService(IOService& io_service, const char& port, const bool use_ipv4, const bool use_ipv6, SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) : impl_(NULL), io_service_(io_service) { const ip::address v4addr_any = ip::address(ip::address_v4::any()); const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; const ip::address v6addr_any = ip::address(ip::address_v6::any()); const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL; impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer); } DNSService::~DNSService() { delete impl_; } RecursiveQuery::RecursiveQuery(DNSService& dns_service, const char& forward, uint16_t port) : dns_service_(dns_service), ns_addr_(&forward), port_(port) {} void RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer, DNSServer* server) { // XXX: eventually we will need to be able to determine whether // the message should be sent via TCP or UDP, or sent initially via // UDP and then fall back to TCP on failure, but for the moment // we're only going to handle UDP. asio::io_service& io = dns_service_.get_io_service(); UDPQuery q(io, question, ns_addr_, port_, buffer, server); io.post(q); } }