else:
self.delegations = Delegations()
- def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
- node = super()._maybe_cow(name)
- if name == self.zone.origin:
- node.flags |= NodeFlags.ORIGIN # type: ignore
+ def _is_origin(self, name: dns.name.Name) -> bool:
+ # Assumes name has already been validated (and thus adjusted to the right
+ # relativity too)
+ if self.zone.relativize:
+ return name == dns.name.empty
+ else:
+ return name == self.zone.origin
+
+ def _maybe_cow_with_name(
+ self, name: dns.name.Name
+ ) -> Tuple[dns.node.Node, dns.name.Name]:
+ (node, name) = super()._maybe_cow_with_name(name)
+ node = cast(Node, node)
+ if self._is_origin(name):
+ node.flags |= NodeFlags.ORIGIN
elif self.delegations.is_glue(name):
- node.flags |= NodeFlags.GLUE # type: ignore
- return node
+ node.flags |= NodeFlags.GLUE
+ return (node, name)
def update_glue_flag(self, name: dns.name.Name, is_glue: bool) -> None:
cursor = self.nodes.cursor() # type: ignore
def put_rdataset(
self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset
) -> None:
- node = self._maybe_cow(name)
+ (node, name) = self._maybe_cow_with_name(name)
if (
rdataset.rdtype == dns.rdatatype.NS and not node.is_origin_or_glue() # type: ignore
):
rdtype: dns.rdatatype.RdataType,
covers: dns.rdatatype.RdataType,
) -> None:
- node = self._maybe_cow(name)
+ (node, name) = self._maybe_cow_with_name(name)
if rdtype == dns.rdatatype.NS and name in self.delegations: # type: ignore
node.flags &= ~NodeFlags.DELEGATION # type: ignore
self.delegations.discard(name) # type: ignore
self.origin = zone.origin
self.changed: Set[dns.name.Name] = set()
- def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
+ def _maybe_cow_with_name(
+ self, name: dns.name.Name
+ ) -> Tuple[dns.node.Node, dns.name.Name]:
name = self._validate_name(name)
node = self.nodes.get(name)
if node is None or name not in self.changed:
new_node.rdatasets.extend(node.rdatasets)
self.nodes[name] = new_node
self.changed.add(name)
- return new_node
+ return (new_node, name)
else:
- return node
+ return (node, name)
+
+ def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
+ return self._maybe_cow_with_name(name)[0]
def delete_node(self, name: dns.name.Name) -> None:
name = self._validate_name(name)
--- /dev/null
+from typing import cast
+
+import dns.btreezone
+import dns.rdataset
+import dns.zone
+
+Node = dns.btreezone.Node
+
+simple_zone = """
+$ORIGIN example.
+$TTL 300
+@ soa foo bar 1 2 3 4 5
+@ ns ns1
+@ ns ns2
+ns1 a 10.0.0.1
+ns2 a 10.0.0.2
+sub ns ns1.sub
+sub ns ns2.sub
+ns1.sub a 10.0.0.3
+ns2.sub a 10.0.0.4
+ns1.sub2 a 10.0.0.5
+ns2.sub2 a 10.0.0.6
+text txt "here to be after sub2"
+"""
+
+
+def make_example(text: str, relativize: bool = False) -> dns.btreezone.Zone:
+ z = dns.zone.from_text(
+ simple_zone, "example.", relativize=relativize, zone_factory=dns.btreezone.Zone
+ )
+ return cast(dns.btreezone.Zone, z)
+
+
+def do_test_node_flags(relativize: bool):
+ z = make_example(simple_zone, relativize)
+ n = cast(Node, z.get_node("@"))
+ assert not n.is_delegation()
+ assert not n.is_glue()
+ assert n.is_origin()
+ assert n.is_origin_or_glue()
+ assert n.is_immutable()
+ n = cast(Node, z.get_node("sub"))
+ assert n.is_delegation()
+ assert not n.is_glue()
+ assert not n.is_origin()
+ assert not n.is_origin_or_glue()
+ n = cast(Node, z.get_node("ns1.sub"))
+ assert not n.is_delegation()
+ assert n.is_glue()
+ assert not n.is_origin()
+ assert n.is_origin_or_glue()
+
+
+def test_node_flags_absolute():
+ do_test_node_flags(False)
+
+
+def test_node_flags_relative():
+ do_test_node_flags(True)
+
+
+def test_flags_in_constructor():
+ n = Node()
+ assert n.flags == 0
+ n = Node(dns.btreezone.NodeFlags.ORIGIN)
+ assert n.is_origin()
+
+
+def do_test_obscure_and_expose(relativize: bool):
+ z = make_example(simple_zone, relativize=relativize)
+ n = cast(Node, z.get_node("ns1.sub2"))
+ assert not n.is_delegation()
+ assert not n.is_glue()
+ assert not n.is_origin()
+ assert not n.is_origin_or_glue()
+ rds = dns.rdataset.from_text("in", "ns", 300, "ns1.sub2", "ns2.sub2")
+ with z.writer() as txn:
+ txn.replace("sub2", rds)
+ n = cast(Node, z.get_node("ns1.sub2"))
+ assert not n.is_delegation()
+ assert n.is_glue()
+ assert not n.is_origin()
+ assert n.is_origin_or_glue()
+ with z.writer() as txn:
+ txn.delete("sub2")
+ txn.delete("ns2.sub2") # for other coverage purposes!
+ n = cast(Node, z.get_node("ns1.sub2"))
+ assert not n.is_delegation()
+ assert not n.is_glue()
+ assert not n.is_origin()
+ assert not n.is_origin_or_glue()
+ # repeat but delete just the rdataset
+ rds = dns.rdataset.from_text("in", "ns", 300, "ns1.sub2", "ns2.sub2")
+ with z.writer() as txn:
+ txn.replace("sub2", rds)
+ n = cast(Node, z.get_node("ns1.sub2"))
+ assert not n.is_delegation()
+ assert n.is_glue()
+ assert not n.is_origin()
+ assert n.is_origin_or_glue()
+ with z.writer() as txn:
+ txn.delete("sub2", "NS")
+ n = cast(Node, z.get_node("ns1.sub2"))
+ assert not n.is_delegation()
+ assert not n.is_glue()
+ assert not n.is_origin()
+ assert not n.is_origin_or_glue()
+
+
+def test_obscure_and_expose_absolute():
+ do_test_obscure_and_expose(False)
+
+
+def test_obscure_and_expose_relative():
+ do_test_obscure_and_expose(True)