Commit 183ea208 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2204'

parents fe827c7e 86e4008a
......@@ -69,6 +69,8 @@
using namespace std;
using boost::shared_ptr;
using namespace isc;
using namespace isc::cc;
using namespace isc::datasrc;
......@@ -264,23 +266,22 @@ public:
AddressList listen_addresses_;
/// The TSIG keyring
const boost::shared_ptr<TSIGKeyRing>* keyring_;
const shared_ptr<TSIGKeyRing>* keyring_;
/// The client list
std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
client_lists_;
/// The data source client list
AuthSrv::DataSrcClientListsPtr datasrc_client_lists_;
boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
rrclass)
shared_ptr<ConfigurableClientList> getDataSrcClientList(
const RRClass& rrclass)
{
// TODO: Debug-build only check
if (!mutex_.locked()) {
isc_throw(isc::Unexpected, "Not locked!");
}
const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
const_iterator it(client_lists_.find(rrclass));
if (it == client_lists_.end()) {
return (boost::shared_ptr<ConfigurableClientList>());
const std::map<RRClass, shared_ptr<ConfigurableClientList> >::
const_iterator it(datasrc_client_lists_->find(rrclass));
if (it == datasrc_client_lists_->end()) {
return (shared_ptr<ConfigurableClientList>());
} else {
return (it->second);
}
......@@ -335,6 +336,8 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
xfrin_session_(NULL),
counters_(),
keyring_(NULL),
datasrc_client_lists_(new std::map<RRClass,
shared_ptr<ConfigurableClientList> >()),
ddns_base_forwarder_(ddns_forwarder),
ddns_forwarder_(NULL),
xfrout_connected_(false),
......@@ -645,13 +648,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
}
// Lock the client lists and keep them under the lock until the processing
// and rendering is done (this is the same mutex as from
// AuthSrv::getClientListMutex()).
// AuthSrv::getDataSrcClientListMutex()).
isc::util::thread::Mutex::Locker locker(mutex_);
try {
const ConstQuestionPtr question = *message.beginQuestion();
const boost::shared_ptr<datasrc::ClientList>
list(getClientList(question->getClass()));
const shared_ptr<datasrc::ClientList>
list(getDataSrcClientList(question->getClass()));
if (list) {
const RRType& qtype = question->getType();
const Name& qname = question->getName();
......@@ -911,7 +914,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
}
void
AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
impl_->keyring_ = keyring;
}
......@@ -930,43 +933,23 @@ AuthSrv::destroyDDNSForwarder() {
}
}
void
AuthSrv::setClientList(const RRClass& rrclass,
const boost::shared_ptr<ConfigurableClientList>& list) {
AuthSrv::DataSrcClientListsPtr
AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) {
// TODO: Debug-build only check
if (!impl_->mutex_.locked()) {
isc_throw(isc::Unexpected, "Not locked");
}
if (list) {
impl_->client_lists_[rrclass] = list;
} else {
impl_->client_lists_.erase(rrclass);
isc_throw(isc::Unexpected, "Not locked!");
}
}
boost::shared_ptr<ConfigurableClientList>
AuthSrv::getClientList(const RRClass& rrclass) {
return (impl_->getClientList(rrclass));
std::swap(new_lists, impl_->datasrc_client_lists_);
return (new_lists);
}
vector<RRClass>
AuthSrv::getClientListClasses() const {
// TODO: Debug-build only check
if (!impl_->mutex_.locked()) {
isc_throw(isc::Unexpected, "Not locked");
}
vector<RRClass> result;
for (std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
const_iterator it(impl_->client_lists_.begin());
it != impl_->client_lists_.end(); ++it) {
result.push_back(it->first);
}
return (result);
shared_ptr<ConfigurableClientList>
AuthSrv::getDataSrcClientList(const RRClass& rrclass) {
return (impl_->getDataSrcClientList(rrclass));
}
util::thread::Mutex&
AuthSrv::getClientListMutex() const {
AuthSrv::getDataSrcClientListMutex() const {
return (impl_->mutex_);
}
......
......@@ -15,10 +15,11 @@
#ifndef __AUTH_SRV_H
#define __AUTH_SRV_H 1
#include <string>
#include <config/ccsession.h>
#include <datasrc/factory.h>
#include <datasrc/client_list.h>
#include <dns/message.h>
#include <dns/opcode.h>
#include <util/buffer.h>
......@@ -35,6 +36,11 @@
#include <server_common/portconfig.h>
#include <auth/statistics.h>
#include <boost/shared_ptr.hpp>
#include <map>
#include <string>
namespace isc {
namespace util {
namespace io {
......@@ -296,31 +302,46 @@ public:
/// If there was no forwarder yet, this method does nothing.
void destroyDDNSForwarder();
/// \brief Sets the currently used list for data sources of given
/// class.
/// \brief Shortcut typedef used for swapDataSrcClientLists().
typedef boost::shared_ptr<std::map<
isc::dns::RRClass, boost::shared_ptr<
isc::datasrc::ConfigurableClientList> > >
DataSrcClientListsPtr;
/// \brief Swap the currently used set of data source client lists with
/// given one.
///
/// The "set" of lists is actually given in the form of map from
/// RRClasses to shared pointers to isc::datasrc::ConfigurableClientList.
///
/// This method returns the swapped set of lists, which was previously
/// used by the server.
///
/// This method is intended to be used by a separate method to update
/// the data source configuration "at once". The caller must hold
/// a lock for the mutex object returned by \c getDataSrcClientListMutex()
/// before calling this method.
///
/// Replaces the internally used client list with a new one. Other
/// classes are not changed.
/// The ownership of the returned pointer is transferred to the caller.
/// The caller is generally expected to release the resources used in
/// the old lists. Note that it could take longer time if some of the
/// data source clients contain a large size of in-memory data.
///
/// \param rrclass The class to modify.
/// \param list Shared pointer to the client list. If it is NULL,
/// the list is removed instead.
void setClientList(const isc::dns::RRClass& rrclass, const
boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
list);
/// The caller can pass a NULL pointer. This effectively disables
/// any data source for the server.
///
/// \param new_lists Shared pointer to a new set of data source client
/// lists.
/// \return The previous set of lists. It can be NULL.
DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr
new_lists);
/// \brief Returns the currently used client list for the class.
///
/// \param rrclass The class for which to get the list.
/// \return The list, or NULL if no list is set for the class.
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
getClientList(const isc::dns::RRClass& rrclass);
/// \brief Returns a list of classes that have a client list.
///
/// \return List of classes for which a non-NULL client list
/// has been set by setClientList.
std::vector<isc::dns::RRClass> getClientListClasses() const;
getDataSrcClientList(const isc::dns::RRClass& rrclass);
/// \brief Return a mutex for the client lists.
///
......@@ -331,9 +352,9 @@ public:
/// is correct:
/// \code
/// {
/// Mutex::Locker locker(auth->getClientListMutex());
/// Mutex::Locker locker(auth->getDataSrcClientListMutex());
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList>
/// list(auth->getClientList(RRClass::IN()));
/// list(auth->getDataSrcClientList(RRClass::IN()));
/// // Do some processing here
/// }
/// \endcode
......@@ -342,8 +363,8 @@ public:
/// \code
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList> list;
/// {
/// Mutex::Locker locker(auth->getClientListMutex());
/// list = auth->getClientList(RRClass::IN()));
/// Mutex::Locker locker(auth->getDataSrcClientListMutex());
/// list = auth->getDataSrcClientList(RRClass::IN()));
/// }
/// // Do some processing here
/// \endcode
......@@ -352,7 +373,7 @@ public:
/// (lock) the mutex. It's because locking of the mutex is not really
/// a modification of the server object and it is needed to protect the
/// lists even on read-only operations.
isc::util::thread::Mutex& getClientListMutex() const;
isc::util::thread::Mutex& getDataSrcClientListMutex() const;
/// \brief Sets the timeout for incoming TCP connections
///
......
......@@ -18,6 +18,8 @@
#include <bench/benchmark_util.h>
#include <util/buffer.h>
#include <util/threads/lock.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/question.h>
......@@ -125,13 +127,15 @@ public:
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
configureDataSource(
*server_,
Element::fromJSON("{\"IN\":"
" [{\"type\": \"sqlite3\","
" \"params\": {"
" \"database_file\": \"" +
string(datasrc_file) + "\"}}]}"));
isc::util::thread::Mutex::Locker locker(
server_->getDataSrcClientListMutex());
server_->swapDataSrcClientLists(
configureDataSource(
Element::fromJSON("{\"IN\":"
" [{\"type\": \"sqlite3\","
" \"params\": {"
" \"database_file\": \"" +
string(datasrc_file) + "\"}}]}")));
}
};
......@@ -144,14 +148,16 @@ public:
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
configureDataSource(
*server_,
Element::fromJSON("{\"IN\":"
" [{\"type\": \"MasterFiles\","
" \"cache-enable\": true, "
" \"params\": {\"" +
string(zone_origin) + "\": \"" +
string(zone_file) + "\"}}]}"));
isc::util::thread::Mutex::Locker locker(
server_->getDataSrcClientListMutex());
server_->swapDataSrcClientLists(
configureDataSource(
Element::fromJSON("{\"IN\":"
" [{\"type\": \"MasterFiles\","
" \"cache-enable\": true, "
" \"params\": {\"" +
string(zone_origin) + "\": \"" +
string(zone_file) + "\"}}]}")));
}
};
......
......@@ -192,9 +192,10 @@ public:
// We're going to work with the client lists. They may be used
// from a different thread too, protect them.
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
isc::util::thread::Mutex::Locker locker(
server.getDataSrcClientListMutex());
const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
list(server.getClientList(zone_class));
list(server.getDataSrcClientList(zone_class));
if (!list) {
isc_throw(AuthCommandError, "There's no client list for "
......
......@@ -18,9 +18,8 @@
// This is a trivial specialization for the commonly used version.
// Defined in .cc to avoid accidental creation of multiple copies.
void
configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config)
{
return (configureDataSourceGeneric<AuthSrv,
isc::datasrc::ConfigurableClientList>(server, config));
AuthSrv::DataSrcClientListsPtr
configureDataSource(const isc::data::ConstElementPtr& config) {
return (configureDataSourceGeneric<
isc::datasrc::ConfigurableClientList>(config));
}
......@@ -19,99 +19,62 @@
#include <cc/data.h>
#include <datasrc/client_list.h>
#include <util/threads/lock.h>
#include <boost/shared_ptr.hpp>
#include <utility>
#include <set>
/// \brief Configure the authoritative server's data source lists
/// \brief Configure data source client lists
///
/// This will hook into the data_sources module configuration and it will
/// keep the local copy of data source clients in the list in the authoritative
/// server.
/// return a new set (in the form of a shared pointer to map) of data source
/// client lists corresponding to the configuration.
///
/// This function is templated. This is simply because of easier testing.
/// You don't need to pay attention to it, use the configureDataSource
/// specialization instead.
///
/// \param server It is the server to configure.
/// \note In future we may want to make the reconfiguration more efficient
/// by only creating newly configured data and just moving the rest from
/// the running configuration if they are used in the new configuration
/// without any parameter change. We could probably do it by passing
/// the old lists in addition to the new config, but further details are
/// still to be defined yet. It will surely require changes in the
/// data source library, too. So, right now, we don't introduce the
/// possibility in the function interface. If and when we decide to introduce
/// the optimization, we'll extend the interface.
///
/// \param config The configuration value to parse. It is in the form
/// as an update from the config manager.
template<class Server, class List>
void
configureDataSourceGeneric(Server& server,
const isc::data::ConstElementPtr& config)
{
/// \return A map from RR classes to configured lists.
template<class List>
boost::shared_ptr<std::map<isc::dns::RRClass,
boost::shared_ptr<List> > > // = ListMap below
configureDataSourceGeneric(const isc::data::ConstElementPtr& config) {
typedef boost::shared_ptr<List> ListPtr;
typedef std::map<std::string, isc::data::ConstElementPtr> Map;
typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
RollbackConfiguration;
typedef std::map<isc::dns::RRClass, ListPtr> ListMap;
// Lock the client lists, we're going to manipulate them.
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
boost::shared_ptr<ListMap> new_lists(new ListMap);
// Some structures to be able to perform a rollback
std::vector<RollbackPair> rollback_sets;
std::vector<RollbackConfiguration> rollback_configurations;
try {
// Get the configuration and current state.
const Map& map(config->mapValue());
const std::vector<isc::dns::RRClass>
activeVector(server.getClientListClasses());
std::set<isc::dns::RRClass> active(activeVector.begin(),
activeVector.end());
// Go through the configuration and change everything.
for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
const isc::dns::RRClass rrclass(it->first);
active.erase(rrclass);
ListPtr list(server.getClientList(rrclass));
bool need_set(false);
if (list) {
rollback_configurations.
push_back(RollbackConfiguration(rrclass,
list->getConfiguration()));
} else {
list.reset(new List(rrclass));
need_set = true;
rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
}
list->configure(it->second, true);
if (need_set) {
server.setClientList(rrclass, list);
}
}
// Remove the ones that are not in the configuration.
for (std::set<isc::dns::RRClass>::iterator it(active.begin());
it != active.end(); ++it) {
// There seems to be no way the setClientList could throw.
// But this is just to make sure in case it did to restore
// the original.
rollback_sets.push_back(
RollbackPair(*it, server.getClientList(*it)));
server.setClientList(*it, ListPtr());
}
} catch (...) {
// Perform a rollback of the changes. The old configuration should
// work.
for (typename std::vector<RollbackPair>::const_iterator
it(rollback_sets.begin()); it != rollback_sets.end(); ++it) {
server.setClientList(it->first, it->second);
}
for (typename std::vector<RollbackConfiguration>::const_iterator
it(rollback_configurations.begin());
it != rollback_configurations.end(); ++it) {
server.getClientList(it->first)->configure(it->second, true);
}
throw;
// Go through the configuration and create corresponding list.
const Map& map(config->mapValue());
for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
const isc::dns::RRClass rrclass(it->first);
ListPtr list(new List(rrclass));
list->configure(it->second, true);
new_lists->insert(std::pair<isc::dns::RRClass, ListPtr>(rrclass,
list));
}
return (new_lists);
}
/// \brief Concrete version of configureDataSource() for the
/// use with authoritative server implementation.
void
configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config);
AuthSrv::DataSrcClientListsPtr
configureDataSource(const isc::data::ConstElementPtr& config);
#endif // DATASRC_CONFIG_H
......
......@@ -18,6 +18,7 @@
#include <util/buffer.h>
#include <util/io/socketsession.h>
#include <util/threads/lock.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
......@@ -93,18 +94,31 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time,
{
assert(server != NULL);
if (config->contains("classes")) {
AuthSrv::DataSrcClientListsPtr lists;
if (*first_time) {
// HACK: The default is not passed to the handler in the first
// callback. This one will get the default (or, current value).
// Further updates will work the usual way.
assert(config_session != NULL);
*first_time = false;
configureDataSource(*auth_server,
config_session->getRemoteConfigValue(
"data_sources", "classes"));
lists = configureDataSource(
config_session->getRemoteConfigValue("data_sources",
"classes"));
} else {
configureDataSource(*server, config->get("classes"));
lists = configureDataSource(config->get("classes"));
}
// Replace the server's lists. The returned lists will be stored
// in a local variable 'lists', and will be destroyed outside of
// the temporary block for the lock scope. That way we can minimize
// the range of the critical section.
{
isc::util::thread::Mutex::Locker locker(
server->getDataSrcClientListMutex());
lists = server->swapDataSrcClientLists(lists);
}
// The previous lists are destroyed here.
}
}
......
......@@ -63,6 +63,7 @@
using namespace std;
using namespace isc::cc;
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::util;
using namespace isc::util::io::internal;
using namespace isc::util::unittests;
......@@ -90,6 +91,9 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
// a signed example zone.
const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
// shortcut commonly used in tests
typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
class AuthSrvTest : public SrvTestBase {
protected:
AuthSrvTest() :
......@@ -721,6 +725,14 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
EXPECT_FALSE(dnsserv.hasAnswer());
}
void
installDataSrcClientLists(AuthSrv& server,
AuthSrv::DataSrcClientListsPtr lists)
{
thread::Mutex::Locker locker(server.getDataSrcClientListMutex());
server.swapDataSrcClientLists(lists);
}
void
updateDatabase(AuthSrv& server, const char* params) {
const ConstElementPtr config(Element::fromJSON("{"
......@@ -728,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) {
" \"type\": \"sqlite3\","
" \"params\": " + string(params) +
"}]}"));
configureDataSource(server, config);
installDataSrcClientLists(server, configureDataSource(config));
}
void
......@@ -745,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) {
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
configureDataSource(server, config);
installDataSrcClientLists(server, configureDataSource(config));
}
void
......@@ -755,7 +767,7 @@ updateBuiltin(AuthSrv& server) {
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
configureDataSource(server, config);
installDataSrcClientLists(server, configureDataSource(config));
}
// Try giving the server a TSIG signed request and see it can anwer signed as
......@@ -953,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
" \"params\": {},"
" \"cache-enable\": true"
"}]}"));
configureDataSource(server, config);
installDataSrcClientLists(server, configureDataSource(config));
// after successful configuration, we should have one (with empty zoneset).
// The memory data source is empty, should return REFUSED rcode.
......@@ -1427,11 +1439,14 @@ TEST_F(AuthSrvTest,
// Set real inmem client to proxy
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
{
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
isc::util::thread::Mutex::Locker locker(
server.getDataSrcClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
false));
server.setClientList(RRClass::IN(), list);
list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
THROW_NEVER, false));
AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
server.swapDataSrcClientLists(lists);
}
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
......@@ -1455,11 +1470,14 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception,
{
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
isc::util::thread::Mutex::Locker locker(
server.getDataSrcClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
list(new FakeList(server.getClientList(RRClass::IN()), throw_when,
isc_exception, rrset));
server.setClientList(RRClass::IN(), list);
list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
throw_when, isc_exception, rrset));
AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
server.swapDataSrcClientLists(lists);
}