Commit 4c0d2595 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac1069'

with fixing conflicts in src/bin/resolver/Makefile.am
parents baf3d878 803a215c
......@@ -60,6 +60,7 @@ b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_resolver_LDADD += $(top_builddir)/src/lib/util/libutil.la
b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
......@@ -68,7 +69,6 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libacl.la
b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
b10_resolver_LDFLAGS = -pthread
......
......@@ -26,7 +26,7 @@
#include <exceptions/exceptions.h>
#include <acl/acl.h>
#include <acl/dns.h>
#include <acl/loader.h>
#include <asiodns/asiodns.h>
......@@ -62,6 +62,7 @@ using boost::shared_ptr;
using namespace isc;
using namespace isc::util;
using namespace isc::acl;
using isc::acl::dns::RequestACL;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
......@@ -82,7 +83,9 @@ public:
client_timeout_(4000),
lookup_timeout_(30000),
retries_(3),
query_acl_(new Resolver::ClientACL(REJECT)),
// we apply "reject all" (implicit default of the loader) ACL by
// default:
query_acl_(acl::dns::getRequestLoader().load(Element::fromJSON("[]"))),
rec_query_(NULL)
{}
......@@ -160,11 +163,11 @@ public:
OutputBufferPtr buffer,
DNSServer* server);
const Resolver::ClientACL& getQueryACL() const {
const RequestACL& getQueryACL() const {
return (*query_acl_);
}
void setQueryACL(shared_ptr<const Resolver::ClientACL> new_acl) {
void setQueryACL(shared_ptr<const RequestACL> new_acl) {
query_acl_ = new_acl;
}
......@@ -192,7 +195,7 @@ public:
private:
/// ACL on incoming queries
shared_ptr<const Resolver::ClientACL> query_acl_;
shared_ptr<const RequestACL> query_acl_;
/// Object to handle upstream queries
RecursiveQuery* rec_query_;
......@@ -514,8 +517,10 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message,
const RRClass qclass = question->getClass();
// Apply query ACL
Client client(io_message);
const BasicAction query_action(getQueryACL().execute(client));
const Client client(io_message);
const BasicAction query_action(
getQueryACL().execute(acl::dns::RequestContext(
client.getRequestSourceIPAddress())));
if (query_action == isc::acl::REJECT) {
LOG_INFO(resolver_logger, RESOLVER_QUERY_REJECTED)
.arg(question->getName()).arg(qtype).arg(qclass).arg(client);
......@@ -574,32 +579,6 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message,
return (RECURSION);
}
namespace {
// This is a simplified ACL parser for the initial implementation with minimal
// external dependency. For a longer term we'll switch to a more generic
// loader with allowing more complicated ACL syntax.
shared_ptr<const Resolver::ClientACL>
createQueryACL(isc::data::ConstElementPtr acl_config) {
if (!acl_config) {
return (shared_ptr<const Resolver::ClientACL>());
}
shared_ptr<Resolver::ClientACL> new_acl(
new Resolver::ClientACL(REJECT));
BOOST_FOREACH(ConstElementPtr rule, acl_config->listValue()) {
ConstElementPtr action = rule->get("action");
ConstElementPtr from = rule->get("from");
if (!action || !from) {
isc_throw(BadValue, "query ACL misses mandatory parameter");
}
new_acl->append(shared_ptr<IPCheck<Client> >(
new IPCheck<Client>(from->stringValue())),
defaultActionLoader(action));
}
return (new_acl);
}
}
ConstElementPtr
Resolver::updateConfig(ConstElementPtr config) {
LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIG_UPDATED)
......@@ -616,8 +595,10 @@ Resolver::updateConfig(ConstElementPtr config) {
ConstElementPtr listenAddressesE(config->get("listen_on"));
AddressList listenAddresses(parseAddresses(listenAddressesE,
"listen_on"));
shared_ptr<const ClientACL> query_acl(createQueryACL(
config->get("query_acl")));
const ConstElementPtr query_acl_cfg(config->get("query_acl"));
const shared_ptr<const RequestACL> query_acl =
query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) :
shared_ptr<const RequestACL>();
bool set_timeouts(false);
int qtimeout = impl_->query_timeout_;
int ctimeout = impl_->client_timeout_;
......@@ -777,13 +758,13 @@ Resolver::getListenAddresses() const {
return (impl_->listen_);
}
const Resolver::ClientACL&
const RequestACL&
Resolver::getQueryACL() const {
return (impl_->getQueryACL());
}
void
Resolver::setQueryACL(shared_ptr<const ClientACL> new_acl) {
Resolver::setQueryACL(shared_ptr<const RequestACL> new_acl) {
if (!new_acl) {
isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL");
}
......
......@@ -21,10 +21,9 @@
#include <boost/shared_ptr.hpp>
#include <acl/acl.h>
#include <cc/data.h>
#include <config/ccsession.h>
#include <acl/dns.h>
#include <dns/message.h>
#include <util/buffer.h>
......@@ -41,12 +40,6 @@
#include <resolve/resolver_interface.h>
namespace isc {
namespace server_common {
class Client;
}
}
class ResolverImpl;
/**
......@@ -246,13 +239,10 @@ public:
*/
int getRetries() const;
// Shortcut typedef used for query ACL.
typedef isc::acl::ACL<isc::server_common::Client> ClientACL;
/// Get the query ACL.
///
/// \exception None
const ClientACL& getQueryACL() const;
const isc::acl::dns::RequestACL& getQueryACL() const;
/// Set the new query ACL.
///
......@@ -265,7 +255,8 @@ public:
/// \exception InvalidParameter The given pointer is NULL
///
/// \param new_acl The new ACL to replace the existing one.
void setQueryACL(boost::shared_ptr<const ClientACL> new_acl);
void setQueryACL(boost::shared_ptr<const isc::acl::dns::RequestACL>
new_acl);
private:
ResolverImpl* impl_;
......
......@@ -39,6 +39,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
......
......@@ -43,6 +43,7 @@
using namespace std;
using boost::scoped_ptr;
using namespace isc::acl;
using isc::acl::dns::RequestContext;
using namespace isc::data;
using namespace isc::testutils;
using namespace isc::asiodns;
......@@ -57,19 +58,22 @@ protected:
DNSService dnss;
Resolver server;
scoped_ptr<const IOEndpoint> endpoint;
scoped_ptr<const IOMessage> request;
scoped_ptr<const IOMessage> query_message;
scoped_ptr<const Client> client;
scoped_ptr<const RequestContext> request;
ResolverConfig() : dnss(ios, NULL, NULL, NULL) {
server.setDNSService(dnss);
server.setConfigured();
}
const Client& createClient(const string& source_addr) {
const RequestContext& createRequest(const string& source_addr) {
endpoint.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress(source_addr),
53210));
request.reset(new IOMessage(NULL, 0, IOSocket::getDummyUDPSocket(),
*endpoint));
client.reset(new Client(*request));
return (*client);
query_message.reset(new IOMessage(NULL, 0,
IOSocket::getDummyUDPSocket(),
*endpoint));
client.reset(new Client(*query_message));
request.reset(new RequestContext(client->getRequestSourceIPAddress()));
return (*request);
}
void invalidTest(const string &JSON, const string& name);
};
......@@ -100,14 +104,14 @@ TEST_F(ResolverConfig, forwardAddresses) {
TEST_F(ResolverConfig, forwardAddressConfig) {
// Try putting there some address
ElementPtr config(Element::fromJSON("{"
"\"forward_addresses\": ["
" {"
" \"address\": \"192.0.2.1\","
" \"port\": 53"
" }"
"]"
"}"));
ConstElementPtr config(Element::fromJSON("{"
"\"forward_addresses\": ["
" {"
" \"address\": \"192.0.2.1\","
" \"port\": 53"
" }"
"]"
"}"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_TRUE(server.isForwarding());
......@@ -127,14 +131,14 @@ TEST_F(ResolverConfig, forwardAddressConfig) {
TEST_F(ResolverConfig, rootAddressConfig) {
// Try putting there some address
ElementPtr config(Element::fromJSON("{"
"\"root_addresses\": ["
" {"
" \"address\": \"192.0.2.1\","
" \"port\": 53"
" }"
"]"
"}"));
ConstElementPtr config(Element::fromJSON("{"
"\"root_addresses\": ["
" {"
" \"address\": \"192.0.2.1\","
" \"port\": 53"
" }"
"]"
"}"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
ASSERT_EQ(1, server.getRootAddresses().size());
......@@ -210,12 +214,12 @@ TEST_F(ResolverConfig, timeouts) {
}
TEST_F(ResolverConfig, timeoutsConfig) {
ElementPtr config = Element::fromJSON("{"
"\"timeout_query\": 1000,"
"\"timeout_client\": 2000,"
"\"timeout_lookup\": 3000,"
"\"retries\": 4"
"}");
ConstElementPtr config = Element::fromJSON("{"
"\"timeout_query\": 1000,"
"\"timeout_client\": 2000,"
"\"timeout_lookup\": 3000,"
"\"retries\": 4"
"}");
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(1000, server.getQueryTimeout());
......@@ -253,51 +257,51 @@ TEST_F(ResolverConfig, invalidTimeoutsConfig) {
TEST_F(ResolverConfig, defaultQueryACL) {
// If no configuration is loaded, the default ACL should reject everything.
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(
createClient("2001:db8::1")));
createRequest("2001:db8::1")));
// The following would be allowed if the server had loaded the default
// configuration from the spec file. In this context it should not have
// happened, and they should be rejected just like the above cases.
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("127.0.0.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("::1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("127.0.0.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("::1")));
}
TEST_F(ResolverConfig, emptyQueryACL) {
// Explicitly configured empty ACL should have the same effect.
ElementPtr config(Element::fromJSON("{ \"query_acl\": [] }"));
ConstElementPtr config(Element::fromJSON("{ \"query_acl\": [] }"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(
createClient("2001:db8::1")));
createRequest("2001:db8::1")));
}
TEST_F(ResolverConfig, queryACLIPv4) {
// A simple "accept" query for a specific IPv4 address
ElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"192.0.2.1\"} ] }"));
ConstElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"192.0.2.1\"} ] }"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1")));
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(
createClient("2001:db8::1")));
createRequest("2001:db8::1")));
}
TEST_F(ResolverConfig, queryACLIPv6) {
// same for IPv6
ElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"2001:db8::1\"} ] }"));
ConstElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"2001:db8::1\"} ] }"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(
createClient("2001:db8::1")));
createRequest("2001:db8::1")));
}
TEST_F(ResolverConfig, multiEntryACL) {
......@@ -306,25 +310,26 @@ TEST_F(ResolverConfig, multiEntryACL) {
// as it should have been tested in the underlying ACL module. All we
// have to do to check is a reasonably complicated ACL configuration is
// loaded as expected.
ElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"192.0.2.1\"},"
" {\"action\": \"REJECT\","
" \"from\": \"192.0.2.0/24\"},"
" {\"action\": \"DROP\","
" \"from\": \"2001:db8::1\"},"
"] }"));
ConstElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"192.0.2.1\"},"
" {\"action\": \"REJECT\","
" \"from\": \"192.0.2.0/24\"},"
" {\"action\": \"DROP\","
" \"from\": \"2001:db8::1\"},"
"] }"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.2")));
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.2")));
EXPECT_EQ(DROP, server.getQueryACL().execute(
createClient("2001:db8::1")));
createRequest("2001:db8::1")));
EXPECT_EQ(REJECT, server.getQueryACL().execute(
createClient("2001:db8::2"))); // match the default rule
createRequest("2001:db8::2"))); // match the default rule
}
int
getResultCode(ConstElementPtr result) {
int rcode;
......@@ -332,6 +337,22 @@ getResultCode(ConstElementPtr result) {
return (rcode);
}
TEST_F(ResolverConfig, queryACLActionOnly) {
// "action only" rule will be accepted by the loader, which can
// effectively change the default action.
ConstElementPtr config(Element::fromJSON(
"{ \"query_acl\": "
" [ {\"action\": \"ACCEPT\","
" \"from\": \"192.0.2.1\"},"
" {\"action\": \"DROP\"} ] }"));
EXPECT_EQ(0, getResultCode(server.updateConfig(config)));
EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
// We reject non matching queries by default, but the last resort
// rule should have changed the action in that case to "DROP".
EXPECT_EQ(DROP, server.getQueryACL().execute(createRequest("192.0.2.2")));
}
TEST_F(ResolverConfig, badQueryACL) {
// Most of these cases shouldn't happen in practice because the syntax
// check should be performed before updateConfig(). But we check at
......@@ -346,10 +367,6 @@ TEST_F(ResolverConfig, badQueryACL) {
server.updateConfig(
Element::fromJSON("{ \"query_acl\":"
" [ {\"from\": \"192.0.2.1\"} ] }"))));
EXPECT_EQ(1, getResultCode(
server.updateConfig(
Element::fromJSON("{ \"query_acl\":"
" [ {\"action\": \"DROP\"} ] }"))));
// invalid "action"
EXPECT_EQ(1, getResultCode(
server.updateConfig(
......@@ -361,7 +378,6 @@ TEST_F(ResolverConfig, badQueryACL) {
Element::fromJSON("{ \"query_acl\":"
" [ {\"action\": \"BADACTION\","
" \"from\": \"192.0.2.1\"}]}"))));
// invalid "from"
EXPECT_EQ(1, getResultCode(
server.updateConfig(
......
......@@ -27,6 +27,7 @@
using namespace std;
using namespace isc::dns;
using namespace isc::data;
using isc::acl::dns::RequestACL;
using namespace isc::testutils;
using isc::UnitTestUtil;
......@@ -156,8 +157,7 @@ TEST_F(ResolverTest, notifyFail) {
TEST_F(ResolverTest, setQueryACL) {
// valid cases are tested through other tests. We only explicitly check
// an invalid case: passing a NULL shared pointer.
EXPECT_THROW(server.setQueryACL(
boost::shared_ptr<const Resolver::ClientACL>()),
EXPECT_THROW(server.setQueryACL(boost::shared_ptr<const RequestACL>()),
isc::InvalidParameter);
}
......
......@@ -12,20 +12,97 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "dns.h"
#include <memory>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
#include <cc/data.h>
#include <acl/dns.h>
#include <acl/ip_check.h>
#include <acl/loader.h>
using namespace std;
using boost::shared_ptr;
using namespace isc::data;
namespace isc {
namespace acl {
/// The specialization of \c IPCheck for access control with \c RequestContext.
///
/// It returns \c true if the remote (source) IP address of the request
/// matches the expression encapsulated in the \c IPCheck, and returns
/// \c false if not.
///
/// \note The match logic is expected to be extended as we add
/// more match parameters (at least there's a plan for TSIG key).
template <>
bool
IPCheck<dns::RequestContext>::matches(
const dns::RequestContext& request) const
{
return (compare(request.remote_address.getData(),
request.remote_address.getFamily()));
}
namespace dns {
Loader&
getLoader() {
static Loader* loader(NULL);
vector<string>
internal::RequestCheckCreator::names() const {
// Probably we should eventually build this vector in a more
// sophisticated way. For now, it's simple enough to hardcode
// everything.
vector<string> supported_names;
supported_names.push_back("from");
return (supported_names);
}
shared_ptr<RequestCheck>
internal::RequestCheckCreator::create(const string& name,
ConstElementPtr definition,
// unused:
const acl::Loader<RequestContext>&)
{
if (!definition) {
isc_throw(LoaderError,
"NULL pointer is passed to RequestCheckCreator");
}
if (name == "from") {
return (shared_ptr<internal::RequestIPCheck>(
new internal::RequestIPCheck(definition->stringValue())));
} else {
// This case shouldn't happen (normally) as it should have been
// rejected at the loader level. But we explicitly catch the case
// and throw an exception for that.
isc_throw(LoaderError, "Invalid check name for RequestCheck: " <<
name);
}
}
RequestLoader&
getRequestLoader() {
static RequestLoader* loader(NULL);
if (loader == NULL) {
loader = new Loader(REJECT);
// TODO: This is the place where we register default check creators
// like IP check, etc, once we have them.
// Creator registration may throw, so we first store the new loader
// in an auto pointer in order to provide the strong exception
// guarantee.
auto_ptr<RequestLoader> loader_ptr =
auto_ptr<RequestLoader>(new RequestLoader(REJECT));
// Register default check creator(s)
loader_ptr->registerCreator(shared_ptr<internal::RequestCheckCreator>(
new internal::RequestCheckCreator()));
// From this point there shouldn't be any exception thrown
loader = loader_ptr.release();
}
return (*loader);
}
......
......@@ -13,12 +13,17 @@
// PERFORMANCE OF THIS SOFTWARE.
#ifndef ACL_DNS_H
#define ACL_DNS_H
#define ACL_DNS_H 1
#include "loader.h"
#include <string>
#include <vector>
#include <asiolink/io_address.h>
#include <dns/message.h>
#include <boost/shared_ptr.hpp>
#include <cc/data.h>
#include <acl/ip_check.h>
#include <acl/loader.h>
namespace isc {
namespace acl {
......@@ -30,47 +35,65 @@ namespace dns {
* This plays the role of Context of the generic template ACLs (in namespace
* isc::acl).
*
* It is simple structure holding just the bunch of information. Therefore
* the names don't end up with a slash, there are no methods so they can't be
* confused with local variables.
* It is a simple structure holding just the bunch of information. Therefore
* the names don't end up with an underscore; there are no methods so they
* can't be confused with local variables.
*
* This structure is generally expected to be ephemeral and read-only: It
* would be constructed immediately before a particular ACL is checked