You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
3.9 KiB
109 lines
3.9 KiB
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license |
|
|
|
# Copyright (C) 2003-2017 Nominum, Inc. |
|
# Copyright (C) 2016 Coresec Systems AB |
|
# |
|
# Permission to use, copy, modify, and distribute this software and its |
|
# documentation for any purpose with or without fee is hereby granted, |
|
# provided that the above copyright notice and this permission notice |
|
# appear in all copies. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES |
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR |
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL |
|
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC |
|
# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR |
|
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
|
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
|
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION |
|
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
|
|
"""DNS name dictionary""" |
|
|
|
# pylint seems to be confused about this one! |
|
from collections.abc import MutableMapping # pylint: disable=no-name-in-module |
|
|
|
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() |
|
#: the maximum depth of the keys that have ever been added |
|
self.max_depth = 0 |
|
#: the number of items of maximum depth |
|
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)
|
|
|