Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
256c0a08
Commit
256c0a08
authored
Nov 18, 2011
by
JINMEI Tatuya
Browse files
[1372] more complete setup for IXFR. falling back to AXFR-style was supported.
also refactored the code to better clarify the code flow.
parent
5c92f567
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/bin/xfrout/tests/xfrout_test.py.in
View file @
256c0a08
...
...
@@ -36,6 +36,8 @@ TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
TEST_ZONE_NAME_STR = "example.com."
TEST_ZONE_NAME = Name(TEST_ZONE_NAME_STR)
TEST_RRCLASS = RRClass.IN()
IXFR_OK_VERSION = 2011111802
IXFR_NG_VERSION = 2011112800
# SOA intended to be used for the new SOA as a result of transfer.
soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
...
...
@@ -83,6 +85,15 @@ class MockDataSrcClient:
def __init__(self, type, config):
pass
def __create_soa(self):
soa_rrset = RRset(self._zone_name, RRClass.IN(), RRType.SOA(),
RRTTL(3600))
soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
'master.example.com. ' +
'admin.example.com. 1234 ' +
'3600 1800 2419200 7200'))
return soa_rrset
def find_zone(self, zone_name):
'''Mock version of find_zone().
...
...
@@ -91,9 +102,10 @@ class MockDataSrcClient:
or PARTIALMATCH.
'''
if zone_name == TEST_ZONE_NAME:
return (isc.datasrc.DataSourceClient.SUCCESS, self)
raise ValueError('Unexpected input to mock client: bug in test case?')
self._zone_name = zone_name
if zone_name == Name('notauth.example.com'):
return (isc.datasrc.DataSourceClient.NOTFOUND, None)
return (isc.datasrc.DataSourceClient.SUCCESS, self)
def find(self, name, rrtype, target, options):
'''Mock ZoneFinder.find().
...
...
@@ -104,6 +116,12 @@ class MockDataSrcClient:
'''
if name == TEST_ZONE_NAME and rrtype == RRType.SOA():
return (ZoneFinder.SUCCESS, self.__create_soa())
elif name == Name('nosoa.example.com') and rrtype == RRType.SOA():
return (ZoneFinder.NXDOMAIN, None)
elif name == Name('multisoa.example.com') and rrtype == RRType.SOA():
soa_rrset = self.__create_soa()
soa_rrset.add_rdata(soa_rrset.get_rdata()[0])
return (ZoneFinder.SUCCESS, soa_rrset)
raise ValueError('Unexpected input to mock finder: bug in test case?')
...
...
@@ -116,20 +134,14 @@ class MockDataSrcClient:
def get_soa(self): # emulate ZoneIterator.get_soa()
if self._zone_name == Name('nosoa.example.com'):
return None
soa_rrset = RRset(self._zone_name, RRClass.IN(), RRType.SOA(),
RRTTL(3600))
soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
'master.example.com. ' +
'admin.example.com. 1234 ' +
'3600 1800 2419200 7200'))
soa_rrset = self.__create_soa()
if self._zone_name == Name('multisoa.example.com'):
soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
'master.example.com. ' +
'admin.example.com. 1300 ' +
'3600 1800 2419200 7200'))
soa_rrset.add_rdata(soa_rrset.get_rdata()[0])
return soa_rrset
def get_journal_reader(self, zone_name, begin_serial, end_serial):
if begin_serial == IXFR_NG_VERSION:
return isc.datasrc.ZoneJournalReader.NO_SUCH_VERSION, self
return isc.datasrc.ZoneJournalReader.SUCCESS, self
class MyCCSession(isc.config.ConfigData):
...
...
@@ -663,8 +675,12 @@ class TestXfroutSession(TestXfroutSessionBase):
def test_check_xfrout_axfr_available(self):
self.xfrsess.ClientClass = MockDataSrcClient
# Successful case. A zone iterator should be set up.
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('example.com')), Rcode.NOERROR())
self.assertNotEqual(None, self.xfrsess._iterator)
# Failure cases
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('notauth.example.com')), Rcode.NOTAUTH())
self.assertEqual(self.xfrsess._check_xfrout_available(
...
...
@@ -675,10 +691,29 @@ class TestXfroutSession(TestXfroutSessionBase):
def test_check_xfrout_ixfr_available(self):
self.xfrsess.ClientClass = MockDataSrcClient
self.set_request_type(RRType.IXFR())
self.mdata = self.create_request_data(ixfr=2011111802)
request_msg = self.getmsg()
# Successful case of pure IXFR. A zone journal reader should be set
# up.
self.mdata = self.create_request_data(ixfr=IXFR_OK_VERSION)
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('example.com')), Rcode.NOERROR())
self.getmsg(), TEST_ZONE_NAME), Rcode.NOERROR())
self.assertNotEqual(None, self.xfrsess._jnl_reader)
# Successful case, but as a result of falling back to AXFR-style
# IXFR. A zone iterator should be set up instead of a journal reader.
self.mdata = self.create_request_data(ixfr=IXFR_NG_VERSION)
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), TEST_ZONE_NAME), Rcode.NOERROR())
self.assertNotEqual(None, self.xfrsess._iterator)
self.assertEqual(None, self.xfrsess._jnl_reader)
# Failure cases
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('notauth.example.com')), Rcode.NOTAUTH())
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('nosoa.example.com')), Rcode.SERVFAIL())
self.assertEqual(self.xfrsess._check_xfrout_available(
self.getmsg(), Name('multisoa.example.com')), Rcode.SERVFAIL())
def test_dns_xfrout_start_formerror(self):
# formerror
...
...
src/bin/xfrout/xfrout.py.in
View file @
256c0a08
...
...
@@ -22,7 +22,7 @@ import isc.cc
import threading
import struct
import signal
from isc.datasrc import DataSourceClient, ZoneFinder
from isc.datasrc import DataSourceClient, ZoneFinder
, ZoneJournalReader
from socketserver import *
import os
from isc.config.ccsession import *
...
...
@@ -237,8 +237,8 @@ class XfroutSession():
elif self._request_type == RRType.IXFR():
self._request_typestr = 'IXFR'
else:
# Likewise, this should be impossible.
raise Runtime
e
rror('Unexpected XFR type: ' + \
# Likewise, this should be impossible.
(TBD: to be tested)
raise Runtime
E
rror('Unexpected XFR type: ' + \
str(self._request_type))
# ACL checks
...
...
@@ -314,19 +314,82 @@ class XfroutSession():
self._send_message(sock_fd, msg, self._tsig_ctx)
def _get_zone_soa(self, zone_name):
'''Retrieve the SOA RR of the given zone.
It returns a pair of RCODE and the SOA (in the form of RRset).
On success RCODE is NOERROR and returned SOA is not None;
on failure RCODE indicates the appropriate code in the context of
xfr processing, and the returned SOA is None.
'''
result, finder = self._datasrc_client.find_zone(zone_name)
if result != DataSourceClient.SUCCESS:
return
None # XXX
result, soa_rrset = finder.find(zone_name, RRType.SOA(),
None,
ZoneFinder.FIND_DEFAULT)
return
(Rcode.NOTAUTH(), None)
result, soa_rrset = finder.find(zone_name, RRType.SOA(),
None,
ZoneFinder.FIND_DEFAULT)
if result != ZoneFinder.SUCCESS:
return None
return
(Rcode.SERVFAIL(),
None
)
# Especially for database-based zones, a working zone may be in
# a broken state where it has more than one SOA RR. We proactively
# check the condition and abort the xfr attempt if we identify it.
if soa_rrset.get_rdata_count() != 1:
return None
return soa_rrset
return (Rcode.SERVFAIL(), None)
return (Rcode.NOERROR(), soa_rrset)
def __setup_axfr(self, zone_name):
'''Setup a zone iterator for AXFR or AXFR-style IXFR.
'''
try:
# Note that we disable 'adjust_ttl'. In xfr-out we need to
# preserve as many things as possible (even if it's half
# broken) stored in the zone.
self._iterator = self._datasrc_client.get_iterator(zone_name,
False)
except isc.datasrc.Error:
# If the current name server does not have authority for the
# zone, xfrout can't serve for it, return rcode NOTAUTH.
# Note: this exception can happen for other reasons. We should
# update get_iterator() API so that we can distinguish "no such
# zone" and other cases (#1373). For now we consider all these
# cases as NOTAUTH.
return Rcode.NOTAUTH()
# If we are an authoritative name server for the zone, but fail
# to find the zone's SOA record in datasource, xfrout can't
# provide zone transfer for it.
self._soa = self._iterator.get_soa()
if self._soa is None or self._soa.get_rdata_count() != 1:
return Rcode.SERVFAIL()
return Rcode.NOERROR()
def __setup_ixfr(self, request_msg, zone_name):
'''Setup a zone journal reader for IXFR.
If the underlying data source does not know the requested range
of zone differences it automatically falls back to AXFR-style
IXFR by setting up a zone iterator instead of a journal reader.
'''
# TODO: more error case handling
remote_soa = None
for auth_rrset in request_msg.get_section(Message.SECTION_AUTHORITY):
if auth_rrset.get_type() != RRType.SOA():
continue
remote_soa = auth_rrset
rcode, self._soa = self._get_zone_soa(zone_name)
if rcode != Rcode.NOERROR():
return rcode
code, self._jnl_reader = self._datasrc_client.get_journal_reader(
remote_soa.get_name(), get_soa_serial(remote_soa.get_rdata()[0]),
get_soa_serial(self._soa.get_rdata()[0]))
if code == ZoneJournalReader.NO_SUCH_VERSION:
# fallback to AXFR-style IXFR
self._jnl_reader = None # clear it just in case
return self.__setup_axfr(zone_name)
return Rcode.NOERROR()
def _check_xfrout_available(self, request_msg, zone_name):
'''Check if xfr request can be responsed.
...
...
@@ -340,51 +403,16 @@ class XfroutSession():
# We should eventually generalize this so that we can choose the
# appropriate data source from (possible) multiple candidates.
# We should eventually take into account the RR class here.
# For now, we
hardcode a particular type (SQLite3-based), and only
# For now, we hardcode a particular type (SQLite3-based), and only
# consider that one.
datasrc_config = '{ "database_file": "' + \
self._server.get_db_file() + '"}'
self._datasrc_client = self.ClientClass('sqlite3', datasrc_config)
if self._request_type == RRType.AXFR():
try:
# Note that we disable 'adjust_ttl'. In xfr-out we need to
# preserve as many things as possible (even if it's half
# broken) stored in the zone.
self._iterator = self._datasrc_client.get_iterator(zone_name,
False)
except isc.datasrc.Error:
# If the current name server does not have authority for the
# zone, xfrout can't serve for it, return rcode NOTAUTH.
# Note: this exception can happen for other reasons. We should
# update get_iterator() API so that we can distinguish "no such
# zone" and other cases (#1373). For now we consider all these
# cases as NOTAUTH.
return Rcode.NOTAUTH()
self._soa = self._iterator.get_soa()
return self.__setup_axfr(zone_name)
else:
# TODO: error case handling
remote_soa = None
for auth_rrset in \
request_msg.get_section(Message.SECTION_AUTHORITY):
if auth_rrset.get_type() != RRType.SOA():
continue
remote_soa = auth_rrset
self._soa = self._get_zone_soa(remote_soa.get_name())
code, self._jnl_reader = self._datasrc_client.get_journal_reader(
remote_soa.get_name(),
get_soa_serial(remote_soa.get_rdata()[0]),
get_soa_serial(self._soa.get_rdata()[0]))
# If we are an authoritative name server for the zone, but fail
# to find the zone's SOA record in datasource, xfrout can't
# provide zone transfer for it.
if self._soa is None or self._soa.get_rdata_count() != 1:
return Rcode.SERVFAIL()
return Rcode.NOERROR()
return self.__setup_ixfr(request_msg, zone_name)
def dns_xfrout_start(self, sock_fd, msg_query, quota_ok=True):
rcode_, msg = self._parse_query_message(msg_query)
...
...
@@ -458,7 +486,6 @@ class XfroutSession():
msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
self._send_message(sock_fd, msg, self._tsig_ctx)
def _reply_xfrout_query(self, msg, sock_fd):
#TODO, there should be a better way to insert rrset.
msg.make_response()
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment