|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""DNS name dictionary""" |
|
|
|
|
|
from collections.abc import MutableMapping |
|
|
|
import dns.name |
|
|
|
|
|
class NameDict(MutableMapping): |
|
"""A dictionary whose keys are dns.name.Name objects. |
|
|
|
In addition to being like a regular Python dictionary, this |
|
dictionary can also get the deepest match for a given key. |
|
""" |
|
|
|
__slots__ = ["max_depth", "max_depth_items", "__store"] |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__() |
|
self.__store = dict() |
|
|
|
self.max_depth = 0 |
|
|
|
self.max_depth_items = 0 |
|
self.update(dict(*args, **kwargs)) |
|
|
|
def __update_max_depth(self, key): |
|
if len(key) == self.max_depth: |
|
self.max_depth_items = self.max_depth_items + 1 |
|
elif len(key) > self.max_depth: |
|
self.max_depth = len(key) |
|
self.max_depth_items = 1 |
|
|
|
def __getitem__(self, key): |
|
return self.__store[key] |
|
|
|
def __setitem__(self, key, value): |
|
if not isinstance(key, dns.name.Name): |
|
raise ValueError("NameDict key must be a name") |
|
self.__store[key] = value |
|
self.__update_max_depth(key) |
|
|
|
def __delitem__(self, key): |
|
self.__store.pop(key) |
|
if len(key) == self.max_depth: |
|
self.max_depth_items = self.max_depth_items - 1 |
|
if self.max_depth_items == 0: |
|
self.max_depth = 0 |
|
for k in self.__store: |
|
self.__update_max_depth(k) |
|
|
|
def __iter__(self): |
|
return iter(self.__store) |
|
|
|
def __len__(self): |
|
return len(self.__store) |
|
|
|
def has_key(self, key): |
|
return key in self.__store |
|
|
|
def get_deepest_match(self, name): |
|
"""Find the deepest match to *name* in the dictionary. |
|
|
|
The deepest match is the longest name in the dictionary which is |
|
a superdomain of *name*. Note that *superdomain* includes matching |
|
*name* itself. |
|
|
|
*name*, a ``dns.name.Name``, the name to find. |
|
|
|
Returns a ``(key, value)`` where *key* is the deepest |
|
``dns.name.Name``, and *value* is the value associated with *key*. |
|
""" |
|
|
|
depth = len(name) |
|
if depth > self.max_depth: |
|
depth = self.max_depth |
|
for i in range(-depth, 0): |
|
n = dns.name.Name(name[i:]) |
|
if n in self: |
|
return (n, self[n]) |
|
v = self[dns.name.empty] |
|
return (dns.name.empty, v) |
|
|