d2_client_mgr.cc 13 KB

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