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

File edns-message-5675.OPTHeader-integration.patch, 13.3 KB (added by rwall, 16 months 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    """