mysql_host_data_source.cc 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468
  1. // Copyright (C) 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 <stdint.h>
  16. #include <asiolink/io_address.h>
  17. #include <dhcp/duid.h>
  18. #include <dhcp/hwaddr.h>
  19. #include <dhcpsrv/dhcpsrv_log.h>
  20. #include <dhcpsrv/mysql_host_data_source.h>
  21. #include <dhcpsrv/mysql_connection.h>
  22. #include <dhcpsrv/db_exceptions.h>
  23. #include <dhcpsrv/host.h>
  24. #include <boost/static_assert.hpp>
  25. #include <mysqld_error.h>
  26. #include <iostream>
  27. #include <iomanip>
  28. #include <sstream>
  29. #include <string>
  30. using namespace isc;
  31. using namespace isc::dhcp;
  32. using namespace isc::asiolink;
  33. using namespace std;
  34. namespace {
  35. /// @brief Maximum length of classes stored in a dhcp4/6_client_classes field
  36. ///
  37. const size_t CLIENT_CLASSES_MAX_LEN = 255;
  38. /// @brief Maximum length of the hostname stored in DNS.
  39. ///
  40. /// This length is restricted by the length of the domain-name carried
  41. /// in the Client FQDN %Option (see RFC4702 and RFC4704).
  42. const size_t HOSTNAME_MAX_LEN = 255;
  43. TaggedStatement tagged_statements[] = {
  44. {MySqlHostDataSource::INSERT_HOST,
  45. "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
  46. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  47. "dhcp4_client_classes, dhcp6_client_classes) "
  48. "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
  49. {MySqlHostDataSource::INSERT_V6_RESRV,
  50. "INSERT INTO ipv6_reservations(address, prefix_len, type, "
  51. "dhcp6_iaid, host_id) "
  52. "VALUES (?,?,?,?,?)"},
  53. {MySqlHostDataSource::GET_V6_RESRV,
  54. "SELECT reservation_id, address, prefix_len, type, dhcp6_iaid, host_id "
  55. "FROM ipv6_reservations "
  56. "WHERE host_id = ?"},
  57. {MySqlHostDataSource::GET_HOST_HWADDR_DUID,
  58. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  59. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  60. "dhcp4_client_classes, dhcp6_client_classes "
  61. "FROM hosts "
  62. "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ?"},
  63. {MySqlHostDataSource::GET_HOST_ADDR,
  64. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  65. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  66. "dhcp4_client_classes, dhcp6_client_classes "
  67. "FROM hosts "
  68. "WHERE ipv4_address = ?"},
  69. {MySqlHostDataSource::GET_HOST_SUBID4_DHCPID,
  70. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  71. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  72. "dhcp4_client_classes, dhcp6_client_classes "
  73. "FROM hosts "
  74. "WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type = ? "
  75. " AND dhcp_identifier = ?"},
  76. {MySqlHostDataSource::GET_HOST_SUBID6_DHCPID,
  77. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  78. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  79. "dhcp4_client_classes, dhcp6_client_classes "
  80. "FROM hosts "
  81. "WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type = ? "
  82. " AND dhcp_identifier = ?"},
  83. {MySqlHostDataSource::GET_HOST_SUBID_ADDR,
  84. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  85. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  86. "dhcp4_client_classes, dhcp6_client_classes "
  87. "FROM hosts "
  88. "WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
  89. {MySqlHostDataSource::GET_HOST_PREFIX,
  90. "SELECT h.host_id, dhcp_identifier, dhcp_identifier_type, "
  91. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  92. "dhcp4_client_classes, dhcp6_client_classes "
  93. "FROM hosts h, ipv6_reservations r "
  94. "WHERE h.host_id = r.host_id "
  95. "AND r.address = ? AND r.prefix_len = ?"},
  96. {MySqlHostDataSource::GET_VERSION,
  97. "SELECT version, minor FROM schema_version"},
  98. {MySqlHostDataSource::NUM_STATEMENTS, NULL}
  99. };
  100. };
  101. namespace isc {
  102. namespace dhcp {
  103. /// @brief This class represents the exchanges related to hosts.
  104. class MySqlHostReservationExchange {
  105. /// @brief Set number of database columns for this host structure
  106. static const size_t HOST_COLUMNS = 9;
  107. public:
  108. /// @brief Constructor
  109. ///
  110. /// The initialization of the variables here is only to satisfy cppcheck -
  111. /// all variables are initialized/set in the methods before they are used.
  112. MySqlHostReservationExchange() : host_id_(0), dhcp_identifier_length_(0),
  113. dhcp_identifier_type_(0), dhcp4_subnet_id_(0), dhcp6_subnet_id_(0),
  114. ipv4_address_(0), hostname_length_(0), dhcp4_client_classes_length_(0),
  115. dhcp6_client_classes_length_(0), dhcp4_subnet_id_null_(MLM_FALSE),
  116. dhcp6_subnet_id_null_(MLM_FALSE), ipv4_address_null_(MLM_FALSE),
  117. hostname_null_(MLM_FALSE), dhcp4_client_classes_null_(MLM_FALSE),
  118. dhcp6_client_classes_null_(MLM_FALSE){
  119. memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
  120. memset(hostname_, 0, sizeof(hostname_));
  121. memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
  122. memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
  123. std::fill(&error_[0], &error_[HOST_COLUMNS], MLM_FALSE);
  124. // Set the column names (for error messages)
  125. columns_[0] = "host_id";
  126. columns_[1] = "dhcp_identifier";
  127. columns_[2] = "dhcp_identifier_type";
  128. columns_[3] = "dhcp4_subnet_id";
  129. columns_[4] = "dhcp6_subnet_id";
  130. columns_[5] = "ipv4_address";
  131. columns_[6] = "hostname";
  132. columns_[7] = "dhcp4_client_classes";
  133. columns_[8] = "dhcp6_client_classes";
  134. BOOST_STATIC_ASSERT(8 < HOST_COLUMNS);
  135. }
  136. /// @brief Set error indicators
  137. ///
  138. /// Sets the error indicator for each of the MYSQL_BIND elements. It points
  139. /// the "error" field within an element of the input array to the
  140. /// corresponding element of the passed error array.
  141. ///
  142. /// @param bind Array of BIND elements
  143. /// @param error Array of error elements. If there is an error in getting
  144. /// data associated with one of the "bind" elements, the
  145. /// corresponding element in the error array is set to MLM_TRUE.
  146. /// @param count Size of each of the arrays.
  147. static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
  148. size_t count) {
  149. for (size_t i = 0; i < count; ++i) {
  150. error[i] = MLM_FALSE;
  151. bind[i].error = reinterpret_cast<char*>(&error[i]);
  152. }
  153. }
  154. /// @brief Return columns in error
  155. ///
  156. /// If an error is returned from a fetch (in particular, a truncated
  157. /// status), this method can be called to get the names of the fields in
  158. /// error. It returns a string comprising the names of the fields
  159. /// separated by commas. In the case of there being no error indicators
  160. /// set, it returns the string "(None)".
  161. ///
  162. /// @param error Array of error elements. An element is set to MLM_TRUE
  163. /// if the corresponding column in the database is the source of
  164. /// the error.
  165. /// @param names Array of column names, the same size as the error array.
  166. /// @param count Size of each of the arrays.
  167. static std::string getColumnsInError(my_bool* error, std::string* names,
  168. size_t count) {
  169. std::string result = "";
  170. // Accumulate list of column names
  171. for (size_t i = 0; i < count; ++i) {
  172. if (error[i] == MLM_TRUE) {
  173. if (!result.empty()) {
  174. result += ", ";
  175. }
  176. result += names[i];
  177. }
  178. }
  179. if (result.empty()) {
  180. result = "(None)";
  181. }
  182. return (result);
  183. }
  184. /// @brief Create MYSQL_BIND objects for Host Pointer
  185. ///
  186. /// Fills in the MYSQL_BIND array for sending data in the Host object to
  187. /// the database.
  188. ///
  189. /// @param host Host object to be added to the database.
  190. /// None of the fields in the host reservation are modified -
  191. /// the host data is only read.
  192. ///
  193. /// @return Vector of MySQL BIND objects representing the data to be added.
  194. std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host) {
  195. // Store host object to ensure it remains valid.
  196. host_ = host;
  197. // Initialize prior to constructing the array of MYSQL_BIND structures.
  198. // It sets all fields, including is_null, to zero, so we need to set
  199. // is_null only if it should be true. This gives up minor performance
  200. // benefit while being safe approach. For improved readability, the
  201. // code that explicitly sets is_null is there, but is commented out.
  202. // This also takes care of seeeting bind_[X].is_null to MLM_FALSE.
  203. memset(bind_, 0, sizeof(bind_));
  204. // Set up the structures for the various components of the host structure.
  205. try {
  206. // host_id : INT UNSIGNED NOT NULL
  207. // The host_id is auto_incremented by MySQL database,
  208. // so we need to pass the NULL value
  209. host_id_ = static_cast<uint32_t>(NULL);
  210. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  211. bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
  212. bind_[0].is_unsigned = MLM_TRUE;
  213. // dhcp_identifier : VARBINARY(128) NOT NULL
  214. // Check which of the identifiers is used and set values accordingly
  215. if (host_->getDuid()) {
  216. dhcp_identifier_length_ = host_->getDuid()->getDuid().size();
  217. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  218. bind_[1].buffer = reinterpret_cast<char*>
  219. (const_cast<uint8_t*>(&(host_->getDuid()->getDuid()[0])));
  220. bind_[1].buffer_length = dhcp_identifier_length_;
  221. bind_[1].length = &dhcp_identifier_length_;
  222. } else if (host_->getHWAddress()){
  223. dhcp_identifier_length_ = host_->getHWAddress()->hwaddr_.size();
  224. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  225. bind_[1].buffer = reinterpret_cast<char*>
  226. (&(host_->getHWAddress()->hwaddr_[0]));
  227. bind_[1].buffer_length = dhcp_identifier_length_;
  228. bind_[1].length = &dhcp_identifier_length_;
  229. }
  230. // dhcp_identifier_type : TINYINT NOT NULL
  231. // Check which of the identifier types is used and set values accordingly
  232. if (host_->getHWAddress()) {
  233. dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
  234. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  235. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  236. bind_[2].is_unsigned = MLM_TRUE;
  237. } else if (host_->getDuid()) {
  238. dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
  239. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  240. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  241. bind_[2].is_unsigned = MLM_TRUE;
  242. }
  243. // dhcp4_subnet_id : INT UNSIGNED NULL
  244. // Can't take an address of intermediate object, so let's store it
  245. // in dhcp4_subnet_id_
  246. dhcp4_subnet_id_ = host_->getIPv4SubnetID();
  247. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  248. bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
  249. bind_[3].is_unsigned = MLM_TRUE;
  250. // dhcp6_subnet_id : INT UNSIGNED NULL
  251. // Can't take an address of intermediate object, so let's store it
  252. // in dhcp6_subnet_id_
  253. dhcp6_subnet_id_ = host_->getIPv6SubnetID();
  254. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  255. bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
  256. bind_[4].is_unsigned = MLM_TRUE;
  257. // ipv4_address : INT UNSIGNED NULL
  258. // The address in the Host structure is an IOAddress object. Convert
  259. // this to an integer for storage.
  260. ipv4_address_ = static_cast<uint32_t>(host_->getIPv4Reservation());
  261. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  262. bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
  263. bind_[5].is_unsigned = MLM_TRUE;
  264. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  265. // reasons, see memset() above
  266. // hostname : VARCHAR(255) NULL
  267. strncpy(hostname_, host_->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
  268. hostname_length_ = host_->getHostname().length();
  269. bind_[6].buffer_type = MYSQL_TYPE_STRING;
  270. bind_[6].buffer = reinterpret_cast<char*>(hostname_);
  271. bind_[6].buffer_length = hostname_length_;
  272. // dhcp4_client_classes : VARCHAR(255) NULL
  273. bind_[7].buffer_type = MYSQL_TYPE_STRING;
  274. string classes4_txt = classesToString(host_->getClientClasses4());
  275. strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
  276. bind_[7].buffer = dhcp4_client_classes_;
  277. bind_[7].buffer_length = classes4_txt.length();
  278. // dhcp6_client_classes : VARCHAR(255) NULL
  279. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  280. string classes6_txt = classesToString(host->getClientClasses6());
  281. strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
  282. bind_[8].buffer = dhcp6_client_classes_;
  283. bind_[8].buffer_length = classes6_txt.length();
  284. bind_[8].buffer_length = sizeof(host_->getClientClasses6());
  285. } catch (const std::exception& ex) {
  286. isc_throw(DbOperationError,
  287. "Could not create bind array from Host: "
  288. << host_->getHostname() << ", reason: " << ex.what());
  289. }
  290. // Add the data to the vector. Note the end element is one after the
  291. // end of the array.
  292. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[HOST_COLUMNS]));
  293. }
  294. /// @brief Create BIND array to receive data
  295. ///
  296. /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
  297. /// After data is successfully received, getLeaseData() can be used to copy
  298. /// it to a Lease6 object.
  299. ///
  300. std::vector<MYSQL_BIND> createBindForReceive() {
  301. // Initialize MYSQL_BIND array.
  302. // It sets all fields, including is_null, to zero, so we need to set
  303. // is_null only if it should be true. This gives up minor performance
  304. // benefit while being safe approach. For improved readability, the
  305. // code that explicitly sets is_null is there, but is commented out.
  306. // This also takes care of seeeting bind_[X].is_null to MLM_FALSE.
  307. memset(bind_, 0, sizeof(bind_));
  308. // host_id : INT UNSIGNED NOT NULL
  309. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  310. bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
  311. bind_[0].is_unsigned = MLM_TRUE;
  312. // dhcp_identifier : VARBINARY(128) NOT NULL
  313. dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
  314. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  315. bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
  316. bind_[1].buffer_length = dhcp_identifier_length_;
  317. bind_[1].length = &dhcp_identifier_length_;
  318. // dhcp_identifier_type : TINYINT NOT NULL
  319. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  320. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  321. bind_[2].is_unsigned = MLM_TRUE;
  322. // dhcp4_subnet_id : INT UNSIGNED NULL
  323. dhcp4_subnet_id_null_ = MLM_FALSE;
  324. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  325. bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
  326. bind_[3].is_unsigned = MLM_TRUE;
  327. bind_[3].is_null = &dhcp4_subnet_id_null_;
  328. // dhcp6_subnet_id : INT UNSIGNED NULL
  329. dhcp6_subnet_id_null_ = MLM_FALSE;
  330. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  331. bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
  332. bind_[4].is_unsigned = MLM_TRUE;
  333. bind_[4].is_null = &dhcp6_subnet_id_null_;
  334. // ipv4_address : INT UNSIGNED NULL
  335. ipv4_address_null_ = MLM_FALSE;
  336. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  337. bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
  338. bind_[5].is_unsigned = MLM_TRUE;
  339. bind_[5].is_null = &ipv4_address_null_;
  340. // hostname : VARCHAR(255) NULL
  341. hostname_null_ = MLM_FALSE;
  342. hostname_length_ = sizeof(hostname_);
  343. bind_[6].buffer_type = MYSQL_TYPE_STRING;
  344. bind_[6].buffer = reinterpret_cast<char*>(hostname_);
  345. bind_[6].buffer_length = hostname_length_;
  346. bind_[6].length = &hostname_length_;
  347. bind_[6].is_null = &hostname_null_;
  348. // dhcp4_client_classes : VARCHAR(255) NULL
  349. dhcp4_client_classes_null_ = MLM_FALSE;
  350. dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
  351. bind_[7].buffer_type = MYSQL_TYPE_STRING;
  352. bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
  353. bind_[7].buffer_length = dhcp4_client_classes_length_;
  354. bind_[7].length = &dhcp4_client_classes_length_;
  355. bind_[7].is_null = &dhcp4_client_classes_null_;
  356. // dhcp6_client_classes : VARCHAR(255) NULL
  357. dhcp6_client_classes_null_ = MLM_FALSE;
  358. dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
  359. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  360. bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
  361. bind_[8].buffer_length = dhcp6_client_classes_length_;
  362. bind_[8].length = &dhcp6_client_classes_length_;
  363. bind_[8].is_null = &dhcp6_client_classes_null_;
  364. // Add the error flags
  365. setErrorIndicators(bind_, error_, HOST_COLUMNS);
  366. // .. and check that we have the numbers correct at compile time.
  367. BOOST_STATIC_ASSERT(8 < HOST_COLUMNS);
  368. // Add the data to the vector. Note the end element is one after the
  369. // end of the array.
  370. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[HOST_COLUMNS]));
  371. }
  372. /// @brief Copy Received Data into Host Object
  373. ///
  374. /// Called after the MYSQL_BIND array created by createBindForReceive()
  375. /// has been used, this copies data from the internal member variables
  376. /// into a Host objec.
  377. ///
  378. /// @return Host Pointer to a Lease6 object holding the relevant data.
  379. HostPtr getHostData(){
  380. // Set the dhcp identifier type in a variable of the appropriate data type,
  381. // which has been initialized with an arbitrary (but valid) value.
  382. Host::IdentifierType type = Host::IDENT_HWADDR;
  383. switch (dhcp_identifier_type_) {
  384. case 0:
  385. type = Host::IDENT_HWADDR;
  386. break;
  387. case 1:
  388. type = Host::IDENT_DUID;
  389. break;
  390. default:
  391. isc_throw(BadValue, "invalid dhcp identifier type returned: "
  392. << static_cast<int>(dhcp_identifier_type_)
  393. << ". Only 0 or 1 are allowed.");
  394. }
  395. // Set subnets ID's if they are given, if not, leave an empty object
  396. SubnetID ipv4_subnet_id(0);
  397. if (dhcp4_subnet_id_null_ == MLM_FALSE) {
  398. ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
  399. }
  400. SubnetID ipv6_subnet_id(0);
  401. if (dhcp6_subnet_id_null_ == MLM_FALSE) {
  402. ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
  403. }
  404. // Set IPv4 address reservation if it was given, if not, set IPv4 zero address
  405. asiolink::IOAddress ipv4_reservation = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
  406. if (ipv4_address_null_ == MLM_FALSE) {
  407. ipv4_reservation = asiolink::IOAddress(ipv4_address_);
  408. }
  409. // Set hostname if it was given, if not lease empty string
  410. std::string hostname;
  411. if (hostname_null_ == MLM_FALSE) {
  412. hostname = std::string (hostname_, hostname_ + hostname_length_);
  413. }
  414. // Set client classes if they were given, if not, set empty client classes
  415. ClientClasses dhcp4_client_classes;
  416. if (dhcp4_client_classes_null_ == MLM_FALSE) {
  417. dhcp4_client_classes.insert(dhcp4_client_classes_);
  418. }
  419. ClientClasses dhcp6_client_classes;
  420. if (dhcp6_client_classes_null_ == MLM_FALSE) {
  421. dhcp6_client_classes.insert(dhcp6_client_classes_);
  422. }
  423. // Returning Host object with set fields
  424. HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
  425. type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
  426. hostname, dhcp4_client_classes_, dhcp6_client_classes_));
  427. h->setHostId(host_id_);
  428. return (h);
  429. }
  430. /// @brief Return columns in error
  431. ///
  432. /// If an error is returned from a fetch (in particular, a truncated
  433. /// status), this method can be called to get the names of the fields in
  434. /// error. It returns a string comprising the names of the fields
  435. /// separated by commas. In the case of there being no error indicators
  436. /// set, it returns the string "(None)".
  437. ///
  438. /// @return Comma-separated list of columns in error, or the string
  439. /// "(None)".
  440. std::string getErrorColumns() {
  441. return (getColumnsInError(error_, columns_, HOST_COLUMNS));
  442. }
  443. /// @brief Converts ClientClasses to a signgle string with coma separated values
  444. ///
  445. /// @param classes classes structure that contains zero or more classes
  446. /// @return a single string with coma separated values
  447. std::string classesToString(const ClientClasses& classes) {
  448. string txt;
  449. bool first = true;
  450. for (ClientClasses::const_iterator it = classes.begin();
  451. it != classes.end(); ++it) {
  452. if (!first) {
  453. txt += ",";
  454. }
  455. txt += (*it);
  456. first = false;
  457. }
  458. return (txt);
  459. }
  460. private:
  461. uint64_t host_id_; /// Host unique identifier
  462. std::vector<uint8_t> dhcp_identifier_; /// HW address (0) / DUID (1)
  463. uint8_t dhcp_identifier_buffer_[DUID::MAX_DUID_LEN];
  464. /// Buffer for dhcp identifier
  465. size_t dhcp_identifier_length_; /// Length of dhcp identifier
  466. uint8_t dhcp_identifier_type_; /// Type of dhcp_identifier
  467. uint32_t dhcp4_subnet_id_; /// Subnet DHCPv4 identifier
  468. uint32_t dhcp6_subnet_id_; /// Subnet DHCPv6 identifier
  469. uint32_t ipv4_address_; /// Reserved IPv4 address.
  470. IPv6ResrvCollection ipv6_reservations_; /// IPv6 reservations collection
  471. char hostname_[HOSTNAME_MAX_LEN]; /// Name reserved for the host
  472. unsigned long hostname_length_; /// hostname length
  473. char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
  474. /// DHCPv4 client classes
  475. unsigned long dhcp4_client_classes_length_; /// dhcp4_client_classes length
  476. char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
  477. /// DHCPv6 client classes
  478. unsigned long dhcp6_client_classes_length_; /// dhcp6_client_classes length
  479. HWAddrPtr hw_address_; /// Pointer to hardware address
  480. DuidPtr duid_; /// Pointer to DUID
  481. // NULL flags for subnets id, ipv4 address, hostname and client classes
  482. my_bool dhcp4_subnet_id_null_;
  483. my_bool dhcp6_subnet_id_null_;
  484. my_bool ipv4_address_null_;
  485. my_bool hostname_null_;
  486. my_bool dhcp4_client_classes_null_;
  487. my_bool dhcp6_client_classes_null_;
  488. MYSQL_BIND bind_[HOST_COLUMNS];
  489. std::string columns_[HOST_COLUMNS]; /// Column names
  490. my_bool error_[HOST_COLUMNS]; /// Error array
  491. HostPtr host_; // Pointer to Host object
  492. };
  493. /// @brief This class represents the exchanges related to IPv6 Reservations.
  494. class MySqlIPv6ReservationExchange {
  495. /// @brief Set number of database columns for this reservation structure
  496. static const size_t RESRV_COLUMNS = 6;
  497. public:
  498. /// @brief Constructor
  499. ///
  500. /// The initialization of the variables here is only to satisfy cppcheck -
  501. /// all variables are initialized/set in the methods before they are used.
  502. MySqlIPv6ReservationExchange()
  503. : host_id_(0), address_("::"), prefix_len_(0), type_(0),
  504. iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
  505. std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
  506. // Set the column names (for error messages)
  507. columns_[0] = "host_id";
  508. columns_[1] = "address";
  509. columns_[2] = "prefix_len";
  510. columns_[3] = "type";
  511. columns_[4] = "dhcp6_iaid";
  512. BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
  513. }
  514. /// @brief Set error indicators
  515. ///
  516. /// Sets the error indicator for each of the MYSQL_BIND elements. It points
  517. /// the "error" field within an element of the input array to the
  518. /// corresponding element of the passed error array.
  519. ///
  520. /// @param bind Array of BIND elements
  521. /// @param error Array of error elements. If there is an error in getting
  522. /// data associated with one of the "bind" elements, the
  523. /// corresponding element in the error array is set to MLM_TRUE.
  524. /// @param count Size of each of the arrays.
  525. static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
  526. size_t count) {
  527. for (size_t i = 0; i < count; ++i) {
  528. error[i] = MLM_FALSE;
  529. bind[i].error = reinterpret_cast<char*>(&error[i]);
  530. }
  531. }
  532. /// @brief Return columns in error
  533. ///
  534. /// If an error is returned from a fetch (in particular, a truncated
  535. /// status), this method can be called to get the names of the fields in
  536. /// error. It returns a string comprising the names of the fields
  537. /// separated by commas. In the case of there being no error indicators
  538. /// set, it returns the string "(None)".
  539. ///
  540. /// @param error Array of error elements. An element is set to MLM_TRUE
  541. /// if the corresponding column in the database is the source of
  542. /// the error.
  543. /// @param names Array of column names, the same size as the error array.
  544. /// @param count Size of each of the arrays.
  545. static std::string getColumnsInError(my_bool* error, std::string* names,
  546. size_t count) {
  547. std::string result = "";
  548. // Accumulate list of column names
  549. for (size_t i = 0; i < count; ++i) {
  550. if (error[i] == MLM_TRUE) {
  551. if (!result.empty()) {
  552. result += ", ";
  553. }
  554. result += names[i];
  555. }
  556. }
  557. if (result.empty()) {
  558. result = "(None)";
  559. }
  560. return (result);
  561. }
  562. /// @brief Create MYSQL_BIND objects for IPv6 Reservation
  563. ///
  564. /// Fills in the MYSQL_BIND array for sending data in the IPv6 Reservation
  565. /// object to the database.
  566. ///
  567. /// @param resv IPv6 reservation object to be added to the database.
  568. /// None of the fields in the reservation are modified -
  569. /// the reservation data is only read.
  570. /// @param id ID of a host owning this reservation
  571. ///
  572. /// @return Vector of MySQL BIND objects representing the data to be added.
  573. std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv, const HostID& id) {
  574. // Store the values to ensure they remain valid.
  575. resv_ = resv;
  576. host_id_ = id;
  577. // Initialize prior to constructing the array of MYSQL_BIND structures.
  578. // It sets all fields, including is_null, to zero, so we need to set
  579. // is_null only if it should be true. This gives up minor performance
  580. // benefit while being safe approach. For improved readability, the
  581. // code that explicitly sets is_null is there, but is commented out.
  582. memset(bind_, 0, sizeof(bind_));
  583. // Set up the structures for the various components of the host structure.
  584. try {
  585. // address VARCHAR(39)
  586. address_ = resv.getPrefix().toText();
  587. address_len_ = address_.length();
  588. bind_[0].buffer_type = MYSQL_TYPE_BLOB;
  589. bind_[0].buffer = reinterpret_cast<char*>
  590. (const_cast<char*>(address_.c_str()));
  591. bind_[0].buffer_length = address_len_;
  592. bind_[0].length = &address_len_;
  593. // prefix_len tinyint
  594. prefix_len_ = resv.getPrefixLen();
  595. bind_[1].buffer_type = MYSQL_TYPE_TINY;
  596. bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
  597. bind_[1].is_unsigned = MLM_TRUE;
  598. // type tinyint
  599. // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
  600. type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
  601. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  602. bind_[2].buffer = reinterpret_cast<char*>(&type_);
  603. bind_[2].is_unsigned = MLM_TRUE;
  604. // dhcp6_iaid INT UNSIGNED
  605. /// @todo: We don't support iaid in the IPv6Resrv yet.
  606. iaid_ = 0;
  607. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  608. bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
  609. bind_[3].is_unsigned = MLM_TRUE;
  610. // host_id INT UNSIGNED NOT NULL
  611. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  612. bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
  613. bind_[4].is_unsigned = MLM_TRUE;
  614. } catch (const std::exception& ex) {
  615. isc_throw(DbOperationError,
  616. "Could not create bind array from IPv6 Reservation: "
  617. << resv_.toText() << ", reason: " << ex.what());
  618. }
  619. // Add the data to the vector. Note the end element is one after the
  620. // end of the array.
  621. // RESRV_COLUMNS -1 as we do not set reservation_id.
  622. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[RESRV_COLUMNS-1]));
  623. }
  624. /// @brief Create BIND array to receive data
  625. ///
  626. /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
  627. /// After data is successfully received, getLeaseData() can be used to copy
  628. /// it to a Lease6 object.
  629. ///
  630. std::vector<MYSQL_BIND> createBindForReceive() {
  631. // Initialize MYSQL_BIND array.
  632. // It sets all fields, including is_null, to zero, so we need to set
  633. // is_null only if it should be true. This gives up minor performance
  634. // benefit while being safe approach. For improved readability, the
  635. // code that explicitly sets is_null is there, but is commented out.
  636. memset(bind_, 0, sizeof(bind_));
  637. // reservation_id INT UNSIGNED NOT NULL
  638. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  639. bind_[0].buffer = reinterpret_cast<char*>(&reservation_id_);
  640. bind_[0].is_unsigned = MLM_TRUE;
  641. // address VARCHAR(39)
  642. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  643. bind_[1].buffer = reinterpret_cast<char*>
  644. (const_cast<char*>(&address_[0]));
  645. bind_[1].buffer_length = address_len_;
  646. bind_[1].length = &address_len_;
  647. // prefix_len tinyint
  648. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  649. bind_[2].buffer = reinterpret_cast<char*>(&prefix_len_);
  650. bind_[2].is_unsigned = MLM_TRUE;
  651. // type tinyint
  652. bind_[3].buffer_type = MYSQL_TYPE_TINY;
  653. bind_[3].buffer = reinterpret_cast<char*>(&type_);
  654. bind_[3].is_unsigned = MLM_TRUE;
  655. // dhcp6_iaid INT UNSIGNED
  656. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  657. bind_[4].buffer = reinterpret_cast<char*>(&iaid_);
  658. bind_[4].is_unsigned = MLM_TRUE;
  659. // host_id INT UNSIGNED NOT NULL
  660. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  661. bind_[5].buffer = reinterpret_cast<char*>(&host_id_);
  662. bind_[5].is_unsigned = MLM_TRUE;
  663. // Add the error flags
  664. setErrorIndicators(bind_, error_, RESRV_COLUMNS);
  665. // .. and check that we have the numbers correct at compile time.
  666. BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
  667. // Add the data to the vector. Note the end element is one after the
  668. // end of the array.
  669. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[RESRV_COLUMNS]));
  670. }
  671. /// @brief Copy Received Data into IPv6 reservation
  672. ///
  673. /// Called after the MYSQL_BIND array created by createBindForReceive()
  674. /// has been used, this copies data from the internal member variables
  675. /// into a IPv6Resrv object.
  676. ///
  677. /// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
  678. IPv6Resrv getIPv6ReservData(){
  679. // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
  680. IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
  681. switch (type_) {
  682. case 0:
  683. type = IPv6Resrv::TYPE_NA;
  684. break;
  685. case 2:
  686. type = IPv6Resrv::TYPE_PD;
  687. break;
  688. default:
  689. isc_throw(BadValue,
  690. "invalid IPv6 reservation type returned: "
  691. << static_cast<int>(type_)
  692. << ". Only 0 or 2 are allowed.");
  693. }
  694. IPv6Resrv r(type, IOAddress(address_), prefix_len_);
  695. return (r);
  696. }
  697. /// @brief Return columns in error
  698. ///
  699. /// If an error is returned from a fetch (in particular, a truncated
  700. /// status), this method can be called to get the names of the fields in
  701. /// error. It returns a string comprising the names of the fields
  702. /// separated by commas. In the case of there being no error indicators
  703. /// set, it returns the string "(None)".
  704. ///
  705. /// @return Comma-separated list of columns in error, or the string
  706. /// "(None)".
  707. std::string getErrorColumns() {
  708. return (getColumnsInError(error_, columns_, RESRV_COLUMNS));
  709. }
  710. /// @brief Converts ClientClasses to a signgle string with coma separated values
  711. ///
  712. /// @param classes classes structure that contains zero or more classes
  713. /// @return a single string with coma separated values
  714. std::string classesToString(const ClientClasses& classes) {
  715. string txt;
  716. bool first = true;
  717. for (ClientClasses::const_iterator it = classes.begin();
  718. it != classes.end(); ++it) {
  719. if (!first) {
  720. txt += ",";
  721. }
  722. txt += (*it);
  723. first = false;
  724. }
  725. return (txt);
  726. }
  727. private:
  728. uint64_t host_id_; /// Host unique identifier
  729. uint64_t reservation_id_; /// Reservation unique identifier
  730. std::string address_; ///< Address (or prefix)
  731. size_t address_len_; ///< Length of the textual address representation
  732. uint8_t prefix_len_; ///< Length of the prefix (128 for addresses)
  733. uint8_t type_;
  734. uint8_t iaid_;
  735. IPv6Resrv resv_;
  736. MYSQL_BIND bind_[RESRV_COLUMNS];
  737. std::string columns_[RESRV_COLUMNS]; /// Column names
  738. my_bool error_[RESRV_COLUMNS]; /// Error array
  739. HostPtr host_; /// Pointer to Host object
  740. };
  741. // MySqlHostDataSource Constructor and Destructor
  742. MySqlHostDataSource::MySqlHostDataSource(
  743. const MySqlConnection::ParameterMap& parameters) : conn_(parameters) {
  744. // Open the database.
  745. conn_.openDatabase();
  746. // Enable autocommit. To avoid a flush to disk on every commit, the global
  747. // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
  748. // cause the changes to be written to the log, but flushed to disk in the
  749. // background every second. Setting the parameter to that value will speed
  750. // up the system, but at the risk of losing data if the system crashes.
  751. my_bool result = mysql_autocommit(conn_.mysql_, 1);
  752. if (result != 0) {
  753. isc_throw(DbOperationError, mysql_error(conn_.mysql_));
  754. }
  755. // Prepare all statements likely to be used.
  756. conn_.prepareStatements(tagged_statements,
  757. MySqlHostDataSource::NUM_STATEMENTS);
  758. // Create the exchange objects for use in exchanging data between the
  759. // program and the database.
  760. host_exchange_.reset(new MySqlHostReservationExchange());
  761. resv_exchange_.reset(new MySqlIPv6ReservationExchange());
  762. }
  763. MySqlHostDataSource::~MySqlHostDataSource() {
  764. // Free up the prepared statements, ignoring errors. (What would we do
  765. // about them? We're destroying this object and are not really concerned
  766. // with errors on a database connection that is about to go away.)
  767. for (int i = 0; i < conn_.statements_.size(); ++i) {
  768. if (conn_.statements_[i] != NULL) {
  769. (void) mysql_stmt_close(conn_.statements_[i]);
  770. conn_.statements_[i] = NULL;
  771. }
  772. }
  773. // There is no need to close the database in this destructor: it is
  774. // closed in the destructor of the mysql_ member variable.
  775. }
  776. bool
  777. MySqlHostDataSource::checkIfExists(const HostPtr& host){
  778. /// @todo: Implement this as a single query get(identifier_type, identifier)
  779. return (get4(host->getIPv4SubnetID(), host->getHWAddress(), host->getDuid()) ||
  780. get6(host->getIPv6SubnetID(), host->getDuid(), host->getHWAddress()));
  781. }
  782. void
  783. MySqlHostDataSource::add(const HostPtr& host) {
  784. // Check if the host is not a duplicate
  785. if (checkIfExists(host)){
  786. isc_throw(DuplicateEntry, "Host with same parameters already exists.");
  787. } else {
  788. // Create the MYSQL_BIND array for the host
  789. std::vector<MYSQL_BIND> bind = host_exchange_->createBindForSend(host);
  790. // ... and call addHost() code.
  791. addQuery(INSERT_HOST, bind);
  792. IPv6ResrvRange v6resv = host->getIPv6Reservations();
  793. if (std::distance(v6resv.first, v6resv.second) == 0) {
  794. // If there are no v6 reservations, we're done here.
  795. return;
  796. }
  797. // Gets the last inserted hosts id
  798. uint64_t host_id = mysql_insert_id(conn_.mysql_);
  799. for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second; ++resv) {
  800. addResv(resv->second, host_id);
  801. }
  802. }
  803. }
  804. void
  805. MySqlHostDataSource::addResv(const IPv6Resrv& resv, HostID id) {
  806. std::vector<MYSQL_BIND> bind =
  807. resv_exchange_->createBindForSend(resv, id);
  808. addQuery(INSERT_V6_RESRV, bind);
  809. }
  810. void
  811. MySqlHostDataSource::addQuery(StatementIndex stindex,
  812. std::vector<MYSQL_BIND>& bind) {
  813. // Bind the parameters to the statement
  814. int status = mysql_stmt_bind_param(conn_.statements_[stindex],
  815. &bind[0]);
  816. checkError(status, stindex, "unable to bind parameters");
  817. // Execute the statement
  818. status = mysql_stmt_execute(conn_.statements_[stindex]);
  819. if (status != 0) {
  820. // Failure: check for the special case of duplicate entry.
  821. if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
  822. isc_throw(DuplicateEntry, "Database duplicate entry error");
  823. }
  824. checkError(status, stindex, "unable to execute");
  825. }
  826. }
  827. void
  828. MySqlHostDataSource::getIPv6ReservationCollection(StatementIndex stindex,
  829. MYSQL_BIND* bind, boost::shared_ptr<MySqlIPv6ReservationExchange> exchange,
  830. IPv6ResrvCollection& result) const {
  831. // Bind the selection parameters to the statement
  832. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  833. checkError(status, stindex, "unable to bind WHERE clause parameter");
  834. // Set up the MYSQL_BIND array for the data being returned and bind it to
  835. // the statement.
  836. std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
  837. status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
  838. checkError(status, stindex, "unable to bind SELECT clause parameters");
  839. // Execute the statement
  840. status = mysql_stmt_execute(conn_.statements_[stindex]);
  841. checkError(status, stindex, "unable to execute");
  842. // Ensure that all the lease information is retrieved in one go to avoid
  843. // overhead of going back and forth between client and server.
  844. status = mysql_stmt_store_result(conn_.statements_[stindex]);
  845. checkError(status, stindex, "unable to set up for storing all results");
  846. // Set up the fetch "release" object to release resources associated
  847. // with the call to mysql_stmt_fetch when this method exits, then
  848. // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
  849. // successful data fetch.
  850. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  851. while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) ==
  852. MLM_MYSQL_FETCH_SUCCESS) {
  853. try {
  854. result.insert(IPv6ResrvTuple(exchange->getIPv6ReservData().getType(),
  855. exchange->getIPv6ReservData()));
  856. } catch (const isc::BadValue& ex) {
  857. // Rethrow the exception with a bit more data.
  858. isc_throw(BadValue, ex.what() << ". Statement is <" <<
  859. conn_.text_statements_[stindex] << ">");
  860. }
  861. }
  862. // How did the fetch end?
  863. // If mysql_stmt_fetch return value is equal to 1 an error occurred.
  864. if (status == MLM_MYSQL_FETCH_FAILURE) {
  865. // Error - unable to fetch results
  866. checkError(status, stindex, "unable to fetch results");
  867. } else if (status == MYSQL_DATA_TRUNCATED) {
  868. // Data truncated - throw an exception indicating what was at fault
  869. isc_throw(DataTruncated, conn_.text_statements_[stindex]
  870. << " returned truncated data: columns affected are "
  871. << exchange->getErrorColumns());
  872. }
  873. }
  874. IPv6ResrvCollection
  875. MySqlHostDataSource::getAllReservations(HostID host_id) const{
  876. // Set up the WHERE clause value
  877. MYSQL_BIND inbind[1];
  878. memset(inbind, 0, sizeof(inbind));
  879. uint32_t id = static_cast<uint32_t>(host_id);
  880. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  881. inbind[0].buffer = reinterpret_cast<char*>(&id);
  882. inbind[0].is_unsigned = MLM_TRUE;
  883. IPv6ResrvCollection result;
  884. getIPv6ReservationCollection(GET_V6_RESRV, inbind, resv_exchange_, result);
  885. return (result);
  886. }
  887. void
  888. MySqlHostDataSource::assignReservations(HostPtr& host) const {
  889. IPv6ResrvCollection reservations;
  890. reservations = getAllReservations(host->getHostId());
  891. for (IPv6ResrvIterator resv = reservations.begin(); resv != reservations.end(); ++resv){
  892. host->addReservation(resv->second);
  893. }
  894. }
  895. void
  896. MySqlHostDataSource::getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
  897. boost::shared_ptr<MySqlHostReservationExchange> exchange,
  898. ConstHostCollection& result, bool single) const {
  899. // Bind the selection parameters to the statement
  900. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  901. checkError(status, stindex, "unable to bind WHERE clause parameter");
  902. // Set up the MYSQL_BIND array for the data being returned and bind it to
  903. // the statement.
  904. std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
  905. status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
  906. checkError(status, stindex, "unable to bind SELECT clause parameters");
  907. // Execute the statement
  908. status = mysql_stmt_execute(conn_.statements_[stindex]);
  909. checkError(status, stindex, "unable to execute");
  910. // Ensure that all the lease information is retrieved in one go to avoid
  911. // overhead of going back and forth between client and server.
  912. status = mysql_stmt_store_result(conn_.statements_[stindex]);
  913. checkError(status, stindex, "unable to set up for storing all results");
  914. // Set up the fetch "release" object to release resources associated
  915. // with the call to mysql_stmt_fetch when this method exits, then
  916. // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
  917. // successful data fetch.
  918. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  919. int count = 0;
  920. HostPtr host;
  921. while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) ==
  922. MLM_MYSQL_FETCH_SUCCESS) {
  923. try {
  924. host = exchange->getHostData();
  925. assignReservations(host);
  926. result.push_back(host);
  927. } catch (const isc::BadValue& ex) {
  928. // Rethrow the exception with a bit more data.
  929. isc_throw(BadValue, ex.what() << ". Statement is <" <<
  930. conn_.text_statements_[stindex] << ">");
  931. }
  932. if (single && (++count > 1)) {
  933. isc_throw(MultipleRecords, "multiple records were found in the "
  934. "database where only one was expected for query "
  935. << conn_.text_statements_[stindex]);
  936. }
  937. }
  938. // How did the fetch end?
  939. // If mysql_stmt_fetch return value is equal to 1 an error occurred.
  940. if (status == MLM_MYSQL_FETCH_FAILURE) {
  941. // Error - unable to fetch results
  942. checkError(status, stindex, "unable to fetch results");
  943. } else if (status == MYSQL_DATA_TRUNCATED) {
  944. // Data truncated - throw an exception indicating what was at fault
  945. isc_throw(DataTruncated, conn_.text_statements_[stindex]
  946. << " returned truncated data: columns affected are "
  947. << exchange->getErrorColumns());
  948. }
  949. }
  950. ConstHostCollection
  951. MySqlHostDataSource::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
  952. // Set up the WHERE clause value
  953. MYSQL_BIND inbind[2];
  954. memset(inbind, 0, sizeof(inbind));
  955. uint8_t dhcp_identifier_type = 0;
  956. long unsigned int length = 0;
  957. if (duid){
  958. // DUID
  959. // set proper dhcp_identifier_type
  960. dhcp_identifier_type = BaseHostDataSource::ID_DUID; // 1
  961. inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
  962. const vector<uint8_t>& duid_vector = duid->getDuid();
  963. length = duid_vector.size();
  964. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  965. inbind[0].buffer = reinterpret_cast<char*>
  966. (const_cast<uint8_t*>(&duid_vector[0]));
  967. inbind[0].buffer_length = length;
  968. inbind[0].length = &length;
  969. } else if (hwaddr) {
  970. // HW Address
  971. dhcp_identifier_type = BaseHostDataSource::ID_HWADDR; // 0
  972. inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
  973. const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
  974. length = hwaddr_vector.size();
  975. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  976. inbind[0].buffer = reinterpret_cast<char*>
  977. (const_cast<uint8_t*>(&hwaddr_vector[0]));
  978. inbind[0].buffer_length = length;
  979. inbind[0].length = &length;
  980. }
  981. // dhcp identifier type
  982. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  983. inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
  984. inbind[1].is_unsigned = MLM_TRUE;
  985. ConstHostCollection result;
  986. getHostCollection(GET_HOST_HWADDR_DUID, inbind, host_exchange_, result, false);
  987. return (result);
  988. }
  989. ConstHostCollection
  990. MySqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
  991. // Set up the WHERE clause value
  992. MYSQL_BIND inbind[1];
  993. memset(inbind, 0, sizeof(inbind));
  994. uint32_t addr4 = static_cast<uint32_t>(address);
  995. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  996. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  997. inbind[0].is_unsigned = MLM_TRUE;
  998. ConstHostCollection result;
  999. getHostCollection(GET_HOST_ADDR, inbind, host_exchange_, result, false);
  1000. return (result);
  1001. }
  1002. ConstHostPtr
  1003. MySqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
  1004. const DuidPtr& duid) const {
  1005. // Set up the WHERE clause value
  1006. MYSQL_BIND inbind[3];
  1007. memset(inbind, 0, sizeof(inbind));
  1008. uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
  1009. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1010. inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
  1011. inbind[0].is_unsigned = MLM_TRUE;
  1012. /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr, duid)
  1013. if (hwaddr && duid) {
  1014. isc_throw(BadValue, "MySQL host data source get4() called with both"
  1015. " hwaddr and duid, only one of them is allowed");
  1016. }
  1017. if (!hwaddr && !duid) {
  1018. isc_throw(BadValue, "MySQL host data source get4() called with "
  1019. "neither hwaddr or duid specified, one of them is required");
  1020. }
  1021. unsigned long length = 0;
  1022. uint8_t dhcp_identifier_type_ = 0;
  1023. // Choosing one of the identifiers
  1024. if (hwaddr) {
  1025. // set identifier type
  1026. dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
  1027. // set identifier value
  1028. const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
  1029. length = hwaddr_vector.size();
  1030. inbind[2].buffer_type = MYSQL_TYPE_BLOB;
  1031. inbind[2].buffer = reinterpret_cast<char*>
  1032. (const_cast<uint8_t*>(&hwaddr_vector[0]));
  1033. inbind[2].buffer_length = length;
  1034. inbind[2].length = &length;
  1035. } else if (duid) {
  1036. // set identifier type
  1037. dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
  1038. // set identifier value
  1039. const vector<uint8_t>& duid_vector = duid->getDuid();
  1040. length = duid_vector.size();
  1041. inbind[2].buffer_type = MYSQL_TYPE_BLOB;
  1042. inbind[2].buffer = reinterpret_cast<char*>
  1043. (const_cast<uint8_t*>(&duid_vector[0]));
  1044. inbind[2].buffer_length = length;
  1045. inbind[2].length = &length;
  1046. }
  1047. // dhcp identifier type
  1048. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1049. inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  1050. inbind[1].is_unsigned = MLM_TRUE;
  1051. ConstHostCollection collection;
  1052. getHostCollection(GET_HOST_SUBID4_DHCPID, inbind, host_exchange_,
  1053. collection, true);
  1054. // Return single record if present, else clear the host.
  1055. ConstHostPtr result;
  1056. if (!collection.empty())
  1057. result = *collection.begin();
  1058. return (result);
  1059. }
  1060. ConstHostPtr
  1061. MySqlHostDataSource::get4(const SubnetID& subnet_id,
  1062. const asiolink::IOAddress& address) const {
  1063. // Set up the WHERE clause value
  1064. MYSQL_BIND inbind[2];
  1065. uint32_t subnet = subnet_id;
  1066. memset(inbind, 0, sizeof(inbind));
  1067. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1068. inbind[0].buffer = reinterpret_cast<char*>(&subnet);
  1069. inbind[0].is_unsigned = MLM_TRUE;
  1070. uint32_t addr4 = static_cast<uint32_t>(address);
  1071. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1072. inbind[1].buffer = reinterpret_cast<char*>(&addr4);
  1073. inbind[1].is_unsigned = MLM_TRUE;
  1074. ConstHostCollection collection;
  1075. getHostCollection(GET_HOST_SUBID_ADDR, inbind, host_exchange_,
  1076. collection, true);
  1077. // Return single record if present, else clear the host.
  1078. ConstHostPtr result;
  1079. if (!collection.empty())
  1080. result = *collection.begin();
  1081. return (result);
  1082. }
  1083. ConstHostPtr
  1084. MySqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
  1085. const HWAddrPtr& hwaddr) const {
  1086. // Set up the WHERE clause value
  1087. MYSQL_BIND inbind[3];
  1088. memset(inbind, 0, sizeof(inbind));
  1089. uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
  1090. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1091. inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
  1092. inbind[0].is_unsigned = MLM_TRUE;
  1093. /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr, duid)
  1094. if (hwaddr && duid) {
  1095. isc_throw(BadValue, "MySQL host data source get6() called with both"
  1096. " hwaddr and duid, only one of them is allowed");
  1097. }
  1098. if (!hwaddr && !duid) {
  1099. isc_throw(BadValue, "MySQL host data source get6() called with "
  1100. "neither hwaddr or duid specified, one of them is required");
  1101. }
  1102. unsigned long length = 0;
  1103. uint8_t dhcp_identifier_type_ = 0;
  1104. // Choosing one of the identifiers
  1105. if (hwaddr) {
  1106. // set identifier type
  1107. dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
  1108. // set identifier value
  1109. const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
  1110. length = hwaddr_vector.size();
  1111. inbind[2].buffer_type = MYSQL_TYPE_BLOB;
  1112. inbind[2].buffer = reinterpret_cast<char*>
  1113. (const_cast<uint8_t*>(&hwaddr_vector[0]));
  1114. inbind[2].buffer_length = length;
  1115. inbind[2].length = &length;
  1116. } else if (duid) {
  1117. // set identifier type
  1118. dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
  1119. // set identifier value
  1120. const vector<uint8_t>& duid_vector = duid->getDuid();
  1121. length = duid_vector.size();
  1122. inbind[2].buffer_type = MYSQL_TYPE_BLOB;
  1123. inbind[2].buffer = reinterpret_cast<char*>
  1124. (const_cast<uint8_t*>(&duid_vector[0]));
  1125. inbind[2].buffer_length = length;
  1126. inbind[2].length = &length;
  1127. }
  1128. // dhcp identifier type
  1129. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1130. inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  1131. inbind[1].is_unsigned = MLM_TRUE;
  1132. ConstHostCollection collection;
  1133. getHostCollection(GET_HOST_SUBID6_DHCPID, inbind, host_exchange_,
  1134. collection, true);
  1135. // Return single record if present, else clear the host.
  1136. ConstHostPtr result;
  1137. if (!collection.empty()) {
  1138. result = *collection.begin();
  1139. }
  1140. return (result);
  1141. }
  1142. ConstHostPtr
  1143. MySqlHostDataSource::get6(const asiolink::IOAddress& prefix,
  1144. const uint8_t prefix_len) const {
  1145. // Set up the WHERE clause value
  1146. MYSQL_BIND inbind[2];
  1147. memset(inbind, 0, sizeof(inbind));
  1148. std::string addr6 = prefix.toText();
  1149. unsigned long addr6_length = addr6.size();
  1150. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1151. inbind[0].buffer = reinterpret_cast<char*>
  1152. (const_cast<char*>(addr6.c_str()));
  1153. inbind[0].length = &addr6_length;
  1154. inbind[0].buffer_length = addr6_length;
  1155. uint8_t tmp = prefix_len;
  1156. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1157. inbind[1].buffer = reinterpret_cast<char*>(&tmp);
  1158. inbind[1].is_unsigned = MLM_TRUE;
  1159. ConstHostCollection collection;
  1160. getHostCollection(GET_HOST_PREFIX, inbind, host_exchange_,
  1161. collection, true);
  1162. // Return single record if present, else clear the host.
  1163. ConstHostPtr result;
  1164. if (!collection.empty()) {
  1165. result = *collection.begin();
  1166. }
  1167. return (result);
  1168. }
  1169. // Miscellaneous database methods.
  1170. std::string MySqlHostDataSource::getName() const {
  1171. std::string name = "";
  1172. try {
  1173. name = conn_.getParameter("name");
  1174. } catch (...) {
  1175. // Return an empty name
  1176. }
  1177. return (name);
  1178. }
  1179. std::string MySqlHostDataSource::getDescription() const {
  1180. return (std::string("Host data source that stores host information"
  1181. "in MySQL database"));
  1182. }
  1183. std::pair<uint32_t, uint32_t> MySqlHostDataSource::getVersion() const {
  1184. const StatementIndex stindex = GET_VERSION;
  1185. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1186. DHCPSRV_MYSQL_GET_VERSION);
  1187. uint32_t major; // Major version number
  1188. uint32_t minor; // Minor version number
  1189. // Execute the prepared statement
  1190. int status = mysql_stmt_execute(conn_.statements_[stindex]);
  1191. if (status != 0) {
  1192. isc_throw(DbOperationError, "unable to execute <"
  1193. << conn_.text_statements_[stindex]
  1194. << "> - reason: " << mysql_error(conn_.mysql_));
  1195. }
  1196. // Bind the output of the statement to the appropriate variables.
  1197. MYSQL_BIND bind[2];
  1198. memset(bind, 0, sizeof(bind));
  1199. bind[0].buffer_type = MYSQL_TYPE_LONG;
  1200. bind[0].is_unsigned = 1;
  1201. bind[0].buffer = &major;
  1202. bind[0].buffer_length = sizeof(major);
  1203. bind[1].buffer_type = MYSQL_TYPE_LONG;
  1204. bind[1].is_unsigned = 1;
  1205. bind[1].buffer = &minor;
  1206. bind[1].buffer_length = sizeof(minor);
  1207. status = mysql_stmt_bind_result(conn_.statements_[stindex], bind);
  1208. if (status != 0) {
  1209. isc_throw(DbOperationError, "unable to bind result set: "
  1210. << mysql_error(conn_.mysql_));
  1211. }
  1212. // Fetch the data and set up the "release" object to release associated
  1213. // resources when this method exits then retrieve the data.
  1214. // mysql_stmt_fetch return value other than 0 means error occurrence.
  1215. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  1216. status = mysql_stmt_fetch(conn_.statements_[stindex]);
  1217. if (status != 0) {
  1218. isc_throw(DbOperationError, "unable to obtain result set: "
  1219. << mysql_error(conn_.mysql_));
  1220. }
  1221. return (std::make_pair(major, minor));
  1222. }
  1223. void
  1224. MySqlHostDataSource::commit() {
  1225. conn_.commit();
  1226. }
  1227. void
  1228. MySqlHostDataSource::rollback() {
  1229. conn_.rollback();
  1230. }
  1231. }; // end of isc::dhcp namespace
  1232. }; // end of isc namespace