summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--src/bin/Makefile.am2
-rw-r--r--src/bin/auth/Makefile.am1
-rw-r--r--src/bin/auth/main.cc107
-rw-r--r--src/bin/bind10/bind10.py.in18
-rw-r--r--src/bin/xfrout/Makefile.am18
-rw-r--r--src/bin/xfrout/run_b10-xfrout.sh.in12
-rw-r--r--src/bin/xfrout/xfrout.pre.in27
-rw-r--r--src/bin/xfrout/xfrout.py.in425
-rw-r--r--src/lib/Makefile.am2
-rw-r--r--src/lib/python/isc/auth/sqlite3_ds.py34
-rw-r--r--src/lib/xfr/Makefile.am31
-rw-r--r--src/lib/xfr/fd_share.cc102
-rw-r--r--src/lib/xfr/fd_share.h19
-rw-r--r--src/lib/xfr/python_xfr.cc38
-rw-r--r--src/lib/xfr/xfrout_client.cc59
-rw-r--r--src/lib/xfr/xfrout_client.h41
17 files changed, 909 insertions, 33 deletions
diff --git a/configure.ac b/configure.ac
index d618490105..8b6c7e5e37 100644
--- a/configure.ac
+++ b/configure.ac
@@ -361,6 +361,7 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/Makefile
src/bin/auth/tests/Makefile
src/bin/xfrin/Makefile
+ src/bin/xfrout/Makefile
src/bin/usermgr/Makefile
src/lib/Makefile
src/lib/cc/Makefile
@@ -376,6 +377,7 @@ AC_CONFIG_FILES([Makefile
src/lib/exceptions/Makefile
src/lib/auth/Makefile
src/lib/auth/tests/Makefile
+ src/lib/xfr/Makefile
])
AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/bin/cmdctl/cmdctl.py
@@ -385,6 +387,9 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/bin/xfrin/xfrin.py
src/bin/xfrin/xfrin.pre
src/bin/xfrin/run_b10-xfrin.sh
+ src/bin/xfrout/xfrout.py
+ src/bin/xfrout/xfrout.pre
+ src/bin/xfrout/run_b10-xfrout.sh
src/bin/bind10/bind10.py
src/bin/bind10/tests/bind10_test
src/bin/bind10/run_bind10.sh
@@ -408,6 +413,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
], [
chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/xfrin/run_b10-xfrin.sh
+ chmod +x src/bin/xfrout/run_b10-xfrout.sh
chmod +x src/bin/bind10/run_bind10.sh
chmod +x src/bin/cmdctl/unittest/cmdctl_test
chmod +x src/bin/xfrin/unittest/xfrin_test
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 7b331018f2..e91b658244 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1 +1 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin usermgr
+SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout usermgr
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index ae22bd34ab..e90584f106 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -33,6 +33,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.a
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
b10_auth_LDADD += $(SQLITE_LIBS)
+b10_auth_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a
if HAVE_BOOST_SYSTEM
b10_auth_LDFLAGS = $(AM_LDFLAGS) $(BOOST_LDFLAGS)
b10_auth_LDADD += $(BOOST_SYSTEM_LIB)
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 1e1307ae99..96884dddd1 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -42,12 +42,14 @@
#include <cc/session.h>
#include <cc/data.h>
#include <config/ccsession.h>
+#include <xfr/xfrout_client.h>
#include "spec_config.h"
#include "common.h"
#include "auth_srv.h"
using namespace std;
+using namespace isc::xfr;
#ifdef HAVE_BOOST_SYSTEM
using namespace boost::asio;
@@ -103,7 +105,38 @@ my_command_handler(const string& command, const ElementPtr args) {
return answer;
}
-#ifdef HAVE_BOOST_SYSTEM
+//TODO. The sample way for checking axfr query, the code should be merged to auth server class
+static bool
+check_axfr_query(char *msg_data, uint16_t msg_len)
+{
+ if (msg_len < 15)
+ return false;
+
+ uint16_t query_type = *(uint16_t *)(msg_data + (msg_len - 4));
+ if ( query_type == 0xFC00)
+ return true;
+
+ return false;
+}
+
+//TODO. Send the xfr query to xfrout module, the code should be merged to auth server class
+static void
+dispatch_axfr_query(int tcp_sock, char axfr_query[], uint16_t query_len)
+{
+ std::string path = "/tmp/auth_xfrout_conn";
+ XfroutClient xfr_client(path);
+ try {
+ xfr_client.connect();
+ xfr_client.sendXfroutRequestInfo(tcp_sock, (uint8_t *)axfr_query, query_len);
+ xfr_client.disconnect();
+ }
+ catch (const std::exception & err) {
+ //if (verbose_mode)
+ cerr << "error handle xfr query:" << err.what() << endl;
+ }
+}
+
+#ifdef HAVE_BOOSTLIB
//
// Helper classes for asynchronous I/O using boost::asio
//
@@ -148,17 +181,24 @@ public:
{
if (!error) {
InputBuffer dnsbuffer(data_, bytes_transferred);
- if (auth_server->processMessage(dnsbuffer, dns_message_,
- response_renderer_, false)) {
- responselen_buffer_.writeUint16(response_buffer_.getLength());
- async_write(socket_,
- boost::asio::buffer(
- responselen_buffer_.getData(),
- responselen_buffer_.getLength()),
- boost::bind(&TCPClient::responseWrite, this,
- placeholders::error));
- } else {
- delete this;
+ if (check_axfr_query(data_, bytes_transferred)) {
+ dispatch_axfr_query(socket_.native(), data_, bytes_transferred);
+ // start to get new query ?
+ start();
+ }
+ else {
+ if (auth_server->processMessage(dnsbuffer, dns_message_,
+ response_renderer_, false)) {
+ responselen_buffer_.writeUint16(response_buffer_.getLength());
+ async_write(socket_,
+ boost::asio::buffer(
+ responselen_buffer_.getData(),
+ responselen_buffer_.getLength()),
+ boost::bind(&TCPClient::responseWrite, this,
+ placeholders::error));
+ } else {
+ delete this;
+ }
}
} else {
delete this;
@@ -537,25 +577,30 @@ processMessageTCP(const int fd, Message& dns_message,
cc += cc0;
}
- InputBuffer buffer(&message_buffer[0], size);
- dns_message.clear(Message::PARSE);
- response_renderer.clear();
- if (auth_server->processMessage(buffer, dns_message, response_renderer,
- false)) {
- size = response_renderer.getLength();
- size_n = htons(size);
- if (send(ts, &size_n, 2, 0) == 2) {
- cc = send(ts, response_renderer.getData(),
- response_renderer.getLength(), 0);
- if (cc == -1) {
- if (verbose_mode) {
- cerr << "[AuthSrv] error in sending TCP response message" <<
- endl;
- }
- } else {
- if (verbose_mode) {
- cerr << "[XX] sent TCP response: " << cc << " bytes"
- << endl;
+ if (check_axfr_query(&message_buffer[0], size)) {
+ dispatch_axfr_query(ts, &message_buffer[0], size);
+ }
+ else {
+ InputBuffer buffer(&message_buffer[0], size);
+ dns_message.clear(Message::PARSE);
+ response_renderer.clear();
+ if (auth_server->processMessage(buffer, dns_message, response_renderer,
+ false)) {
+ size = response_renderer.getLength();
+ size_n = htons(size);
+ if (send(ts, &size_n, 2, 0) == 2) {
+ cc = send(ts, response_renderer.getData(),
+ response_renderer.getLength(), 0);
+ if (cc == -1) {
+ if (verbose_mode) {
+ cerr << "[AuthSrv] error in sending TCP response message" <<
+ endl;
+ }
+ } else {
+ if (verbose_mode) {
+ cerr << "[XX] sent TCP response: " << cc << " bytes"
+ << endl;
+ }
}
}
} else {
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
index 01223dcaa9..5ef69a5077 100644
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -265,6 +265,21 @@ class BoB:
if self.verbose:
print("[XX] ccsession started")
+ # start the xfrout before auth-server, to make sure every xfr-query can be
+ # processed properly.
+ if self.verbose:
+ sys.stdout.write("Starting b10-xfrout\n")
+ try:
+ xfrout = ProcessInfo("b10-xfrout", ["b10-xfrout"],
+ { 'ISC_MSGQ_PORT': str(self.c_channel_port)})
+ except Exception as e:
+ c_channel.process.kill()
+ bind_cfgd.process.kill()
+ return "Unable to start b10-xfrout; " + str(e)
+ self.processes[xfrout.pid] = xfrout
+ if self.verbose:
+ sys.stdout.write("Started b10-xfrout (PID %d)\n" % xfrout.pid)
+
# start b10-auth
# XXX: this must be read from the configuration manager in the future
authargs = ['b10-auth', '-p', str(self.auth_port)]
@@ -278,6 +293,7 @@ class BoB:
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
+ xfrout.process.kill()
return "Unable to start b10-auth; " + str(e)
self.processes[auth.pid] = auth
if self.verbose:
@@ -292,6 +308,7 @@ class BoB:
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
+ xfrout.process.kill()
auth.process.kill()
return "Unable to start b10-xfrin; " + str(e)
self.processes[xfrind.pid] = xfrind
@@ -308,6 +325,7 @@ class BoB:
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
+ xfrout.process.kill()
auth.process.kill()
xfrind.process.kill()
return "Unable to start b10-cmdctl; " + str(e)
diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am
new file mode 100644
index 0000000000..1284ba6d5e
--- /dev/null
+++ b/src/bin/xfrout/Makefile.am
@@ -0,0 +1,18 @@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+pkglibexec_SCRIPTS = b10-xfrout
+
+b10_xfroutdir = $(DESTDIR)$(pkgdatadir)
+b10_xfrout_DATA = xfrout.spec
+
+CLEANFILES= xfrout.py xfrout.pre xfrout.spec
+
+xfrout.spec: xfrout.pre
+ $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.pre >$@
+
+# TODO: does this need $$(DESTDIR) also?
+# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
+b10-xfrout: xfrout.py
+ $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
+ -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.py >$@
+ chmod a+x $@
diff --git a/src/bin/xfrout/run_b10-xfrout.sh.in b/src/bin/xfrout/run_b10-xfrout.sh.in
new file mode 100644
index 0000000000..9be34921e7
--- /dev/null
+++ b/src/bin/xfrout/run_b10-xfrout.sh.in
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
+export PYTHON_EXEC
+
+MYPATH_PATH=@abs_top_builddir@/src/bin/xfrout
+PYTHONPATH=@abs_top_srcdir@/src/lib/python
+export PYTHONPATH
+
+cd ${MYPATH_PATH}
+${PYTHON_EXEC} b10-xfrout
+
diff --git a/src/bin/xfrout/xfrout.pre.in b/src/bin/xfrout/xfrout.pre.in
new file mode 100644
index 0000000000..495d3fd2a0
--- /dev/null
+++ b/src/bin/xfrout/xfrout.pre.in
@@ -0,0 +1,27 @@
+{
+ "module_spec": {
+ "module_name": "Xfrout",
+ "config_data": [
+ {
+ "item_name": "transfers_out",
+ "item_type": "integer",
+ "item_optional": False,
+ "item_default": 10
+ },
+ {
+ "item_name": "db_file",
+ "item_type": "string",
+ "item_optional": False,
+ "item_default": '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
+ }
+ ],
+ "commands": [
+ {
+ "command_name": "shutdown",
+ "command_description": "Shut down Xfrout",
+ "command_args": []
+ }
+ ]
+ }
+}
+
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
new file mode 100644
index 0000000000..d909f43e16
--- /dev/null
+++ b/src/bin/xfrout/xfrout.py.in
@@ -0,0 +1,425 @@
+#!@PYTHON@
+
+# Copyright (C) 2010 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import isc
+import isc.cc
+import os
+import threading
+import struct
+import signal
+from isc.auth import sqlite3_ds
+from socketserver import *
+import os
+from isc.config.ccsession import *
+import socket
+from bind10_xfr import *
+from bind10_dns import *
+from optparse import OptionParser, OptionValueError
+try:
+ from bind10_xfr import *
+ from bind10_dns import *
+except:
+ pass
+
+if "B10_FROM_SOURCE" in os.environ:
+ SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/xfrout"
+else:
+ PREFIX = "@prefix@"
+ DATAROOTDIR = "@datarootdir@"
+ SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
+
+MAX_TRANSFERS_OUT = 10
+verbose_mode = True
+
+
+class XfroutException(Exception): pass
+
+
+class XfroutSession(BaseRequestHandler):
+ def handle(self):
+ fd = recv_fd(self.request.fileno())
+ data_len = self.request.recv(2)
+ msg_len = struct.unpack('H', data_len)[0]
+ msgdata = self.request.recv(msg_len)
+ sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ self.dns_xfrout_start(sock, msgdata)
+ except Exception as e:
+ self.log_msg(str(e))
+
+ sock.close()
+
+ def _parse_query_message(self, mdata):
+ ''' parse query message to [socket,message]'''
+ #TODO, need to add parseHeader() in case the message header is invalid
+ try:
+ msg = message(message_mode.PARSE)
+ msg.from_wire(input_buffer(mdata))
+ except Exception as err:
+ self.log_msg(str(err))
+ return rcode.FORMERR(), None
+
+ return rcode.NOERROR(), msg
+
+
+ def _get_query_zone_name(self, msg):
+ q_iter = question_iter(msg)
+ question = q_iter.get_question()
+ return question.get_name().to_text()
+
+
+ def _send_data(self, sock, data):
+ size = len(data)
+ total_count = 0
+ while total_count < size:
+ count = sock.send(data[total_count:])
+ total_count += count
+
+
+ def _send_message(self, sock, msg):
+ obuf = output_buffer(0)
+ render = message_render(obuf)
+ msg.to_wire(render)
+ header_len = struct.pack('H', socket.htons(obuf.get_length()))
+ self._send_data(sock, header_len)
+ self._send_data(sock, obuf.get_data())
+
+
+ def _reply_query_with_error_rcode(self, msg, sock, rcode_):
+ msg.make_response()
+ msg.set_rcode(rcode_)
+ self._send_message(sock, msg)
+
+
+ def _reply_query_with_format_error(self, sock, msg):
+ '''query message format isn't legal.'''
+ if not msg:
+ return # query message is invalid. send nothing back.
+
+ msg.make_response()
+ msg.set_rcode(rcode.FORMERR())
+ self._send_message(sock, msg)
+
+
+ def _zone_is_empty(self, zone):
+ if sqlite3_ds.get_zone_soa(zone, self.server.get_db_file()):
+ return False
+
+ return True
+
+ def _zone_exist(self, zonename):
+ # Find zone in datasource, should this works? maybe should ask
+ # config manager.
+ soa = sqlite3_ds.get_zone_soa(zonename, self.server.get_db_file())
+ if soa:
+ return True
+ return False
+
+
+ def _check_xfrout_available(self, zone_name):
+ '''Check if xfr request can be responsed.
+ TODO, Get zone's configuration from cfgmgr or some other place
+ eg. check allow_transfer setting,
+ '''
+ if not self._zone_exist(zone_name):
+ return rcode.NOTAUTH()
+
+ if self._zone_is_empty(zone_name):
+ return rcode.SERVFAIL()
+
+ #TODO, check allow_transfer
+ if not self.server.increase_transfers_counter():
+ return rcode.REFUSED()
+
+ return rcode.NOERROR()
+
+
+ def dns_xfrout_start(self, sock, msg_query):
+ rcode_, msg = self._parse_query_message(msg_query)
+ #TODO. create query message and parse header
+ if rcode_ != rcode.NOERROR():
+ return self._reply_query_with_format_error(msg, sock, msg_query)
+
+ zone_name = self._get_query_zone_name(msg)
+ rcode_ = self._check_xfrout_available(zone_name)
+ if rcode_ != rcode.NOERROR():
+ return self. _reply_query_with_error_rcode(msg, sock, rcode_)
+
+ try:
+ if verbose_mode:
+ self.log_msg("transfer of '%s/IN': AXFR started" % zone_name)
+
+ self._reply_xfrout_query(msg, sock, zone_name)
+
+ if verbose_mode:
+ self.log_msg("transfer of '%s/IN': AXFR end" % zone_name)
+ except Exception as err:
+ if verbose_mode:
+ sys.stderr.write(str(err))
+
+ self.server.decrease_transfers_counter()
+ return
+
+
+ def _clear_message(self, msg):
+ qid = msg.get_qid()
+ opcode = msg.get_opcode()
+ rcode = msg.get_rcode()
+
+ msg.clear(message_mode.RENDER)
+ msg.set_qid(qid)
+ msg.set_opcode(opcode)
+ msg.set_rcode(rcode)
+ msg.set_header_flag(message_flag.AA())
+ msg.set_header_flag(message_flag.QR())
+ return msg
+
+ def _create_rrset_from_db_record(self, record):
+ '''Create one rrset from one record of datasource, if the schema of record is changed,
+ This function should be updated first.
+ '''
+ rrtype_ = rr_type(record[5])
+ rdata_ = create_rdata(rrtype_, rr_class.IN(), " ".join(record[7:]))
+ rrset_ = rrset(name(record[2]), rr_class.IN(), rrtype_, rr_ttl( int(record[4])))
+ rrset_.add_rdata(rdata_)
+ return rrset_
+
+ def _send_message_with_last_soa(self, msg, sock, rrset_soa):
+ '''Add the SOA record to the end of message. If it can't be
+ added, a new message should be created to send out the last soa .
+ '''
+
+ obuf = output_buffer(0)
+ render = message_render(obuf)
+ msg.to_wire(render)
+ old_message_len = obuf.get_length()
+ msg.add_rrset(section.ANSWER(), rrset_soa)
+
+ msg.to_wire(render)
+ message_len = obuf.get_length()
+
+ if message_len != old_message_len:
+ self._send_message(sock, msg)
+ else:
+ msg = self._clear_message(msg)
+ msg.add_rrset(section.ANSWER(), rrset_soa)
+ self._send_message(sock, msg)
+
+ def _get_message_len(self, msg):
+ '''Get message length, every time need do like this? Actually there should be
+ a better way, I need check with jinmei later.
+ '''
+
+ obuf = output_buffer(0)
+ render = message_render(obuf)
+ msg.to_wire(render)
+ return obuf.get_length()
+
+
+ def _reply_xfrout_query(self, msg, sock, zone_name):
+ #TODO, there should be a better way to insert rrset.
+ msg.make_response()
+ msg.set_header_flag(message_flag.AA())
+ soa_record = sqlite3_ds.get_zone_soa(zone_name, self.server.get_db_file())
+ rrset_soa = self._create_rrset_from_db_record(soa_record)
+ msg.add_rrset(section.ANSWER(), rrset_soa)
+
+ old_message_len = 0
+ # TODO, Since add_rrset() return nothing when rrset can't be added, so I have to compare
+ # the message length to know if the rrset has been added sucessfully.
+ for rr_data in sqlite3_ds.get_zone_datas(zone_name, self.server.get_db_file()):
+ if self.server._shutdown_event.is_set(): # Check if xfrout is shutdown
+ raise XfroutException("shutdown!")
+
+ if rr_type(rr_data[5]) == rr_type.SOA(): #ignore soa record
+ continue
+
+ rrset_ = self._create_rrset_from_db_record(rr_data)
+ msg.add_rrset(section.ANSWER(), rrset_)
+ message_len = self._get_message_len(msg)
+ if message_len != old_message_len:
+ old_message_len = message_len
+ continue
+
+ self._send_message(sock, msg)
+ msg = self._clear_message(msg)
+ msg.add_rrset(section.ANSWER(), rrset_) # Add the rrset to the new message
+ old_message_len = 0
+
+ self._send_message_with_last_soa(msg, sock, rrset_soa)
+
+ def log_msg(self, msg):
+ print('[b10-xfrout] ', msg)
+
+
+class UnixSockServer(ThreadingUnixStreamServer):
+ '''The unix domain socket server which accept xfr query sent from auth server.'''
+
+ def __init__(self, sock_file, handle_class, shutdown_event, config_data):
+ ThreadingUnixStreamServer.__init__(self, sock_file, handle_class)
+ self._lock = threading.Lock()
+ self._transfers_counter = 0
+ self._shutdown_event = shutdown_event
+ self.update_config_data(config_data)
+
+
+ def update_config_data(self, new_config):
+ '''Apply the new config setting of xfrout module. '''
+
+ self._lock.acquire()
+ self._max_transfers_out = new_config.get('transfers_out')
+ self._db_file = new_config.get('db_file')
+ self._lock.release()
+
+ def get_db_file(self):
+ self._lock.acquire()
+ file = self._db_file
+ self._lock.release()
+ return file
+
+ def increase_transfers_counter(self):
+ '''Return False, if counter + 1 > max_transfers_out, or else
+ return True
+ '''
+ ret = False
+ self._lock.acquire()
+ if self._transfers_counter < self._max_transfers_out:
+ self._transfers_counter += 1
+ ret = True
+ self._lock.release()
+ return ret
+
+ def decrease_transfers_counter(self):
+ self._lock.acquire()
+ self._transfers_counter -= 1
+ self._lock.release()
+
+
+def listen_on_xfr_query(unix_socket_server):
+ '''Listen xfr query in one single thread. '''
+ unix_socket_server.serve_forever()
+
+
+class XfroutServer:
+ def __init__(self):
+ self._init_config_data()
+ self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
+ self._cc.start()
+ self._lock = threading.Lock()
+ self._shutdown_event = threading.Event()
+ self._listen_sock_file = '/tmp/auth_xfrout_conn' # TODO, should this be configurable in cfgmgr
+ self._start_xfr_query_listener()
+
+ def _start_xfr_query_listener(self):
+ '''Start a new thread to accept xfr query. '''
+ try:
+ os.unlink(self._listen_sock_file)
+ except:
+ pass
+
+ self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession,
+ self._shutdown_event, self._config_data);
+ listener = threading.Thread(target = listen_on_xfr_query, args = (self._unix_socket_server,))
+ listener.start()
+
+
+ def _init_config_data(self):
+ '''Init the config item here. In case there is some error in config data got from cfgmgr.'''
+ self._config_data = {'transfers_out': 10, 'db_file':'/tmp/zone.sqlite3'}
+
+
+ def config_handler(self, new_config):
+ '''Update config data. TODO. Do error check'''
+
+ answer = create_answer(0)
+ for key in new_config:
+ if key not in self._config_data:
+ answer = create_answer(1, "Unknown config data: " + str(key))
+ continue
+ self._config_data[key] = new_config[key]
+
+ return answer
+
+
+ def shutdown(self):
+ ''' shutdown the xfrin process. the thread which is doing xfrin should be
+ terminated.
+ '''
+ self._shutdown_event.set()
+ self._unix_socket_server.shutdown()
+
+ main_thread = threading.currentThread()
+ for th in threading.enumerate():
+ if th is main_thread:
+ continue
+ th.join()
+
+
+ def command_handler(self, cmd, args):
+ if cmd == "shutdown":
+ if verbose_mode:
+ log_msg("Received shutdown command")
+ self.shutdown()
+ answer = create_answer(0)
+ else:
+ answer = create_answer(1, "Unknown command:" + str(cmd))
+
+ return answer
+
+
+ def run(self):
+ '''Get and process all commands sent from cfgmgr or other modules. '''
+ while not self._shutdown_event.is_set():
+ self._cc.check_command()
+
+
+xfrout_server = None
+
+def signal_handler(signal, frame):
+ if xfrout_server:
+ xfrout_server.shutdown()
+ sys.exit(0)
+
+def set_signal_handler():
+ signal.signal(signal.SIGTERM, signal_handler)
+ signal.signal(signal.SIGINT, signal_handler)
+
+def set_cmd_options(parser):
+ parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
+ help="display more about what is going on")
+
+if '__main__' == __name__:
+ try:
+ parser = OptionParser()
+ set_cmd_options(parser)
+ (options, args) = parser.parse_args()
+ verbose_mode = options.verbose
+
+ set_signal_handler()
+ xfrout_server = XfroutServer()
+ xfrout_server.run()
+ except KeyboardInterrupt:
+ print("[b10-xfrout] exit xfrout process")
+ except Exception as e:
+ print('[b10-xfrout] ', e)
+
+ if xfrout_server:
+ xfrout_server.shutdown()
+
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index ed52529fb2..cd262a1d74 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1 +1 @@
-SUBDIRS = exceptions dns cc config auth python
+SUBDIRS = exceptions dns cc config auth xfr python
diff --git a/src/lib/python/isc/auth/sqlite3_ds.py b/src/lib/python/isc/auth/sqlite3_ds.py
index 014494edfc..937c4e7148 100644
--- a/src/lib/python/isc/auth/sqlite3_ds.py
+++ b/src/lib/python/isc/auth/sqlite3_ds.py
@@ -84,6 +84,40 @@ def open(dbfile):
return conn, cur
+
+#########################################################################
+# get_zone_datas
+# returns all the records for one zone with the given zone name.
+#########################################################################
+def get_zone_datas(zonename, dbfile):
+ conn, cur = open(dbfile)
+ zone_id = get_zoneid(zonename, cur)
+
+ cur.execute("SELECT * FROM records WHERE zone_id = ?", [zone_id])
+ record = cur.fetchone()
+ while record:
+ yield record
+ record = cur.fetchone()
+
+ cur.close()
+ conn.close()
+
+
+#########################################################################
+# get_zone_soa
+# returns the soa record of the zone with the given zone name.
+# If the zone doesn't exist, return None.
+#########################################################################
+def get_zone_soa(zonename, dbfile):
+ conn, cur = open(dbfile)
+ id = get_zoneid(zonename, cur)
+ cur.execute("SELECT * FROM records WHERE zone_id = ? and rdtype = ?", [id, 'SOA'])
+ datas = cur.fetchone()
+ cur.close()
+ conn.close()
+
+ return datas
+
#########################################################################
# get_zoneid:
# returns the zone_id for a given zone name, or an empty
diff --git a/src/lib/xfr/Makefile.am b/src/lib/xfr/Makefile.am
new file mode 100644
index 0000000000..1f481992b4
--- /dev/null
+++ b/src/lib/xfr/Makefile.am
@@ -0,0 +1,31 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/ext
+if GCC_WERROR_OK
+AM_CPPFLAGS += -Werror
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libxfr.la
+libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc
+libxfr_la_SOURCES += fd_share.h fd_share.cc
+
+if HAVE_BOOST_PYTHON
+pyexec_LTLIBRARIES = bind10_xfr.la
+bind10_xfr_la_SOURCES = python_xfr.cc fd_share.cc fd_share.h
+bind10_xfr_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+if GCC_WERROR_OK
+# XXX: Boost.Python triggers strict aliasing violation, so if we use -Werror
+# we need to suppress the warnings.
+bind10_xfr_la_CPPFLAGS += -fno-strict-aliasing
+endif
+bind10_xfr_la_LDFLAGS = $(BOOST_LDFLAGS) $(PYTHON_LDFLAGS)
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+bind10_xfr_la_LDFLAGS += -module
+bind10_xfr_la_LIBADD = $(BOOST_PYTHON_LIB) $(PYTHON_LIB)
+endif
+
diff --git a/src/lib/xfr/fd_share.cc b/src/lib/xfr/fd_share.cc
new file mode 100644
index 0000000000..d8cedb1d49
--- /dev/null
+++ b/src/lib/xfr/fd_share.cc
@@ -0,0 +1,102 @@
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include "fd_share.h"
+
+namespace isc {
+namespace xfr {
+
+#define FD_BUFFER_CREATE(n) \
+ struct { \
+ struct cmsghdr h; \
+ int fd[n]; \
+ }
+
+int
+send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer)
+{
+ struct msghdr msghdr;
+ char nothing = '!';
+ struct iovec nothing_ptr;
+ struct cmsghdr *cmsg;
+ int i;
+
+ nothing_ptr.iov_base = &nothing;
+ nothing_ptr.iov_len = 1;
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &nothing_ptr;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = buffer;
+ msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = msghdr.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ for(i = 0; i < n_fds; i++)
+ ((int *)CMSG_DATA(cmsg))[i] = fds[i];
+
+ int ret = sendmsg(sock, &msghdr, 0);
+ return (ret >= 0 ? 0 : -1);
+}
+
+int
+recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
+{
+ struct msghdr msghdr;
+ char nothing;
+ struct iovec nothing_ptr;
+ struct cmsghdr *cmsg;
+ int i;
+
+ nothing_ptr.iov_base = &nothing;
+ nothing_ptr.iov_len = 1;
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &nothing_ptr;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = buffer;
+ msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = msghdr.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ for(i = 0; i < n_fds; i++)
+ ((int *)CMSG_DATA(cmsg))[i] = -1;
+
+ if(recvmsg(sock, &msghdr, 0) < 0)
+ return (-1);
+
+ for(i = 0; i < n_fds; i++) {
+ fds[i] = ((int *)CMSG_DATA(cmsg))[i];
+ }
+
+ n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
+ return n_fds;
+}
+
+int
+recv_fd(int sock)
+{
+ FD_BUFFER_CREATE(1) buffer;
+ int fd = 0;
+ int ret = recv_fds_with_buffer(sock, &fd, 1, &buffer);
+ if (ret == -1)
+ return -1;
+
+ return fd;
+}
+
+int
+send_fd(int sock, int fd)
+{
+ FD_BUFFER_CREATE(1) buffer;
+ int ret = send_fds_with_buffer(sock, &fd, 1, &buffer);
+ return ((ret < 0) ? -1 : ret);
+}
+
+} // End for namespace xfr
+} // End for namespace isc
diff --git a/src/lib/xfr/fd_share.h b/src/lib/xfr/fd_share.h
new file mode 100644
index 0000000000..dd2bf78b3c
--- /dev/null
+++ b/src/lib/xfr/fd_share.h
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+
+namespace isc {
+namespace xfr {
+
+// Receive socket descriptor on unix domain socket 'sock'.
+// Returned value is the socket descriptor received.
+// Errors are indicated by a return value of -1.
+int
+recv_fd(int sock);
+
+// Send socket descriptor "fd" to server over unix domain socket 'sock',
+// the connection from socket 'sock' to unix domain server should be established first.
+// Errors are indicated by a return value of -1.
+int
+send_fd(int sock, int fd);
+
+} // End for namespace xfr
+} // End for namespace isc
diff --git a/src/lib/xfr/python_xfr.cc b/src/lib/xfr/python_xfr.cc
new file mode 100644
index 0000000000..c7d80654fb
--- /dev/null
+++ b/src/lib/xfr/python_xfr.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: message_python.cc 2010-03-08 18:44:00 feng $
+
+#include <string>
+#include <boost/python.hpp>
+#include <boost/python/class.hpp>
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+#include <boost/python/copy_const_reference.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "fd_share.h"
+
+using namespace isc::xfr;
+using namespace boost::python;
+
+
+BOOST_PYTHON_MODULE(bind10_xfr)
+{
+ def("recv_fd", &recv_fd);
+ def("send_fd", &send_fd);
+}
+
diff --git a/src/lib/xfr/xfrout_client.cc b/src/lib/xfr/xfrout_client.cc
new file mode 100644
index 0000000000..32cf65715f
--- /dev/null
+++ b/src/lib/xfr/xfrout_client.cc
@@ -0,0 +1,59 @@
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <boost/asio.hpp>
+#include "fd_share.h"
+#include "xfrout_client.h"
+
+using boost::asio::local::stream_protocol;
+
+namespace isc {
+namespace xfr {
+
+void
+XfroutClient::connect()
+{
+ socket_.connect(stream_protocol::endpoint(file_path_));
+}
+
+void
+XfroutClient::disconnect()
+{
+ socket_.close();
+}
+
+void
+XfroutClient::sendData(uint8_t *msg_data, uint16_t msg_len)
+{
+ int count = 0;
+ while(count < msg_len) {
+ int size = send(socket_.native(), msg_data + count, msg_len - count, 0);
+ if (size == -1)
+ isc_throw(XfroutError, "auth failed to send data to xfrout module\n");
+
+ count += size;
+ }
+
+ return;
+}
+
+int
+XfroutClient::sendXfroutRequestInfo(int tcp_sock, uint8_t *msg_data, uint16_t msg_len)
+{
+ if (-1 == send_fd(socket_.native(), tcp_sock))
+ isc_throw(XfroutError, "Fail to send socket descriptor to xfrout module\n");
+
+ sendData((uint8_t *)&msg_len, 2);
+ sendData(msg_data, msg_len);
+
+ int databuf = 0;
+ int status = recv(socket_.native(), &databuf, sizeof(int), 0);
+ if (status != 0)
+ isc_throw(XfroutError, "xfr query doesn't been processed properly by xfrout module\n");
+
+ return 0;
+}
+
+} // End for xfr
+} // End for isc
+
diff --git a/src/lib/xfr/xfrout_client.h b/src/lib/xfr/xfrout_client.h
new file mode 100644
index 0000000000..f05abd79c5
--- /dev/null
+++ b/src/lib/xfr/xfrout_client.h
@@ -0,0 +1,41 @@
+#ifndef _XFROUT_CLIENT_H
+#define _XFROUT_CLIENT_H
+
+#include <boost/asio.hpp>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace xfr {
+
+class XfroutError: public Exception
+{
+public:
+ XfroutError(const char *file, size_t line, const char *what):
+ isc::Exception(file, line, what) {}
+};
+
+using boost::asio::local::stream_protocol;
+class XfroutClient
+{
+public:
+ XfroutClient(const std::string &file):
+ socket_(io_service_), file_path_(file) {}
+
+ void connect();
+ void disconnect();
+ int sendXfroutRequestInfo(int tcp_sock, uint8_t *msg_data, uint16_t msg_len);
+
+private:
+ void sendData(uint8_t *msg_data, uint16_t msg_len);
+
+private:
+ boost::asio::io_service io_service_;
+ // The socket used to communicate with the xfrout server.
+ stream_protocol::socket socket_;
+ const std::string file_path_;
+};
+
+} // End for namespace xfr
+} // End for namespace isc
+
+#endif