2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[trac914] Merge branch 'trac811_new' into trac914

This commit is contained in:
JINMEI Tatuya
2011-05-13 15:40:42 -07:00
5 changed files with 241 additions and 58 deletions

View File

@@ -534,6 +534,35 @@ class TestXfrin(unittest.TestCase):
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
def test_command_handler_retransfer_short_command1(self):
# try it when only specifying the zone name (of unknown zone)
short_args = {}
short_args['zone_name'] = TEST_ZONE_NAME
self.assertEqual(self.xfr.command_handler("retransfer",
short_args)['result'][0], 0)
def test_command_handler_retransfer_short_command2(self):
# try it when only specifying the zone name (of unknown zone)
short_args = {}
short_args['zone_name'] = TEST_ZONE_NAME + "."
self.assertEqual(self.xfr.command_handler("retransfer",
short_args)['result'][0], 0)
def test_command_handler_retransfer_short_command3(self):
# try it when only specifying the zone name (of known zone)
short_args = {}
short_args['zone_name'] = TEST_ZONE_NAME
zones = { 'zones': [
{ 'name': TEST_ZONE_NAME,
'master_addr': TEST_MASTER_IPV4_ADDRESS,
'master_port': TEST_MASTER_PORT
}
]}
self.xfr.config_handler(zones)
self.assertEqual(self.xfr.command_handler("retransfer",
short_args)['result'][0], 0)
def test_command_handler_retransfer_badcommand(self):
self.args['master'] = 'invalid'
self.assertEqual(self.xfr.command_handler("retransfer",
@@ -574,7 +603,21 @@ class TestXfrin(unittest.TestCase):
def test_command_handler_notify(self):
# at this level, refresh is no different than retransfer.
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
# ...but right now we disable the feature due to security concerns.
# ...but the zone is unknown so this would return an error
self.assertEqual(self.xfr.command_handler("notify",
self.args)['result'][0], 1)
def test_command_handler_notify_known_zone(self):
# try it with a known zone
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
zones = { 'zones': [
{ 'name': TEST_ZONE_NAME,
'master_addr': TEST_MASTER_IPV4_ADDRESS,
'master_port': TEST_MASTER_PORT
}
]}
self.xfr.config_handler(zones)
self.assertEqual(self.xfr.command_handler("notify",
self.args)['result'][0], 0)
@@ -586,20 +629,37 @@ class TestXfrin(unittest.TestCase):
self.assertEqual(self.xfr.config_handler({'transfers_in': 3})['result'][0], 0)
self.assertEqual(self.xfr._max_transfers_in, 3)
def test_command_handler_masters(self):
master_info = {'master_addr': '1.1.1.1', 'master_port':53}
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 0)
def test_command_handler_zones(self):
zones = { 'zones': [
{ 'name': 'test.com.',
'master_addr': '1.1.1.1',
'master_port': 53
}
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 0)
master_info = {'master_addr': '1111.1.1.1', 'master_port':53 }
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
zones = { 'zones': [
{ 'master_addr': '1.1.1.1',
'master_port': 53
}
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
master_info = {'master_addr': '2.2.2.2', 'master_port':530000 }
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
zones = { 'zones': [
{ 'name': 'test.com',
'master_addr': 'badaddress',
'master_port': 53
}
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
master_info = {'master_addr': '2.2.2.2', 'master_port':53 }
self.xfr.config_handler(master_info)
self.assertEqual(self.xfr._master_addr, '2.2.2.2')
self.assertEqual(self.xfr._master_port, 53)
zones = { 'zones': [
{ 'name': 'test.com',
'master_addr': '1.1.1.1',
'master_port': 'bad_port'
}
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
def raise_interrupt():

View File

@@ -70,6 +70,9 @@ def log_error(msg):
class XfrinException(Exception):
pass
class XfrinConfigException(Exception):
pass
class XfrinConnection(asyncore.dispatcher):
'''Do xfrin in this class. '''
@@ -378,12 +381,41 @@ class XfrinRecorder:
self._lock.release()
return ret
class ZoneInfo:
def __init__(self, config_data):
"""Creates a zone_info with the config data element as
specified by the 'zones' list in xfrin.spec"""
self.name = config_data.get('name')
self.class_str = config_data.get('class') or 'IN'
if self.name is None:
raise XfrinConfigException("Configuration zones list "
"element does not contain "
"'name' attribute")
# add the root dot if the user forgot
if len(self.name) > 0 and self.name[-1] != '.':
self.name += '.'
self.master_addr_str = config_data.get('master_addr') or DEFAULT_MASTER
self.master_port_str = config_data.get('master_port') or DEFAULT_MASTER_PORT
try:
self.master_addr = isc.net.parse.addr_parse(self.master_addr_str)
self.master_port = isc.net.parse.port_parse(self.master_port_str)
except ValueError:
errmsg = "bad format for zone's master: " + str(config_data)
log_error(errmsg)
raise XfrinConfigException(errmsg)
self.tsig_key_str = config_data.get('tsig_key') or None
def get_master_addr_info(self):
return (self.master_addr.family, socket.SOCK_STREAM,
(self.master_addr_str, self.master_port))
class Xfrin:
def __init__(self, verbose = False):
self._max_transfers_in = 10
#TODO, this is the temp way to set the zone's master.
self._master_addr = DEFAULT_MASTER
self._master_port = DEFAULT_MASTER_PORT
self._zones = {}
self._cc_setup()
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
@@ -402,10 +434,7 @@ class Xfrin:
self.command_handler)
self._module_cc.start()
config_data = self._module_cc.get_full_config()
self._max_transfers_in = config_data.get("transfers_in")
self._master_addr = config_data.get('master_addr') or self._master_addr
self._master_port = config_data.get('master_port') or self._master_port
self._tsig_key_str = config_data.get('tsig_key') or None
self.config_handler(config_data)
def _cc_check_command(self):
'''This is a straightforward wrapper for cc.check_command,
@@ -413,22 +442,34 @@ class Xfrin:
of unit tests.'''
self._module_cc.check_command(False)
def _get_zone_info(self, name, class_str = "IN"):
"""Returns the ZoneInfo object containing the configured data
for the given zone name. If the zone name did not have any
data, returns None"""
# add the root dot if the user forgot
if len(name) > 0 and name[-1] != '.':
name += '.'
if (name, class_str) in self._zones:
return self._zones[(name, class_str)]
else:
return None
def _clear_zone_info(self):
self._zones = {}
def _add_zone_info(self, zone_info):
self._zones[(zone_info.name, zone_info.class_str)] = zone_info
def config_handler(self, new_config):
self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
self._tsig_key_str = new_config.get('tsig_key') or None
if ('master_addr' in new_config) or ('master_port' in new_config):
# User should change the port and address together.
try:
addr = new_config.get('master_addr') or self._master_addr
port = new_config.get('master_port') or self._master_port
isc.net.parse.addr_parse(addr)
isc.net.parse.port_parse(port)
self._master_addr = addr
self._master_port = port
except ValueError:
errmsg = "bad format for zone's master: " + str(new_config)
log_error(errmsg)
return create_answer(1, errmsg)
if 'zones' in new_config:
self._clear_zone_info()
for zone_config in new_config.get('zones'):
try:
zone_info = ZoneInfo(zone_config)
self._add_zone_info(zone_info)
except XfrinConfigException as xce:
return create_answer(1, str(xce))
return create_answer(0)
@@ -454,14 +495,21 @@ class Xfrin:
# specify the notifyfrom address and port, according the RFC1996, zone
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
(master_addr) = build_addr_info(self._master_addr, self._master_port)
ret = self.xfrin_start(zone_name,
rrclass,
self._get_db_file(),
master_addr,
self._tsig_key_str,
True)
answer = create_answer(ret[0], ret[1])
zone_info = self._get_zone_info(zone_name)
if zone_info is None:
# TODO what to do? no info known about zone. defaults?
errmsg = "Got notification to retransfer unknown zone " + zone_name
log_error(errmsg)
answer = create_answer(1, errmsg)
else:
master_addr = zone_info.get_master_addr_info()
ret = self.xfrin_start(zone_name,
rrclass,
self._get_db_file(),
master_addr,
zone_info.tsig_key_str,
True)
answer = create_answer(ret[0], ret[1])
elif command == 'retransfer' or command == 'refresh':
# Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
@@ -469,12 +517,16 @@ class Xfrin:
# master address, or else do transfer from the configured masters.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
master_addr = self._parse_master_and_port(args)
zone_info = self._get_zone_info(zone_name)
tsig_key_str = None
if zone_info:
tsig_key_str = zone_info.tsig_key_str
db_file = args.get('db_file') or self._get_db_file()
ret = self.xfrin_start(zone_name,
rrclass,
db_file,
master_addr,
self._tsig_key_str,
tsig_key_str,
(False if command == 'retransfer' else True))
answer = create_answer(ret[0], ret[1])
@@ -502,8 +554,24 @@ class Xfrin:
return zone_name, rrclass
def _parse_master_and_port(self, args):
port = args.get('port') or self._master_port
master = args.get('master') or self._master_addr
# check if we have configured info about this zone, in case
# port or master are not specified
zone_info = self._get_zone_info(args.get('zone_name'))
port = args.get('port')
if port is None:
if zone_info is not None:
port = zone_info.master_port_str
else:
port = DEFAULT_MASTER_PORT
master = args.get('master')
if master is None:
if zone_info is not None:
master = zone_info.master_addr_str
else:
master = DEFAULT_MASTER
return build_addr_info(master, port)
def _get_db_file(self):

View File

@@ -9,21 +9,43 @@
"item_optional": false,
"item_default": 10
},
{
"item_name": "master_addr",
"item_type": "string",
{ "item_name": "zones",
"item_type": "list",
"item_optional": false,
"item_default": ""
},
{ "item_name": "master_port",
"item_type": "integer",
"item_optional": false,
"item_default": 53
},
{ "item_name": "tsig_key",
"item_type": "string",
"item_optional": true,
"item_default": ""
"item_default": [],
"list_item_spec":
{ "item_type": "map",
"item_name": "zone_info",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "class",
"item_type": "string",
"item_optional": false,
"item_default": "IN"
},
{
"item_name": "master_addr",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "master_port",
"item_type": "integer",
"item_optional": false,
"item_default": 53
},
{ "item_name": "tsig_key",
"item_type": "string",
"item_optional": true
}
]
}
}
],
"commands": [

View File

@@ -301,6 +301,11 @@ class XfroutSession():
self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len)
class ZoneInfo:
def __init__(self, zone_config):
self.name = zone_config.get('name')
self.tsig_key_str = zone_config.get('tsig_key')
class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
'''The unix domain socket server which accept xfr query sent from auth server.'''
@@ -450,6 +455,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
self._lock.acquire()
self._max_transfers_out = new_config.get('transfers_out')
self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
zones = new_config.get('zones')
if zones is not None:
for zone_config in zones:
zone_info = ZoneInfo(zone_config)
self.zones[zone_info.name] = zone_info
self._lock.release()
self._log.log_message('info', 'update config data complete.')

View File

@@ -37,6 +37,29 @@
"item_type": "integer",
"item_optional": false,
"item_default": 1048576
},
{
"item_name": "zones",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{ "item_name": "zone_info",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "tsig_key",
"item_type": "string",
"item_optional": true
}
]
}
}
],
"commands": [