]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
processing order implementation
authorBob Halley <halley@dnspython.org>
Sat, 29 Aug 2020 20:48:51 +0000 (13:48 -0700)
committerBob Halley <halley@dnspython.org>
Wed, 2 Sep 2020 14:29:26 +0000 (07:29 -0700)
dns/rdata.py
dns/rdataset.py
dns/rdtypes/ANY/URI.py
dns/rdtypes/IN/NAPTR.py
dns/rdtypes/IN/PX.py
dns/rdtypes/IN/SRV.py
dns/rdtypes/mxbase.py
dns/rdtypes/svcbbase.py
dns/rdtypes/util.py

index acf34aecbf8c18ff2c11ea2a64c06a9fdea5b3da..78462051659c72ca298cd7a94dd1b11986e6820f 100644 (file)
@@ -23,6 +23,7 @@ import binascii
 import io
 import inspect
 import itertools
+import random
 
 import dns.wire
 import dns.exception
@@ -459,6 +460,14 @@ class Rdata:
             # against *as_value*.
             return tuple(as_value(v) for v in value)
 
+    # Processing order
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        items = list(iterable)
+        random.shuffle(items)
+        return items
+
 
 class GenericRdata(Rdata):
 
index 2e3b4d4ad609369cb459228a6b5ec2c48e6a9d69..f27e251bf2ff92bea89bcb62a5707667f0297ebb 100644 (file)
@@ -306,6 +306,20 @@ class Rdataset(dns.set.Set):
             return True
         return False
 
+    def processing_order(self):
+        """Return rdatas in a valid processing order according to the type's
+        specification.  For example, MX records are in preference order from
+        lowest to highest preferences, with items of the same perference
+        shuffled.
+
+        For types that do not define a processing order, the rdatas are
+        simply shuffled.
+        """
+        if len(self) == 0:
+            return []
+        else:
+            return self[0]._processing_order(iter(self))
+
 
 @dns.immutable.immutable
 class ImmutableRdataset(Rdataset):
index 60a43c88b05326878ba39cd62e8b7b2fbb83cb08..4e7ad01c6f20939e01716372e106885935fa9e7f 100644 (file)
@@ -67,3 +67,13 @@ class URI(dns.rdata.Rdata):
         if len(target) == 0:
             raise dns.exception.FormError('URI target may not be empty')
         return cls(rdclass, rdtype, priority, weight, target)
+
+    def _processing_priority(self):
+        return self.priority
+
+    def _processing_weight(self):
+        return self.weight
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.weighted_processing_order(iterable, False)
index a4058adc6014f9d26741a058e3aaa3df5e70fe70..1f072f89e7f031afde2243b465f0e7ab2c41e6d0 100644 (file)
@@ -89,3 +89,10 @@ class NAPTR(dns.rdata.Rdata):
         replacement = parser.get_name(origin)
         return cls(rdclass, rdtype, order, preference, strings[0], strings[1],
                    strings[2], replacement)
+
+    def _processing_priority(self):
+        return (self.order, self.preference)
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
index 3a744f6b646f001857a878376ed96bcee129cd65..8abfb291c9b6f45e82a8d85388cdb147a4f77abe 100644 (file)
@@ -63,3 +63,10 @@ class PX(dns.rdata.Rdata):
         map822 = parser.get_name(origin)
         mapx400 = parser.get_name(origin)
         return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    def _processing_priority(self):
+        return self.preference
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
index 99ff70bffdb86562139340c84c8dc3a17fc646f1..3eb227f98ec70198fdc7756573a5bdd5e27448ca 100644 (file)
@@ -63,3 +63,13 @@ class SRV(dns.rdata.Rdata):
         (priority, weight, port) = parser.get_struct('!HHH')
         target = parser.get_name(origin)
         return cls(rdclass, rdtype, priority, weight, port, target)
+
+    def _processing_priority(self):
+        return self.priority
+
+    def _processing_weight(self):
+        return self.weight
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.weighted_processing_order(iterable, True)
index 7bacbd8b78633e2385b4e84f18ba74c8b91aebff..564182347c1236b08b1f40c9b8b780ecbe111f96 100644 (file)
@@ -23,6 +23,7 @@ import dns.exception
 import dns.immutable
 import dns.rdata
 import dns.name
+import dns.rdtypes.util
 
 
 @dns.immutable.immutable
@@ -59,6 +60,13 @@ class MXBase(dns.rdata.Rdata):
         exchange = parser.get_name(origin)
         return cls(rdclass, rdtype, preference, exchange)
 
+    def _processing_priority(self):
+        return self.preference
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
+
 
 @dns.immutable.immutable
 class UncompressedMX(MXBase):
index 4a3a2179cfb1ea1dbb6dedea97c51f4895aa6b73..585c05e0e1964a015a12e339973d678262667969 100644 (file)
@@ -534,3 +534,10 @@ class SVCBBase(dns.rdata.Rdata):
                 value = pcls.from_wire_parser(parser, origin)
             params[key] = value
         return cls(rdclass, rdtype, priority, target, params)
+
+    def _processing_priority(self):
+        return self.priority
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
index 30be37dcc01d905a8de879494a3807e32647203f..c2fb6038256f5634dde084d1aa3a2dacf782126e 100644 (file)
@@ -15,6 +15,7 @@
 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import random
 import struct
 
 import dns.exception
@@ -183,3 +184,63 @@ class Bitmap:
             bitmap = parser.get_counted_bytes()
             windows.append((window, bitmap))
         return cls(windows)
+
+
+def _priority_table(items):
+    by_priority = {}
+    for rdata in items:
+        key = rdata._processing_priority()
+        rdatas = by_priority.get(key)
+        if rdatas is None:
+            rdatas = []
+            by_priority[key] = rdatas
+        rdatas.append(rdata)
+    return by_priority
+
+def priority_processing_order(iterable):
+    items = list(iterable)
+    if len(items) == 1:
+        return [items[0]]
+    by_priority = _priority_table(items)
+    ordered = []
+    for k in sorted(by_priority.keys()):
+        rdatas = by_priority[k]
+        random.shuffle(rdatas)
+        ordered.extend(rdatas)
+    return ordered
+
+def _processing_weight(rdata, adjust_zero_weight):
+    weight = rdata._processing_weight()
+    if weight == 0 and adjust_zero_weight:
+        return 0.1
+    else:
+        return weight
+
+def weighted_processing_order(iterable, adjust_zero_weight=False):
+    items = list(iterable)
+    if len(items) == 1:
+        return [items[0]]
+    by_priority = _priority_table(items)
+    ordered = []
+    for k in sorted(by_priority.keys()):
+        weights_vary = False
+        weights = []
+        rdatas = by_priority[k]
+        for rdata in rdatas:
+            weight = _processing_weight(rdata, adjust_zero_weight)
+            if len(weights) > 0 and weight != weights[-1]:
+                weights_vary = True
+            weights.append(weight)
+        if weights_vary:
+            while len(rdatas) > 1:
+                items = random.choices(rdatas, weights)
+                rdata = items[0]
+                ordered.append(rdata)
+                rdatas.remove(rdata)
+                weight = _processing_weight(rdata, adjust_zero_weight)
+                weights.remove(weight)
+            ordered.append(rdatas[0])
+        else:
+            random.shuffle(rdatas)
+            ordered.extend(rdatas)
+    return ordered