nc_test_utils.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright (C) 2013-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. #ifndef NC_TEST_UTILS_H
  15. #define NC_TEST_UTILS_H
  16. /// @file nc_test_utils.h prototypes for functions related transaction testing.
  17. #include <asiolink/io_service.h>
  18. #include <asiolink/interval_timer.h>
  19. #include <d2/nc_trans.h>
  20. #include <boost/asio/ip/udp.hpp>
  21. #include <boost/asio/socket_base.hpp>
  22. #include <gtest/gtest.h>
  23. namespace isc {
  24. namespace d2 {
  25. extern const char* TEST_DNS_SERVER_IP;
  26. extern size_t TEST_DNS_SERVER_PORT;
  27. // Not extern'ed to allow use as array size
  28. const int TEST_MSG_MAX = 1024;
  29. typedef boost::shared_ptr<boost::asio::ip::udp::socket> SocketPtr;
  30. /// @brief This class simulates a DNS server. It is capable of performing
  31. /// an asynchronous read, governed by an IOService, and responding to received
  32. /// requests in a given manner.
  33. class FauxServer {
  34. public:
  35. enum ResponseMode {
  36. USE_RCODE, // Generate a response with a given RCODE
  37. CORRUPT_RESP, // Generate a corrupt response
  38. INVALID_TSIG // Generate a repsonse with the wrong TSIG key
  39. };
  40. // Reference to IOService to use for IO processing.
  41. asiolink::IOService& io_service_;
  42. // IP address at which to listen for requests.
  43. const asiolink::IOAddress& address_;
  44. // Port on which to listen for requests.
  45. size_t port_;
  46. // Socket on which listening is done.
  47. SocketPtr server_socket_;
  48. // Stores the end point of requesting client.
  49. boost::asio::ip::udp::endpoint remote_;
  50. // Buffer in which received packets are stuffed.
  51. uint8_t receive_buffer_[TEST_MSG_MAX];
  52. // Flag which indicates if a receive has been initiated but
  53. // not yet completed.
  54. bool receive_pending_;
  55. // Indicates if server is in perpetual receive mode. If true once
  56. // a receive has been completed, a new one will be automatically
  57. // initiated.
  58. bool perpetual_receive_;
  59. // TSIG Key to use to verify requests and sign responses. If its
  60. // NULL TSIG is not used.
  61. dns::TSIGKeyPtr tsig_key_;
  62. /// @brief Constructor
  63. ///
  64. /// @param io_service IOService to be used for socket IO.
  65. /// @param address IP address at which the server should listen.
  66. /// @param port Port number at which the server should listen.
  67. FauxServer(asiolink::IOService& io_service, asiolink::IOAddress& address,
  68. size_t port);
  69. /// @brief Constructor
  70. ///
  71. /// @param io_service IOService to be used for socket IO.
  72. /// @param server DnsServerInfo of server the DNS server. This supplies the
  73. /// server's ip address and port.
  74. FauxServer(asiolink::IOService& io_service, DnsServerInfo& server);
  75. /// @brief Destructor
  76. virtual ~FauxServer();
  77. /// @brief Initiates an asynchronous receive
  78. ///
  79. /// Starts the server listening for requests. Upon completion of the
  80. /// listen, the callback method, requestHandler, is invoked.
  81. ///
  82. /// @param response_mode Selects how the server responds to a request
  83. /// @param response_rcode The Rcode value set in the response. Not used
  84. /// for all modes.
  85. void receive (const ResponseMode& response_mode,
  86. const dns::Rcode& response_rcode=dns::Rcode::NOERROR());
  87. /// @brief Socket IO Completion callback
  88. ///
  89. /// This method servers as the Server's UDP socket receive callback handler.
  90. /// When the receive completes the handler is invoked with the parameters
  91. /// listed.
  92. ///
  93. /// @param error result code of the receive (determined by asio layer)
  94. /// @param bytes_recvd number of bytes received, if any
  95. /// @param response_mode type of response the handler should produce
  96. /// @param response_rcode value of Rcode in the response constructed by
  97. /// handler
  98. void requestHandler(const boost::system::error_code& error,
  99. std::size_t bytes_recvd,
  100. const ResponseMode& response_mode,
  101. const dns::Rcode& response_rcode);
  102. /// @brief Returns true if a receive has been started but not completed.
  103. bool isReceivePending() {
  104. return receive_pending_;
  105. }
  106. /// @brief Sets the TSIG key to the given value.
  107. ///
  108. /// @param tsig_key Pointer to the TSIG key to use. If the pointer is
  109. /// empty, TSIG will not be used.
  110. void setTSIGKey (const dns::TSIGKeyPtr& tsig_key) {
  111. tsig_key_ = tsig_key;
  112. }
  113. };
  114. /// @brief Provides a means to process IOService IO for a finite amount of time.
  115. ///
  116. /// This class instantiates an IOService provides a single method, runTimedIO
  117. /// which will run the IOService for no more than a finite amount of time,
  118. /// at least one event is executed or the IOService is stopped.
  119. /// It provides an virtual handler for timer expiration event. It is
  120. /// intended to be used as a base class for test fixtures that need to process
  121. /// IO by providing them a consistent way to do so while retaining a safety
  122. /// valve so tests do not hang.
  123. class TimedIO {
  124. public:
  125. asiolink::IOServicePtr io_service_;
  126. asiolink::IntervalTimer timer_;
  127. int run_time_;
  128. // Constructor
  129. TimedIO();
  130. // Destructor
  131. virtual ~TimedIO();
  132. /// @brief IO Timer expiration handler
  133. ///
  134. /// Stops the IOService and fails the current test.
  135. virtual void timesUp();
  136. /// @brief Processes IO till time expires or at least one handler executes.
  137. ///
  138. /// This method first polls IOService to run any ready handlers. If no
  139. /// handlers are ready, it starts the internal time to run for the given
  140. /// amount of time and invokes service's run_one method. This method
  141. /// blocks until at least one handler executes or the IO Service is stopped.
  142. /// Upon completion of this method the timer is cancelled. Should the
  143. /// timer expires prior to run_one returning, the timesUp handler will be
  144. /// invoked which stops the IO service and fails the test.
  145. ///
  146. /// Note that this method closely mimics the runIO method in D2Process.
  147. ///
  148. /// @param run_time maximum length of time to run in milliseconds before
  149. /// timing out.
  150. ///
  151. /// @return Returns the number of handlers executed or zero. A return of
  152. /// zero indicates that the IOService has been stopped.
  153. int runTimedIO(int run_time);
  154. };
  155. /// @brief Base class Test fixture for testing transactions.
  156. class TransactionTest : public TimedIO, public ::testing::Test {
  157. public:
  158. dhcp_ddns::NameChangeRequestPtr ncr_;
  159. DdnsDomainPtr forward_domain_;
  160. DdnsDomainPtr reverse_domain_;
  161. D2CfgMgrPtr cfg_mgr_;
  162. /// #brief constants used to specify change directions for a transaction.
  163. static const unsigned int FORWARD_CHG; // Only forward change.
  164. static const unsigned int REVERSE_CHG; // Only reverse change.
  165. static const unsigned int FWD_AND_REV_CHG; // Both forward and reverse.
  166. TransactionTest();
  167. virtual ~TransactionTest();
  168. /// @brief Creates a transaction which requests an IPv4 DNS update.
  169. ///
  170. /// The transaction is constructed around a predefined (i.e. "canned")
  171. /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
  172. /// changes requested. Based upon the change mask, the transaction
  173. /// will have either the forward, reverse, or both domains populated.
  174. ///
  175. /// @param change_type selects the type of change requested, CHG_ADD or
  176. /// CHG_REMOVE.
  177. /// @param change_mask determines which change directions are requested
  178. /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
  179. /// @param tsig_key_info pointer to the TSIGKeyInfo to assign to both
  180. /// domains in the transaction. This will cause the transaction to
  181. /// use TSIG. If the pointer is empty, TSIG will not be used.
  182. void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type,
  183. int change_mask,
  184. const TSIGKeyInfoPtr& tsig_key_info =
  185. TSIGKeyInfoPtr());
  186. /// @brief Creates a transaction which requests an IPv4 DNS update.
  187. ///
  188. /// Convenience wrapper around the above method which accepts a string
  189. /// key_name from which the TSIGKeyInfo is constructed. Note the string
  190. /// may not be blank.
  191. ///
  192. /// @param change_type selects the type of change requested, CHG_ADD or
  193. /// CHG_REMOVE.
  194. /// @param change_mask determines which change directions are requested
  195. /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
  196. /// @param key_name value to use to create TSIG key. The value may not
  197. /// be blank.
  198. void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type,
  199. int change_mask, const std::string& key_name);
  200. /// @brief Creates a transaction which requests an IPv6 DNS update.
  201. ///
  202. /// The transaction is constructed around a predefined (i.e. "canned")
  203. /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
  204. /// changes requested. Based upon the change mask, the transaction
  205. /// will have either the forward, reverse, or both domains populated.
  206. ///
  207. /// @param change_type selects the type of change requested, CHG_ADD or
  208. /// CHG_REMOVE.
  209. /// @param change_mask determines which change directions are requested
  210. /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
  211. /// @param tsig_key_info pointer to the TSIGKeyInfo to assign to both
  212. /// domains in the transaction. This will cause the transaction to
  213. /// use TSIG. If the pointer is empty, TSIG will not be used.
  214. void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type,
  215. int change_mask,
  216. const TSIGKeyInfoPtr& tsig_key_info =
  217. TSIGKeyInfoPtr());
  218. /// @brief Creates a transaction which requests an IPv6 DNS update.
  219. ///
  220. /// Convenience wrapper around the above method which accepts a string
  221. /// key_name from which the TSIGKeyInfo is constructed. Note the string
  222. /// may not be blank.
  223. ///
  224. /// @param change_type selects the type of change requested, CHG_ADD or
  225. /// CHG_REMOVE.
  226. /// @param change_mask determines which change directions are requested
  227. /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG.
  228. /// @param key_name value to use to create TSIG key, if blank TSIG will not
  229. /// be used.
  230. void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type,
  231. int change_mask, const std::string& key_name);
  232. };
  233. /// @brief Tests the number of RRs in a request section against a given count.
  234. ///
  235. /// This function actually returns the number of RRsetPtrs in a section. Since
  236. /// D2 only uses RRsets with a single RData in each (i.e. 1 RR), it is used
  237. /// as the number of RRs. The dns::Message::getRRCount() cannot be used for
  238. /// this as it returns the number of RDatas in an RRSet which does NOT equate
  239. /// to the number of RRs. RRs with no RData, those with class or type of ANY,
  240. /// are not counted.
  241. ///
  242. /// @param request DNS update request to test
  243. /// @param section enum value of the section to count
  244. /// @param count the expected number of RRs
  245. extern void checkRRCount(const D2UpdateMessagePtr& request,
  246. D2UpdateMessage::UpdateMsgSection section, int count);
  247. /// @brief Tests the zone content of a given request.
  248. ///
  249. /// @param request DNS update request to validate
  250. /// @param exp_zone_name expected value of the zone name in the zone section
  251. extern void checkZone(const D2UpdateMessagePtr& request,
  252. const std::string& exp_zone_name);
  253. /// @brief Tests the contents of an RRset
  254. ///
  255. /// @param rrset Pointer the RRset to test
  256. /// @param exp_name expected value of RRset name (FQDN or reverse ip)
  257. /// @param exp_class expected RRClass value of RRset
  258. /// @param exp_typ expected RRType value of RRset
  259. /// @param exp_ttl expected TTL value of RRset
  260. /// @param ncr NameChangeRequest on which the RRset is based
  261. /// @param has_rdata if true, RRset's rdata will be checked based on it's
  262. /// RRType. Set this to false if the RRset's type supports Rdata but it does
  263. /// not contain it. For instance, prerequisites of type NONE have no Rdata
  264. /// where updates of type NONE may.
  265. extern void checkRR(dns::RRsetPtr rrset, const std::string& exp_name,
  266. const dns::RRClass& exp_class, const dns::RRType& exp_type,
  267. unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr,
  268. bool has_rdata=true);
  269. /// @brief Fetches an RR(set) from a given section of a request
  270. ///
  271. /// @param request DNS update request from which the RR should come
  272. /// @param section enum value of the section from which the RR should come
  273. /// @param index zero-based index of the RR of interest.
  274. ///
  275. /// @return Pointer to the RR of interest, empty pointer if the index is out
  276. /// of range.
  277. extern dns::RRsetPtr getRRFromSection(const D2UpdateMessagePtr& request,
  278. D2UpdateMessage::UpdateMsgSection section,
  279. int index);
  280. /// @brief Creates a NameChangeRequest from a JSON string
  281. ///
  282. /// @param ncr_str JSON string form of a NameChangeRequest. Example:
  283. /// @code
  284. /// const char* msg_str =
  285. /// "{"
  286. /// " \"change-type\" : 0 , "
  287. /// " \"forward-change\" : true , "
  288. /// " \"reverse-change\" : true , "
  289. /// " \"fqdn\" : \"my.example.com.\" , "
  290. /// " \"ip-address\" : \"192.168.2.1\" , "
  291. /// " \"dhcid\" : \"0102030405060708\" , "
  292. /// " \"lease-expires-on\" : \"20130121132405\" , "
  293. /// " \"lease-length\" : 1300 "
  294. /// "}";
  295. ///
  296. /// @endcode
  297. /// @brief Verifies a forward mapping addition DNS update request
  298. ///
  299. /// Tests that the DNS Update request for a given transaction, is correct for
  300. /// adding a forward DNS mapping.
  301. ///
  302. /// @param tran Transaction containing the request to be verified.
  303. extern void checkAddFwdAddressRequest(NameChangeTransaction& tran);
  304. /// @brief Verifies a forward mapping replacement DNS update request
  305. ///
  306. /// Tests that the DNS Update request for a given transaction, is correct for
  307. /// replacing a forward DNS mapping.
  308. ///
  309. /// @param tran Transaction containing the request to be verified.
  310. extern void checkReplaceFwdAddressRequest(NameChangeTransaction& tran);
  311. /// @brief Verifies a reverse mapping replacement DNS update request
  312. ///
  313. /// Tests that the DNS Update request for a given transaction, is correct for
  314. /// replacing a reverse DNS mapping.
  315. ///
  316. /// @param tran Transaction containing the request to be verified.
  317. extern void checkReplaceRevPtrsRequest(NameChangeTransaction& tran);
  318. /// @brief Verifies a forward address removal DNS update request
  319. ///
  320. /// Tests that the DNS Update request for a given transaction, is correct for
  321. /// removing the forward address DNS entry.
  322. ///
  323. /// @param tran Transaction containing the request to be verified.
  324. extern void checkRemoveFwdAddressRequest(NameChangeTransaction& tran);
  325. /// @brief Verifies a forward RR removal DNS update request
  326. ///
  327. /// Tests that the DNS Update request for a given transaction, is correct for
  328. /// removing forward RR DNS entries.
  329. ///
  330. /// @param tran Transaction containing the request to be verified.
  331. extern void checkRemoveFwdRRsRequest(NameChangeTransaction& tran);
  332. /// @brief Verifies a reverse mapping removal DNS update request
  333. ///
  334. /// Tests that the DNS Update request for a given transaction, is correct for
  335. /// removing a reverse DNS mapping.
  336. ///
  337. /// @param tran Transaction containing the request to be verified.
  338. extern void checkRemoveRevPtrsRequest(NameChangeTransaction& tran);
  339. /// @brief Creates a NameChangeRequest from JSON string.
  340. ///
  341. /// @param ncr_str string of JSON text from which to make the request.
  342. ///
  343. /// @return Pointer to newly created request.
  344. ///
  345. /// @throw Underlying methods may throw.
  346. extern
  347. dhcp_ddns::NameChangeRequestPtr makeNcrFromString(const std::string& ncr_str);
  348. /// @brief Creates a DdnsDomain with the one server.
  349. ///
  350. /// @param zone_name zone name of the domain
  351. /// @param key_name TSIG key name of the TSIG key for this domain. It will
  352. /// create a TSIGKeyInfo based on the key_name and assign it to the domain.
  353. ///
  354. /// @throw Underlying methods may throw.
  355. extern DdnsDomainPtr makeDomain(const std::string& zone_name,
  356. const std::string& key_name);
  357. /// @brief Creates a DdnsDomain with the one server.
  358. ///
  359. /// @param zone_name zone name of the domain
  360. /// @param tsig_key_info pointer to the TSIGInfog key for this domain.
  361. /// Defaults to an empty pointer, meaning this domain has no key.
  362. ///
  363. /// @throw Underlying methods may throw.
  364. extern DdnsDomainPtr makeDomain(const std::string& zone_name,
  365. const TSIGKeyInfoPtr&
  366. tsig_key_info = TSIGKeyInfoPtr());
  367. /// @brief Creates a TSIGKeyInfo
  368. ///
  369. /// @param key_name name of the key
  370. /// @param secret key secret data as a base64 encoded string. If blank,
  371. /// then the secret value will be generated from key_name.
  372. /// @param algorithm algorithm to use. Defaults to MD5.
  373. /// @return a TSIGKeyInfoPtr for the newly created key. If key_name is blank
  374. /// the pointer will be empty.
  375. /// @throw Underlying methods may throw.
  376. extern
  377. TSIGKeyInfoPtr makeTSIGKeyInfo(const std::string& key_name,
  378. const std::string& secret = "",
  379. const std::string& algorithm
  380. = TSIGKeyInfo::HMAC_MD5_STR);
  381. /// @brief Creates a DnsServerInfo and adds it to the given DdnsDomain.
  382. ///
  383. /// The server is created and added to the domain, without duplicate entry
  384. /// checking.
  385. ///
  386. /// @param domain DdnsDomain to which to add the server
  387. /// @param name new server's host name of the server
  388. /// @param ip new server's ip address
  389. /// @param port new server's port
  390. ///
  391. /// @throw Underlying methods may throw.
  392. extern void addDomainServer(DdnsDomainPtr& domain, const std::string& name,
  393. const std::string& ip = TEST_DNS_SERVER_IP,
  394. const size_t port = TEST_DNS_SERVER_PORT);
  395. /// @brief Creates a hex text dump of the given data buffer.
  396. ///
  397. /// This method is not used for testing but is handy for debugging. It creates
  398. /// a pleasantly formatted string of 2-digits per byte separated by spaces with
  399. /// 16 bytes per line.
  400. ///
  401. /// @param data pointer to the data to dump
  402. /// @param len size (in bytes) of data
  403. extern std::string toHexText(const uint8_t* data, size_t len);
  404. }; // namespace isc::d2
  405. }; // namespace isc
  406. #endif