d2_client_mgr.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/iface_mgr.h>
  8. #include <dhcp_ddns/ncr_udp.h>
  9. #include <dhcpsrv/d2_client_mgr.h>
  10. #include <dhcpsrv/dhcpsrv_log.h>
  11. #include <boost/bind.hpp>
  12. #include <string>
  13. using namespace std;
  14. namespace isc {
  15. namespace dhcp {
  16. D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
  17. name_change_sender_(), private_io_service_(),
  18. registered_select_fd_(util::WatchSocket::SOCKET_NOT_VALID) {
  19. // Default constructor initializes with a disabled configuration.
  20. }
  21. D2ClientMgr::~D2ClientMgr(){
  22. stopSender();
  23. }
  24. void
  25. D2ClientMgr::suspendUpdates() {
  26. if (ddnsEnabled()) {
  27. /// @todo For now we will disable updates and stop sending.
  28. /// This at least provides a means to shut it off if there are errors.
  29. LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES);
  30. d2_client_config_->enableUpdates(false);
  31. if (name_change_sender_) {
  32. stopSender();
  33. }
  34. }
  35. }
  36. void
  37. D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
  38. if (!new_config) {
  39. isc_throw(D2ClientError,
  40. "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
  41. }
  42. // Don't do anything unless configuration values are actually different.
  43. if (*d2_client_config_ != *new_config) {
  44. // Make sure we stop sending first.
  45. stopSender();
  46. if (!new_config->getEnableUpdates()) {
  47. // Updating has been turned off.
  48. // Destroy current sender (any queued requests are tossed).
  49. name_change_sender_.reset();
  50. } else {
  51. dhcp_ddns::NameChangeSenderPtr new_sender;
  52. switch (new_config->getNcrProtocol()) {
  53. case dhcp_ddns::NCR_UDP: {
  54. // Instantiate a new sender.
  55. new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
  56. new_config->getSenderIp(),
  57. new_config->getSenderPort(),
  58. new_config->getServerIp(),
  59. new_config->getServerPort(),
  60. new_config->getNcrFormat(),
  61. *this,
  62. new_config->getMaxQueueSize()));
  63. break;
  64. }
  65. default:
  66. // In theory you can't get here.
  67. isc_throw(D2ClientError, "Invalid sender Protocol: "
  68. << new_config->getNcrProtocol());
  69. break;
  70. }
  71. // Transfer queued requests from previous sender to the new one.
  72. /// @todo - Should we consider anything queued to be wrong?
  73. /// If only server values changed content might still be right but
  74. /// if content values changed (e.g. suffix or an override flag)
  75. /// then the queued contents might now be invalid. There is
  76. /// no way to regenerate them if they are wrong.
  77. if (name_change_sender_) {
  78. new_sender->assumeQueue(*name_change_sender_);
  79. }
  80. // Replace the old sender with the new one.
  81. name_change_sender_ = new_sender;
  82. }
  83. }
  84. // Update the configuration.
  85. d2_client_config_ = new_config;
  86. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
  87. .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
  88. "DHCP_DDNS updates enabled");
  89. }
  90. bool
  91. D2ClientMgr::ddnsEnabled() {
  92. return (d2_client_config_->getEnableUpdates());
  93. }
  94. const D2ClientConfigPtr&
  95. D2ClientMgr::getD2ClientConfig() const {
  96. return (d2_client_config_);
  97. }
  98. void
  99. D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
  100. bool& server_s, bool& server_n) const {
  101. // Per RFC 4702 & 4704, the client N and S flags allow the client to
  102. // request one of three options:
  103. //
  104. // N flag S flag Option
  105. // ------------------------------------------------------------------
  106. // 0 0 client wants to do forward updates (section 3.2)
  107. // 0 1 client wants server to do forward updates (section 3.3)
  108. // 1 0 client wants no one to do updates (section 3.4)
  109. // 1 1 invalid combination
  110. // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
  111. //
  112. // Make a bit mask from the client's flags and use it to set the response
  113. // flags accordingly.
  114. const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
  115. switch (mask) {
  116. case 0:
  117. if (!d2_client_config_->getEnableUpdates()) {
  118. server_s = false;
  119. server_n = true;
  120. } else {
  121. // If updates are enabled and we are overriding client delegation
  122. // then S flag should be true. N-flag should be false.
  123. server_s = d2_client_config_->getOverrideClientUpdate();
  124. server_n = false;
  125. }
  126. break;
  127. case 1:
  128. server_s = d2_client_config_->getEnableUpdates();
  129. server_n = !server_s;
  130. break;
  131. case 2:
  132. // If updates are enabled and we are overriding "no updates" then
  133. // S flag should be true.
  134. server_s = (d2_client_config_->getEnableUpdates() &&
  135. d2_client_config_->getOverrideNoUpdate());
  136. server_n = !server_s;
  137. break;
  138. default:
  139. // RFCs declare this an invalid combination.
  140. isc_throw(isc::BadValue,
  141. "Invalid client FQDN - N and S cannot both be 1");
  142. break;
  143. }
  144. }
  145. std::string
  146. D2ClientMgr::generateFqdn(const asiolink::IOAddress& address,
  147. const bool trailing_dot) const {
  148. std::string hostname = address.toText();
  149. std::replace(hostname.begin(), hostname.end(),
  150. (address.isV4() ? '.' : ':'), '-');
  151. std::ostringstream gen_name;
  152. gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
  153. return (qualifyName(gen_name.str(), trailing_dot));
  154. }
  155. std::string
  156. D2ClientMgr::qualifyName(const std::string& partial_name,
  157. const bool trailing_dot) const {
  158. std::ostringstream gen_name;
  159. gen_name << partial_name;
  160. if (!d2_client_config_->getQualifyingSuffix().empty()) {
  161. std::string str = gen_name.str();
  162. size_t len = str.length();
  163. if ((len > 0) && (str[len - 1] != '.')) {
  164. gen_name << ".";
  165. }
  166. gen_name << d2_client_config_->getQualifyingSuffix();
  167. }
  168. std::string str = gen_name.str();
  169. size_t len = str.length();
  170. if (trailing_dot) {
  171. // If trailing dot should be added but there is no trailing dot,
  172. // append it.
  173. if ((len > 0) && (str[len - 1] != '.')) {
  174. gen_name << ".";
  175. }
  176. } else {
  177. // If the trailing dot should not be appended but it is present,
  178. // remove it.
  179. if ((len > 0) && (str[len - 1] == '.')) {
  180. gen_name.str(str.substr(0,len-1));
  181. }
  182. }
  183. return (gen_name.str());
  184. }
  185. void
  186. D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
  187. if (amSending()) {
  188. return;
  189. }
  190. // Create a our own service instance when we are not being multiplexed
  191. // into an external service..
  192. private_io_service_.reset(new asiolink::IOService());
  193. startSender(error_handler, *private_io_service_);
  194. LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STARTED)
  195. .arg(d2_client_config_->toText());
  196. }
  197. void
  198. D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
  199. isc::asiolink::IOService& io_service) {
  200. if (amSending()) {
  201. return;
  202. }
  203. if (!name_change_sender_) {
  204. isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
  205. }
  206. if (!error_handler) {
  207. isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
  208. }
  209. // Set the error handler.
  210. client_error_handler_ = error_handler;
  211. // Start the sender on the given service.
  212. name_change_sender_->startSending(io_service);
  213. // Register sender's select-fd with IfaceMgr.
  214. // We need to remember the fd that is registered so we can unregister later.
  215. // IO error handling in the sender may alter its select-fd.
  216. registered_select_fd_ = name_change_sender_->getSelectFd();
  217. IfaceMgr::instance().addExternalSocket(registered_select_fd_,
  218. boost::bind(&D2ClientMgr::runReadyIO,
  219. this));
  220. }
  221. bool
  222. D2ClientMgr::amSending() const {
  223. return (name_change_sender_ && name_change_sender_->amSending());
  224. }
  225. void
  226. D2ClientMgr::stopSender() {
  227. /// Unregister sender's select-fd.
  228. if (registered_select_fd_ != util::WatchSocket::SOCKET_NOT_VALID) {
  229. IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
  230. registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
  231. }
  232. // If its not null, call stop.
  233. if (amSending()) {
  234. name_change_sender_->stopSending();
  235. LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STOPPED);
  236. }
  237. }
  238. void
  239. D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
  240. if (!amSending()) {
  241. // This is programmatic error so bust them for it.
  242. isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
  243. }
  244. try {
  245. name_change_sender_->sendRequest(ncr);
  246. } catch (const std::exception& ex) {
  247. LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_NCR_REJECTED)
  248. .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
  249. invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR, ncr);
  250. }
  251. }
  252. void
  253. D2ClientMgr::invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::
  254. Result result,
  255. dhcp_ddns::NameChangeRequestPtr& ncr) {
  256. // Handler is mandatory to enter send mode but test it just to be safe.
  257. if (!client_error_handler_) {
  258. LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
  259. } else {
  260. // Handler is not supposed to throw, but catch just in case.
  261. try {
  262. (client_error_handler_)(result, ncr);
  263. } catch (const std::exception& ex) {
  264. LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
  265. .arg(ex.what());
  266. }
  267. }
  268. }
  269. size_t
  270. D2ClientMgr::getQueueSize() const {
  271. if (!name_change_sender_) {
  272. isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
  273. }
  274. return(name_change_sender_->getQueueSize());
  275. }
  276. size_t
  277. D2ClientMgr::getQueueMaxSize() const {
  278. if (!name_change_sender_) {
  279. isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
  280. }
  281. return(name_change_sender_->getQueueMaxSize());
  282. }
  283. const dhcp_ddns::NameChangeRequestPtr&
  284. D2ClientMgr::peekAt(const size_t index) const {
  285. if (!name_change_sender_) {
  286. isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
  287. }
  288. return (name_change_sender_->peekAt(index));
  289. }
  290. void
  291. D2ClientMgr::clearQueue() {
  292. if (!name_change_sender_) {
  293. isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
  294. }
  295. name_change_sender_->clearSendQueue();
  296. }
  297. void
  298. D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result,
  299. dhcp_ddns::NameChangeRequestPtr& ncr) {
  300. if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
  301. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  302. DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
  303. } else {
  304. invokeClientErrorHandler(result, ncr);
  305. }
  306. }
  307. int
  308. D2ClientMgr::getSelectFd() {
  309. if (!amSending()) {
  310. isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
  311. " not in send mode");
  312. }
  313. return (name_change_sender_->getSelectFd());
  314. }
  315. void
  316. D2ClientMgr::runReadyIO() {
  317. if (!name_change_sender_) {
  318. // This should never happen.
  319. isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
  320. " name_change_sender is null");
  321. }
  322. name_change_sender_->runReadyIO();
  323. }
  324. }; // namespace dhcp
  325. }; // namespace isc