asiolink.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <cstdlib> // For rand(), temporary until better forwarding is done
  16. #include <unistd.h> // for some IPC/network system calls
  17. #include <sys/socket.h>
  18. #include <netinet/in.h>
  19. #include <vector>
  20. #include <asio.hpp>
  21. #include <boost/lexical_cast.hpp>
  22. #include <boost/bind.hpp>
  23. #include <boost/date_time/posix_time/posix_time_types.hpp>
  24. #include <boost/shared_ptr.hpp>
  25. #include <dns/buffer.h>
  26. #include <dns/message.h>
  27. #include <dns/rcode.h>
  28. #include <asiolink/asiolink.h>
  29. #include <asiolink/internal/tcpdns.h>
  30. #include <asiolink/internal/udpdns.h>
  31. #include <asiolink/internal/iofetch.h>
  32. #include <log/dummylog.h>
  33. using namespace asio;
  34. using asio::ip::udp;
  35. using asio::ip::tcp;
  36. using namespace std;
  37. using namespace isc::dns;
  38. using isc::log::dlog;
  39. using namespace boost;
  40. // Is this something we can use in libdns++?
  41. namespace {
  42. class SectionInserter {
  43. public:
  44. SectionInserter(MessagePtr message, const Message::Section sect) :
  45. message_(message), section_(sect)
  46. {}
  47. void operator()(const RRsetPtr rrset) {
  48. message_->addRRset(section_, rrset, true);
  49. }
  50. MessagePtr message_;
  51. const Message::Section section_;
  52. };
  53. /// \brief Copies the parts relevant for a DNS answer to the
  54. /// target message
  55. ///
  56. /// This adds all the RRsets in the answer, authority and
  57. /// additional sections to the target, as well as the response
  58. /// code
  59. void copyAnswerMessage(const Message& source, MessagePtr target) {
  60. target->setRcode(source.getRcode());
  61. for_each(source.beginSection(Message::SECTION_ANSWER),
  62. source.endSection(Message::SECTION_ANSWER),
  63. SectionInserter(target, Message::SECTION_ANSWER));
  64. for_each(source.beginSection(Message::SECTION_AUTHORITY),
  65. source.endSection(Message::SECTION_AUTHORITY),
  66. SectionInserter(target, Message::SECTION_AUTHORITY));
  67. for_each(source.beginSection(Message::SECTION_ADDITIONAL),
  68. source.endSection(Message::SECTION_ADDITIONAL),
  69. SectionInserter(target, Message::SECTION_ADDITIONAL));
  70. }
  71. }
  72. namespace asiolink {
  73. typedef pair<string, uint16_t> addr_t;
  74. class IOServiceImpl {
  75. private:
  76. IOServiceImpl(const IOService& source);
  77. IOServiceImpl& operator=(const IOService& source);
  78. public:
  79. /// \brief The constructor
  80. IOServiceImpl() :
  81. io_service_(),
  82. work_(io_service_)
  83. {};
  84. /// \brief The destructor.
  85. ~IOServiceImpl() {};
  86. //@}
  87. /// \brief Start the underlying event loop.
  88. ///
  89. /// This method does not return control to the caller until
  90. /// the \c stop() method is called via some handler.
  91. void run() { io_service_.run(); };
  92. /// \brief Run the underlying event loop for a single event.
  93. ///
  94. /// This method return control to the caller as soon as the
  95. /// first handler has completed. (If no handlers are ready when
  96. /// it is run, it will block until one is.)
  97. void run_one() { io_service_.run_one();} ;
  98. /// \brief Stop the underlying event loop.
  99. ///
  100. /// This will return the control to the caller of the \c run() method.
  101. void stop() { io_service_.stop();} ;
  102. /// \brief Return the native \c io_service object used in this wrapper.
  103. ///
  104. /// This is a short term work around to support other BIND 10 modules
  105. /// that share the same \c io_service with the authoritative server.
  106. /// It will eventually be removed once the wrapper interface is
  107. /// generalized.
  108. asio::io_service& get_io_service() { return io_service_; };
  109. private:
  110. asio::io_service io_service_;
  111. asio::io_service::work work_;
  112. };
  113. IOService::IOService() {
  114. io_impl_ = new IOServiceImpl();
  115. }
  116. IOService::~IOService() {
  117. delete io_impl_;
  118. }
  119. void
  120. IOService::run() {
  121. io_impl_->run();
  122. }
  123. void
  124. IOService::run_one() {
  125. io_impl_->run_one();
  126. }
  127. void
  128. IOService::stop() {
  129. io_impl_->stop();
  130. }
  131. asio::io_service&
  132. IOService::get_io_service() {
  133. return (io_impl_->get_io_service());
  134. }
  135. class DNSServiceImpl {
  136. public:
  137. DNSServiceImpl(IOService& io_service, const char& port,
  138. const ip::address* v4addr, const ip::address* v6addr,
  139. SimpleCallback* checkin, DNSLookup* lookup,
  140. DNSAnswer* answer);
  141. IOService& io_service_;
  142. typedef boost::shared_ptr<UDPServer> UDPServerPtr;
  143. typedef boost::shared_ptr<TCPServer> TCPServerPtr;
  144. typedef boost::shared_ptr<DNSServer> DNSServerPtr;
  145. vector<DNSServerPtr> servers_;
  146. SimpleCallback *checkin_;
  147. DNSLookup *lookup_;
  148. DNSAnswer *answer_;
  149. void addServer(uint16_t port, const ip::address& address) {
  150. try {
  151. dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
  152. TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
  153. address, port, checkin_, lookup_, answer_));
  154. (*tcpServer)();
  155. servers_.push_back(tcpServer);
  156. dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
  157. UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
  158. address, port, checkin_, lookup_, answer_));
  159. (*udpServer)();
  160. servers_.push_back(udpServer);
  161. }
  162. catch (const asio::system_error& err) {
  163. // We need to catch and convert any ASIO level exceptions.
  164. // This can happen for unavailable address, binding a privilege port
  165. // without the privilege, etc.
  166. isc_throw(IOError, "Failed to initialize network servers: " <<
  167. err.what());
  168. }
  169. }
  170. void addServer(const char& port, const ip::address& address) {
  171. uint16_t portnum;
  172. try {
  173. // XXX: SunStudio with stlport4 doesn't reject some invalid
  174. // representation such as "-1" by lexical_cast<uint16_t>, so
  175. // we convert it into a signed integer of a larger size and perform
  176. // range check ourselves.
  177. const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
  178. if (portnum32 < 0 || portnum32 > 65535) {
  179. isc_throw(IOError, "Invalid port number '" << &port);
  180. }
  181. portnum = portnum32;
  182. } catch (const boost::bad_lexical_cast& ex) {
  183. isc_throw(IOError, "Invalid port number '" << &port << "': " <<
  184. ex.what());
  185. }
  186. addServer(portnum, address);
  187. }
  188. };
  189. DNSServiceImpl::DNSServiceImpl(IOService& io_service,
  190. const char& port,
  191. const ip::address* const v4addr,
  192. const ip::address* const v6addr,
  193. SimpleCallback* checkin,
  194. DNSLookup* lookup,
  195. DNSAnswer* answer) :
  196. io_service_(io_service),
  197. checkin_(checkin),
  198. lookup_(lookup),
  199. answer_(answer)
  200. {
  201. if (v4addr) {
  202. addServer(port, *v4addr);
  203. }
  204. if (v6addr) {
  205. addServer(port, *v6addr);
  206. }
  207. }
  208. DNSService::DNSService(IOService& io_service,
  209. const char& port, const char& address,
  210. SimpleCallback* checkin,
  211. DNSLookup* lookup,
  212. DNSAnswer* answer) :
  213. impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
  214. answer)), io_service_(io_service)
  215. {
  216. addServer(port, &address);
  217. }
  218. DNSService::DNSService(IOService& io_service,
  219. const char& port,
  220. const bool use_ipv4, const bool use_ipv6,
  221. SimpleCallback* checkin,
  222. DNSLookup* lookup,
  223. DNSAnswer* answer) :
  224. impl_(NULL), io_service_(io_service)
  225. {
  226. const ip::address v4addr_any = ip::address(ip::address_v4::any());
  227. const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
  228. const ip::address v6addr_any = ip::address(ip::address_v6::any());
  229. const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
  230. impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
  231. }
  232. DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
  233. DNSLookup* lookup, DNSAnswer *answer) :
  234. impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
  235. answer)), io_service_(io_service)
  236. {
  237. }
  238. DNSService::~DNSService() {
  239. delete impl_;
  240. }
  241. namespace {
  242. typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
  243. }
  244. // Here we do not use the typedef above, as the SunStudio compiler
  245. // mishandles this in its name mangling, and wouldn't compile.
  246. // We can probably use a typedef, but need to move it to a central
  247. // location and use it consistently.
  248. RecursiveQuery::RecursiveQuery(DNSService& dns_service,
  249. const std::vector<std::pair<std::string, uint16_t> >& upstream,
  250. const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
  251. int query_timeout, int client_timeout, int lookup_timeout,
  252. unsigned retries) :
  253. dns_service_(dns_service), upstream_(new AddressVector(upstream)),
  254. upstream_root_(new AddressVector(upstream_root)),
  255. query_timeout_(query_timeout), client_timeout_(client_timeout),
  256. lookup_timeout_(lookup_timeout), retries_(retries)
  257. {}
  258. namespace {
  259. ip::address
  260. convertAddr(const string& address) {
  261. error_code err;
  262. ip::address addr = ip::address::from_string(address, err);
  263. if (err) {
  264. isc_throw(IOError, "Invalid IP address '" << &address << "': "
  265. << err.message());
  266. }
  267. return (addr);
  268. }
  269. }
  270. void
  271. DNSService::addServer(const char& port, const string& address) {
  272. impl_->addServer(port, convertAddr(address));
  273. }
  274. void
  275. DNSService::addServer(uint16_t port, const string& address) {
  276. impl_->addServer(port, convertAddr(address));
  277. }
  278. void
  279. DNSService::clearServers() {
  280. // FIXME: This does not work, it does not close the socket.
  281. // How is it done?
  282. impl_->servers_.clear();
  283. }
  284. namespace {
  285. /*
  286. * This is a query in progress. When a new query is made, this one holds
  287. * the context information about it, like how many times we are allowed
  288. * to retry on failure, what to do when we succeed, etc.
  289. *
  290. * Used by RecursiveQuery::sendQuery.
  291. */
  292. class RunningQuery : public UDPQuery::Callback {
  293. private:
  294. // The io service to handle async calls
  295. asio::io_service& io_;
  296. // Info for (re)sending the query (the question and destination)
  297. Question question_;
  298. // This is where we build and store our final answer
  299. MessagePtr answer_message_;
  300. // currently we use upstream as the current list of NS records
  301. // we should differentiate between forwarding and resolving
  302. shared_ptr<AddressVector> upstream_;
  303. // root servers...just copied over to the zone_servers_
  304. shared_ptr<AddressVector> upstream_root_;
  305. // Buffer to store the result.
  306. OutputBufferPtr buffer_;
  307. // Server to notify when we succeed or fail
  308. shared_ptr<DNSServer> server_;
  309. /*
  310. * TODO Do something more clever with timeouts. In the long term, some
  311. * computation of average RTT, increase with each retry, etc.
  312. */
  313. // Timeout information
  314. int query_timeout_;
  315. unsigned retries_;
  316. // normal query state
  317. // Not using NSAS at this moment, so we keep a list
  318. // of 'current' zone servers
  319. vector<addr_t> zone_servers_;
  320. // Update the question that will be sent to the server
  321. void setQuestion(const Question& new_question) {
  322. question_ = new_question;
  323. }
  324. deadline_timer client_timer;
  325. deadline_timer lookup_timer;
  326. size_t queries_out_;
  327. // If we timed out ourselves (lookup timeout), stop issuing queries
  328. bool done_;
  329. // (re)send the query to the server.
  330. void send() {
  331. const int uc = upstream_->size();
  332. const int zs = zone_servers_.size();
  333. buffer_->clear();
  334. if (uc > 0) {
  335. int serverIndex = rand() % uc;
  336. dlog("Sending upstream query (" + question_.toText() +
  337. ") to " + upstream_->at(serverIndex).first);
  338. UDPQuery query(io_, question_,
  339. upstream_->at(serverIndex).first,
  340. upstream_->at(serverIndex).second, buffer_, this,
  341. query_timeout_);
  342. ++queries_out_;
  343. io_.post(query);
  344. } else if (zs > 0) {
  345. int serverIndex = rand() % zs;
  346. dlog("Sending query to zone server (" + question_.toText() +
  347. ") to " + zone_servers_.at(serverIndex).first);
  348. UDPQuery query(io_, question_,
  349. zone_servers_.at(serverIndex).first,
  350. zone_servers_.at(serverIndex).second, buffer_, this,
  351. query_timeout_);
  352. ++queries_out_;
  353. io_.post(query);
  354. } else {
  355. dlog("Error, no upstream servers to send to.");
  356. }
  357. }
  358. // This function is called by operator() if there is an actual
  359. // answer from a server and we are in recursive mode
  360. // depending on the contents, we go on recursing or return
  361. //
  362. // Note that the footprint may change as this function may
  363. // need to append data to the answer we are building later.
  364. //
  365. // returns true if we are done
  366. // returns false if we are not done
  367. bool handleRecursiveAnswer(const Message& incoming) {
  368. if (incoming.getRRCount(Message::SECTION_ANSWER) > 0) {
  369. dlog("Got final result, copying answer.");
  370. copyAnswerMessage(incoming, answer_message_);
  371. return true;
  372. } else {
  373. dlog("Got delegation, continuing");
  374. // ok we need to do some more processing.
  375. // the ns list should contain all nameservers
  376. // while the additional may contain addresses for
  377. // them.
  378. // this needs to tie into NSAS of course
  379. // for this very first mockup, hope there is an
  380. // address in additional and just use that
  381. // send query to the addresses in the delegation
  382. bool found_ns_address = false;
  383. zone_servers_.clear();
  384. for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
  385. rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
  386. rrsi++) {
  387. ConstRRsetPtr rrs = *rrsi;
  388. if (rrs->getType() == RRType::A()) {
  389. // found address
  390. RdataIteratorPtr rdi = rrs->getRdataIterator();
  391. // just use the first for now
  392. if (!rdi->isLast()) {
  393. std::string addr_str = rdi->getCurrent().toText();
  394. dlog("[XX] first address found: " + addr_str);
  395. // now we have one address, simply
  396. // resend that exact same query
  397. // to that address and yield, when it
  398. // returns, loop again.
  399. // should use NSAS
  400. zone_servers_.push_back(addr_t(addr_str, 53));
  401. found_ns_address = true;
  402. }
  403. }
  404. }
  405. if (found_ns_address) {
  406. // next resolver round
  407. send();
  408. return false;
  409. } else {
  410. dlog("[XX] no ready-made addresses in additional. need nsas.");
  411. // this will result in answering with the delegation. oh well
  412. copyAnswerMessage(incoming, answer_message_);
  413. return true;
  414. }
  415. }
  416. }
  417. public:
  418. RunningQuery(asio::io_service& io, const Question &question,
  419. MessagePtr answer_message, shared_ptr<AddressVector> upstream,
  420. shared_ptr<AddressVector> upstream_root,
  421. OutputBufferPtr buffer, DNSServer* server,
  422. int query_timeout, int client_timeout, int lookup_timeout,
  423. unsigned retries) :
  424. io_(io),
  425. question_(question),
  426. answer_message_(answer_message),
  427. upstream_(upstream),
  428. upstream_root_(upstream_root),
  429. buffer_(buffer),
  430. server_(server->clone()),
  431. query_timeout_(query_timeout),
  432. retries_(retries),
  433. client_timer(io),
  434. lookup_timer(io),
  435. queries_out_(0),
  436. done_(false)
  437. {
  438. // Setup the timer to stop trying (lookup_timeout)
  439. if (lookup_timeout >= 0) {
  440. lookup_timer.expires_from_now(
  441. boost::posix_time::milliseconds(lookup_timeout));
  442. lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
  443. }
  444. // Setup the timer to send an answer (client_timeout)
  445. if (client_timeout >= 0) {
  446. client_timer.expires_from_now(
  447. boost::posix_time::milliseconds(client_timeout));
  448. client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
  449. }
  450. // should use NSAS for root servers
  451. // Adding root servers if not a forwarder
  452. if (upstream_->empty()) {
  453. if (upstream_root_->empty()) { //if no root ips given, use this
  454. zone_servers_.push_back(addr_t("192.5.5.241", 53));
  455. }
  456. else
  457. {
  458. //copy the list
  459. dlog("Size is " +
  460. boost::lexical_cast<string>(upstream_root_->size()) +
  461. "\n");
  462. //Use BOOST_FOREACH here? Is it faster?
  463. for(AddressVector::iterator it = upstream_root_->begin();
  464. it < upstream_root_->end(); it++) {
  465. zone_servers_.push_back(addr_t(it->first,it->second));
  466. dlog("Put " + zone_servers_.back().first + "into root list\n");
  467. }
  468. }
  469. }
  470. send();
  471. }
  472. virtual void clientTimeout() {
  473. // right now, just stop (should make SERVFAIL and send that
  474. // back, but not stop)
  475. stop(false);
  476. }
  477. virtual void stop(bool resume) {
  478. // if we cancel our timers, we will still get an event for
  479. // that, so we cannot delete ourselves just yet (those events
  480. // would be bound to a deleted object)
  481. // cancel them one by one, both cancels should get us back
  482. // here again.
  483. // same goes if we have an outstanding query (can't delete
  484. // until that one comes back to us)
  485. done_ = true;
  486. server_->resume(resume);
  487. if (lookup_timer.cancel() != 0) {
  488. return;
  489. }
  490. if (client_timer.cancel() != 0) {
  491. return;
  492. }
  493. if (queries_out_ > 0) {
  494. return;
  495. }
  496. delete this;
  497. }
  498. // This function is used as callback from DNSQuery.
  499. virtual void operator()(UDPQuery::Result result) {
  500. // XXX is this the place for TCP retry?
  501. --queries_out_;
  502. if (!done_ && result != UDPQuery::TIME_OUT) {
  503. // we got an answer
  504. Message incoming(Message::PARSE);
  505. InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
  506. incoming.fromWire(ibuf);
  507. if (upstream_->size() == 0 &&
  508. incoming.getRcode() == Rcode::NOERROR()) {
  509. done_ = handleRecursiveAnswer(incoming);
  510. } else {
  511. copyAnswerMessage(incoming, answer_message_);
  512. done_ = true;
  513. }
  514. if (done_) {
  515. stop(result == UDPQuery::SUCCESS);
  516. }
  517. } else if (!done_ && retries_--) {
  518. // We timed out, but we have some retries, so send again
  519. dlog("Timeout, resending query");
  520. send();
  521. } else {
  522. // We are done
  523. stop(false);
  524. }
  525. }
  526. };
  527. }
  528. void
  529. RecursiveQuery::sendQuery(const Question& question,
  530. MessagePtr answer_message,
  531. OutputBufferPtr buffer,
  532. DNSServer* server)
  533. {
  534. // XXX: eventually we will need to be able to determine whether
  535. // the message should be sent via TCP or UDP, or sent initially via
  536. // UDP and then fall back to TCP on failure, but for the moment
  537. // we're only going to handle UDP.
  538. asio::io_service& io = dns_service_.get_io_service();
  539. // It will delete itself when it is done
  540. new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
  541. buffer, server, query_timeout_, client_timeout_,
  542. lookup_timeout_, retries_);
  543. }
  544. class IntervalTimerImpl {
  545. private:
  546. // prohibit copy
  547. IntervalTimerImpl(const IntervalTimerImpl& source);
  548. IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
  549. public:
  550. IntervalTimerImpl(IOService& io_service);
  551. ~IntervalTimerImpl();
  552. void setupTimer(const IntervalTimer::Callback& cbfunc,
  553. const uint32_t interval);
  554. void callback(const asio::error_code& error);
  555. void cancel() {
  556. timer_.cancel();
  557. interval_ = 0;
  558. }
  559. uint32_t getInterval() const { return (interval_); }
  560. private:
  561. // a function to update timer_ when it expires
  562. void updateTimer();
  563. // a function to call back when timer_ expires
  564. IntervalTimer::Callback cbfunc_;
  565. // interval in seconds
  566. uint32_t interval_;
  567. // asio timer
  568. asio::deadline_timer timer_;
  569. };
  570. IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
  571. interval_(0), timer_(io_service.get_io_service())
  572. {}
  573. IntervalTimerImpl::~IntervalTimerImpl()
  574. {}
  575. void
  576. IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
  577. const uint32_t interval)
  578. {
  579. // Interval should not be 0.
  580. if (interval == 0) {
  581. isc_throw(isc::BadValue, "Interval should not be 0");
  582. }
  583. // Call back function should not be empty.
  584. if (cbfunc.empty()) {
  585. isc_throw(isc::InvalidParameter, "Callback function is empty");
  586. }
  587. cbfunc_ = cbfunc;
  588. interval_ = interval;
  589. // Set initial expire time.
  590. // At this point the timer is not running yet and will not expire.
  591. // After calling IOService::run(), the timer will expire.
  592. updateTimer();
  593. return;
  594. }
  595. void
  596. IntervalTimerImpl::updateTimer() {
  597. if (interval_ == 0) {
  598. // timer has been canceled. Do nothing.
  599. return;
  600. }
  601. try {
  602. // Update expire time to (current time + interval_).
  603. timer_.expires_from_now(boost::posix_time::seconds(interval_));
  604. } catch (const asio::system_error& e) {
  605. isc_throw(isc::Unexpected, "Failed to update timer");
  606. }
  607. // Reset timer.
  608. timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
  609. }
  610. void
  611. IntervalTimerImpl::callback(const asio::error_code& cancelled) {
  612. // Do not call cbfunc_ in case the timer was cancelled.
  613. // The timer will be canelled in the destructor of asio::deadline_timer.
  614. if (!cancelled) {
  615. cbfunc_();
  616. // Set next expire time.
  617. updateTimer();
  618. }
  619. }
  620. IntervalTimer::IntervalTimer(IOService& io_service) {
  621. impl_ = new IntervalTimerImpl(io_service);
  622. }
  623. IntervalTimer::~IntervalTimer() {
  624. delete impl_;
  625. }
  626. void
  627. IntervalTimer::setupTimer(const Callback& cbfunc, const uint32_t interval) {
  628. return (impl_->setupTimer(cbfunc, interval));
  629. }
  630. void
  631. IntervalTimer::cancel() {
  632. impl_->cancel();
  633. }
  634. uint32_t
  635. IntervalTimer::getInterval() const {
  636. return (impl_->getInterval());
  637. }
  638. }