1828 lines
71 KiB
Diff
1828 lines
71 KiB
Diff
commit 6ef02087bdf0f5158ad0654d9bdc30c166241e19
|
|
Author: Razvan Becheriu <razvan@isc.org>
|
|
Date: Thu Mar 12 12:38:45 2026 +0200
|
|
|
|
[#4387] backport #4275, #4288 to v3_0
|
|
|
|
[Martin Osvald <mosvald@redhat.com>]
|
|
NOTE: Tests were removed.
|
|
|
|
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
|
|
index f48bed5e81..49a9359ae2 100644
|
|
--- a/src/lib/cc/data.cc
|
|
+++ b/src/lib/cc/data.cc
|
|
@@ -1,4 +1,4 @@
|
|
-// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC")
|
|
+// Copyright (C) 2010-2026 Internet Systems Consortium, Inc. ("ISC")
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
@@ -17,6 +17,7 @@
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
+#include <set>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
@@ -37,6 +38,8 @@ const char* const WHITESPACE = " \b\f\n\r\t";
|
|
namespace isc {
|
|
namespace data {
|
|
|
|
+constexpr unsigned Element::MAX_NESTING_LEVEL;
|
|
+
|
|
std::string
|
|
Element::Position::str() const {
|
|
std::ostringstream ss;
|
|
@@ -50,6 +53,53 @@ operator<<(std::ostream& out, const Element::Position& pos) {
|
|
return (out);
|
|
}
|
|
|
|
+void
|
|
+Element::removeEmptyContainersRecursively(unsigned level) {
|
|
+ if (level <= 0) {
|
|
+ // Cycles are by definition not empty so no need to throw.
|
|
+ return;
|
|
+ }
|
|
+ if (type_ == list || type_ == map) {
|
|
+ size_t s(size());
|
|
+ for (size_t i = 0; i < s; ++i) {
|
|
+ // Get child.
|
|
+ ElementPtr child;
|
|
+ if (type_ == list) {
|
|
+ child = getNonConst(i);
|
|
+ } else if (type_ == map) {
|
|
+ std::string const key(get(i)->stringValue());
|
|
+ // The ElementPtr - ConstElementPtr disparity between
|
|
+ // ListElement and MapElement is forcing a const cast here.
|
|
+ // It's undefined behavior to modify it after const casting.
|
|
+ // The options are limited. I've tried templating, moving
|
|
+ // this function from a member function to free-standing and
|
|
+ // taking the Element template as argument. I've tried
|
|
+ // making it a virtual function with overridden
|
|
+ // implementations in ListElement and MapElement. Nothing
|
|
+ // works.
|
|
+ child = boost::const_pointer_cast<Element>(get(key));
|
|
+ }
|
|
+
|
|
+ // Makes no sense to continue for non-container children.
|
|
+ if (child->getType() != list && child->getType() != map) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // Recurse if not empty.
|
|
+ if (!child->empty()){
|
|
+ child->removeEmptyContainersRecursively(level - 1);
|
|
+ }
|
|
+
|
|
+ // When returning from recursion, remove if empty.
|
|
+ if (child->empty()) {
|
|
+ remove(i);
|
|
+ --i;
|
|
+ --s;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
std::string
|
|
Element::str() const {
|
|
std::stringstream ss;
|
|
@@ -600,7 +650,10 @@ fromStringstreamString(std::istream& in, const std::string& file, int& line,
|
|
|
|
ElementPtr
|
|
fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
|
- int& pos) {
|
|
+ int& pos, unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(JSONError, "fromJSON elements nested too deeply");
|
|
+ }
|
|
int c = 0;
|
|
ElementPtr list = Element::createList(Element::Position(file, line, pos));
|
|
ElementPtr cur_list_element;
|
|
@@ -608,7 +661,8 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
|
skipChars(in, WHITESPACE, line, pos);
|
|
while (c != EOF && c != ']') {
|
|
if (in.peek() != ']') {
|
|
- cur_list_element = Element::fromJSON(in, file, line, pos);
|
|
+ cur_list_element =
|
|
+ Element::fromJSON(in, file, line, pos, level - 1);
|
|
list->add(cur_list_element);
|
|
c = skipTo(in, file, line, pos, ",]", WHITESPACE);
|
|
} else {
|
|
@@ -621,7 +675,10 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
|
|
|
ElementPtr
|
|
fromStringstreamMap(std::istream& in, const std::string& file, int& line,
|
|
- int& pos) {
|
|
+ int& pos, unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(JSONError, "fromJSON elements nested too deeply");
|
|
+ }
|
|
ElementPtr map = Element::createMap(Element::Position(file, line, pos));
|
|
skipChars(in, WHITESPACE, line, pos);
|
|
int c = in.peek();
|
|
@@ -637,7 +694,8 @@ fromStringstreamMap(std::istream& in, const std::string& file, int& line,
|
|
skipTo(in, file, line, pos, ":", WHITESPACE);
|
|
// skip the :
|
|
|
|
- ConstElementPtr value = Element::fromJSON(in, file, line, pos);
|
|
+ ConstElementPtr value =
|
|
+ Element::fromJSON(in, file, line, pos, level - 1);
|
|
map->set(key, value);
|
|
|
|
c = skipTo(in, file, line, pos, ",}", WHITESPACE);
|
|
@@ -726,7 +784,10 @@ Element::fromJSON(std::istream& in, const std::string& file_name, bool preproc)
|
|
|
|
ElementPtr
|
|
Element::fromJSON(std::istream& in, const std::string& file, int& line,
|
|
- int& pos) {
|
|
+ int& pos, unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(JSONError, "fromJSON elements nested too deeply");
|
|
+ }
|
|
int c = 0;
|
|
ElementPtr element;
|
|
bool el_read = false;
|
|
@@ -773,11 +834,11 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line,
|
|
el_read = true;
|
|
break;
|
|
case '[':
|
|
- element = fromStringstreamList(in, file, line, pos);
|
|
+ element = fromStringstreamList(in, file, line, pos, level);
|
|
el_read = true;
|
|
break;
|
|
case '{':
|
|
- element = fromStringstreamMap(in, file, line, pos);
|
|
+ element = fromStringstreamMap(in, file, line, pos, level);
|
|
el_read = true;
|
|
break;
|
|
case EOF:
|
|
@@ -831,17 +892,17 @@ Element::fromJSONFile(const std::string& file_name, bool preproc) {
|
|
// to JSON format
|
|
|
|
void
|
|
-IntElement::toJSON(std::ostream& ss) const {
|
|
+IntElement::toJSON(std::ostream& ss, unsigned) const {
|
|
ss << intValue();
|
|
}
|
|
|
|
void
|
|
-BigIntElement::toJSON(std::ostream& ss) const {
|
|
+BigIntElement::toJSON(std::ostream& ss, unsigned) const {
|
|
ss << bigIntValue();
|
|
}
|
|
|
|
void
|
|
-DoubleElement::toJSON(std::ostream& ss) const {
|
|
+DoubleElement::toJSON(std::ostream& ss, unsigned) const {
|
|
// The default output for doubles nicely drops off trailing
|
|
// zeros, however this produces strings without decimal points
|
|
// for whole number values. When reparsed this will create
|
|
@@ -857,7 +918,7 @@ DoubleElement::toJSON(std::ostream& ss) const {
|
|
}
|
|
|
|
void
|
|
-BoolElement::toJSON(std::ostream& ss) const {
|
|
+BoolElement::toJSON(std::ostream& ss, unsigned) const {
|
|
if (boolValue()) {
|
|
ss << "true";
|
|
} else {
|
|
@@ -866,12 +927,12 @@ BoolElement::toJSON(std::ostream& ss) const {
|
|
}
|
|
|
|
void
|
|
-NullElement::toJSON(std::ostream& ss) const {
|
|
+NullElement::toJSON(std::ostream& ss, unsigned) const {
|
|
ss << "null";
|
|
}
|
|
|
|
void
|
|
-StringElement::toJSON(std::ostream& ss) const {
|
|
+StringElement::toJSON(std::ostream& ss, unsigned) const {
|
|
ss << "\"";
|
|
const std::string& str = stringValue();
|
|
for (size_t i = 0; i < str.size(); ++i) {
|
|
@@ -919,7 +980,11 @@ StringElement::toJSON(std::ostream& ss) const {
|
|
}
|
|
|
|
void
|
|
-ListElement::toJSON(std::ostream& ss) const {
|
|
+ListElement::toJSON(std::ostream& ss, unsigned level) const {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "toJSON got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
ss << "[ ";
|
|
|
|
const std::vector<ElementPtr>& v = listValue();
|
|
@@ -930,13 +995,17 @@ ListElement::toJSON(std::ostream& ss) const {
|
|
} else {
|
|
first = false;
|
|
}
|
|
- it->toJSON(ss);
|
|
+ it->toJSON(ss, level - 1);
|
|
}
|
|
ss << " ]";
|
|
}
|
|
|
|
void
|
|
-MapElement::toJSON(std::ostream& ss) const {
|
|
+MapElement::toJSON(std::ostream& ss, unsigned level) const {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "toJSON got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
ss << "{ ";
|
|
|
|
bool first = true;
|
|
@@ -948,7 +1017,7 @@ MapElement::toJSON(std::ostream& ss) const {
|
|
}
|
|
ss << "\"" << it.first << "\": ";
|
|
if (it.second) {
|
|
- it.second->toJSON(ss);
|
|
+ it.second->toJSON(ss, level - 1);
|
|
} else {
|
|
ss << "None";
|
|
}
|
|
@@ -1024,9 +1093,9 @@ MapElement::find(const std::string& id, ConstElementPtr& t) const {
|
|
}
|
|
|
|
bool
|
|
-IntElement::equals(const Element& other) const {
|
|
+IntElement::equals(const Element& other, unsigned) const {
|
|
// Let's not be very picky with constraining the integer types to be the
|
|
- // same. Equality is sometimes checked from high-up in the Element hierarcy.
|
|
+ // same. Equality is sometimes checked from high-up in the Element hierarchy.
|
|
// That is a context which, most of the time, does not have information on
|
|
// the type of integers stored on Elements lower in the hierarchy. So it
|
|
// would be difficult to differentiate between the integer types.
|
|
@@ -1035,9 +1104,9 @@ IntElement::equals(const Element& other) const {
|
|
}
|
|
|
|
bool
|
|
-BigIntElement::equals(const Element& other) const {
|
|
+BigIntElement::equals(const Element& other, unsigned) const {
|
|
// Let's not be very picky with constraining the integer types to be the
|
|
- // same. Equality is sometimes checked from high-up in the Element hierarcy.
|
|
+ // same. Equality is sometimes checked from high-up in the Element hierarchy.
|
|
// That is a context which, most of the time, does not have information on
|
|
// the type of integers stored on Elements lower in the hierarchy. So it
|
|
// would be difficult to differentiate between the integer types.
|
|
@@ -1046,37 +1115,41 @@ BigIntElement::equals(const Element& other) const {
|
|
}
|
|
|
|
bool
|
|
-DoubleElement::equals(const Element& other) const {
|
|
+DoubleElement::equals(const Element& other, unsigned) const {
|
|
return (other.getType() == Element::real) &&
|
|
(fabs(d - other.doubleValue()) < 1e-14);
|
|
}
|
|
|
|
bool
|
|
-BoolElement::equals(const Element& other) const {
|
|
+BoolElement::equals(const Element& other, unsigned) const {
|
|
return (other.getType() == Element::boolean) &&
|
|
(b == other.boolValue());
|
|
}
|
|
|
|
bool
|
|
-NullElement::equals(const Element& other) const {
|
|
+NullElement::equals(const Element& other, unsigned) const {
|
|
return (other.getType() == Element::null);
|
|
}
|
|
|
|
bool
|
|
-StringElement::equals(const Element& other) const {
|
|
+StringElement::equals(const Element& other, unsigned) const {
|
|
return (other.getType() == Element::string) &&
|
|
(s == other.stringValue());
|
|
}
|
|
|
|
bool
|
|
-ListElement::equals(const Element& other) const {
|
|
+ListElement::equals(const Element& other, unsigned level) const {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "equals got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (other.getType() == Element::list) {
|
|
const size_t s = size();
|
|
if (s != other.size()) {
|
|
return (false);
|
|
}
|
|
for (size_t i = 0; i < s; ++i) {
|
|
- if (!get(i)->equals(*other.get(i))) {
|
|
+ if (!get(i)->equals(*other.get(i), level - 1)) {
|
|
return (false);
|
|
}
|
|
}
|
|
@@ -1123,7 +1196,11 @@ ListElement::sort(std::string const& index /* = std::string() */) {
|
|
}
|
|
|
|
bool
|
|
-MapElement::equals(const Element& other) const {
|
|
+MapElement::equals(const Element& other, unsigned level) const {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "equals got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (other.getType() == Element::map) {
|
|
if (size() != other.size()) {
|
|
return (false);
|
|
@@ -1131,7 +1208,7 @@ MapElement::equals(const Element& other) const {
|
|
for (auto const& kv : mapValue()) {
|
|
auto key = kv.first;
|
|
if (other.contains(key)) {
|
|
- if (!get(key)->equals(*other.get(key))) {
|
|
+ if (!get(key)->equals(*other.get(key), level - 1)) {
|
|
return (false);
|
|
}
|
|
} else {
|
|
@@ -1215,7 +1292,12 @@ merge(ElementPtr element, ConstElementPtr other) {
|
|
|
|
void
|
|
mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
- HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
|
|
+ HierarchyDescriptor& hierarchy, std::string key, size_t idx,
|
|
+ unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "mergeDiffAdd got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (element->getType() != other->getType()) {
|
|
isc_throw(TypeError, "mergeDiffAdd arguments not same type");
|
|
}
|
|
@@ -1237,7 +1319,8 @@ mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
// entity.
|
|
if (f->second.match_(mutable_left, mutable_right)) {
|
|
found = true;
|
|
- mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
|
|
+ mergeDiffAdd(mutable_left, mutable_right, hierarchy,
|
|
+ key, idx, level - 1);
|
|
}
|
|
}
|
|
if (!found) {
|
|
@@ -1263,7 +1346,8 @@ mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
(value->getType() == Element::map ||
|
|
value->getType() == Element::list)) {
|
|
ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
|
|
- mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
|
|
+ mergeDiffAdd(mutable_element, value, hierarchy,
|
|
+ current_key, idx + 1, level - 1);
|
|
} else {
|
|
element->set(current_key, value);
|
|
}
|
|
@@ -1276,7 +1360,12 @@ mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
|
|
void
|
|
mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
- HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
|
|
+ HierarchyDescriptor& hierarchy, std::string key, size_t idx,
|
|
+ unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "mergeDiffDel got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (element->getType() != other->getType()) {
|
|
isc_throw(TypeError, "mergeDiffDel arguments not same type");
|
|
}
|
|
@@ -1301,7 +1390,8 @@ mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
element->remove(iter);
|
|
removed = true;
|
|
} else {
|
|
- mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
|
|
+ mergeDiffDel(mutable_left, mutable_right,
|
|
+ hierarchy, key, idx, level - 1);
|
|
if (mutable_left->empty()) {
|
|
element->remove(iter);
|
|
removed = true;
|
|
@@ -1332,7 +1422,8 @@ mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
|
|
if (mutable_element->getType() == Element::map ||
|
|
mutable_element->getType() == Element::list) {
|
|
- mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
|
|
+ mergeDiffDel(mutable_element, value, hierarchy,
|
|
+ current_key, idx + 1, level - 1);
|
|
if (mutable_element->empty()) {
|
|
element->remove(current_key);
|
|
}
|
|
@@ -1367,7 +1458,12 @@ mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
void
|
|
extend(const std::string& container, const std::string& extension,
|
|
ElementPtr& element, ElementPtr& other, HierarchyDescriptor& hierarchy,
|
|
- std::string key, size_t idx, bool alter) {
|
|
+ std::string key, size_t idx, bool alter, unsigned level) {
|
|
+
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "extend got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (element->getType() != other->getType()) {
|
|
isc_throw(TypeError, "extend arguments not same type");
|
|
}
|
|
@@ -1386,7 +1482,7 @@ extend(const std::string& container, const std::string& extension,
|
|
}
|
|
if (f->second.match_(mutable_left, mutable_right)) {
|
|
extend(container, extension, mutable_left, mutable_right,
|
|
- hierarchy, key, idx, alter);
|
|
+ hierarchy, key, idx, alter, level - 1);
|
|
}
|
|
}
|
|
}
|
|
@@ -1406,7 +1502,8 @@ extend(const std::string& container, const std::string& extension,
|
|
if (container == key) {
|
|
alter = true;
|
|
}
|
|
- extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
|
|
+ extend(container, extension, mutable_element, value,
|
|
+ hierarchy, current_key, idx + 1, alter, level - 1);
|
|
} else if (alter && current_key == extension) {
|
|
element->set(current_key, value);
|
|
}
|
|
@@ -1417,7 +1514,7 @@ extend(const std::string& container, const std::string& extension,
|
|
}
|
|
|
|
ElementPtr
|
|
-copy(ConstElementPtr from, int level) {
|
|
+copy(ConstElementPtr from, unsigned level) {
|
|
if (!from) {
|
|
isc_throw(BadValue, "copy got a null pointer");
|
|
}
|
|
@@ -1543,9 +1640,15 @@ isEquivalent(ConstElementPtr a, ConstElementPtr b) {
|
|
return (isEquivalent0(a, b, 100));
|
|
}
|
|
|
|
+namespace {
|
|
+
|
|
void
|
|
-prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
- unsigned indent, unsigned step) {
|
|
+prettyPrint0(ConstElementPtr element, std::ostream& out,
|
|
+ unsigned indent, unsigned step, unsigned level) {
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "prettyPrint got infinite recursion: "
|
|
+ "arguments include cycles");
|
|
+ }
|
|
if (!element) {
|
|
isc_throw(BadValue, "prettyPrint got a null pointer");
|
|
}
|
|
@@ -1585,7 +1688,7 @@ prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
out << std::string(indent + step, ' ');
|
|
}
|
|
// recursive call
|
|
- prettyPrint(it, out, indent + step, step);
|
|
+ prettyPrint0(it, out, indent + step, step, level - 1);
|
|
}
|
|
|
|
// close the list
|
|
@@ -1620,7 +1723,7 @@ prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
// add keyword:
|
|
out << "\"" << it.first << "\": ";
|
|
// recursive call
|
|
- prettyPrint(it.second, out, indent + step, step);
|
|
+ prettyPrint0(it.second, out, indent + step, step, level - 1);
|
|
}
|
|
|
|
// close the map
|
|
@@ -1631,6 +1734,14 @@ prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
}
|
|
}
|
|
|
|
+} // end anonymous namespace
|
|
+
|
|
+void
|
|
+prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
+ unsigned indent, unsigned step) {
|
|
+ prettyPrint0(element, out, indent, step, Element::MAX_NESTING_LEVEL);
|
|
+}
|
|
+
|
|
std::string
|
|
prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
|
|
std::stringstream ss;
|
|
@@ -1657,5 +1768,90 @@ void Element::preprocess(std::istream& in, std::stringstream& out) {
|
|
}
|
|
}
|
|
|
|
+namespace {
|
|
+
|
|
+// Type of arcs.
|
|
+typedef std::set<ConstElementPtr> Arc;
|
|
+
|
|
+// Helper function walking on the supposed tree.
|
|
+bool
|
|
+IsCircular0(ConstElementPtr element, Arc arc) {
|
|
+ // Sanity check.
|
|
+ if (!element) {
|
|
+ return (false);
|
|
+ }
|
|
+ auto type = element->getType();
|
|
+ // Container?
|
|
+ if ((type != Element::list) && (type != Element::map)) {
|
|
+ return (false);
|
|
+ }
|
|
+ // Empty? A cycle requires at least one element.
|
|
+ if (element->empty()) {
|
|
+ return (false);
|
|
+ }
|
|
+ // In the arc?
|
|
+ if (arc.count(element) > 0) {
|
|
+ return (true);
|
|
+ }
|
|
+ // This requires to work on a copy of the arc but it should be small.
|
|
+ arc.insert(element);
|
|
+ if (type == Element::list) {
|
|
+ for (auto const& it : element->listValue()) {
|
|
+ if (IsCircular0(it, arc)) {
|
|
+ return (true);
|
|
+ }
|
|
+ }
|
|
+ return (false);
|
|
+ }
|
|
+ // The argument is a map.
|
|
+ for (auto const& it : element->mapValue()) {
|
|
+ if (IsCircular0(it.second, arc)) {
|
|
+ return (true);
|
|
+ }
|
|
+ }
|
|
+ return (false);
|
|
+}
|
|
+
|
|
+} // end anonymous namespace
|
|
+
|
|
+bool
|
|
+IsCircular(ConstElementPtr element) {
|
|
+ return (IsCircular0(element, Arc()));
|
|
+}
|
|
+
|
|
+unsigned
|
|
+getNestDepth(ConstElementPtr element, unsigned max_depth) {
|
|
+ if (max_depth == 0U) {
|
|
+ return (0U);
|
|
+ }
|
|
+ if (!element) {
|
|
+ return (0U);
|
|
+ }
|
|
+ unsigned ret = 1U;
|
|
+ if (element->getType() == Element::list) {
|
|
+ for (auto const& i : element->listValue()) {
|
|
+ unsigned sub = getNestDepth(i, max_depth - 1);
|
|
+ if (sub == max_depth - 1) {
|
|
+ return (max_depth);
|
|
+ }
|
|
+ if (sub + 1 > ret) {
|
|
+ ret = sub + 1;
|
|
+ }
|
|
+ }
|
|
+ } else if (element->getType() == Element::map) {
|
|
+ for (auto const& i : element->mapValue()) {
|
|
+ unsigned sub = getNestDepth(i.second, max_depth - 1);
|
|
+ if (sub == max_depth - 1) {
|
|
+ return (max_depth);
|
|
+ }
|
|
+ if (sub + 1 > ret) {
|
|
+ ret = sub + 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return (ret);
|
|
+}
|
|
+
|
|
} // end of isc::data namespace
|
|
} // end of isc namespace
|
|
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
|
|
index b93e2ec29e..e473f947b3 100644
|
|
--- a/src/lib/cc/data.h
|
|
+++ b/src/lib/cc/data.h
|
|
@@ -21,7 +21,8 @@
|
|
|
|
#include <exceptions/exceptions.h>
|
|
|
|
-namespace isc { namespace data {
|
|
+namespace isc {
|
|
+namespace data {
|
|
|
|
class Element;
|
|
// todo: describe the rationale behind ElementPtr?
|
|
@@ -70,8 +71,20 @@ public:
|
|
/// the type in question.
|
|
///
|
|
class Element {
|
|
-
|
|
public:
|
|
+ /// @brief Maximum nesting level of Element objects.
|
|
+ ///
|
|
+ /// Many methods and functions perform a recursive walk on an element
|
|
+ /// containing lists or/and maps. This recursion is limited to using
|
|
+ /// an allowed level of nesting argument which is decremented at
|
|
+ /// each recursive call until it reaches 0. This was extended to
|
|
+ /// recursive parsing of a JSON text as stack overflows were reported
|
|
+ /// with excessive recursion on specially crafted input.
|
|
+ /// This constant is the default allowed level of nesting, its value
|
|
+ /// is arbitrary (but enough for all realistic cases) and used before
|
|
+ /// limiting recursion in *all* recursive methods/functions.
|
|
+ static constexpr unsigned MAX_NESTING_LEVEL = 100U;
|
|
+
|
|
/// @brief Represents the position of the data element within a
|
|
/// configuration string.
|
|
///
|
|
@@ -121,7 +134,7 @@ public:
|
|
///
|
|
/// The object containing two zeros is a default for most of the
|
|
/// methods creating @c Element objects. The returned value is static
|
|
- /// so as it is not created everytime the function with the default
|
|
+ /// so as it is not created every time the function with the default
|
|
/// position argument is called.
|
|
static const Position& ZERO_POSITION() {
|
|
static Position position("", 0, 0);
|
|
@@ -171,37 +184,53 @@ protected:
|
|
|
|
|
|
public:
|
|
- // base class; make dtor virtual
|
|
+ // Base class; make destructor virtual.
|
|
virtual ~Element() {}
|
|
|
|
- /// @return the type of this element
|
|
- types getType() const { return (type_); }
|
|
+ /// @return the type of this element.
|
|
+ types getType() const {
|
|
+ return (type_);
|
|
+ }
|
|
|
|
/// @brief Returns position where the data element's value starts in a
|
|
/// configuration string.
|
|
///
|
|
/// @warning The returned reference is valid as long as the object which
|
|
/// created it lives.
|
|
- const Position& getPosition() const { return (position_); }
|
|
+ /// @return The position.
|
|
+ const Position& getPosition() const {
|
|
+ return (position_);
|
|
+ }
|
|
|
|
- /// Returns a string representing the Element and all its
|
|
- /// child elements; note that this is different from stringValue(),
|
|
+ /// @brief Returns a string representing the Element and all its
|
|
+ /// child elements
|
|
+ ///
|
|
+ /// @note: that this is different from stringValue(),
|
|
/// which only returns the single value of a StringElement
|
|
///
|
|
/// The resulting string will contain the Element in JSON format.
|
|
+ /// Based on @ref toJSON.
|
|
///
|
|
- /// @return std::string containing the string representation
|
|
+ /// @return std::string containing the string representation.
|
|
std::string str() const;
|
|
|
|
- /// Returns the wireformat for the Element and all its child
|
|
+ /// @brief Returns the wireformat for the Element and all its child
|
|
/// elements.
|
|
///
|
|
+ /// Based on @ref toJSON.
|
|
+ ///
|
|
/// @return std::string containing the element in wire format
|
|
std::string toWire() const;
|
|
+
|
|
+ /// @brief Appends the wireformat for the Element to the stream.
|
|
+ ///
|
|
+ /// @param out The output stream where to append the wireformat.
|
|
void toWire(std::ostream& out) const;
|
|
|
|
/// @brief Add the position to a TypeError message
|
|
/// should be used in place of isc_throw(TypeError, error)
|
|
+ ///
|
|
+ /// @param error The error message.
|
|
#define throwTypeError(error) \
|
|
{ \
|
|
std::string msg_ = error; \
|
|
@@ -213,15 +242,26 @@ public:
|
|
isc_throw(TypeError, msg_); \
|
|
}
|
|
|
|
- /// @name pure virtuals, every derived class must implement these
|
|
+ /// @name pure virtuals, every derived class must implement these.
|
|
|
|
+ /// @brief Test equality.
|
|
+ ///
|
|
+ /// @param other The other element to compare with.
|
|
+ /// @param level The maximum level of recursion.
|
|
/// @return true if the other ElementPtr has the same value and the same
|
|
/// type (or a different and compatible type), false otherwise.
|
|
- virtual bool equals(const Element& other) const = 0;
|
|
+ /// @throw BadValue when nesting depth is more than level.
|
|
+ virtual bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const = 0;
|
|
|
|
- /// Converts the Element to JSON format and appends it to
|
|
- /// the given stringstream.
|
|
- virtual void toJSON(std::ostream& ss) const = 0;
|
|
+ /// @brief Converts the Element to JSON format and appends it to
|
|
+ /// the given output stream.
|
|
+ ///
|
|
+ /// @param ss The output stream where to append the JSON format.
|
|
+ /// @param level The maximum level of recursion.
|
|
+ /// @throw BadValue when nesting depth is more than level.
|
|
+ virtual void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const = 0;
|
|
|
|
/// @name Type-specific getters
|
|
///
|
|
@@ -231,21 +271,38 @@ public:
|
|
/// If you want an exception-safe getter method, use
|
|
/// getValue() below
|
|
//@{
|
|
- virtual int64_t intValue() const
|
|
- { throwTypeError("intValue() called on non-integer Element"); }
|
|
+ /// @brief Return the integer value.
|
|
+ virtual int64_t intValue() const {
|
|
+ throwTypeError("intValue() called on non-integer Element");
|
|
+ }
|
|
+
|
|
+ /// @brief Return the big integer value.
|
|
virtual isc::util::int128_t bigIntValue() const {
|
|
throwTypeError("bigIntValue() called on non-big-integer Element");
|
|
}
|
|
- virtual double doubleValue() const
|
|
- { throwTypeError("doubleValue() called on non-double Element"); }
|
|
- virtual bool boolValue() const
|
|
- { throwTypeError("boolValue() called on non-Bool Element"); }
|
|
- virtual std::string stringValue() const
|
|
- { throwTypeError("stringValue() called on non-string Element"); }
|
|
+
|
|
+ /// @brief Return the double value.
|
|
+ virtual double doubleValue() const {
|
|
+ throwTypeError("doubleValue() called on non-double Element");
|
|
+ }
|
|
+
|
|
+ /// @brief Return the boolean value.
|
|
+ virtual bool boolValue() const {
|
|
+ throwTypeError("boolValue() called on non-Bool Element");
|
|
+ }
|
|
+
|
|
+ /// @brief Return the string value.
|
|
+ virtual std::string stringValue() const {
|
|
+ throwTypeError("stringValue() called on non-string Element");
|
|
+ }
|
|
+
|
|
+ /// @brief Return the list value.
|
|
virtual const std::vector<ElementPtr>& listValue() const {
|
|
// replace with real exception or empty vector?
|
|
throwTypeError("listValue() called on non-list Element");
|
|
}
|
|
+
|
|
+ /// @brief Return the map value.
|
|
virtual const std::map<std::string, ConstElementPtr>& mapValue() const {
|
|
// replace with real exception or empty map?
|
|
throwTypeError("mapValue() called on non-map Element");
|
|
@@ -261,11 +318,40 @@ public:
|
|
/// data to the given reference and returning true
|
|
///
|
|
//@{
|
|
+ /// @brief Get the integer value.
|
|
+ ///
|
|
+ /// @param t The reference to the integer.
|
|
+ /// @return false.
|
|
virtual bool getValue(int64_t& t) const;
|
|
+
|
|
+ /// @brief Get the double value.
|
|
+ ///
|
|
+ /// @param t The reference to the double.
|
|
+ /// @return false.
|
|
virtual bool getValue(double& t) const;
|
|
+
|
|
+ /// @brief Get the boolean value.
|
|
+ ///
|
|
+ /// @param t The reference to the boolean.
|
|
+ /// @return false.
|
|
virtual bool getValue(bool& t) const;
|
|
+
|
|
+ /// @brief Get the string value.
|
|
+ ///
|
|
+ /// @param t The reference to the string.
|
|
+ /// @return false.
|
|
virtual bool getValue(std::string& t) const;
|
|
+
|
|
+ /// @brief Get the list value.
|
|
+ ///
|
|
+ /// @param t The reference to the list.
|
|
+ /// @return false.
|
|
virtual bool getValue(std::vector<ElementPtr>& t) const;
|
|
+
|
|
+ /// @brief Get the map value.
|
|
+ ///
|
|
+ /// @param t The reference to the map.
|
|
+ /// @return false.
|
|
virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const;
|
|
//@}
|
|
|
|
@@ -279,20 +365,70 @@ public:
|
|
/// Notes: Read notes of IntElement definition about the use of
|
|
/// long long int, long int and int.
|
|
//@{
|
|
+ /// @brief Set the integer value.
|
|
+ ///
|
|
+ /// @param v The new integer value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const long long int v);
|
|
+
|
|
+ /// @brief Set the big integer value.
|
|
+ ///
|
|
+ /// @param v The new big integer value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const isc::util::int128_t& v);
|
|
- bool setValue(const long int i) { return (setValue(static_cast<long long int>(i))); }
|
|
- bool setValue(const int i) { return (setValue(static_cast<long long int>(i))); }
|
|
+
|
|
+ /// @brief Set the double value.
|
|
+ ///
|
|
+ /// @param v The new double value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const double v);
|
|
+
|
|
+ /// @brief Set the boolean value.
|
|
+ ///
|
|
+ /// @param t The new boolean value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const bool t);
|
|
+
|
|
+ /// @brief Set the string value.
|
|
+ ///
|
|
+ /// @param v The new string value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const std::string& v);
|
|
+
|
|
+ /// @brief Set the list value.
|
|
+ ///
|
|
+ /// @param v The new list value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const std::vector<ElementPtr>& v);
|
|
+
|
|
+ /// @brief Set the map value.
|
|
+ ///
|
|
+ /// @param v The new map value.
|
|
+ /// @return False.
|
|
virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
|
|
+
|
|
+ /// @brief Set the integer value (long int overload).
|
|
+ ///
|
|
+ /// @param i The new integer value.
|
|
+ /// @return True (and set the value) when the Element type is integer,
|
|
+ /// false otherwise.
|
|
+ bool setValue(const long int i) {
|
|
+ return (setValue(static_cast<long long int>(i)));
|
|
+ }
|
|
+
|
|
+ /// @brief Set the integer value (int overload).
|
|
+ ///
|
|
+ /// @param i The new integer value.
|
|
+ /// @return True (and set the value) when the Element type is integer,
|
|
+ /// false otherwise.
|
|
+ bool setValue(const int i) {
|
|
+ return (setValue(static_cast<long long int>(i)));
|
|
+ }
|
|
//@}
|
|
|
|
- // Other functions for specific subtypes
|
|
+ // Other functions for specific subtypes.
|
|
|
|
- /// @name ListElement functions
|
|
+ /// @name ListElement functions.
|
|
///
|
|
/// @brief If the Element on which these functions are called are not
|
|
/// an instance of ListElement, a TypeError exception is thrown.
|
|
@@ -300,33 +436,34 @@ public:
|
|
/// Returns the ElementPtr at the given index. If the index is out
|
|
/// of bounds, this function throws an std::out_of_range exception.
|
|
/// @param i The position of the ElementPtr to return
|
|
+ /// @return specified element pointer.
|
|
virtual ConstElementPtr get(const int i) const;
|
|
|
|
- /// @brief returns element as non-const pointer
|
|
+ /// @brief returns element as non-const pointer.
|
|
///
|
|
- /// @param i The position of the ElementPtr to retrieve
|
|
- /// @return specified element pointer
|
|
+ /// @param i The position of the ElementPtr to retrieve.
|
|
+ /// @return specified element pointer.
|
|
virtual ElementPtr getNonConst(const int i) const;
|
|
|
|
- /// Sets the ElementPtr at the given index. If the index is out
|
|
+ /// @brief Sets the ElementPtr at the given index. If the index is out
|
|
/// of bounds, this function throws an std::out_of_range exception.
|
|
/// @param i The position of the ElementPtr to set
|
|
/// @param element The ElementPtr to set at the position
|
|
virtual void set(const size_t i, ElementPtr element);
|
|
|
|
- /// Adds an ElementPtr to the list
|
|
+ /// @brief Adds an ElementPtr to the list
|
|
/// @param element The ElementPtr to add
|
|
virtual void add(ElementPtr element);
|
|
|
|
- /// Removes the element at the given position. If the index is out
|
|
+ /// @brief Removes the element at the given position. If the index is out
|
|
/// of nothing happens.
|
|
/// @param i The index of the element to remove.
|
|
virtual void remove(const int i);
|
|
|
|
- /// Returns the number of elements in the list.
|
|
+ /// @brief Returns the number of elements in the list.
|
|
virtual size_t size() const;
|
|
|
|
- /// Return true if there are no elements in the list.
|
|
+ /// @brief Return true if there are no elements in the list.
|
|
virtual bool empty() const;
|
|
//@}
|
|
|
|
@@ -336,26 +473,26 @@ public:
|
|
/// @brief If the Element on which these functions are called are not
|
|
/// an instance of MapElement, a TypeError exception is thrown.
|
|
//@{
|
|
- /// Returns the ElementPtr at the given key
|
|
+ /// @brief Returns the ElementPtr at the given key
|
|
/// @param name The key of the Element to return
|
|
/// @return The ElementPtr at the given key, or null if not present
|
|
virtual ConstElementPtr get(const std::string& name) const;
|
|
|
|
- /// Sets the ElementPtr at the given key
|
|
+ /// @brief Sets the ElementPtr at the given key
|
|
/// @param name The key of the Element to set
|
|
/// @param element The ElementPtr to set at the given key.
|
|
virtual void set(const std::string& name, ConstElementPtr element);
|
|
|
|
- /// Remove the ElementPtr at the given key
|
|
+ /// @brief Remove the ElementPtr at the given key
|
|
/// @param name The key of the Element to remove
|
|
virtual void remove(const std::string& name);
|
|
|
|
- /// Checks if there is data at the given key
|
|
+ /// @brief Checks if there is data at the given key
|
|
/// @param name The key of the Element checked for existence
|
|
/// @return true if there is data at the key, false if not.
|
|
virtual bool contains(const std::string& name) const;
|
|
|
|
- /// Recursively finds any data at the given identifier. The
|
|
+ /// @brief Recursively finds any data at the given identifier. The
|
|
/// identifier is a /-separated list of names of nested maps, with
|
|
/// the last name being the leaf that is returned.
|
|
///
|
|
@@ -370,7 +507,7 @@ public:
|
|
/// Element::is_null(ElementPtr e).
|
|
virtual ConstElementPtr find(const std::string& identifier) const;
|
|
|
|
- /// See @c Element::find()
|
|
+ /// @brief See @c Element::find()
|
|
/// @param identifier The identifier of the element to find
|
|
/// @param t Reference to store the resulting ElementPtr, if found.
|
|
/// @return true if the element was found, false if not.
|
|
@@ -396,25 +533,84 @@ public:
|
|
/// Notes: Read notes of IntElement definition about the use of
|
|
/// long long int, long int and int.
|
|
//@{
|
|
+ /// @brief Create a NullElement.
|
|
+ ///
|
|
+ /// @param pos The position.
|
|
+ /// @return The NullElement at the position.
|
|
static ElementPtr create(const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create an IntElement.
|
|
+ ///
|
|
+ /// @param i The integer.
|
|
+ /// @param pos The position.
|
|
+ /// @return The IntElement with the argument at the position.
|
|
static ElementPtr create(const long long int i,
|
|
const Position& pos = ZERO_POSITION());
|
|
- static ElementPtr create(const isc::util::int128_t& i,
|
|
- const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create an IntElement (int overload).
|
|
+ ///
|
|
+ /// @param i The integer.
|
|
+ /// @param pos The position.
|
|
+ /// @return The IntElement with the argument at the position.
|
|
static ElementPtr create(const int i,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create an IntElement (long int overload).
|
|
+ ///
|
|
+ /// @param i The integer.
|
|
+ /// @param pos The position.
|
|
+ /// @return The IntElement with the argument at the position.
|
|
static ElementPtr create(const long int i,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create an IntElement (int32_t overload).
|
|
+ ///
|
|
+ /// @param i The integer.
|
|
+ /// @param pos The position.
|
|
+ /// @return The IntElement with the argument at the position.
|
|
static ElementPtr create(const uint32_t i,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create a BigIntElement.
|
|
+ ///
|
|
+ /// @param i The big integer.
|
|
+ /// @param pos The position.
|
|
+ /// @return The BigIntElement with the argument at the position.
|
|
+ static ElementPtr create(const isc::util::int128_t& i,
|
|
+ const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create a DoubleElement.
|
|
+ ///
|
|
+ /// @param d The double.
|
|
+ /// @param pos The position.
|
|
+ /// @return The DoubleElement with the argument at the position.
|
|
static ElementPtr create(const double d,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create a BoolElement.
|
|
+ ///
|
|
+ /// @param b The boolean.
|
|
+ /// @param pos The position.
|
|
+ /// @return The BoolElement with the argument at the position.
|
|
static ElementPtr create(const bool b,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
+ /// @brief Create a StringElement.
|
|
+ ///
|
|
+ /// @param s The string.
|
|
+ /// @param pos The position.
|
|
+ /// @return The StringElement with the argument at the position.
|
|
static ElementPtr create(const std::string& s,
|
|
const Position& pos = ZERO_POSITION());
|
|
+
|
|
// need both std:string and char *, since c++ will match
|
|
// bool before std::string when you pass it a char *
|
|
+
|
|
+ /// @brief Create a StringElement (char* overload).
|
|
+ ///
|
|
+ /// @param s The string.
|
|
+ /// @param pos The position.
|
|
+ /// @return The StringElement with the argument at the position.
|
|
static ElementPtr create(const char *s,
|
|
const Position& pos = ZERO_POSITION());
|
|
|
|
@@ -438,7 +634,7 @@ public:
|
|
/// error, an exception of the type isc::data::JSONError is thrown.
|
|
|
|
//@{
|
|
- /// Creates an Element from the given JSON string
|
|
+ /// @brief Creates an Element from the given JSON string
|
|
/// @param in The string to parse the element from
|
|
/// @param preproc specified whether preprocessing (e.g. comment removal)
|
|
/// should be performed
|
|
@@ -446,7 +642,7 @@ public:
|
|
/// in the given string.
|
|
static ElementPtr fromJSON(const std::string& in, bool preproc = false);
|
|
|
|
- /// Creates an Element from the given input stream containing JSON
|
|
+ /// @brief Creates an Element from the given input stream containing JSON
|
|
/// formatted data.
|
|
///
|
|
/// @param in The string to parse the element from
|
|
@@ -457,7 +653,7 @@ public:
|
|
/// in the given input stream.
|
|
static ElementPtr fromJSON(std::istream& in, bool preproc = false);
|
|
|
|
- /// Creates an Element from the given input stream containing JSON
|
|
+ /// @brief Creates an Element from the given input stream containing JSON
|
|
/// formatted data.
|
|
///
|
|
/// @param in The string to parse the element from
|
|
@@ -471,7 +667,7 @@ public:
|
|
static ElementPtr fromJSON(std::istream& in, const std::string& file_name,
|
|
bool preproc = false);
|
|
|
|
- /// Creates an Element from the given input stream, where we keep
|
|
+ /// @brief Creates an Element from the given input stream, where we keep
|
|
/// track of the location in the stream for error reporting.
|
|
///
|
|
/// @param in The string to parse the element from.
|
|
@@ -480,13 +676,14 @@ public:
|
|
/// track of the current line.
|
|
/// @param pos A reference to the int where the function keeps
|
|
/// track of the current position within the current line.
|
|
- /// @throw JSONError
|
|
+ /// @param level The maximum level of recursion.
|
|
/// @return An ElementPtr that contains the element(s) specified
|
|
/// in the given input stream.
|
|
// make this one private?
|
|
/// @throw JSONError
|
|
static ElementPtr fromJSON(std::istream& in, const std::string& file,
|
|
- int& line, int &pos);
|
|
+ int& line, int &pos,
|
|
+ unsigned level = MAX_NESTING_LEVEL);
|
|
|
|
/// Reads contents of specified file and interprets it as JSON.
|
|
///
|
|
@@ -499,23 +696,23 @@ public:
|
|
bool preproc = false);
|
|
//@}
|
|
|
|
- /// @name Type name conversion functions
|
|
+ /// @name Type name conversion functions.
|
|
|
|
- /// Returns the name of the given type as a string
|
|
+ /// @brief Returns the name of the given type as a string
|
|
///
|
|
- /// @param type The type to return the name of
|
|
+ /// @param type The type to return the name of.
|
|
/// @return The name of the type, or "unknown" if the type
|
|
/// is not known.
|
|
static std::string typeToName(Element::types type);
|
|
|
|
- /// Converts the string to the corresponding type
|
|
+ /// @brief Converts the string to the corresponding type
|
|
/// Throws a TypeError if the name is unknown.
|
|
///
|
|
- /// @param type_name The name to get the type of
|
|
+ /// @param type_name The name to get the type of.
|
|
/// @return the corresponding type value
|
|
static Element::types nameToType(const std::string& type_name);
|
|
|
|
- /// @brief input text preprocessor
|
|
+ /// @brief input text preprocessor.
|
|
///
|
|
/// This method performs preprocessing of the input stream (which is
|
|
/// expected to contain a text version of to be parsed JSON). For now the
|
|
@@ -527,79 +724,43 @@ public:
|
|
/// the input stream, filters the content and returns the result in a
|
|
/// different stream.
|
|
///
|
|
- /// @param in input stream to be preprocessed
|
|
- /// @param out output stream (filtered content will be written here)
|
|
+ /// @param in input stream to be preprocessed.
|
|
+ /// @param out output stream (filtered content will be written here).
|
|
static void preprocess(std::istream& in, std::stringstream& out);
|
|
|
|
/// @name Wire format factory functions
|
|
|
|
- /// These function pparse the wireformat at the given stringstream
|
|
+ /// These function parse the wireformat at the given stringstream
|
|
/// (of the given length). If there is a parse error an exception
|
|
/// of the type isc::cc::DecodeError is raised.
|
|
|
|
//@{
|
|
- /// Creates an Element from the wire format in the given
|
|
+ /// @brief Creates an Element from the wire format in the given
|
|
/// stringstream of the given length.
|
|
- /// Since the wire format is JSON, this is the same as
|
|
+ ///
|
|
+ /// @note: Since the wire format is JSON, this is the same as
|
|
/// fromJSON, and could be removed.
|
|
///
|
|
/// @param in The input stringstream.
|
|
- /// @param length The length of the wireformat data in the stream
|
|
+ /// @param length The length of the wireformat data in the stream.
|
|
/// @return ElementPtr with the data that is parsed.
|
|
static ElementPtr fromWire(std::stringstream& in, int length);
|
|
|
|
- /// Creates an Element from the wire format in the given string
|
|
- /// Since the wire format is JSON, this is the same as
|
|
+ /// @brief Creates an Element from the wire format in the given string.
|
|
+ ///
|
|
+ /// @note: Since the wire format is JSON, this is the same as
|
|
/// fromJSON, and could be removed.
|
|
///
|
|
- /// @param s The input string
|
|
+ /// @param s The input string.
|
|
/// @return ElementPtr with the data that is parsed.
|
|
static ElementPtr fromWire(const std::string& s);
|
|
//@}
|
|
|
|
/// @brief Remove all empty maps and lists from this Element and its
|
|
/// descendants.
|
|
- void removeEmptyContainersRecursively() {
|
|
- if (type_ == list || type_ == map) {
|
|
- size_t s(size());
|
|
- for (size_t i = 0; i < s; ++i) {
|
|
- // Get child.
|
|
- ElementPtr child;
|
|
- if (type_ == list) {
|
|
- child = getNonConst(i);
|
|
- } else if (type_ == map) {
|
|
- std::string const key(get(i)->stringValue());
|
|
- // The ElementPtr - ConstElementPtr disparity between
|
|
- // ListElement and MapElement is forcing a const cast here.
|
|
- // It's undefined behavior to modify it after const casting.
|
|
- // The options are limited. I've tried templating, moving
|
|
- // this function from a member function to free-standing and
|
|
- // taking the Element template as argument. I've tried
|
|
- // making it a virtual function with overridden
|
|
- // implementations in ListElement and MapElement. Nothing
|
|
- // works.
|
|
- child = boost::const_pointer_cast<Element>(get(key));
|
|
- }
|
|
-
|
|
- // Makes no sense to continue for non-container children.
|
|
- if (child->getType() != list && child->getType() != map) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- // Recurse if not empty.
|
|
- if (!child->empty()){
|
|
- child->removeEmptyContainersRecursively();
|
|
- }
|
|
-
|
|
- // When returning from recursion, remove if empty.
|
|
- if (child->empty()) {
|
|
- remove(i);
|
|
- --i;
|
|
- --s;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
+ ///
|
|
+ /// @param level nesting level.
|
|
+ void removeEmptyContainersRecursively(unsigned level = MAX_NESTING_LEVEL);
|
|
};
|
|
|
|
/// Notes: IntElement type is changed to int64_t.
|
|
@@ -622,8 +783,10 @@ public:
|
|
bool getValue(int64_t& t) const { t = i; return (true); }
|
|
using Element::setValue;
|
|
bool setValue(long long int v) { i = v; return (true); }
|
|
- void toJSON(std::ostream& ss) const;
|
|
- bool equals(const Element& other) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
};
|
|
|
|
/// @brief Wrapper over int128_t
|
|
@@ -655,13 +818,20 @@ public:
|
|
|
|
/// @brief Converts the Element to JSON format and appends it to the given
|
|
/// stringstream.
|
|
- void toJSON(std::ostream& ss) const override;
|
|
+ ///
|
|
+ /// @param ss The output stream where to append the JSON format.
|
|
+ /// @param level The maximum level of recursion. Ignored.
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const override;
|
|
|
|
/// @brief Checks whether the other Element is equal.
|
|
///
|
|
+ /// @param other The other element to compare with.
|
|
+ /// @param level The maximum level of recursion. Ignored.
|
|
/// @return true if the other ElementPtr has the same value and the same
|
|
/// type (or a different and compatible type), false otherwise.
|
|
- bool equals(const Element& other) const override;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const override;
|
|
|
|
private:
|
|
/// @brief the underlying stored value
|
|
@@ -679,8 +849,10 @@ public:
|
|
bool getValue(double& t) const { t = d; return (true); }
|
|
using Element::setValue;
|
|
bool setValue(const double v) { d = v; return (true); }
|
|
- void toJSON(std::ostream& ss) const;
|
|
- bool equals(const Element& other) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
};
|
|
|
|
class BoolElement : public Element {
|
|
@@ -694,16 +866,20 @@ public:
|
|
bool getValue(bool& t) const { t = b; return (true); }
|
|
using Element::setValue;
|
|
bool setValue(const bool v) { b = v; return (true); }
|
|
- void toJSON(std::ostream& ss) const;
|
|
- bool equals(const Element& other) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
};
|
|
|
|
class NullElement : public Element {
|
|
public:
|
|
NullElement(const Position& pos = ZERO_POSITION())
|
|
: Element(null, pos) {}
|
|
- void toJSON(std::ostream& ss) const;
|
|
- bool equals(const Element& other) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
};
|
|
|
|
class StringElement : public Element {
|
|
@@ -717,8 +893,10 @@ public:
|
|
bool getValue(std::string& t) const { t = s; return (true); }
|
|
using Element::setValue;
|
|
bool setValue(const std::string& v) { s = v; return (true); }
|
|
- void toJSON(std::ostream& ss) const;
|
|
- bool equals(const Element& other) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
};
|
|
|
|
class ListElement : public Element {
|
|
@@ -748,10 +926,12 @@ public:
|
|
void add(ElementPtr e) { l.push_back(e); }
|
|
using Element::remove;
|
|
void remove(int i) { l.erase(l.begin() + i); }
|
|
- void toJSON(std::ostream& ss) const;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
size_t size() const { return (l.size()); }
|
|
bool empty() const { return (l.empty()); }
|
|
- bool equals(const Element& other) const;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const;
|
|
|
|
/// @brief Sorts the elements inside the list.
|
|
///
|
|
@@ -820,7 +1000,8 @@ public:
|
|
bool contains(const std::string& s) const override {
|
|
return (m.find(s) != m.end());
|
|
}
|
|
- void toJSON(std::ostream& ss) const override;
|
|
+ void toJSON(std::ostream& ss,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const override;
|
|
|
|
// we should name the two finds better...
|
|
// find the element at id; raises TypeError if one of the
|
|
@@ -842,46 +1023,60 @@ public:
|
|
return (m.size());
|
|
}
|
|
|
|
- bool equals(const Element& other) const override;
|
|
+ bool equals(const Element& other,
|
|
+ unsigned level = MAX_NESTING_LEVEL) const override;
|
|
|
|
bool empty() const override { return (m.empty()); }
|
|
};
|
|
|
|
-/// Checks whether the given ElementPtr is a NULL pointer
|
|
+/// Checks whether the given ElementPtr is a null pointer
|
|
/// @param p The ElementPtr to check
|
|
-/// @return true if it is NULL, false if not.
|
|
+/// @return true if it is null, false if not.
|
|
bool isNull(ConstElementPtr p);
|
|
|
|
///
|
|
/// @brief Remove all values from the first ElementPtr that are
|
|
/// equal in the second. Both ElementPtrs MUST be MapElements
|
|
+///
|
|
/// The use for this function is to end up with a MapElement that
|
|
/// only contains new and changed values (for ModuleCCSession and
|
|
/// configuration update handlers)
|
|
-/// Raises a TypeError if a or b are not MapElements
|
|
+///
|
|
+/// @param a Pointer to the first element.
|
|
+/// @param b Pointer to the second element.
|
|
+/// @throw TypeError if a or b are not MapElements
|
|
void removeIdentical(ElementPtr a, ConstElementPtr b);
|
|
|
|
/// @brief Create a new ElementPtr from the first ElementPtr, removing all
|
|
/// values that are equal in the second. Both ElementPtrs MUST be MapElements.
|
|
-/// The returned ElementPtr will be a MapElement that only contains new and
|
|
-/// changed values (for ModuleCCSession and configuration update handlers).
|
|
-/// Raises a TypeError if a or b are not MapElements
|
|
+///
|
|
+/// @param a Pointer to the first element.
|
|
+/// @param b Pointer to the second element.
|
|
+/// @throw TypeError if a or b are not MapElements
|
|
+/// @return ElementPtr will be a MapElement that only contains new and changed
|
|
+/// values (for ModuleCCSession and configuration update handlers).
|
|
ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);
|
|
|
|
-/// @brief Merges the data from other into element. (on the first level). Both
|
|
-/// elements must be MapElements. Every string, value pair in other is copied
|
|
-/// into element (the ElementPtr of value is copied, this is not a new object)
|
|
-/// Unless the value is a NullElement, in which case the key is removed from
|
|
-/// element, rather than setting the value to the given NullElement.
|
|
+/// @brief Merges the data from other into element. (on the first level).
|
|
+
|
|
+/// Both elements must be MapElements. Every string, value pair in
|
|
+/// other is copied into element (the ElementPtr of value is copied,
|
|
+/// this is not a new object) Unless the value is a NullElement, in
|
|
+/// which case the key is removed from element, rather than setting
|
|
+/// the value to the given NullElement.
|
|
/// This way, we can remove values from for instance maps with configuration
|
|
/// data (which would then result in reverting back to the default).
|
|
-/// Raises a TypeError if either ElementPtr is not a MapElement
|
|
+///
|
|
+/// @param element Pointer to the Element holding data.
|
|
+/// @param other Pointer to the other / from Element.
|
|
+/// @throw TypeError if either ElementPtr is not a MapElement
|
|
void merge(ElementPtr element, ConstElementPtr other);
|
|
|
|
/// @brief Function used to check if two MapElements refer to the same
|
|
-/// configuration data. It can check if the two MapElements have the same or
|
|
-/// have equivalent value for some members.
|
|
-/// e.g.
|
|
+/// configuration data.
|
|
+///
|
|
+/// It can check if the two MapElements have the same or have
|
|
+/// equivalent value for some members. e.g.
|
|
/// (
|
|
/// left->get("prefix")->stringValue() == right->get("prefix")->stringValue() &&
|
|
/// left->get("prefix-len")->intValue() == right->get("prefix-len")->intValue() &&
|
|
@@ -925,7 +1120,7 @@ typedef std::vector<FunctionMap> HierarchyDescriptor;
|
|
|
|
/// @brief Merges the diff data by adding the missing elements from 'other'
|
|
/// to 'element' (recursively). Both elements must be the same Element type.
|
|
-/// Raises a TypeError if elements are not the same Element type.
|
|
+///
|
|
/// @note
|
|
/// for non map and list elements the values are updated with the new values
|
|
/// for maps:
|
|
@@ -941,13 +1136,15 @@ typedef std::vector<FunctionMap> HierarchyDescriptor;
|
|
/// identification keys.
|
|
/// @param key The container holding the current element.
|
|
/// @param idx The level inside the hierarchy the current element is located.
|
|
+/// @param level The maximum level of recursion.
|
|
+/// @throw TypeError if elements are not the same Element type.
|
|
void mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
HierarchyDescriptor& hierarchy, std::string key,
|
|
- size_t idx = 0);
|
|
+ size_t idx = 0, unsigned level = Element::MAX_NESTING_LEVEL);
|
|
|
|
/// @brief Merges the diff data by removing the data present in 'other' from
|
|
/// 'element' (recursively). Both elements must be the same Element type.
|
|
-/// Raises a TypeError if elements are not the same Element type.
|
|
+////
|
|
/// for non map and list elements the values are set to NullElement
|
|
/// for maps:
|
|
/// - non map and list elements are removed from the map
|
|
@@ -962,14 +1159,15 @@ void mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
/// identification keys.
|
|
/// @param key The container holding the current element.
|
|
/// @param idx The level inside the hierarchy the current element is located.
|
|
+/// @param level The maximum level of recursion.
|
|
+/// @throw TypeError if elements are not the same Element type.
|
|
void mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
HierarchyDescriptor& hierarchy, std::string key,
|
|
- size_t idx = 0);
|
|
+ size_t idx = 0, unsigned level = Element::MAX_NESTING_LEVEL);
|
|
|
|
/// @brief Extends data by adding the specified 'extension' elements from
|
|
/// 'other' inside the 'container' element (recursively). Both elements must be
|
|
/// the same Element type.
|
|
-/// Raises a TypeError if elements are not the same Element type.
|
|
///
|
|
/// @param container The container holding the data that must be extended.
|
|
/// @param extension The name of the element that contains the data that must be
|
|
@@ -982,30 +1180,53 @@ void mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
/// @param idx The level inside the hierarchy the current element is located.
|
|
/// @param alter The flag which indicates if the current element should be
|
|
/// updated.
|
|
+/// @param level The maximum level of recursion.
|
|
+/// @throw TypeError if elements are not the same Element type.
|
|
void extend(const std::string& container, const std::string& extension,
|
|
ElementPtr& element, ElementPtr& other,
|
|
HierarchyDescriptor& hierarchy, std::string key, size_t idx = 0,
|
|
- bool alter = false);
|
|
+ bool alter = false, unsigned level = Element::MAX_NESTING_LEVEL);
|
|
|
|
/// @brief Copy the data up to a nesting level.
|
|
///
|
|
/// The copy is a deep copy so nothing is shared if it is not
|
|
/// under the given nesting level.
|
|
///
|
|
-/// @param from the pointer to the element to copy
|
|
-/// @param level nesting level (default is 100, 0 means shallow copy,
|
|
-/// negative means outbound and perhaps looping forever).
|
|
-/// @return a pointer to a fresh copy
|
|
+/// @note: copy is the ONLY method taking a level argument which make
|
|
+/// sense outside unit tests, and also which accepts the 0 value.
|
|
+///
|
|
+/// @param from the pointer to the element to copy.
|
|
+/// @param level nesting level (default is 100, 0 means shallow copy).
|
|
+/// @return Pointer to a fresh copy
|
|
/// @throw raises a BadValue is a null pointer occurs.
|
|
-ElementPtr copy(ConstElementPtr from, int level = 100);
|
|
+ElementPtr copy(ConstElementPtr from, unsigned level = Element::MAX_NESTING_LEVEL);
|
|
|
|
-/// @brief Compares the data with other using unordered lists
|
|
+/// @brief Compares the data with other using unordered lists.
|
|
///
|
|
/// This comparison function handles lists (JSON arrays) as
|
|
/// unordered multi sets (multi means an item can occurs more
|
|
/// than once as soon as it occurs the same number of times).
|
|
+///
|
|
+/// @param a Pointer to the first element.
|
|
+/// @param b Pointer to the second element.
|
|
+/// @return Result of loose comparison.
|
|
bool isEquivalent(ConstElementPtr a, ConstElementPtr b);
|
|
|
|
+/// @brief Check if the data is circular.
|
|
+///
|
|
+/// @param element The @c ConstElementPtr object to check.
|
|
+/// @return True if the argument is circular, false otherwise.
|
|
+bool IsCircular(ConstElementPtr element);
|
|
+
|
|
+/// @brief Compute the nesting depth.
|
|
+///
|
|
+/// @param element The @c ConstElementPtr object.
|
|
+/// @param max_depth Maximal nesting depth.
|
|
+/// @return The nesting depth or max_depth if the object has deeper nesting
|
|
+/// including being circular.
|
|
+unsigned getNestDepth(ConstElementPtr element,
|
|
+ unsigned max_depth = Element::MAX_NESTING_LEVEL);
|
|
+
|
|
/// @brief Pretty prints the data into stream.
|
|
///
|
|
/// This operator converts the @c ConstElementPtr into a string and
|
|
@@ -1013,23 +1234,23 @@ bool isEquivalent(ConstElementPtr a, ConstElementPtr b);
|
|
/// indentation @c indent and add at each level @c step spaces.
|
|
/// For maps if there is a comment property it is printed first.
|
|
///
|
|
-/// @param element A @c ConstElementPtr to pretty print
|
|
-/// @param out A @c std::ostream on which the print operation is performed
|
|
-/// @param indent An initial number of spaces to add each new line
|
|
-/// @param step A number of spaces to add to indentation at a new level
|
|
+/// @param element A @c ConstElementPtr to pretty print.
|
|
+/// @param out A @c std::ostream on which the print operation is performed.
|
|
+/// @param indent An initial number of spaces to add each new line.
|
|
+/// @param step A number of spaces to add to indentation at a new level.
|
|
void prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
unsigned indent = 0, unsigned step = 2);
|
|
|
|
-/// @brief Pretty prints the data into string
|
|
+/// @brief Pretty prints the data into string.
|
|
///
|
|
/// This operator converts the @c ConstElementPtr into a string with
|
|
/// an initial indentation @c indent and add at each level @c step spaces.
|
|
/// For maps if there is a comment property it is printed first.
|
|
///
|
|
-/// @param element A @c ConstElementPtr to pretty print
|
|
-/// @param indent An initial number of spaces to add each new line
|
|
-/// @param step A number of spaces to add to indentation at a new level
|
|
-/// @return a string where element was pretty printed
|
|
+/// @param element A @c ConstElementPtr to pretty print.
|
|
+/// @param indent An initial number of spaces to add each new line.
|
|
+/// @param step A number of spaces to add to indentation at a new level.
|
|
+/// @return a string where element was pretty printed.
|
|
std::string prettyPrint(ConstElementPtr element,
|
|
unsigned indent = 0, unsigned step = 2);
|
|
|
|
@@ -1061,8 +1282,31 @@ std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
|
|
/// parameter @c out after the insertion operation.
|
|
std::ostream& operator<<(std::ostream& out, const Element& e);
|
|
|
|
+/// @brief Test equality.
|
|
+///
|
|
+/// @param a First element.
|
|
+/// @param b Second Element.
|
|
+/// @return True when the two elements are equal, false otherwise.
|
|
bool operator==(const Element& a, const Element& b);
|
|
+
|
|
+/// @brief Test inequality.
|
|
+///
|
|
+/// @param a First element.
|
|
+/// @param b Second Element.
|
|
+/// @return True when the two elements are not equal, false otherwise.
|
|
bool operator!=(const Element& a, const Element& b);
|
|
+
|
|
+/// @brief Test less than.
|
|
+///
|
|
+/// @note: both arguments must have the same supported type i.e. integer,
|
|
+/// double, boolean or string.
|
|
+///
|
|
+/// @param a First element.
|
|
+/// @param b Second Element.
|
|
+/// @return True when the value of the first element is less than the value
|
|
+/// of the second element.
|
|
+/// @throw BadValue when arguments have different type or the type is not
|
|
+/// supported.
|
|
bool operator<(const Element& a, const Element& b);
|
|
|
|
} // namespace data
|
|
diff --git a/src/lib/process/redact_config.cc b/src/lib/process/redact_config.cc
|
|
index 2127c724d4..10ae6fec73 100644
|
|
--- a/src/lib/process/redact_config.cc
|
|
+++ b/src/lib/process/redact_config.cc
|
|
@@ -18,10 +18,14 @@ namespace {
|
|
|
|
template <typename ElementPtrType>
|
|
ElementPtrType
|
|
-redact(ElementPtrType const& element, list<string> json_path, string obscure) {
|
|
+ redact(ElementPtrType const& element, list<string> json_path,
|
|
+ string obscure, unsigned level) {
|
|
if (!element) {
|
|
isc_throw(BadValue, "redact() got a null pointer");
|
|
}
|
|
+ if (level == 0) {
|
|
+ isc_throw(BadValue, "redact() elements nested too deeply");
|
|
+ }
|
|
|
|
string const next_key(json_path.empty() ? string() : json_path.front());
|
|
ElementPtr result;
|
|
@@ -36,9 +40,9 @@ redact(ElementPtrType const& element, list<string> json_path, string obscure) {
|
|
// Then redact all children.
|
|
result = Element::createList();
|
|
for (ElementPtr const& child : element->listValue()) {
|
|
- result->add(redact(child, json_path, obscure));
|
|
+ result->add(redact(child, json_path, obscure, level - 1));
|
|
}
|
|
- return result;
|
|
+ return (result);
|
|
}
|
|
} else if (element->getType() == Element::map) {
|
|
// If we are looking for anything or if we have reached the end of a
|
|
@@ -64,23 +68,25 @@ redact(ElementPtrType const& element, list<string> json_path, string obscure) {
|
|
result->set(key, value);
|
|
} else {
|
|
// We are looking for anything '*' so redact further.
|
|
- result->set(key, redact(value, json_path, obscure));
|
|
+ result->set(key, redact(value, json_path, obscure,
|
|
+ level - 1));
|
|
}
|
|
}
|
|
}
|
|
- return result;
|
|
+ return (result);
|
|
} else {
|
|
ConstElementPtr child(element->get(next_key));
|
|
if (child) {
|
|
- result = isc::data::copy(element, 1);
|
|
+ result = isc::data::copy(element, 1U);
|
|
json_path.pop_front();
|
|
- result->set(next_key, redact(child, json_path, obscure));
|
|
- return result;
|
|
+ result->set(next_key,
|
|
+ redact(child, json_path, obscure, level - 1));
|
|
+ return (result);
|
|
}
|
|
}
|
|
}
|
|
|
|
- return element;
|
|
+ return (element);
|
|
}
|
|
|
|
} // namespace
|
|
@@ -90,8 +96,8 @@ namespace process {
|
|
|
|
ConstElementPtr
|
|
redactConfig(ConstElementPtr const& element, list<string> const& json_path,
|
|
- string obscure) {
|
|
- return redact(element, json_path, obscure);
|
|
+ string obscure, unsigned max_nesting_depth) {
|
|
+ return (redact(element, json_path, obscure, max_nesting_depth));
|
|
}
|
|
|
|
} // namespace process
|
|
diff --git a/src/lib/process/redact_config.h b/src/lib/process/redact_config.h
|
|
index 0b66e22970..6ea5d3a06d 100644
|
|
--- a/src/lib/process/redact_config.h
|
|
+++ b/src/lib/process/redact_config.h
|
|
@@ -26,13 +26,15 @@ namespace process {
|
|
/// configuration and smaller subtrees in recursive calls.
|
|
/// @param json_path JSON path to redact
|
|
/// @param obscure new value of secrets / passwords
|
|
+/// @param max_nesting_depth maximum nesting depth
|
|
///
|
|
/// @return a copy of the config where passwords and secrets were replaced by
|
|
/// asterisks so it can be safely logged to an unprivileged place.
|
|
isc::data::ConstElementPtr
|
|
redactConfig(isc::data::ConstElementPtr const& element,
|
|
std::list<std::string> const& json_path = {"*"},
|
|
- std::string obscure = "*****");
|
|
+ std::string obscure = "*****",
|
|
+ unsigned max_nesting_depth = isc::data::Element::MAX_NESTING_LEVEL);
|
|
|
|
} // namespace process
|
|
} // namespace isc
|
|
commit 316e8d04aedf9392bf9a2edcb35bf2a719c17b13
|
|
Author: Razvan Becheriu <razvan@isc.org>
|
|
Date: Thu Mar 12 14:56:47 2026 +0200
|
|
|
|
[#4388] backport #4365 to v3_0
|
|
|
|
[Martin Osvald <mosvald@redhat.com>]
|
|
NOTE: Tests were removed.
|
|
|
|
diff --git a/src/bin/agent/simple_parser.cc b/src/bin/agent/simple_parser.cc
|
|
index b4564fa5ce..182abcbf06 100644
|
|
--- a/src/bin/agent/simple_parser.cc
|
|
+++ b/src/bin/agent/simple_parser.cc
|
|
@@ -16,6 +16,7 @@
|
|
#include <http/basic_auth_config.h>
|
|
|
|
using namespace isc::data;
|
|
+using namespace isc::dhcp;
|
|
using namespace isc::asiolink;
|
|
using namespace isc::config;
|
|
|
|
@@ -151,6 +152,10 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
|
|
if (ctrl_sockets) {
|
|
auto const& sockets_map = ctrl_sockets->mapValue();
|
|
for (auto const& cs : sockets_map) {
|
|
+ if (!cs.second->get("socket-name")) {
|
|
+ isc_throw(DhcpConfigError, "missing parameter 'socket-name' from '" << cs.first
|
|
+ << "' service map (" << cs.second->getPosition() << ")");
|
|
+ }
|
|
// Add a validated socket name so we can suppress it in
|
|
// toElement() but don't have to revalidate it every time we
|
|
// want to use it.
|
|
commit f1647a5b6d531c1cac4063bef22c4d97568a788e
|
|
Author: Razvan Becheriu <razvan@isc.org>
|
|
Date: Fri Mar 13 11:54:10 2026 +0200
|
|
|
|
[#4402] bump up library versions for 3.0.3 release
|
|
|
|
[Martin Osvald <mosvald@redhat.com>]
|
|
NOTE: Version and KEA_HOOKS_VERSION did not match.
|
|
|
|
diff --git a/src/lib/cc/meson.build b/src/lib/cc/meson.build
|
|
index c22a1a1..b9924f7 100644
|
|
--- a/src/lib/cc/meson.build
|
|
+++ b/src/lib/cc/meson.build
|
|
@@ -21,7 +21,7 @@ kea_cc_lib = shared_library(
|
|
install_rpath: INSTALL_RPATH,
|
|
build_rpath: BUILD_RPATH,
|
|
link_with: LIBS_BUILT_SO_FAR,
|
|
- version: '82.0.0',
|
|
+ version: '83.0.0',
|
|
)
|
|
LIBS_BUILT_SO_FAR = [kea_cc_lib] + LIBS_BUILT_SO_FAR
|
|
subdir('tests')
|
|
diff --git a/src/lib/config/meson.build b/src/lib/config/meson.build
|
|
index 803cdfb..efd11a7 100644
|
|
--- a/src/lib/config/meson.build
|
|
+++ b/src/lib/config/meson.build
|
|
@@ -20,7 +20,7 @@ kea_config_lib = shared_library(
|
|
install_rpath: INSTALL_RPATH,
|
|
build_rpath: BUILD_RPATH,
|
|
link_with: LIBS_BUILT_SO_FAR,
|
|
- version: '83.0.0',
|
|
+ version: '84.0.0',
|
|
cpp_args: [f'-DCONTROL_SOCKET_DIR="@RUNSTATEDIR_INSTALLED@"'],
|
|
)
|
|
LIBS_BUILT_SO_FAR = [kea_config_lib] + LIBS_BUILT_SO_FAR
|
|
diff --git a/src/lib/hooks/hooks.h b/src/lib/hooks/hooks.h
|
|
index 27be442..222c2e5 100644
|
|
--- a/src/lib/hooks/hooks.h
|
|
+++ b/src/lib/hooks/hooks.h
|
|
@@ -12,8 +12,15 @@
|
|
|
|
namespace {
|
|
|
|
-// Version 30001 of the hooks framework, set for Kea 3.0.1
|
|
-const int KEA_HOOKS_VERSION = 30001;
|
|
+// [Martin Osvald <mosvald@redhat.com>]
|
|
+// NOTE: I am not sure how many custom hooks exist that compile different
|
|
+// code depending on the value of KEA_HOOKS_VERSION and expect
|
|
+// specific functionality to be present.
|
|
+// We have not backported the changes that led to the bump to 30002.
|
|
+// I am concerned that a SONAME version bump alone may not be
|
|
+// sufficient with changes fixing CVE-2026-3608.
|
|
+// Version 30003 of the hooks framework, set for Kea 3.0.3
|
|
+const int KEA_HOOKS_VERSION = 30003;
|
|
|
|
// Names of the framework functions.
|
|
const char* const LOAD_FUNCTION_NAME = "load";
|
|
diff --git a/src/lib/hooks/meson.build b/src/lib/hooks/meson.build
|
|
index 51256d7..bc3b977 100644
|
|
--- a/src/lib/hooks/meson.build
|
|
+++ b/src/lib/hooks/meson.build
|
|
@@ -23,7 +23,7 @@ kea_hooks_lib = shared_library(
|
|
install_rpath: INSTALL_RPATH,
|
|
build_rpath: BUILD_RPATH,
|
|
link_with: LIBS_BUILT_SO_FAR,
|
|
- version: '119.0.0',
|
|
+ version: '121.0.0',
|
|
)
|
|
LIBS_BUILT_SO_FAR = [kea_hooks_lib] + LIBS_BUILT_SO_FAR
|
|
subdir('tests')
|
|
diff --git a/src/lib/process/meson.build b/src/lib/process/meson.build
|
|
index ce49503..7ffaa74 100644
|
|
--- a/src/lib/process/meson.build
|
|
+++ b/src/lib/process/meson.build
|
|
@@ -22,7 +22,7 @@ kea_process_lib = shared_library(
|
|
install_rpath: INSTALL_RPATH,
|
|
build_rpath: BUILD_RPATH,
|
|
link_with: LIBS_BUILT_SO_FAR,
|
|
- version: '90.0.0',
|
|
+ version: '91.0.0',
|
|
)
|
|
LIBS_BUILT_SO_FAR = [kea_process_lib] + LIBS_BUILT_SO_FAR
|
|
subdir('testutils')
|
|
diff --git a/src/lib/util/meson.build b/src/lib/util/meson.build
|
|
index d3e1efa..3a6d85d 100644
|
|
--- a/src/lib/util/meson.build
|
|
+++ b/src/lib/util/meson.build
|
|
@@ -25,7 +25,7 @@ kea_util_lib = shared_library(
|
|
install_rpath: INSTALL_RPATH,
|
|
build_rpath: BUILD_RPATH,
|
|
link_with: LIBS_BUILT_SO_FAR,
|
|
- version: '101.0.0',
|
|
+ version: '102.0.0',
|
|
)
|
|
subdir('io')
|
|
subdir('unittests')
|