Ticket #5675: edns-message-5675.OPTHeader-integration.patch

File edns-message-5675.OPTHeader-integration.patch, 13.3 KB (added by rwall, 3 years ago)

Start using _OPTHeader from source:branches/opt-record-5668-3

  • twisted/names/dns.py

    === modified file 'twisted/names/dns.py'
     
    20892089                                a particular operation (e.g., zone
    20902090                                transfer) for particular data.
    20912091
     2092    @ivar ednsVersion: Indicates the EDNS implementation level. Set to
     2093        C{None} to prevent any EDNS attributes and options being added
     2094        to the encoded byte string.
     2095
    20922096    @ivar queries: A L{list} of L{Query} instances.
    20932097
    20942098    @ivar answers: A L{list} of L{RRHeader} instances.
     
    21002104
    21012105    showAttributes = (
    21022106        'id', 'answer', 'opCode', 'auth', 'trunc',
    2103         'recDes', 'recAv', 'rCode',
     2107        'recDes', 'recAv', 'rCode', 'ednsVersion',
    21042108        'queries', 'answers', 'authority', 'additional')
    21052109
    21062110    compareAttributes = showAttributes
     
    21082112    def __init__(self, id=0, answer=0,
    21092113                 opCode=OP_QUERY, auth=0,
    21102114                 trunc=0, recDes=0,
    2111                  recAv=0, rCode=0,
    2112                  queries=None, answers=None, authority=None, additional=None, optRecords=None):
     2115                 recAv=0, rCode=0, ednsVersion=0,
     2116                 queries=None, answers=None, authority=None, additional=None):
    21132117        """
    21142118        All arguments are stored as attributes with the same names.
    21152119
     
    21242128        @type recDes: C{int}
    21252129        @type recAv: C{int}
    21262130        @type rCode: C{int}
     2131        @type ednsVersion: C{int} or C{None}
    21272132        @type queries: C{list} of L{Query}
    21282133        @type answers: C{list} of L{RRHeader}
    21292134        @type authority: C{list} of L{RRHeader}
     
    21452150        self.recDes = recDes
    21462151        self.recAv = recAv
    21472152        self.rCode = rCode
     2153        self.ednsVersion = ednsVersion
    21482154
    21492155        self.queries = queries or []
    21502156        self.answers = answers or []
    21512157        self.authority = authority or []
    21522158        self.additional = additional or []
    21532159
    2154         self.optRecords = optRecords or []
     2160        self._decodingErrors = []
    21552161
    21562162
    21572163    def toStr(self):
    21582164        """
    2159         Encode to a wire format.
     2165        Encode to wire format.
     2166
     2167        If C{ednsVersion} is not None, an L{_OPTHeader} instance
     2168        containing all the I{EDNS} specific attributes and options
     2169        will be appended to the list of C{additional} records and this
     2170        will be encoded into the byte string as an C{OPT} record byte
     2171        string.
    21602172
    21612173        @return: A L{bytes} string.
    21622174        """
     
    21772189        m.authority = self.authority
    21782190        m.additional = self.additional
    21792191
     2192        if self.ednsVersion is not None:
     2193            o = _OPTHeader(version=self.ednsVersion)
     2194            m.additional.append(o)
     2195
    21802196        return m.toStr()
    21812197
    21822198
     2199    @classmethod
     2200    def fromMessage(cls, message):
     2201        """
     2202        Construct and return a new L(_EDNSMessage} whose attributes
     2203        and records are derived from the attributes and records of
     2204        C{message} (a L{Message} instance)
     2205
     2206        If present, an I{OPT} record will be extracted from the
     2207        C{additional} section and its attributes and options will be
     2208        used to set the EDNS specific attributes C{extendedRCODE},
     2209        c{ednsVersion}, c{dnssecOK}, c{ednsOptions}.
     2210
     2211        The C{extendedRCODE} will be combined with C{message.rCode}
     2212        and assigned to C{self.rCode}.
     2213
     2214        If multiple I{OPT} records are found, this is considered an
     2215        error and no EDNS specific attributes will be
     2216        set. Additionally, an L{EFORMAT} error will be appended to
     2217        C{_decodingErrors}.
     2218        """
     2219        additional = []
     2220        optRecords = []
     2221        for r in message.additional:
     2222            if r.type == OPT:
     2223                optRecords.append(_OPTHeader.fromRRHeader(r))
     2224            else:
     2225                additional.append(r)
     2226
     2227        newMessage = cls(
     2228            id=message.id,
     2229            answer=message.answer,
     2230            opCode=message.opCode,
     2231            auth=message.auth,
     2232            trunc=message.trunc,
     2233            recDes=message.recDes,
     2234            recAv=message.recAv,
     2235            rCode=message.rCode,
     2236            # Default to None, it will be updated later when the OPT
     2237            # records are parsed.
     2238            ednsVersion=None,
     2239            queries=list(message.queries),
     2240            answers=list(message.answers),
     2241            authority=list(message.authority),
     2242            additional=additional,
     2243            )
     2244
     2245        if optRecords:
     2246            if len(optRecords) > 1:
     2247                newMessage._decodingErrors.append(EFORMAT)
     2248            else:
     2249                opt = optRecords[0]
     2250                newMessage.ednsVersion = opt.version
     2251
     2252        return newMessage
     2253
     2254
    21832255    def fromStr(self, bytes):
    21842256        """
    21852257        Decode from wire format, saving flags, values and records to
     
    21912263        m = Message()
    21922264        m.fromStr(bytes)
    21932265
    2194         optRecords = []
    2195         for r in reversed(m.additional):
    2196             if r.type == OPT:
    2197                 optRecords.append(r)
    2198                 m.additional.remove(r)
    2199 
    2200         self.id = m.id
    2201         self.answer = m.answer
    2202         self.opCode = m.opCode
    2203         self.auth = m.auth
    2204         self.trunc = m.trunc
    2205         self.recDes = m.recDes
    2206         self.recAv = m.recAv
    2207         self.rCode = m.rCode
    2208         self.queries = m.queries
    2209         self.answers = m.answers
    2210         self.authority = m.authority
    2211         self.additional = m.additional
    2212         self.optRecords = optRecords
     2266        ednsMessage = self.fromMessage(m)
     2267        self.__dict__ = ednsMessage.__dict__
    22132268
    22142269
    22152270
  • twisted/names/test/test_dns.py

    === modified file 'twisted/names/test/test_dns.py'
     
    18031803            opCode=dns.OP_STATUS,
    18041804            recDes=1,
    18051805            recAv=1,
    1806             rCode=15)
     1806            rCode=15,
     1807            ednsVersion=None,)
    18071808
    18081809
    18091810
     
    18321833            trunc=1,
    18331834            recDes=0,
    18341835            recAv=0,
    1835             rCode=0)
     1836            rCode=0,
     1837            ednsVersion=None,)
    18361838
    18371839
    18381840
     
    18631865        return dict(
    18641866            id=256,
    18651867            auth=0,
     1868            ednsVersion=None,
    18661869            answers=[
    18671870                dns.RRHeader(
    18681871                    b'',
     
    18981901        return dict(
    18991902            id=256,
    19001903            auth=1,
     1904            ednsVersion=None,
    19011905            answers=[
    19021906                dns.RRHeader(
    19031907                    b'',
     
    19681972            recDes=1,
    19691973            recAv=1,
    19701974            rCode=15,
     1975            ednsVersion=None,
    19711976            queries=[dns.Query(b'example.com', dns.SOA)],
    19721977            answers=[
    19731978                dns.RRHeader(
     
    20252030        b'\x00\x29' # TYPE (OPT 41)
    20262031        b'\x10\x00' # UDP Payload Size (4096)
    20272032        b'\x00' # Extended RCODE
    2028         b'\x00' # EDNS version
     2033        b'\x03' # EDNS version
    20292034        b'\x00\x00' # DO bit + Z
    20302035        b'\x00\x00' # RDLENGTH
    20312036        )
     
    20412046            recDes=0,
    20422047            recAv=0,
    20432048            rCode=0,
     2049            ednsVersion=3,
    20442050            queries=[dns.Query(b'www.example.com', dns.A)],
    2045             additional=[dns.RRHeader(
    2046                     b'',
    2047                     type=dns.OPT,
    2048                     cls=4096,
    2049                     payload=dns.UnknownRecord(b'', ttl=0))])
     2051            additional=[])
    20502052
    20512053
    20522054
     
    20552057    A version of L{dns.Message} which is comparable so that it can be
    20562058    tested using some of the L{dns._EDNSMessage} tests.
    20572059    """
    2058     showAttributes = compareAttributes = dns._EDNSMessage.compareAttributes
     2060    showAttributes = compareAttributes = (
     2061        'id', 'answer', 'opCode', 'auth', 'trunc',
     2062        'recDes', 'recAv', 'rCode',
     2063        'queries', 'answers', 'authority', 'additional')
    20592064
    20602065
    20612066
     
    21822187    """
    21832188    messageFactory = dns._EDNSMessage
    21842189
     2190    def test_ednsVersion(self):
     2191        """
     2192        L{dns._EDNSMessage.__init__} accepts an optional ednsVersion argument
     2193        whose default value is 0 and which is saved as a public
     2194        instance attribute.
     2195        """
     2196        self.assertEqual(self.messageFactory().ednsVersion, 0)
     2197        self.assertEqual(
     2198            self.messageFactory(ednsVersion=None).ednsVersion, None)
     2199
     2200
    21852201    def test_queries(self):
    21862202        """
    21872203        L{dns._EDNSMessage.__init__} accepts an optional queries argument
     
    22682284            'recDes=1 '
    22692285            'recAv=1 '
    22702286            'rCode=15 '
     2287            'ednsVersion=None '
    22712288            "queries=[Query('example.com', 6, 1)] "
    22722289            'answers=['
    22732290            '<RR name=example.com type=SOA class=IN ttl=4294967295s auth=True>'
     
    22812298            '>')
    22822299
    22832300
     2301    def test_fromMessage(self):
     2302        """
     2303        L{dns._EDNSMessage.fromMessage} constructs a new
     2304        L{dns._EDNSMessage} using the attributes and records from an
     2305        existing L{dns.Message} instance.
     2306        """
     2307        m = dns.Message(rCode=0xabcd)
     2308        m.queries = [dns.Query(b'www.example.com')]
     2309
     2310        ednsMessage = dns._EDNSMessage.fromMessage(m)
     2311        self.assertEqual(ednsMessage.rCode, 0xabcd)
     2312
    22842313
    22852314class EDNSMessageEqualityTests(ComparisonTestsMixin, unittest.SynchronousTestCase):
    22862315    """
     
    23882417            )
    23892418
    23902419
     2420    def test_ednsVersion(self):
     2421        """
     2422        Two L{dns._EDNSMessage} instances compare equal if they have the same
     2423        ednsVersion.
     2424        """
     2425        self.assertNormalEqualityImplementation(
     2426            self.messageFactory(ednsVersion=1),
     2427            self.messageFactory(ednsVersion=1),
     2428            self.messageFactory(ednsVersion=None),
     2429            )
     2430
     2431
    23912432    def test_queries(self):
    23922433        """
    23932434        Two L{dns._EDNSMessage} instances compare equal if they have the same
     
    26032644        L{dns._EDNSMessage}, L{dns.Message.__init__} does not accept
    26042645        queries, answers etc as arguments.
    26052646
     2647        Also removes any L{dns._EDNSMessage} specific arguments.
     2648
    26062649        @return: An L{dns.Message} instance.
    26072650        """
    26082651        queries = kwargs.pop('queries', [])
     
    26102653        authority = kwargs.pop('authority', [])
    26112654        additional = kwargs.pop('additional', [])
    26122655
     2656        kwargs.pop('ednsVersion', None)
     2657
    26132658        m = MessageComparable(*args, **kwargs)
    26142659        m.queries = queries
    26152660        m.answers = answers
     
    26392684        self.assertEqual(m.additional, [])
    26402685
    26412686
     2687    def test_ednsMessageDecodeMultipleOptRecords(self):
     2688        """
     2689        An L(_EDNSMessage} instance created from a byte string
     2690        containing multiple I{OPT} records will discard all the C{OPT}
     2691        records.
     2692
     2693        L{dns.EFORMAT} will be appended to C{_decodingErrors} list so
     2694        that a server responding to this message can respond with the
     2695        C{rCode = dns.EFORMAT}.
     2696
     2697        C{ednsVersion} will be set to C{None}.
     2698
     2699        "If a query message with more than one
     2700        OPT RR is received, a FORMERR (RCODE=1) MUST be returned."
     2701
     2702        RFC6891 does not say whether any OPT records should be
     2703        included in the response.
     2704
     2705        Querying ISC.ORG Bind servers with a multi OPT message,
     2706        results in a response message without any OPT records so lets
     2707        copy that behaviour.
     2708
     2709        @see: U{https://tools.ietf.org/html/rfc6891#section-6.1.1}
     2710        """
     2711        m = dns.Message()
     2712        m.additional = [
     2713            dns._OPTHeader(version=2),
     2714            dns._OPTHeader(version=3)]
     2715
     2716        ednsMessage = dns._EDNSMessage()
     2717        ednsMessage.fromStr(m.toStr())
     2718        self.assertEqual(ednsMessage._decodingErrors, [dns.EFORMAT])
     2719        self.assertEqual(ednsMessage.ednsVersion, None)
     2720
     2721
     2722    def test_optHeaderPosition(self):
     2723        """
     2724        L{dns._EDNSMessage} can decode OPT records, regardless of
     2725        their position in the additional records section.
     2726
     2727        "The OPT RR MAY be placed anywhere within the additional data
     2728        section."
     2729
     2730        @see: U{https://tools.ietf.org/html/rfc6891#section-6.1.1}
     2731        """
     2732        m = dns.Message()
     2733        m.additional = [dns.RRHeader(type=dns.OPT)]
     2734        self.assertEqual(dns._EDNSMessage.fromMessage(m)._decodingErrors, [])
     2735
     2736        m.additional.append(dns.RRHeader(type=dns.A))
     2737        self.assertEqual(dns._EDNSMessage.fromMessage(m)._decodingErrors, [])
     2738
     2739        m.additional.insert(0, dns.RRHeader(type=dns.A))
     2740        self.assertEqual(dns._EDNSMessage.fromMessage(m)._decodingErrors, [])
     2741
     2742
     2743    def test_ednsDecode(self):
     2744        """
     2745        The L(_EDNSMessage} instance created by
     2746        L{dns._EDNSMessage.fromStr} derives its edns specific values
     2747        (C{ednsVersion}, etc) from the supplied OPT record.
     2748        """
     2749        m = self.messageFactory()
     2750        m.fromStr(MESSAGE_EDNS_QUERY.bytes)
     2751
     2752        self.assertEqual(m, self.messageFactory(**MESSAGE_EDNS_QUERY.kwargs()))
     2753
     2754
     2755    def test_ednsEncode(self):
     2756        """
     2757        The L(_EDNSMessage} instance created by
     2758        L{dns._EDNSMessage.toStr} encodes its edns specific values
     2759        (C{ednsVersion}, etc) into an OPT record added to the
     2760        additional section.
     2761        """
     2762        self.assertEqual(
     2763            self.messageFactory(**MESSAGE_EDNS_QUERY.kwargs()).toStr(),
     2764            MESSAGE_EDNS_QUERY.bytes)
     2765
     2766
     2767def sendMessage():
     2768    p = dns.DNSDatagramProtocol(None)
     2769    p.startListening()
     2770    p.writeMessage(m, ('199.6.0.30', 53))
     2771
     2772
    26422773
    26432774class OPTHeaderTests(ComparisonTestsMixin, unittest.TestCase):
    26442775    """