asiolink.cc 23 KB

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