]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Rework wire format processing. 528/head
authorBob Halley <halley@play-bow.org>
Thu, 2 Jul 2020 15:23:52 +0000 (08:23 -0700)
committerBob Halley <halley@dnspython.org>
Thu, 2 Jul 2020 22:11:08 +0000 (15:11 -0700)
Wire format data is now done via a dns.wire.Parser, which does all of the
bookkeeping and also provides convenience routines (e.g. get_uint16() or
get_name()).

52 files changed:
dns/__init__.py
dns/edns.py
dns/message.py
dns/name.py
dns/rdata.py
dns/rdtypes/ANY/AMTRELAY.py
dns/rdtypes/ANY/CAA.py
dns/rdtypes/ANY/CERT.py
dns/rdtypes/ANY/CSYNC.py
dns/rdtypes/ANY/GPOS.py
dns/rdtypes/ANY/HINFO.py
dns/rdtypes/ANY/HIP.py
dns/rdtypes/ANY/ISDN.py
dns/rdtypes/ANY/LOC.py
dns/rdtypes/ANY/NSEC.py
dns/rdtypes/ANY/NSEC3.py
dns/rdtypes/ANY/NSEC3PARAM.py
dns/rdtypes/ANY/OPENPGPKEY.py
dns/rdtypes/ANY/OPT.py
dns/rdtypes/ANY/RP.py
dns/rdtypes/ANY/RRSIG.py
dns/rdtypes/ANY/SOA.py
dns/rdtypes/ANY/SSHFP.py
dns/rdtypes/ANY/TLSA.py
dns/rdtypes/ANY/TSIG.py
dns/rdtypes/ANY/URI.py
dns/rdtypes/ANY/X25.py
dns/rdtypes/CH/A.py
dns/rdtypes/IN/A.py
dns/rdtypes/IN/AAAA.py
dns/rdtypes/IN/APL.py
dns/rdtypes/IN/DHCID.py
dns/rdtypes/IN/IPSECKEY.py
dns/rdtypes/IN/NAPTR.py
dns/rdtypes/IN/NSAP.py
dns/rdtypes/IN/PX.py
dns/rdtypes/IN/SRV.py
dns/rdtypes/IN/WKS.py
dns/rdtypes/dnskeybase.py
dns/rdtypes/dnskeybase.pyi
dns/rdtypes/dsbase.py
dns/rdtypes/euibase.py
dns/rdtypes/mxbase.py
dns/rdtypes/nsbase.py
dns/rdtypes/txtbase.py
dns/rdtypes/util.py
dns/wire.py [new file with mode: 0644]
dns/wiredata.py [deleted file]
tests/test_rdata.py
tests/test_rdtypeanyeui.py
tests/test_wire.py [new file with mode: 0644]
tests/test_wiredata.py [deleted file]

index bb87ff477702607fd13505e8330b25bfe332656f..b944701d3c84e3e4080e676ab75e1bc11d72ba81 100644 (file)
@@ -54,7 +54,7 @@ __all__ = [
     'rdtypes',
     'update',
     'version',
-    'wiredata',
+    'wire',
     'zone',
 ]
 
index 82e7abcb861514b7b0caecf752c1b829a76e07bd..4ad3a5c1547807e1e6da5697e59ee3482dfcc0cd 100644 (file)
@@ -72,21 +72,16 @@ class Option:
         raise NotImplementedError  # pragma: no cover
 
     @classmethod
-    def from_wire(cls, otype, wire, current, olen):
+    def from_wire_parser(cls, otype, parser):
         """Build an EDNS option object from wire format.
 
         *otype*, an ``int``, is the option type.
 
-        *wire*, a ``bytes``, is the wire-format message.
-
-        *current*, an ``int``, is the offset in *wire* of the beginning
-        of the rdata.
-
-        *olen*, an ``int``, is the length of the wire-format option data
+        *parser*, a ``dns.wire.Parser``, the parser, which should be
+        restructed to the option length.
 
         Returns a ``dns.edns.Option``.
         """
-
         raise NotImplementedError  # pragma: no cover
 
     def _cmp(self, other):
@@ -163,8 +158,8 @@ class GenericOption(Option):
         return "Generic %d" % self.otype
 
     @classmethod
-    def from_wire(cls, otype, wire, current, olen):
-        return cls(otype, wire[current: current + olen])
+    def from_wire_parser(cls, otype, parser):
+        return cls(otype, parser.get_remaining())
 
     def __str__(self):
         return self.to_text()
@@ -281,18 +276,16 @@ class ECSOption(Option):
             return value
 
     @classmethod
-    def from_wire(cls, otype, wire, cur, olen):
-        family, src, scope = struct.unpack('!HBB', wire[cur:cur + 4])
-        cur += 4
-
+    def from_wire_parser(cls, otype, parser):
+        family, src, scope = parser.get_struct('!HBB')
         addrlen = int(math.ceil(src / 8.0))
-
+        prefix = parser.get_bytes(addrlen)
         if family == 1:
             pad = 4 - addrlen
-            addr = dns.ipv4.inet_ntoa(wire[cur:cur + addrlen] + b'\x00' * pad)
+            addr = dns.ipv4.inet_ntoa(prefix + b'\x00' * pad)
         elif family == 2:
             pad = 16 - addrlen
-            addr = dns.ipv6.inet_ntoa(wire[cur:cur + addrlen] + b'\x00' * pad)
+            addr = dns.ipv6.inet_ntoa(prefix + b'\x00' * pad)
         else:
             raise ValueError('unsupported family')
 
@@ -301,6 +294,7 @@ class ECSOption(Option):
     def __str__(self):
         return self.to_text()
 
+
 _type_to_class = {
     OptionType.ECS: ECSOption
 }
@@ -318,6 +312,21 @@ def get_option_class(otype):
     return cls
 
 
+def option_from_wire_parser(otype, parser):
+    """Build an EDNS option object from wire format.
+
+    *otype*, an ``int``, is the option type.
+
+    *parser*, a ``dns.wire.Parser``, the parser, which should be
+    restricted to the option length.
+
+    Returns an instance of a subclass of ``dns.edns.Option``.
+    """
+    cls = get_option_class(otype)
+    otype = OptionType.make(otype)
+    return cls.from_wire_parser(otype, parser)
+
+
 def option_from_wire(otype, wire, current, olen):
     """Build an EDNS option object from wire format.
 
@@ -332,7 +341,6 @@ def option_from_wire(otype, wire, current, olen):
 
     Returns an instance of a subclass of ``dns.edns.Option``.
     """
-
-    cls = get_option_class(otype)
-    otype = OptionType.make(otype)
-    return cls.from_wire(otype, wire, current, olen)
+    parser = dns.wire.Parser(wire, current)
+    with parser.restrict_to(olen):
+        return option_from_wire_parser(otype, parser)
index fdaec026321e8b260777be741ddffd0243d3b3c3..4b322128a0347c1d62e6d14086ed167fc93c1341 100644 (file)
@@ -19,9 +19,9 @@
 
 import contextlib
 import io
-import struct
 import time
 
+import dns.wire
 import dns.edns
 import dns.enum
 import dns.exception
@@ -36,7 +36,6 @@ import dns.rdatatype
 import dns.rrset
 import dns.renderer
 import dns.tsig
-import dns.wiredata
 import dns.rdtypes.ANY.OPT
 import dns.rdtypes.ANY.TSIG
 
@@ -727,11 +726,8 @@ class _WireReader:
 
     """Wire format reader.
 
-    wire: a binary, is the wire-format message.
+    parser: the binary parser
     message: The message object being built
-    current: When building a message object from wire format, this
-    variable contains the offset from the beginning of wire of the next octet
-    to be read.
     initialize_message: Callback to set message parsing options
     question_only: Are we only reading the question?
     one_rr_per_rrset: Put each RR into its own RRset?
@@ -744,9 +740,8 @@ class _WireReader:
     def __init__(self, wire, initialize_message, question_only=False,
                  one_rr_per_rrset=False, ignore_trailing=False,
                  keyring=None, multi=False):
-        self.wire = dns.wiredata.maybe_wrap(wire)
+        self.parser = dns.wire.Parser(wire)
         self.message = None
-        self.current = 0
         self.initialize_message = initialize_message
         self.question_only = question_only
         self.one_rr_per_rrset = one_rr_per_rrset
@@ -761,14 +756,8 @@ class _WireReader:
 
         section = self.message.sections[section_number]
         for i in range(qcount):
-            (qname, used) = dns.name.from_wire(self.wire, self.current)
-            if self.message.origin is not None:
-                qname = qname.relativize(self.message.origin)
-            self.current += used
-            (rdtype, rdclass) = \
-                struct.unpack('!HH',
-                              self.wire[self.current:self.current + 4])
-            self.current += 4
+            qname = self.parser.get_name(self.message.origin)
+            (rdtype, rdclass) = self.parser.get_struct('!HH')
             (rdclass, rdtype, _, _) = \
                 self.message._parse_rr_header(section_number, qname, rdclass,
                                               rdtype)
@@ -786,16 +775,13 @@ class _WireReader:
         section = self.message.sections[section_number]
         force_unique = self.one_rr_per_rrset
         for i in range(count):
-            rr_start = self.current
-            (name, used) = dns.name.from_wire(self.wire, self.current)
-            absolute_name = name
+            rr_start = self.parser.current
+            absolute_name = self.parser.get_name()
             if self.message.origin is not None:
-                name = name.relativize(self.message.origin)
-            self.current += used
-            (rdtype, rdclass, ttl, rdlen) = \
-                struct.unpack('!HHIH',
-                              self.wire[self.current:self.current + 10])
-            self.current += 10
+                name = absolute_name.relativize(self.message.origin)
+            else:
+                name = absolute_name
+            (rdtype, rdclass, ttl, rdlen) = self.parser.get_struct('!HHIH')
             if rdtype in (dns.rdatatype.OPT, dns.rdatatype.TSIG):
                 (rdclass, rdtype, deleting, empty) = \
                     self.message._parse_special_rr_header(section_number,
@@ -811,9 +797,10 @@ class _WireReader:
                 rd = None
                 covers = dns.rdatatype.NONE
             else:
-                rd = dns.rdata.from_wire(rdclass, rdtype,
-                                         self.wire, self.current, rdlen,
-                                         self.message.origin)
+                with self.parser.restrict_to(rdlen):
+                    rd = dns.rdata.from_wire_parser(rdclass, rdtype,
+                                                    self.parser,
+                                                    self.message.origin)
                 covers = rd.covers()
             if self.message.xfr and rdtype == dns.rdatatype.SOA:
                 force_unique = True
@@ -832,7 +819,7 @@ class _WireReader:
                     raise UnknownTSIGKey("key '%s' unknown" % name)
                 self.message.keyring = key
                 self.message.tsig_ctx = \
-                    dns.tsig.validate(self.wire,
+                    dns.tsig.validate(self.parser.wire,
                                       key,
                                       absolute_name,
                                       rd,
@@ -851,18 +838,15 @@ class _WireReader:
                     if ttl > 0x7fffffff:
                         ttl = 0
                     rrset.add(rd, ttl)
-            self.current += rdlen
 
     def read(self):
         """Read a wire format DNS message and build a dns.message.Message
         object."""
 
-        l = len(self.wire)
-        if l < 12:
+        if self.parser.remaining() < 12:
             raise ShortHeader
         (id, flags, qcount, ancount, aucount, adcount) = \
-            struct.unpack('!HHHHHH', self.wire[:12])
-        self.current = 12
+            self.parser.get_struct('!HHHHHH')
         factory = _message_factory_from_opcode(dns.opcode.from_flags(flags))
         self.message = factory(id=id)
         self.message.flags = flags
@@ -875,10 +859,10 @@ class _WireReader:
         self._get_section(MessageSection.ANSWER, ancount)
         self._get_section(MessageSection.AUTHORITY, aucount)
         self._get_section(MessageSection.ADDITIONAL, adcount)
-        if not self.ignore_trailing and self.current != l:
+        if not self.ignore_trailing and self.parser.remaining() != 0:
             raise TrailingJunk
         if self.multi and self.message.tsig_ctx and not self.message.had_tsig:
-            self.message.tsig_ctx.update(self.wire)
+            self.message.tsig_ctx.update(self.parser.wire)
         return self.message
 
 
index ee87c752a31af06b65a12e028ec2e2529bb01f59..477d0b742fffc870d973596be4b8508f17b603fa 100644 (file)
@@ -28,8 +28,8 @@ try:
 except ImportError:  # pragma: no cover
     have_idna_2008 = False
 
+import dns.wire
 import dns.exception
-import dns.wiredata
 
 # fullcompare() result values
 
@@ -966,6 +966,39 @@ def from_text(text, origin=root, idna_codec=None):
     return Name(labels)
 
 
+def from_wire_parser(parser):
+    """Convert possibly compressed wire format into a Name.
+
+    *parser* is a dns.wire.Parser.
+
+    Raises ``dns.name.BadPointer`` if a compression pointer did not
+    point backwards in the message.
+
+    Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
+
+    Returns a ``dns.name.Name``
+    """
+
+    labels = []
+    biggest_pointer = parser.current
+    with parser.restore_furthest():
+        count = parser.get_uint8()
+        while count != 0:
+            if count < 64:
+                labels.append(parser.get_bytes(count))
+            elif count >= 192:
+                current = (count & 0x3f) * 256 + parser.get_uint8()
+                if current >= biggest_pointer:
+                    raise BadPointer
+                biggest_pointer = current
+                parser.seek(current)
+            else:
+                raise BadLabelType
+            count = parser.get_uint8()
+        labels.append(b'')
+    return Name(labels)
+
+
 def from_wire(message, current):
     """Convert possibly compressed wire format into a Name.
 
@@ -987,32 +1020,6 @@ def from_wire(message, current):
 
     if not isinstance(message, bytes):
         raise ValueError("input to from_wire() must be a byte string")
-    message = dns.wiredata.maybe_wrap(message)
-    labels = []
-    biggest_pointer = current
-    hops = 0
-    count = message[current]
-    current += 1
-    cused = 1
-    while count != 0:
-        if count < 64:
-            labels.append(message[current: current + count].unwrap())
-            current += count
-            if hops == 0:
-                cused += count
-        elif count >= 192:
-            current = (count & 0x3f) * 256 + message[current]
-            if hops == 0:
-                cused += 1
-            if current >= biggest_pointer:
-                raise BadPointer
-            biggest_pointer = current
-            hops += 1
-        else:
-            raise BadLabelType
-        count = message[current]
-        current += 1
-        if hops == 0:
-            cused += 1
-    labels.append('')
-    return (Name(labels), cused)
+    parser = dns.wire.Parser(message, current)
+    name = from_wire_parser(parser)
+    return (name, parser.current - current)
index 2de1763a4d703dba7742a7436a6cb324d113a8dd..64d20245d8c3c12ae9dd5a71808a9c1841c7abc6 100644 (file)
@@ -24,12 +24,12 @@ import io
 import inspect
 import itertools
 
+import dns.wire
 import dns.exception
 import dns.name
 import dns.rdataclass
 import dns.rdatatype
 import dns.tokenizer
-import dns.wiredata
 
 _hex_chunksize = 32
 
@@ -377,8 +377,8 @@ class GenericRdata(Rdata):
         file.write(self.data)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        return cls(rdclass, rdtype, wire[current: current + rdlen])
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        return cls(rdclass, rdtype, parser.get_remaining())
 
 _rdata_classes = {}
 _module_prefix = 'dns.rdtypes'
@@ -474,6 +474,36 @@ def from_text(rdclass, rdtype, tok, origin=None, relativize=True,
                          relativize_to)
 
 
+def from_wire_parser(rdclass, rdtype, parser, origin=None):
+    """Build an rdata object from wire format
+
+    This function attempts to dynamically load a class which
+    implements the specified rdata class and type.  If there is no
+    class-and-type-specific implementation, the GenericRdata class
+    is used.
+
+    Once a class is chosen, its from_wire() class method is called
+    with the parameters to this function.
+
+    *rdclass*, an ``int``, the rdataclass.
+
+    *rdtype*, an ``int``, the rdatatype.
+
+    *parser*, a ``dns.wire.Parser``, the parser, which should be
+    restricted to the rdata length.
+
+    *origin*, a ``dns.name.Name`` (or ``None``).  If not ``None``,
+    then names will be relativized to this origin.
+
+    Returns an instance of the chosen Rdata subclass.
+    """
+
+    rdclass = dns.rdataclass.RdataClass.make(rdclass)
+    rdtype = dns.rdatatype.RdataType.make(rdtype)
+    cls = get_rdata_class(rdclass, rdtype)
+    return cls.from_wire_parser(rdclass, rdtype, parser, origin)
+
+
 def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None):
     """Build an rdata object from wire format
 
@@ -501,12 +531,9 @@ def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None):
 
     Returns an instance of the chosen Rdata subclass.
     """
-
-    wire = dns.wiredata.maybe_wrap(wire)
-    rdclass = dns.rdataclass.RdataClass.make(rdclass)
-    rdtype = dns.rdatatype.RdataType.make(rdtype)
-    cls = get_rdata_class(rdclass, rdtype)
-    return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
+    parser = dns.wire.Parser(wire, current)
+    with parser.restrict_to(rdlen):
+        return from_wire_parser(rdclass, rdtype, parser, origin)
 
 
 class RdatatypeExists(dns.exception.DNSException):
index 8fb18c19d187c5135e4a6971a099f7265a5d3c53..4e012a271ce2fe4871b4c864d261870d1e63c39c 100644 (file)
@@ -70,18 +70,10 @@ class AMTRELAY(dns.rdata.Rdata):
                                                    canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        if rdlen < 2:
-            raise dns.exception.FormError
-        (precedence, relay_type) = struct.unpack('!BB',
-                                                 wire[current: current + 2])
-        current += 2
-        rdlen -= 2
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (precedence, relay_type) = parser.get_struct('!BB')
         discovery_optional = bool(relay_type >> 7)
         relay_type &= 0x7f
-        (relay, cused) = Relay(relay_type).from_wire(wire, current, rdlen,
-                                                     origin)
-        current += cused
-        rdlen -= cused
+        relay = Relay(relay_type).from_wire_parser(parser, origin)
         return cls(rdclass, rdtype, precedence, discovery_optional, relay_type,
                    relay)
index d498107e03bec89d0d0a6eb48beffbc63762fb4b..b7edae8768cb03626301c654ab66e44613eef992 100644 (file)
@@ -62,9 +62,8 @@ class CAA(dns.rdata.Rdata):
         file.write(self.value)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (flags, l) = struct.unpack('!BB', wire[current: current + 2])
-        current += 2
-        tag = wire[current: current + l]
-        value = wire[current + l:current + rdlen - 2]
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        flags = parser.get_uint8()
+        tag = parser.get_counted_bytes()
+        value = parser.get_remaining()
         return cls(rdclass, rdtype, flags, tag, value)
index 98f8b67c7265a9011a91bc255f3ccdad81b574d0..62df241ced1ca21e472ebfcb5360d22e36e8d85c 100644 (file)
@@ -96,13 +96,8 @@ class CERT(dns.rdata.Rdata):
         file.write(self.certificate)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        prefix = wire[current: current + 5].unwrap()
-        current += 5
-        rdlen -= 5
-        if rdlen < 0:
-            raise dns.exception.FormError
-        (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix)
-        certificate = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (certificate_type, key_tag, algorithm) = parser.get_struct("!HHB")
+        certificate = parser.get_remaining()
         return cls(rdclass, rdtype, certificate_type, key_tag, algorithm,
                    certificate)
index 66f2f31b34fd2cca7be1a076d3dbca9a8a42430d..c62dad8a90429779e9b172e142ab6e277976b226 100644 (file)
@@ -93,26 +93,14 @@ class CSYNC(dns.rdata.Rdata):
             file.write(bitmap)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        if rdlen < 6:
-            raise dns.exception.FormError("CSYNC too short")
-        (serial, flags) = struct.unpack("!IH", wire[current: current + 6])
-        current += 6
-        rdlen -= 6
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (serial, flags) = parser.get_struct("!IH")
         windows = []
-        while rdlen > 0:
-            if rdlen < 3:
-                raise dns.exception.FormError("CSYNC too short")
-            window = wire[current]
-            octets = wire[current + 1]
+        while parser.remaining() > 0:
+            window = parser.get_uint8()
+            octets = parser.get_uint8()
             if octets == 0 or octets > 32:
                 raise dns.exception.FormError("bad CSYNC octets")
-            current += 2
-            rdlen -= 2
-            if rdlen < octets:
-                raise dns.exception.FormError("bad CSYNC bitmap length")
-            bitmap = bytearray(wire[current: current + octets].unwrap())
-            current += octets
-            rdlen -= octets
+            bitmap = parser.get_bytes(octets)
             windows.append((window, bitmap))
         return cls(rdclass, rdtype, serial, flags, windows)
index e8b69eedd3c3299cd722073e73b0f6e4e565e980..03677fd22cdd10256706365850fec26969b14bc3 100644 (file)
@@ -111,29 +111,10 @@ class GPOS(dns.rdata.Rdata):
         file.write(self.altitude)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l > rdlen:
-            raise dns.exception.FormError
-        latitude = wire[current: current + l].unwrap()
-        current += l
-        rdlen -= l
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l > rdlen:
-            raise dns.exception.FormError
-        longitude = wire[current: current + l].unwrap()
-        current += l
-        rdlen -= l
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l != rdlen:
-            raise dns.exception.FormError
-        altitude = wire[current: current + l].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        latitude = parser.get_counted_bytes()
+        longitude = parser.get_counted_bytes()
+        altitude = parser.get_counted_bytes()
         return cls(rdclass, rdtype, latitude, longitude, altitude)
 
     @property
index dc982f11c9aa749f96879b6e9de481c75b90b96d..587e0ad169249c6b2da66c3e7f30ec9f9e1b9e74 100644 (file)
@@ -64,19 +64,7 @@ class HINFO(dns.rdata.Rdata):
         file.write(self.os)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l > rdlen:
-            raise dns.exception.FormError
-        cpu = wire[current:current + l].unwrap()
-        current += l
-        rdlen -= l
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l != rdlen:
-            raise dns.exception.FormError
-        os = wire[current: current + l].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        cpu = parser.get_counted_bytes()
+        os = parser.get_counted_bytes()
         return cls(rdclass, rdtype, cpu, os)
index 51e42ac9bbd12014ab9b46c3a46b54df4134471e..1c774bbff4eab998e5b99baee67f99272e514e84 100644 (file)
@@ -77,24 +77,12 @@ class HIP(dns.rdata.Rdata):
             server.to_wire(file, None, origin, False)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (lh, algorithm, lk) = struct.unpack('!BBH',
-                                            wire[current: current + 4])
-        current += 4
-        rdlen -= 4
-        hit = wire[current: current + lh].unwrap()
-        current += lh
-        rdlen -= lh
-        key = wire[current: current + lk].unwrap()
-        current += lk
-        rdlen -= lk
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (lh, algorithm, lk) = parser.get_struct('!BBH')
+        hit = parser.get_bytes(lh)
+        key = parser.get_bytes(lk)
         servers = []
-        while rdlen > 0:
-            (server, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                                 current)
-            current += cused
-            rdlen -= cused
-            if origin is not None:
-                server = server.relativize(origin)
+        while parser.remaining() > 0:
+            server = parser.get_name(origin)
             servers.append(server)
         return cls(rdclass, rdtype, hit, algorithm, key, servers)
index 37332321e11aea96b75c59a3043926adb3b46462..6834b3c7c964ddcf474e9517dd0b536862c5383c 100644 (file)
@@ -74,22 +74,10 @@ class ISDN(dns.rdata.Rdata):
             file.write(self.subaddress)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l > rdlen:
-            raise dns.exception.FormError
-        address = wire[current: current + l].unwrap()
-        current += l
-        rdlen -= l
-        if rdlen > 0:
-            l = wire[current]
-            current += 1
-            rdlen -= 1
-            if l != rdlen:
-                raise dns.exception.FormError
-            subaddress = wire[current: current + l].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_counted_bytes()
+        if parser.remaining() > 0:
+            subaddress = parser.get_counted_bytes()
         else:
-            subaddress = ''
+            subaddress = b''
         return cls(rdclass, rdtype, address, subaddress)
index 31065bbabe6a71d2901ce179d16ae780de2ec906..eb00a1cd03dcde76be58b7b653a20eb3c6c8ceaf 100644 (file)
@@ -293,9 +293,9 @@ class LOC(dns.rdata.Rdata):
         file.write(wire)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
         (version, size, hprec, vprec, latitude, longitude, altitude) = \
-            struct.unpack("!BBBBIII", wire[current: current + rdlen])
+            parser.get_struct("!BBBBIII")
         if latitude < _MIN_LATITUDE or latitude > _MAX_LATITUDE:
             raise dns.exception.FormError("bad latitude")
         if latitude > 0x80000000:
index 0f79d94a48a7411d4c9b2dc4a77f5a586407926a..8c1da5aec6ee7a559184e1c7c26321cbeaf11abd 100644 (file)
@@ -93,26 +93,13 @@ class NSEC(dns.rdata.Rdata):
             file.write(bitmap)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
-        current += cused
-        rdlen -= cused
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        next = parser.get_name(origin)
         windows = []
-        while rdlen > 0:
-            if rdlen < 3:
-                raise dns.exception.FormError("NSEC too short")
-            window = wire[current]
-            octets = wire[current + 1]
-            if octets == 0 or octets > 32:
+        while parser.remaining() > 0:
+            window = parser.get_uint8()
+            bitmap = parser.get_counted_bytes()
+            if len(bitmap) == 0 or len(bitmap) > 32:
                 raise dns.exception.FormError("bad NSEC octets")
-            current += 2
-            rdlen -= 2
-            if rdlen < octets:
-                raise dns.exception.FormError("bad NSEC bitmap length")
-            bitmap = bytearray(wire[current: current + octets].unwrap())
-            current += octets
-            rdlen -= octets
             windows.append((window, bitmap))
-        if origin is not None:
-            next = next.relativize(origin)
         return cls(rdclass, rdtype, next, windows)
index 208f9e8c93d821ec53611be1c866c706dd3dd272..32dfe3e08442155c162b8f6000a209d3e83fd117 100644 (file)
@@ -138,36 +138,16 @@ class NSEC3(dns.rdata.Rdata):
             file.write(bitmap)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (algorithm, flags, iterations, slen) = \
-            struct.unpack('!BBHB', wire[current: current + 5])
-
-        current += 5
-        rdlen -= 5
-        salt = wire[current: current + slen].unwrap()
-        current += slen
-        rdlen -= slen
-        nlen = wire[current]
-        current += 1
-        rdlen -= 1
-        next = wire[current: current + nlen].unwrap()
-        current += nlen
-        rdlen -= nlen
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (algorithm, flags, iterations) = parser.get_struct('!BBH')
+        salt = parser.get_counted_bytes()
+        next = parser.get_counted_bytes()
         windows = []
-        while rdlen > 0:
-            if rdlen < 3:
-                raise dns.exception.FormError("NSEC3 too short")
-            window = wire[current]
-            octets = wire[current + 1]
-            if octets == 0 or octets > 32:
+        while parser.remaining() > 0:
+            window = parser.get_uint8()
+            bitmap = parser.get_counted_bytes()
+            if len(bitmap) == 0 or len(bitmap) > 32:
                 raise dns.exception.FormError("bad NSEC3 octets")
-            current += 2
-            rdlen -= 2
-            if rdlen < octets:
-                raise dns.exception.FormError("bad NSEC3 bitmap length")
-            bitmap = bytearray(wire[current: current + octets].unwrap())
-            current += octets
-            rdlen -= octets
             windows.append((window, bitmap))
         return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next,
                    windows)
index d10321b3417865f86f13c3e8ae12a5eefc1eb372..8ac76271df32ffef5ff420423298e4de5a38c384 100644 (file)
@@ -67,15 +67,7 @@ class NSEC3PARAM(dns.rdata.Rdata):
         file.write(self.salt)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (algorithm, flags, iterations, slen) = \
-             struct.unpack('!BBHB',
-                           wire[current: current + 5])
-        current += 5
-        rdlen -= 5
-        salt = wire[current: current + slen].unwrap()
-        current += slen
-        rdlen -= slen
-        if rdlen != 0:
-            raise dns.exception.FormError
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (algorithm, flags, iterations) = parser.get_struct('!BBH')
+        salt = parser.get_counted_bytes()
         return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
index eebe2a98792b335cab5288c9ba9586f10d7b9fdd..f632132e29827ccacfc9e7c9179d66aeed8a6702 100644 (file)
@@ -45,6 +45,6 @@ class OPENPGPKEY(dns.rdata.Rdata):
         file.write(self.key)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        key = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        key = parser.get_remaining()
         return cls(rdclass, rdtype, key)
index 0a0e7afe1a9e3c50814f8874eca3f0ed13055bc0..c48aa12faa8a10e131ed639b6e658eb119e3aea4 100644 (file)
@@ -52,19 +52,12 @@ class OPT(dns.rdata.Rdata):
         return ' '.join(opt.to_text() for opt in self.options)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
         options = []
-        while rdlen > 0:
-            if rdlen < 4:
-                raise dns.exception.FormError
-            (otype, olen) = struct.unpack('!HH', wire[current:current + 4])
-            current += 4
-            rdlen -= 4
-            if olen > rdlen:
-                raise dns.exception.FormError
-            opt = dns.edns.option_from_wire(otype, wire, current, olen)
-            current += olen
-            rdlen -= olen
+        while parser.remaining() > 0:
+            (otype, olen) = parser.get_struct('!HH')
+            with parser.restrict_to(olen):
+                opt = dns.edns.option_from_wire_parser(otype, parser)
             options.append(opt)
         return cls(rdclass, rdtype, options)
 
index fa3aaac91c74b4c586f8c18340b8ae0d3886efbb..7446de6deef175531f88e3bdef3bd93936a67f5e 100644 (file)
@@ -51,18 +51,7 @@ class RP(dns.rdata.Rdata):
         self.txt.to_wire(file, None, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (mbox, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                           current)
-        current += cused
-        rdlen -= cused
-        if rdlen <= 0:
-            raise dns.exception.FormError
-        (txt, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                          current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            mbox = mbox.relativize(origin)
-            txt = txt.relativize(origin)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        mbox = parser.get_name(origin)
+        txt = parser.get_name(origin)
         return cls(rdclass, rdtype, mbox, txt)
index ddc6141c2477ba984d34df5079b249c74fc03ae6..2077d905fb5c871b820e9a26bdde9f13229dcdba 100644 (file)
@@ -115,16 +115,8 @@ class RRSIG(dns.rdata.Rdata):
         file.write(self.signature)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        header = struct.unpack('!HBBIIIH', wire[current: current + 18])
-        current += 18
-        rdlen -= 18
-        (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current)
-        current += cused
-        rdlen -= cused
-        if origin is not None:
-            signer = signer.relativize(origin)
-        signature = wire[current: current + rdlen].unwrap()
-        return cls(rdclass, rdtype, header[0], header[1], header[2],
-                   header[3], header[4], header[5], header[6], signer,
-                   signature)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct('!HBBIIIH')
+        signer = parser.get_name(origin)
+        signature = parser.get_remaining()
+        return cls(rdclass, rdtype, *header, signer, signature)
index cd59ec0d2aa8b06e13499e7bf2b9f6d8dfd2adc3..e93274ed729167442201f8ccc3f54471c13be886 100644 (file)
@@ -71,20 +71,7 @@ class SOA(dns.rdata.Rdata):
         file.write(five_ints)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
-        current += cused
-        rdlen -= cused
-        (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
-        current += cused
-        rdlen -= cused
-        if rdlen != 20:
-            raise dns.exception.FormError
-        five_ints = struct.unpack('!IIIII',
-                                  wire[current: current + rdlen])
-        if origin is not None:
-            mname = mname.relativize(origin)
-            rname = rname.relativize(origin)
-        return cls(rdclass, rdtype, mname, rname,
-                   five_ints[0], five_ints[1], five_ints[2], five_ints[3],
-                   five_ints[4])
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        mname = parser.get_name(origin)
+        rname = parser.get_name(origin)
+        return cls(rdclass, rdtype, mname, rname, *parser.get_struct('!IIIII'))
index 49a1239bc04ef839a0e9f390aec88dc56c383c46..a3cc00391e9be95e260a472e55e2896f6a9e039f 100644 (file)
@@ -58,9 +58,7 @@ class SSHFP(dns.rdata.Rdata):
         file.write(self.fingerprint)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        header = struct.unpack("!BB", wire[current: current + 2])
-        current += 2
-        rdlen -= 2
-        fingerprint = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("BB")
+        fingerprint = parser.get_remaining()
         return cls(rdclass, rdtype, header[0], header[1], fingerprint)
index 3ffaaca67efc70149f66ebe38c087d428a63964e..9c9c8662b8f823924d210b36ccdf02bfe419e9ab 100644 (file)
@@ -61,9 +61,7 @@ class TLSA(dns.rdata.Rdata):
         file.write(self.cert)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        header = struct.unpack("!BBB", wire[current: current + 3])
-        current += 3
-        rdlen -= 3
-        cert = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("BBB")
+        cert = parser.get_remaining()
         return cls(rdclass, rdtype, header[0], header[1], header[2], cert)
index 002c2dbc87a29bb2fc5804ff441dba7eef30f14a..0d4b48b6bda45ead9c7b5e30375e2db64ca44d3e 100644 (file)
@@ -80,33 +80,12 @@ class TSIG(dns.rdata.Rdata):
         file.write(self.other)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (algorithm, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                                current)
-        current += cused
-        rdlen -= cused
-        if rdlen < 10:
-            raise dns.exception.FormError
-        (time_hi, time_lo, fudge, mac_len) = \
-                struct.unpack('!HIHH', wire[current: current + 10])
-        current += 10
-        rdlen -= 10
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        algorithm = parser.get_name(origin)
+        (time_hi, time_lo, fudge) = parser.get_struct('!HIH')
         time_signed = (time_hi << 32) + time_lo
-        if rdlen < mac_len:
-            raise dns.exception.FormError
-        mac = wire[current: current + mac_len].unwrap()
-        current += mac_len
-        rdlen -= mac_len
-        if rdlen < 6:
-            raise dns.exception.FormError
-        (original_id, error, other_len) = \
-                struct.unpack('!HHH', wire[current: current + 6])
-        current += 6
-        rdlen -= 6
-        if rdlen < other_len:
-            raise dns.exception.FormError
-        other = wire[current: current + other_len].unwrap()
-        current += other_len
-        rdlen -= other_len
+        mac = parser.get_counted_bytes(2)
+        (original_id, error) = parser.get_struct('!HH')
+        other = parser.get_counted_bytes(2)
         return cls(rdclass, rdtype, algorithm, time_signed, fudge, mac,
                    original_id, error, other)
index 77f89645795d15f98acb41c78abde4a4a6516734..84296f52aee3ee096b388583e30fc1701b47883c 100644 (file)
@@ -63,14 +63,9 @@ class URI(dns.rdata.Rdata):
         file.write(self.target)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        if rdlen < 5:
-            raise dns.exception.FormError('URI RR is shorter than 5 octets')
-
-        (priority, weight) = struct.unpack('!HH', wire[current: current + 4])
-        current += 4
-        rdlen -= 4
-        target = wire[current: current + rdlen]
-        current += rdlen
-
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (priority, weight) = parser.get_struct('!HH')
+        target = parser.get_remaining()
+        if len(target) == 0:
+            raise dns.exception.FormError('URI target may not be empty')
         return cls(rdclass, rdtype, priority, weight, target)
index ac61849e1fffb7d11d7ca5df8965ea7d14e083b4..214f1dca4136cd68dae8a405101984731cd345f5 100644 (file)
@@ -54,11 +54,6 @@ class X25(dns.rdata.Rdata):
         file.write(self.address)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        l = wire[current]
-        current += 1
-        rdlen -= 1
-        if l != rdlen:
-            raise dns.exception.FormError
-        address = wire[current: current + l].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_counted_bytes()
         return cls(rdclass, rdtype, address)
index ca349320a9352b28a25d51b7e127d80b6b008bc2..b738ac6cb020970612dabb08c79c9c436827c4a6 100644 (file)
@@ -18,7 +18,7 @@
 import dns.rdtypes.mxbase
 import struct
 
-class A(dns.rdtypes.mxbase.MXBase):
+class A(dns.rdata.Rdata):
 
     """A record for Chaosnet"""
 
@@ -27,8 +27,8 @@ class A(dns.rdtypes.mxbase.MXBase):
 
     __slots__ = ['domain', 'address']
 
-    def __init__(self, rdclass, rdtype, address, domain):
-        super().__init__(rdclass, rdtype, address, domain)
+    def __init__(self, rdclass, rdtype, domain, address):
+        super().__init__(rdclass, rdtype)
         object.__setattr__(self, 'domain', domain)
         object.__setattr__(self, 'address', address)
 
@@ -42,7 +42,7 @@ class A(dns.rdtypes.mxbase.MXBase):
         domain = tok.get_name(origin, relativize, relativize_to)
         address = tok.get_uint16(base=8)
         tok.get_eol()
-        return cls(rdclass, rdtype, address, domain)
+        return cls(rdclass, rdtype, domain, address)
 
     def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
         self.domain.to_wire(file, compress, origin, canonicalize)
@@ -50,13 +50,7 @@ class A(dns.rdtypes.mxbase.MXBase):
         file.write(pref)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (domain, cused) = dns.name.from_wire(wire[:current + rdlen - 2],
-                                             current)
-        current += cused
-        (address,) = struct.unpack('!H', wire[current:current + 2])
-        if cused + 2 != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            domain = domain.relativize(origin)
-        return cls(rdclass, rdtype, address, domain)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        domain = parser.get_name(origin)
+        address = parser.get_uint16()
+        return cls(rdclass, rdtype, domain, address)
index 461ce9275e7a2dd4fe3e34ce6aa84b2f1bc1c562..8b71e329f62ed9be316578ee79393046d90ce077 100644 (file)
@@ -47,6 +47,6 @@ class A(dns.rdata.Rdata):
         file.write(dns.ipv4.inet_aton(self.address))
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        address = dns.ipv4.inet_ntoa(wire[current: current + rdlen])
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = dns.ipv4.inet_ntoa(parser.get_remaining())
         return cls(rdclass, rdtype, address)
index 9751f82cb5796023cd2e752260d2d108a50b4600..08f9d679489628e8a424ef99302fd79b38ae9408 100644 (file)
@@ -47,6 +47,6 @@ class AAAA(dns.rdata.Rdata):
         file.write(dns.ipv6.inet_aton(self.address))
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        address = dns.ipv6.inet_ntoa(wire[current: current + rdlen])
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = dns.ipv6.inet_ntoa(parser.get_remaining())
         return cls(rdclass, rdtype, address)
index 7149a6481503a6120e2fb4119a9ef6b04c667c6c..ab7fe4bca914d4dbbe490f3bea19702e1e906629 100644 (file)
@@ -111,26 +111,18 @@ class APL(dns.rdata.Rdata):
             item.to_wire(file)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
 
         items = []
-        while 1:
-            if rdlen == 0:
-                break
-            if rdlen < 4:
-                raise dns.exception.FormError
-            header = struct.unpack('!HBB', wire[current: current + 4])
+        while parser.remaining() > 0:
+            header = parser.get_struct('!HBB')
             afdlen = header[2]
             if afdlen > 127:
                 negation = True
                 afdlen -= 128
             else:
                 negation = False
-            current += 4
-            rdlen -= 4
-            if rdlen < afdlen:
-                raise dns.exception.FormError
-            address = wire[current: current + afdlen].unwrap()
+            address = parser.get_bytes(afdlen)
             l = len(address)
             if header[0] == 1:
                 if l < 4:
@@ -146,8 +138,6 @@ class APL(dns.rdata.Rdata):
                 # seems better than throwing an exception
                 #
                 address = codecs.encode(address, 'hex_codec')
-            current += afdlen
-            rdlen -= afdlen
             item = APLItem(header[0], negation, address, header[1])
             items.append(item)
         return cls(rdclass, rdtype, items)
index da834500edc50302d6b1a7544270cb0eb68cde4e..6f66eb89363855067a3868b6378534a8190a1c3a 100644 (file)
@@ -46,6 +46,6 @@ class DHCID(dns.rdata.Rdata):
         file.write(self.data)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        data = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        data = parser.get_remaining()
         return cls(rdclass, rdtype, data)
index 6d0f42593aa9068e1e386ea3a553f2d535530add..182ad2cbb20e398fbd5c150afb5c4d69023e0766 100644 (file)
@@ -72,19 +72,10 @@ class IPSECKEY(dns.rdata.Rdata):
         file.write(self.key)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        if rdlen < 3:
-            raise dns.exception.FormError
-        header = struct.unpack('!BBB', wire[current: current + 3])
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct('!BBB')
         gateway_type = header[1]
-        current += 3
-        rdlen -= 3
-        (gateway, cused) = Gateway(gateway_type).from_wire(wire, current,
-                                                           rdlen, origin)
-        current += cused
-        rdlen -= cused
-        key = wire[current: current + rdlen].unwrap()
-        if origin is not None and gateway_type == 3:
-            gateway = gateway.relativize(origin)
+        gateway = Gateway(gateway_type).from_wire_parser(parser, origin)
+        key = parser.get_remaining()
         return cls(rdclass, rdtype, header[0], gateway_type, header[2],
                    gateway, key)
index d8f2958c680606e7e667373b8bdb638629dc32f4..48d43562511994ae5419ebcaaf2b72d91ae3a78b 100644 (file)
@@ -85,26 +85,12 @@ class NAPTR(dns.rdata.Rdata):
         self.replacement.to_wire(file, compress, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (order, preference) = struct.unpack('!HH', wire[current: current + 4])
-        current += 4
-        rdlen -= 4
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (order, preference) = parser.get_struct('!HH')
         strings = []
         for i in range(3):
-            l = wire[current]
-            current += 1
-            rdlen -= 1
-            if l > rdlen or rdlen < 0:
-                raise dns.exception.FormError
-            s = wire[current: current + l].unwrap()
-            current += l
-            rdlen -= l
+            s = parser.get_counted_bytes()
             strings.append(s)
-        (replacement, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                                  current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            replacement = replacement.relativize(origin)
+        replacement = parser.get_name(origin)
         return cls(rdclass, rdtype, order, preference, strings[0], strings[1],
                    strings[2], replacement)
index a42e5928782bf051d45a2c067c43ce57cda5d0b8..227465fadc2885ea235796e4266ee15b8f198975 100644 (file)
@@ -54,6 +54,6 @@ class NSAP(dns.rdata.Rdata):
         file.write(self.address)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        address = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_remaining()
         return cls(rdclass, rdtype, address)
index e8e0adacfa6670bd50744039b161aadabeb92db2..946d79f8c3441f7116f81df161198129abd6d110 100644 (file)
@@ -57,22 +57,8 @@ class PX(dns.rdata.Rdata):
         self.mapx400.to_wire(file, None, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (preference, ) = struct.unpack('!H', wire[current: current + 2])
-        current += 2
-        rdlen -= 2
-        (map822, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                             current)
-        if cused > rdlen:
-            raise dns.exception.FormError
-        current += cused
-        rdlen -= cused
-        if origin is not None:
-            map822 = map822.relativize(origin)
-        (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                              current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            mapx400 = mapx400.relativize(origin)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        map822 = parser.get_name(origin)
+        mapx400 = parser.get_name(origin)
         return cls(rdclass, rdtype, preference, map822, mapx400)
index 02603f24060975741b9bf4486bc07822bbe94429..485153f4358345b7be3d86e385ffb7a4bcc3aeae 100644 (file)
@@ -58,15 +58,7 @@ class SRV(dns.rdata.Rdata):
         self.target.to_wire(file, compress, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (priority, weight, port) = struct.unpack('!HHH',
-                                                 wire[current: current + 6])
-        current += 6
-        rdlen -= 6
-        (target, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                             current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            target = target.relativize(origin)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (priority, weight, port) = parser.get_struct('!HHH')
+        target = parser.get_name(origin)
         return cls(rdclass, rdtype, priority, weight, port, target)
index a56413581a5ff86be22f51d90cf26cd8ae140aaa..d66d85832bd3345d97f28e0df4fcb7e1b5168d51 100644 (file)
@@ -89,10 +89,8 @@ class WKS(dns.rdata.Rdata):
         file.write(self.bitmap)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        address = dns.ipv4.inet_ntoa(wire[current: current + 4])
-        protocol, = struct.unpack('!B', wire[current + 4: current + 5])
-        current += 5
-        rdlen -= 5
-        bitmap = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = dns.ipv4.inet_ntoa(parser.get_bytes(4))
+        protocol = parser.get_uint8()
+        bitmap = parser.get_remaining()
         return cls(rdclass, rdtype, address, protocol, bitmap)
index 31fa0ecfa5ced527278cf8af114258ef792e493e..0243d6f33709330af7c2046f5ded8585b944ec85 100644 (file)
@@ -67,12 +67,8 @@ class DNSKEYBase(dns.rdata.Rdata):
         file.write(self.key)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        if rdlen < 4:
-            raise dns.exception.FormError
-        header = struct.unpack('!HBB', wire[current: current + 4])
-        current += 4
-        rdlen -= 4
-        key = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct('!HBB')
+        key = parser.get_remaining()
         return cls(rdclass, rdtype, header[0], header[1], header[2],
                    key)
index 2588cf379a4ea407f0c390a825bec98eae833713..1b999cfdef079176f744cab8388dd0bb386799d3 100644 (file)
@@ -31,7 +31,7 @@ class DNSKEYBase(rdata.Rdata):
         ...
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+    def from_parser(cls, rdclass, rdtype, parser, origin=None):
         ...
 
     def flags_to_text_set(self) -> Set[str]:
index dec16f3fe0032ad968856b8f248a6faef2c9a226..d7850beec5e011fc189859ef5bbfc4139c4a2451 100644 (file)
@@ -61,9 +61,7 @@ class DSBase(dns.rdata.Rdata):
         file.write(self.digest)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        header = struct.unpack("!HBB", wire[current: current + 4])
-        current += 4
-        rdlen -= 4
-        digest = wire[current: current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!HBB")
+        digest = parser.get_remaining()
         return cls(rdclass, rdtype, header[0], header[1], header[2], digest)
index 7756538c5c3541bed69750de401e539f3c612ff9..c1677a81d8b9a524b5caabb0f329b9435ee01ba4 100644 (file)
@@ -63,6 +63,6 @@ class EUIBase(dns.rdata.Rdata):
         file.write(self.eui)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        eui = wire[current:current + rdlen].unwrap()
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        eui = parser.get_bytes(cls.byte_len)
         return cls(rdclass, rdtype, eui)
index 63fd86979f5c3af4aed90025e4276e001205b323..d6a6efed5936df19bdccf6e6856832f7267845c3 100644 (file)
@@ -53,16 +53,9 @@ class MXBase(dns.rdata.Rdata):
         self.exchange.to_wire(file, compress, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (preference, ) = struct.unpack('!H', wire[current: current + 2])
-        current += 2
-        rdlen -= 2
-        (exchange, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                               current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            exchange = exchange.relativize(origin)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        exchange = parser.get_name(origin)
         return cls(rdclass, rdtype, preference, exchange)
 
 
index 28daca20fc2d2e4538e7d3df95d94f74b8e79475..93d3ee53659c08d0b745633f1c700538fdb51025 100644 (file)
@@ -47,13 +47,8 @@ class NSBase(dns.rdata.Rdata):
         self.target.to_wire(file, compress, origin, canonicalize)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
-        (target, cused) = dns.name.from_wire(wire[: current + rdlen],
-                                             current)
-        if cused != rdlen:
-            raise dns.exception.FormError
-        if origin is not None:
-            target = target.relativize(origin)
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        target = parser.get_name(origin)
         return cls(rdclass, rdtype, target)
 
 
index ec061a185022d77aeef1fd28d4c48cf1205f7d56..ad0093daa39a1e695a2ebb94894207fd5300cfb2 100644 (file)
@@ -84,16 +84,9 @@ class TXTBase(dns.rdata.Rdata):
             file.write(s)
 
     @classmethod
-    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
         strings = []
-        while rdlen > 0:
-            l = wire[current]
-            current += 1
-            rdlen -= 1
-            if l > rdlen:
-                raise dns.exception.FormError
-            s = wire[current: current + l].unwrap()
-            current += l
-            rdlen -= l
+        while parser.remaining() > 0:
+            s = parser.get_counted_bytes()
             strings.append(s)
         return cls(rdclass, rdtype, strings)
index 5d1f6c92f92e8a61206b9dab9e0c44fdd8da4c8c..3dc636d06599d52be77260657890096596752d2d 100644 (file)
@@ -78,14 +78,14 @@ class Gateway:
         else:
             raise ValueError(self._invalid_type())
 
-    def from_wire(self, wire, current, rdlen, origin=None):
+    def from_wire_parser(self, parser, origin=None):
         if self.type == 0:
-            return (None, 0)
+            return None
         elif self.type == 1:
-            return (dns.ipv4.inet_ntoa(wire[current: current + 4]), 4)
+            return dns.ipv4.inet_ntoa(parser.get_bytes(4))
         elif self.type == 2:
-            return (dns.ipv6.inet_ntoa(wire[current: current + 16]), 16)
+            return dns.ipv6.inet_ntoa(parser.get_bytes(16))
         elif self.type == 3:
-            return dns.name.from_wire(wire[: current + rdlen], current)
+            return parser.get_name(origin)
         else:
             raise dns.exception.FormError(self._invalid_type())
diff --git a/dns/wire.py b/dns/wire.py
new file mode 100644 (file)
index 0000000..a314960
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import contextlib
+import struct
+
+import dns.exception
+import dns.name
+
+class Parser:
+    def __init__(self, wire, current=0):
+        self.wire = wire
+        self.current = 0
+        self.end = len(self.wire)
+        if current:
+            self.seek(current)
+        self.furthest = current
+
+    def remaining(self):
+        return self.end - self.current
+
+    def get_bytes(self, size):
+        if size > self.remaining():
+            raise dns.exception.FormError
+        output = self.wire[self.current:self.current + size]
+        self.current += size
+        self.furthest = max(self.furthest, self.current)
+        return output
+
+    def get_counted_bytes(self, length_size=1):
+        length = int.from_bytes(self.get_bytes(length_size), 'big')
+        return self.get_bytes(length)
+
+    def get_remaining(self):
+        return self.get_bytes(self.remaining())
+
+    def get_uint8(self):
+        return struct.unpack('!B', self.get_bytes(1))[0]
+
+    def get_uint16(self):
+        return struct.unpack('!H', self.get_bytes(2))[0]
+
+    def get_uint32(self):
+        return struct.unpack('!I', self.get_bytes(4))[0]
+
+    def get_struct(self, format):
+        return struct.unpack(format, self.get_bytes(struct.calcsize(format)))
+
+    def get_name(self, origin=None):
+        name = dns.name.from_wire_parser(self)
+        if origin:
+            name = name.relativize(origin)
+        return name
+
+    def seek(self, where):
+        # Note that seeking to the end is OK!  (If you try to read
+        # after such a seek, you'll get an exception as expected.)
+        if where < 0 or where > self.end:
+            raise dns.exception.FormError
+        self.current = where
+
+    @contextlib.contextmanager
+    def restrict_to(self, size):
+        if size > self.remaining():
+            raise dns.exception.FormError
+        saved_end = self.end
+        try:
+            self.end = self.current + size
+            yield
+            # We make this check here and not in the finally as we
+            # don't want to raise if we're already raising for some
+            # other reason.
+            if self.current != self.end:
+                raise dns.exception.FormError
+        finally:
+            self.end = saved_end
+
+    @contextlib.contextmanager
+    def restore_furthest(self):
+        try:
+            yield None
+        finally:
+            self.current = self.furthest
diff --git a/dns/wiredata.py b/dns/wiredata.py
deleted file mode 100644 (file)
index 51f12fc..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
-
-# Copyright (C) 2011,2017 Nominum, Inc.
-#
-# 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.
-
-"""DNS Wire Data Helper"""
-
-import dns.exception
-
-
-class WireData(bytes):
-    # WireData is a binary type with stricter slicing
-
-    def __getitem__(self, key):
-        try:
-            if isinstance(key, slice):
-                # make sure we are not going outside of valid ranges,
-                # do stricter control of boundaries than python does
-                # by default
-
-                for index in (key.start, key.stop):
-                    if index is None:
-                        continue
-                    elif abs(index) > len(self):
-                        raise dns.exception.FormError
-
-                return WireData(super().__getitem__(key))
-            return super().__getitem__(key)
-        except IndexError:
-            raise dns.exception.FormError
-
-    def unwrap(self):
-        return bytes(self)
-
-
-def maybe_wrap(wire):
-    if isinstance(wire, WireData):
-        return wire
-    elif isinstance(wire, bytes):
-        return WireData(wire)
-    elif isinstance(wire, str):
-        return WireData(wire.encode())
-    raise ValueError("unhandled type %s" % type(wire))
index 0ed38b760c176863143649a2c5710761d881f0a9..ca2389533d3d293550a2a70f634ce7618a628588 100644 (file)
@@ -23,6 +23,7 @@ import pickle
 import struct
 import unittest
 
+import dns.wire
 import dns.exception
 import dns.name
 import dns.rdata
@@ -401,13 +402,19 @@ class RdataTestCase(unittest.TestCase):
 
     def test_opt_short_lengths(self):
         def bad1():
-            opt = OPT.from_wire(4096, dns.rdatatype.OPT,
-                                binascii.unhexlify('f00102'), 0, 3)
+            parser = dns.wire.Parser(bytes.fromhex('f00102'))
+            opt = OPT.from_wire_parser(4096, dns.rdatatype.OPT, parser)
         self.assertRaises(dns.exception.FormError, bad1)
         def bad2():
-            opt = OPT.from_wire(4096, dns.rdatatype.OPT,
-                                binascii.unhexlify('f00100030000'), 0, 6)
+            parser = dns.wire.Parser(bytes.fromhex('f00100030000'))
+            opt = OPT.from_wire_parser(4096, dns.rdatatype.OPT, parser)
         self.assertRaises(dns.exception.FormError, bad2)
 
+    def test_from_wire_parser(self):
+        wire = bytes.fromhex('01020304')
+        rdata = dns.rdata.from_wire('in', 'a', wire, 0, 4)
+        print(rdata)
+        self.assertEqual(rdata, dns.rdata.from_text('in', 'a', '1.2.3.4'))
+
 if __name__ == '__main__':
     unittest.main()
index e8645820175d85853432ab6748838cb1ae124710..085272739776cf040d608ced4e6ccc718d23c5e0 100644 (file)
@@ -94,25 +94,19 @@ class RdtypeAnyEUI48TestCase(unittest.TestCase):
         '''Valid wire format.'''
         eui = b'\x01\x23\x45\x67\x89\xab'
         pad_len = 100
-        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
-        inst = dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN,
-                                                     dns.rdatatype.EUI48,
-                                                     wire,
-                                                     pad_len,
-                                                     len(eui))
+        wire = b'x' * pad_len + eui + b'y' * pad_len * 2
+        inst = dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI48,
+                                   wire, pad_len, len(eui))
         self.assertEqual(inst.eui, eui)
 
     def testFromWireLength(self):
         '''Valid wire format.'''
         eui = b'\x01\x23\x45\x67\x89'
         pad_len = 100
-        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        wire = b'x' * pad_len + eui + b'y' * pad_len * 2
         with self.assertRaises(dns.exception.FormError):
-            dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN,
-                                                  dns.rdatatype.EUI48,
-                                                  wire,
-                                                  pad_len,
-                                                  len(eui))
+            dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI48,
+                                wire, pad_len, len(eui))
 
 
 class RdtypeAnyEUI64TestCase(unittest.TestCase):
@@ -191,25 +185,19 @@ class RdtypeAnyEUI64TestCase(unittest.TestCase):
         '''Valid wire format.'''
         eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
         pad_len = 100
-        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
-        inst = dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN,
-                                                     dns.rdatatype.EUI64,
-                                                     wire,
-                                                     pad_len,
-                                                     len(eui))
+        wire = b'x' * pad_len + eui + b'y' * pad_len * 2
+        inst = dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI64,
+                                   wire, pad_len, len(eui))
         self.assertEqual(inst.eui, eui)
 
     def testFromWireLength(self):
         '''Valid wire format.'''
         eui = b'\x01\x23\x45\x67\x89'
         pad_len = 100
-        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        wire = b'x' * pad_len + eui + b'y' * pad_len * 2
         with self.assertRaises(dns.exception.FormError):
-            dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN,
-                                                  dns.rdatatype.EUI64,
-                                                  wire,
-                                                  pad_len,
-                                                  len(eui))
+            dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI64,
+                                wire, pad_len, len(eui))
 
 
 if __name__ == '__main__':
diff --git a/tests/test_wire.py b/tests/test_wire.py
new file mode 100644 (file)
index 0000000..ac4401c
--- /dev/null
@@ -0,0 +1,55 @@
+
+import unittest
+
+import dns.exception
+import dns.wire
+import dns.name
+
+
+class BinaryTestCase(unittest.TestCase):
+
+    def test_basic(self):
+        wire = bytes.fromhex('0102010203040102')
+        p = dns.wire.Parser(wire)
+        self.assertEqual(p.get_uint16(), 0x0102)
+        with p.restrict_to(5):
+            self.assertEqual(p.get_uint32(), 0x01020304)
+            self.assertEqual(p.get_uint8(), 0x01)
+            self.assertEqual(p.remaining(), 0)
+            with self.assertRaises(dns.exception.FormError):
+                p.get_uint16()
+        self.assertEqual(p.remaining(), 1)
+        self.assertEqual(p.get_uint8(), 0x02)
+        with self.assertRaises(dns.exception.FormError):
+            p.get_uint8()
+
+    def test_name(self):
+        # www.dnspython.org NS IN question
+        wire = b'\x03www\x09dnspython\x03org\x00\x00\x02\x00\x01'
+        expected = dns.name.from_text('www.dnspython.org')
+        p = dns.wire.Parser(wire)
+        self.assertEqual(p.get_name(), expected)
+        self.assertEqual(p.get_uint16(), 2)
+        self.assertEqual(p.get_uint16(), 1)
+        self.assertEqual(p.remaining(), 0)
+
+    def test_relativized_name(self):
+        # www.dnspython.org NS IN question
+        wire = b'\x03www\x09dnspython\x03org\x00\x00\x02\x00\x01'
+        origin = dns.name.from_text('dnspython.org')
+        expected = dns.name.from_text('www', None)
+        p = dns.wire.Parser(wire)
+        self.assertEqual(p.get_name(origin), expected)
+        self.assertEqual(p.remaining(), 4)
+
+    def test_compressed_name(self):
+        # www.dnspython.org NS IN question
+        wire = b'\x09dnspython\x03org\x00\x03www\xc0\x00'
+        expected1 = dns.name.from_text('dnspython.org')
+        expected2 = dns.name.from_text('www.dnspython.org')
+        p = dns.wire.Parser(wire)
+        self.assertEqual(p.get_name(), expected1)
+        self.assertEqual(p.get_name(), expected2)
+        self.assertEqual(p.remaining(), 0)
+        # verify the unseek()
+        self.assertEqual(p.current, len(wire))
diff --git a/tests/test_wiredata.py b/tests/test_wiredata.py
deleted file mode 100644 (file)
index 9274259..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright (C) 2016
-# Author: Martin Basti <martin.basti@gmail.com>
-#
-# 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.
-
-import unittest
-
-from dns.exception import FormError
-from dns.wiredata import WireData, maybe_wrap
-
-
-class WireDataSlicingTestCase(unittest.TestCase):
-
-    def testSliceAll(self):
-        """Get all data"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[:], WireData(b'0123456789'))
-
-    def testSliceAllExplicitlyDefined(self):
-        """Get all data"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[0:10], WireData(b'0123456789'))
-
-    def testSliceLowerHalf(self):
-        """Get lower half of data"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[:5], WireData(b'01234'))
-
-    def testSliceLowerHalfWithNegativeIndex(self):
-        """Get lower half of data"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[:-5], WireData(b'01234'))
-
-    def testSliceUpperHalf(self):
-        """Get upper half of data"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[5:], WireData(b'56789'))
-
-    def testSliceMiddle(self):
-        """Get data from middle"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[3:6], WireData(b'345'))
-
-    def testSliceMiddleWithNegativeIndex(self):
-        """Get data from middle"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[-6:-3], WireData(b'456'))
-
-    def testSliceMiddleWithMixedIndex(self):
-        """Get data from middle"""
-        inst = WireData(b'0123456789')
-        self.assertEqual(inst[-8:3], WireData(b'2'))
-        self.assertEqual(inst[5:-3], WireData(b'56'))
-
-    def testGetOne(self):
-        """Get data one by one item"""
-        data = b'0123456789'
-        inst = WireData(data)
-        for i, byte in enumerate(data):
-            self.assertEqual(inst[i], byte)
-        for i in range(-1, len(data) * -1, -1):
-            self.assertEqual(inst[i], data[i])
-
-    def testEmptySlice(self):
-        """Test empty slice"""
-        data = b'0123456789'
-        inst = WireData(data)
-        for i, byte in enumerate(data):
-            self.assertEqual(inst[i:i], b'')
-        for i in range(-1, len(data) * -1, -1):
-            self.assertEqual(inst[i:i], b'')
-        self.assertEqual(inst[-3:-6], b'')
-
-    def testSliceStartOutOfLowerBorder(self):
-        """Get data from out of lower border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[-11:]  # pylint: disable=pointless-statement
-
-    def testSliceStopOutOfLowerBorder(self):
-        """Get data from out of lower border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[:-11]  # pylint: disable=pointless-statement
-
-    def testSliceBothOutOfLowerBorder(self):
-        """Get data from out of lower border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[-12:-11]  # pylint: disable=pointless-statement
-
-    def testSliceStartOutOfUpperBorder(self):
-        """Get data from out of upper border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[11:]  # pylint: disable=pointless-statement
-
-    def testSliceStopOutOfUpperBorder(self):
-        """Get data from out of upper border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[:11]  # pylint: disable=pointless-statement
-
-    def testSliceBothOutOfUpperBorder(self):
-        """Get data from out of lower border"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[10:20]  # pylint: disable=pointless-statement
-
-    def testGetOneOutOfLowerBorder(self):
-        """Get item outside of range"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[-11]  # pylint: disable=pointless-statement
-
-    def testGetOneOutOfUpperBorder(self):
-        """Get item outside of range"""
-        inst = WireData(b'0123456789')
-        with self.assertRaises(FormError):
-            inst[10]  # pylint: disable=pointless-statement
-
-    def testIteration(self):
-        bval = b'0123'
-        inst = WireData(bval)
-        l = list(inst)
-        self.assertEqual(l, [x for x in bval])
-
-    def testBadWrap(self):
-        def bad():
-            w = maybe_wrap(123)
-        self.assertRaises(ValueError, bad)