mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 14:35:29 +00:00
[trac914] Merge branch 'trac811_new' into trac914
This commit is contained in:
@@ -534,6 +534,35 @@ class TestXfrin(unittest.TestCase):
|
|||||||
self.assertEqual(self.xfr.command_handler("retransfer",
|
self.assertEqual(self.xfr.command_handler("retransfer",
|
||||||
self.args)['result'][0], 0)
|
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):
|
def test_command_handler_retransfer_badcommand(self):
|
||||||
self.args['master'] = 'invalid'
|
self.args['master'] = 'invalid'
|
||||||
self.assertEqual(self.xfr.command_handler("retransfer",
|
self.assertEqual(self.xfr.command_handler("retransfer",
|
||||||
@@ -574,7 +603,21 @@ class TestXfrin(unittest.TestCase):
|
|||||||
def test_command_handler_notify(self):
|
def test_command_handler_notify(self):
|
||||||
# at this level, refresh is no different than retransfer.
|
# at this level, refresh is no different than retransfer.
|
||||||
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
|
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.assertEqual(self.xfr.command_handler("notify",
|
||||||
self.args)['result'][0], 0)
|
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.config_handler({'transfers_in': 3})['result'][0], 0)
|
||||||
self.assertEqual(self.xfr._max_transfers_in, 3)
|
self.assertEqual(self.xfr._max_transfers_in, 3)
|
||||||
|
|
||||||
def test_command_handler_masters(self):
|
def test_command_handler_zones(self):
|
||||||
master_info = {'master_addr': '1.1.1.1', 'master_port':53}
|
zones = { 'zones': [
|
||||||
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 0)
|
{ '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 }
|
zones = { 'zones': [
|
||||||
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
|
{ '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 }
|
zones = { 'zones': [
|
||||||
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
|
{ '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 }
|
zones = { 'zones': [
|
||||||
self.xfr.config_handler(master_info)
|
{ 'name': 'test.com',
|
||||||
self.assertEqual(self.xfr._master_addr, '2.2.2.2')
|
'master_addr': '1.1.1.1',
|
||||||
self.assertEqual(self.xfr._master_port, 53)
|
'master_port': 'bad_port'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
|
||||||
|
|
||||||
|
|
||||||
def raise_interrupt():
|
def raise_interrupt():
|
||||||
|
@@ -70,6 +70,9 @@ def log_error(msg):
|
|||||||
class XfrinException(Exception):
|
class XfrinException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class XfrinConfigException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class XfrinConnection(asyncore.dispatcher):
|
class XfrinConnection(asyncore.dispatcher):
|
||||||
'''Do xfrin in this class. '''
|
'''Do xfrin in this class. '''
|
||||||
|
|
||||||
@@ -378,12 +381,41 @@ class XfrinRecorder:
|
|||||||
self._lock.release()
|
self._lock.release()
|
||||||
return ret
|
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:
|
class Xfrin:
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, verbose = False):
|
||||||
self._max_transfers_in = 10
|
self._max_transfers_in = 10
|
||||||
#TODO, this is the temp way to set the zone's master.
|
self._zones = {}
|
||||||
self._master_addr = DEFAULT_MASTER
|
|
||||||
self._master_port = DEFAULT_MASTER_PORT
|
|
||||||
self._cc_setup()
|
self._cc_setup()
|
||||||
self.recorder = XfrinRecorder()
|
self.recorder = XfrinRecorder()
|
||||||
self._shutdown_event = threading.Event()
|
self._shutdown_event = threading.Event()
|
||||||
@@ -402,10 +434,7 @@ class Xfrin:
|
|||||||
self.command_handler)
|
self.command_handler)
|
||||||
self._module_cc.start()
|
self._module_cc.start()
|
||||||
config_data = self._module_cc.get_full_config()
|
config_data = self._module_cc.get_full_config()
|
||||||
self._max_transfers_in = config_data.get("transfers_in")
|
self.config_handler(config_data)
|
||||||
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
|
|
||||||
|
|
||||||
def _cc_check_command(self):
|
def _cc_check_command(self):
|
||||||
'''This is a straightforward wrapper for cc.check_command,
|
'''This is a straightforward wrapper for cc.check_command,
|
||||||
@@ -413,22 +442,34 @@ class Xfrin:
|
|||||||
of unit tests.'''
|
of unit tests.'''
|
||||||
self._module_cc.check_command(False)
|
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):
|
def config_handler(self, new_config):
|
||||||
self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
|
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 'zones' in new_config:
|
||||||
if ('master_addr' in new_config) or ('master_port' in new_config):
|
self._clear_zone_info()
|
||||||
# User should change the port and address together.
|
for zone_config in new_config.get('zones'):
|
||||||
try:
|
try:
|
||||||
addr = new_config.get('master_addr') or self._master_addr
|
zone_info = ZoneInfo(zone_config)
|
||||||
port = new_config.get('master_port') or self._master_port
|
self._add_zone_info(zone_info)
|
||||||
isc.net.parse.addr_parse(addr)
|
except XfrinConfigException as xce:
|
||||||
isc.net.parse.port_parse(port)
|
return create_answer(1, str(xce))
|
||||||
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)
|
|
||||||
|
|
||||||
return create_answer(0)
|
return create_answer(0)
|
||||||
|
|
||||||
@@ -454,14 +495,21 @@ class Xfrin:
|
|||||||
# specify the notifyfrom address and port, according the RFC1996, zone
|
# specify the notifyfrom address and port, according the RFC1996, zone
|
||||||
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
|
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
|
||||||
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
|
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
|
||||||
(master_addr) = build_addr_info(self._master_addr, self._master_port)
|
zone_info = self._get_zone_info(zone_name)
|
||||||
ret = self.xfrin_start(zone_name,
|
if zone_info is None:
|
||||||
rrclass,
|
# TODO what to do? no info known about zone. defaults?
|
||||||
self._get_db_file(),
|
errmsg = "Got notification to retransfer unknown zone " + zone_name
|
||||||
master_addr,
|
log_error(errmsg)
|
||||||
self._tsig_key_str,
|
answer = create_answer(1, errmsg)
|
||||||
True)
|
else:
|
||||||
answer = create_answer(ret[0], ret[1])
|
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':
|
elif command == 'retransfer' or command == 'refresh':
|
||||||
# Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
|
# 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.
|
# master address, or else do transfer from the configured masters.
|
||||||
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
|
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
|
||||||
master_addr = self._parse_master_and_port(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()
|
db_file = args.get('db_file') or self._get_db_file()
|
||||||
ret = self.xfrin_start(zone_name,
|
ret = self.xfrin_start(zone_name,
|
||||||
rrclass,
|
rrclass,
|
||||||
db_file,
|
db_file,
|
||||||
master_addr,
|
master_addr,
|
||||||
self._tsig_key_str,
|
tsig_key_str,
|
||||||
(False if command == 'retransfer' else True))
|
(False if command == 'retransfer' else True))
|
||||||
answer = create_answer(ret[0], ret[1])
|
answer = create_answer(ret[0], ret[1])
|
||||||
|
|
||||||
@@ -502,8 +554,24 @@ class Xfrin:
|
|||||||
return zone_name, rrclass
|
return zone_name, rrclass
|
||||||
|
|
||||||
def _parse_master_and_port(self, args):
|
def _parse_master_and_port(self, args):
|
||||||
port = args.get('port') or self._master_port
|
# check if we have configured info about this zone, in case
|
||||||
master = args.get('master') or self._master_addr
|
# 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)
|
return build_addr_info(master, port)
|
||||||
|
|
||||||
def _get_db_file(self):
|
def _get_db_file(self):
|
||||||
|
@@ -9,21 +9,43 @@
|
|||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": 10
|
"item_default": 10
|
||||||
},
|
},
|
||||||
{
|
{ "item_name": "zones",
|
||||||
"item_name": "master_addr",
|
"item_type": "list",
|
||||||
"item_type": "string",
|
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": ""
|
"item_default": [],
|
||||||
},
|
"list_item_spec":
|
||||||
{ "item_name": "master_port",
|
{ "item_type": "map",
|
||||||
"item_type": "integer",
|
"item_name": "zone_info",
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": 53
|
"item_default": {},
|
||||||
},
|
"map_item_spec": [
|
||||||
{ "item_name": "tsig_key",
|
{ "item_name": "name",
|
||||||
"item_type": "string",
|
"item_type": "string",
|
||||||
"item_optional": true,
|
"item_optional": false,
|
||||||
"item_default": ""
|
"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": [
|
"commands": [
|
||||||
|
@@ -301,6 +301,11 @@ class XfroutSession():
|
|||||||
|
|
||||||
self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len)
|
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):
|
class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
|
||||||
'''The unix domain socket server which accept xfr query sent from auth server.'''
|
'''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._lock.acquire()
|
||||||
self._max_transfers_out = new_config.get('transfers_out')
|
self._max_transfers_out = new_config.get('transfers_out')
|
||||||
self._log.log_message('info', 'max transfer out : %d', self._max_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._lock.release()
|
||||||
self._log.log_message('info', 'update config data complete.')
|
self._log.log_message('info', 'update config data complete.')
|
||||||
|
|
||||||
|
@@ -37,6 +37,29 @@
|
|||||||
"item_type": "integer",
|
"item_type": "integer",
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": 1048576
|
"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": [
|
"commands": [
|
||||||
|
Reference in New Issue
Block a user