Commit f8d7452f authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

adapted command channel module with boost::asio. ugly hack, but works.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-asio@1223 e5f2f494-b856-4b98-b285-d166d9295462
parents 64593f39 07cfeddc
......@@ -4,19 +4,7 @@
"config_data": [
{ "item_name": "database_file",
"item_type": "string",
"item_optional": False,
"item_default": "b10-auth.db"
},
{ "item_name": "zone_list",
"item_type": "list",
"item_optional": False,
"item_default": [],
"list_item_spec":
{ "item_name": "zone_name",
"item_type": "string",
"item_optional": True,
"item_default": ""
}
"item_optional": True
}
],
"commands": [
......
......@@ -32,6 +32,8 @@
#include <dns/rrttl.h>
#include <dns/message.h>
#include <config/ccsession.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <auth/query.h>
#include <auth/data_source.h>
......@@ -65,18 +67,17 @@ public:
};
AuthSrvImpl::AuthSrvImpl() {
// add static data source
data_sources.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
// add SQL data source
Sqlite3DataSrc* sd = new Sqlite3DataSrc;
sd->init();
data_sources.addDataSrc(ConstDataSrcPtr(sd));
}
AuthSrv::AuthSrv()
{
impl_ = new AuthSrvImpl;
// set empty (sqlite) data source, once ccsession is up
// the datasource will be set by the configuration setting
// (or the default one if none is set)
cur_datasrc_ = ConstDataSrcPtr();
// add static data source
impl_->data_sources.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
}
AuthSrv::~AuthSrv()
......@@ -125,24 +126,52 @@ AuthSrv::processMessage(InputBuffer& request_buffer,
return (0);
}
void
AuthSrv::setDbFile(const std::string& db_file)
ElementPtr
AuthSrv::setDbFile(const isc::data::ElementPtr config)
{
cout << "Change data source file, call our data source's function to now read " << db_file << endl;
impl_->_db_file = db_file;
if (config) {
impl_->_db_file = config->get("database_file")->stringValue();
cout << "[AuthSrv] Data source database file: " << impl_->_db_file << endl;
}
try {
// create SQL data source
// config may be empty here; in that case it will load the default
// database file
Sqlite3DataSrc* sd = new Sqlite3DataSrc;
sd->init(config);
if (cur_datasrc_) {
impl_->data_sources.removeDataSrc(cur_datasrc_);
}
ConstDataSrcPtr csd = ConstDataSrcPtr(sd);
impl_->data_sources.addDataSrc(csd);
cur_datasrc_ = csd;
return isc::config::createAnswer(0);
} catch (isc::Exception error) {
cout << "[AuthSrv] error: " << error.what() << endl;
return isc::config::createAnswer(1, error.what());
}
}
ElementPtr
AuthSrv::updateConfig(isc::data::ElementPtr new_config)
{
ElementPtr answer = isc::config::createAnswer(0);
if (new_config) {
// the ModuleCCSession has already checked if we have
// the correct ElementPtr type as specified in our .spec file
if (new_config->contains("database_file")) {
// We only get this if the value has actually changed.
setDbFile(new_config->get("database_file")->stringValue());
answer = setDbFile(new_config);
}
}
// if we have no sqlite3 data source, use the default
if (!cur_datasrc_) {
setDbFile(ElementPtr());
}
return isc::config::createAnswer(0);
return answer;
}
......@@ -20,6 +20,7 @@
#include <string>
#include <cc/data.h>
#include <auth/data_source.h>
namespace isc {
namespace dns {
......@@ -50,10 +51,14 @@ public:
isc::dns::MessageRenderer& response_renderer,
bool udp_buffer);
void serve(std::string zone_name);
void setDbFile(const std::string& db_file);
isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
isc::data::ElementPtr updateConfig(isc::data::ElementPtr config);
private:
AuthSrvImpl* impl_;
/// We keep a pointer to the currently running sqlite datasource
/// so that we can specifically remove that one should the database
/// file change
isc::auth::ConstDataSrcPtr cur_datasrc_;
};
#endif // __AUTH_SRV_H
......
......@@ -283,15 +283,13 @@ usage() {
ElementPtr
my_config_handler(ElementPtr new_config)
{
auth_server->updateConfig(new_config);
return createAnswer(0);
return auth_server->updateConfig(new_config);
}
ElementPtr
my_command_handler(const string& command, const ElementPtr args) {
ElementPtr answer = createAnswer(0);
cout << "[XX] Handle command: " << endl << command << endl;
if (command == "print_message")
{
cout << args << endl;
......@@ -356,13 +354,15 @@ main(int argc, char* argv[]) {
} else {
specfile = string(AUTH_SPECFILE_LOCATION);
}
ModuleCCSession cs = ModuleCCSession(specfile, my_config_handler,
my_command_handler);
// XXX: in this prototype code we'll ignore any message on the command
// channel.
boost::asio::io_service io_service;
ModuleCCSession cs(specfile, io_service, my_config_handler,
my_command_handler);
if (use_ipv4) {
udp4_server = new UDPServer(io_service, AF_INET, port);
tcp4_server = new TCPServer(io_service, AF_INET, port);
......
......@@ -16,7 +16,8 @@
import sys; sys.path.append ('@@PYTHONPATH@@')
import re, getopt
import isc
import isc.auth
import isc.auth.master
#########################################################################
# usage: print usage note and exit
......@@ -55,13 +56,20 @@ def main():
zonefile = args[0]
try:
zone, zonedata = isc.auth.master.parse(zonefile, initial_origin)
zf = isc.auth.master.openzone(zonefile, initial_origin)
except Exception as e:
print("Error reading zone file: " + str(e))
exit(1)
try:
isc.auth.sqlite3_ds.load(dbfile, zone, zonedata)
zone = isc.auth.master.zonename(zf, initial_origin)
except Exception as e:
print("Error reading zone file: " + str(e))
exit(1)
try:
isc.auth.sqlite3_ds.load(dbfile, zone, isc.auth.master.zonedata, zf)
except Exception as e:
print("Error loading database: " + str(e))
exit(1)
......
......@@ -23,6 +23,7 @@ run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += .libs/libauth.a
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
endif
......
......@@ -781,6 +781,19 @@ MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src)
data_sources.push_back(data_src);
}
void
MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src)
{
std::vector<ConstDataSrcPtr>::iterator it, itr;
for (it = data_sources.begin(); it != data_sources.end(); it++) {
if (*it == data_src) {
itr = it;
}
}
data_sources.erase(itr);
}
void
MetaDataSrc::findClosestEnclosure(NameMatch& match, const RRClass& qclass) const
{
......
......@@ -25,6 +25,7 @@
#include <dns/name.h>
#include <dns/rrclass.h>
#include <cc/data.h>
namespace isc {
......@@ -102,6 +103,7 @@ public:
// Optional 'low-level' methods. These will have stub implementations
// in the general DataSrc class but MAY be overwritten by subclasses
virtual Result init() = 0;
virtual Result init(const isc::data::ElementPtr config) = 0;
virtual Result close() = 0;
// Mandatory 'low-level' methods: These will NOT be implemented by
......@@ -181,6 +183,7 @@ public:
void setClass(const isc::dns::RRClass& c) { rrclass = c; }
Result init() { return NOT_IMPLEMENTED; }
Result init(const isc::data::ElementPtr config) { return NOT_IMPLEMENTED; }
Result close() { return NOT_IMPLEMENTED; }
virtual Result findRRset(const Query& q,
......@@ -245,6 +248,9 @@ public:
//@}
void addDataSrc(ConstDataSrcPtr data_src);
void removeDataSrc(ConstDataSrcPtr data_src);
size_t dataSrcCount() { return data_sources.size(); };
void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass) const;
......
......@@ -487,18 +487,15 @@ Sqlite3DataSrc::~Sqlite3DataSrc() {
}
DataSrc::Result
Sqlite3DataSrc::init(const string& dbfile) {
open(dbfile);
cerr << "Schema version: " << getVersion() << endl;
Sqlite3DataSrc::init(const isc::data::ElementPtr config) {
if (config and config->contains("database_file")) {
open(config->get("database_file")->stringValue());
} else {
open(DEFAULT_DB_FILE);
}
return (SUCCESS);
}
DataSrc::Result
Sqlite3DataSrc::init() {
return (init(DEFAULT_DB_FILE));
}
void
Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
const RRClass& qclass) const {
......
......@@ -102,8 +102,8 @@ public:
std::string& hash,
isc::dns::RRsetList& target) const;
Result init();
Result init(const std::string& dbfile);
Result init() { return init(isc::data::ElementPtr()); };
Result init(const isc::data::ElementPtr config);
Result close();
private:
......
......@@ -29,6 +29,7 @@
#include <dns/rrtype.h>
#include <dns/rdataclass.h>
#include <dns/rrsetlist.h>
#include <cc/data.h>
#include "query.h"
#include "data_source.h"
......@@ -38,15 +39,19 @@ using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::auth;
using namespace isc::data;
namespace {
static const char* SQLITE_DBFILE_EXAMPLE = "testdata/test.sqlite3";
static const char* SQLITE_DBFILE_EXAMPLE2 = "testdata/test2.sqlite3";
static ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
"{ \"database_file\": \"testdata/test.sqlite3\"}");
static ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
"{ \"database_file\": \"testdata/test2.sqlite3\"}");
// The following file must be non existent and mutt be "creatable";
// the sqlite3 library will try to create a new DB file if it doesn't exist,
// so to test a failure case the create operation should also fail.
// The "nodir", a non existent directory, is inserted for this purpose.
static const char* SQLITE_DBFILE_NOTEXIST = "testdata/nodir/notexist";
static ElementPtr SQLITE_DBFILE_NOTEXIST = Element::createFromString(
"{ \"database_file\": \"testdata/nodir/notexist\"}");
static const string sigdata_common(" 20100322084538 20100220084538 "
"33495 example.com. FAKEFAKEFAKEFAKE");
......@@ -350,6 +355,7 @@ TEST_F(Sqlite3DataSourceTest, reOpen) {
// Replace the data with a totally different zone. This should succeed,
// and shouldn't match any names in the previously managed domains.
EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
NameMatch name_match(www_name);
......
......@@ -88,6 +88,7 @@ public:
isc::dns::RRsetList& target) const;
Result init();
Result init(const isc::data::ElementPtr config) { return init(); };
Result close();
private:
StaticDataSrcImpl* impl_;
......
......@@ -27,6 +27,7 @@
#include <dns/rrtype.h>
#include <dns/rdataclass.h>
#include <dns/rrsetlist.h>
#include <cc/data.h>
#include "query.h"
#include "data_source.h"
......
......@@ -489,5 +489,15 @@ TEST_F(DataSrcTest, Nsec3Hash) {
EXPECT_EQ("FHA27EURONFH5640SFJQ8MJAKMCVB7UJ", nsec3.getHash(Name("test2")));
EXPECT_EQ("A4M93LR7A60IDDQMO6TCVUPCC60CU38A", nsec3.getHash(Name("test3")));
}
TEST_F(DataSrcTest, AddRemoveDataSrc) {
MetaDataSrc ds;
ConstDataSrcPtr tsp = ConstDataSrcPtr(new TestDataSrc);
EXPECT_EQ(0, ds.dataSrcCount());
ds.addDataSrc(tsp);
EXPECT_EQ(1, ds.dataSrcCount());
ds.removeDataSrc(tsp);
EXPECT_EQ(0, ds.dataSrcCount());
}
}
......@@ -95,6 +95,12 @@ RRsetPtr loop1_cname;
RRsetPtr loop2_cname;
}
DataSrc::Result
TestDataSrc::init(const isc::data::ElementPtr config)
{
return init();
}
DataSrc::Result
TestDataSrc::init() {
if (initialized) {
......
......@@ -92,6 +92,7 @@ public:
isc::dns::RRsetList& target) const;
Result init();
Result init(const isc::data::ElementPtr config);
Result close() { return (SUCCESS); }
private:
......
......@@ -14,44 +14,204 @@
// $Id$
#include "data.h"
#include "session.h"
#include <stdint.h>
#include <cstdio>
#include <vector>
#include <iostream>
#include <sstream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <exceptions/exceptions.h>
#include "data.h"
#include "session.h"
using namespace std;
using namespace isc::cc;
using namespace isc::data;
// some of the boost::asio names conflict with socket API system calls
// (e.g. write(2)) so we don't import the entire boost::asio namespace.
using boost::asio::io_service;
using boost::asio::ip::tcp;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Session::Session()
{
sock = -1;
sequence = 1;
namespace isc {
namespace cc {
class SessionImpl {
public:
SessionImpl() : sequence_(-1) {}
virtual ~SessionImpl() {}
virtual void establish() = 0;
virtual int getSocket() = 0;
virtual void disconnect() = 0;
virtual void writeData(const void* data, size_t datalen) = 0;
virtual size_t readDataLength() = 0;
virtual void readData(void* data, size_t datalen) = 0;
virtual void startRead(boost::function<void()> user_handler) = 0;
int sequence_; // the next sequence number to use
std::string lname_;
};
class ASIOSession : public SessionImpl {
public:
ASIOSession(io_service& io_service) :
io_service_(io_service), socket_(io_service_), data_length_(0)
{}
virtual void establish();
virtual void disconnect();
virtual int getSocket() { return (socket_.native()); }
virtual void writeData(const void* data, size_t datalen);
virtual size_t readDataLength();
virtual void readData(void* data, size_t datalen);
virtual void startRead(boost::function<void()> user_handler);
private:
void internalRead(const boost::system::error_code& error,
size_t bytes_transferred);
private:
io_service& io_service_;
tcp::socket socket_;
uint32_t data_length_;
boost::function<void()> user_handler_;
boost::system::error_code error_;
};
void
ASIOSession::establish() {
socket_.connect(tcp::endpoint(boost::asio::ip::address_v4::loopback(),
9912), error_);
if (error_) {
isc_throw(SessionError, "Unable to connect to message queue");
}
}
void
Session::disconnect()
{
close(sock);
sock = -1;
ASIOSession::disconnect() {
socket_.close();
data_length_ = 0;
}
void
Session::establish()
ASIOSession::writeData(const void* data, size_t datalen) {
try {
boost::asio::write(socket_, boost::asio::buffer(data, datalen));
} catch (const boost::system::system_error& boost_ex) {
isc_throw(SessionError, "ASIO write failed: " << boost_ex.what());
}
}
size_t
ASIOSession::readDataLength() {
size_t ret_len = data_length_;
if (ret_len == 0) {
readData(&data_length_, sizeof(data_length_));
if (data_length_ == 0) {
isc_throw(SessionError, "ASIO read: data length is not ready");
}
ret_len = ntohl(data_length_);
}
data_length_ = 0;
return (ret_len);
}
void
ASIOSession::readData(void* data, size_t datalen) {
try {
boost::asio::read(socket_, boost::asio::buffer(data, datalen));
} catch (const boost::system::system_error& boost_ex) {
// to hide boost specific exceptions, we catch them explicitly
// and convert it to SessionError.
isc_throw(SessionError, "ASIO read failed: " << boost_ex.what());
}
}
void
ASIOSession::startRead(boost::function<void()> user_handler) {
data_length_ = 0;
user_handler_ = user_handler;
async_read(socket_, boost::asio::buffer(&data_length_,
sizeof(data_length_)),
boost::bind(&ASIOSession::internalRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void
ASIOSession::internalRead(const boost::system::error_code& error,
size_t bytes_transferred)
{
int ret;
if (!error) {
assert(bytes_transferred == sizeof(data_length_));
data_length_ = ntohl(data_length_);
if (data_length_ == 0) {
isc_throw(SessionError, "Invalid message length (0)");
}
user_handler_();
} else {
isc_throw(SessionError, "asynchronous read failed");
}
}
class SocketSession : public SessionImpl {
public:
SocketSession() : sock_(-1) {}
virtual ~SocketSession() { disconnect(); }
virtual int getSocket() { return (sock_); }
void establish();
virtual void disconnect()
{
if (sock_ >= 0) {
close(sock_);
}
sock_ = -1;
}
virtual void writeData(const void* data, size_t datalen);
virtual void readData(void* data, size_t datalen);
virtual size_t readDataLength();
virtual void startRead(boost::function<void()> user_handler)
{} // nothing to do for this class
private:
int sock_;
};
namespace { // maybe unnecessary.
// This is a helper class to make the establish() method (below) exception-safe
// with the RAII approach.
class SessionHolder {
public:
SessionHolder(SessionImpl* obj) : impl_obj_(obj) {}
~SessionHolder()
{
if (impl_obj_ != NULL) {
impl_obj_->disconnect();
}
}
void clear() { impl_obj_ = NULL; }
SessionImpl* impl_obj_;
};
}
void
SocketSession::establish() {
int s;
struct sockaddr_in sin;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < -1)
throw SessionError("socket() failed");
s =