Kea 1.5.0
mysql_host_data_source.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <dhcp/libdhcp++.h>
11#include <dhcp/option.h>
13#include <dhcp/option_space.h>
14#include <dhcpsrv/cfg_option.h>
15#include <dhcpsrv/dhcpsrv_log.h>
17#include <util/buffer.h>
18#include <util/optional_value.h>
19
20#include <boost/algorithm/string/split.hpp>
21#include <boost/algorithm/string/classification.hpp>
22#include <boost/array.hpp>
23#include <boost/pointer_cast.hpp>
24#include <boost/static_assert.hpp>
25
26#include <mysql.h>
27#include <mysqld_error.h>
28
29#include <stdint.h>
30#include <string>
31
32using namespace isc;
33using namespace isc::asiolink;
34using namespace isc::db;
35using namespace isc::dhcp;
36using namespace isc::util;
37using namespace isc::data;
38using namespace std;
39
40namespace {
41
46const size_t ADDRESS6_TEXT_MAX_LEN = 39;
47
50const size_t CLIENT_CLASSES_MAX_LEN = 255;
51
56const size_t HOSTNAME_MAX_LEN = 255;
57
59const size_t OPTION_VALUE_MAX_LEN = 4096;
60
62const size_t OPTION_FORMATTED_VALUE_MAX_LEN = 8192;
63
65const size_t OPTION_SPACE_MAX_LEN = 128;
66
68const size_t USER_CONTEXT_MAX_LEN = 8192;
69
71const size_t SERVER_HOSTNAME_MAX_LEN = 64;
72
74const size_t BOOT_FILE_NAME_MAX_LEN = 128;
75
77const size_t KEY_LEN = 16;
78
83const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
84
113class MySqlHostExchange {
114private:
115
117 static const size_t HOST_COLUMNS = 14;
118
119public:
120
127 MySqlHostExchange(const size_t additional_columns_num = 0)
128 : columns_num_(HOST_COLUMNS + additional_columns_num),
129 bind_(columns_num_), columns_(columns_num_),
130 error_(columns_num_, MLM_FALSE), host_id_(0),
131 dhcp_identifier_length_(0), dhcp_identifier_type_(0),
132 dhcp4_subnet_id_(SUBNET_ID_UNUSED),
133 dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
134 hostname_length_(0), dhcp4_client_classes_length_(0),
135 dhcp6_client_classes_length_(0),
136 user_context_length_(0),
137 dhcp4_next_server_(0),
138 dhcp4_server_hostname_length_(0),
139 dhcp4_boot_file_name_length_(0),
140 dhcp4_subnet_id_null_(MLM_FALSE),
141 dhcp6_subnet_id_null_(MLM_FALSE),
142 ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE),
143 dhcp4_client_classes_null_(MLM_FALSE),
144 dhcp6_client_classes_null_(MLM_FALSE),
145 user_context_null_(MLM_FALSE),
146 dhcp4_next_server_null_(MLM_FALSE),
147 dhcp4_server_hostname_null_(MLM_FALSE),
148 dhcp4_boot_file_name_null_(MLM_FALSE),
149 auth_key_null_(MLM_FALSE) {
150
151 // Fill arrays with 0 so as they don't include any garbage.
152 memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
153 memset(hostname_, 0, sizeof(hostname_));
154 memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
155 memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
156 memset(user_context_, 0, sizeof(user_context_));
157 memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_));
158 memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_));
159
160 // Set the column names for use by this class. This only comprises
161 // names used by the MySqlHostExchange class. Derived classes will
162 // need to set names for the columns they use.
163 columns_[0] = "host_id";
164 columns_[1] = "dhcp_identifier";
165 columns_[2] = "dhcp_identifier_type";
166 columns_[3] = "dhcp4_subnet_id";
167 columns_[4] = "dhcp6_subnet_id";
168 columns_[5] = "ipv4_address";
169 columns_[6] = "hostname";
170 columns_[7] = "dhcp4_client_classes";
171 columns_[8] = "dhcp6_client_classes";
172 columns_[9] = "user_context";
173 columns_[10] = "dhcp4_next_server";
174 columns_[11] = "dhcp4_server_hostname";
175 columns_[12] = "dhcp4_boot_file_name";
176 columns_[13] = "auth_key";
177
178 BOOST_STATIC_ASSERT(13 < HOST_COLUMNS);
179 };
180
182 virtual ~MySqlHostExchange() {
183 }
184
199 size_t findAvailColumn() const {
200 std::vector<std::string>::const_iterator empty_column =
201 std::find(columns_.begin(), columns_.end(), std::string());
202 return (std::distance(columns_.begin(), empty_column));
203 }
204
208 uint64_t getHostId() const {
209 return (host_id_);
210 };
211
222 static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
223 std::vector<my_bool>& error) {
224 for (size_t i = 0; i < error.size(); ++i) {
225 error[i] = MLM_FALSE;
226 bind[i].error = reinterpret_cast<char*>(&error[i]);
227 }
228 };
229
243 static std::string getColumnsInError(std::vector<my_bool>& error,
244 const std::vector<std::string>& names) {
245 std::string result = "";
246
247 // Accumulate list of column names
248 for (size_t i = 0; i < names.size(); ++i) {
249 if (error[i] == MLM_TRUE) {
250 if (!result.empty()) {
251 result += ", ";
252 }
253 result += names[i];
254 }
255 }
256
257 if (result.empty()) {
258 result = "(None)";
259 }
260
261 return (result);
262 };
263
274 std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host) {
275 // Store host object to ensure it remains valid.
276 host_ = host;
277
278 // Initialize prior to constructing the array of MYSQL_BIND structures.
279 // It sets all fields, including is_null, to zero, so we need to set
280 // is_null only if it should be true. This gives up minor performance
281 // benefit while being safe approach.
282 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
283
284 // Set up the structures for the various components of the host structure.
285
286 try {
287 // host_id : INT UNSIGNED NOT NULL
288 // The host_id is auto_incremented by MySQL database,
289 // so we need to pass the NULL value
290 host_id_ = 0;
291 bind_[0].buffer_type = MYSQL_TYPE_LONG;
292 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
293 bind_[0].is_unsigned = MLM_TRUE;
294
295 // dhcp_identifier : VARBINARY(128) NOT NULL
296 dhcp_identifier_length_ = host->getIdentifier().size();
297 memcpy(static_cast<void*>(dhcp_identifier_buffer_),
298 &(host->getIdentifier())[0],
299 host->getIdentifier().size());
300
301 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
302 bind_[1].buffer = dhcp_identifier_buffer_;
303 bind_[1].buffer_length = dhcp_identifier_length_;
304 bind_[1].length = &dhcp_identifier_length_;
305
306 // dhcp_identifier_type : TINYINT NOT NULL
307 dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
308 bind_[2].buffer_type = MYSQL_TYPE_TINY;
309 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
310 bind_[2].is_unsigned = MLM_TRUE;
311
312 // dhcp4_subnet_id : INT UNSIGNED NULL
313 // Can't take an address of intermediate object, so let's store it
314 // in dhcp4_subnet_id_
315 dhcp4_subnet_id_ = host->getIPv4SubnetID();
316 dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
317 bind_[3].buffer_type = MYSQL_TYPE_LONG;
318 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
319 bind_[3].is_unsigned = MLM_TRUE;
320 bind_[3].is_null = &dhcp4_subnet_id_null_;
321
322 // dhcp6_subnet_id : INT UNSIGNED NULL
323 // Can't take an address of intermediate object, so let's store it
324 // in dhcp6_subnet_id_
325 dhcp6_subnet_id_ = host->getIPv6SubnetID();
326 dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
327 bind_[4].buffer_type = MYSQL_TYPE_LONG;
328 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
329 bind_[4].is_unsigned = MLM_TRUE;
330 bind_[4].is_null = &dhcp6_subnet_id_null_;
331
332 // ipv4_address : INT UNSIGNED NULL
333 // The address in the Host structure is an IOAddress object. Convert
334 // this to an integer for storage.
335 ipv4_address_ = host->getIPv4Reservation().toUint32();
336 ipv4_address_null_ = ipv4_address_ == 0 ? MLM_TRUE : MLM_FALSE;
337 bind_[5].buffer_type = MYSQL_TYPE_LONG;
338 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
339 bind_[5].is_unsigned = MLM_TRUE;
340 bind_[5].is_null = &ipv4_address_null_;
341
342 // hostname : VARCHAR(255) NULL
343 strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
344 hostname_length_ = host->getHostname().length();
345 bind_[6].buffer_type = MYSQL_TYPE_STRING;
346 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
347 bind_[6].buffer_length = hostname_length_;
348
349 // dhcp4_client_classes : VARCHAR(255) NULL
350 bind_[7].buffer_type = MYSQL_TYPE_STRING;
351 // Override default separator to not include space after comma.
352 string classes4_txt = host->getClientClasses4().toText(",");
353 strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
354 bind_[7].buffer = dhcp4_client_classes_;
355 bind_[7].buffer_length = classes4_txt.length();
356
357 // dhcp6_client_classes : VARCHAR(255) NULL
358 bind_[8].buffer_type = MYSQL_TYPE_STRING;
359 // Override default separator to not include space after comma.
360 string classes6_txt = host->getClientClasses6().toText(",");
361 strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
362 bind_[8].buffer = dhcp6_client_classes_;
363 bind_[8].buffer_length = classes6_txt.length();
364
365 // user_context : TEXT NULL
366 ConstElementPtr ctx = host->getContext();
367 if (ctx) {
368 bind_[9].buffer_type = MYSQL_TYPE_STRING;
369 string ctx_txt = ctx->str();
370 strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
371 bind_[9].buffer = user_context_;
372 bind_[9].buffer_length = ctx_txt.length();
373 } else {
374 bind_[9].buffer_type = MYSQL_TYPE_NULL;
375 }
376
377 // ipv4_address : INT UNSIGNED NULL
378 // The address in the Host structure is an IOAddress object. Convert
379 // this to an integer for storage.
380 dhcp4_next_server_ = host->getNextServer().toUint32();
381 bind_[10].buffer_type = MYSQL_TYPE_LONG;
382 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
383 bind_[10].is_unsigned = MLM_TRUE;
384 // bind_[10].is_null = &MLM_FALSE; // commented out for performance
385 // reasons, see memset() above
386
387 // dhcp4_server_hostname
388 bind_[11].buffer_type = MYSQL_TYPE_STRING;
389 std::string server_hostname = host->getServerHostname();
390 strncpy(dhcp4_server_hostname_, server_hostname.c_str(),
391 SERVER_HOSTNAME_MAX_LEN - 1);
392 bind_[11].buffer = dhcp4_server_hostname_;
393 bind_[11].buffer_length = server_hostname.length();
394
395 // dhcp4_boot_file_name
396 bind_[12].buffer_type = MYSQL_TYPE_STRING;
397 std::string boot_file_name = host->getBootFileName();
398 strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(),
399 BOOT_FILE_NAME_MAX_LEN - 1);
400 bind_[12].buffer = dhcp4_boot_file_name_;
401 bind_[12].buffer_length = boot_file_name.length();
402
403 // auth key
404 bind_[13].buffer_type = MYSQL_TYPE_STRING;
405 std::string auth_key = host->getKey().ToText();
406 std::strncpy(auth_key_, auth_key.c_str(), KEY_LEN);
407 auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
408 bind_[13].buffer = auth_key_;
409 bind_[13].buffer_length = auth_key.length();
410
411 } catch (const std::exception& ex) {
413 "Could not create bind array from Host: "
414 << host->getHostname() << ", reason: " << ex.what());
415 }
416
417 // Add the data to the vector. Note the end element is one after the
418 // end of the array.
419 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[columns_num_]));
420 };
421
429 virtual std::vector<MYSQL_BIND> createBindForReceive() {
430 // Initialize MYSQL_BIND array.
431 // It sets all fields, including is_null, to zero, so we need to set
432 // is_null only if it should be true. This gives up minor performance
433 // benefit while being safe approach. For improved readability, the
434 // code that explicitly sets is_null is there, but is commented out.
435 // This also takes care of setting bind_[X].is_null to MLM_FALSE.
436 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
437
438 // host_id : INT UNSIGNED NOT NULL
439 bind_[0].buffer_type = MYSQL_TYPE_LONG;
440 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
441 bind_[0].is_unsigned = MLM_TRUE;
442
443 // dhcp_identifier : VARBINARY(128) NOT NULL
444 dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
445 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
446 bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
447 bind_[1].buffer_length = dhcp_identifier_length_;
448 bind_[1].length = &dhcp_identifier_length_;
449
450 // dhcp_identifier_type : TINYINT NOT NULL
451 bind_[2].buffer_type = MYSQL_TYPE_TINY;
452 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
453 bind_[2].is_unsigned = MLM_TRUE;
454
455 // dhcp4_subnet_id : INT UNSIGNED NULL
456 dhcp4_subnet_id_null_ = MLM_FALSE;
457 bind_[3].buffer_type = MYSQL_TYPE_LONG;
458 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
459 bind_[3].is_unsigned = MLM_TRUE;
460 bind_[3].is_null = &dhcp4_subnet_id_null_;
461
462 // dhcp6_subnet_id : INT UNSIGNED NULL
463 dhcp6_subnet_id_null_ = MLM_FALSE;
464 bind_[4].buffer_type = MYSQL_TYPE_LONG;
465 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
466 bind_[4].is_unsigned = MLM_TRUE;
467 bind_[4].is_null = &dhcp6_subnet_id_null_;
468
469 // ipv4_address : INT UNSIGNED NULL
470 ipv4_address_null_ = MLM_FALSE;
471 bind_[5].buffer_type = MYSQL_TYPE_LONG;
472 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
473 bind_[5].is_unsigned = MLM_TRUE;
474 bind_[5].is_null = &ipv4_address_null_;
475
476 // hostname : VARCHAR(255) NULL
477 hostname_null_ = MLM_FALSE;
478 hostname_length_ = sizeof(hostname_);
479 bind_[6].buffer_type = MYSQL_TYPE_STRING;
480 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
481 bind_[6].buffer_length = hostname_length_;
482 bind_[6].length = &hostname_length_;
483 bind_[6].is_null = &hostname_null_;
484
485 // dhcp4_client_classes : VARCHAR(255) NULL
486 dhcp4_client_classes_null_ = MLM_FALSE;
487 dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
488 bind_[7].buffer_type = MYSQL_TYPE_STRING;
489 bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
490 bind_[7].buffer_length = dhcp4_client_classes_length_;
491 bind_[7].length = &dhcp4_client_classes_length_;
492 bind_[7].is_null = &dhcp4_client_classes_null_;
493
494 // dhcp6_client_classes : VARCHAR(255) NULL
495 dhcp6_client_classes_null_ = MLM_FALSE;
496 dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
497 bind_[8].buffer_type = MYSQL_TYPE_STRING;
498 bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
499 bind_[8].buffer_length = dhcp6_client_classes_length_;
500 bind_[8].length = &dhcp6_client_classes_length_;
501 bind_[8].is_null = &dhcp6_client_classes_null_;
502
503 // user_context : TEXT NULL
504 user_context_null_ = MLM_FALSE;
505 user_context_length_ = sizeof(user_context_);
506 bind_[9].buffer_type = MYSQL_TYPE_STRING;
507 bind_[9].buffer = reinterpret_cast<char*>(user_context_);
508 bind_[9].buffer_length = user_context_length_;
509 bind_[9].length = &user_context_length_;
510 bind_[9].is_null = &user_context_null_;
511
512 // dhcp4_next_server
513 dhcp4_next_server_null_ = MLM_FALSE;
514 bind_[10].buffer_type = MYSQL_TYPE_LONG;
515 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
516 bind_[10].is_unsigned = MLM_TRUE;
517 bind_[10].is_null = &dhcp4_next_server_null_;
518
519 // dhcp4_server_hostname
520 dhcp4_server_hostname_null_ = MLM_FALSE;
521 dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_);
522 bind_[11].buffer_type = MYSQL_TYPE_STRING;
523 bind_[11].buffer = reinterpret_cast<char*>(dhcp4_server_hostname_);
524 bind_[11].buffer_length = dhcp4_server_hostname_length_;
525 bind_[11].length = &dhcp4_server_hostname_length_;
526 bind_[11].is_null = &dhcp4_server_hostname_null_;
527
528 // dhcp4_boot_file_name
529 dhcp4_boot_file_name_null_ = MLM_FALSE;
530 dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_);
531 bind_[12].buffer_type = MYSQL_TYPE_STRING;
532 bind_[12].buffer = reinterpret_cast<char*>(dhcp4_boot_file_name_);
533 bind_[12].buffer_length = dhcp4_boot_file_name_length_;
534 bind_[12].length = &dhcp4_boot_file_name_length_;
535 bind_[12].is_null = &dhcp4_boot_file_name_null_;
536
537 // auth_key_
538 auth_key_null_ = MLM_FALSE;
539 auth_key_length_ = sizeof(auth_key_);
540 bind_[13].buffer_type = MYSQL_TYPE_STRING;
541 bind_[13].buffer = reinterpret_cast<char*>(auth_key_);
542 bind_[13].buffer_length = auth_key_length_;
543 bind_[13].length = &auth_key_length_;
544 bind_[13].is_null = &auth_key_null_;
545
546 // Add the error flags
547 setErrorIndicators(bind_, error_);
548
549 // Add the data to the vector. Note the end element is one after the
550 // end of the array.
551 return (bind_);
552 };
553
562 HostPtr retrieveHost() {
563 // Check if the identifier stored in the database is correct.
564 if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
565 isc_throw(BadValue, "invalid dhcp identifier type returned: "
566 << static_cast<int>(dhcp_identifier_type_));
567 }
568 // Set the dhcp identifier type in a variable of the appropriate
569 // data type.
571 static_cast<Host::IdentifierType>(dhcp_identifier_type_);
572
573 // Set DHCPv4 subnet ID to the value returned. If NULL returned,
574 // set to 0.
575 SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
576 if (dhcp4_subnet_id_null_ == MLM_FALSE) {
577 ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
578 }
579
580 // Set DHCPv6 subnet ID to the value returned. If NULL returned,
581 // set to 0.
582 SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
583 if (dhcp6_subnet_id_null_ == MLM_FALSE) {
584 ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
585 }
586
587 // Set IPv4 address reservation if it was given, if not, set IPv4 zero
588 // address
590 if (ipv4_address_null_ == MLM_FALSE) {
591 ipv4_reservation = asiolink::IOAddress(ipv4_address_);
592 }
593
594 // Set hostname if non NULL value returned. Otherwise, leave an
595 // empty string.
596 std::string hostname;
597 if (hostname_null_ == MLM_FALSE) {
598 hostname = std::string(hostname_, hostname_length_);
599 }
600
601 // Set DHCPv4 client classes if non NULL value returned.
602 std::string dhcp4_client_classes;
603 if (dhcp4_client_classes_null_ == MLM_FALSE) {
604 dhcp4_client_classes = std::string(dhcp4_client_classes_,
605 dhcp4_client_classes_length_);
606 }
607
608 // Set DHCPv6 client classes if non NULL value returned.
609 std::string dhcp6_client_classes;
610 if (dhcp6_client_classes_null_ == MLM_FALSE) {
611 dhcp6_client_classes = std::string(dhcp6_client_classes_,
612 dhcp6_client_classes_length_);
613 }
614
615 // Convert user_context to string as well.
616 std::string user_context;
617 if (user_context_null_ == MLM_FALSE) {
618 user_context_[user_context_length_] = '\0';
619 user_context.assign(user_context_);
620 }
621
622 // Set next server value (siaddr) if non NULL value returned.
624 if (dhcp4_next_server_null_ == MLM_FALSE) {
625 next_server = asiolink::IOAddress(dhcp4_next_server_);
626 }
627
628 // Set server hostname (sname) if non NULL value returned.
629 std::string dhcp4_server_hostname;
630 if (dhcp4_server_hostname_null_ == MLM_FALSE) {
631 dhcp4_server_hostname = std::string(dhcp4_server_hostname_,
632 dhcp4_server_hostname_length_);
633 }
634
635 // Set boot file name (file) if non NULL value returned.
636 std::string dhcp4_boot_file_name;
637 if (dhcp4_boot_file_name_null_ == MLM_FALSE) {
638 dhcp4_boot_file_name = std::string(dhcp4_boot_file_name_,
639 dhcp4_boot_file_name_length_);
640 }
641
642 // Set the auth key if a non empty array is retrieved
643 std::string auth_key;
644 if (auth_key_null_ == MLM_FALSE) {
645 auth_key = std::string(auth_key_, auth_key_length_);
646 }
647
648 // Create and return Host object from the data gathered.
649 HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
650 type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
651 hostname, dhcp4_client_classes, dhcp6_client_classes,
652 next_server, dhcp4_server_hostname,
653 dhcp4_boot_file_name, AuthKey(auth_key)));
654 h->setHostId(host_id_);
655
656 // Set the user context if there is one.
657 if (!user_context.empty()) {
658 try {
659 ConstElementPtr ctx = Element::fromJSON(user_context);
660 if (!ctx || (ctx->getType() != Element::map)) {
661 isc_throw(BadValue, "user context '" << user_context
662 << "' is not a JSON map");
663 }
664 h->setContext(ctx);
665 } catch (const isc::data::JSONError& ex) {
666 isc_throw(BadValue, "user context '" << user_context
667 << "' is invalid JSON: " << ex.what());
668 }
669 }
670
671 return (h);
672 };
673
687 virtual void processFetchedData(ConstHostCollection& hosts) {
688 HostPtr host;
689 // Add new host only if there are no hosts yet or the host id of the
690 // most recently added host is different than the host id of the
691 // currently processed host.
692 if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
693 // Create Host object from the fetched data and append it to the
694 // collection.
695 host = retrieveHost();
696 hosts.push_back(host);
697 }
698 }
699
710 std::string getErrorColumns() {
711 return (getColumnsInError(error_, columns_));
712 };
713
714protected:
715
717 size_t columns_num_;
718
720 std::vector<MYSQL_BIND> bind_;
721
723 std::vector<std::string> columns_;
724
726 std::vector<my_bool> error_;
727
730 HostPtr host_;
731
732private:
733
735 uint64_t host_id_;
736
739 uint8_t dhcp_identifier_buffer_[DUID::MAX_DUID_LEN];
740
742 unsigned long dhcp_identifier_length_;
743
746 uint8_t dhcp_identifier_type_;
747
749 uint32_t dhcp4_subnet_id_;
750
752 uint32_t dhcp6_subnet_id_;
753
755 uint32_t ipv4_address_;
756
758 char hostname_[HOSTNAME_MAX_LEN];
759
761 unsigned long hostname_length_;
762
764 char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
765
768 unsigned long dhcp4_client_classes_length_;
769
771 char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
772
775 unsigned long dhcp6_client_classes_length_;
776
778 char user_context_[USER_CONTEXT_MAX_LEN];
779
781 unsigned long user_context_length_;
782
784 uint32_t dhcp4_next_server_;
785
787 char dhcp4_server_hostname_[SERVER_HOSTNAME_MAX_LEN];
788
790 unsigned long dhcp4_server_hostname_length_;
791
793 char dhcp4_boot_file_name_[BOOT_FILE_NAME_MAX_LEN];
794
796 unsigned long dhcp4_boot_file_name_length_;
797
799 char auth_key_[KEY_LEN];
800
802 unsigned long auth_key_length_;
803
806
807
808 my_bool dhcp4_subnet_id_null_;
809
811 my_bool dhcp6_subnet_id_null_;
812
814 my_bool ipv4_address_null_;
815
817 my_bool hostname_null_;
818
821 my_bool dhcp4_client_classes_null_;
822
825 my_bool dhcp6_client_classes_null_;
826
828 my_bool user_context_null_;
829
831 my_bool dhcp4_next_server_null_;
832
834 my_bool dhcp4_server_hostname_null_;
835
837 my_bool dhcp4_boot_file_name_null_;
838
840 my_bool auth_key_null_;
841
843};
844
854class MySqlHostWithOptionsExchange : public MySqlHostExchange {
855private:
856
858 static const size_t OPTION_COLUMNS = 7;
859
874 class OptionProcessor {
875 public:
876
883 OptionProcessor(const Option::Universe& universe,
884 const size_t start_column)
885 : universe_(universe), start_column_(start_column), option_id_(0),
886 code_(0), value_length_(0), formatted_value_length_(0),
887 space_length_(0), persistent_(false), user_context_length_(0),
888 option_id_null_(MLM_FALSE), code_null_(MLM_FALSE),
889 value_null_(MLM_FALSE), formatted_value_null_(MLM_FALSE),
890 space_null_(MLM_FALSE), user_context_null_(MLM_FALSE),
891 option_id_index_(start_column), code_index_(start_column_ + 1),
892 value_index_(start_column_ + 2),
893 formatted_value_index_(start_column_ + 3),
894 space_index_(start_column_ + 4),
895 persistent_index_(start_column_ + 5),
896 user_context_index_(start_column_ + 6),
897 most_recent_option_id_(0) {
898
899 memset(value_, 0, sizeof(value_));
900 memset(formatted_value_, 0, sizeof(formatted_value_));
901 memset(space_, 0, sizeof(space_));
902 memset(user_context_, 0, sizeof(user_context_));
903 }
904
906 uint64_t getOptionId() const {
907 if (option_id_null_ == MLM_FALSE) {
908 return (option_id_);
909 }
910 return (0);
911 }
912
925 void retrieveOption(const CfgOptionPtr& cfg) {
926 // option_id may be NULL if dhcp4_options or dhcp6_options table
927 // doesn't contain any options for the particular host. Also, the
928 // current option id must be greater than id if the most recent
929 // option because options are ordered by option id. Otherwise
930 // we assume that this is already processed option.
931 if ((option_id_null_ == MLM_TRUE) ||
932 (most_recent_option_id_ >= option_id_)) {
933 return;
934 }
935
936 // Remember current option id as the most recent processed one. We
937 // will be comparing it with option ids in subsequent rows.
938 most_recent_option_id_ = option_id_;
939
940 // Convert it to string object for easier comparison.
941 std::string space;
942 if (space_null_ == MLM_FALSE) {
943 // Typically, the string values returned by the database are not
944 // NULL terminated.
945 space_[space_length_] = '\0';
946 space.assign(space_);
947 }
948
949 // If empty or null space provided, use a default top level space.
950 if (space.empty()) {
951 space = (universe_ == Option::V4 ? "dhcp4" : "dhcp6");
952 }
953
954 // Convert formatted_value to string as well.
955 std::string formatted_value;
956 if (formatted_value_null_ == MLM_FALSE) {
957 formatted_value_[formatted_value_length_] = '\0';
958 formatted_value.assign(formatted_value_);
959 }
960
961 // Convert user_context to string as well.
962 std::string user_context;
963 if (user_context_null_ == MLM_FALSE) {
964 user_context_[user_context_length_] = '\0';
965 user_context.assign(user_context_);
966 }
967
968 // Options are held in a binary or textual format in the database.
969 // This is similar to having an option specified in a server
970 // configuration file. Such option is converted to appropriate C++
971 // class, using option definition. Thus, we need to find the
972 // option definition for this option code and option space.
973
974 // Check if this is a standard option.
975 OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
976
977 // Otherwise, we may check if this an option encapsulated within the
978 // vendor space.
979 if (!def && (space != DHCP4_OPTION_SPACE) &&
980 (space != DHCP6_OPTION_SPACE)) {
981 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
982 if (vendor_id > 0) {
983 def = LibDHCP::getVendorOptionDef(universe_, vendor_id, code_);
984 }
985 }
986
987 // In all other cases, we use runtime option definitions, which
988 // should be also registered within the libdhcp++.
989 if (!def) {
990 def = LibDHCP::getRuntimeOptionDef(space, code_);
991 }
992
993 OptionPtr option;
994
995 if (!def) {
996 // If no definition found, we use generic option type.
997 OptionBuffer buf(value_, value_ + value_length_);
998 option.reset(new Option(universe_, code_, buf.begin(),
999 buf.end()));
1000 } else {
1001 // The option value may be specified in textual or binary format
1002 // in the database. If formatted_value is empty, the binary
1003 // format is used. Depending on the format we use a different
1004 // variant of the optionFactory function.
1005 if (formatted_value.empty()) {
1006 OptionBuffer buf(value_, value_ + value_length_);
1007 option = def->optionFactory(universe_, code_, buf.begin(),
1008 buf.end());
1009 } else {
1010 // Spit the value specified in comma separated values
1011 // format.
1012 std::vector<std::string> split_vec;
1013 boost::split(split_vec, formatted_value, boost::is_any_of(","));
1014 option = def->optionFactory(universe_, code_, split_vec);
1015 }
1016 }
1017
1018 OptionDescriptor desc(option, persistent_, formatted_value);
1019
1020 // Set the user context if there is one into the option descriptor.
1021 if (!user_context.empty()) {
1022 try {
1023 ConstElementPtr ctx = Element::fromJSON(user_context);
1024 if (!ctx || (ctx->getType() != Element::map)) {
1025 isc_throw(BadValue, "user context '" << user_context
1026 << "' is no a JSON map");
1027 }
1028 desc.setContext(ctx);
1029 } catch (const isc::data::JSONError& ex) {
1030 isc_throw(BadValue, "user context '" << user_context
1031 << "' is invalid JSON: " << ex.what());
1032 }
1033 }
1034
1035 cfg->add(desc, space);
1036 }
1037
1042 void setColumnNames(std::vector<std::string>& columns) {
1043 columns[option_id_index_] = "option_id";
1044 columns[code_index_] = "code";
1045 columns[value_index_] = "value";
1046 columns[formatted_value_index_] = "formatted_value";
1047 columns[space_index_] = "space";
1048 columns[persistent_index_] = "persistent";
1049 columns[user_context_index_] = "user_context";
1050 }
1051
1057 void setBindFields(std::vector<MYSQL_BIND>& bind) {
1058 // This method is called just before making a new query, so we
1059 // reset the most_recent_option_id_ to start over with options
1060 // processing.
1061 most_recent_option_id_ = 0;
1062
1063 // option_id : INT UNSIGNED NOT NULL AUTO_INCREMENT,
1064 bind[option_id_index_].buffer_type = MYSQL_TYPE_LONG;
1065 bind[option_id_index_].buffer = reinterpret_cast<char*>(&option_id_);
1066 bind[option_id_index_].is_unsigned = MLM_TRUE;
1067
1068 // code : TINYINT OR SHORT UNSIGNED NOT NULL
1069 bind[code_index_].buffer_type = MYSQL_TYPE_SHORT;
1070 bind[code_index_].buffer = reinterpret_cast<char*>(&code_);
1071 bind[code_index_].is_unsigned = MLM_TRUE;
1072 bind[code_index_].is_null = &code_null_;
1073
1074 // value : BLOB NULL
1075 value_length_ = sizeof(value_);
1076 bind[value_index_].buffer_type = MYSQL_TYPE_BLOB;
1077 bind[value_index_].buffer = reinterpret_cast<char*>(value_);
1078 bind[value_index_].buffer_length = value_length_;
1079 bind[value_index_].length = &value_length_;
1080 bind[value_index_].is_null = &value_null_;
1081
1082 // formatted_value : TEXT NULL
1083 formatted_value_length_ = sizeof(formatted_value_);
1084 bind[formatted_value_index_].buffer_type = MYSQL_TYPE_STRING;
1085 bind[formatted_value_index_].buffer = reinterpret_cast<char*>(formatted_value_);
1086 bind[formatted_value_index_].buffer_length = formatted_value_length_;
1087 bind[formatted_value_index_].length = &formatted_value_length_;
1088 bind[formatted_value_index_].is_null = &formatted_value_null_;
1089
1090 // space : VARCHAR(128) NULL
1091 space_length_ = sizeof(space_);
1092 bind[space_index_].buffer_type = MYSQL_TYPE_STRING;
1093 bind[space_index_].buffer = reinterpret_cast<char*>(space_);
1094 bind[space_index_].buffer_length = space_length_;
1095 bind[space_index_].length = &space_length_;
1096 bind[space_index_].is_null = &space_null_;
1097
1098 // persistent : TINYINT(1) NOT NULL DEFAULT 0
1099 bind[persistent_index_].buffer_type = MYSQL_TYPE_TINY;
1100 bind[persistent_index_].buffer = reinterpret_cast<char*>(&persistent_);
1101 bind[persistent_index_].is_unsigned = MLM_TRUE;
1102
1103 // user_context : TEXT NULL
1104 user_context_length_ = sizeof(user_context_);
1105 bind[user_context_index_].buffer_type = MYSQL_TYPE_STRING;
1106 bind[user_context_index_].buffer = reinterpret_cast<char*>(user_context_);
1107 bind[user_context_index_].buffer_length = user_context_length_;
1108 bind[user_context_index_].length = &user_context_length_;
1109 bind[user_context_index_].is_null = &user_context_null_;
1110 }
1111
1112 private:
1113
1115 Option::Universe universe_;
1116
1118 size_t start_column_;
1119
1121 uint32_t option_id_;
1122
1124 uint16_t code_;
1125
1127 uint8_t value_[OPTION_VALUE_MAX_LEN];
1128
1130 unsigned long value_length_;
1131
1133 char formatted_value_[OPTION_FORMATTED_VALUE_MAX_LEN];
1134
1136 unsigned long formatted_value_length_;
1137
1139 char space_[OPTION_SPACE_MAX_LEN];
1140
1142 unsigned long space_length_;
1143
1146 bool persistent_;
1147
1149 char user_context_[USER_CONTEXT_MAX_LEN];
1150
1152 unsigned long user_context_length_;
1153
1156
1157
1158 my_bool option_id_null_;
1159
1161 my_bool code_null_;
1162
1164 my_bool value_null_;
1165
1168 my_bool formatted_value_null_;
1169
1171 my_bool space_null_;
1172
1174 my_bool user_context_null_;
1176
1178
1179
1180 size_t option_id_index_;
1181
1183 size_t code_index_;
1184
1186 size_t value_index_;
1187
1189 size_t formatted_value_index_;
1190
1192 size_t space_index_;
1193
1195 size_t persistent_index_;
1197
1199 size_t user_context_index_;
1200
1202 uint32_t most_recent_option_id_;
1203 };
1204
1206 typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
1207
1208public:
1209
1216 enum FetchedOptions {
1217 DHCP4_ONLY,
1218 DHCP6_ONLY,
1219 DHCP4_AND_DHCP6
1220 };
1221
1230 MySqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
1231 const size_t additional_columns_num = 0)
1232 : MySqlHostExchange(getRequiredColumnsNum(fetched_options)
1233 + additional_columns_num),
1234 opt_proc4_(), opt_proc6_() {
1235
1236 // Create option processor for DHCPv4 options, if required.
1237 if ((fetched_options == DHCP4_ONLY) ||
1238 (fetched_options == DHCP4_AND_DHCP6)) {
1239 opt_proc4_.reset(new OptionProcessor(Option::V4,
1240 findAvailColumn()));
1241 opt_proc4_->setColumnNames(columns_);
1242 }
1243
1244 // Create option processor for DHCPv6 options, if required.
1245 if ((fetched_options == DHCP6_ONLY) ||
1246 (fetched_options == DHCP4_AND_DHCP6)) {
1247 opt_proc6_.reset(new OptionProcessor(Option::V6,
1248 findAvailColumn()));
1249 opt_proc6_->setColumnNames(columns_);
1250 }
1251 }
1252
1261 virtual void processFetchedData(ConstHostCollection& hosts) {
1262 // Holds pointer to the previously parsed host.
1263 HostPtr most_recent_host;
1264 if (!hosts.empty()) {
1265 // Const cast is not very elegant way to deal with it, but
1266 // there is a good reason to use it here. This method is called
1267 // to build a collection of const hosts to be returned to the
1268 // caller. If we wanted to use non-const collection we'd need
1269 // to copy the whole collection before returning it, which has
1270 // performance implications. Alternatively, we could store the
1271 // most recently added host in a class member but this would
1272 // make the code less readable.
1273 most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
1274 }
1275
1276 // If no host has been parsed yet or we're at the row holding next
1277 // host, we create a new host object and put it at the end of the
1278 // list.
1279 if (!most_recent_host || (most_recent_host->getHostId() < getHostId())) {
1280 HostPtr host = retrieveHost();
1281 hosts.push_back(host);
1282 most_recent_host = host;
1283 }
1284
1285 // Parse DHCPv4 options if required to do so.
1286 if (opt_proc4_) {
1287 CfgOptionPtr cfg = most_recent_host->getCfgOption4();
1288 opt_proc4_->retrieveOption(cfg);
1289 }
1290
1291 // Parse DHCPv6 options if required to do so.
1292 if (opt_proc6_) {
1293 CfgOptionPtr cfg = most_recent_host->getCfgOption6();
1294 opt_proc6_->retrieveOption(cfg);
1295 }
1296 }
1297
1301 virtual std::vector<MYSQL_BIND> createBindForReceive() {
1302 // The following call sets bind_ values between 0 and 8.
1303 static_cast<void>(MySqlHostExchange::createBindForReceive());
1304
1305 // Bind variables for DHCPv4 options.
1306 if (opt_proc4_) {
1307 opt_proc4_->setBindFields(bind_);
1308 }
1309
1310 // Bind variables for DHCPv6 options.
1311 if (opt_proc6_) {
1312 opt_proc6_->setBindFields(bind_);
1313 }
1314
1315 // Add the error flags
1316 setErrorIndicators(bind_, error_);
1317
1318 return (bind_);
1319 };
1320
1321private:
1322
1334 static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
1335 return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
1336 OPTION_COLUMNS);
1337 }
1338
1342 OptionProcessorPtr opt_proc4_;
1343
1347 OptionProcessorPtr opt_proc6_;
1348};
1349
1362class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange {
1363private:
1364
1366 static const size_t RESERVATION_COLUMNS = 5;
1367
1368public:
1369
1374 MySqlHostIPv6Exchange(const FetchedOptions& fetched_options)
1375 : MySqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
1376 reservation_id_(0),
1377 reserv_type_(0), reserv_type_null_(MLM_FALSE),
1378 ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0),
1379 reservation_id_index_(findAvailColumn()),
1380 address_index_(reservation_id_index_ + 1),
1381 prefix_len_index_(reservation_id_index_ + 2),
1382 type_index_(reservation_id_index_ + 3),
1383 iaid_index_(reservation_id_index_ + 4),
1384 most_recent_reservation_id_(0) {
1385
1386 memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
1387
1388 // Provide names of additional columns returned by the queries.
1389 columns_[reservation_id_index_] = "reservation_id";
1390 columns_[address_index_] = "address";
1391 columns_[prefix_len_index_] = "prefix_len";
1392 columns_[type_index_] = "type";
1393 columns_[iaid_index_] = "dhcp6_iaid";
1394 }
1395
1399 uint32_t getReservationId() const {
1400 if (reserv_type_null_ == MLM_FALSE) {
1401 return (reservation_id_);
1402 }
1403 return (0);
1404 };
1405
1412 IPv6Resrv retrieveReservation() {
1413 // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
1415
1416 switch (reserv_type_) {
1417 case 0:
1418 type = IPv6Resrv::TYPE_NA;
1419 break;
1420
1421 case 2:
1422 type = IPv6Resrv::TYPE_PD;
1423 break;
1424
1425 default:
1427 "invalid IPv6 reservation type returned: "
1428 << static_cast<int>(reserv_type_)
1429 << ". Only 0 or 2 are allowed.");
1430 }
1431
1432 ipv6_address_buffer_[ipv6_address_buffer_len_] = '\0';
1433 std::string address = ipv6_address_buffer_;
1434 IPv6Resrv r(type, IOAddress(address), prefix_len_);
1435 return (r);
1436 };
1437
1457 virtual void processFetchedData(ConstHostCollection& hosts) {
1458
1459 // Call parent class to fetch host information and options.
1460 MySqlHostWithOptionsExchange::processFetchedData(hosts);
1461
1462 if (getReservationId() == 0) {
1463 return;
1464 }
1465
1466 if (hosts.empty()) {
1467 isc_throw(Unexpected, "no host information while retrieving"
1468 " IPv6 reservation");
1469 }
1470 HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1471
1472 // If we're dealing with a new reservation, let's add it to the
1473 // host.
1474 if (getReservationId() > most_recent_reservation_id_) {
1475 most_recent_reservation_id_ = getReservationId();
1476
1477 if (most_recent_reservation_id_ > 0) {
1478 host->addReservation(retrieveReservation());
1479 }
1480 }
1481 }
1482
1491 virtual std::vector<MYSQL_BIND> createBindForReceive() {
1492 // Reset most recent reservation id value because we're now making
1493 // a new SELECT query.
1494 most_recent_reservation_id_ = 0;
1495
1496 // Bind values supported by parent classes.
1497 static_cast<void>(MySqlHostWithOptionsExchange::createBindForReceive());
1498
1499 // reservation_id : INT UNSIGNED NOT NULL AUTO_INCREMENT
1500 bind_[reservation_id_index_].buffer_type = MYSQL_TYPE_LONG;
1501 bind_[reservation_id_index_].buffer = reinterpret_cast<char*>(&reservation_id_);
1502 bind_[reservation_id_index_].is_unsigned = MLM_TRUE;
1503
1504 // IPv6 address/prefix VARCHAR(39)
1505 ipv6_address_buffer_len_ = sizeof(ipv6_address_buffer_) - 1;
1506 bind_[address_index_].buffer_type = MYSQL_TYPE_STRING;
1507 bind_[address_index_].buffer = ipv6_address_buffer_;
1508 bind_[address_index_].buffer_length = ipv6_address_buffer_len_;
1509 bind_[address_index_].length = &ipv6_address_buffer_len_;
1510
1511 // prefix_len : TINYINT
1512 bind_[prefix_len_index_].buffer_type = MYSQL_TYPE_TINY;
1513 bind_[prefix_len_index_].buffer = reinterpret_cast<char*>(&prefix_len_);
1514 bind_[prefix_len_index_].is_unsigned = MLM_TRUE;
1515
1516 // (reservation) type : TINYINT
1517 reserv_type_null_ = MLM_FALSE;
1518 bind_[type_index_].buffer_type = MYSQL_TYPE_TINY;
1519 bind_[type_index_].buffer = reinterpret_cast<char*>(&reserv_type_);
1520 bind_[type_index_].is_unsigned = MLM_TRUE;
1521 bind_[type_index_].is_null = &reserv_type_null_;
1522
1523 // dhcp6_iaid INT UNSIGNED
1524 bind_[iaid_index_].buffer_type = MYSQL_TYPE_LONG;
1525 bind_[iaid_index_].buffer = reinterpret_cast<char*>(&iaid_);
1526 bind_[iaid_index_].is_unsigned = MLM_TRUE;
1527
1528 // Add the error flags
1529 setErrorIndicators(bind_, error_);
1530
1531 return (bind_);
1532 };
1533
1534private:
1535
1537 uint32_t reservation_id_;
1538
1540 uint8_t reserv_type_;
1541
1546 my_bool reserv_type_null_;
1547
1549 char ipv6_address_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1550
1552 unsigned long ipv6_address_buffer_len_;
1553
1555 uint8_t prefix_len_;
1556
1558 uint32_t iaid_;
1559
1561
1562
1563 size_t reservation_id_index_;
1564
1566 size_t address_index_;
1567
1569 size_t prefix_len_index_;
1570
1572 size_t type_index_;
1573
1575 size_t iaid_index_;
1576
1578
1580 uint32_t most_recent_reservation_id_;
1581};
1582
1593class MySqlIPv6ReservationExchange {
1594private:
1595
1597 static const size_t RESRV_COLUMNS = 6;
1598
1599public:
1600
1604 MySqlIPv6ReservationExchange()
1605 : host_id_(0), address_("::"), address_len_(0), prefix_len_(0), type_(0),
1606 iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1607
1608 // Reset error table.
1609 std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
1610
1611 // Set the column names (for error messages)
1612 columns_[0] = "host_id";
1613 columns_[1] = "address";
1614 columns_[2] = "prefix_len";
1615 columns_[3] = "type";
1616 columns_[4] = "dhcp6_iaid";
1617 BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
1618 }
1619
1632 std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
1633 const HostID& id) {
1634
1635 // Store the values to ensure they remain valid.
1636 resv_ = resv;
1637 host_id_ = id;
1638
1639 // Initialize prior to constructing the array of MYSQL_BIND structures.
1640 // It sets all fields, including is_null, to zero, so we need to set
1641 // is_null only if it should be true. This gives up minor performance
1642 // benefit while being safe approach. For improved readability, the
1643 // code that explicitly sets is_null is there, but is commented out.
1644 memset(bind_, 0, sizeof(bind_));
1645
1646 // Set up the structures for the various components of the host structure.
1647
1648 try {
1649 // address VARCHAR(39)
1650 address_ = resv.getPrefix().toText();
1651 address_len_ = address_.length();
1652 bind_[0].buffer_type = MYSQL_TYPE_BLOB;
1653 bind_[0].buffer = reinterpret_cast<char*>
1654 (const_cast<char*>(address_.c_str()));
1655 bind_[0].buffer_length = address_len_;
1656 bind_[0].length = &address_len_;
1657
1658 // prefix_len tinyint
1659 prefix_len_ = resv.getPrefixLen();
1660 bind_[1].buffer_type = MYSQL_TYPE_TINY;
1661 bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
1662 bind_[1].is_unsigned = MLM_TRUE;
1663
1664 // type tinyint
1665 // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
1666 type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1667 bind_[2].buffer_type = MYSQL_TYPE_TINY;
1668 bind_[2].buffer = reinterpret_cast<char*>(&type_);
1669 bind_[2].is_unsigned = MLM_TRUE;
1670
1671 // dhcp6_iaid INT UNSIGNED
1673 iaid_ = 0;
1674 bind_[3].buffer_type = MYSQL_TYPE_LONG;
1675 bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
1676 bind_[3].is_unsigned = MLM_TRUE;
1677
1678 // host_id INT UNSIGNED NOT NULL
1679 bind_[4].buffer_type = MYSQL_TYPE_LONG;
1680 bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
1681 bind_[4].is_unsigned = MLM_TRUE;
1682
1683 } catch (const std::exception& ex) {
1685 "Could not create bind array from IPv6 Reservation: "
1686 << resv_.toText() << ", reason: " << ex.what());
1687 }
1688
1689 // Add the data to the vector. Note the end element is one after the
1690 // end of the array.
1691 // RESRV_COLUMNS -1 as we do not set reservation_id.
1692 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[RESRV_COLUMNS-1]));
1693 }
1694
1695private:
1696
1698 uint64_t host_id_;
1699
1701 std::string address_;
1702
1704 unsigned long address_len_;
1705
1707 uint8_t prefix_len_;
1708
1710 uint8_t type_;
1711
1713 uint8_t iaid_;
1714
1716 IPv6Resrv resv_;
1717
1719 MYSQL_BIND bind_[RESRV_COLUMNS];
1720
1722 std::string columns_[RESRV_COLUMNS];
1723
1726 my_bool error_[RESRV_COLUMNS];
1727};
1728
1732class MySqlOptionExchange {
1733private:
1734
1736 static const size_t OPTION_COLUMNS = 10;
1737
1738public:
1739
1741 MySqlOptionExchange()
1742
1743 : type_(0), value_len_(0), formatted_value_len_(0), space_(),
1744 space_len_(0), persistent_(false), user_context_(),
1745 user_context_len_(0), client_class_(), client_class_len_(0),
1746 subnet_id_(SUBNET_ID_UNUSED), host_id_(0), option_() {
1747
1748 BOOST_STATIC_ASSERT(9 < OPTION_COLUMNS);
1749 }
1750
1754 std::vector<MYSQL_BIND>
1755 createBindForSend(const OptionDescriptor& opt_desc,
1756 const std::string& opt_space,
1757 const OptionalValue<SubnetID>& subnet_id,
1758 const HostID& host_id) {
1759
1760 // Hold pointer to the option to make sure it remains valid until
1761 // we complete a query.
1762 option_ = opt_desc.option_;
1763
1764 memset(bind_, 0, sizeof(bind_));
1765
1766 try {
1767 // option_id: INT UNSIGNED NOT NULL
1768 // The option_id is auto_incremented, so we need to pass the NULL
1769 // value.
1770 bind_[0].buffer_type = MYSQL_TYPE_NULL;
1771
1772 // code: SMALLINT UNSIGNED NOT NULL
1773 type_ = option_->getType();
1774 bind_[1].buffer_type = MYSQL_TYPE_SHORT;
1775 bind_[1].buffer = reinterpret_cast<char*>(&type_);
1776 bind_[1].is_unsigned = MLM_TRUE;
1777
1778 // value: BLOB NULL
1779 if (opt_desc.formatted_value_.empty() &&
1780 (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1781 // The formatted_value is empty and the option value is
1782 // non-empty so we need to prepare on-wire format for the
1783 // option and store it in the database as a blob.
1784 OutputBuffer buf(opt_desc.option_->len());
1785 opt_desc.option_->pack(buf);
1786 const char* buf_ptr = static_cast<const char*>(buf.getData());
1787 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1788 buf_ptr + buf.getLength());
1789 value_len_ = value_.size();
1790 bind_[2].buffer_type = MYSQL_TYPE_BLOB;
1791 bind_[2].buffer = &value_[0];
1792 bind_[2].buffer_length = value_len_;
1793 bind_[2].length = &value_len_;
1794
1795 } else {
1796 // No value or formatted_value specified. In this case, the
1797 // value blob is NULL.
1798 value_.clear();
1799 bind_[2].buffer_type = MYSQL_TYPE_NULL;
1800 }
1801
1802 // formatted_value: TEXT NULL,
1803 if (!opt_desc.formatted_value_.empty()) {
1804 formatted_value_len_ = opt_desc.formatted_value_.size();
1805 bind_[3].buffer_type = MYSQL_TYPE_STRING;
1806 bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
1807 bind_[3].buffer_length = formatted_value_len_;
1808 bind_[3].length = &formatted_value_len_;
1809
1810 } else {
1811 bind_[3].buffer_type = MYSQL_TYPE_NULL;
1812 }
1813
1814 // space: VARCHAR(128) NULL
1815 space_ = opt_space;
1816 space_len_ = space_.size();
1817 bind_[4].buffer_type = MYSQL_TYPE_STRING;
1818 bind_[4].buffer = const_cast<char*>(space_.c_str());
1819 bind_[4].buffer_length = space_len_;
1820 bind_[4].length = &space_len_;
1821
1822 // persistent: TINYINT(1) NOT NULL DEFAULT 0
1823 persistent_ = opt_desc.persistent_;
1824 bind_[5].buffer_type = MYSQL_TYPE_TINY;
1825 bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
1826 bind_[5].is_unsigned = MLM_TRUE;
1827
1828 // user_context: TEST NULL,
1829 ConstElementPtr ctx = opt_desc.getContext();
1830 if (ctx) {
1831 user_context_ = ctx->str();
1832 user_context_len_ = user_context_.size();
1833 bind_[6].buffer_type = MYSQL_TYPE_STRING;
1834 bind_[6].buffer = const_cast<char*>(user_context_.c_str());
1835 bind_[6].buffer_length = user_context_len_;
1836 bind_[6].length = &user_context_len_;
1837 } else {
1838 bind_[6].buffer_type = MYSQL_TYPE_NULL;
1839 }
1840
1841 // dhcp_client_class: VARCHAR(128) NULL
1842 client_class_len_ = client_class_.size();
1843 bind_[7].buffer_type = MYSQL_TYPE_STRING;
1844 bind_[7].buffer = const_cast<char*>(client_class_.c_str());
1845 bind_[7].buffer_length = client_class_len_;
1846 bind_[7].length = &client_class_len_;
1847
1848 // dhcp4_subnet_id: INT UNSIGNED NULL
1849 if (subnet_id.isSpecified()) {
1850 subnet_id_ = subnet_id;
1851 bind_[8].buffer_type = MYSQL_TYPE_LONG;
1852 bind_[8].buffer = reinterpret_cast<char*>(subnet_id_);
1853 bind_[8].is_unsigned = MLM_TRUE;
1854
1855 } else {
1856 bind_[8].buffer_type = MYSQL_TYPE_NULL;
1857 }
1858
1859 // host_id: INT UNSIGNED NOT NULL
1860 host_id_ = host_id;
1861 bind_[9].buffer_type = MYSQL_TYPE_LONG;
1862 bind_[9].buffer = reinterpret_cast<char*>(&host_id_);
1863 bind_[9].is_unsigned = MLM_TRUE;
1864
1865 } catch (const std::exception& ex) {
1867 "Could not create bind array for inserting DHCP "
1868 "option: " << option_->toText() << ", reason: "
1869 << ex.what());
1870 }
1871
1872 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[OPTION_COLUMNS]));
1873 }
1874
1875private:
1876
1878 uint16_t type_;
1879
1881 std::vector<uint8_t> value_;
1882
1884 unsigned long value_len_;
1885
1887 unsigned long formatted_value_len_;
1888
1890 std::string space_;
1891
1893 unsigned long space_len_;
1894
1897 bool persistent_;
1898
1900 std::string user_context_;
1901
1903 unsigned long user_context_len_;
1904
1906 std::string client_class_;
1907
1909 unsigned long client_class_len_;
1910
1912 uint32_t subnet_id_;
1913
1915 uint32_t host_id_;
1916
1918 OptionPtr option_;
1919
1921 MYSQL_BIND bind_[OPTION_COLUMNS];
1922};
1923
1924} // end of anonymous namespace
1925
1926namespace isc {
1927namespace dhcp {
1928
1931public:
1932
1940 GET_HOST_DHCPID, // Gets hosts by host identifier
1941 GET_HOST_ADDR, // Gets hosts by IPv4 address
1942 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
1943 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
1944 GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
1945 GET_HOST_PREFIX, // Gets host by IPv6 prefix
1946 GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
1947 INSERT_HOST, // Insert new host to collection
1948 INSERT_V6_RESRV, // Insert v6 reservation
1949 INSERT_V4_OPTION, // Insert DHCPv4 option
1950 INSERT_V6_OPTION, // Insert DHCPv6 option
1951 DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
1952 DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
1953 DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
1954 NUM_STATEMENTS // Number of statements
1956
1963
1969
1972
1979 //
1986 std::pair<uint32_t, uint32_t> getVersion() const;
1987
1996 std::vector<MYSQL_BIND>& bind);
1997
2004 bool
2005 delStatement(StatementIndex stindex, MYSQL_BIND* bind);
2006
2011 void addResv(const IPv6Resrv& resv, const HostID& id);
2012
2022 const OptionDescriptor& opt_desc,
2023 const std::string& opt_space,
2024 const OptionalValue<SubnetID>& subnet_id,
2025 const HostID& host_id);
2026
2033 void addOptions(const StatementIndex& stindex, const ConstCfgOptionPtr& options_cfg,
2034 const uint64_t host_id);
2035
2046 void checkError(const int status, const StatementIndex index,
2047 const char* what) const;
2048
2066 void getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
2067 boost::shared_ptr<MySqlHostExchange> exchange,
2068 ConstHostCollection& result, bool single) const;
2069
2086 ConstHostPtr getHost(const SubnetID& subnet_id,
2087 const Host::IdentifierType& identifier_type,
2088 const uint8_t* identifier_begin,
2089 const size_t identifier_len,
2090 StatementIndex stindex,
2091 boost::shared_ptr<MySqlHostExchange> exchange) const;
2092
2100 void checkReadOnly() const;
2101
2104 boost::shared_ptr<MySqlHostWithOptionsExchange> host_exchange_;
2105
2108 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
2109
2113 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv46_exchange_;
2114
2117 boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
2118
2122 boost::shared_ptr<MySqlOptionExchange> host_option_exchange_;
2123
2126
2129};
2130
2132typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
2134
2138 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2139 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2140 // to retrieve information from 4 different tables using a single query.
2141 // Hence, this query returns multiple rows for a single host.
2143 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2144 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2145 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2146 "h.user_context, "
2147 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2148 "h.dhcp4_boot_file_name, h.auth_key, "
2149 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2150 "o4.persistent, o4.user_context, "
2151 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2152 "o6.persistent, o6.user_context, "
2153 "r.reservation_id, r.address, r.prefix_len, r.type, "
2154 "r.dhcp6_iaid "
2155 "FROM hosts AS h "
2156 "LEFT JOIN dhcp4_options AS o4 "
2157 "ON h.host_id = o4.host_id "
2158 "LEFT JOIN dhcp6_options AS o6 "
2159 "ON h.host_id = o6.host_id "
2160 "LEFT JOIN ipv6_reservations AS r "
2161 "ON h.host_id = r.host_id "
2162 "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ? "
2163 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2164
2165 // Retrieves host information along with the DHCPv4 options associated with
2166 // it. Left joining the dhcp4_options table results in multiple rows being
2167 // returned for the same host. The host is retrieved by IPv4 address.
2169 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2170 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2171 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2172 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2173 "h.dhcp4_boot_file_name, h.auth_key, "
2174 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2175 "o.persistent, o.user_context "
2176 "FROM hosts AS h "
2177 "LEFT JOIN dhcp4_options AS o "
2178 "ON h.host_id = o.host_id "
2179 "WHERE ipv4_address = ? "
2180 "ORDER BY h.host_id, o.option_id"},
2181
2182 // Retrieves host information and DHCPv4 options using subnet identifier
2183 // and client's identifier. Left joining the dhcp4_options table results in
2184 // multiple rows being returned for the same host.
2186 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2187 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2188 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2189 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2190 "h.dhcp4_boot_file_name, h.auth_key, "
2191 ""
2192 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2193 "o.persistent, o.user_context "
2194 "FROM hosts AS h "
2195 "LEFT JOIN dhcp4_options AS o "
2196 "ON h.host_id = o.host_id "
2197 "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? "
2198 " AND h.dhcp_identifier = ? "
2199 "ORDER BY h.host_id, o.option_id"},
2200
2201 // Retrieves host information, IPv6 reservations and DHCPv6 options
2202 // associated with a host. The number of rows returned is a multiplication
2203 // of number of IPv6 reservations and DHCPv6 options.
2205 "SELECT h.host_id, h.dhcp_identifier, "
2206 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2207 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2208 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2209 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2210 "h.dhcp4_boot_file_name, h.auth_key, "
2211 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2212 "o.persistent, o.user_context, "
2213 "r.reservation_id, r.address, r.prefix_len, r.type, "
2214 "r.dhcp6_iaid "
2215 "FROM hosts AS h "
2216 "LEFT JOIN dhcp6_options AS o "
2217 "ON h.host_id = o.host_id "
2218 "LEFT JOIN ipv6_reservations AS r "
2219 "ON h.host_id = r.host_id "
2220 "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? "
2221 "AND h.dhcp_identifier = ? "
2222 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2223
2224 // Retrieves host information and DHCPv4 options for the host using subnet
2225 // identifier and IPv4 reservation. Left joining the dhcp4_options table
2226 // results in multiple rows being returned for the host. The number of
2227 // rows depends on the number of options defined for the host.
2229 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2230 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2231 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2232 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2233 "h.dhcp4_boot_file_name, h.auth_key, "
2234 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2235 "o.persistent, o.user_context "
2236 "FROM hosts AS h "
2237 "LEFT JOIN dhcp4_options AS o "
2238 "ON h.host_id = o.host_id "
2239 "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? "
2240 "ORDER BY h.host_id, o.option_id"},
2241
2242 // Retrieves host information, IPv6 reservations and DHCPv6 options
2243 // associated with a host using prefix and prefix length. This query
2244 // returns host information for a single host. However, multiple rows
2245 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2246 // The number of rows returned is multiplication of number of existing
2247 // IPv6 reservations and DHCPv6 options.
2249 "SELECT h.host_id, h.dhcp_identifier, "
2250 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2251 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2252 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2253 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2254 "h.dhcp4_boot_file_name, h.auth_key, "
2255 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2256 "o.persistent, o.user_context,"
2257 "r.reservation_id, r.address, r.prefix_len, r.type, "
2258 "r.dhcp6_iaid "
2259 "FROM hosts AS h "
2260 "LEFT JOIN dhcp6_options AS o "
2261 "ON h.host_id = o.host_id "
2262 "LEFT JOIN ipv6_reservations AS r "
2263 "ON h.host_id = r.host_id "
2264 "WHERE h.host_id = "
2265 "(SELECT host_id FROM ipv6_reservations "
2266 "WHERE address = ? AND prefix_len = ?) "
2267 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2268
2269 // Retrieves host information, IPv6 reservations and DHCPv6 options
2270 // associated with a host using subnet id and prefix. This query
2271 // returns host information for a single host. However, multiple rows
2272 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2273 // The number of rows returned is multiplication of number of existing
2274 // IPv6 reservations and DHCPv6 options.
2276 "SELECT h.host_id, h.dhcp_identifier, "
2277 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2278 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2279 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2280
2281 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2282 "h.dhcp4_boot_file_name, h.auth_key, "
2283 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2284 "o.persistent, o.user_context, "
2285 "r.reservation_id, r.address, r.prefix_len, r.type, "
2286 "r.dhcp6_iaid "
2287 "FROM hosts AS h "
2288 "LEFT JOIN dhcp6_options AS o "
2289 "ON h.host_id = o.host_id "
2290 "LEFT JOIN ipv6_reservations AS r "
2291 "ON h.host_id = r.host_id "
2292 "WHERE h.dhcp6_subnet_id = ? AND r.address = ? "
2293 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2294
2295 // Inserts a host into the 'hosts' table.
2297 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2298 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2299 "dhcp4_client_classes, dhcp6_client_classes, "
2300 "user_context, dhcp4_next_server, "
2301 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2302 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
2303
2304 // Inserts a single IPv6 reservation into 'reservations' table.
2306 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2307 "dhcp6_iaid, host_id) "
2308 "VALUES (?,?,?,?,?)"},
2309
2310 // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2311 // Using fixed scope_id = 3, which associates an option with host.
2313 "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
2314 "persistent, user_context, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id) "
2315 " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2316
2317 // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2318 // Using fixed scope_id = 3, which associates an option with host.
2320 "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
2321 "persistent, user_context, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id) "
2322 " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2323
2325 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
2326
2328 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? "
2329 "AND dhcp_identifier = ?"},
2330
2332 "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? "
2333 "AND dhcp_identifier = ?"}
2334
2335 }
2336};
2337
2340 : host_exchange_(new MySqlHostWithOptionsExchange(MySqlHostWithOptionsExchange::DHCP4_ONLY)),
2341 host_ipv6_exchange_(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP6_ONLY)),
2342 host_ipv46_exchange_(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::
2343 DHCP4_AND_DHCP6)),
2344 host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
2345 host_option_exchange_(new MySqlOptionExchange()),
2346 conn_(parameters),
2347 is_readonly_(false) {
2348
2349 // Open the database.
2351
2352 // Test schema version before we try to prepare statements.
2353 std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
2355 std::pair<uint32_t, uint32_t> db_version = getVersion();
2356 if (code_version != db_version) {
2357 isc_throw(DbOpenError, "MySQL schema version mismatch: need version: "
2358 << code_version.first << "." << code_version.second
2359 << " found version: " << db_version.first << "."
2360 << db_version.second);
2361 }
2362
2363 // Enable autocommit. In case transaction is explicitly used, this
2364 // setting will be overwritten for the transaction. However, there are
2365 // cases when lack of autocommit could cause transactions to hang
2366 // until commit or rollback is explicitly called. This already
2367 // caused issues for some unit tests which were unable to cleanup
2368 // the database after the test because of pending transactions.
2369 // Use of autocommit will eliminate this problem.
2370 my_bool result = mysql_autocommit(conn_.mysql_, 1);
2371 if (result != 0) {
2372 isc_throw(DbOperationError, mysql_error(conn_.mysql_));
2373 }
2374
2375 // Prepare query statements. Those are will be only used to retrieve
2376 // information from the database, so they can be used even if the
2377 // database is read only for the current user.
2380
2381 // Check if the backend is explicitly configured to operate with
2382 // read only access to the database.
2384
2385 // If we are using read-write mode for the database we also prepare
2386 // statements for INSERTS etc.
2387 if (!is_readonly_) {
2388 // Prepare statements for writing to the database, e.g. INSERT.
2390 tagged_statements.end());
2391 } else {
2392 LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_HOST_DB_READONLY);
2393 }
2394}
2395
2397 // Free up the prepared statements, ignoring errors. (What would we do
2398 // about them? We're destroying this object and are not really concerned
2399 // with errors on a database connection that is about to go away.)
2400 for (int i = 0; i < conn_.statements_.size(); ++i) {
2401 if (conn_.statements_[i] != NULL) {
2402 (void) mysql_stmt_close(conn_.statements_[i]);
2403 conn_.statements_[i] = NULL;
2404 }
2405 }
2406
2407 // There is no need to close the database in this destructor: it is
2408 // closed in the destructor of the mysql_ member variable.
2409}
2410
2411std::pair<uint32_t, uint32_t>
2414 DHCPSRV_MYSQL_HOST_DB_GET_VERSION);
2415
2416 // Allocate a new statement.
2417 MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
2418 if (stmt == NULL) {
2419 isc_throw(DbOperationError, "unable to allocate MySQL prepared "
2420 "statement structure, reason: " << mysql_error(conn_.mysql_));
2421 }
2422
2423 // Prepare the statement from SQL text.
2424 const char* version_sql = "SELECT version, minor FROM schema_version";
2425 int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
2426 if (status != 0) {
2427 isc_throw(DbOperationError, "unable to prepare MySQL statement <"
2428 << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2429 }
2430
2431 // Execute the prepared statement.
2432 if (mysql_stmt_execute(stmt) != 0) {
2433 isc_throw(DbOperationError, "cannot execute schema version query <"
2434 << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2435 }
2436
2437 // Bind the output of the statement to the appropriate variables.
2438 MYSQL_BIND bind[2];
2439 memset(bind, 0, sizeof(bind));
2440
2441 uint32_t major;
2442 bind[0].buffer_type = MYSQL_TYPE_LONG;
2443 bind[0].is_unsigned = 1;
2444 bind[0].buffer = &major;
2445 bind[0].buffer_length = sizeof(major);
2446
2447 uint32_t minor;
2448 bind[1].buffer_type = MYSQL_TYPE_LONG;
2449 bind[1].is_unsigned = 1;
2450 bind[1].buffer = &minor;
2451 bind[1].buffer_length = sizeof(minor);
2452
2453 if (mysql_stmt_bind_result(stmt, bind)) {
2454 isc_throw(DbOperationError, "unable to bind result set for <"
2455 << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2456 }
2457
2458 // Fetch the data.
2459 if (mysql_stmt_fetch(stmt)) {
2460 mysql_stmt_close(stmt);
2461 isc_throw(DbOperationError, "unable to bind result set for <"
2462 << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2463 }
2464
2465 // Discard the statement and its resources
2466 mysql_stmt_close(stmt);
2467
2468 return (std::make_pair(major, minor));
2469}
2470
2471
2472void
2474 std::vector<MYSQL_BIND>& bind) {
2475
2476 // Bind the parameters to the statement
2477 int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
2478 checkError(status, stindex, "unable to bind parameters");
2479
2480 // Execute the statement
2481 status = mysql_stmt_execute(conn_.statements_[stindex]);
2482
2483 if (status != 0) {
2484 // Failure: check for the special case of duplicate entry.
2485 if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
2486 isc_throw(DuplicateEntry, "Database duplicate entry error");
2487 }
2488 checkError(status, stindex, "unable to execute");
2489 }
2490}
2491
2492bool
2494 MYSQL_BIND* bind) {
2495 // Bind the parameters to the statement
2496 int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
2497 checkError(status, stindex, "unable to bind parameters");
2498
2499 // Execute the statement
2500 status = mysql_stmt_execute(conn_.statements_[stindex]);
2501
2502 if (status != 0) {
2503 checkError(status, stindex, "unable to execute");
2504 }
2505
2506 // Let's check how many hosts were deleted.
2507 my_ulonglong numrows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
2508 return (numrows != 0);
2509}
2510
2511void
2513 const HostID& id) {
2514 std::vector<MYSQL_BIND> bind =
2515 host_ipv6_reservation_exchange_->createBindForSend(resv, id);
2516
2518}
2519
2520void
2522 const OptionDescriptor& opt_desc,
2523 const std::string& opt_space,
2524 const OptionalValue<SubnetID>& subnet_id,
2525 const HostID& id) {
2526 std::vector<MYSQL_BIND> bind =
2527 host_option_exchange_->createBindForSend(opt_desc, opt_space,
2528 subnet_id, id);
2529
2530 addStatement(stindex, bind);
2531}
2532
2533void
2535 const ConstCfgOptionPtr& options_cfg,
2536 const uint64_t host_id) {
2537 // Get option space names and vendor space names and combine them within a
2538 // single list.
2539 std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
2540 std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
2541 option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
2542 vendor_spaces.end());
2543
2544 // For each option space retrieve all options and insert them into the
2545 // database.
2546 for (std::list<std::string>::const_iterator space = option_spaces.begin();
2547 space != option_spaces.end(); ++space) {
2548 OptionContainerPtr options = options_cfg->getAll(*space);
2549 if (options && !options->empty()) {
2550 for (OptionContainer::const_iterator opt = options->begin();
2551 opt != options->end(); ++opt) {
2552 addOption(stindex, *opt, *space, OptionalValue<SubnetID>(),
2553 host_id);
2554 }
2555 }
2556 }
2557}
2558
2559void
2561checkError(const int status, const StatementIndex index,
2562 const char* what) const {
2563 conn_.checkError(status, index, what);
2564}
2565
2566void
2568getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
2569 boost::shared_ptr<MySqlHostExchange> exchange,
2570 ConstHostCollection& result, bool single) const {
2571
2572 // Bind the selection parameters to the statement
2573 int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
2574 checkError(status, stindex, "unable to bind WHERE clause parameter");
2575
2576 // Set up the MYSQL_BIND array for the data being returned and bind it to
2577 // the statement.
2578 std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
2579 status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
2580 checkError(status, stindex, "unable to bind SELECT clause parameters");
2581
2582 // Execute the statement
2583 status = mysql_stmt_execute(conn_.statements_[stindex]);
2584 checkError(status, stindex, "unable to execute");
2585
2586 // Ensure that all the lease information is retrieved in one go to avoid
2587 // overhead of going back and forth between client and server.
2588 status = mysql_stmt_store_result(conn_.statements_[stindex]);
2589 checkError(status, stindex, "unable to set up for storing all results");
2590
2591 // Set up the fetch "release" object to release resources associated
2592 // with the call to mysql_stmt_fetch when this method exits, then
2593 // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
2594 // successful data fetch.
2595 MySqlFreeResult fetch_release(conn_.statements_[stindex]);
2596 while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) ==
2598 try {
2599 exchange->processFetchedData(result);
2600
2601 } catch (const isc::BadValue& ex) {
2602 // Rethrow the exception with a bit more data.
2603 isc_throw(BadValue, ex.what() << ". Statement is <" <<
2604 conn_.text_statements_[stindex] << ">");
2605 }
2606
2607 if (single && (result.size() > 1)) {
2608 isc_throw(MultipleRecords, "multiple records were found in the "
2609 "database where only one was expected for query "
2610 << conn_.text_statements_[stindex]);
2611 }
2612 }
2613
2614 // How did the fetch end?
2615 // If mysql_stmt_fetch return value is equal to 1 an error occurred.
2616 if (status == MLM_MYSQL_FETCH_FAILURE) {
2617 // Error - unable to fetch results
2618 checkError(status, stindex, "unable to fetch results");
2619
2620 } else if (status == MYSQL_DATA_TRUNCATED) {
2621 // Data truncated - throw an exception indicating what was at fault
2623 << " returned truncated data: columns affected are "
2624 << exchange->getErrorColumns());
2625 }
2626}
2627
2630getHost(const SubnetID& subnet_id,
2631 const Host::IdentifierType& identifier_type,
2632 const uint8_t* identifier_begin,
2633 const size_t identifier_len,
2634 StatementIndex stindex,
2635 boost::shared_ptr<MySqlHostExchange> exchange) const {
2636
2637 // Set up the WHERE clause value
2638 MYSQL_BIND inbind[3];
2639 memset(inbind, 0, sizeof(inbind));
2640
2641 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
2642 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2643 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
2644 inbind[0].is_unsigned = MLM_TRUE;
2645
2646 // Identifier value.
2647 std::vector<char> identifier_vec(identifier_begin,
2648 identifier_begin + identifier_len);
2649 unsigned long length = identifier_vec.size();
2650 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
2651 inbind[2].buffer = &identifier_vec[0];
2652 inbind[2].buffer_length = length;
2653 inbind[2].length = &length;
2654
2655 // Identifier type.
2656 char identifier_type_copy = static_cast<char>(identifier_type);
2657 inbind[1].buffer_type = MYSQL_TYPE_TINY;
2658 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
2659 inbind[1].is_unsigned = MLM_TRUE;
2660
2661 ConstHostCollection collection;
2662 getHostCollection(stindex, inbind, exchange, collection, true);
2663
2664 // Return single record if present, else clear the host.
2665 ConstHostPtr result;
2666 if (!collection.empty())
2667 result = *collection.begin();
2668
2669 return (result);
2670}
2671
2672void
2674 if (is_readonly_) {
2675 isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
2676 " operate in read only mode");
2677 }
2678}
2679
2682 : impl_(new MySqlHostDataSourceImpl(parameters)) {
2683}
2684
2686 delete impl_;
2687}
2688
2689void
2691 // If operating in read-only mode, throw exception.
2692 impl_->checkReadOnly();
2693
2694 // Initiate MySQL transaction as we will have to make multiple queries
2695 // to insert host information into multiple tables. If that fails on
2696 // any stage, the transaction will be rolled back by the destructor of
2697 // the MySqlTransaction class.
2698 MySqlTransaction transaction(impl_->conn_);
2699
2700 // Create the MYSQL_BIND array for the host
2701 std::vector<MYSQL_BIND> bind = impl_->host_exchange_->createBindForSend(host);
2702
2703 // ... and insert the host.
2705
2706 // Gets the last inserted hosts id
2707 uint64_t host_id = mysql_insert_id(impl_->conn_.mysql_);
2708
2709 // Insert DHCPv4 options.
2710 ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
2711 if (cfg_option4) {
2713 cfg_option4, host_id);
2714 }
2715
2716 // Insert DHCPv6 options.
2717 ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
2718 if (cfg_option6) {
2720 cfg_option6, host_id);
2721 }
2722
2723 // Insert IPv6 reservations.
2724 IPv6ResrvRange v6resv = host->getIPv6Reservations();
2725 if (std::distance(v6resv.first, v6resv.second) > 0) {
2726 for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
2727 ++resv) {
2728 impl_->addResv(resv->second, host_id);
2729 }
2730 }
2731
2732 // Everything went fine, so explicitly commit the transaction.
2733 transaction.commit();
2734}
2735
2736bool
2738 // If operating in read-only mode, throw exception.
2739 impl_->checkReadOnly();
2740
2741 if (addr.isV4()) {
2742 // Set up the WHERE clause value
2743 MYSQL_BIND inbind[2];
2744
2745 uint32_t subnet = subnet_id;
2746 memset(inbind, 0, sizeof(inbind));
2747 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2748 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
2749 inbind[0].is_unsigned = MLM_TRUE;
2750
2751 uint32_t addr4 = addr.toUint32();
2752 inbind[1].buffer_type = MYSQL_TYPE_LONG;
2753 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
2754 inbind[1].is_unsigned = MLM_TRUE;
2755
2756 ConstHostCollection collection;
2758 }
2759
2760 // v6
2761 ConstHostPtr host = get6(subnet_id, addr);
2762 if (!host) {
2763 return (false);
2764 }
2765
2766 // Ok, there is a host. Let's delete it.
2767 return del6(subnet_id, host->getIdentifierType(), &host->getIdentifier()[0],
2768 host->getIdentifier().size());
2769}
2770
2771bool
2773 const Host::IdentifierType& identifier_type,
2774 const uint8_t* identifier_begin, const size_t identifier_len) {
2775 // If operating in read-only mode, throw exception.
2776 impl_->checkReadOnly();
2777
2778 // Set up the WHERE clause value
2779 MYSQL_BIND inbind[3];
2780
2781 // subnet-id
2782 memset(inbind, 0, sizeof(inbind));
2783 uint32_t subnet = static_cast<uint32_t>(subnet_id);
2784 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2785 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
2786 inbind[0].is_unsigned = MLM_TRUE;
2787
2788 // identifier type
2789 char identifier_type_copy = static_cast<char>(identifier_type);
2790 inbind[1].buffer_type = MYSQL_TYPE_TINY;
2791 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
2792 inbind[1].is_unsigned = MLM_TRUE;
2793
2794 // identifier value
2795 std::vector<char> identifier_vec(identifier_begin,
2796 identifier_begin + identifier_len);
2797 unsigned long length = identifier_vec.size();
2798 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
2799 inbind[2].buffer = &identifier_vec[0];
2800 inbind[2].buffer_length = length;
2801 inbind[2].length = &length;
2802
2803 ConstHostCollection collection;
2805}
2806
2807bool
2809 const Host::IdentifierType& identifier_type,
2810 const uint8_t* identifier_begin, const size_t identifier_len) {
2811 // If operating in read-only mode, throw exception.
2812 impl_->checkReadOnly();
2813
2814 // Set up the WHERE clause value
2815 MYSQL_BIND inbind[3];
2816
2817 // subnet-id
2818 memset(inbind, 0, sizeof(inbind));
2819 uint32_t subnet = static_cast<uint32_t>(subnet_id);
2820 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2821 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
2822 inbind[0].is_unsigned = MLM_TRUE;
2823
2824 // identifier type
2825 char identifier_type_copy = static_cast<char>(identifier_type);
2826 inbind[1].buffer_type = MYSQL_TYPE_TINY;
2827 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
2828 inbind[1].is_unsigned = MLM_TRUE;
2829
2830 // identifier value
2831 std::vector<char> identifier_vec(identifier_begin,
2832 identifier_begin + identifier_len);
2833 unsigned long length = identifier_vec.size();
2834 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
2835 inbind[2].buffer = &identifier_vec[0];
2836 inbind[2].buffer_length = length;
2837 inbind[2].length = &length;
2838
2839 ConstHostCollection collection;
2841}
2842
2845 const uint8_t* identifier_begin,
2846 const size_t identifier_len) const {
2847 // Set up the WHERE clause value
2848 MYSQL_BIND inbind[2];
2849 memset(inbind, 0, sizeof(inbind));
2850
2851 // Identifier type.
2852 char identifier_type_copy = static_cast<char>(identifier_type);
2853 inbind[1].buffer = &identifier_type_copy;
2854 inbind[1].buffer_type = MYSQL_TYPE_TINY;
2855 inbind[1].is_unsigned = MLM_TRUE;
2856
2857 // Identifier value.
2858 std::vector<char> identifier_vec(identifier_begin,
2859 identifier_begin + identifier_len);
2860 unsigned long int length = identifier_vec.size();
2861 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2862 inbind[0].buffer = &identifier_vec[0];
2863 inbind[0].buffer_length = length;
2864 inbind[0].length = &length;
2865
2866 ConstHostCollection result;
2868 impl_->host_ipv46_exchange_,
2869 result, false);
2870 return (result);
2871}
2872
2875
2876 // Set up the WHERE clause value
2877 MYSQL_BIND inbind[1];
2878 memset(inbind, 0, sizeof(inbind));
2879
2880 uint32_t addr4 = address.toUint32();
2881 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2882 inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2883 inbind[0].is_unsigned = MLM_TRUE;
2884
2885 ConstHostCollection result;
2887 impl_->host_exchange_, result, false);
2888
2889 return (result);
2890}
2891
2894 const Host::IdentifierType& identifier_type,
2895 const uint8_t* identifier_begin,
2896 const size_t identifier_len) const {
2897
2898 return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
2900 impl_->host_exchange_));
2901}
2902
2905 const asiolink::IOAddress& address) const {
2906 // Check that address is IPv4, not IPv6.
2907 if (!address.isV4()) {
2908 isc_throw(BadValue, "MySqlHostDataSource::get4(2): wrong address type, "
2909 "address supplied is not an IPv4 address");
2910 }
2911
2912 // Set up the WHERE clause value
2913 MYSQL_BIND inbind[2];
2914 uint32_t subnet = subnet_id;
2915 memset(inbind, 0, sizeof(inbind));
2916 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2917 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
2918 inbind[0].is_unsigned = MLM_TRUE;
2919
2920 uint32_t addr4 = address.toUint32();
2921 inbind[1].buffer_type = MYSQL_TYPE_LONG;
2922 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
2923 inbind[1].is_unsigned = MLM_TRUE;
2924
2925 ConstHostCollection collection;
2927 inbind, impl_->host_exchange_, collection, true);
2928
2929 // Return single record if present, else clear the host.
2930 ConstHostPtr result;
2931 if (!collection.empty())
2932 result = *collection.begin();
2933
2934 return (result);
2935}
2936
2939 const Host::IdentifierType& identifier_type,
2940 const uint8_t* identifier_begin,
2941 const size_t identifier_len) const {
2942
2943 return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
2945 impl_->host_ipv6_exchange_));
2946}
2947
2950 const uint8_t prefix_len) const {
2952
2953 // Set up the WHERE clause value
2954 MYSQL_BIND inbind[2];
2955 memset(inbind, 0, sizeof(inbind));
2956
2957 std::string addr6 = prefix.toText();
2958 unsigned long addr6_length = addr6.size();
2959
2960 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2961 inbind[0].buffer = reinterpret_cast<char*>
2962 (const_cast<char*>(addr6.c_str()));
2963 inbind[0].length = &addr6_length;
2964 inbind[0].buffer_length = addr6_length;
2965
2966 uint8_t tmp = prefix_len;
2967 inbind[1].buffer_type = MYSQL_TYPE_TINY;
2968 inbind[1].buffer = reinterpret_cast<char*>(&tmp);
2969 inbind[1].is_unsigned = MLM_TRUE;
2970
2971 ConstHostCollection collection;
2973 inbind, impl_->host_ipv6_exchange_,
2974 collection, true);
2975
2976 // Return single record if present, else clear the host.
2977 ConstHostPtr result;
2978 if (!collection.empty()) {
2979 result = *collection.begin();
2980 }
2981
2982 return (result);
2983}
2984
2987 const asiolink::IOAddress& address) const {
2988 // Set up the WHERE clause value
2989 MYSQL_BIND inbind[2];
2990 memset(inbind, 0, sizeof(inbind));
2991
2992 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
2993 inbind[0].buffer_type = MYSQL_TYPE_LONG;
2994 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
2995 inbind[0].is_unsigned = MLM_TRUE;
2996
2997 std::string addr6 = address.toText();
2998 unsigned long addr6_length = addr6.size();
2999
3000 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3001 inbind[1].buffer = reinterpret_cast<char*>
3002 (const_cast<char*>(addr6.c_str()));
3003 inbind[1].length = &addr6_length;
3004 inbind[1].buffer_length = addr6_length;
3005
3006 ConstHostCollection collection;
3008 inbind, impl_->host_ipv6_exchange_,
3009 collection, true);
3010
3011 // Return single record if present, else clear the host.
3012 ConstHostPtr result;
3013 if (!collection.empty()) {
3014 result = *collection.begin();
3015 }
3016
3017 return (result);
3018}
3019
3020// Miscellaneous database methods.
3021
3022std::string MySqlHostDataSource::getName() const {
3023 std::string name = "";
3024 try {
3025 name = impl_->conn_.getParameter("name");
3026 } catch (...) {
3027 // Return an empty name
3028 }
3029 return (name);
3030}
3031
3033 return (std::string("Host data source that stores host information"
3034 "in MySQL database"));
3035}
3036
3037std::pair<uint32_t, uint32_t> MySqlHostDataSource::getVersion() const {
3038 return(impl_->getVersion());
3039}
3040
3041void
3043 // If operating in read-only mode, throw exception.
3044 impl_->checkReadOnly();
3045 impl_->conn_.commit();
3046}
3047
3048void
3050 // If operating in read-only mode, throw exception.
3051 impl_->checkReadOnly();
3052 impl_->conn_.rollback();
3053}
3054
3055}; // end of isc::dhcp namespace
3056}; // end of isc namespace
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition: data.cc:750
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
Data is truncated.
Definition: db_exceptions.h:35
bool configuredReadOnly() const
Convenience method checking if database should be opened with read only access.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown on failure to open database.
Exception thrown on failure to execute a database function.
Database duplicate entry error.
Definition: db_exceptions.h:42
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
Common MySQL Connector Pool.
MySqlHolder mysql_
MySQL connection handle.
std::vector< MYSQL_STMT * > statements_
Prepared statements.
std::vector< std::string > text_statements_
Raw text of statements.
void commit()
Commit Transactions.
void openDatabase()
Open Database.
void prepareStatements(const TaggedStatement *start_statement, const TaggedStatement *end_statement)
Prepare statements.
void checkError(const int status, const StatementIndex &index, const char *what) const
Check Error and Throw Exception.
void rollback()
Rollback Transactions.
Fetch and Release MySQL Results.
RAII object representing MySQL transaction.
void commit()
Commits transaction.
Attempt to modify data in read-only database.
Definition: db_exceptions.h:49
Authentication keys.
Definition: host.h:34
static const size_t MAX_DUID_LEN
maximum duid size As defined in RFC 8415, section 11.1
Definition: duid.h:31
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:242
IdentifierType
Type of the host identifier.
Definition: host.h:252
static const IdentifierType LAST_IDENTIFIER_TYPE
Constant pointing to the last identifier of the IdentifierType enumeration.
Definition: host.h:262
IPv6 reservation for a host.
Definition: host.h:106
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:135
Type getType() const
Returns reservation type.
Definition: host.h:149
Type
Type of the reservation.
Definition: host.h:112
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:140
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:144
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition: libdhcp++.cc:184
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
Definition: libdhcp++.cc:916
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition: libdhcp++.cc:205
Implementation of the MySqlHostDataSource.
void getHostCollection(StatementIndex stindex, MYSQL_BIND *bind, boost::shared_ptr< MySqlHostExchange > exchange, ConstHostCollection &result, bool single) const
Creates collection of Host objects with associated information such as IPv6 reservations and/or DHCP ...
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
void checkReadOnly() const
Throws exception if database is read only.
std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
void addOptions(const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
ConstHostPtr getHost(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, StatementIndex stindex, boost::shared_ptr< MySqlHostExchange > exchange) const
Retrieves a host by subnet and client's unique identifier.
boost::shared_ptr< MySqlHostWithOptionsExchange > host_exchange_
Pointer to the object representing an exchange which can be used to retrieve hosts and DHCPv4 options...
bool is_readonly_
Indicates if the database is opened in read only mode.
void checkError(const int status, const StatementIndex index, const char *what) const
Check Error and Throw Exception.
void addStatement(MySqlHostDataSourceImpl::StatementIndex stindex, std::vector< MYSQL_BIND > &bind)
Executes statements which inserts a row into one of the tables.
bool delStatement(StatementIndex stindex, MYSQL_BIND *bind)
Executes statements that delete records.
void addResv(const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
boost::shared_ptr< MySqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap &parameters)
Constructor.
boost::shared_ptr< MySqlOptionExchange > host_option_exchange_
Pointer to an object representing an exchange which can be used to insert DHCPv4 or DHCPv6 option int...
MySqlConnection conn_
MySQL connection.
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
void addOption(const MySqlHostDataSourceImpl::StatementIndex &stindex, const OptionDescriptor &opt_desc, const std::string &opt_space, const OptionalValue< SubnetID > &subnet_id, const HostID &host_id)
Inserts a single DHCP option into the database.
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
virtual void commit()
Commit Transactions.
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete a host by (subnet-id, address)
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
virtual std::string getDescription() const
Returns description of the backend.
MySqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
virtual ~MySqlHostDataSource()
Virtual destructor.
virtual bool del6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet6-id, identifier type, identifier)
virtual bool del4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet4-id, identifier type, identifier)
virtual void rollback()
Rollback Transactions.
virtual std::string getName() const
Returns backend name.
virtual void add(const HostPtr &host)
Adds a new host to the collection.
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
virtual ConstHostCollection getAll4(const asiolink::IOAddress &address) const
Returns a collection of hosts using the specified IPv4 address.
Option descriptor.
Definition: cfg_option.h:35
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:59
bool persistent_
Persistence flag.
Definition: cfg_option.h:44
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
Simple class representing an optional value.
bool isSpecified() const
Checks if the value is specified or unspecified.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
const my_bool MLM_FALSE
MySQL false value.
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
const my_bool MLM_TRUE
MySQL true value.
const int MLM_MYSQL_FETCH_FAILURE
MySQL fetch failure code.
const int MLM_MYSQL_FETCH_SUCCESS
MySQL fetch success code.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:186
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:497
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:725
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:731
TaggedStatementArray tagged_statements
Prepared MySQL statements used by the backend to insert and retrieve hosts from the database.
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:188
boost::array< TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS > TaggedStatementArray
Array of tagged statements.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
uint64_t HostID
HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
Definition: host.h:28
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:728
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
boost::shared_ptr< Option > OptionPtr
Definition: option.h:38
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:500
Definition: edns.h:19
Defines the logger used by the top-level component of kea-dhcp-ddns.
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
uint16_t code_
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24