Commit 9f52917c authored by Michal Vaner's avatar Michal Vaner
Browse files

Merge recursor config into trac327

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac327@3448 e5f2f494-b856-4b98-b285-d166d9295462
parents f8e8b2de 07111640
......@@ -568,7 +568,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench ../src/lib/asiolink/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
......
......@@ -363,13 +363,14 @@ class BoB:
dns_prog = 'b10-recurse'
else:
dns_prog = 'b10-auth'
dnsargs = [dns_prog, '-p', str(self.dns_port)]
if self.forward:
dnsargs += ['-f', str(self.forward)]
if self.address:
dnsargs += ['-a', str(self.address)]
if self.nocache:
dnsargs += ['-n']
dnsargs = [dns_prog]
if not self.recursive:
# The recursive uses configuration manager for these
dnsargs += ['-p', str(self.dns_port)]
if self.address:
dnsargs += ['-a', str(self.address)]
if self.nocache:
dnsargs += ['-n']
if self.uid:
dnsargs += ['-u', str(self.uid)]
if self.verbose:
......
......@@ -61,7 +61,6 @@ static bool verbose_mode = false;
// Default port current 5300 for testing purposes
static const string PROGRAM = "Recurse";
static const char* DNSPORT = "5300";
IOService io_service;
static Recursor *recursor;
......@@ -82,21 +81,13 @@ my_command_handler(const string& command, ConstElementPtr args) {
} else if (command == "shutdown") {
io_service.stop();
}
return (answer);
}
void
usage() {
cerr << "Usage: b10-recurse -f nameserver [-a address] [-p port] [-u user]"
"[-4|-6] [-v]" << endl;
cerr << "\t-f: specify the nameserver to which queries should be forwarded"
<< endl;
cerr << "\t-a: specify the address to listen on (default: all)" << endl;
cerr << "\t-p: specify the port to listen on (default: " << DNSPORT << ")"
<< endl;
cerr << "\t-4: listen on all IPv4 addresses (incompatible with -a)" << endl;
cerr << "\t-6: listen on all IPv6 addresses (incompatible with -a)" << endl;
cerr << "Usage: b10-recurse [-u user] [-v]" << endl;
cerr << "\t-u: change process UID to the specified user" << endl;
cerr << "\t-v: verbose output" << endl;
exit(1);
......@@ -106,34 +97,10 @@ usage() {
int
main(int argc, char* argv[]) {
int ch;
const char* port = DNSPORT;
const char* address = NULL;
const char* forward = NULL;
const char* uid = NULL;
bool use_ipv4 = true, use_ipv6 = true;
while ((ch = getopt(argc, argv, "46a:f:p:u:v")) != -1) {
while ((ch = getopt(argc, argv, "u:v")) != -1) {
switch (ch) {
case '4':
// Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
// not "use_ipv4". We could use something like "ipv4_only", but
// we found the negatively named variable could confuse the code
// logic.
use_ipv6 = false;
break;
case '6':
// The same note as -4 applies.
use_ipv4 = false;
break;
case 'a':
address = optarg;
break;
case 'f':
forward = optarg;
break;
case 'p':
port = optarg;
break;
case 'u':
uid = optarg;
break;
......@@ -150,23 +117,6 @@ main(int argc, char* argv[]) {
usage();
}
if (!use_ipv4 && !use_ipv6) {
cerr << "[b10-auth] Error: Cannot specify both -4 and -6 "
<< "at the same time" << endl;
usage();
}
if ((!use_ipv4 || !use_ipv6) && address != NULL) {
cerr << "[b10-auth] Error: Cannot specify -4 or -6 "
<< "at the same time as -a" << endl;
usage();
}
if (forward == NULL) {
cerr << "[b10-recurse] No forward name server specified" << endl;
usage();
}
int ret = 0;
// XXX: we should eventually pass io_service here.
......@@ -181,7 +131,7 @@ main(int argc, char* argv[]) {
specfile = string(RECURSE_SPECFILE_LOCATION);
}
recursor = new Recursor(*forward);
recursor = new Recursor();
recursor->setVerbose(verbose_mode);
cout << "[b10-recurse] Server created." << endl;
......@@ -189,22 +139,9 @@ main(int argc, char* argv[]) {
DNSLookup* lookup = recursor->getDNSLookupProvider();
DNSAnswer* answer = recursor->getDNSAnswerProvider();
DNSService* dns_service;
if (address != NULL) {
// XXX: we can only specify at most one explicit address.
// This also means the server cannot run in the dual address
// family mode if explicit addresses need to be specified.
// We don't bother to fix this problem, however. The -a option
// is a short term workaround until we support dynamic listening
// port allocation.
dns_service = new DNSService(io_service, *port, *address,
checkin, lookup, answer);
} else {
dns_service = new DNSService(io_service, *port, use_ipv4, use_ipv6,
checkin, lookup, answer);
}
recursor->setDNSService(*dns_service);
DNSService dns_service(io_service, checkin, lookup, answer);
recursor->setDNSService(dns_service);
cout << "[b10-recurse] IOService created." << endl;
cc_session = new Session(io_service.get_io_service());
......@@ -215,12 +152,13 @@ main(int argc, char* argv[]) {
my_command_handler);
cout << "[b10-recurse] Configuration channel established." << endl;
// FIXME: This does not belong here, but inside Boss
if (uid != NULL) {
changeUser(uid);
}
recursor->setConfigSession(config_session);
recursor->updateConfig(ElementPtr());
recursor->updateConfig(config_session->getFullConfig());
cout << "[b10-recurse] Server started." << endl;
io_service.run();
......
{
"module_spec": {
"module_name": "Auth",
"module_description": "Authoritative service",
"module_name": "Recurse",
"module_description": "Recursive service",
"config_data": [
{ "item_name": "database_file",
"item_type": "string",
"item_optional": true,
"item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
{
"item_name": "forward_addresses",
"item_type": "list",
"item_optional": True,
"item_default": [],
"list_item_spec" : {
"item_name": "address",
"item_type": "map",
"item_optional": False,
"item_default": {},
"map_item_spec": [
{
"item_name": "address",
"item_type": "string",
"item_optional": False,
"item_default": "::1"
},
{
"item_name": "port",
"item_type": "integer",
"item_optional": False,
"item_default": 53
}
]
}
},
{
"item_name": "listen_on",
"item_type": "list",
"item_optional": False,
"item_default": [
{
"address": "::1",
"port": 5300
},
{
"address": "127.0.0.1",
"port": 5300
},
],
"list_item_spec": {
"item_name": "address",
"item_type": "map",
"item_optional": False,
"item_default": {},
"map_item_spec": [
{
"item_name": "address",
"item_type": "string",
"item_optional": False,
"item_default": "::1"
},
{
"item_name": "port",
"item_type": "integer",
"item_optional": False,
"item_default": 5300
}
]
}
}
],
"commands": [
{
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
"command_description": "Shut down recursive DNS server",
"command_args": []
}
]
......
......@@ -24,6 +24,7 @@
#include <vector>
#include <asiolink/asiolink.h>
#include <asiolink/ioaddress.h>
#include <boost/foreach.hpp>
......@@ -57,15 +58,18 @@ using namespace isc::config;
using namespace isc::xfr;
using namespace asiolink;
typedef pair<string, uint16_t> addr_t;
class RecursorImpl {
private:
// prohibit copy
RecursorImpl(const RecursorImpl& source);
RecursorImpl& operator=(const RecursorImpl& source);
public:
RecursorImpl(const char& forward) :
config_session_(NULL), verbose_mode_(false),
forward_(forward), rec_query_()
RecursorImpl() :
config_session_(NULL),
verbose_mode_(false),
rec_query_()
{}
~RecursorImpl() {
......@@ -73,12 +77,26 @@ public:
}
void querySetup(DNSService& dnss) {
rec_query_ = new RecursiveQuery(dnss, forward_);
rec_query_ = new RecursiveQuery(dnss, upstream_);
}
void queryShutdown() {
if (rec_query_) {
delete rec_query_;
delete rec_query_;
rec_query_ = NULL;
}
void setForwardAddresses(const vector<addr_t>& upstream,
DNSService *dnss)
{
queryShutdown();
upstream_ = upstream;
if (dnss) {
if (upstream_.empty()) {
cerr << "[b10-recurse] Asked to do full recursive," << endl <<
"but not implemented yet. I'll do nothing." << endl;
} else {
querySetup(*dnss);
}
}
}
......@@ -92,10 +110,12 @@ public:
/// These members are public because Recursor accesses them directly.
ModuleCCSession* config_session_;
bool verbose_mode_;
/// Addresses of the forward nameserver
vector<addr_t> upstream_;
/// Addresses we listen on
vector<addr_t> listen_;
private:
/// Address of the forward nameserver
const char& forward_;
/// Object to handle upstream queries
RecursiveQuery* rec_query_;
......@@ -281,8 +301,8 @@ private:
Recursor* server_;
};
Recursor::Recursor(const char& forward) :
impl_(new RecursorImpl(forward)),
Recursor::Recursor() :
impl_(new RecursorImpl()),
checkin_(new ConfigCheck(this)),
dns_lookup_(new MessageLookup(this)),
dns_answer_(new MessageAnswer(this))
......@@ -431,11 +451,68 @@ RecursorImpl::processNormalQuery(const Question& question, MessagePtr message,
rec_query_->sendQuery(question, buffer, server);
}
namespace {
vector<addr_t>
parseAddresses(ConstElementPtr addresses) {
vector<addr_t> result;
if (addresses) {
if (addresses->getType() == Element::list) {
for (size_t i(0); i < addresses->size(); ++ i) {
ConstElementPtr addrPair(addresses->get(i));
ConstElementPtr addr(addrPair->get("address"));
ConstElementPtr port(addrPair->get("port"));
if (!addr || ! port) {
isc_throw(BadValue, "Address must contain both the IP"
"address and port");
}
try {
IOAddress(addr->stringValue());
if (port->intValue() < 0 ||
port->intValue() > 0xffff) {
isc_throw(BadValue, "Bad port value (" <<
port->intValue() << ")");
}
result.push_back(addr_t(addr->stringValue(),
port->intValue()));
}
catch (const TypeError &e) { // Better error message
isc_throw(TypeError,
"Address must be a string and port an integer");
}
}
} else if (addresses->getType() != Element::null) {
isc_throw(TypeError,
"forward_addresses config element must be a list");
}
}
return (result);
}
}
ConstElementPtr
Recursor::updateConfig(ConstElementPtr new_config UNUSED_PARAM) {
Recursor::updateConfig(ConstElementPtr config) {
if (impl_->verbose_mode_) {
cout << "[b10-recurse] Update with config: " << config->str() << endl;
}
try {
// We will do configuration updates here. None are presently
// defined, so we just return an empty answer.
// Parse forward_addresses
// FIXME Once the config parser is fixed, remove the slashes. They
// appear only on the default/startup value and shouldn't be here.
// See ticket #384
ConstElementPtr forwardAddressesE(config->get("forward_addresses/"));
vector<addr_t> forwardAddresses(parseAddresses(forwardAddressesE));
ConstElementPtr listenAddressesE(config->get("listen_on/"));
vector<addr_t> listenAddresses(parseAddresses(listenAddressesE));
// Everything OK, so commit the changes
// listenAddresses can fail to bind, so try them first
if (listenAddressesE) {
setListenAddresses(listenAddresses);
}
if (forwardAddressesE) {
setForwardAddresses(forwardAddresses);
}
return (isc::config::createAnswer());
} catch (const isc::Exception& error) {
if (impl_->verbose_mode_) {
......@@ -444,3 +521,62 @@ Recursor::updateConfig(ConstElementPtr new_config UNUSED_PARAM) {
return (isc::config::createAnswer(1, error.what()));
}
}
void
Recursor::setForwardAddresses(const vector<addr_t>& addresses)
{
impl_->setForwardAddresses(addresses, dnss_);
}
bool
Recursor::isForwarding() const {
return (!impl_->upstream_.empty());
}
vector<addr_t>
Recursor::getForwardAddresses() const {
return (impl_->upstream_);
}
namespace {
void
setAddresses(DNSService *service, const vector<addr_t>& addresses) {
service->clearServers();
BOOST_FOREACH(const addr_t &address, addresses) {
service->addServer(address.second, address.first);
}
}
}
void
Recursor::setListenAddresses(const vector<addr_t>& addresses) {
try {
setAddresses(dnss_, addresses);
impl_->listen_ = addresses;
}
catch (const exception& e) {
/*
* We couldn't set it. So return it back. If that fails as well,
* we have a problem.
*
* If that fails, bad luck, but we are useless anyway, so just die
* and let boss start us again.
*/
try {
setAddresses(dnss_, impl_->listen_);
}
catch (const exception& e2) {
cerr << "[b10-recurse] Unable to recover from error: " << e.what()
<< endl << "Rollback failed with: " << e2.what() << endl;
abort();
}
throw e; // Let it fly a little bit further
}
}
vector<addr_t>
Recursor::getListenAddresses() const {
return (impl_->listen_);
}
......@@ -18,6 +18,8 @@
#define __RECURSOR_H 1
#include <string>
#include <vector>
#include <utility>
#include <cc/data.h>
#include <config/ccsession.h>
......@@ -38,12 +40,7 @@ private:
Recursor& operator=(const Recursor& source);
public:
/// The constructor.
///
/// \param forward The address of the name server to which requests
/// should be forwarded. (In the future, when the server is running
/// in forwarding mode, the forward nameserver addresses will be set
/// via the config channel instaed.)
Recursor(const char& forward);
Recursor();
~Recursor();
//@}
......@@ -93,6 +90,34 @@ public:
/// \brief Return pointer to the Checkin callback function
asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
/**
* \brief Specify the list of upstream servers.
*
* Specify the list off addresses of upstream servers to forward queries
* to. If the list is empty, this server is set to full recursive mode.
* If it is non-empty, it switches to forwarder.
*
* @param addresses The list of addresses to use (each one is the address
* and port pair).
*/
void setForwardAddresses(const std::vector<std::pair<std::string,
uint16_t> >& addresses);
/**
* \short Get list of upstream addresses.
*
* \see setForwardAddresses.
*/
std::vector<std::pair<std::string, uint16_t> > getForwardAddresses() const;
/// Return if we are in forwarding mode (if not, we are in fully recursive)
bool isForwarding() const;
/**
* Set and get the addresses we listen on.
*/
void setListenAddresses(const std::vector<std::pair<std::string,
uint16_t> >& addresses);
std::vector<std::pair<std::string, uint16_t> > getListenAddresses() const;
private:
RecursorImpl* impl_;
asiolink::DNSService* dnss_;
......
......@@ -63,7 +63,7 @@ private:
class RecursorTest : public ::testing::Test {
protected:
RecursorTest() : server(*DEFAULT_REMOTE_ADDRESS),
RecursorTest() : server(),
request_message(Message::RENDER),
parse_message(new Message(Message::PARSE)),
default_qid(0x1035), opcode(Opcode(Opcode::QUERY())),
......@@ -72,7 +72,11 @@ protected:
io_message(NULL), endpoint(NULL), request_obuffer(0),
request_renderer(request_obuffer),
response_obuffer(new OutputBuffer(0))
{}
{
vector<pair<string, uint16_t> > upstream;
upstream.push_back(pair<string, uint16_t>(DEFAULT_REMOTE_ADDRESS, 53));
server.setForwardAddresses(upstream);
}
~RecursorTest() {
delete io_message;
delete endpoint;
......@@ -325,4 +329,181 @@ TEST_F(RecursorTest, notifyFail) {
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}
class RecursorConfig : public ::testing::Test {
public:
IOService ios;
DNSService dnss;
Recursor server;
RecursorConfig() :
dnss(ios, NULL, NULL, NULL)
{
server.setDNSService(dnss);
}
void invalidTest(const string &JOSN);
};
TEST_F(RecursorConfig, forwardAddresses) {
// Default value should be fully recursive
EXPECT_TRUE(server.getForwardAddresses().empty());
EXPECT_FALSE(server.isForwarding());
// Try putting there some addresses
vector<pair<string, uint16_t> > addresses;
addresses.push_back(pair<string, uint16_t>(DEFAULT_REMOTE_ADDRESS, 53));
addresses.push_back(pair<string, uint16_t>("::1", 53));
server.setForwardAddresses(addresses);
EXPECT_EQ(2, server.getForwardAddresses().size());
EXPECT_EQ("::1", server.getForwardAddresses()[1].first);
EXPECT_TRUE(server.isForwarding());
// Is it independent from what we do with the vector later?
addresses.clear();
EXPECT_EQ(2, server.getForwardAddresses().size());
// Did it return to fully recursive?
server.setForwardAddresses(addresses);
EXPECT_TRUE(server.getForwardAddresses().empty());
EXPECT_FALSE(server.isForwarding());
}
TEST_F(RecursorConfig, forwardAddressConfig) {
// Try putting there some address
ElementPtr 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());
ASSERT_EQ(1, server.getForwardAddresses().size());
EXPECT_EQ("192.0.2.1", server.getForwardAddresses()[0].first);
EXPECT_EQ(53, server.getForwardAddresses()[0].second);
// And then remove all addresses
config = Element::fromJSON("{"
"\"forward_addresses/\": null"
"}");
result = server.updateConfig(config);
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_FALSE(server.isForwarding());
EXPECT_EQ(0, server.getForwardAddresses().size());
}
void RecursorConfig::invalidTest(const string &JOSN) {
ElementPtr config(Element::fromJSON(JOSN));
EXPECT_FALSE(server.updateConfig(config)->equals(
*isc::config::createAnswer())) << "Accepted config " << JOSN << endl;