mysql_lease_mgr.cc 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765
  1. // Copyright (C) 2012-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 <asiolink/io_address.h>
  16. #include <dhcp/duid.h>
  17. #include <dhcp/hwaddr.h>
  18. #include <dhcpsrv/dhcpsrv_log.h>
  19. #include <dhcpsrv/mysql_lease_mgr.h>
  20. #include <dhcpsrv/mysql_connection.h>
  21. #include <boost/static_assert.hpp>
  22. #include <mysqld_error.h>
  23. #include <iostream>
  24. #include <iomanip>
  25. #include <sstream>
  26. #include <string>
  27. #include <time.h>
  28. using namespace isc;
  29. using namespace isc::dhcp;
  30. using namespace std;
  31. /// @file
  32. ///
  33. /// This file holds the implementation of the Lease Manager using MySQL. The
  34. /// implementation uses MySQL's C API, as it comes as standard with the MySQL
  35. /// client libraries.
  36. ///
  37. /// In general, each of the database access methods corresponds to one SQL
  38. /// statement. To avoid the overhead of parsing a statement every time it is
  39. /// used, when the database is opened "prepared statements" are created -
  40. /// essentially doing the SQL parsing up front. Every time a method is used
  41. /// to access data, the corresponding prepared statement is referenced. Each
  42. /// prepared statement contains a set of placeholders for data, each
  43. /// placeholder being for:
  44. ///
  45. /// - data being added to the database (as in adding or updating a lease)
  46. /// - data being retrieved from the database (as in getting lease information)
  47. /// - selection criteria used to determine which records to update/retrieve.
  48. ///
  49. /// All such data is associated with the prepared statment using an array of
  50. /// MYSQL_BIND structures. Each element in the array corresponds to one
  51. /// parameter in the prepared statement - the first element in the array is
  52. /// associated with the first parameter, the second element with the second
  53. /// parameter etc.
  54. ///
  55. /// Within this file, the setting up of the MYSQL_BIND arrays for data being
  56. /// passed to and retrieved from the database is handled in the
  57. /// isc::dhcp::MySqlLease4Exchange and isc::dhcp::MySqlLease6Exchange classes.
  58. /// The classes also hold intermediate variables required for exchanging some
  59. /// of the data.
  60. ///
  61. /// With these exchange objects in place, many of the methods follow similar
  62. /// logic:
  63. /// - Set up the MYSQL_BIND array for data being transferred to/from the
  64. /// database. For data being transferred to the database, some of the
  65. /// data is extracted from the lease to intermediate variables, whilst
  66. /// in other cases the MYSQL_BIND arrays point to the data in the lease.
  67. /// - Set up the MYSQL_BIND array for the data selection parameters.
  68. /// - Bind these arrays to the prepared statement.
  69. /// - Execute the statement.
  70. /// - If there is output, copy the data from the bound variables to the output
  71. /// lease object.
  72. namespace isc {
  73. namespace dhcp {
  74. /// @brief Common MySQL and Lease Data Methods
  75. ///
  76. /// The MySqlLease4Exchange and MySqlLease6Exchange classes provide the
  77. /// functionaility to set up binding information between variables in the
  78. /// program and data extracted from the database. This class is the common
  79. /// base to both of them, containing some common methods.
  80. class MySqlLeaseExchange {
  81. public:
  82. /// @brief Set error indicators
  83. ///
  84. /// Sets the error indicator for each of the MYSQL_BIND elements. It points
  85. /// the "error" field within an element of the input array to the
  86. /// corresponding element of the passed error array.
  87. ///
  88. /// @param bind Array of BIND elements
  89. /// @param error Array of error elements. If there is an error in getting
  90. /// data associated with one of the "bind" elements, the
  91. /// corresponding element in the error array is set to MLM_TRUE.
  92. /// @param count Size of each of the arrays.
  93. static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
  94. size_t count) {
  95. for (size_t i = 0; i < count; ++i) {
  96. error[i] = MLM_FALSE;
  97. bind[i].error = reinterpret_cast<char*>(&error[i]);
  98. }
  99. }
  100. /// @brief Return columns in error
  101. ///
  102. /// If an error is returned from a fetch (in particular, a truncated
  103. /// status), this method can be called to get the names of the fields in
  104. /// error. It returns a string comprising the names of the fields
  105. /// separated by commas. In the case of there being no error indicators
  106. /// set, it returns the string "(None)".
  107. ///
  108. /// @param error Array of error elements. An element is set to MLM_TRUE
  109. /// if the corresponding column in the database is the source of
  110. /// the error.
  111. /// @param names Array of column names, the same size as the error array.
  112. /// @param count Size of each of the arrays.
  113. static std::string getColumnsInError(my_bool* error, std::string* names,
  114. size_t count) {
  115. std::string result = "";
  116. // Accumulate list of column names
  117. for (size_t i = 0; i < count; ++i) {
  118. if (error[i] == MLM_TRUE) {
  119. if (!result.empty()) {
  120. result += ", ";
  121. }
  122. result += names[i];
  123. }
  124. }
  125. if (result.empty()) {
  126. result = "(None)";
  127. }
  128. return (result);
  129. }
  130. };
  131. /// @brief Exchange MySQL and Lease4 Data
  132. ///
  133. /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
  134. /// describe the parameters in the prepared statements. Where information is
  135. /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
  136. /// structure is identical. This class handles the creation of that array.
  137. ///
  138. /// Owing to the MySQL API, the process requires some intermediate variables
  139. /// to hold things like data length etc. This object holds those variables.
  140. ///
  141. /// @note There are no unit tests for this class. It is tested indirectly
  142. /// in all MySqlLeaseMgr::xxx4() calls where it is used.
  143. class MySqlLease4Exchange : public MySqlLeaseExchange {
  144. /// @brief Set number of database columns for this lease structure
  145. static const size_t LEASE_COLUMNS = 9;
  146. public:
  147. /// @brief Constructor
  148. ///
  149. /// The initialization of the variables here is only to satisfy cppcheck -
  150. /// all variables are initialized/set in the methods before they are used.
  151. MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
  152. client_id_null_(MLM_FALSE),
  153. fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0) {
  154. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  155. memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
  156. memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
  157. std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
  158. // Set the column names (for error messages)
  159. columns_[0] = "address";
  160. columns_[1] = "hwaddr";
  161. columns_[2] = "client_id";
  162. columns_[3] = "valid_lifetime";
  163. columns_[4] = "expire";
  164. columns_[5] = "subnet_id";
  165. columns_[6] = "fqdn_fwd";
  166. columns_[7] = "fqdn_rev";
  167. columns_[8] = "hostname";
  168. BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
  169. }
  170. /// @brief Create MYSQL_BIND objects for Lease4 Pointer
  171. ///
  172. /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
  173. /// the database.
  174. ///
  175. /// @param lease Lease object to be added to the database. None of the
  176. /// fields in the lease are modified - the lease data is only read.
  177. ///
  178. /// @return Vector of MySQL BIND objects representing the data to be added.
  179. std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
  180. // Store lease object to ensure it remains valid.
  181. lease_ = lease;
  182. // Initialize prior to constructing the array of MYSQL_BIND structures.
  183. // It sets all fields, including is_null, to zero, so we need to set
  184. // is_null only if it should be true. This gives up minor performance
  185. // benefit while being safe approach. For improved readability, the
  186. // code that explicitly sets is_null is there, but is commented out.
  187. memset(bind_, 0, sizeof(bind_));
  188. // Set up the structures for the various components of the lease4
  189. // structure.
  190. try {
  191. // Address: uint32_t
  192. // The address in the Lease structure is an IOAddress object. Convert
  193. // this to an integer for storage.
  194. addr4_ = static_cast<uint32_t>(lease_->addr_);
  195. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  196. bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
  197. bind_[0].is_unsigned = MLM_TRUE;
  198. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  199. // reasons, see memset() above
  200. // hwaddr: varbinary(128)
  201. // For speed, we avoid copying the data into temporary storage and
  202. // instead extract it from the lease structure directly.
  203. hwaddr_length_ = lease_->hwaddr_->hwaddr_.size();
  204. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  205. bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_->hwaddr_[0]));
  206. bind_[1].buffer_length = hwaddr_length_;
  207. bind_[1].length = &hwaddr_length_;
  208. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  209. // reasons, see memset() above
  210. // client_id: varbinary(128)
  211. if (lease_->client_id_) {
  212. client_id_ = lease_->client_id_->getClientId();
  213. client_id_length_ = client_id_.size();
  214. bind_[2].buffer_type = MYSQL_TYPE_BLOB;
  215. bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
  216. bind_[2].buffer_length = client_id_length_;
  217. bind_[2].length = &client_id_length_;
  218. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  219. // reasons, see memset() above
  220. } else {
  221. bind_[2].buffer_type = MYSQL_TYPE_NULL;
  222. // According to http://dev.mysql.com/doc/refman/5.5/en/
  223. // c-api-prepared-statement-data-structures.html, the other
  224. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  225. // but let's set them to some sane values in case earlier versions
  226. // didn't have that assumption.
  227. client_id_null_ = MLM_TRUE;
  228. bind_[2].buffer = NULL;
  229. bind_[2].is_null = &client_id_null_;
  230. }
  231. // valid lifetime: unsigned int
  232. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  233. bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
  234. bind_[3].is_unsigned = MLM_TRUE;
  235. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  236. // reasons, see memset() above
  237. // expire: timestamp
  238. // The lease structure holds the client last transmission time (cltt_)
  239. // For convenience for external tools, this is converted to lease
  240. // expiry time (expire). The relationship is given by:
  241. //
  242. // expire = cltt_ + valid_lft_
  243. MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
  244. expire_);
  245. bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
  246. bind_[4].buffer = reinterpret_cast<char*>(&expire_);
  247. bind_[4].buffer_length = sizeof(expire_);
  248. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  249. // reasons, see memset() above
  250. // subnet_id: unsigned int
  251. // Can use lease_->subnet_id_ directly as it is of type uint32_t.
  252. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  253. bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
  254. bind_[5].is_unsigned = MLM_TRUE;
  255. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  256. // reasons, see memset() above
  257. // fqdn_fwd: boolean
  258. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  259. bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
  260. bind_[6].is_unsigned = MLM_TRUE;
  261. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  262. // reasons, see memset() above
  263. // fqdn_rev: boolean
  264. bind_[7].buffer_type = MYSQL_TYPE_TINY;
  265. bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
  266. bind_[7].is_unsigned = MLM_TRUE;
  267. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  268. // reasons, see memset() above
  269. // hostname: varchar(255)
  270. bind_[8].buffer_type = MYSQL_TYPE_VARCHAR;
  271. bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
  272. bind_[8].buffer_length = lease_->hostname_.length();
  273. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  274. // reasons, see memset() above
  275. // Add the error flags
  276. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  277. // .. and check that we have the numbers correct at compile time.
  278. BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
  279. } catch (const std::exception& ex) {
  280. isc_throw(DbOperationError,
  281. "Could not create bind array from Lease4: "
  282. << lease_->addr_.toText() << ", reason: " << ex.what());
  283. }
  284. // Add the data to the vector. Note the end element is one after the
  285. // end of the array.
  286. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  287. }
  288. /// @brief Create BIND array to receive data
  289. ///
  290. /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
  291. /// After data is successfully received, getLeaseData() can be used to copy
  292. /// it to a Lease6 object.
  293. ///
  294. std::vector<MYSQL_BIND> createBindForReceive() {
  295. // Initialize MYSQL_BIND array.
  296. // It sets all fields, including is_null, to zero, so we need to set
  297. // is_null only if it should be true. This gives up minor performance
  298. // benefit while being safe approach. For improved readability, the
  299. // code that explicitly sets is_null is there, but is commented out.
  300. memset(bind_, 0, sizeof(bind_));
  301. // address: uint32_t
  302. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  303. bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
  304. bind_[0].is_unsigned = MLM_TRUE;
  305. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  306. // reasons, see memset() above
  307. // hwaddr: varbinary(20)
  308. hwaddr_length_ = sizeof(hwaddr_buffer_);
  309. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  310. bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
  311. bind_[1].buffer_length = hwaddr_length_;
  312. bind_[1].length = &hwaddr_length_;
  313. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  314. // reasons, see memset() above
  315. // client_id: varbinary(128)
  316. client_id_length_ = sizeof(client_id_buffer_);
  317. bind_[2].buffer_type = MYSQL_TYPE_BLOB;
  318. bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
  319. bind_[2].buffer_length = client_id_length_;
  320. bind_[2].length = &client_id_length_;
  321. bind_[2].is_null = &client_id_null_;
  322. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  323. // reasons, see memset() above
  324. // lease_time: unsigned int
  325. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  326. bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
  327. bind_[3].is_unsigned = MLM_TRUE;
  328. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  329. // reasons, see memset() above
  330. // expire: timestamp
  331. bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
  332. bind_[4].buffer = reinterpret_cast<char*>(&expire_);
  333. bind_[4].buffer_length = sizeof(expire_);
  334. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  335. // reasons, see memset() above
  336. // subnet_id: unsigned int
  337. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  338. bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
  339. bind_[5].is_unsigned = MLM_TRUE;
  340. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  341. // reasons, see memset() above
  342. // fqdn_fwd: boolean
  343. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  344. bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
  345. bind_[6].is_unsigned = MLM_TRUE;
  346. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  347. // reasons, see memset() above
  348. // fqdn_rev: boolean
  349. bind_[7].buffer_type = MYSQL_TYPE_TINY;
  350. bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
  351. bind_[7].is_unsigned = MLM_TRUE;
  352. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  353. // reasons, see memset() above
  354. // hostname: varchar(255)
  355. hostname_length_ = sizeof(hostname_buffer_);
  356. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  357. bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
  358. bind_[8].buffer_length = hostname_length_;
  359. bind_[8].length = &hostname_length_;
  360. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  361. // reasons, see memset() above
  362. // Add the error flags
  363. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  364. // .. and check that we have the numbers correct at compile time.
  365. BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
  366. // Add the data to the vector. Note the end element is one after the
  367. // end of the array.
  368. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  369. }
  370. /// @brief Copy Received Data into Lease6 Object
  371. ///
  372. /// Called after the MYSQL_BIND array created by createBindForReceive()
  373. /// has been used, this copies data from the internal member variables
  374. /// into a Lease4 objec.
  375. ///
  376. /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
  377. /// data.
  378. Lease4Ptr getLeaseData() {
  379. // Convert times received from the database to times for the lease
  380. // structure
  381. time_t cltt = 0;
  382. MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
  383. if (client_id_null_==MLM_TRUE) {
  384. // There's no client-id, so we pass client-id_length_ set to 0
  385. client_id_length_ = 0;
  386. }
  387. // Hostname is passed to Lease4 as a string object. We have to create
  388. // it from the buffer holding hostname and the buffer length.
  389. std::string hostname(hostname_buffer_,
  390. hostname_buffer_ + hostname_length_);
  391. // Recreate the hardware address.
  392. HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
  393. // note that T1 and T2 are not stored
  394. return (Lease4Ptr(new Lease4(addr4_, hwaddr,
  395. client_id_buffer_, client_id_length_,
  396. valid_lifetime_, 0, 0, cltt, subnet_id_,
  397. fqdn_fwd_, fqdn_rev_, hostname)));
  398. }
  399. /// @brief Return columns in error
  400. ///
  401. /// If an error is returned from a fetch (in particular, a truncated
  402. /// status), this method can be called to get the names of the fields in
  403. /// error. It returns a string comprising the names of the fields
  404. /// separated by commas. In the case of there being no error indicators
  405. /// set, it returns the string "(None)".
  406. ///
  407. /// @return Comma-separated list of columns in error, or the string
  408. /// "(None)".
  409. std::string getErrorColumns() {
  410. return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
  411. }
  412. private:
  413. // Note: All array lengths are equal to the corresponding variable in the
  414. // schema.
  415. // Note: Arrays are declared fixed length for speed of creation
  416. uint32_t addr4_; ///< IPv4 address
  417. MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
  418. std::string columns_[LEASE_COLUMNS];///< Column names
  419. my_bool error_[LEASE_COLUMNS]; ///< Error array
  420. std::vector<uint8_t> hwaddr_; ///< Hardware address
  421. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  422. ///< Hardware address buffer
  423. unsigned long hwaddr_length_; ///< Hardware address length
  424. std::vector<uint8_t> client_id_; ///< Client identification
  425. uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
  426. ///< Client ID buffer
  427. unsigned long client_id_length_; ///< Client ID address length
  428. my_bool client_id_null_; ///< Is Client ID null?
  429. MYSQL_TIME expire_; ///< Lease expiry time
  430. Lease4Ptr lease_; ///< Pointer to lease object
  431. uint32_t subnet_id_; ///< Subnet identification
  432. uint32_t valid_lifetime_; ///< Lease time
  433. my_bool fqdn_fwd_; ///< Has forward DNS update been
  434. ///< performed
  435. my_bool fqdn_rev_; ///< Has reverse DNS update been
  436. ///< performed
  437. char hostname_buffer_[HOSTNAME_MAX_LEN];
  438. ///< Client hostname
  439. unsigned long hostname_length_; ///< Client hostname length
  440. };
  441. /// @brief Exchange MySQL and Lease6 Data
  442. ///
  443. /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
  444. /// describe the parameters in the prepared statements. Where information is
  445. /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
  446. /// structure is identical. This class handles the creation of that array.
  447. ///
  448. /// Owing to the MySQL API, the process requires some intermediate variables
  449. /// to hold things like data length etc. This object holds those variables.
  450. ///
  451. /// @note There are no unit tests for this class. It is tested indirectly
  452. /// in all MySqlLeaseMgr::xxx6() calls where it is used.
  453. class MySqlLease6Exchange : public MySqlLeaseExchange {
  454. /// @brief Set number of database columns for this lease structure
  455. static const size_t LEASE_COLUMNS = 15;
  456. public:
  457. /// @brief Constructor
  458. ///
  459. /// The initialization of the variables here is nonly to satisfy cppcheck -
  460. /// all variables are initialized/set in the methods before they are used.
  461. MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
  462. fqdn_fwd_(false), fqdn_rev_(false),
  463. hostname_length_(0), hwaddr_length_(0),
  464. hwaddr_null_(MLM_FALSE), hwtype_(0), hwaddr_source_(0) {
  465. memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
  466. memset(duid_buffer_, 0, sizeof(duid_buffer_));
  467. memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
  468. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  469. std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
  470. // Set the column names (for error messages)
  471. columns_[0] = "address";
  472. columns_[1] = "duid";
  473. columns_[2] = "valid_lifetime";
  474. columns_[3] = "expire";
  475. columns_[4] = "subnet_id";
  476. columns_[5] = "pref_lifetime";
  477. columns_[6] = "lease_type";
  478. columns_[7] = "iaid";
  479. columns_[8] = "prefix_len";
  480. columns_[9] = "fqdn_fwd";
  481. columns_[10] = "fqdn_rev";
  482. columns_[11] = "hostname";
  483. columns_[12] = "hwaddr";
  484. columns_[13] = "hwtype";
  485. columns_[14] = "hwaddr_source";
  486. BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
  487. }
  488. /// @brief Create MYSQL_BIND objects for Lease6 Pointer
  489. ///
  490. /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
  491. /// the database.
  492. ///
  493. /// @param lease Lease object to be added to the database.
  494. ///
  495. /// @return Vector of MySQL BIND objects representing the data to be added.
  496. std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
  497. // Store lease object to ensure it remains valid.
  498. lease_ = lease;
  499. // Ensure bind_ array clear for constructing the MYSQL_BIND structures
  500. // for this lease.
  501. // It sets all fields, including is_null, to zero, so we need to set
  502. // is_null only if it should be true. This gives up minor performance
  503. // benefit while being safe approach. For improved readability, the
  504. // code that explicitly sets is_null is there, but is commented out.
  505. memset(bind_, 0, sizeof(bind_));
  506. try {
  507. // address: varchar(39)
  508. addr6_ = lease_->addr_.toText();
  509. addr6_length_ = addr6_.size();
  510. // In the following statement, the string is being read. However, the
  511. // MySQL C interface does not use "const", so the "buffer" element
  512. // is declared as "char*" instead of "const char*". To resolve this,
  513. // the "const" is discarded. (Note that the address of addr6_.c_str()
  514. // is guaranteed to be valid until the next non-const operation on
  515. // addr6_.)
  516. //
  517. // The const_cast could be avoided by copying the string to a writeable
  518. // buffer and storing the address of that in the "buffer" element.
  519. // However, this introduces a copy operation (with additional overhead)
  520. // purely to get round the structures introduced by design of the
  521. // MySQL interface (which uses the area pointed to by "buffer" as input
  522. // when specifying query parameters and as output when retrieving data).
  523. // For that reason, "const_cast" has been used.
  524. bind_[0].buffer_type = MYSQL_TYPE_STRING;
  525. bind_[0].buffer = const_cast<char*>(addr6_.c_str());
  526. bind_[0].buffer_length = addr6_length_;
  527. bind_[0].length = &addr6_length_;
  528. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  529. // reasons, see memset() above
  530. // duid: varchar(128)
  531. if (!lease_->duid_) {
  532. isc_throw(DbOperationError, "lease6 for address " << addr6_
  533. << " is missing mandatory client-id.");
  534. }
  535. duid_ = lease_->duid_->getDuid();
  536. duid_length_ = duid_.size();
  537. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  538. bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
  539. bind_[1].buffer_length = duid_length_;
  540. bind_[1].length = &duid_length_;
  541. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  542. // reasons, see memset() above
  543. // valid lifetime: unsigned int
  544. bind_[2].buffer_type = MYSQL_TYPE_LONG;
  545. bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
  546. bind_[2].is_unsigned = MLM_TRUE;
  547. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  548. // reasons, see memset() above
  549. // expire: timestamp
  550. // The lease structure holds the client last transmission time (cltt_)
  551. // For convenience for external tools, this is converted to lease
  552. /// expiry time (expire). The relationship is given by:
  553. //
  554. // expire = cltt_ + valid_lft_
  555. //
  556. MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
  557. expire_);
  558. bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
  559. bind_[3].buffer = reinterpret_cast<char*>(&expire_);
  560. bind_[3].buffer_length = sizeof(expire_);
  561. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  562. // reasons, see memset() above
  563. // subnet_id: unsigned int
  564. // Can use lease_->subnet_id_ directly as it is of type uint32_t.
  565. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  566. bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
  567. bind_[4].is_unsigned = MLM_TRUE;
  568. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  569. // reasons, see memset() above
  570. // pref_lifetime: unsigned int
  571. // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
  572. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  573. bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
  574. bind_[5].is_unsigned = MLM_TRUE;
  575. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  576. // reasons, see memset() above
  577. // lease_type: tinyint
  578. // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
  579. lease_type_ = lease_->type_;
  580. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  581. bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
  582. bind_[6].is_unsigned = MLM_TRUE;
  583. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  584. // reasons, see memset() above
  585. // iaid: unsigned int
  586. // Can use lease_->iaid_ directly as it is of type uint32_t.
  587. bind_[7].buffer_type = MYSQL_TYPE_LONG;
  588. bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
  589. bind_[7].is_unsigned = MLM_TRUE;
  590. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  591. // reasons, see memset() above
  592. // prefix_len: unsigned tinyint
  593. // Can use lease_->prefixlen_ directly as it is uint32_t.
  594. bind_[8].buffer_type = MYSQL_TYPE_TINY;
  595. bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
  596. bind_[8].is_unsigned = MLM_TRUE;
  597. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  598. // reasons, see memset() above
  599. // fqdn_fwd: boolean
  600. bind_[9].buffer_type = MYSQL_TYPE_TINY;
  601. bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
  602. bind_[9].is_unsigned = MLM_TRUE;
  603. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  604. // reasons, see memset() above
  605. // fqdn_rev: boolean
  606. bind_[10].buffer_type = MYSQL_TYPE_TINY;
  607. bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
  608. bind_[10].is_unsigned = MLM_TRUE;
  609. // bind_[10].is_null = &MLM_FALSE; // commented out for performance
  610. // reasons, see memset() above
  611. // hostname: varchar(255)
  612. bind_[11].buffer_type = MYSQL_TYPE_VARCHAR;
  613. bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
  614. bind_[11].buffer_length = lease_->hostname_.length();
  615. // bind_[11].is_null = &MLM_FALSE; // commented out for performance
  616. // reasons, see memset() above
  617. // hwaddr: varbinary(20) - hardware/MAC address
  618. HWAddrPtr hwaddr = lease_->hwaddr_;
  619. if (hwaddr) {
  620. hwaddr_ = hwaddr->hwaddr_;
  621. hwaddr_length_ = hwaddr->hwaddr_.size();
  622. bind_[12].buffer_type = MYSQL_TYPE_BLOB;
  623. bind_[12].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
  624. bind_[12].buffer_length = hwaddr_length_;
  625. bind_[12].length = &hwaddr_length_;
  626. } else {
  627. bind_[12].buffer_type = MYSQL_TYPE_NULL;
  628. // According to http://dev.mysql.com/doc/refman/5.5/en/
  629. // c-api-prepared-statement-data-structures.html, the other
  630. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  631. // but let's set them to some sane values in case earlier versions
  632. // didn't have that assumption.
  633. hwaddr_null_ = MLM_TRUE;
  634. bind_[12].buffer = NULL;
  635. bind_[12].is_null = &hwaddr_null_;
  636. }
  637. // hwtype
  638. if (hwaddr) {
  639. hwtype_ = lease->hwaddr_->htype_;
  640. bind_[13].buffer_type = MYSQL_TYPE_SHORT;
  641. bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
  642. bind_[13].is_unsigned = MLM_TRUE;
  643. } else {
  644. hwtype_ = 0;
  645. bind_[13].buffer_type = MYSQL_TYPE_NULL;
  646. // According to http://dev.mysql.com/doc/refman/5.5/en/
  647. // c-api-prepared-statement-data-structures.html, the other
  648. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  649. // but let's set them to some sane values in case earlier versions
  650. // didn't have that assumption.
  651. hwaddr_null_ = MLM_TRUE;
  652. bind_[13].buffer = NULL;
  653. bind_[13].is_null = &hwaddr_null_;
  654. }
  655. /// Hardware source
  656. if (hwaddr) {
  657. hwaddr_source_ = lease->hwaddr_->source_;
  658. bind_[14].buffer_type = MYSQL_TYPE_LONG;
  659. bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
  660. bind_[14].is_unsigned = MLM_TRUE;
  661. } else {
  662. hwaddr_source_ = 0;
  663. bind_[14].buffer_type = MYSQL_TYPE_NULL;
  664. // According to http://dev.mysql.com/doc/refman/5.5/en/
  665. // c-api-prepared-statement-data-structures.html, the other
  666. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  667. // but let's set them to some sane values in case earlier versions
  668. // didn't have that assumption.
  669. hwaddr_null_ = MLM_TRUE;
  670. bind_[14].buffer = NULL;
  671. bind_[14].is_null = &hwaddr_null_;
  672. }
  673. // Add the error flags
  674. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  675. // .. and check that we have the numbers correct at compile time.
  676. BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
  677. } catch (const std::exception& ex) {
  678. isc_throw(DbOperationError,
  679. "Could not create bind array from Lease6: "
  680. << lease_->addr_.toText() << ", reason: " << ex.what());
  681. }
  682. // Add the data to the vector. Note the end element is one after the
  683. // end of the array.
  684. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  685. }
  686. /// @brief Create BIND array to receive data
  687. ///
  688. /// Creates a MYSQL_BIND array to receive Lease6 data from the database.
  689. /// After data is successfully received, getLeaseData() is used to copy
  690. /// it to a Lease6 object.
  691. ///
  692. /// @return Vector of MySQL BIND objects passed to the MySQL data retrieval
  693. /// functions.
  694. std::vector<MYSQL_BIND> createBindForReceive() {
  695. // Initialize MYSQL_BIND array.
  696. // It sets all fields, including is_null, to zero, so we need to set
  697. // is_null only if it should be true. This gives up minor performance
  698. // benefit while being safe approach. For improved readability, the
  699. // code that explicitly sets is_null is there, but is commented out.
  700. memset(bind_, 0, sizeof(bind_));
  701. // address: varchar(39)
  702. // A Lease6_ address has a maximum of 39 characters. The array is
  703. // one byte longer than this to guarantee that we can always null
  704. // terminate it whatever is returned.
  705. addr6_length_ = sizeof(addr6_buffer_) - 1;
  706. bind_[0].buffer_type = MYSQL_TYPE_STRING;
  707. bind_[0].buffer = addr6_buffer_;
  708. bind_[0].buffer_length = addr6_length_;
  709. bind_[0].length = &addr6_length_;
  710. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  711. // reasons, see memset() above
  712. // client_id: varbinary(128)
  713. duid_length_ = sizeof(duid_buffer_);
  714. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  715. bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
  716. bind_[1].buffer_length = duid_length_;
  717. bind_[1].length = &duid_length_;
  718. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  719. // reasons, see memset() above
  720. // lease_time: unsigned int
  721. bind_[2].buffer_type = MYSQL_TYPE_LONG;
  722. bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
  723. bind_[2].is_unsigned = MLM_TRUE;
  724. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  725. // reasons, see memset() above
  726. // expire: timestamp
  727. bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
  728. bind_[3].buffer = reinterpret_cast<char*>(&expire_);
  729. bind_[3].buffer_length = sizeof(expire_);
  730. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  731. // reasons, see memset() above
  732. // subnet_id: unsigned int
  733. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  734. bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
  735. bind_[4].is_unsigned = MLM_TRUE;
  736. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  737. // reasons, see memset() above
  738. // pref_lifetime: unsigned int
  739. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  740. bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
  741. bind_[5].is_unsigned = MLM_TRUE;
  742. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  743. // reasons, see memset() above
  744. // lease_type: tinyint
  745. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  746. bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
  747. bind_[6].is_unsigned = MLM_TRUE;
  748. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  749. // reasons, see memset() above
  750. // iaid: unsigned int
  751. bind_[7].buffer_type = MYSQL_TYPE_LONG;
  752. bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
  753. bind_[7].is_unsigned = MLM_TRUE;
  754. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  755. // reasons, see memset() above
  756. // prefix_len: unsigned tinyint
  757. bind_[8].buffer_type = MYSQL_TYPE_TINY;
  758. bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
  759. bind_[8].is_unsigned = MLM_TRUE;
  760. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  761. // reasons, see memset() above
  762. // fqdn_fwd: boolean
  763. bind_[9].buffer_type = MYSQL_TYPE_TINY;
  764. bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
  765. bind_[9].is_unsigned = MLM_TRUE;
  766. // bind_[9].is_null = &MLM_FALSE; // commented out for performance
  767. // reasons, see memset() above
  768. // fqdn_rev: boolean
  769. bind_[10].buffer_type = MYSQL_TYPE_TINY;
  770. bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
  771. bind_[10].is_unsigned = MLM_TRUE;
  772. // bind_[10].is_null = &MLM_FALSE; // commented out for performance
  773. // reasons, see memset() above
  774. // hostname: varchar(255)
  775. hostname_length_ = sizeof(hostname_buffer_);
  776. bind_[11].buffer_type = MYSQL_TYPE_STRING;
  777. bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
  778. bind_[11].buffer_length = hostname_length_;
  779. bind_[11].length = &hostname_length_;
  780. // bind_[11].is_null = &MLM_FALSE; // commented out for performance
  781. // reasons, see memset() above
  782. // hardware address
  783. // hwaddr: varbinary(20)
  784. hwaddr_null_ = MLM_FALSE;
  785. hwaddr_length_ = sizeof(hwaddr_buffer_);
  786. bind_[12].buffer_type = MYSQL_TYPE_BLOB;
  787. bind_[12].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
  788. bind_[12].buffer_length = hwaddr_length_;
  789. bind_[12].length = &hwaddr_length_;
  790. bind_[12].is_null = &hwaddr_null_;
  791. // hardware type: unsigned short int (16 bits)
  792. bind_[13].buffer_type = MYSQL_TYPE_SHORT;
  793. bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
  794. bind_[13].is_unsigned = MLM_TRUE;
  795. // hardware source: unsigned int (32 bits)
  796. bind_[14].buffer_type = MYSQL_TYPE_LONG;
  797. bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
  798. bind_[14].is_unsigned = MLM_TRUE;
  799. // Add the error flags
  800. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  801. // .. and check that we have the numbers correct at compile time.
  802. BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
  803. // Add the data to the vector. Note the end element is one after the
  804. // end of the array.
  805. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  806. }
  807. /// @brief Copy Received Data into Lease6 Object
  808. ///
  809. /// Called after the MYSQL_BIND array created by createBindForReceive()
  810. /// has been used, this copies data from the internal member variables
  811. /// into a Lease6 object.
  812. ///
  813. /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
  814. /// data.
  815. ///
  816. /// @throw isc::BadValue Unable to convert Lease Type value in database
  817. Lease6Ptr getLeaseData() {
  818. // The address buffer is declared larger than the buffer size passed
  819. // to the access function so that we can always append a null byte.
  820. // Create the IOAddress object corresponding to the received data.
  821. addr6_buffer_[addr6_length_] = '\0';
  822. std::string address = addr6_buffer_;
  823. isc::asiolink::IOAddress addr(address);
  824. // Set the lease type in a variable of the appropriate data type, which
  825. // has been initialized with an arbitrary (but valid) value.
  826. Lease::Type type = Lease::TYPE_NA;
  827. switch (lease_type_) {
  828. case Lease::TYPE_NA:
  829. type = Lease::TYPE_NA;
  830. break;
  831. case Lease::TYPE_TA:
  832. type = Lease::TYPE_TA;
  833. break;
  834. case Lease::TYPE_PD:
  835. type = Lease::TYPE_PD;
  836. break;
  837. default:
  838. isc_throw(BadValue, "invalid lease type returned (" <<
  839. static_cast<int>(lease_type_) << ") for lease with "
  840. << "address " << address << ". Only 0, 1, or 2 are "
  841. << "allowed.");
  842. }
  843. // Set up DUID,
  844. DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
  845. // Hostname is passed to Lease6 as a string object, so we have to
  846. // create it from the hostname buffer and length.
  847. std::string hostname(hostname_buffer_,
  848. hostname_buffer_ + hostname_length_);
  849. /// Set hardware address if it was set
  850. HWAddrPtr hwaddr;
  851. if (hwaddr_null_ == MLM_FALSE) {
  852. hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, hwtype_));
  853. hwaddr->source_ = hwaddr_source_;
  854. }
  855. // Create the lease and set the cltt (after converting from the
  856. // expire time retrieved from the database).
  857. Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
  858. pref_lifetime_, valid_lifetime_, 0, 0,
  859. subnet_id_, fqdn_fwd_, fqdn_rev_,
  860. hostname, hwaddr, prefixlen_));
  861. time_t cltt = 0;
  862. MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
  863. result->cltt_ = cltt;
  864. return (result);
  865. }
  866. /// @brief Return columns in error
  867. ///
  868. /// If an error is returned from a fetch (in particular, a truncated
  869. /// status), this method can be called to get the names of the fields in
  870. /// error. It returns a string comprising the names of the fields
  871. /// separated by commas. In the case of there being no error indicators
  872. /// set, it returns the string "(None)".
  873. ///
  874. /// @return Comma-separated list of columns in error, or the string
  875. /// "(None)".
  876. std::string getErrorColumns() {
  877. return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
  878. }
  879. private:
  880. // Note: All array lengths are equal to the corresponding variable in the
  881. // schema.
  882. // Note: arrays are declared fixed length for speed of creation
  883. std::string addr6_; ///< String form of address
  884. char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< Character
  885. ///< array form of V6 address
  886. unsigned long addr6_length_; ///< Length of the address
  887. MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
  888. std::string columns_[LEASE_COLUMNS];///< Column names
  889. std::vector<uint8_t> duid_; ///< Client identification
  890. uint8_t duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID
  891. unsigned long duid_length_; ///< Length of the DUID
  892. my_bool error_[LEASE_COLUMNS]; ///< Error indicators
  893. MYSQL_TIME expire_; ///< Lease expiry time
  894. uint32_t iaid_; ///< Identity association ID
  895. Lease6Ptr lease_; ///< Pointer to lease object
  896. uint8_t lease_type_; ///< Lease type
  897. uint8_t prefixlen_; ///< Prefix length
  898. uint32_t pref_lifetime_; ///< Preferred lifetime
  899. uint32_t subnet_id_; ///< Subnet identification
  900. uint32_t valid_lifetime_; ///< Lease time
  901. my_bool fqdn_fwd_; ///< Has forward DNS update been
  902. ///< performed
  903. my_bool fqdn_rev_; ///< Has reverse DNS update been
  904. ///< performed
  905. char hostname_buffer_[HOSTNAME_MAX_LEN];
  906. ///< Client hostname
  907. unsigned long hostname_length_; ///< Client hostname length
  908. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  909. ///< Buffer for Hardware address
  910. std::vector<uint8_t> hwaddr_; ///< Hardware address (optional)
  911. unsigned long hwaddr_length_; ///< Aux. variable denoting hwaddr_ size()
  912. my_bool hwaddr_null_; ///< Used when HWAddr is null
  913. uint16_t hwtype_; ///< Hardware type
  914. uint32_t hwaddr_source_; ///< Source of the hardware address
  915. };
  916. // MySqlLeaseMgr Constructor and Destructor
  917. MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
  918. : MySqlConnection(parameters) {
  919. // Open the database.
  920. openDatabase();
  921. // Enable autocommit. To avoid a flush to disk on every commit, the global
  922. // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
  923. // cause the changes to be written to the log, but flushed to disk in the
  924. // background every second. Setting the parameter to that value will speed
  925. // up the system, but at the risk of losing data if the system crashes.
  926. my_bool result = mysql_autocommit(mysql_, 1);
  927. if (result != 0) {
  928. isc_throw(DbOperationError, mysql_error(mysql_));
  929. }
  930. // Prepare all statements likely to be used.
  931. prepareStatements();
  932. // Create the exchange objects for use in exchanging data between the
  933. // program and the database.
  934. exchange4_.reset(new MySqlLease4Exchange());
  935. exchange6_.reset(new MySqlLease6Exchange());
  936. }
  937. MySqlLeaseMgr::~MySqlLeaseMgr() {
  938. // Free up the prepared statements, ignoring errors. (What would we do
  939. // about them? We're destroying this object and are not really concerned
  940. // with errors on a database connection that is about to go away.)
  941. for (int i = 0; i < statements_.size(); ++i) {
  942. if (statements_[i] != NULL) {
  943. (void) mysql_stmt_close(statements_[i]);
  944. statements_[i] = NULL;
  945. }
  946. }
  947. // There is no need to close the database in this destructor: it is
  948. // closed in the destructor of the mysql_ member variable.
  949. }
  950. std::string
  951. MySqlLeaseMgr::getDBVersion() {
  952. std::stringstream tmp;
  953. tmp << "MySQL backend " << CURRENT_VERSION_VERSION;
  954. tmp << "." << CURRENT_VERSION_MINOR;
  955. tmp << ", library " << mysql_get_client_info();
  956. return (tmp.str());
  957. }
  958. // Time conversion methods.
  959. //
  960. // Note that the MySQL TIMESTAMP data type (used for "expire") converts data
  961. // from the current timezone to UTC for storage, and from UTC to the current
  962. // timezone for retrieval.
  963. //
  964. // This causes no problems providing that:
  965. // a) cltt is given in local time
  966. // b) We let the system take care of timezone conversion when converting
  967. // from a time read from the database into a local time.
  968. void
  969. MySqlLeaseMgr::convertToDatabaseTime(const time_t cltt,
  970. const uint32_t valid_lifetime,
  971. MYSQL_TIME& expire) {
  972. // Calculate expiry time. Store it in the 64-bit value so as we can detect
  973. // overflows.
  974. int64_t expire_time_64 = static_cast<int64_t>(cltt) +
  975. static_cast<int64_t>(valid_lifetime);
  976. // Even on 64-bit systems MySQL doesn't seem to accept the timestamps
  977. // beyond the max value of int32_t.
  978. if (expire_time_64 > LeaseMgr::MAX_DB_TIME) {
  979. isc_throw(BadValue, "Time value is too large: " << expire_time_64);
  980. }
  981. const time_t expire_time = static_cast<const time_t>(expire_time_64);
  982. // Convert to broken-out time
  983. struct tm expire_tm;
  984. (void) localtime_r(&expire_time, &expire_tm);
  985. // Place in output expire structure.
  986. expire.year = expire_tm.tm_year + 1900;
  987. expire.month = expire_tm.tm_mon + 1; // Note different base
  988. expire.day = expire_tm.tm_mday;
  989. expire.hour = expire_tm.tm_hour;
  990. expire.minute = expire_tm.tm_min;
  991. expire.second = expire_tm.tm_sec;
  992. expire.second_part = 0; // No fractional seconds
  993. expire.neg = my_bool(0); // Not negative
  994. }
  995. void
  996. MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
  997. uint32_t valid_lifetime, time_t& cltt) {
  998. // Copy across fields from MYSQL_TIME structure.
  999. struct tm expire_tm;
  1000. memset(&expire_tm, 0, sizeof(expire_tm));
  1001. expire_tm.tm_year = expire.year - 1900;
  1002. expire_tm.tm_mon = expire.month - 1;
  1003. expire_tm.tm_mday = expire.day;
  1004. expire_tm.tm_hour = expire.hour;
  1005. expire_tm.tm_min = expire.minute;
  1006. expire_tm.tm_sec = expire.second;
  1007. expire_tm.tm_isdst = -1; // Let the system work out about DST
  1008. // Convert to local time
  1009. cltt = mktime(&expire_tm) - valid_lifetime;
  1010. }
  1011. // Add leases to the database. The two public methods accept a lease object
  1012. // (either V4 of V6), bind the contents to the appropriate prepared
  1013. // statement, then call common code to execute the statement.
  1014. bool
  1015. MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
  1016. std::vector<MYSQL_BIND>& bind) {
  1017. // Bind the parameters to the statement
  1018. int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
  1019. checkError(status, stindex, "unable to bind parameters");
  1020. // Execute the statement
  1021. status = mysql_stmt_execute(statements_[stindex]);
  1022. if (status != 0) {
  1023. // Failure: check for the special case of duplicate entry. If this is
  1024. // the case, we return false to indicate that the row was not added.
  1025. // Otherwise we throw an exception.
  1026. if (mysql_errno(mysql_) == ER_DUP_ENTRY) {
  1027. return (false);
  1028. }
  1029. checkError(status, stindex, "unable to execute");
  1030. }
  1031. // Insert succeeded
  1032. return (true);
  1033. }
  1034. bool
  1035. MySqlLeaseMgr::addLease(const Lease4Ptr& lease) {
  1036. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1037. DHCPSRV_MYSQL_ADD_ADDR4).arg(lease->addr_.toText());
  1038. // Create the MYSQL_BIND array for the lease
  1039. std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
  1040. // ... and drop to common code.
  1041. return (addLeaseCommon(INSERT_LEASE4, bind));
  1042. }
  1043. bool
  1044. MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
  1045. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1046. DHCPSRV_MYSQL_ADD_ADDR6).arg(lease->addr_.toText())
  1047. .arg(lease->type_);
  1048. // Create the MYSQL_BIND array for the lease
  1049. std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
  1050. // ... and drop to common code.
  1051. return (addLeaseCommon(INSERT_LEASE6, bind));
  1052. }
  1053. // Extraction of leases from the database.
  1054. //
  1055. // All getLease() methods ultimately call getLeaseCollection(). This
  1056. // binds the input parameters passed to it with the appropriate prepared
  1057. // statement and executes the statement. It then gets the results from the
  1058. // database. getlease() methods that expect a single result back call it
  1059. // with the "single" parameter set true: this causes an exception to be
  1060. // generated if multiple records can be retrieved from the result set. (Such
  1061. // an occurrence either indicates corruption in the database, or that an
  1062. // assumption that a query can only return a single record is incorrect.)
  1063. // Methods that require a collection of records have "single" set to the
  1064. // default value of false. The logic is the same for both Lease4 and Lease6
  1065. // objects, so the code is templated.
  1066. //
  1067. // Methods that require a collection of objects access this method through
  1068. // two interface methods (also called getLeaseCollection()). These are
  1069. // short enough as to be defined in the header file: all they do is to supply
  1070. // the appropriate MySqlLeaseXExchange object depending on the type of the
  1071. // LeaseCollection objects passed to them.
  1072. //
  1073. // Methods that require a single object to be returned access the method
  1074. // through two interface methods (called getLease()). As well as supplying
  1075. // the appropriate exchange object, they convert between lease collection
  1076. // holding zero or one leases into an appropriate Lease object.
  1077. template <typename Exchange, typename LeaseCollection>
  1078. void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
  1079. MYSQL_BIND* bind,
  1080. Exchange& exchange,
  1081. LeaseCollection& result,
  1082. bool single) const {
  1083. // Bind the selection parameters to the statement
  1084. int status = mysql_stmt_bind_param(statements_[stindex], bind);
  1085. checkError(status, stindex, "unable to bind WHERE clause parameter");
  1086. // Set up the MYSQL_BIND array for the data being returned and bind it to
  1087. // the statement.
  1088. std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
  1089. status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
  1090. checkError(status, stindex, "unable to bind SELECT clause parameters");
  1091. // Execute the statement
  1092. status = mysql_stmt_execute(statements_[stindex]);
  1093. checkError(status, stindex, "unable to execute");
  1094. // Ensure that all the lease information is retrieved in one go to avoid
  1095. // overhead of going back and forth between client and server.
  1096. status = mysql_stmt_store_result(statements_[stindex]);
  1097. checkError(status, stindex, "unable to set up for storing all results");
  1098. // Set up the fetch "release" object to release resources associated
  1099. // with the call to mysql_stmt_fetch when this method exits, then
  1100. // retrieve the data.
  1101. MySqlFreeResult fetch_release(statements_[stindex]);
  1102. int count = 0;
  1103. while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) {
  1104. try {
  1105. result.push_back(exchange->getLeaseData());
  1106. } catch (const isc::BadValue& ex) {
  1107. // Rethrow the exception with a bit more data.
  1108. isc_throw(BadValue, ex.what() << ". Statement is <" <<
  1109. text_statements_[stindex] << ">");
  1110. }
  1111. if (single && (++count > 1)) {
  1112. isc_throw(MultipleRecords, "multiple records were found in the "
  1113. "database where only one was expected for query "
  1114. << text_statements_[stindex]);
  1115. }
  1116. }
  1117. // How did the fetch end?
  1118. if (status == 1) {
  1119. // Error - unable to fetch results
  1120. checkError(status, stindex, "unable to fetch results");
  1121. } else if (status == MYSQL_DATA_TRUNCATED) {
  1122. // Data truncated - throw an exception indicating what was at fault
  1123. isc_throw(DataTruncated, text_statements_[stindex]
  1124. << " returned truncated data: columns affected are "
  1125. << exchange->getErrorColumns());
  1126. }
  1127. }
  1128. void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
  1129. Lease4Ptr& result) const {
  1130. // Create appropriate collection object and get all leases matching
  1131. // the selection criteria. The "single" paraeter is true to indicate
  1132. // that the called method should throw an exception if multiple
  1133. // matching records are found: this particular method is called when only
  1134. // one or zero matches is expected.
  1135. Lease4Collection collection;
  1136. getLeaseCollection(stindex, bind, exchange4_, collection, true);
  1137. // Return single record if present, else clear the lease.
  1138. if (collection.empty()) {
  1139. result.reset();
  1140. } else {
  1141. result = *collection.begin();
  1142. }
  1143. }
  1144. void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
  1145. Lease6Ptr& result) const {
  1146. // Create appropriate collection object and get all leases matching
  1147. // the selection criteria. The "single" paraeter is true to indicate
  1148. // that the called method should throw an exception if multiple
  1149. // matching records are found: this particular method is called when only
  1150. // one or zero matches is expected.
  1151. Lease6Collection collection;
  1152. getLeaseCollection(stindex, bind, exchange6_, collection, true);
  1153. // Return single record if present, else clear the lease.
  1154. if (collection.empty()) {
  1155. result.reset();
  1156. } else {
  1157. result = *collection.begin();
  1158. }
  1159. }
  1160. // Basic lease access methods. Obtain leases from the database using various
  1161. // criteria.
  1162. Lease4Ptr
  1163. MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  1164. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1165. DHCPSRV_MYSQL_GET_ADDR4).arg(addr.toText());
  1166. // Set up the WHERE clause value
  1167. MYSQL_BIND inbind[1];
  1168. memset(inbind, 0, sizeof(inbind));
  1169. uint32_t addr4 = static_cast<uint32_t>(addr);
  1170. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1171. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  1172. inbind[0].is_unsigned = MLM_TRUE;
  1173. // Get the data
  1174. Lease4Ptr result;
  1175. getLease(GET_LEASE4_ADDR, inbind, result);
  1176. return (result);
  1177. }
  1178. Lease4Collection
  1179. MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
  1180. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1181. DHCPSRV_MYSQL_GET_HWADDR).arg(hwaddr.toText());
  1182. // Set up the WHERE clause value
  1183. MYSQL_BIND inbind[1];
  1184. memset(inbind, 0, sizeof(inbind));
  1185. // As "buffer" is "char*" - even though the data is being read - we need
  1186. // to cast away the "const"ness as well as reinterpreting the data as
  1187. // a "char*". (We could avoid the "const_cast" by copying the data to a
  1188. // local variable, but as the data is only being read, this introduces
  1189. // an unnecessary copy).
  1190. unsigned long hwaddr_length = hwaddr.hwaddr_.size();
  1191. uint8_t* data = const_cast<uint8_t*>(&hwaddr.hwaddr_[0]);
  1192. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1193. inbind[0].buffer = reinterpret_cast<char*>(data);
  1194. inbind[0].buffer_length = hwaddr_length;
  1195. inbind[0].length = &hwaddr_length;
  1196. // Get the data
  1197. Lease4Collection result;
  1198. getLeaseCollection(GET_LEASE4_HWADDR, inbind, result);
  1199. return (result);
  1200. }
  1201. Lease4Ptr
  1202. MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  1203. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1204. DHCPSRV_MYSQL_GET_SUBID_HWADDR)
  1205. .arg(subnet_id).arg(hwaddr.toText());
  1206. // Set up the WHERE clause value
  1207. MYSQL_BIND inbind[2];
  1208. memset(inbind, 0, sizeof(inbind));
  1209. // As "buffer" is "char*" - even though the data is being read - we need
  1210. // to cast away the "const"ness as well as reinterpreting the data as
  1211. // a "char*". (We could avoid the "const_cast" by copying the data to a
  1212. // local variable, but as the data is only being read, this introduces
  1213. // an unnecessary copy).
  1214. unsigned long hwaddr_length = hwaddr.hwaddr_.size();
  1215. uint8_t* data = const_cast<uint8_t*>(&hwaddr.hwaddr_[0]);
  1216. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1217. inbind[0].buffer = reinterpret_cast<char*>(data);
  1218. inbind[0].buffer_length = hwaddr_length;
  1219. inbind[0].length = &hwaddr_length;
  1220. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1221. inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
  1222. inbind[1].is_unsigned = MLM_TRUE;
  1223. // Get the data
  1224. Lease4Ptr result;
  1225. getLease(GET_LEASE4_HWADDR_SUBID, inbind, result);
  1226. return (result);
  1227. }
  1228. Lease4Collection
  1229. MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
  1230. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1231. DHCPSRV_MYSQL_GET_CLIENTID).arg(clientid.toText());
  1232. // Set up the WHERE clause value
  1233. MYSQL_BIND inbind[1];
  1234. memset(inbind, 0, sizeof(inbind));
  1235. std::vector<uint8_t> client_data = clientid.getClientId();
  1236. unsigned long client_data_length = client_data.size();
  1237. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1238. inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
  1239. inbind[0].buffer_length = client_data_length;
  1240. inbind[0].length = &client_data_length;
  1241. // Get the data
  1242. Lease4Collection result;
  1243. getLeaseCollection(GET_LEASE4_CLIENTID, inbind, result);
  1244. return (result);
  1245. }
  1246. Lease4Ptr
  1247. MySqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
  1248. /// This function is currently not implemented because allocation engine
  1249. /// searches for the lease using HW address or client identifier.
  1250. /// It never uses both parameters in the same time. We need to
  1251. /// consider if this function is needed at all.
  1252. isc_throw(NotImplemented, "The MySqlLeaseMgr::getLease4 function was"
  1253. " called, but it is not implemented");
  1254. }
  1255. Lease4Ptr
  1256. MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
  1257. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1258. DHCPSRV_MYSQL_GET_SUBID_CLIENTID)
  1259. .arg(subnet_id).arg(clientid.toText());
  1260. // Set up the WHERE clause value
  1261. MYSQL_BIND inbind[2];
  1262. memset(inbind, 0, sizeof(inbind));
  1263. std::vector<uint8_t> client_data = clientid.getClientId();
  1264. unsigned long client_data_length = client_data.size();
  1265. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1266. inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
  1267. inbind[0].buffer_length = client_data_length;
  1268. inbind[0].length = &client_data_length;
  1269. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1270. inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
  1271. inbind[1].is_unsigned = MLM_TRUE;
  1272. // Get the data
  1273. Lease4Ptr result;
  1274. getLease(GET_LEASE4_CLIENTID_SUBID, inbind, result);
  1275. return (result);
  1276. }
  1277. Lease6Ptr
  1278. MySqlLeaseMgr::getLease6(Lease::Type lease_type,
  1279. const isc::asiolink::IOAddress& addr) const {
  1280. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1281. DHCPSRV_MYSQL_GET_ADDR6).arg(addr.toText())
  1282. .arg(lease_type);
  1283. // Set up the WHERE clause value
  1284. MYSQL_BIND inbind[2];
  1285. memset(inbind, 0, sizeof(inbind));
  1286. std::string addr6 = addr.toText();
  1287. unsigned long addr6_length = addr6.size();
  1288. // See the earlier description of the use of "const_cast" when accessing
  1289. // the address for an explanation of the reason.
  1290. inbind[0].buffer_type = MYSQL_TYPE_STRING;
  1291. inbind[0].buffer = const_cast<char*>(addr6.c_str());
  1292. inbind[0].buffer_length = addr6_length;
  1293. inbind[0].length = &addr6_length;
  1294. // LEASE_TYPE
  1295. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1296. inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
  1297. inbind[1].is_unsigned = MLM_TRUE;
  1298. Lease6Ptr result;
  1299. getLease(GET_LEASE6_ADDR, inbind, result);
  1300. return (result);
  1301. }
  1302. Lease6Collection
  1303. MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
  1304. const DUID& duid, uint32_t iaid) const {
  1305. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1306. DHCPSRV_MYSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
  1307. .arg(lease_type);
  1308. // Set up the WHERE clause value
  1309. MYSQL_BIND inbind[3];
  1310. memset(inbind, 0, sizeof(inbind));
  1311. // In the following statement, the DUID is being read. However, the
  1312. // MySQL C interface does not use "const", so the "buffer" element
  1313. // is declared as "char*" instead of "const char*". To resolve this,
  1314. // the "const" is discarded before the uint8_t* is cast to char*.
  1315. //
  1316. // Note that the const_cast could be avoided by copying the DUID to
  1317. // a writeable buffer and storing the address of that in the "buffer"
  1318. // element. However, this introduces a copy operation (with additional
  1319. // overhead) purely to get round the structures introduced by design of
  1320. // the MySQL interface (which uses the area pointed to by "buffer" as
  1321. // input when specifying query parameters and as output when retrieving
  1322. // data). For that reason, "const_cast" has been used.
  1323. const vector<uint8_t>& duid_vector = duid.getDuid();
  1324. unsigned long duid_length = duid_vector.size();
  1325. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1326. inbind[0].buffer = reinterpret_cast<char*>(
  1327. const_cast<uint8_t*>(&duid_vector[0]));
  1328. inbind[0].buffer_length = duid_length;
  1329. inbind[0].length = &duid_length;
  1330. // IAID
  1331. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1332. inbind[1].buffer = reinterpret_cast<char*>(&iaid);
  1333. inbind[1].is_unsigned = MLM_TRUE;
  1334. // LEASE_TYPE
  1335. inbind[2].buffer_type = MYSQL_TYPE_TINY;
  1336. inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
  1337. inbind[2].is_unsigned = MLM_TRUE;
  1338. // ... and get the data
  1339. Lease6Collection result;
  1340. getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
  1341. return (result);
  1342. }
  1343. Lease6Collection
  1344. MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
  1345. const DUID& duid, uint32_t iaid,
  1346. SubnetID subnet_id) const {
  1347. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1348. DHCPSRV_MYSQL_GET_IAID_SUBID_DUID)
  1349. .arg(iaid).arg(subnet_id).arg(duid.toText())
  1350. .arg(lease_type);
  1351. // Set up the WHERE clause value
  1352. MYSQL_BIND inbind[4];
  1353. memset(inbind, 0, sizeof(inbind));
  1354. // See the earlier description of the use of "const_cast" when accessing
  1355. // the DUID for an explanation of the reason.
  1356. const vector<uint8_t>& duid_vector = duid.getDuid();
  1357. unsigned long duid_length = duid_vector.size();
  1358. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1359. inbind[0].buffer = reinterpret_cast<char*>(
  1360. const_cast<uint8_t*>(&duid_vector[0]));
  1361. inbind[0].buffer_length = duid_length;
  1362. inbind[0].length = &duid_length;
  1363. // IAID
  1364. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1365. inbind[1].buffer = reinterpret_cast<char*>(&iaid);
  1366. inbind[1].is_unsigned = MLM_TRUE;
  1367. // Subnet ID
  1368. inbind[2].buffer_type = MYSQL_TYPE_LONG;
  1369. inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
  1370. inbind[2].is_unsigned = MLM_TRUE;
  1371. // LEASE_TYPE
  1372. inbind[3].buffer_type = MYSQL_TYPE_TINY;
  1373. inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
  1374. inbind[3].is_unsigned = MLM_TRUE;
  1375. // ... and get the data
  1376. Lease6Collection result;
  1377. getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
  1378. return (result);
  1379. }
  1380. // Update lease methods. These comprise common code that handles the actual
  1381. // update, and type-specific methods that set up the parameters for the prepared
  1382. // statement depending on the type of lease.
  1383. template <typename LeasePtr>
  1384. void
  1385. MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
  1386. const LeasePtr& lease) {
  1387. // Bind the parameters to the statement
  1388. int status = mysql_stmt_bind_param(statements_[stindex], bind);
  1389. checkError(status, stindex, "unable to bind parameters");
  1390. // Execute
  1391. status = mysql_stmt_execute(statements_[stindex]);
  1392. checkError(status, stindex, "unable to execute");
  1393. // See how many rows were affected. The statement should only update a
  1394. // single row.
  1395. int affected_rows = mysql_stmt_affected_rows(statements_[stindex]);
  1396. if (affected_rows == 0) {
  1397. isc_throw(NoSuchLease, "unable to update lease for address " <<
  1398. lease->addr_ << " as it does not exist");
  1399. } else if (affected_rows > 1) {
  1400. // Should not happen - primary key constraint should only have selected
  1401. // one row.
  1402. isc_throw(DbOperationError, "apparently updated more than one lease "
  1403. "that had the address " << lease->addr_);
  1404. }
  1405. }
  1406. void
  1407. MySqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
  1408. const StatementIndex stindex = UPDATE_LEASE4;
  1409. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1410. DHCPSRV_MYSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
  1411. // Create the MYSQL_BIND array for the data being updated
  1412. std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
  1413. // Set up the WHERE clause and append it to the MYSQL_BIND array
  1414. MYSQL_BIND where;
  1415. memset(&where, 0, sizeof(where));
  1416. uint32_t addr4 = static_cast<uint32_t>(lease->addr_);
  1417. where.buffer_type = MYSQL_TYPE_LONG;
  1418. where.buffer = reinterpret_cast<char*>(&addr4);
  1419. where.is_unsigned = MLM_TRUE;
  1420. bind.push_back(where);
  1421. // Drop to common update code
  1422. updateLeaseCommon(stindex, &bind[0], lease);
  1423. }
  1424. void
  1425. MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
  1426. const StatementIndex stindex = UPDATE_LEASE6;
  1427. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1428. DHCPSRV_MYSQL_UPDATE_ADDR6).arg(lease->addr_.toText())
  1429. .arg(lease->type_);
  1430. // Create the MYSQL_BIND array for the data being updated
  1431. std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
  1432. // Set up the WHERE clause value
  1433. MYSQL_BIND where;
  1434. memset(&where, 0, sizeof(where));
  1435. std::string addr6 = lease->addr_.toText();
  1436. unsigned long addr6_length = addr6.size();
  1437. // See the earlier description of the use of "const_cast" when accessing
  1438. // the address for an explanation of the reason.
  1439. where.buffer_type = MYSQL_TYPE_STRING;
  1440. where.buffer = const_cast<char*>(addr6.c_str());
  1441. where.buffer_length = addr6_length;
  1442. where.length = &addr6_length;
  1443. bind.push_back(where);
  1444. // Drop to common update code
  1445. updateLeaseCommon(stindex, &bind[0], lease);
  1446. }
  1447. // Delete lease methods. Similar to other groups of methods, these comprise
  1448. // a per-type method that sets up the relevant MYSQL_BIND array (in this
  1449. // case, a single method for both V4 and V6 addresses) and a common method that
  1450. // handles the common processing.
  1451. bool
  1452. MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
  1453. // Bind the input parameters to the statement
  1454. int status = mysql_stmt_bind_param(statements_[stindex], bind);
  1455. checkError(status, stindex, "unable to bind WHERE clause parameter");
  1456. // Execute
  1457. status = mysql_stmt_execute(statements_[stindex]);
  1458. checkError(status, stindex, "unable to execute");
  1459. // See how many rows were affected. Note that the statement may delete
  1460. // multiple rows.
  1461. return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
  1462. }
  1463. bool
  1464. MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  1465. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1466. DHCPSRV_MYSQL_DELETE_ADDR).arg(addr.toText());
  1467. // Set up the WHERE clause value
  1468. MYSQL_BIND inbind[1];
  1469. memset(inbind, 0, sizeof(inbind));
  1470. if (addr.isV4()) {
  1471. uint32_t addr4 = static_cast<uint32_t>(addr);
  1472. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1473. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  1474. inbind[0].is_unsigned = MLM_TRUE;
  1475. return (deleteLeaseCommon(DELETE_LEASE4, inbind));
  1476. } else {
  1477. std::string addr6 = addr.toText();
  1478. unsigned long addr6_length = addr6.size();
  1479. // See the earlier description of the use of "const_cast" when accessing
  1480. // the address for an explanation of the reason.
  1481. inbind[0].buffer_type = MYSQL_TYPE_STRING;
  1482. inbind[0].buffer = const_cast<char*>(addr6.c_str());
  1483. inbind[0].buffer_length = addr6_length;
  1484. inbind[0].length = &addr6_length;
  1485. return (deleteLeaseCommon(DELETE_LEASE6, inbind));
  1486. }
  1487. }
  1488. }; // end of isc::dhcp namespace
  1489. }; // end of isc namespace