1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license 2 3# Copyright (C) 2003-2017 Nominum, Inc. 4# Copyright (C) 2016 Coresec Systems AB 5# 6# Permission to use, copy, modify, and distribute this software and its 7# documentation for any purpose with or without fee is hereby granted, 8# provided that the above copyright notice and this permission notice 9# appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18# 19# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL 20# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 21# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC 22# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 23# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 24# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 25# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 26# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 28"""DNS name dictionary""" 29 30from collections.abc import MutableMapping 31 32import dns.name 33 34 35class NameDict(MutableMapping): 36 """A dictionary whose keys are dns.name.Name objects. 37 38 In addition to being like a regular Python dictionary, this 39 dictionary can also get the deepest match for a given key. 40 """ 41 42 __slots__ = ["max_depth", "max_depth_items", "__store"] 43 44 def __init__(self, *args, **kwargs): 45 super().__init__() 46 self.__store = dict() 47 #: the maximum depth of the keys that have ever been added 48 self.max_depth = 0 49 #: the number of items of maximum depth 50 self.max_depth_items = 0 51 self.update(dict(*args, **kwargs)) 52 53 def __update_max_depth(self, key): 54 if len(key) == self.max_depth: 55 self.max_depth_items = self.max_depth_items + 1 56 elif len(key) > self.max_depth: 57 self.max_depth = len(key) 58 self.max_depth_items = 1 59 60 def __getitem__(self, key): 61 return self.__store[key] 62 63 def __setitem__(self, key, value): 64 if not isinstance(key, dns.name.Name): 65 raise ValueError('NameDict key must be a name') 66 self.__store[key] = value 67 self.__update_max_depth(key) 68 69 def __delitem__(self, key): 70 self.__store.pop(key) 71 if len(key) == self.max_depth: 72 self.max_depth_items = self.max_depth_items - 1 73 if self.max_depth_items == 0: 74 self.max_depth = 0 75 for k in self.__store: 76 self.__update_max_depth(k) 77 78 def __iter__(self): 79 return iter(self.__store) 80 81 def __len__(self): 82 return len(self.__store) 83 84 def has_key(self, key): 85 return key in self.__store 86 87 def get_deepest_match(self, name): 88 """Find the deepest match to *fname* in the dictionary. 89 90 The deepest match is the longest name in the dictionary which is 91 a superdomain of *name*. Note that *superdomain* includes matching 92 *name* itself. 93 94 *name*, a ``dns.name.Name``, the name to find. 95 96 Returns a ``(key, value)`` where *key* is the deepest 97 ``dns.name.Name``, and *value* is the value associated with *key*. 98 """ 99 100 depth = len(name) 101 if depth > self.max_depth: 102 depth = self.max_depth 103 for i in range(-depth, 0): 104 n = dns.name.Name(name[i:]) 105 if n in self: 106 return (n, self[n]) 107 v = self[dns.name.empty] 108 return (dns.name.empty, v) 109