Commit 21d3d1aa authored by Jelte Jansen's avatar Jelte Jansen

[master] Merge remote-tracking branch 'origin/trac1455'

parents 73b30ba8 72670be7
......@@ -130,6 +130,11 @@ public:
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, ConstRdataPtr rdata,
Message::ParseOptions options);
// There are also times where an RR needs to be added that
// represents an empty RRset. There is no Rdata in that case
void addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, Message::ParseOptions options);
void addEDNS(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, const Rdata& rdata);
......@@ -740,6 +745,17 @@ MessageImpl::parseSection(const Message::Section section,
const RRClass rrclass(buffer.readUint16());
const RRTTL ttl(buffer.readUint32());
const size_t rdlen = buffer.readUint16();
// If class is ANY or NONE, rdlength may be zero, to signal
// an empty RRset.
// (the class check must be done to differentiate from RRTypes
// that can have zero length rdata
if ((rrclass == RRClass::ANY() || rrclass == RRClass::NONE()) &&
rdlen == 0) {
addRR(section, name, rrclass, rrtype, ttl, options);
++added;
continue;
}
ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
if (rrtype == RRType::OPT()) {
......@@ -777,6 +793,24 @@ MessageImpl::addRR(Message::Section section, const Name& name,
rrsets_[section].push_back(rrset);
}
void
MessageImpl::addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, Message::ParseOptions options)
{
if ((options & Message::PRESERVE_ORDER) == 0) {
vector<RRsetPtr>::iterator it =
find_if(rrsets_[section].begin(), rrsets_[section].end(),
MatchRR(name, rrtype, rrclass));
if (it != rrsets_[section].end()) {
(*it)->setTTL(min((*it)->getTTL(), ttl));
return;
}
}
RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
rrsets_[section].push_back(rrset);
}
void
MessageImpl::addEDNS(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
......
......@@ -221,6 +221,15 @@ initModulePart_Name(PyObject* mod) {
NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR");
addClassVariable(name_comparison_result_type, "NameRelation",
po_NameRelation);
// Add the constants themselves too
addClassVariable(name_comparison_result_type, "SUPERDOMAIN",
Py_BuildValue("I", NameComparisonResult::SUPERDOMAIN));
addClassVariable(name_comparison_result_type, "SUBDOMAIN",
Py_BuildValue("I", NameComparisonResult::SUBDOMAIN));
addClassVariable(name_comparison_result_type, "EQUAL",
Py_BuildValue("I", NameComparisonResult::EQUAL));
addClassVariable(name_comparison_result_type, "COMMONANCESTOR",
Py_BuildValue("I", NameComparisonResult::COMMONANCESTOR));
PyModule_AddObject(mod, "NameComparisonResult",
reinterpret_cast<PyObject*>(&name_comparison_result_type));
......
......@@ -30,6 +30,7 @@ class TestModuleSpec(unittest.TestCase):
self.test_nsname = Name("ns.example.com")
self.rrset_a = RRset(self.test_name, RRClass("IN"), RRType("A"), RRTTL(3600))
self.rrset_a_empty = RRset(self.test_name, RRClass("IN"), RRType("A"), RRTTL(3600))
self.rrset_any_a_empty = RRset(self.test_name, RRClass("ANY"), RRType("A"), RRTTL(3600))
self.rrset_ns = RRset(self.test_domain, RRClass("IN"), RRType("NS"), RRTTL(86400))
self.rrset_ch_txt = RRset(self.test_domain, RRClass("CH"), RRType("TXT"), RRTTL(0))
self.MAX_RDATA_COUNT = 100
......@@ -90,6 +91,9 @@ class TestModuleSpec(unittest.TestCase):
self.assertRaises(EmptyRRset, self.rrset_a_empty.to_text)
self.assertEqual("test.example.com. 3600 ANY A\n",
self.rrset_any_a_empty.to_text())
def test_to_wire_buffer(self):
exp_buffer = bytearray(b'\x04test\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x01\x04test\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x02')
buffer = bytearray()
......@@ -99,6 +103,11 @@ class TestModuleSpec(unittest.TestCase):
self.assertRaises(EmptyRRset, self.rrset_a_empty.to_wire, buffer);
self.assertRaises(TypeError, self.rrset_a.to_wire, 1)
exp_buffer = bytearray(b'\x04test\x07example\x03com\x00\x00\x01\x00\xff\x00\x00\x0e\x10\x00\x00')
buffer = bytearray()
self.rrset_any_a_empty.to_wire(buffer)
self.assertEqual(exp_buffer, buffer)
def test_to_wire_renderer(self):
exp_buffer = bytearray(b'\x04test\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x01\xc0\x00\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x02')
mr = MessageRenderer()
......
......@@ -43,14 +43,24 @@ AbstractRRset::toText() const {
string s;
RdataIteratorPtr it = getRdataIterator();
// In the case of an empty rrset, just print name, ttl, class, and
// type
if (it->isLast()) {
isc_throw(EmptyRRset, "ToText() is attempted for an empty RRset");
// But only for class ANY or NONE
if (getClass() != RRClass::ANY() &&
getClass() != RRClass::NONE()) {
isc_throw(EmptyRRset, "toText() is attempted for an empty RRset");
}
s += getName().toText() + " " + getTTL().toText() + " " +
getClass().toText() + " " + getType().toText() + "\n";
return (s);
}
do {
s += getName().toText() + " " + getTTL().toText() + " " +
getClass().toText() + " " + getType().toText() + " " +
it->getCurrent().toText() + "\n";
getClass().toText() + " " + getType().toText() + " " +
it->getCurrent().toText() + "\n";
it->next();
} while (!it->isLast());
......@@ -65,7 +75,21 @@ rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) {
RdataIteratorPtr it = rrset.getRdataIterator();
if (it->isLast()) {
isc_throw(EmptyRRset, "ToWire() is attempted for an empty RRset");
// empty rrsets are only allowed for classes ANY and NONE
if (rrset.getClass() != RRClass::ANY() &&
rrset.getClass() != RRClass::NONE()) {
isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset");
}
// For an empty RRset, write the name, type, class and TTL once,
// followed by empty rdata.
rrset.getName().toWire(output);
rrset.getType().toWire(output);
rrset.getClass().toWire(output);
rrset.getTTL().toWire(output);
output.writeUint16(0);
// Still counts as 1 'rr'; it does show up in the message
return (1);
}
// sort the set of Rdata based on rrset-order and sortlist, and possible
......
......@@ -267,8 +267,8 @@ public:
/// the resulting string with a trailing newline character.
/// (following the BIND9 convention)
///
/// The RRset must contain some RDATA; otherwise, an exception of class
/// \c EmptyRRset will be thrown.
/// If the class is not ANY or NONE, the RRset must contain some RDATA;
/// otherwise, an exception of class \c EmptyRRset will be thrown.
/// If resource allocation fails, a corresponding standard exception
/// will be thrown.
/// The default implementation may throw other exceptions if the
......@@ -299,8 +299,8 @@ public:
///
/// If resource allocation fails, a corresponding standard exception
/// will be thrown.
/// The RRset must contain some RDATA; otherwise, an exception of class
/// \c EmptyRRset will be thrown.
/// If the class is not ANY or NONE, the RRset must contain some RDATA;
/// otherwise, an exception of class \c EmptyRRset will be thrown.
/// The default implementation may throw other exceptions if the
/// \c toWire() method of the RDATA objects throws.
/// If a derived class of \c AbstractRRset overrides the default
......
......@@ -46,6 +46,10 @@ protected:
rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)),
rrset_a_empty(test_name, RRClass::IN(), RRType::A(),
RRTTL(3600)),
rrset_any_a_empty(test_name, RRClass::ANY(), RRType::A(),
RRTTL(3600)),
rrset_none_a_empty(test_name, RRClass::NONE(), RRType::A(),
RRTTL(3600)),
rrset_ns(test_domain, RRClass::IN(), RRType::NS(),
RRTTL(86400)),
rrset_ch_txt(test_domain, RRClass::CH(), RRType::TXT(),
......@@ -62,6 +66,8 @@ protected:
Name test_nsname;
RRset rrset_a;
RRset rrset_a_empty;
RRset rrset_any_a_empty;
RRset rrset_none_a_empty;
RRset rrset_ns;
RRset rrset_ch_txt;
std::vector<unsigned char> wiredata;
......@@ -193,8 +199,14 @@ TEST_F(RRsetTest, toText) {
"test.example.com. 3600 IN A 192.0.2.2\n",
rrset_a.toText());
// toText() cannot be performed for an empty RRset.
// toText() cannot be performed for an empty RRset
EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
// Unless it is type ANY or NONE
EXPECT_EQ("test.example.com. 3600 ANY A\n",
rrset_any_a_empty.toText());
EXPECT_EQ("test.example.com. 3600 CLASS254 A\n",
rrset_none_a_empty.toText());
}
TEST_F(RRsetTest, toWireBuffer) {
......@@ -207,6 +219,20 @@ TEST_F(RRsetTest, toWireBuffer) {
// toWire() cannot be performed for an empty RRset.
buffer.clear();
EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset);
// Unless it is type ANY or None
buffer.clear();
rrset_any_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire3", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
buffer.getLength(), &wiredata[0], wiredata.size());
buffer.clear();
rrset_none_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire4", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
buffer.getLength(), &wiredata[0], wiredata.size());
}
TEST_F(RRsetTest, toWireRenderer) {
......@@ -220,8 +246,24 @@ TEST_F(RRsetTest, toWireRenderer) {
renderer.getLength(), &wiredata[0], wiredata.size());
// toWire() cannot be performed for an empty RRset.
renderer.clear();
EXPECT_THROW(rrset_a_empty.toWire(renderer), EmptyRRset);
buffer.clear();
EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset);
// Unless it is type ANY or None
// toWire() can also be performed for an empty RRset.
buffer.clear();
rrset_any_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire3", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
buffer.getLength(), &wiredata[0], wiredata.size());
buffer.clear();
rrset_none_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire4", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
buffer.getLength(), &wiredata[0], wiredata.size());
}
// test operator<<. We simply confirm it appends the result of toText().
......
......@@ -146,6 +146,7 @@ EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire
EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2
EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2
EXTRA_DIST += rrset_toWire1 rrset_toWire2
EXTRA_DIST += rrset_toWire3 rrset_toWire4
EXTRA_DIST += rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec
EXTRA_DIST += rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec
EXTRA_DIST += rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec
......
#
# Rendering an empty IN/A RRset
#
#(4) t e s t (7) e x a m p l e (3) c o m .
04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
# type/class: A = 1, ANY = 255
00 01 00 ff
# TTL: 3600
00 00 0e 10
#6 7
# RDLENGTH: 0
00 00
#
# Rendering an empty IN/A RRset
#
#(4) t e s t (7) e x a m p l e (3) c o m .
04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
# type/class: A = 1, ANY = 255
00 01 00 fe
# TTL: 3600
00 00 0e 10
#6 7
# RDLENGTH: 0
00 00
......@@ -147,7 +147,8 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
// increases the refcount and the container decreases it
// later. This way, it feels safer in case the build function
// would fail.
return (Py_BuildValue("IO", r, list_container.get()));
return (Py_BuildValue("IOI", r, list_container.get(),
result_flags));
} else {
if (rrsp) {
// Use N instead of O so the refcount isn't increased twice
......
......@@ -83,7 +83,7 @@ def test_findall_common(self, tested):
# A success. It should return the list now.
# This also tests we can ommit the options parameter
result, rrsets = tested.find_all(isc.dns.Name("mix.example.com."))
result, rrsets, _ = tested.find_all(isc.dns.Name("mix.example.com."))
self.assertEqual(ZoneFinder.SUCCESS, result)
self.assertEqual(2, len(rrsets))
rrsets.sort(key=lambda rrset: rrset.get_type().to_text())
......
......@@ -15,6 +15,84 @@
# No namespace declaration - these constants go in the global namespace
# of the libddns_messages python module.
% LIBDDNS_PREREQ_FORMERR update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL.
The prerequisite with the given name, class and type is not well-formed.
The specific prerequisite is shown. In this case, it has a non-zero TTL value.
A FORMERR error response is sent to the client.
% LIBDDNS_PREREQ_FORMERR_ANY update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.
The prerequisite with the given name, class and type is not well-formed.
The specific prerequisite is shown. In this case, it either has a non-zero
TTL value, or has rdata fields. A FORMERR error response is sent to the client.
% LIBDDNS_PREREQ_FORMERR_CLASS update client %1 for zone %2: Format error in prerequisite (%3). Bad class.
The prerequisite with the given name, class and type is not well-formed.
The specific prerequisite is shown. In this case, the class of the
prerequisite should either match the class of the zone in the Zone Section,
or it should be ANY or NONE, and it is not. A FORMERR error response is sent
to the client.
% LIBDDNS_PREREQ_FORMERR_NONE update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.
The prerequisite with the given name, class and type is not well-formed.
The specific prerequisite is shown. In this case, it either has a non-zero
TTL value, or has rdata fields. A FORMERR error response is sent to the client.
% LIBDDNS_PREREQ_NAME_IN_USE_FAILED update client %1 for zone %2: 'Name is in use' prerequisite not satisfied (%3), rcode: %4
A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
was not satisfied is shown. The client is sent an error response with the
given rcode.
In this case, the specific prerequisite is 'Name is in use'. From RFC2136:
Name is in use. At least one RR with a specified NAME (in
the zone and class specified by the Zone Section) must exist.
Note that this prerequisite is NOT satisfied by empty
nonterminals.
% LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED update client %1 for zone %2: 'Name is not in use' (%3) prerequisite not satisfied, rcode: %4
A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
was not satisfied is shown. The client is sent an error response with the
given rcode.
In this case, the specific prerequisite is 'Name is not in use'.
From RFC2136:
Name is not in use. No RR of any type is owned by a
specified NAME. Note that this prerequisite IS satisfied by
empty nonterminals.
% LIBDDNS_PREREQ_NOTZONE update client %1 for zone %2: prerequisite not in zone (%3)
A DNS UPDATE prerequisite has a name that does not appear to be inside
the zone specified in the Zone section of the UPDATE message.
The specific prerequisite is shown. A NOTZONE error response is sent to
the client.
% LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED update client %1 for zone %2: 'RRset does not exist' (%3) prerequisite not satisfied, rcode: %4
A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
was not satisfied is shown. The client is sent an error response with the
given rcode.
In this case, the specific prerequisite is 'RRset does not exist'.
From RFC2136:
RRset does not exist. No RRs with a specified NAME and TYPE
(in the zone and class denoted by the Zone Section) can exist.
% LIBDDNS_PREREQ_RRSET_EXISTS_FAILED update client %1 for zone %2: 'RRset exists (value independent)' (%3) prerequisite not satisfied, rcode: %4
A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
was not satisfied is shown. The client is sent an error response with the
given rcode.
In this case, the specific prerequisite is 'RRset exists (value independent)'.
From RFC2136:
RRset exists (value dependent). A set of RRs with a
specified NAME and TYPE exists and has the same members
with the same RDATAs as the RRset specified here in this
Section.
% LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED update client %1 for zone %2: 'RRset exists (value dependent)' (%3) prerequisite not satisfied, rcode: %4
A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
was not satisfied is shown. The client is sent an error response with the
given rcode.
In this case, the specific prerequisite is 'RRset exists (value dependent)'.
From RFC2136:
RRset exists (value independent). At least one RR with a
specified NAME and TYPE (in the zone and class specified by
the Zone Section) must exist.
% LIBDDNS_UPDATE_ERROR update client %1 for zone %2: %3
Debug message. An error is found in processing a dynamic update
request. This log message is used for general errors that are not
......@@ -39,3 +117,10 @@ possible, you may want to check the implementation or configuration of
those clients to suppress the requests. As specified in Section 3.1
of RFC2136, the receiving server will return a response with an RCODE
of NOTAUTH.
% LIBDDNS_UPDATE_PREREQUISITE_FAILED prerequisite failed in update update client %1 for zone %2: result code %3
The handling of the prerequisite section (RFC2136 Section 3.2) found
that one of the prerequisites was not satisfied. The result code
should give more information on what prerequisite type failed.
If the result code is FORMERR, the prerequisite section was not well-formed.
An error response with the given result code is sent back to the client.
......@@ -85,3 +85,28 @@ class ZoneFormatter:
if self.__zname is None:
return '(zone unknown/not determined)'
return self.__zname.to_text(True) + '/' + self.__zclass.to_text()
class RRsetFormatter:
"""A utility class to convert rrsets to a short descriptive string.
This class is constructed with an rrset (isc.dns.RRset object).
Its text conversion method (__str__) converts it into a string
with only the name, class and type of the rrset.
This is used in logging so that the RRset can be identified, without
being completely printed, which would result in an unnecessary
multi-line message.
This class is designed to delay the conversion until it's explicitly
requested, so the conversion doesn't happen if the corresponding log
message is suppressed because of its log level.
See the note for the ClientFormatter class about overhead tradeoff.
This class shares the same discussion.
"""
def __init__(self, rrset):
self.__rrset = rrset
def __str__(self):
return self.__rrset.get_name().to_text() + " " +\
self.__rrset.get_class().to_text() + " " +\
self.__rrset.get_type().to_text()
......@@ -16,8 +16,10 @@
from isc.dns import *
import isc.ddns.zone_config
from isc.log import *
from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter
from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter,\
RRsetFormatter
from isc.log_messages.libddns_messages import *
import copy
# Result codes for UpdateSession.handle()
UPDATE_SUCCESS = 0
......@@ -123,7 +125,11 @@ class UpdateSession:
try:
datasrc_client, zname, zclass = self.__get_update_zone()
# conceptual code that would follow
# self.__check_prerequisites()
prereq_result = self.__check_prerequisites(datasrc_client,
zname, zclass)
if prereq_result != Rcode.NOERROR():
self.__make_response(prereq_result)
return UPDATE_ERROR, zname, zclass
# self.__check_update_acl()
# self.__do_update()
# self.__make_response(Rcode.NOERROR())
......@@ -187,3 +193,180 @@ class UpdateSession:
self.__message.make_response()
self.__message.clear_section(SECTION_ZONE)
self.__message.set_rcode(rcode)
def __prereq_rrset_exists(self, datasrc_client, rrset):
'''Check whether an rrset with the given name and type exists. Class,
TTL, and Rdata (if any) of the given RRset are ignored.
RFC2136 Section 2.4.1.
Returns True if the prerequisite is satisfied, False otherwise.
Note: the only thing used in the call to find() here is the
result status. The actual data is immediately dropped. As
a future optimization, we may want to add a find() option to
only return what the result code would be (and not read/copy
any actual data).
'''
_, finder = datasrc_client.find_zone(rrset.get_name())
result, _, _ = finder.find(rrset.get_name(), rrset.get_type(),
finder.NO_WILDCARD | finder.FIND_GLUE_OK)
return result == finder.SUCCESS
def __prereq_rrset_exists_value(self, datasrc_client, rrset):
'''Check whether an rrset that matches name, type, and rdata(s) of the
given rrset exists.
RFC2136 Section 2.4.2
Returns True if the prerequisite is satisfied, False otherwise.
'''
_, finder = datasrc_client.find_zone(rrset.get_name())
result, found_rrset, _ = finder.find(rrset.get_name(), rrset.get_type(),
finder.NO_WILDCARD |
finder.FIND_GLUE_OK)
if result == finder.SUCCESS and\
rrset.get_name() == found_rrset.get_name() and\
rrset.get_type() == found_rrset.get_type():
# We need to match all actual RRs, unfortunately there is no
# direct order-independent comparison for rrsets, so this
# a slightly inefficient way to handle that.
# shallow copy of the rdata list, so we are sure that this
# loop does not mess with actual data.
found_rdata = copy.copy(found_rrset.get_rdata())
for rdata in rrset.get_rdata():
if rdata in found_rdata:
found_rdata.remove(rdata)
else:
return False
return len(found_rdata) == 0
return False
def __prereq_rrset_does_not_exist(self, datasrc_client, rrset):
'''Check whether no rrsets with the same name and type as the given
rrset exist.
RFC2136 Section 2.4.3.
Returns True if the prerequisite is satisfied, False otherwise.
'''
return not self.__prereq_rrset_exists(datasrc_client, rrset)
def __prereq_name_in_use(self, datasrc_client, rrset):
'''Check whether the name of the given RRset is in use (i.e. has
1 or more RRs).
RFC2136 Section 2.4.4
Returns True if the prerequisite is satisfied, False otherwise.
Note: the only thing used in the call to find_all() here is
the result status. The actual data is immediately dropped. As
a future optimization, we may want to add a find_all() option
to only return what the result code would be (and not read/copy
any actual data).
'''
_, finder = datasrc_client.find_zone(rrset.get_name())
result, rrsets, flags = finder.find_all(rrset.get_name(),
finder.NO_WILDCARD |
finder.FIND_GLUE_OK)
if result == finder.SUCCESS and\
(flags & finder.RESULT_WILDCARD == 0):
return True
return False
def __prereq_name_not_in_use(self, datasrc_client, rrset):
'''Check whether the name of the given RRset is not in use (i.e. does
not exist at all, or is an empty nonterminal.
RFC2136 Section 2.4.5.
Returns True if the prerequisite is satisfied, False otherwise.
'''
return not self.__prereq_name_in_use(datasrc_client, rrset)
def __check_prerequisites(self, datasrc_client, zname, zclass):
'''Check the prerequisites section of the UPDATE Message.
RFC2136 Section 2.4.
Returns a dns Rcode signaling either no error (Rcode.NOERROR())
or that one of the prerequisites failed (any other Rcode).
'''
for rrset in self.__message.get_section(SECTION_PREREQUISITE):
# First check if the name is in the zone
relation = rrset.get_name().compare(zname).get_relation()
if relation != NameComparisonResult.SUBDOMAIN and\
relation != NameComparisonResult.EQUAL:
logger.info(LIBDDNS_PREREQ_NOTZONE,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset))
return Rcode.NOTZONE()
# Algorithm taken from RFC2136 Section 3.2
if rrset.get_class() == RRClass.ANY():
if rrset.get_ttl().get_value() != 0 or\
rrset.get_rdata_count() != 0:
logger.info(LIBDDNS_PREREQ_FORMERR_ANY,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset))
return Rcode.FORMERR()
elif rrset.get_type() == RRType.ANY():
if not self.__prereq_name_in_use(datasrc_client,
rrset):
rcode = Rcode.NXDOMAIN()
logger.info(LIBDDNS_PREREQ_NAME_IN_USE_FAILED,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset), rcode)
return rcode
else:
if not self.__prereq_rrset_exists(datasrc_client, rrset):
rcode = Rcode.NXRRSET()
logger.info(LIBDDNS_PREREQ_RRSET_EXISTS_FAILED,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset), rcode)
return rcode
elif rrset.get_class() == RRClass.NONE():
if rrset.get_ttl().get_value() != 0 or\
rrset.get_rdata_count() != 0:
logger.info(LIBDDNS_PREREQ_FORMERR_NONE,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset))
return Rcode.FORMERR()
elif rrset.get_type() == RRType.ANY():
if not self.__prereq_name_not_in_use(datasrc_client,
rrset):
rcode = Rcode.YXDOMAIN()
logger.info(LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset), rcode)
return rcode
else:
if not self.__prereq_rrset_does_not_exist(datasrc_client,
rrset):
rcode = Rcode.YXRRSET()
logger.info(LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset), rcode)
return rcode
elif rrset.get_class() == zclass:
if rrset.get_ttl().get_value() != 0:
logger.info(LIBDDNS_PREREQ_FORMERR,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset))
return Rcode.FORMERR()
else:
if not self.__prereq_rrset_exists_value(datasrc_client,
rrset):
rcode = Rcode.NXRRSET()
logger.info(LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset), rcode)
return rcode
else:
logger.info(LIBDDNS_PREREQ_FORMERR_CLASS,
ClientFormatter(self.__client_addr),
ZoneFormatter(zname, zclass),
RRsetFormatter(rrset))
return Rcode.FORMERR()
# All prerequisites are satisfied
return Rcode.NOERROR()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment