Commit 8390f9de authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[5110_fix] re-applying patch made on trac5110

parent 9eadd3f9
......@@ -64,7 +64,10 @@ libd2_la_SOURCES += d2_log.cc d2_log.h
libd2_la_SOURCES += d2_process.cc d2_process.h
libd2_la_SOURCES += d2_config.cc d2_config.h
libd2_la_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
libd2_la_SOURCES += d2_lexer.ll location.hh position.hh stack.hh
libd2_la_SOURCES += d2_parser.cc d2_parser.h
libd2_la_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h
libd2_la_SOURCES += d2_simple_parser.cc d2_simple_parser.h
libd2_la_SOURCES += d2_update_message.cc d2_update_message.h
libd2_la_SOURCES += d2_update_mgr.cc d2_update_mgr.h
libd2_la_SOURCES += d2_zone.cc d2_zone.h
......@@ -73,6 +76,7 @@ libd2_la_SOURCES += nc_add.cc nc_add.h
libd2_la_SOURCES += nc_remove.cc nc_remove.h
libd2_la_SOURCES += nc_trans.cc nc_trans.h
libd2_la_SOURCES += d2_controller.cc d2_controller.h
libd2_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
nodist_libd2_la_SOURCES = d2_messages.h d2_messages.cc
EXTRA_DIST += d2_messages.mes
......@@ -115,3 +119,31 @@ endif
kea_dhcp_ddnsdir = $(pkgdatadir)
kea_dhcp_ddns_DATA = dhcp-ddns.spec
if GENERATE_PARSER
parser: d2_lexer.cc location.hh position.hh stack.hh d2_parser.cc d2_parser.h
@echo "Flex/bison files regenerated"
# --- Flex/Bison stuff below --------------------------------------------------
# When debugging grammar issues, it's useful to add -v to bison parameters.
# bison will generate parser.output file that explains the whole grammar.
# It can be used to manually follow what's going on in the parser.
# This is especially useful if yydebug_ is set to 1 as that variable
# will cause parser to print out its internal state.
# Call flex with -s to check that the default rule can be suppressed
# Call bison with -W to get warnings like unmarked empty rules
# Note C++11 deprecated register still used by flex < 2.6.0
location.hh position.hh stack.hh d2_parser.cc d2_parser.h: d2_parser.yy
$(YACC) --defines=d2_parser.h --report=all --report-file=d2_parser.report -o d2_parser.cc d2_parser.yy
d2_lexer.cc: d2_lexer.ll
$(LEX) --prefix d2_parser_ -o d2_lexer.cc d2_lexer.ll
else
parser location.hh position.hh stack.hh d2_parser.cc d2_parser.h d2_lexer.cc:
@echo Parser generation disabled. Configure with --enable-generate-parser to enable it.
endif
......@@ -8,6 +8,7 @@
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <d2/d2_simple_parser.h>
#include <util/encode/hex.h>
#include <boost/foreach.hpp>
......@@ -197,96 +198,164 @@ D2CfgMgr::getConfigSummary(const uint32_t) {
return (getD2Params()->getConfigSummary());
}
void
D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
// Base class build creates parses and invokes build on each parser.
// This populate the context scalar stores with all of the parameters.
DCfgMgrBase::buildParams(params_config);
// Fetch and validate the parameters from the context to create D2Params.
// We validate them here rather than just relying on D2Param constructor
// so we can spit out config text position info with errors.
namespace {
// Fetch and validate ip_address.
D2CfgContextPtr context = getD2CfgContext();
isc::dhcp::StringStoragePtr strings = context->getStringStorage();
asiolink::IOAddress ip_address(D2Params::DFT_IP_ADDRESS);
template <typename int_type> int_type
getInt(const std::string& name, isc::data::ConstElementPtr value) {
int64_t val_int = value->intValue();
if ((val_int < std::numeric_limits<int_type>::min()) ||
(val_int > std::numeric_limits<int_type>::max())) {
isc_throw(D2CfgError, "out of range value (" << val_int
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
return (static_cast<int_type>(val_int));
}
std::string ip_address_str = strings->getOptionalParam("ip-address",
D2Params::
DFT_IP_ADDRESS);
isc::asiolink::IOAddress
getIOAddress(const std::string& name, isc::data::ConstElementPtr value) {
std::string str = value->stringValue();
try {
ip_address = asiolink::IOAddress(ip_address_str);
return (isc::asiolink::IOAddress(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "IP address invalid : \""
<< ip_address_str << "\" ("
<< strings->getPosition("ip-address") << ")");
}
if ((ip_address.toText() == "0.0.0.0") || (ip_address.toText() == "::")) {
isc_throw(D2CfgError, "IP address cannot be \"" << ip_address << "\" ("
<< strings->getPosition("ip-address") << ")");
isc_throw(D2CfgError, "invalid address (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
// Fetch and validate port.
isc::dhcp::Uint32StoragePtr ints = context->getUint32Storage();
uint32_t port = ints->getOptionalParam("port", D2Params::DFT_PORT);
if (port == 0) {
isc_throw(D2CfgError, "port cannot be 0 ("
<< ints->getPosition("port") << ")");
}
// Fetch and validate dns_server_timeout.
uint32_t dns_server_timeout
= ints->getOptionalParam("dns-server-timeout",
D2Params::DFT_DNS_SERVER_TIMEOUT);
if (dns_server_timeout < 1) {
isc_throw(D2CfgError, "DNS server timeout must be larger than 0 ("
<< ints->getPosition("dns-server-timeout") << ")");
dhcp_ddns::NameChangeProtocol
getProtocol(const std::string& name, isc::data::ConstElementPtr value) {
std::string str = value->stringValue();
try {
return (dhcp_ddns::stringToNcrProtocol(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError,
"invalid NameChangeRequest protocol (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
// Fetch and validate ncr_protocol.
dhcp_ddns::NameChangeProtocol ncr_protocol;
dhcp_ddns::NameChangeFormat
getFormat(const std::string& name, isc::data::ConstElementPtr value) {
std::string str = value->stringValue();
try {
ncr_protocol = dhcp_ddns::
stringToNcrProtocol(strings->
getOptionalParam("ncr-protocol",
D2Params::
DFT_NCR_PROTOCOL));
return (dhcp_ddns::stringToNcrFormat(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "ncr-protocol : "
<< ex.what() << " ("
<< strings->getPosition("ncr-protocol") << ")");
isc_throw(D2CfgError,
"invalid NameChangeRequest format (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
if (ncr_protocol != dhcp_ddns::NCR_UDP) {
isc_throw(D2CfgError, "ncr-protocol : "
<< dhcp_ddns::ncrProtocolToString(ncr_protocol)
<< " is not yet supported ("
<< strings->getPosition("ncr-protocol") << ")");
}
} // anon
// Fetch and validate ncr_format.
dhcp_ddns::NameChangeFormat ncr_format;
void
D2CfgMgr::parseElement(const std::string& element_id,
isc::data::ConstElementPtr element) {
try {
ncr_format = dhcp_ddns::
stringToNcrFormat(strings->
getOptionalParam("ncr-format",
D2Params::
DFT_NCR_FORMAT));
// Get D2 specific context.
D2CfgContextPtr context = getD2CfgContext();
if ((element_id == "ip-address") ||
(element_id == "ncr-protocol") ||
(element_id == "ncr-format") ||
(element_id == "port") ||
(element_id == "dns-server-timeout")) {
// global scalar params require nothing extra be done
} else if (element_id == "tsig-keys") {
TSIGKeyInfoListParser parser;
context->setKeys(parser.parse(element));
} else if (element_id == "forward-ddns") {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
context->getKeys());
context->setForwardMgr(mgr);
} else if (element_id == "reverse-ddns") {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
context->getKeys());
context->setReverseMgr(mgr);
} else {
// Shouldn't occur if the JSON parser is doing its job.
isc_throw(D2CfgError, "Unsupported element: "
<< element_id << element->getPosition());
}
} catch (const D2CfgError& ex) {
// Should already have a specific error and position info
throw ex;
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "ncr-format : "
<< ex.what() << " ("
<< strings->getPosition("ncr-format") << ")");
isc_throw(D2CfgError, "element: " << element_id << " : " << ex.what()
<< element->getPosition());
}
};
void
D2CfgMgr::setCfgDefaults(isc::data::ElementPtr mutable_config) {
D2SimpleParser::setAllDefaults(mutable_config);
}
void
D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
// Base class build creates parses and invokes build on each parser.
// This populate the context scalar stores with all of the parameters.
DCfgMgrBase::buildParams(params_config);
// Fetch the parameters in the config, performing any logcial
// validation required.
asiolink::IOAddress ip_address(0);
uint32_t port = 0;
uint32_t dns_server_timeout = 0;
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
// Assumes that params_config has had defaults added
BOOST_FOREACH(isc::dhcp::ConfigPair param, params_config->mapValue()) {
std::string entry(param.first);
isc::data::ConstElementPtr value(param.second);
try {
if (entry == "ip-address") {
ip_address = getIOAddress(entry, value);
if ((ip_address.toText() == "0.0.0.0") ||
(ip_address.toText() == "::")) {
isc_throw(D2CfgError, "IP address cannot be \""
<< ip_address << "\""
<< " (" << value->getPosition() << ")");
}
} else if (entry == "port") {
port = getInt<uint32_t>(entry, value);
} else if (entry == "dns-server-timeout") {
dns_server_timeout = getInt<uint32_t>(entry, value);
} else if (entry == "ncr-protocol") {
ncr_protocol = getProtocol(entry, value);
if (ncr_protocol != dhcp_ddns::NCR_UDP) {
isc_throw(D2CfgError, "ncr-protocol : "
<< dhcp_ddns::ncrProtocolToString(ncr_protocol)
<< " is not yet supported "
<< " (" << value->getPosition() << ")");
}
} else if (entry == "ncr-format") {
ncr_format = getFormat(entry, value);
if (ncr_format != dhcp_ddns::FMT_JSON) {
isc_throw(D2CfgError, "NCR Format:"
<< dhcp_ddns::ncrFormatToString(ncr_format)
<< " is not yet supported"
<< " (" << value->getPosition() << ")");
}
} else {
isc_throw(D2CfgError,
"unsupported parameter '" << entry
<< " (" << value->getPosition() << ")");
}
} catch (const isc::data::TypeError&) {
isc_throw(D2CfgError,
"invalid value type specified for parameter '" << entry
<< " (" << value->getPosition() << ")");
}
if (ncr_format != dhcp_ddns::FMT_JSON) {
isc_throw(D2CfgError, "NCR Format:"
<< dhcp_ddns::ncrFormatToString(ncr_format)
<< " is not yet supported ("
<< strings->getPosition("ncr-format") << ")");
}
// Attempt to create the new client config. This ought to fly as
......@@ -294,44 +363,7 @@ D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout,
ncr_protocol, ncr_format));
context->getD2Params() = params;
}
isc::dhcp::ParserPtr
D2CfgMgr::createConfigParser(const std::string& config_id,
const isc::data::Element::Position& pos) {
// Get D2 specific context.
D2CfgContextPtr context = getD2CfgContext();
// Create parser instance based on element_id.
isc::dhcp::ParserPtr parser;
if ((config_id.compare("port") == 0) ||
(config_id.compare("dns-server-timeout") == 0)) {
parser.reset(new isc::dhcp::Uint32Parser(config_id,
context->getUint32Storage()));
} else if ((config_id.compare("ip-address") == 0) ||
(config_id.compare("ncr-protocol") == 0) ||
(config_id.compare("ncr-format") == 0)) {
parser.reset(new isc::dhcp::StringParser(config_id,
context->getStringStorage()));
} else if (config_id == "forward-ddns") {
parser.reset(new DdnsDomainListMgrParser("forward-ddns",
context->getForwardMgr(),
context->getKeys()));
} else if (config_id == "reverse-ddns") {
parser.reset(new DdnsDomainListMgrParser("reverse-ddns",
context->getReverseMgr(),
context->getKeys()));
} else if (config_id == "tsig-keys") {
parser.reset(new TSIGKeyInfoListParser("tsig-key-list",
context->getKeys()));
} else {
isc_throw(NotImplemented,
"parser error: D2CfgMgr parameter not supported : "
" (" << config_id << pos << ")");
}
return (parser);
getD2CfgContext()->getD2Params() = params;
}
}; // end of isc::dhcp namespace
......
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2017 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
......@@ -57,6 +57,12 @@ public:
return (forward_mgr_);
}
/// @brief Sets the forward domain list manager
/// @param forward_mgr pointer to the new forward manager
void setForwardMgr(DdnsDomainListMgrPtr forward_mgr) {
forward_mgr_ = forward_mgr;
}
/// @brief Fetches the reverse DNS domain list manager.
///
/// @return returns a pointer to the reverse manager.
......@@ -64,6 +70,12 @@ public:
return (reverse_mgr_);
}
/// @brief Sets the reverse domain list manager
/// @param reverse_mgr pointer to the new reverse manager
void setReverseMgr(DdnsDomainListMgrPtr reverse_mgr) {
reverse_mgr_ = reverse_mgr;
}
/// @brief Fetches the map of TSIG keys.
///
/// @return returns a pointer to the key map.
......@@ -71,6 +83,13 @@ public:
return (keys_);
}
/// @brief Sets the map of TSIG keys
///
/// @param keys pointer to the new TSIG key map
void setKeys(const TSIGKeyInfoMapPtr& keys) {
keys_ = keys;
}
protected:
/// @brief Copy constructor for use by derivations in clone().
D2CfgContext(const D2CfgContext& rhs);
......@@ -239,6 +258,29 @@ public:
virtual std::string getConfigSummary(const uint32_t selection);
protected:
/// @brief Parses an element using alternate parsers
///
/// Each element to be parsed is passed first into this method to allow
/// it to be processed by SimpleParser derivations if they've been
/// implemented. The method should return true if it has processed the
/// element or false if the element should be passed onto the original
/// DhcpConfigParer mechanisms. This method is invoked in both
/// @c DCfgMgrBase::buildParams() and DCfgMgrBase::buildAndCommit().
///
/// @param element_id name of the element as it is expected in the cfg
/// @param element value of the element as ElementPtr
virtual void parseElement(const std::string& element_id,
isc::data::ConstElementPtr element);
/// @brief Adds default values to the given config
///
/// Adds the D2 default values to the configuration Element map. This
/// method is invoked by @c DCfgMgrBase::paserConfig().
///
/// @param mutable_config - configuration to which defaults should be added
virtual void setCfgDefaults(isc::data::ElementPtr mutable_config);
/// @brief Performs the parsing of the given "params" element.
///
/// Iterates over the set of parameters, creating a parser based on the
......@@ -259,32 +301,6 @@ protected:
/// -# ncr_format is invalid, currently only FMT_JSON is supported
virtual void buildParams(isc::data::ConstElementPtr params_config);
/// @brief Given an element_id returns an instance of the appropriate
/// parser.
///
/// It is responsible for top-level or outermost DHCP-DDNS configuration
/// elements (see dhcp-ddns.spec):
/// -# ip_address
/// -# port
/// -# dns_server_timeout
/// -# ncr_protocol
/// -# ncr_format
/// -# tsig_keys
/// -# forward_ddns
/// -# reverse_ddns
///
/// @param element_id is the string name of the element as it will appear
/// in the configuration set.
/// @param pos position within the configuration text (or file) of element
/// to be parsed. This is passed for error messaging.
///
/// @return returns a ParserPtr to the parser instance.
/// @throw throws DCfgMgrBaseError if an error occurs.
virtual isc::dhcp::ParserPtr
createConfigParser(const std::string& element_id,
const isc::data::Element::Position& pos =
isc::data::Element::Position());
/// @brief Creates an new, blank D2CfgContext context
///
/// This method is used at the beginning of configuration process to
......
......@@ -13,7 +13,6 @@
#include <asiolink/io_error.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string/predicate.hpp>
......@@ -27,12 +26,6 @@ namespace d2 {
// *********************** D2Params *************************
const char *D2Params::DFT_IP_ADDRESS = "127.0.0.1";
const size_t D2Params::DFT_PORT = 53001;
const size_t D2Params::DFT_DNS_SERVER_TIMEOUT = 100;
const char *D2Params::DFT_NCR_PROTOCOL = "UDP";
const char *D2Params::DFT_NCR_FORMAT = "JSON";
D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
const size_t port,
const size_t dns_server_timeout,
......@@ -47,9 +40,8 @@ D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
}
D2Params::D2Params()
: ip_address_(isc::asiolink::IOAddress(DFT_IP_ADDRESS)),
port_(DFT_PORT),
dns_server_timeout_(DFT_DNS_SERVER_TIMEOUT),
: ip_address_(isc::asiolink::IOAddress("127.0.0.1")),
port_(53001), dns_server_timeout_(100),
ncr_protocol_(dhcp_ddns::NCR_UDP),
ncr_format_(dhcp_ddns::FMT_JSON) {
validateContents();
......@@ -189,9 +181,6 @@ TSIGKeyInfo::remakeKey() {
}
// *********************** DnsServerInfo *************************
const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";
DnsServerInfo::DnsServerInfo(const std::string& hostname,
isc::asiolink::IOAddress ip_address, uint32_t port,
bool enabled)
......@@ -336,120 +325,37 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
// *********************** TSIGKeyInfoParser *************************
TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
TSIGKeyInfoMapPtr keys)
: entry_name_(entry_name), keys_(keys), local_scalars_() {
if (!keys_) {
isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
" key storage cannot be null");
}
}
TSIGKeyInfoParser::~TSIGKeyInfoParser() {
}
void
TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
isc::dhcp::ConfigPair config_pair;
// For each element in the key configuration:
// 1. Create a parser for the element.
// 2. Invoke the parser's build method passing in the element's
// configuration.
// 3. Invoke the parser's commit method to store the element's parsed
// data to the parser's local storage.
BOOST_FOREACH (config_pair, key_config->mapValue()) {
isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
config_pair.second->
getPosition()));
parser->build(config_pair.second);
parser->commit();
}
std::string name;
std::string algorithm;
uint32_t digestbits = 0;
std::string secret;
std::map<std::string, isc::data::Element::Position> pos;
// Fetch the key's parsed scalar values from parser's local storage.
// Only digestbits is optional and doesn't throw when missing
try {
pos["name"] = local_scalars_.getParam("name", name);
pos["algorithm"] = local_scalars_.getParam("algorithm", algorithm);
pos["digest-bits"] = local_scalars_.getParam("digest-bits", digestbits,
DCfgContextBase::OPTIONAL);
pos["secret"] = local_scalars_.getParam("secret", secret);
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "TSIG Key incomplete : " << ex.what()
<< " (" << key_config->getPosition() << ")");
}
// Name cannot be blank.
if (name.empty()) {
isc_throw(D2CfgError, "TSIG key must specify name (" << pos["name"] << ")");
}
// Currently, the premise is that key storage is always empty prior to
// parsing so we are always adding keys never replacing them. Duplicates
// are not allowed and should be flagged as a configuration error.
if (keys_->find(name) != keys_->end()) {
isc_throw(D2CfgError, "Duplicate TSIG key name specified : " << name
<< " (" << pos["name"] << ")");
}
TSIGKeyInfoPtr
TSIGKeyInfoParser::parse(data::ConstElementPtr key_config) {
std::string name = getString(key_config, "name");
std::string algorithm = getString(key_config, "algorithm");
uint32_t digestbits = getInteger(key_config, "digest-bits");
std::string secret = getString(key_config, "secret");
// Algorithm must be valid.
try {
TSIGKeyInfo::stringToAlgorithmName(algorithm);
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "TSIG key : " << ex.what() << " (" << pos["algorithm"] << ")");
}
// Not zero Digestbits must be an integral number of octets, greater
// than 80 and the half of the full length
if (digestbits > 0) {
if ((digestbits % 8) != 0) {
isc_throw(D2CfgError, "Invalid TSIG key digest_bits specified : " <<
digestbits << " (" << pos["digest-bits"] << ")");
}
if (digestbits < 80) {
isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
digestbits << " (" << pos["digest-bits"] << ")");
}
if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)) {
if (digestbits < 112) {
isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
digestbits << " (" << pos["digest-bits"]
<< ")");
}
} else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)) {
if (digestbits < 128) {
isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
digestbits << " (" << pos["digest-bits"]
<< ")");
}
} else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR)) {
if (digestbits < 192) {
isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
digestbits << " (" << pos["digest-bits"]
<< ")");
}
} else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR)) {
if (digestbits < 256) {
isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
digestbits << " (" << pos["digest-bits"]
<< ")");
}
}
}
// Secret cannot be blank.
// Cryptolink lib doesn't offer any way to validate these. As long as it
// isn't blank we'll accept it. If the content is bad, the call to in
// TSIGKeyInfo::remakeKey() made in the TSIGKeyInfo ctor will throw.
// We'll deal with that below.
if (secret.empty()) {
isc_throw(D2CfgError, "TSIG key must specify secret (" << pos["secret"] << ")");
}
isc_throw(D2CfgError, "tsig-key : " << ex.what()
<< " (" << getPosition("algorithm", key_config) << ")");
}
// Non-zero digest-bits must be an integral number of octets, greater
// than 80 and at least half of the algorithm key length. It defaults
// to zero and JSON parsing ensures it's a multiple of 8.
if ((digestbits > 0) &&
((digestbits < 80) ||
(boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)
&& (digestbits < 112)) ||
(boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)