asiolink.cc 8.34 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 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 <config.h>

19
#include <unistd.h>             // for some IPC/network system calls
20 21 22
#include <sys/socket.h>
#include <netinet/in.h>

23
#include <asio.hpp>
Evan Hunt's avatar
Evan Hunt committed
24
#include <boost/lexical_cast.hpp>
25 26
#include <boost/bind.hpp>

27 28
#include <boost/shared_ptr.hpp>

29 30 31
#include <dns/buffer.h>
#include <dns/message.h>

32 33 34
#include <asiolink/asiolink.h>
#include <asiolink/internal/tcpdns.h>
#include <asiolink/internal/udpdns.h>
35 36

using namespace asio;
37 38
using asio::ip::udp;
using asio::ip::tcp;
39

40
using namespace std;
41
using namespace isc::dns;
42

43
namespace asiolink {
44

45
class IOServiceImpl {
46 47 48
private:
    IOServiceImpl(const IOService& source);
    IOServiceImpl& operator=(const IOService& source);
49
public:
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    /// \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() {
Jelte Jansen's avatar
Jelte Jansen committed
110
    return (io_impl_->get_io_service());
111 112 113 114 115
}

class DNSServiceImpl {
public:
    DNSServiceImpl(IOService& io_service, const char& port,
116
                  const ip::address* v4addr, const ip::address* v6addr,
117
                  SimpleCallback* checkin, DNSLookup* lookup,
118
                  DNSAnswer* answer);
119
    //asio::io_service io_service_;
120

121
    void stop();
122 123 124 125 126 127
    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
    UDPServerPtr udp4_server_;
    UDPServerPtr udp6_server_;
    TCPServerPtr tcp4_server_;
    TCPServerPtr tcp6_server_;
128 129
};

130 131 132 133 134 135 136
DNSServiceImpl::DNSServiceImpl(IOService& io_service_,
                               const char& port,
                               const ip::address* const v4addr,
                               const ip::address* const v6addr,
                               SimpleCallback* checkin,
                               DNSLookup* lookup,
                               DNSAnswer* answer) :
137 138
    udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
    tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
139
{
140
    uint16_t portnum;
Evan Hunt's avatar
Evan Hunt committed
141
    try {
142 143 144 145
        // XXX: SunStudio with stlport4 doesn't reject some invalid
        // representation such as "-1" by lexical_cast<uint16_t>, so
        // we convert it into a signed integer of a larger size and perform
        // range check ourselves.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
146
        const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
147 148 149 150
        if (portnum32 < 0 || portnum32 > 65535) {
            isc_throw(IOError, "Invalid port number '" << &port);
        }
        portnum = portnum32;
151
    } catch (const boost::bad_lexical_cast& ex) {
152 153
        isc_throw(IOError, "Invalid port number '" << &port << "': " <<
                  ex.what());
154
    }
Evan Hunt's avatar
Evan Hunt committed
155

156
    try {
157
        if (v4addr != NULL) {
158
            udp4_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(),
159
                                                      *v4addr, portnum,
160
                                                      checkin, lookup, answer));
161
            (*udp4_server_)();
162
            tcp4_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(),
163
                                                      *v4addr, portnum,
164
                                                      checkin, lookup, answer));
165
            (*tcp4_server_)();
166
        }
167
        if (v6addr != NULL) {
168
            udp6_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(),
169
                                                      *v6addr, portnum,
170
                                                      checkin, lookup, answer));
171
            (*udp6_server_)();
172
            tcp6_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(),
173
                                                      *v6addr, portnum,
174
                                                      checkin, lookup, answer));
175
            (*tcp6_server_)();
176 177 178 179 180 181 182
        }
    } 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());
183 184
    }
}
Evan Hunt's avatar
Evan Hunt committed
185

186 187 188 189 190 191
DNSService::DNSService(IOService& io_service,
                       const char& port, const char& address,
                       SimpleCallback* checkin,
                       DNSLookup* lookup,
                       DNSAnswer* answer) :
    impl_(NULL), io_service_(io_service)
192 193 194 195 196 197
{
    error_code err;
    const ip::address addr = ip::address::from_string(&address, err);
    if (err) {
        isc_throw(IOError, "Invalid IP address '" << &address << "': "
                  << err.message());
198
    }
199

200
    impl_ = new DNSServiceImpl(io_service, port,
201
                              addr.is_v4() ? &addr : NULL,
202
                              addr.is_v6() ? &addr : NULL,
203
                              checkin, lookup, answer);
204 205
}

206 207 208 209 210 211 212
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)
213
{
214 215 216 217
    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;
218
    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
219 220
}

221
DNSService::~DNSService() {
222 223 224
    delete impl_;
}

225
RecursiveQuery::RecursiveQuery(DNSService& dns_service, const char& forward,
226
                               uint16_t port) :
227
    dns_service_(dns_service), ns_addr_(&forward), port_(port) 
228
{}
229 230 231 232 233 234 235 236 237 238

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.
239
    asio::io_service& io = dns_service_.get_io_service();
240 241 242 243
    UDPQuery q(io, question, ns_addr_, port_, buffer, server);
    io.post(q);
}

244
}