d2_client.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // Copyright (C) 2013-2014 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 <dhcp_ddns/ncr_udp.h>
  15. #include <dhcpsrv/d2_client.h>
  16. #include <dhcpsrv/dhcpsrv_log.h>
  17. #include <string>
  18. using namespace std;
  19. namespace isc {
  20. namespace dhcp {
  21. //***************************** D2ClientConfig ********************************
  22. D2ClientConfig::D2ClientConfig(const bool enable_updates,
  23. const isc::asiolink::IOAddress& server_ip,
  24. const size_t server_port,
  25. const dhcp_ddns::
  26. NameChangeProtocol& ncr_protocol,
  27. const dhcp_ddns::
  28. NameChangeFormat& ncr_format,
  29. const bool always_include_fqdn,
  30. const bool override_no_update,
  31. const bool override_client_update,
  32. const bool replace_client_name,
  33. const std::string& generated_prefix,
  34. const std::string& qualifying_suffix)
  35. : enable_updates_(enable_updates),
  36. server_ip_(server_ip.getAddress()),
  37. server_port_(server_port),
  38. ncr_protocol_(ncr_protocol),
  39. ncr_format_(ncr_format),
  40. always_include_fqdn_(always_include_fqdn),
  41. override_no_update_(override_no_update),
  42. override_client_update_(override_client_update),
  43. replace_client_name_(replace_client_name),
  44. generated_prefix_(generated_prefix),
  45. qualifying_suffix_(qualifying_suffix) {
  46. validateContents();
  47. }
  48. D2ClientConfig::D2ClientConfig()
  49. : enable_updates_(false),
  50. server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
  51. server_port_(0),
  52. ncr_protocol_(dhcp_ddns::NCR_UDP),
  53. ncr_format_(dhcp_ddns::FMT_JSON),
  54. always_include_fqdn_(false),
  55. override_no_update_(false),
  56. override_client_update_(false),
  57. replace_client_name_(false),
  58. generated_prefix_("myhost"),
  59. qualifying_suffix_("example.com") {
  60. validateContents();
  61. }
  62. D2ClientConfig::~D2ClientConfig(){};
  63. void
  64. D2ClientConfig::validateContents() {
  65. if (ncr_format_ != dhcp_ddns::FMT_JSON) {
  66. isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
  67. << dhcp_ddns::ncrFormatToString(ncr_format_)
  68. << " is not yet supported");
  69. }
  70. if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
  71. isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
  72. << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
  73. << " is not yet supported");
  74. }
  75. /// @todo perhaps more validation we should do yet?
  76. /// Are there any invalid combinations of options we need to test against?
  77. }
  78. bool
  79. D2ClientConfig::operator == (const D2ClientConfig& other) const {
  80. return ((enable_updates_ == other.enable_updates_) &&
  81. (server_ip_ == other.server_ip_) &&
  82. (server_port_ == other.server_port_) &&
  83. (ncr_protocol_ == other.ncr_protocol_) &&
  84. (ncr_format_ == other.ncr_format_) &&
  85. (always_include_fqdn_ == other.always_include_fqdn_) &&
  86. (override_no_update_ == other.override_no_update_) &&
  87. (override_client_update_ == other.override_client_update_) &&
  88. (replace_client_name_ == other.replace_client_name_) &&
  89. (generated_prefix_ == other.generated_prefix_) &&
  90. (qualifying_suffix_ == other.qualifying_suffix_));
  91. }
  92. bool
  93. D2ClientConfig::operator != (const D2ClientConfig& other) const {
  94. return (!(*this == other));
  95. }
  96. std::string
  97. D2ClientConfig::toText() const {
  98. std::ostringstream stream;
  99. stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
  100. if (enable_updates_) {
  101. stream << ", server_ip: " << server_ip_.toText()
  102. << ", server_port: " << server_port_
  103. << ", ncr_protocol: " << ncr_protocol_
  104. << ", ncr_format: " << ncr_format_
  105. << ", always_include_fqdn: " << (always_include_fqdn_ ?
  106. "yes" : "no")
  107. << ", override_no_update: " << (override_no_update_ ?
  108. "yes" : "no")
  109. << ", override_client_update: " << (override_client_update_ ?
  110. "yes" : "no")
  111. << ", replace_client_name: " << (replace_client_name_ ?
  112. "yes" : "no")
  113. << ", generated_prefix: [" << generated_prefix_ << "]"
  114. << ", qualifying_suffix: [" << qualifying_suffix_ << "]";
  115. }
  116. return (stream.str());
  117. }
  118. std::ostream&
  119. operator<<(std::ostream& os, const D2ClientConfig& config) {
  120. os << config.toText();
  121. return (os);
  122. }
  123. //******************************** D2ClientMgr ********************************
  124. D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
  125. name_change_sender_(), private_io_service_(), sender_io_service_(NULL) {
  126. // Default constructor initializes with a disabled configuration.
  127. }
  128. D2ClientMgr::~D2ClientMgr(){
  129. }
  130. void
  131. D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
  132. if (!new_config) {
  133. isc_throw(D2ClientError,
  134. "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
  135. }
  136. // Don't do anything unless configuration values are actually different.
  137. if (*d2_client_config_ != *new_config) {
  138. if (!new_config->getEnableUpdates()) {
  139. // Updating has been turned off, destroy current sender.
  140. // Any queued requests are tossed.
  141. name_change_sender_.reset();
  142. } else {
  143. dhcp_ddns::NameChangeSenderPtr new_sender;
  144. switch (new_config->getNcrProtocol()) {
  145. case dhcp_ddns::NCR_UDP: {
  146. /// @todo Should we be able to configure a sender's client
  147. /// side ip and port? We should certainly be able to
  148. /// configure a maximum queue size. These were overlooked
  149. /// but are covered in Trac# 3328.
  150. isc::asiolink::IOAddress any_addr("0.0.0.0");
  151. uint32_t any_port = 0;
  152. uint32_t queue_max = 1024;
  153. // Instantiate a new sender.
  154. new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
  155. any_addr, any_port,
  156. new_config->getServerIp(),
  157. new_config->getServerPort(),
  158. new_config->getNcrFormat(),
  159. *this, queue_max));
  160. break;
  161. }
  162. default:
  163. // In theory you can't get here.
  164. isc_throw(D2ClientError, "Invalid sender Protocol: "
  165. << new_config->getNcrProtocol());
  166. break;
  167. }
  168. // Transfer queued requests from previous sender to the new one.
  169. /// @todo - Should we consider anything queued to be wrong?
  170. /// If only server values changed content might still be right but
  171. /// if content values changed (e.g. suffix or an override flag)
  172. /// then the queued contents might now be invalid. There is
  173. /// no way to regenerate them if they are wrong.
  174. if (name_change_sender_) {
  175. name_change_sender_->stopSending();
  176. new_sender->assumeQueue(*name_change_sender_);
  177. }
  178. // Replace the old sender with the new one.
  179. name_change_sender_ = new_sender;
  180. }
  181. }
  182. // Update the configuration.
  183. d2_client_config_ = new_config;
  184. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
  185. .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
  186. "DHCP_DDNS updates enabled");
  187. }
  188. bool
  189. D2ClientMgr::ddnsEnabled() {
  190. return (d2_client_config_->getEnableUpdates());
  191. }
  192. const D2ClientConfigPtr&
  193. D2ClientMgr::getD2ClientConfig() const {
  194. return (d2_client_config_);
  195. }
  196. void
  197. D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
  198. bool& server_s, bool& server_n) const {
  199. // Per RFC 4702 & 4704, the client N and S flags allow the client to
  200. // request one of three options:
  201. //
  202. // N flag S flag Option
  203. // ------------------------------------------------------------------
  204. // 0 0 client wants to do forward updates (section 3.2)
  205. // 0 1 client wants server to do forward updates (section 3.3)
  206. // 1 0 client wants no one to do updates (section 3.4)
  207. // 1 1 invalid combination
  208. // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
  209. //
  210. // Make a bit mask from the client's flags and use it to set the response
  211. // flags accordingly.
  212. const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
  213. switch (mask) {
  214. case 0:
  215. // If updates are enabled and we are overriding client delegation
  216. // then S flag should be true.
  217. server_s = (d2_client_config_->getEnableUpdates() &&
  218. d2_client_config_->getOverrideClientUpdate());
  219. break;
  220. case 1:
  221. server_s = d2_client_config_->getEnableUpdates();
  222. break;
  223. case 2:
  224. // If updates are enabled and we are overriding "no updates" then
  225. // S flag should be true.
  226. server_s = (d2_client_config_->getEnableUpdates() &&
  227. d2_client_config_->getOverrideNoUpdate());
  228. break;
  229. default:
  230. // RFCs declare this an invalid combination.
  231. isc_throw(isc::BadValue,
  232. "Invalid client FQDN - N and S cannot both be 1");
  233. break;
  234. }
  235. /// @todo Currently we are operating under the premise that N should be 1
  236. /// if the server is not doing updates nor do we have configuration
  237. /// controls to govern forward and reverse updates independently.
  238. /// In addition, the client FQDN flags cannot explicitly suggest what to
  239. /// do with reverse updates. They request either forward updates or no
  240. /// updates. In other words, the client cannot request the server do or
  241. /// not do reverse updates. For now, we are either going to do updates in
  242. /// both directions or none at all. If and when additional configuration
  243. /// parameters are added this logic will have to be reassessed.
  244. server_n = !server_s;
  245. }
  246. std::string
  247. D2ClientMgr::generateFqdn(const asiolink::IOAddress& address) const {
  248. std::string hostname = address.toText();
  249. std::replace(hostname.begin(), hostname.end(),
  250. (address.isV4() ? '.' : ':'), '-');
  251. std::ostringstream gen_name;
  252. gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
  253. return (qualifyName(gen_name.str()));
  254. }
  255. std::string
  256. D2ClientMgr::qualifyName(const std::string& partial_name) const {
  257. std::ostringstream gen_name;
  258. gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
  259. // Tack on a trailing dot in case suffix doesn't have one.
  260. std::string str = gen_name.str();
  261. size_t len = str.length();
  262. if ((len > 0) && (str[len - 1] != '.')) {
  263. gen_name << ".";
  264. }
  265. return (gen_name.str());
  266. }
  267. void
  268. D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
  269. // Create a our own service instance when we are not being multiplexed
  270. // into an external service..
  271. private_io_service_.reset(new asiolink::IOService());
  272. startSender(error_handler, *private_io_service_);
  273. }
  274. void
  275. D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
  276. isc::asiolink::IOService& io_service) {
  277. if (!name_change_sender_) {
  278. isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
  279. }
  280. if (!error_handler) {
  281. isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
  282. }
  283. // Set the error handler.
  284. client_error_handler_ = error_handler;
  285. // Remember the io service being used.
  286. sender_io_service_ = &io_service;
  287. // Start the sender on the given service.
  288. name_change_sender_->startSending(*sender_io_service_);
  289. /// @todo need to register sender's select-fd with IfaceMgr once 3315 is
  290. /// done.
  291. }
  292. bool
  293. D2ClientMgr::amSending() const {
  294. return (name_change_sender_ && name_change_sender_->amSending());
  295. }
  296. void
  297. D2ClientMgr::stopSender() {
  298. if (!name_change_sender_) {
  299. isc_throw(D2ClientError, "D2ClientMgr::stopSender sender is null");
  300. }
  301. /// @todo need to unregister sender's select-fd with IfaceMgr once 3315 is
  302. /// done.
  303. name_change_sender_->stopSending();
  304. }
  305. void
  306. D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
  307. if (!name_change_sender_) {
  308. isc_throw(D2ClientError, "D2ClientMgr::sendRequest sender is null");
  309. }
  310. name_change_sender_->sendRequest(ncr);
  311. }
  312. size_t
  313. D2ClientMgr::getQueueSize() const {
  314. if (!name_change_sender_) {
  315. isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
  316. }
  317. return(name_change_sender_->getQueueSize());
  318. }
  319. const dhcp_ddns::NameChangeRequestPtr&
  320. D2ClientMgr::peekAt(const size_t index) const {
  321. if (!name_change_sender_) {
  322. isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
  323. }
  324. return (name_change_sender_->peekAt(index));
  325. }
  326. void
  327. D2ClientMgr::clearQueue() {
  328. if (!name_change_sender_) {
  329. isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
  330. }
  331. name_change_sender_->clearSendQueue();
  332. }
  333. void
  334. D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result,
  335. dhcp_ddns::NameChangeRequestPtr& ncr) {
  336. if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
  337. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  338. DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
  339. } else {
  340. // Handler is mandatory but test it just to be safe.
  341. /// @todo Until we have a better feel for how errors need to be
  342. /// handled we farm it out to the application layer.
  343. if (client_error_handler_) {
  344. // Handler is not supposed to throw, but catch just in case.
  345. try {
  346. (client_error_handler_)(result, ncr);
  347. } catch (const std::exception& ex) {
  348. LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
  349. .arg(ex.what());
  350. }
  351. } else {
  352. LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
  353. }
  354. }
  355. }
  356. int
  357. D2ClientMgr::getSelectFd() {
  358. if (!amSending()) {
  359. isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
  360. " not in send mode");
  361. }
  362. return (name_change_sender_->getSelectFd());
  363. }
  364. void
  365. D2ClientMgr::runReadyIO() {
  366. if (!sender_io_service_) {
  367. // This should never happen.
  368. isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
  369. " sender io service is null");
  370. }
  371. // We shouldn't be here if IO isn't ready to execute.
  372. // By running poll we're gauranteed not to hang.
  373. /// @todo Trac# 3325 requests that asiolink::IOService provide a
  374. /// wrapper for poll().
  375. sender_io_service_->get_io_service().poll();
  376. }
  377. }; // namespace dhcp
  378. }; // namespace isc