Commit b5b877d1 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'master' into trac2499

parents c4a5a733 2cbabde5
553. [func] stephen
Values of the parameters to access the DHCP server lease database
can now be set through the BIND 10 configuration mechanism.
(Trac #2559, git 6c6f405188cc02d2358e114c33daff58edabd52a)
552. [bug] shane
Build on Raspberry PI.
The main issue was use of char for reading from input streams,
which is incorrect, as EOF is returned as an int -1, which would
then get cast into a char -1.
A number of other minor issues were also fixed.
(Trac #2571, git 525333e187cc4bbbbde288105c9582c1024caa4a)
551. [bug] shane
Kill msgq if we cannot connect to it on startup.
When the boss process was unable to connect to the msgq, it would
exit. However, it would leave the msgq process running. This has
been fixed, and the msgq is now stopped in this case.
(Trac #2608, git 016925ef2437e0396127e135c937d3a55539d224)
550. [func] tomek
b10-dhcp4: The DHCPv4 server now generates a server identifier
the first time it is run. The identifier is preserved in a file
across server restarts.
b10-dhcp6: The server identifier is now preserved in a file across
server restarts.
(Trac #2597, git fa342a994de5dbefe32996be7eebe58f6304cff7)
549. [func] tomek
b10-dhcp6: It is now possible to specify that a configured subnet
is reachable locally over specified interface (see "interface"
parameter in Subnet6 configuration).
(Trac #2596, git a70f6172194a976b514cd7d67ce097bbca3c2798)
548. [func] vorner
The message queue daemon now appears on the bus. This has two
effects, one is it obeys logging configuration and logs to the
correct place like the rest of the modules. The other is it
appears in bindctl as module (but it doesn't have any commands or
configuration yet).
(Trac #2582, git ced31d8c5a0f2ca930b976d3caecfc24fc04634e)
547. [func]* vorner
The b10-loadzone now performs more thorough sanity check on the
loaded data. Some of the checks are now fatal and zone failing
them will be rejected.
(Trac #2436, git 48d999f1cb59f308f9f30ba2639521d2a5a85baa)
546. [func] marcin
DHCP option definitions can be now created using the
......
......@@ -282,7 +282,10 @@ AC_SUBST(PYTHON_LOGMSGPKG_DIR)
# This is python package paths commonly used in python tests. See
# README of log_messages for why it's included.
COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python"
# lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python:\$(abs_top_builddir)/src/lib/dns/python/.libs"
AC_SUBST(COMMON_PYTHON_PATH)
# Check for python development environments
......@@ -1214,6 +1217,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/server_common/tests/Makefile
src/lib/python/isc/sysinfo/Makefile
src/lib/python/isc/sysinfo/tests/Makefile
src/lib/python/isc/statistics/Makefile
src/lib/python/isc/statistics/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
......
......@@ -3485,18 +3485,34 @@ Dhcp4/subnet4 [] list (default)</screen>
src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
recompile:
<screen>
const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
const std::string HARDCODED_NETMASK = "255.255.255.0";
const uint32_t HARDCODED_LEASE_TIME = 60; // in seconds
const std::string HARDCODED_GATEWAY = "192.0.2.1";
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";</screen>
Lease database and configuration support is planned for end of 2012.
</para>
</section>
<section id="dhcp4-serverid">
<title>Server Identifier in DHCPv4</title>
<para>The DHCPv4 protocol uses a "server identifier" for clients to be able
to discriminate between several servers present on the same link: this
value is an IPv4 address of the server. When started for the first time,
the DHCPv4 server will choose one of its IPv4 addresses as its server-id,
and store the chosen value to a file. (The file is named b10-dhcp4-serverid and is
stored in the "local state directory". This is set during installation
when "configure" is run, and can be changed by using "--localstatedir"
on the "configure" command line.) That file will be read by the server
and the contained value used whenever the server is subsequently started.
</para>
<para>
It is unlikely that this parameter needs to be changed. If such a need
arises, please stop the server, edit the file and restart the server.
It is a text file that should contain an IPv4 address. Spaces are
ignored. No extra characters are allowed in this file.
</para>
</section>
<section id="dhcp4-std">
<title>Supported standards</title>
<para>The following standards and draft standards are currently
......@@ -3841,6 +3857,31 @@ Dhcp6/subnet6 [] list (default)</screen>
</note>
</section>
<section id="dhcp6-serverid">
<title>Server Identifier in DHCPv6</title>
<para>The DHCPv6 protocol uses a "server identifier" (also known
as a DUID) for clients to be able to discriminate between several
servers present on the same link. There are several types of
DUIDs defined, but RFC 3315 instructs servers to use DUID-LLT if
possible. This format consists of a link-layer (MAC) address and a
timestamp. When started for the first time, the DHCPv6 server will
automatically generate such a DUID and store the chosen value to
a file (The file is named b10-dhcp6-serverid and is stored in the
"local state directory". This is set during installation when
"configure" is run, and can be changed by using "--localstatedir"
on the "configure" command line.) That file will be read by the server
and the contained value used whenever the server is subsequently started.
</para>
<para>
It is unlikely that this parameter needs to be changed. If such a need
arises, please stop the server, edit the file and start the server
again. It is a text file that contains double digit hexadecimal values
separated by colons. This format is similar to typical MAC address
format. Spaces are ignored. No extra characters are allowed in this
file.
</para>
</section>
<section id="dhcp6-std">
<title>Supported DHCPv6 Standards</title>
<para>The following standards and draft standards are currently
......
......@@ -99,18 +99,17 @@ example_nsec3_inc.cc: $(srcdir)/testdata/example-nsec3-inc.zone
$(PYTHON) $(srcdir)/gen-query-testdata.py \
$(srcdir)/testdata/example-nsec3-inc.zone example_nsec3_inc.cc
testdata/example-base.sqlite3: testdata/example-base.zone
testdata/example-common-inc.zone: $(srcdir)/testdata/example-common-inc-template.zone
$(top_srcdir)/install-sh -c \
$(srcdir)/testdata/example-common-inc-template.zone \
testdata/example-common-inc.zone
testdata/example-base.sqlite3: testdata/example-base.zone testdata/example-common-inc.zone
$(SHELL) $(top_builddir)/src/bin/loadzone/run_loadzone.sh \
-c "{\"database_file\": \"$(builddir)/testdata/example-base.sqlite3\"}" \
example.com testdata/example-base.zone
testdata/example-nsec3.sqlite3: testdata/example-nsec3.zone
$(top_srcdir)/install-sh -c \
$(srcdir)/testdata/example-common-inc-template.zone \
testdata/example-common-inc.zone
testdata/example-nsec3.sqlite3: testdata/example-nsec3.zone testdata/example-common-inc.zone
$(SHELL) $(top_builddir)/src/bin/loadzone/run_loadzone.sh \
-c "{\"database_file\": \"$(builddir)/testdata/example-nsec3.sqlite3\"}" \
example.com testdata/example-nsec3.zone
......
......@@ -1286,12 +1286,12 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
createRequestPacket(request_message, IPPROTO_UDP);
// Modify the message.
delete io_message;
endpoint = IOEndpoint::create(IPPROTO_UDP,
IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
io_message = new IOMessage(request_renderer.getData(),
request_renderer.getLength(),
getDummyUnknownSocket(), *endpoint);
endpoint.reset(IOEndpoint::create(IPPROTO_UDP,
IOAddress(DEFAULT_REMOTE_ADDRESS),
53210));
io_message.reset(new IOMessage(request_renderer.getData(),
request_renderer.getLength(),
getDummyUnknownSocket(), *endpoint));
EXPECT_FALSE(dnsserv.hasAnswer());
}
......@@ -1716,9 +1716,20 @@ void
checkAddrPort(const struct sockaddr& actual_sa,
const string& expected_addr, uint16_t expected_port)
{
// ASIO does not set as_len, which is not a problem on most
// systems, but it will make getnameinfo() fail on NetBSD 4
// So we make a copy and if the field is available, we set it
const socklen_t sa_len = getSALength(actual_sa);
struct sockaddr_storage ss;
memcpy(&ss, &actual_sa, sa_len);
struct sockaddr* sa = convertSockAddr(&ss);
#ifdef HAVE_SA_LEN
sa->sa_len = sa_len;
#endif
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
const int error = getnameinfo(&actual_sa, getSALength(actual_sa), hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf),
const int error = getnameinfo(sa, sa_len, hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf),
NI_NUMERICHOST | NI_NUMERICSERV);
if (error != 0) {
isc_throw(isc::Unexpected, "getnameinfo failed: " <<
......
......@@ -491,6 +491,8 @@ class BoB:
# if we have been trying for "a while" give up
if (time.time() - cc_connect_start) > self.msgq_timeout:
if msgq_proc.process:
msgq_proc.process.kill()
logger.error(BIND10_CONNECTING_TO_CC_FAIL)
raise CChannelConnectError("Unable to connect to c-channel after 5 seconds")
......
......@@ -20,7 +20,10 @@ export PYTHON_EXEC
BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
# Note: lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
......
......@@ -20,7 +20,10 @@ export PYTHON_EXEC
DBUTIL_PATH=@abs_top_builddir@/src/bin/dbutil
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
# Note: lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
......
......@@ -18,6 +18,7 @@
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/option_space_container.h>
#include <util/encode/hex.h>
......@@ -49,9 +50,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
/// @brief auxiliary type used for storing element name and its parser
typedef pair<string, ConstElementPtr> ConfigPair;
/// @brief a factory method that will create a parser for a given element name
typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
......@@ -1607,6 +1605,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
factories["option-data"] = OptionDataListParser::factory;
factories["option-def"] = OptionDefListParser::factory;
factories["version"] = StringParser::factory;
factories["lease-database"] = DbAccessParser::factory;
FactoryMap::iterator f = factories.find(config_id);
if (f == factories.end()) {
......@@ -1661,13 +1660,18 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
// rollback informs whether error occured and original data
// have to be restored to global storages.
bool rollback = false;
// config_pair holds the details of the current parser when iterating over
// the parsers. It is declared outside the loops so in case of an error,
// the name of the failing parser can be retrieved in the "catch" clause.
ConfigPair config_pair;
try {
// Make parsers grouping.
const std::map<std::string, ConstElementPtr>& values_map =
config_set->mapValue();
BOOST_FOREACH(ConfigPair config_pair, values_map) {
BOOST_FOREACH(config_pair, values_map) {
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
.arg(config_pair.first);
if (config_pair.first == "subnet4") {
subnet_parser = parser;
......@@ -1702,16 +1706,19 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
}
} catch (const isc::Exception& ex) {
answer =
isc::config::createAnswer(1, string("Configuration parsing failed: ") + ex.what());
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
.arg(config_pair.first).arg(ex.what());
answer = isc::config::createAnswer(1,
string("Configuration parsing failed: ") + ex.what());
// An error occured, so make sure that we restore original data.
rollback = true;
} catch (...) {
// for things like bad_cast in boost::lexical_cast
answer =
isc::config::createAnswer(1, string("Configuration parsing failed"));
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
answer = isc::config::createAnswer(1,
string("Configuration parsing failed"));
// An error occured, so make sure that we restore original data.
rollback = true;
......@@ -1728,14 +1735,16 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
}
}
catch (const isc::Exception& ex) {
answer =
isc::config::createAnswer(2, string("Configuration commit failed: ") + ex.what());
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(2,
string("Configuration commit failed: ") + ex.what());
rollback = true;
} catch (...) {
// for things like bad_cast in boost::lexical_cast
answer =
isc::config::createAnswer(2, string("Configuration commit failed"));
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(2,
string("Configuration commit failed"));
rollback = true;
}
......
......@@ -55,13 +55,13 @@
{ "item_name": "code",
"item_type": "integer",
"item_optional": false,
"item_default": 0,
"item_default": 0
},
{ "item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": "",
"item_default": ""
},
{ "item_name": "array",
......@@ -73,7 +73,7 @@
{ "item_name": "record_types",
"item_type": "string",
"item_optional": false,
"item_default": "",
"item_default": ""
},
{ "item_name": "space",
......@@ -125,6 +125,44 @@
}
},
{ "item_name": "lease-database",
"item_type": "map",
"item_optional": false,
"item_default": {"type": "memfile"},
"map_item_spec": [
{
"item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{
"item_name": "name",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "user",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "host",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "password",
"item_type": "string",
"item_optional": true,
"item_default": ""
}
]
},
{ "item_name": "subnet4",
"item_type": "list",
"item_optional": false,
......
......@@ -115,6 +115,38 @@ This error is output if the server failed to assemble the data to be
returned to the client into a valid packet. The cause is most likely
to be a programming error: please raise a bug report.
% DHCP4_PARSER_COMMIT_EXCEPTION parser failed to commit changes
On receipt of message containing details to a change of the IPv4 DHCP
server configuration, a set of parsers were successfully created, but one
of them failed to commit its changes due to a low-level system exception
being raised. Additional messages may be output indicating the reason.
% DHCP4_PARSER_COMMIT_FAIL parser failed to commit changes: %1
On receipt of message containing details to a change of the IPv4 DHCP
server configuration, a set of parsers were successfully created, but
one of them failed to commit its changes. The reason for the failure
is given in the message.
% DHCP4_PARSER_CREATED created parser for configuration element %1
A debug message output during a configuration update of the IPv4 DHCP
server, notifying that the parser for the specified configuration element
has been successfully created.
% DHCP4_PARSER_FAIL failed to create or run parser for configuration element %1: %2
On receipt of message containing details to a change of its configuration,
the IPv4 DHCP server failed to create a parser to decode the contents
of the named configuration element, or the creation succeeded but the
parsing actions and committal of changes failed. The reason for the
failure is given in the message.
% DHCP4_PARSER_EXCEPTION failed to create or run parser for configuration element %1
On receipt of message containing details to a change of its configuration,
the IPv4 DHCP server failed to create a parser to decode the contents of
the named configuration element, or the creation succeeded but the parsing
actions and committal of changes failed. The message has been output in
response to a non-BIND 10 exception being raised. Additional messages
may give further information.
% DHCP4_QUERY_DATA received packet type %1, data is <%2>
A debug message listing the data received from the client.
......@@ -162,6 +194,26 @@ A debug message listing the data returned to the client.
The IPv4 DHCP server has encountered a fatal error and is terminating.
The reason for the failure is included in the message.
% DHCP4_SERVERID_GENERATED server-id %1 has been generated and will be stored in %2
This informational messages indicates that the server was not able to
read its server identifier and has generated a new one. This server-id
will be stored in a file and will be read (and used) whenever the server
is restarted. This is normal behavior when the server is started for the
first time. If this message is printed every time the server is started,
please check that the server has sufficient permission to write its
server-id file and that the file is not corrupt.
% DHCP4_SERVERID_LOADED server-id %1 has been loaded from file %2
This debug message indicates that the server loaded its server identifier.
That value is sent in all server responses and clients use it to
discriminate between servers. This is a part of normal startup or
reconfiguration procedure.
% DHCP4_SERVERID_WRITE_FAIL server was not able to write its ID to file %1
This warning message indicates that server was not able to write its
server identifier to a file. The most likely cause is is that the server
does not have permissions to write the server id file.
% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
The server has failed to establish communication with the rest of BIND
10 and is running in stand-alone mode. (This behavior will change once
......
......@@ -30,6 +30,11 @@
#include <dhcpsrv/utils.h>
#include <dhcpsrv/addr_utilities.h>
#include <boost/algorithm/string/erase.hpp>
#include <iomanip>
#include <fstream>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
......@@ -41,7 +46,6 @@ using namespace std;
const std::string HARDCODED_GATEWAY = "192.0.2.1";
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
const std::string HARDCODED_SERVER_ID = "192.0.2.1";
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
......@@ -56,7 +60,22 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
IfaceMgr::instance().openSockets4(port);
}
setServerID();
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
if (loadServerID(srvid_file)) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
.arg(srvid_file);
} else {
generateServerID();
LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
.arg(srvidToString(getServerID()))
.arg(srvid_file);
if (!writeServerID(srvid_file)) {
LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
.arg(srvid_file);
}
}
// Instantiate LeaseMgr
LeaseMgrFactory::create(dbconfig);
......@@ -176,19 +195,108 @@ Dhcpv4Srv::run() {
}
}
}
}
return (true);
}
bool Dhcpv4Srv::loadServerID(const std::string& file_name) {
// load content of the file into a string
fstream f(file_name.c_str(), ios::in);
if (!f.is_open()) {
return (false);
}
string hex_string;
f >> hex_string;
f.close();
// remove any spaces
boost::algorithm::erase_all(hex_string, " ");
try {
IOAddress addr(hex_string);
if (!addr.isV4()) {
return (false);
}
// Now create server-id option
serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
// TODO add support for config session (see src/bin/auth/main.cc)
// so this daemon can be controlled from bob
} catch(...) {
// any kind of malformed input (empty string, IPv6 address, complete
// garbate etc.)
return (false);
}
return (true);
}
void
Dhcpv4Srv::setServerID() {
/// @todo: implement this for real (see ticket #2588)
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
IOAddress(HARDCODED_SERVER_ID)));
void Dhcpv4Srv::generateServerID() {
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
// Let's find suitable interface.
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
iface != ifaces.end(); ++iface) {
// Let's don't use loopback.
if (iface->flag_loopback_) {
continue;
}
// Let's skip downed interfaces. It is better to use working ones.
if (!iface->flag_up_) {
continue;
}
const IfaceMgr::AddressCollection addrs = iface->getAddresses();
for (IfaceMgr::AddressCollection::const_iterator addr = addrs.begin();
addr != addrs.end(); ++addr) {
if (addr->getFamily() != AF_INET) {
continue;
}
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
*addr));
return;
}
}
isc_throw(BadValue, "No suitable interfaces for server-identifier found");
}
bool Dhcpv4Srv::writeServerID(const std::string& file_name) {
fstream f(file_name.c_str(), ios::out | ios::trunc);
if (!f.good()) {
return (false);
}
f << srvidToString(getServerID());
f.close();
}
string Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
if (!srvid) {
isc_throw(BadValue, "NULL pointer passed to srvidToString()");
}
boost::shared_ptr<Option4AddrLst> generated =
boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
if (!srvid) {
isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
}
Option4AddrLst::AddressContainer addrs = generated->getAddresses();
if (addrs.size() != 1) {