io_fetch.h 9.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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.

Stephen Morris's avatar
Stephen Morris committed
15 16
#ifndef __IO_FETCH_H
#define __IO_FETCH_H 1
17 18 19

#include <config.h>

Stephen Morris's avatar
Stephen Morris committed
20 21 22 23
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>             // for some IPC/network system calls

24 25
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
Stephen Morris's avatar
Stephen Morris committed
26
#include <boost/date_time/posix_time/posix_time_types.hpp>
27 28
#include <asio/deadline_timer.hpp>

Stephen Morris's avatar
Stephen Morris committed
29 30
#include <coroutine.h>

31 32 33
#include <dns/buffer.h>
#include <dns/question.h>

Stephen Morris's avatar
Stephen Morris committed
34 35 36 37 38 39 40
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <asiolink/udp_endpoint.h>
41 42 43 44


namespace asiolink {

Stephen Morris's avatar
Stephen Morris committed
45

46 47 48
/// \brief Upstream Fetch Processing
///
/// IOFetch is the class used to send upstream fetches and to handle responses.
Stephen Morris's avatar
Stephen Morris committed
49 50 51 52
///
/// \param E Endpoint type to use.

class IOFetch : public coroutine {
53
public:
54 55 56 57 58 59
    /// \brief Protocol to use on the fetch
    enum Protocol {
        UDP = 0,
        TCP = 1
    };

60 61 62 63 64 65 66 67 68 69 70
    /// \brief Origin of Asynchronous I/O Call
    ///
    /// Indicates what initiated an asynchronous I/O call and used in deciding
    /// what error message to output if the I/O fails.
    enum Origin {
        NONE = 0,           ///< No asynchronous call outstanding
        OPEN = 1,
        SEND = 2,
        RECEIVE = 3,
        CLOSE = 4
    };
71 72 73 74 75 76 77 78 79

    /// \brief Result of Upstream Fetch
    ///
    /// Note that this applies to the status of I/Os in the fetch - a fetch
    /// that resulted in a packet being received from the server is a SUCCESS,
    /// even if the contents of the packet indicate that some error occurred.
    enum Result {
        SUCCESS = 0,        ///< Success, fetch completed
        TIME_OUT,           ///< Failure, fetch timed out
Stephen Morris's avatar
Stephen Morris committed
80 81
        STOPPED,            ///< Control code, fetch has been stopped
        NOTSET              ///< For testing, indicates value not set
82
    };
Stephen Morris's avatar
Stephen Morris committed
83

84 85 86 87 88 89 90
    // The next enum is a "trick" to allow constants to be defined in a class
    // declaration.

    /// \brief Integer Constants
    enum {
        MAX_LENGTH = 4096   ///< Maximum size of receive buffer
    };
Stephen Morris's avatar
Stephen Morris committed
91

92 93
    /// \brief I/O Fetch Callback
    ///
94 95 96 97 98 99 100 101 102
    /// Class of callback object for when the fetch itself has completed - an
    /// object of this class is passed to the IOFetch constructor and its
    /// operator() method called when the fetch completes.
    ///
    /// Note the difference between the two operator() methods:
    /// - IOFetch::operator() callback is called when an asynchronous I/O has
    ///   completed.
    /// - IOFetch::Callback::operator() is called when an upstream fetch - which
    ///   may have involved several asynchronous I/O operations - has completed.
103 104 105 106 107 108 109 110 111 112 113 114
    ///
    /// This is an abstract class.
    class Callback {
    public:
        /// \brief Default Constructor
        Callback()
        {}

        /// \brief Virtual Destructor
        virtual ~Callback()
        {}

115 116 117 118 119 120
        /// \brief Callback method called when the fetch completes   /// \brief Origin of Asynchronous I/O Call
    ///

    // The next enum is a "trick" to allow constants to be defined in a class
    // declaration.

121 122 123 124 125 126 127 128 129
        ///
        /// \brief result Result of the fetch
        virtual void operator()(Result result) = 0;
    };

    /// \brief IOFetch Data
    ///
    /// The data for IOFetch is held in a separate struct pointed to by a
    /// shared_ptr object.  This is because the IOFetch object will be copied
Stephen Morris's avatar
Stephen Morris committed
130 131 132
    /// often (it is used as a coroutine and passed as callback to many
    /// async_*() functions) and we want keep the same data).  Organising the
    /// data in this way keeps copying to a minimum.
133 134 135 136 137 138 139
    struct IOFetchData {

        // The next two members are shared pointers to a base class because what
        // is actually instantiated depends on whether the fetch is over UDP or
        // TCP, which is not known until construction of the IOFetch.  Use of
        // a shared pointer here is merely to ensure deletion when the data
        // object is deleted.
Stephen Morris's avatar
Stephen Morris committed
140 141
        boost::shared_ptr<IOAsioSocket<IOFetch> > socket;
                                                ///< Socket to use for I/O
142 143
        boost::shared_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
        isc::dns::Question          question;   ///< Question to be asked
Stephen Morris's avatar
Stephen Morris committed
144
        isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
145
        isc::dns::OutputBufferPtr   buffer;     ///< Received data held here
Stephen Morris's avatar
Stephen Morris committed
146 147 148
        boost::shared_array<char>   data;       ///< Temporary array for data
        IOFetch::Callback*          callback;   ///< Called on I/O Completion
        size_t                      cumulative; ///< Cumulative received amount
149 150 151
        bool                        stopped;    ///< Have we stopped running?
        asio::deadline_timer        timer;      ///< Timer to measure timeouts
        int                         timeout;    ///< Timeout in ms
152
        Origin                      origin;     ///< Origin of last asynchronous I/O
153 154 155 156 157

        /// \brief Constructor
        ///
        /// Just fills in the data members of the IOFetchData structure
        ///
158
        /// \param proto Protocol: either IOFetch::TCP or IOFetch::UDP
Stephen Morris's avatar
Stephen Morris committed
159
        /// \param service I/O Service object to handle the asynchronous
160 161 162 163 164 165 166 167 168
        ///     operations.
        /// \param query DNS question to send to the upstream server.
        /// \param address IP address of upstream server
        /// \param port Port to use for the query
        /// \param buff Output buffer into which the response (in wire format)
        ///     is written (if a response is received).
        /// \param cb Callback object containing the callback to be called
        ///     when we terminate.  The caller is responsible for managing this
        ///     object and deleting it if necessary.
Stephen Morris's avatar
Stephen Morris committed
169
        /// \param wait Timeout for the fetch (in ms).
170 171
        ///
        /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
172
        IOFetchData(Protocol proto, IOService& service,
Stephen Morris's avatar
Stephen Morris committed
173 174 175 176
            const isc::dns::Question& query, const IOAddress& address,
            uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
            int wait)
            :
177
            socket((proto == UDP) ?
Stephen Morris's avatar
Stephen Morris committed
178 179 180 181 182
                static_cast<IOAsioSocket<IOFetch>*>(
                    new UDPSocket<IOFetch>(service)) :
                static_cast<IOAsioSocket<IOFetch>*>(
                    new TCPSocket<IOFetch>(service))
                ),
183
            remote((proto == UDP) ?
Stephen Morris's avatar
Stephen Morris committed
184 185 186 187 188 189 190 191 192 193 194
                static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
                static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
                ),
            question(query),
            msgbuf(new isc::dns::OutputBuffer(512)),
            buffer(buff),
            data(new char[IOFetch::MAX_LENGTH]),
            callback(cb),
            cumulative(0),
            stopped(false),
            timer(service.get_io_service()),
195 196
            timeout(wait),
            origin(NONE)
Stephen Morris's avatar
Stephen Morris committed
197
        {}
198 199 200 201 202 203
    };

    /// \brief Constructor.
    ///
    /// Creates the object that will handle the upstream fetch.
    ///
Stephen Morris's avatar
Stephen Morris committed
204 205
    /// TODO: Need to randomise the source port
    ///
206
    /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
Stephen Morris's avatar
Stephen Morris committed
207
    /// \param service I/O Service object to handle the asynchronous
208 209
    ///     operations.
    /// \param question DNS question to send to the upstream server.
Stephen Morris's avatar
Stephen Morris committed
210
    /// \param buff Output buffer into which the response (in wire format)
211
    ///     is written (if a response is received).
Stephen Morris's avatar
Stephen Morris committed
212
    /// \param cb Callback object containing the callback to be called
213 214
    ///     when we terminate.  The caller is responsible for managing this
    ///     object and deleting it if necessary.
Stephen Morris's avatar
Stephen Morris committed
215 216 217 218
    /// \param address IP address of upstream server
    /// \param port Port to which to connect on the upstream server
    /// (default = 53)
    /// \param wait Timeout for the fetch (in ms).  The default value of
219
    ///     -1 indicates no timeout.
220
    IOFetch(Protocol protocol, IOService& service,
Stephen Morris's avatar
Stephen Morris committed
221 222 223
        const isc::dns::Question& question, const IOAddress& address,
        uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
        int wait = -1);
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
    /// \brief Coroutine entry point
    ///
    /// The operator() method is the method in which the coroutine code enters
    /// this object when an operation has been completed.
    ///
    /// \param ec Error code, the result of the last asynchronous I/O operation.
    /// \param length Amount of data received on the last asynchronous read
    void operator()(asio::error_code ec = asio::error_code(),
        size_t length = 0);

    /// \brief Terminate query
    ///
    /// This method can be called at any point.  It terminates the current
    /// query with the specified reason.
    ///
    /// \param reason Reason for terminating the query
    void stop(Result reason = STOPPED);

private:
244 245 246 247 248 249 250
    /// \brief Log I/O Failure
    ///
    /// Records an I/O failure to the log file
    ///
    /// \param ec ASIO error code
    void logIOFailure(asio::error_code& ec);

Stephen Morris's avatar
Stephen Morris committed
251
    boost::shared_ptr<IOFetchData>  data_;   ///< Private data
252

Stephen Morris's avatar
Stephen Morris committed
253
};
254

Stephen Morris's avatar
Stephen Morris committed
255
} // namespace asiolink
256

Stephen Morris's avatar
Stephen Morris committed
257
#endif // __IO_FETCH_H