nc_trans.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Copyright (C) 2013 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 <d2/d2_log.h>
  15. #include <d2/nc_trans.h>
  16. #include <dns/rdata.h>
  17. namespace isc {
  18. namespace d2 {
  19. // Common transaction states
  20. const int NameChangeTransaction::READY_ST;
  21. const int NameChangeTransaction::SELECTING_FWD_SERVER_ST;
  22. const int NameChangeTransaction::SELECTING_REV_SERVER_ST;
  23. const int NameChangeTransaction::PROCESS_TRANS_OK_ST;
  24. const int NameChangeTransaction::PROCESS_TRANS_FAILED_ST;
  25. const int NameChangeTransaction::NCT_DERIVED_STATE_MIN;
  26. // Common transaction events
  27. const int NameChangeTransaction::SELECT_SERVER_EVT;
  28. const int NameChangeTransaction::SERVER_SELECTED_EVT;
  29. const int NameChangeTransaction::SERVER_IO_ERROR_EVT;
  30. const int NameChangeTransaction::NO_MORE_SERVERS_EVT;
  31. const int NameChangeTransaction::IO_COMPLETED_EVT;
  32. const int NameChangeTransaction::UPDATE_OK_EVT;
  33. const int NameChangeTransaction::UPDATE_FAILED_EVT;
  34. const int NameChangeTransaction::NCT_DERIVED_EVENT_MIN;
  35. const unsigned int NameChangeTransaction::DNS_UPDATE_DEFAULT_TIMEOUT;
  36. const unsigned int NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
  37. NameChangeTransaction::
  38. NameChangeTransaction(IOServicePtr& io_service,
  39. dhcp_ddns::NameChangeRequestPtr& ncr,
  40. DdnsDomainPtr& forward_domain,
  41. DdnsDomainPtr& reverse_domain)
  42. : io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain),
  43. reverse_domain_(reverse_domain), dns_client_(), dns_update_request_(),
  44. dns_update_status_(DNSClient::OTHER), dns_update_response_(),
  45. forward_change_completed_(false), reverse_change_completed_(false),
  46. current_server_list_(), current_server_(), next_server_pos_(0),
  47. update_attempts_(0) {
  48. // @todo if io_service is NULL we are multi-threading and should
  49. // instantiate our own
  50. if (!io_service_) {
  51. isc_throw(NameChangeTransactionError, "IOServicePtr cannot be null");
  52. }
  53. if (!ncr_) {
  54. isc_throw(NameChangeTransactionError,
  55. "NameChangeRequest cannot be null");
  56. }
  57. if (ncr_->isForwardChange() && !(forward_domain_)) {
  58. isc_throw(NameChangeTransactionError,
  59. "Forward change must have a forward domain");
  60. }
  61. if (ncr_->isReverseChange() && !(reverse_domain_)) {
  62. isc_throw(NameChangeTransactionError,
  63. "Reverse change must have a reverse domain");
  64. }
  65. }
  66. NameChangeTransaction::~NameChangeTransaction(){
  67. }
  68. void
  69. NameChangeTransaction::startTransaction() {
  70. LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
  71. DHCP_DDNS_STARTING_TRANSACTION)
  72. .arg(getTransactionKey().toStr());
  73. setNcrStatus(dhcp_ddns::ST_PENDING);
  74. startModel(READY_ST);
  75. }
  76. void
  77. NameChangeTransaction::operator()(DNSClient::Status status) {
  78. // Stow the completion status and re-enter the run loop with the event
  79. // set to indicate IO completed.
  80. // runModel is exception safe so we are good to call it here.
  81. // It won't exit until we hit the next IO wait or the state model ends.
  82. LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
  83. DHCP_DDNS_UPDATE_RESPONSE_RECEIVED)
  84. .arg(getTransactionKey().toStr())
  85. .arg(current_server_->toText())
  86. .arg(status);
  87. setDnsUpdateStatus(status);
  88. runModel(IO_COMPLETED_EVT);
  89. }
  90. void
  91. NameChangeTransaction::sendUpdate(bool /* use_tsig_ */) {
  92. try {
  93. ++update_attempts_;
  94. // @todo add logic to add/replace TSIG key info in request if
  95. // use_tsig_ is true. We should be able to navigate to the TSIG key
  96. // for the current server. If not we would need to add that.
  97. // @todo time out should ultimately be configurable, down to
  98. // server level?
  99. dns_client_->doUpdate(*io_service_, current_server_->getIpAddress(),
  100. current_server_->getPort(), *dns_update_request_,
  101. DNS_UPDATE_DEFAULT_TIMEOUT);
  102. // Message is on its way, so the next event should be NOP_EVT.
  103. postNextEvent(NOP_EVT);
  104. LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
  105. DHCP_DDNS_UPDATE_REQUEST_SENT)
  106. .arg(getTransactionKey().toStr())
  107. .arg(current_server_->toText());
  108. } catch (const std::exception& ex) {
  109. // We were unable to initiate the send.
  110. // It is presumed that any throw from doUpdate is due to a programmatic
  111. // error, such as an unforeseen permutation of data, rather than an IO
  112. // failure. IO errors should be caught by the underlying asiolink
  113. // mechanisms and manifested as an unsuccessful IO status in the
  114. // DNSClient callback. Any problem here most likely means the request
  115. // is corrupt in some way and cannot be completed, therefore we will
  116. // log it and transition it to failure.
  117. LOG_ERROR(dctl_logger, DHCP_DDNS_TRANS_SEND_ERROR).arg(ex.what());
  118. transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
  119. }
  120. }
  121. void
  122. NameChangeTransaction::defineEvents() {
  123. // Call superclass impl first.
  124. StateModel::defineEvents();
  125. // Define NCT events.
  126. defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT");
  127. defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT");
  128. defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT");
  129. defineEvent(NO_MORE_SERVERS_EVT, "NO_MORE_SERVERS_EVT");
  130. defineEvent(IO_COMPLETED_EVT, "IO_COMPLETED_EVT");
  131. defineEvent(UPDATE_OK_EVT, "UPDATE_OK_EVT");
  132. defineEvent(UPDATE_FAILED_EVT, "UPDATE_FAILED_EVT");
  133. }
  134. void
  135. NameChangeTransaction::verifyEvents() {
  136. // Call superclass impl first.
  137. StateModel::verifyEvents();
  138. // Verify NCT events.
  139. getEvent(SELECT_SERVER_EVT);
  140. getEvent(SERVER_SELECTED_EVT);
  141. getEvent(SERVER_IO_ERROR_EVT);
  142. getEvent(NO_MORE_SERVERS_EVT);
  143. getEvent(IO_COMPLETED_EVT);
  144. getEvent(UPDATE_OK_EVT);
  145. getEvent(UPDATE_FAILED_EVT);
  146. }
  147. void
  148. NameChangeTransaction::defineStates() {
  149. // Call superclass impl first.
  150. StateModel::defineStates();
  151. // This class is "abstract" in that it does not supply handlers for its
  152. // states, derivations must do that therefore they must define them.
  153. }
  154. void
  155. NameChangeTransaction::verifyStates() {
  156. // Call superclass impl first.
  157. StateModel::verifyStates();
  158. // Verify NCT states. This ensures that derivations provide the handlers.
  159. getState(READY_ST);
  160. getState(SELECTING_FWD_SERVER_ST);
  161. getState(SELECTING_REV_SERVER_ST);
  162. getState(PROCESS_TRANS_OK_ST);
  163. getState(PROCESS_TRANS_FAILED_ST);
  164. }
  165. void
  166. NameChangeTransaction::onModelFailure(const std::string& explanation) {
  167. setNcrStatus(dhcp_ddns::ST_FAILED);
  168. LOG_ERROR(dctl_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR)
  169. .arg(explanation);
  170. }
  171. void
  172. NameChangeTransaction::retryTransition(const int fail_to_state) {
  173. if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) {
  174. // Re-enter the current state with same server selected.
  175. transition(getCurrState(), SERVER_SELECTED_EVT);
  176. } else {
  177. // Transition to given fail_to_state state if we are out
  178. // of retries.
  179. transition(fail_to_state, SERVER_IO_ERROR_EVT);
  180. }
  181. }
  182. void
  183. NameChangeTransaction::setDnsUpdateRequest(D2UpdateMessagePtr& request) {
  184. dns_update_request_ = request;
  185. }
  186. void
  187. NameChangeTransaction::clearDnsUpdateRequest() {
  188. update_attempts_ = 0;
  189. dns_update_request_.reset();
  190. }
  191. void
  192. NameChangeTransaction::setDnsUpdateStatus(const DNSClient::Status& status) {
  193. dns_update_status_ = status;
  194. }
  195. void
  196. NameChangeTransaction::setDnsUpdateResponse(D2UpdateMessagePtr& response) {
  197. dns_update_response_ = response;
  198. }
  199. void
  200. NameChangeTransaction::clearDnsUpdateResponse() {
  201. dns_update_response_.reset();
  202. }
  203. void
  204. NameChangeTransaction::setForwardChangeCompleted(const bool value) {
  205. forward_change_completed_ = value;
  206. }
  207. void
  208. NameChangeTransaction::setReverseChangeCompleted(const bool value) {
  209. reverse_change_completed_ = value;
  210. }
  211. void
  212. NameChangeTransaction::setUpdateAttempts(const size_t value) {
  213. update_attempts_ = value;
  214. }
  215. D2UpdateMessagePtr
  216. NameChangeTransaction::prepNewRequest(DdnsDomainPtr domain) {
  217. if (!domain) {
  218. isc_throw(NameChangeTransactionError,
  219. "prepNewRequest - domain cannot be null");
  220. }
  221. try {
  222. // Create a "blank" update request.
  223. D2UpdateMessagePtr request(new D2UpdateMessage(D2UpdateMessage::
  224. OUTBOUND));
  225. // Construct the Zone Section.
  226. dns::Name zone_name(domain->getName());
  227. request->setZone(zone_name, dns::RRClass::IN());
  228. return (request);
  229. } catch (const std::exception& ex) {
  230. isc_throw(NameChangeTransactionError, "Cannot create new request :"
  231. << ex.what());
  232. }
  233. }
  234. void
  235. NameChangeTransaction::addLeaseAddressRdata(dns::RRsetPtr& rrset) {
  236. if (!rrset) {
  237. isc_throw(NameChangeTransactionError,
  238. "addLeaseAddressRdata - RRset cannot cannot be null");
  239. }
  240. try {
  241. // Manufacture an RData from the lease address then add it to the RR.
  242. dns::rdata::ConstRdataPtr rdata;
  243. if (ncr_->isV4()) {
  244. rdata.reset(new dns::rdata::in::A(ncr_->getIpAddress()));
  245. } else {
  246. rdata.reset(new dns::rdata::in::AAAA(ncr_->getIpAddress()));
  247. }
  248. rrset->addRdata(rdata);
  249. } catch (const std::exception& ex) {
  250. isc_throw(NameChangeTransactionError, "Cannot add address rdata: "
  251. << ex.what());
  252. }
  253. }
  254. void
  255. NameChangeTransaction::addDhcidRdata(dns::RRsetPtr& rrset) {
  256. if (!rrset) {
  257. isc_throw(NameChangeTransactionError,
  258. "addDhcidRdata - RRset cannot cannot be null");
  259. }
  260. try {
  261. const std::vector<uint8_t>& ncr_dhcid = ncr_->getDhcid().getBytes();
  262. util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
  263. dns::rdata::ConstRdataPtr rdata (new dns::rdata::in::
  264. DHCID(buffer, ncr_dhcid.size()));
  265. rrset->addRdata(rdata);
  266. } catch (const std::exception& ex) {
  267. isc_throw(NameChangeTransactionError, "Cannot add DCHID rdata: "
  268. << ex.what());
  269. }
  270. }
  271. void
  272. NameChangeTransaction::addPtrRdata(dns::RRsetPtr& rrset) {
  273. if (!rrset) {
  274. isc_throw(NameChangeTransactionError,
  275. "addPtrRdata - RRset cannot cannot be null");
  276. }
  277. try {
  278. dns::rdata::ConstRdataPtr rdata(new dns::rdata::generic::
  279. PTR(getNcr()->getFqdn()));
  280. rrset->addRdata(rdata);
  281. } catch (const std::exception& ex) {
  282. isc_throw(NameChangeTransactionError, "Cannot add PTR rdata: "
  283. << ex.what());
  284. }
  285. }
  286. const dhcp_ddns::NameChangeRequestPtr&
  287. NameChangeTransaction::getNcr() const {
  288. return (ncr_);
  289. }
  290. const TransactionKey&
  291. NameChangeTransaction::getTransactionKey() const {
  292. return (ncr_->getDhcid());
  293. }
  294. dhcp_ddns::NameChangeStatus
  295. NameChangeTransaction::getNcrStatus() const {
  296. return (ncr_->getStatus());
  297. }
  298. DdnsDomainPtr&
  299. NameChangeTransaction::getForwardDomain() {
  300. return (forward_domain_);
  301. }
  302. DdnsDomainPtr&
  303. NameChangeTransaction::getReverseDomain() {
  304. return (reverse_domain_);
  305. }
  306. void
  307. NameChangeTransaction::initServerSelection(const DdnsDomainPtr& domain) {
  308. if (!domain) {
  309. isc_throw(NameChangeTransactionError,
  310. "initServerSelection called with an empty domain");
  311. }
  312. current_server_list_ = domain->getServers();
  313. next_server_pos_ = 0;
  314. current_server_.reset();
  315. }
  316. bool
  317. NameChangeTransaction::selectNextServer() {
  318. if ((current_server_list_) &&
  319. (next_server_pos_ < current_server_list_->size())) {
  320. current_server_ = (*current_server_list_)[next_server_pos_];
  321. // Toss out any previous response.
  322. dns_update_response_.reset();
  323. // @todo Protocol is set on DNSClient constructor. We need
  324. // to propagate a configuration value downward, probably starting
  325. // at global, then domain, then server
  326. // Once that is supported we need to add it here.
  327. dns_client_.reset(new DNSClient(dns_update_response_ , this,
  328. DNSClient::UDP));
  329. ++next_server_pos_;
  330. return (true);
  331. }
  332. return (false);
  333. }
  334. const DNSClientPtr&
  335. NameChangeTransaction::getDNSClient() const {
  336. return (dns_client_);
  337. }
  338. const DnsServerInfoPtr&
  339. NameChangeTransaction::getCurrentServer() const {
  340. return (current_server_);
  341. }
  342. void
  343. NameChangeTransaction::setNcrStatus(const dhcp_ddns::NameChangeStatus& status) {
  344. return (ncr_->setStatus(status));
  345. }
  346. const D2UpdateMessagePtr&
  347. NameChangeTransaction::getDnsUpdateRequest() const {
  348. return (dns_update_request_);
  349. }
  350. DNSClient::Status
  351. NameChangeTransaction::getDnsUpdateStatus() const {
  352. return (dns_update_status_);
  353. }
  354. const D2UpdateMessagePtr&
  355. NameChangeTransaction::getDnsUpdateResponse() const {
  356. return (dns_update_response_);
  357. }
  358. bool
  359. NameChangeTransaction::getForwardChangeCompleted() const {
  360. return (forward_change_completed_);
  361. }
  362. bool
  363. NameChangeTransaction::getReverseChangeCompleted() const {
  364. return (reverse_change_completed_);
  365. }
  366. size_t
  367. NameChangeTransaction::getUpdateAttempts() const {
  368. return (update_attempts_);
  369. }
  370. const dns::RRType&
  371. NameChangeTransaction::getAddressRRType() const {
  372. return (ncr_->isV4() ? dns::RRType::A() : dns::RRType::AAAA());
  373. }
  374. } // namespace isc::d2
  375. } // namespace isc