Ticket #5454: addEdsn0AndDnssec5454V04.patch

File addEdsn0AndDnssec5454V04.patch, 105.1 KB (added by BobNovas, 4 years ago)

addresses tjijs' comments

  • doc/names/examples/index.xhtml

     
    1515        <li><a href="testdns.py">testdns.py</a> - Prints the results of an Address record lookup, Mail-Exchanger record lookup, and Nameserver record lookup for the given hostname for a given hostname.</li>
    1616        <li><a href="dns-service.py">dns-service.py</a> - Searches for SRV records in DNS.</li>
    1717        <li><a href="gethostbyname.py">gethostbyname.py</a> - Returns the IP address for a given hostname.</li>
     18        <li><a href="dnssecTest.py">dnssecTest.py</a></li>
    1819    </ul>
    1920</body>
    2021</html>
  • doc/names/examples/testdnssec.py

     
     1#!/usr/bin/env python
     2
     3# Copyright (c) Twisted Matrix Laboratories.
     4# See LICENSE for details.
     5
     6"""
     7Sample app to lookup DS records and show a DNSSEC result
     8"""
     9
     10import sys
     11from twisted.names.common import DnssecConfig
     12from twisted.names.client import Resolver
     13from twisted.names import client
     14from twisted.internet import reactor
     15
     16def deferredAnswer(result):
     17    #expect - a 4-tuple with a couple of DS RR's and an RRSIG RR in the answer.
     18    print "    Answer: %s\n    Authority: %s\n    Additional: %s\n    Message: %s" % result
     19   
     20    #expect - the AD bit to be set
     21    message = result[3]
     22    authData = not not message.authData
     23    print "expect authData to be True: %s" % authData
     24   
     25    reactor.stop()
     26
     27def errAnswer(error):
     28    print "Error: %s" % error
     29    reactor.stop()
     30
     31def main():
     32
     33    reactor.callWhenRunning(doLookup)
     34    reactor.run()
     35
     36def doLookup():
     37
     38    #DnssecConfig - set ednsEnabled True if dnssecOk is True or you won't get
     39    # a DNSSEC result - since DO is in the EDNS OPT record.
     40    #For efficiency, set maxUdpPktSz large enough to get a DNSSEC message
     41        # without falling back to TCP but not so large that a network with small
     42    # MTU fragments packets.  1492 is generally absolutely safe. 
     43        # 4096 usually works. It depends on the network.       
     44    dsc = DnssecConfig(ednsEnabled=True, maxUdpPktSz=4096, dnssecOk=True, chkDis=False)
     45
     46    #setup a DNSSEC resolver using a public validating resolver
     47    # and set it as the client's theResolver
     48    resolver = Resolver(servers=[('149.20.64.20', 53)], dnssecConfig=dsc)
     49    client.theResolver = resolver
     50
     51    #query for DS records for comcast.net
     52    client.lookupDS('comcast.net')\
     53        .addCallback(deferredAnswer)\
     54        .addErrback(errAnswer)
     55
     56if __name__ == '__main__':
     57    main()
     58 No newline at end of file
  • doc/names/howto/names.xhtml

     
    4949directives are not yet supported.
    5050</p>
    5151
     52<h2>Using DNS Security Extensions (DNSSEC)</h2>
     53
     54<p>DNSSEC adds data origin authentication and data integrity to the Domain Name System (see <a href="http://www.ietf.org/rfc/rfc4033.txt">RFC4033</a>.) To use DNSSEC requires EDNS0, because to enable DNSSEC requires setting the DNSSEC OK (DO) bit which is in the EDNS0 OPT Record. <a href="http://www.ietf.org/rfc/rfc4035.txt"> RFC4035</a> discusses how a non-validating security aware resolver (which is what twisted.names is with the DNSSEC change) should handle the DO, CD and AD bits.
     55</p>
     56
     57<p><a href="../../../doc/names/examples/dnssecTest.py" class="py-listings">dnssecTest.py</a> is an example of using DNSSEC to check the AD bit of a domain that should validate.
     58</p>
     59
    5260</body></html>
  • twisted/names/client.py

     
    7171    protocol = property(_getProtocol)
    7272
    7373
    74     def __init__(self, resolv=None, servers=None, timeout=(1, 3, 11, 45), reactor=None):
     74    def __init__(self,
     75                 resolv=None,
     76                 servers=None,
     77                 timeout=(1, 3, 11, 45),
     78                 reactor=None,
     79                 dnssecConfig=None):
    7580        """
    7681        Construct a resolver which will query domain name servers listed in
    7782        the C{resolv.conf(5)}-format file given by C{resolv} as well as
     
    99104            for DNS datagrams, and enforce timeouts.  If not provided, the
    100105            global reactor will be used.
    101106
     107        @param dnssecConfig: An L{DnssecConfig} - see common.DnssecConfig()
     108
    102109        @raise ValueError: Raised if no nameserver addresses can be found.
    103110        """
    104         common.ResolverBase.__init__(self)
     111        common.ResolverBase.__init__(self, dnssecConfig)
    105112
    106113        if reactor is None:
    107114            from twisted.internet import reactor
     
    381388            return self.queryTCP(message.queries).addCallback(self.filterAnswers)
    382389        if message.rCode != dns.OK:
    383390            return failure.Failure(self.exceptionForCode(message.rCode)(message))
    384         return (message.answers, message.authority, message.additional)
    385391
     392        # if dnssecOk is enabled, return a reference to the message as the
     393        # 4th member of the tuple, so the caller can access the message header
     394        # flags. There are more flags than just AD (authentic Data) that are
     395        # set in a response that may be of interest (e.g., Truncate-TC).
     396        if self.dnssecConfig.dnssecOk:
     397            return (message.answers, message.authority,
     398                    message.additional, message)
     399        else:
     400            #dnssecOk was not requested - return a legacy 3-tuple
     401            return (message.answers, message.authority, message.additional)
    386402
     403
    387404    def _lookup(self, name, cls, type, timeout):
    388405        """
    389406        Build a L{dns.Query} for the given parameters and dispatch it via UDP.
     
    953970    @rtype: C{Deferred}
    954971    """
    955972    return getResolver().lookupNamingAuthorityPointer(name, timeout)
     973
     974
     975def lookupDNSKey(name, timeout=None):
     976    """
     977    DNSKEY lookup.
     978
     979    @type name: C{str}
     980    @param name: DNS name to resolve.
     981
     982    @type timeout: Sequence of C{int}
     983    @param timeout: Number of seconds after which to reissue the query.
     984        When the last timeout expires, the query is considered failed.
     985
     986    @rtype: C{Deferred}
     987   
     988    @since: 12.1
     989    """
     990    return getResolver().lookupDNSKey(name, timeout)
     991
     992
     993def lookupDS(name, timeout=None):
     994    """
     995    DS lookup.
     996
     997    @type name: C{str}
     998    @param name: DNS name to resolve.
     999
     1000    @type timeout: Sequence of C{int}
     1001    @param timeout: Number of seconds after which to reissue the query.
     1002        When the last timeout expires, the query is considered failed.
     1003
     1004    @rtype: C{Deferred}
     1005   
     1006    @since: 12.1
     1007    """
     1008    return getResolver().lookupDS(name, timeout)
     1009
     1010
     1011def lookupNSEC(name, timeout=None):
     1012    """
     1013    NSEC lookup.
     1014
     1015    @type name: C{str}
     1016    @param name: DNS name to resolve.
     1017
     1018    @type timeout: Sequence of C{int}
     1019    @param timeout: Number of seconds after which to reissue the query.
     1020        When the last timeout expires, the query is considered failed.
     1021
     1022    @rtype: C{Deferred}
     1023   
     1024    @since: 12.1
     1025    """
     1026    return getResolver().lookupNSEC(name, timeout)
     1027
     1028
     1029def lookupNSEC3(name, timeout=None):
     1030    """
     1031    NSEC3 lookup.
     1032
     1033    @type name: C{str}
     1034    @param name: DNS name to resolve.
     1035
     1036    @type timeout: Sequence of C{int}
     1037    @param timeout: Number of seconds after which to reissue the query.
     1038        When the last timeout expires, the query is considered failed.
     1039
     1040    @rtype: C{Deferred}
     1041   
     1042    @since: 12.1
     1043    """
     1044    return getResolver().lookupNSEC3(name, timeout)
     1045
     1046
     1047def lookupNSEC3Param(name, timeout=None):
     1048    """
     1049    NSEC3 Param lookup.
     1050
     1051    @type name: C{str}
     1052    @param name: DNS name to resolve.
     1053
     1054    @type timeout: Sequence of C{int}
     1055    @param timeout: Number of seconds after which to reissue the query.
     1056        When the last timeout expires, the query is considered failed.
     1057
     1058    @rtype: C{Deferred}
     1059   
     1060    @since: 12.1
     1061    """
     1062    return getResolver().lookupNSEC3Param(name, timeout)
     1063
     1064
     1065def lookupRRSIG(name, timeout=None):
     1066    """
     1067    RRSIG lookup.
     1068
     1069    @type name: C{str}
     1070    @param name: DNS name to resolve.
     1071
     1072    @type timeout: Sequence of C{int}
     1073    @param timeout: Number of seconds after which to reissue the query.
     1074        When the last timeout expires, the query is considered failed.
     1075
     1076    @rtype: C{Deferred}
     1077   
     1078    @since: 12.1
     1079    """
     1080    return getResolver().lookupRRSIG(name, timeout)
  • twisted/names/common.py

     
    1818
    1919EMPTY_RESULT = (), (), ()
    2020
     21
     22
     23class DnssecConfig():
     24    """
     25    Sets recDes and DNSSEC parameters. See the following RFC's for details:
     26   
     27        U{RFC 4033: DNS Security Introduction and Requirements
     28            <http://www.ietf.org/rfc/rfc4033.txt>}
     29   
     30        U{RFC 4034: Resource Records for the DNS Security Extensions
     31            <http://www.ietf.org/rfc/rfc4034.txt>}
     32   
     33        U{RFC 4035: Protocol Modifications for the DNS Security Extensions
     34            <http://www.ietf.org/rfc/rfc4035.txt>}
     35
     36    @type recDes: C{bool}
     37    @ivar recDes: Recursion Desired (RD flag). Not a DNSSEC parameter and does
     38        not require L{ednsEnabled}, but still nice to be able to control
     39        whether or not you're asking for recursion.
     40
     41    @type ednsEnabled: C{bool}
     42    @ivar ednsEnabled: If True, adds an OPT record to the query.
     43        The OPT record contains a version field (see L{version}) that
     44        indicates the version of EDNS that you can handle.
     45
     46    @type maxUdpPktSz: C{int}
     47    @ivar maxUdpPktSz: The max size UDP packet (bytes) that your end-to-end
     48        network can handle. On a modern network a reliable size is 1492,
     49        although up to 65535 is possible. Requires L{ednsEnabled} to be True.
     50
     51    @type dnssecOK: C{bool}
     52    @ivar dnssecOK: Dnssec Ok (DO flag). If True, sets a flag that indicates
     53        you want DNSSEC RR's and validation if the resolver validates. If DO
     54        is set, AD will be set in the response if the answer validates.
     55        Requires L{ednsEnabled}.
     56
     57    @type chkDis: C{bool}
     58    @ivar chkDis: Checking Disabled (CD flag). If dnssecOk and chkDis are both
     59        True, a validating resolver won't do validation but will return the
     60        DNSSEC RR's so that YOU can. Requires L{ednsEnabled}
     61
     62    @type version: C{int}
     63    @ivar version: Sets the EDNS version level. Currently, only version 0 is
     64        defined and supported by U{RFC 2671: Extension Mechanisms for DNS
     65        (EDNS0)<http://www.ietf.org/rfc/rfc2671.txt>}     
     66                   
     67    @since: 12.1
     68    """
     69    def __init__(self,
     70                 recDes=True,
     71                 ednsEnabled=False,
     72                 maxUdpPktSz=512,
     73                 dnssecOk=False,
     74                 chkDis=False,
     75                 version=0):
     76
     77        self.recDes = recDes
     78        self.ednsEnabled = ednsEnabled
     79        self.maxUdpPktSz = maxUdpPktSz
     80        self.dnssecOk = dnssecOk
     81        self.chkDis = chkDis
     82        self.version = version
     83
     84        assert not self.ednsEnabled or 512 <= self.maxUdpPktSz <= 65535
     85        assert version == 0
     86
     87
     88
    2189class ResolverBase:
    2290    """
    2391    L{ResolverBase} is a base class for L{IResolver} implementations which
     
    36104
    37105    typeToMethod = None
    38106
    39     def __init__(self):
     107    def __init__(self, dnssecConfig=None):
    40108        self.typeToMethod = {}
    41109        for (k, v) in typeToMethod.items():
    42110            self.typeToMethod[k] = getattr(self, v)
     111        self.dnssecConfig = dnssecConfig
     112        if self.dnssecConfig == None:
     113            self.dnssecConfig = DnssecConfig()
    43114
    44 
    45115    def exceptionForCode(self, responseCode):
    46116        """
    47117        Convert a response code (one of the possible values of
     
    201271        """
    202272        return self._lookup(name, dns.IN, dns.ALL_RECORDS, timeout)
    203273
     274
     275    def lookupDNSKey(self, name, timeout=None):
     276        """
     277        @see: twisted.names.client.lookupDNSKey
     278       
     279        @since: 12.1
     280        """
     281        return self._lookup(name, dns.IN, dns.DNSKEY, timeout)
     282
     283
     284    def lookupDS(self, name, timeout=None):
     285        """
     286        @see: twisted.names.client.lookupDS
     287       
     288        @since: 12.1
     289        """
     290        return self._lookup(name, dns.IN, dns.DS, timeout)
     291
     292
     293    def lookupNSEC(self, name, timeout=None):
     294        """
     295        @see: twisted.names.client.lookupNSEC
     296       
     297        @since: 12.1
     298        """
     299        return self._lookup(name, dns.IN, dns.NSEC, timeout)
     300
     301
     302    def lookupNSEC3(self, name, timeout=None):
     303        """
     304        @see: twisted.names.client.lookupNSEC3
     305       
     306        @since: 12.1
     307        """
     308        return self._lookup(name, dns.IN, dns.NSEC3, timeout)
     309
     310
     311    def lookupNSEC3Param(self, name, timeout=None):
     312        """
     313        @see: twisted.names.client.lookupNSEC3Param
     314       
     315        @since: 12.1
     316        """
     317        return self._lookup(name, dns.IN, dns.NSEC3PARAM, timeout)
     318
     319
     320    def lookupRRSIG(self, name, timeout=None):
     321        """
     322        @see: twisted.names.client.lookupRRSIG
     323       
     324        @since: 12.1
     325        """
     326        return self._lookup(name, dns.IN, dns.RRSIG, timeout)
     327
     328
    204329    def getHostByName(self, name, timeout = None, effort = 10):
    205330        """
    206331        @see: twisted.names.client.getHostByName
     
    268393    dns.MX:    'lookupMailExchange',
    269394    dns.TXT:   'lookupText',
    270395    dns.SPF:   'lookupSenderPolicy',
     396    dns.DNSKEY:'lookupDNSKey',
     397    dns.DS:    'lookupDS',
     398    dns.NSEC:  'lookupNSEC',
     399    dns.NSEC3: 'lookupNSEC3',
     400    dns.NSEC3PARAM: 'lookupNSEC3Param',
     401    dns.RRSIG: 'lookupRRSIG',
    271402
    272403    dns.RP:    'lookupResponsibility',
    273404    dns.AFSDB: 'lookupAFSDatabase',
  • twisted/names/dns.py

     
    1515__all__ = [
    1616    'IEncodable', 'IRecord',
    1717
    18     'A', 'A6', 'AAAA', 'AFSDB', 'CNAME', 'DNAME', 'HINFO',
     18    'A', 'A6', 'AAAA', 'AFSDB', 'CNAME', 'DNAME', 'DNSKEY', 'DS', 'HINFO',
    1919    'MAILA', 'MAILB', 'MB', 'MD', 'MF', 'MG', 'MINFO', 'MR', 'MX',
    20     'NAPTR', 'NS', 'NULL', 'PTR', 'RP', 'SOA', 'SPF', 'SRV', 'TXT', 'WKS',
     20    'NAPTR', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'NULL', 'OPT', 'PTR',
     21    'RP', 'RRSIG', 'SOA', 'SPF', 'SRV', 'TXT', 'WKS',
    2122
    2223    'ANY', 'CH', 'CS', 'HS', 'IN',
    2324
     
    2627    'EFORMAT', 'ENAME', 'ENOTIMP', 'EREFUSED', 'ESERVER',
    2728
    2829    'Record_A', 'Record_A6', 'Record_AAAA', 'Record_AFSDB', 'Record_CNAME',
    29     'Record_DNAME', 'Record_HINFO', 'Record_MB', 'Record_MD', 'Record_MF',
     30    'Record_DNAME', 'Record_DNSKEY', 'Record_DS', 'Record_HINFO', 'Record_MB',
     31    'Record_MD', 'Record_MF',
    3032    'Record_MG', 'Record_MINFO', 'Record_MR', 'Record_MX', 'Record_NAPTR',
    31     'Record_NS', 'Record_NULL', 'Record_PTR', 'Record_RP', 'Record_SOA',
    32     'Record_SPF', 'Record_SRV', 'Record_TXT', 'Record_WKS', 'UnknownRecord',
     33    'Record_NS', 'Record_NSEC', 'Record_NSEC3', 'Record_NULL', 'Record_OPT',
     34    'Record_PTR', 'Record_RP', 'Record_RRSIG', 'Record_SOA', 'Record_SPF',
     35    'Record_SRV', 'Record_TXT', 'Record_WKS', 'UnknownRecord',
    3336
    3437    'QUERY_CLASSES', 'QUERY_TYPES', 'REV_CLASSES', 'REV_TYPES', 'EXT_QUERIES',
    3538
    36     'Charstr', 'Message', 'Name', 'Query', 'RRHeader', 'SimpleRecord',
    37     'DNSDatagramProtocol', 'DNSMixin', 'DNSProtocol',
     39    'Charstr', 'Message', 'Name', 'OPTHeader', 'Query', 'RRHeader',
     40    'SimpleRecord','DNSDatagramProtocol', 'DNSMixin', 'DNSProtocol',
    3841
    3942    'OK', 'OP_INVERSE', 'OP_NOTIFY', 'OP_QUERY', 'OP_STATUS', 'OP_UPDATE',
    4043    'PORT',
     
    4649# System imports
    4750import warnings
    4851
     52import re
    4953import struct, random, types, socket
    5054
    5155import cStringIO as StringIO
     
    5458
    5559from zope.interface import implements, Interface, Attribute
    5660
     61from base64 import b64decode, b64encode
    5762
    5863# Twisted imports
    5964from twisted.internet import protocol, defer
     
    6166from twisted.python import log, failure
    6267from twisted.python import util as tputil
    6368from twisted.python import randbytes
     69from twisted.names.ser_num_arith import SNA, DateSNA
    6470
    6571
    6672def randomSource():
     
    7985NAPTR = 35
    8086A6 = 38
    8187DNAME = 39
     88OPT = 41
     89DS = 43
     90RRSIG = 46
     91NSEC = 47
     92DNSKEY = 48
     93NSEC3 = 50
     94NSEC3PARAM = 51
    8295SPF = 99
    8396
    8497QUERY_TYPES = {
     
    108121    NAPTR: 'NAPTR',
    109122    A6: 'A6',
    110123    DNAME: 'DNAME',
    111     SPF: 'SPF'
     124    OPT: 'OPT',
     125    DS: 'DS',
     126    RRSIG: 'RRSIG',
     127    NSEC: 'NSEC',
     128    DNSKEY: 'DNSKEY',
     129    NSEC3: 'NSEC3',
     130    NSEC3PARAM: 'NSEC3PARAM',
     131    SPF: 'SPF',
    112132}
    113133
    114134IXFR, AXFR, MAILB, MAILA, ALL_RECORDS = range(251, 256)
     
    371391    def __str__(self):
    372392        return self.name
    373393
     394class Sigstr(object):
     395    """
     396    for signatures and keys. display as b64 encoded
     397   
     398    @since: 12.1
     399    """
     400    implements(IEncodable)
     401
     402    def __init__(self, string=''):
     403        if not isinstance(string, str):
     404            raise ValueError("%r is not a string" % (string, ))
     405        self.string = string # b64encoded string
     406
     407    def encode(self, strio, compDict=None):
     408        """
     409        Write the byte representation (the un-b64-encoded string)
     410        to the file.
     411
     412        @type strio: file
     413        @param srio: The byte representation of this signature or key
     414        will be written to this file
     415
     416        @type compDict: dict
     417        @param compDict: not used.
     418        """
     419        strio.write(b64decode(self.string))
     420
     421    def decode(self, strio, length=None):
     422        """
     423        Decode a signature or a key.
     424
     425        @type strio: file
     426        @param strio: Exactly length bytes will be read from this file
     427        to decode the full signature or key
     428
     429        @type length: int
     430        @param lenth: length must always be given. A signature or key
     431        is always the last thing in an RR and so you can always determine
     432        its length.
     433        """
     434        self.string = ''
     435        if length == None:
     436            return
     437
     438        assert isinstance(length, int)
     439        buff = readPrecisely(strio, length)
     440        self.string = b64encode(buff)
     441
     442    def __eq__(self, other):
     443        if isinstance(other, Sigstr):
     444            return self.string == other.string
     445        return False
     446
     447    def __hash__(self):
     448        return hash(self.string)
     449
     450    def __str__(self):
     451        return self.string
     452
     453
     454class TypeBitmaps(object):
     455    """
     456    bitmap encoding scheme used by NSEC and NSEC3 RR's
     457    to indicate the RRset types that exist at the
     458    NSEC/NSEC3 RR's original owner name or hashed name.
     459
     460    See the following RFC's for details:
     461   
     462        U{RFC 4034: Resource Records for the DNS Security Extensions
     463            <http://www.ietf.org/rfc/rfc4034.txt>}
     464
     465        U{RFC 5155: DNS Security (DNSSEC) Hashed Authenticated Denial of
     466            Existence <http://tools.ietf.org/rfc/rfc5155.txt>}
     467   
     468    @since: 12.1
     469    """
     470    fmt = 'BB'
     471    typeRegex = re.compile('TYPE(\d+)')
     472
     473    def __init__(self, string=''):
     474        self.string = string
     475
     476    def encode(self, strio, compDict=None):
     477        """
     478        Encode the string field, which consists of a set
     479        of type names, into an NSEC/NSEC3 type bitmap.
     480
     481        @type strio: file
     482        @param strio: the byte representation of the type bitmap
     483        will be written to this file.
     484
     485        @type compDict: dict
     486        @param compDict: not used.
     487        """
     488        if not self.string:
     489            return;
     490
     491        # get a sorted list of RR Type Values
     492        mnus = self.string.split(' ')
     493        mnuVals = []
     494        for mnu in mnus:
     495            mnuVal = REV_TYPES.get(mnu, None)
     496            if not mnuVal:
     497                m = self.typeRegex.match(mnu)
     498                if m.groups():
     499                    mnuVal = int(m.group(1))
     500                    assert mnuVal < 65536
     501                else:
     502                    log.err("can't parse %s in %s" % (mnu, self.string, ))
     503                    continue;
     504            mnuVals.append(mnuVal)
     505        mnuVals.sort()
     506
     507        # convert that to a dict of windows and lists
     508        windDict = {}
     509        for v in mnuVals:
     510            window = (v >> 8) & 0xFF
     511            if window not in windDict:
     512                windDict[window] = []
     513            windDict[window].append(v & 0xFF)
     514
     515        # have to sort the keys - they're not in order!
     516        windows = windDict.keys()
     517        windows.sort()
     518
     519        # create the bitmaps
     520        bmap = bytearray()
     521        for w in windows:
     522            bmapseg = bytearray(32)
     523            maxoff = 0
     524            for v in windDict[w]:
     525                vm1 = v - 1
     526                off = vm1 >> 3
     527                bit = vm1 & 0x7
     528                msk = 1 << bit
     529                bmapseg[off] |= msk
     530                maxoff = max(off, maxoff)
     531            bmapseg = bmapseg[0:maxoff+1]
     532            bmap += chr(w) + chr(maxoff+1) + bmapseg
     533
     534        strio.write(str(bmap))
     535
     536    def decode(self, strio, length=None):
     537        """
     538        Decode an NSEC/NSEC3 type bitmap into a string
     539        representation of type names.
     540        """
     541        self.type_bitmaps = ""
     542        if length == None:
     543            return
     544
     545        type_bitmaps = bytearray()
     546        l = struct.calcsize(self.fmt)
     547        parsed_length = 0
     548        while parsed_length < length:
     549            buff = readPrecisely(strio, l)
     550            wb_num, bm_len = struct.unpack(self.fmt, buff)
     551            assert parsed_length + 2 + bm_len <= length
     552            bm = readPrecisely(strio, bm_len)
     553            byteNum = -1
     554            for b in bm:
     555                byteNum += 1
     556                ob = ord(b)
     557                if ob == 0:
     558                    continue
     559
     560                for v in range(8):
     561                    msk = 1<<v
     562                    if ob & msk:
     563                        val = wb_num*256 + byteNum*8 + v + 1
     564                        mnu = QUERY_TYPES.get(val, None)
     565                        if not mnu:
     566                            mnu = 'TYPE' + str(val)
     567                        type_bitmaps += (mnu + ' ')
     568
     569            parsed_length += 2 + bm_len
     570
     571        self.type_bitmaps = str(type_bitmaps[0:-1])
     572
     573    def __eq__(self, other):
     574        if isinstance(other, TypeBitmaps):
     575            return self.string == other.string
     576        return False
     577
     578    def __hash__(self):
     579        return hash(self.string)
     580
     581    def __str__(self):
     582        return self.string
     583
    374584class Query:
    375585    """
    376586    Represent a single DNS query.
     
    434644        return 'Query(%r, %r, %r)' % (str(self.name), self.type, self.cls)
    435645
    436646
    437 class RRHeader(tputil.FancyEqMixin):
     647
     648class OPTHeader(tputil.FancyEqMixin):
    438649    """
     650    A OPT record header.
     651
     652    @cvar fmt: C{str} specifying the byte format of an OPT Header.
     653
     654    @ivar name: Root (0, 8-bits)
     655    @ivar type: 41 (OPT Record)
     656    @ivar payload: An object that implements the IEncodable interface
     657    @ivar auth: Whether this header is authoritative or not.
     658   
     659    @since: 12.1
     660    """
     661
     662    implements(IEncodable)
     663
     664    compareAttributes = ('name', 'type', 'payload', 'auth')
     665
     666    fmt = "!H"
     667
     668    name = None
     669    type = None
     670    payload = None
     671
     672    # OPTHeader _really_ has no ttl or rdlength, but the
     673    # existence of the attributes is required.
     674    ttl = None
     675    rdlength = None
     676
     677    cachedResponse = None
     678
     679
     680    def __init__(self, payload=None, auth=False):
     681        """
     682        @type name: C{str}
     683        @param name: Root (0)
     684
     685        @type type: C{int}
     686        @param type: Query type 41.
     687
     688        @type payload: An object implementing C{IEncodable}
     689        @param payload: The OPT payload
     690        """
     691        assert (payload is None) or (payload.TYPE == OPT)
     692
     693        self.name = 0
     694        self.type = OPT
     695        self.payload = payload
     696        self.auth = auth
     697
     698
     699    def encode(self, strio, compDict=None):
     700        """
     701        Encode this OPT record header into proper format
     702       
     703        @type strio: file
     704        @param strio: the byte representation of this OPTHeader will be written
     705        to this file.
     706       
     707        @type compDict: dict
     708        @param compDict: not used.
     709        """
     710        strio.write(struct.pack('!B', 0))
     711        strio.write(struct.pack(self.fmt, self.type))
     712        if self.payload:
     713            prefix = strio.tell()
     714            self.payload.encode(strio, compDict)
     715            aft = strio.tell()
     716            strio.seek(prefix - 2, 0)
     717            strio.write(struct.pack('!H', aft - prefix))
     718            strio.seek(aft, 0)
     719
     720
     721    def decode(self, strio, length = None):
     722        """
     723        Decode a byte string into this OPTHeader.
     724
     725        @type strio: file
     726        @param strio: Bytes will be read from this file until the full
     727        OPTHeader is decoded.
     728        """
     729        self.name.decode(strio)
     730        l = struct.calcsize(self.fmt)
     731        buff = readPrecisely(strio, l)
     732        r = struct.unpack(self.fmt, buff)
     733        self.type = r[0]
     734
     735
     736    def isAuthoritative(self):
     737        return self.auth
     738
     739
     740    def __str__(self):
     741        return '<OPT auth=%s>' % (self.auth and 'True' or 'False')
     742
     743
     744    @classmethod
     745    def _headerFactory(cls, strio, auth=False):
     746        """
     747        reads enough of the stream to figure out if what is there is
     748        an OPTHeader or an RRHeader
     749        """
     750        beginPos = strio.tell()
     751        name = Name()
     752        name.decode(strio)
     753        type = struct.unpack(cls.fmt, readPrecisely(strio, 2))[0]
     754
     755        if len(name.name) == 0 and type == OPT:
     756            return cls()
     757        else:
     758            # back up to the beginning and try again
     759            strio.seek(beginPos, 0)
     760            rrh = RRHeader(auth=auth)
     761            rrh.decode(strio)
     762            return rrh
     763
     764    __repr__ = __str__
     765
     766
     767
     768class RRHeader(OPTHeader):
     769    """
    439770    A resource record header.
    440771
    441772    @cvar fmt: C{str} specifying the byte format of an RR.
     
    445776    @ivar cls: The query class of the original request.
    446777    @ivar ttl: The time-to-live for this record.
    447778    @ivar payload: An object that implements the IEncodable interface
    448 
    449     @ivar auth: A C{bool} indicating whether this C{RRHeader} was parsed from an
    450         authoritative message.
     779    @ivar auth: Whether this header is authoritative or not.
    451780    """
    452781
    453782    implements(IEncodable)
     
    10501379
    10511380    fancybasename = 'SRV'
    10521381    compareAttributes = ('priority', 'weight', 'target', 'port', 'ttl')
    1053     showAttributes = ('priority', 'weight', ('target', 'target', '%s'), 'port', 'ttl')
     1382    showAttributes = ('priority', 'weight',
     1383                      ('target', 'target', '%s'), 'port', 'ttl')
    10541384
    10551385    def __init__(self, priority=0, weight=0, port=0, target='', ttl=None):
    10561386        self.priority = int(priority)
     
    14641794
    14651795
    14661796
     1797class Record_OPT(tputil.FancyEqMixin, tputil.FancyStrMixin):
     1798    """
     1799    EDNS0 Option record.
     1800
     1801    @type payload_size: C{int}
     1802    @ivar payload_size: Specifies the max UDP Packet size (bytes) that your
     1803        network can handle.
     1804
     1805    @type dnssecOk: C{bool}
     1806    @ivar dnssecOk: Requests the server to send DNSSEC RRs and to do DNSSEC
     1807        validation (and set the AD bit if the response validates).
     1808
     1809    @type version: C{int}
     1810    @ivar version: The version of DNSSEC used. Currently only version 0
     1811        is defined.
     1812       
     1813    @since: 12.1
     1814    """
     1815    implements(IEncodable, IRecord)
     1816    TYPE = OPT
     1817    fmt = '!HBBHH'
     1818
     1819    fancybasename = 'OPT'
     1820    showAttributes = ('payload_size', ('flags', 'flags', '0x%x'), 'version')
     1821    compareAttributes = ('payload_size', 'flags', 'version')
     1822
     1823
     1824    def __init__(self, payload_size=512, dnssecOk=0, version=0, ttl=None):
     1825        self.payload_size = payload_size
     1826        self.version = version
     1827        self.flags = (dnssecOk & 1) << 15
     1828
     1829
     1830    def encode(self, strio, compDict = None):
     1831        OPTHeader().encode(strio)
     1832        strio.write(struct.pack('!H', self.payload_size))
     1833        strio.write(struct.pack('!B', 0)) # high order 0
     1834        strio.write(struct.pack('!B', self.version))
     1835        strio.write(struct.pack('!H', self.flags)) # DO(bit) + Z's
     1836        strio.write(struct.pack('!H', 0)) # Data length: 0
     1837
     1838
     1839    def decode(self, strio, length=None):
     1840        """
     1841        are OPT Records always 0 rdlength?
     1842        """
     1843        l = struct.calcsize(self.fmt)
     1844        buff = readPrecisely(strio, l)
     1845        r = struct.unpack(self.fmt, buff)
     1846        self.payload_size, z, self.version, self.flags, length = r
     1847        assert length == 0
     1848
     1849
     1850    def __hash__(self):
     1851        return hash((self.payload_size, self.version, self.flags))
     1852
     1853
     1854
     1855class Record_RRSIG(tputil.FancyEqMixin, tputil.FancyStrMixin):
     1856    """
     1857    DNSSEC RRSIG record.  See RFC 4034 for details.
     1858
     1859    @type type_covered: C{int}
     1860    @ivar type_covered: Identifies the type of the RRset that this RRSIG covers.
     1861
     1862    @type algo: C{int}
     1863    @ivar algo: Identifies the crypto algorithm type used to create the
     1864        signature.
     1865        (5 - RSH/SHA-1 is mandatory. See RFC 4034 App A.1 for the full list.)
     1866
     1867    @type labels: C{int}
     1868    @ivar labels: Specifies the number of labels in the original RRSIG RR
     1869        owner name. A validator can use this to determine whether the answer
     1870        was synthesized from a wildcard.
     1871
     1872    @type original_ttl: C{int}
     1873    @ivar original_ttl: Specifies the TTL of the covered RRset as it appears
     1874        in the authoritative zone.
     1875
     1876    @type sig_expiration: C{int}
     1877    @ivar sig_expiration: This RRSIG must NOT be used after this time. Seconds
     1878        since 1/1/1970, Modulo 2**32, compare using DNS Serial Number Arithmetic
     1879
     1880    @type sig_inception: C{int}
     1881    @ivar sig_inception: This RRSIG must NOT be used prior to this time. Seconds
     1882        since 1/1/1970, Modulo 2**32, compare using DNS Serial Number Arithmetic
     1883
     1884    @type key_tag: C{int}
     1885    @ivar key_tag: Contains the key tag value of the DNSKEY RR that validates
     1886        this signature, in network byte order. See RFC 4034 App B.
     1887
     1888    @type signers_name: L{Name}
     1889    @ivar signers_name: Identifies the owner name of the DNSKEY RR that a
     1890        validator should use to validate this signature. Must not use DNS
     1891        name compression.
     1892
     1893    @type signature: L{Sigstr}
     1894    @ivar signature: Contains the cryptographic signature that covers the RRSIG
     1895        RDATA (excluding the Signature field and the RRset specified by the
     1896        RRSIG owner name, RRSIG class and RRSIG Type Covered fields.
     1897
     1898    @type ttl: C{int}
     1899    @ivar ttl: The maximum number of seconds which this record should be
     1900        cached.
     1901       
     1902    @since: 12.1
     1903    """
     1904    implements(IEncodable, IRecord)
     1905    TYPE = RRSIG
     1906    fmt = '!HBBIIIH'
     1907
     1908    fancybasename = 'RRSIG'
     1909    showAttributes = ('type_covered', 'algo', 'labels', 'original_ttl',
     1910                         ('_sig_expiration', 'sig_expiration', '%s'),
     1911                         ('_sig_inception', 'sig_inception', '%s'),
     1912                         'key_tag',
     1913                         ('signers_name', 'signers_name', '%s'),
     1914                         ('_signature', 'signature', '%s'), 'ttl')
     1915    compareAttributes = ('type_covered', 'algo', 'labels', 'original_ttl',
     1916                         'sig_expiration', 'sig_inception', 'key_tag',
     1917                         'signers_name', 'signature', 'ttl')
     1918
     1919    _sig_expiration = property(lambda self: str(self.sig_expiration))
     1920    _sig_inception = property(lambda self: str(self.sig_inception))
     1921    _signature = property(lambda self: self.signature.string)
     1922
     1923
     1924    def __init__(self, type_covered=A, algo=0, labels=0, original_ttl=0,
     1925                 sig_expiration='', sig_inception='', key_tag=0,
     1926                 signers_name='', signature='', ttl=None):
     1927        self.type_covered = type_covered
     1928        self.algo = algo
     1929        self.labels = labels
     1930        self.original_ttl = original_ttl
     1931        self.sig_expiration = DateSNA(sig_expiration)
     1932        self.sig_inception = DateSNA(sig_inception)
     1933        self.key_tag = key_tag
     1934        self.signers_name = Name(signers_name)
     1935        self.signature = Sigstr(signature)
     1936        self.ttl = str2time(ttl)
     1937
     1938
     1939    def encode(self, strio, compDict = None):
     1940        strio.write(struct.pack(self.fmt,
     1941                                self.type_covered,
     1942                                self.algo,
     1943                                self.labels,
     1944                                self.original_ttl,
     1945                                self.sig_expiration.asInt(),
     1946                                self.sig_inception.asInt(),
     1947                                self.key_tag))
     1948        self.signers_name.encode(strio, None)
     1949        self.signature.encode(strio, compDict)
     1950
     1951
     1952    def decode(self, strio, length=None):
     1953        start = strio.tell()
     1954        l = struct.calcsize(self.fmt)
     1955        buff = readPrecisely(strio, l)
     1956        r = struct.unpack(self.fmt, buff)
     1957        self.type_covered, self.algo, self.labels, self.original_ttl, \
     1958            sig_expiration, sig_inception, self.key_tag = r
     1959        self.sig_expiration = DateSNA.fromInt(sig_expiration)
     1960        self.sig_inception = DateSNA.fromInt(sig_inception)
     1961        self.signers_name.decode(strio)
     1962        here = strio.tell()
     1963        self.signature.decode(strio, length + start - here if length else None)
     1964
     1965
     1966    def __hash__(self):
     1967        return hash((self.type_covered, self.algo, self.labels, self.original_ttl,
     1968                     self.sig_expiration, self.sig_inception, self.key_tag,
     1969                     self.signers_name, self.signature))
     1970
     1971
     1972
     1973class Record_DS(tputil.FancyEqMixin, tputil.FancyStrMixin):
     1974    """
     1975    A DNSSEC DS record.
     1976
     1977    @type key_tag: C{int}
     1978    @ivar key_tag: Lists the key tag of the DNSKEY RR referred to by this DS
     1979        record.
     1980
     1981    @type algo: C{int}
     1982    @ivar algo: Lists the algorithm number of the DNSKEY RR referenced by this
     1983        DS record.
     1984
     1985    @type digest_type: C{int}
     1986    @ivar digest_type: Identifies the algorithm used to construct the digest
     1987        field.
     1988
     1989    @type digest: L{Sigstr}
     1990    @ivar digest: Contains a digest of the refeerenced DNSKEY RR calculated by
     1991        the algorithm identified by the digest_type field.
     1992
     1993    @type ttl: C{int}
     1994    @ivar ttl: The maximum number of seconds which this record should be
     1995        cached.
     1996       
     1997    @since: 12.1
     1998    """
     1999    implements(IEncodable, IRecord)
     2000    TYPE = DS
     2001    fmt = '!HBB'
     2002
     2003    fancybasename = 'DS'
     2004    showAttributes = ('key_tag', 'algo', 'digest_type',
     2005                      ('_digest', 'digest', '%s'), 'ttl')
     2006    compareAttributes = ('key_tag', 'algo', 'digest_type', 'digest', 'ttl')
     2007
     2008    _digest = property(lambda self: self.digest.string)
     2009
     2010
     2011    def __init__(self, key_tag=0, algo=0, digest_type=0, digest='', ttl=None):
     2012        self.key_tag = key_tag
     2013        self.algo = algo
     2014        self.digest_type = digest_type
     2015        self.digest = Sigstr(digest)
     2016        self.ttl = str2time(ttl)
     2017
     2018
     2019    def encode(self, strio, compDict = None):
     2020        strio.write(struct.pack(self.fmt,
     2021                                self.key_tag,
     2022                                self.algo,
     2023                                self.digest_type))
     2024        self.digest.encode(strio, None)
     2025
     2026
     2027    def decode(self, strio, length=None):
     2028        start = strio.tell()
     2029        l = struct.calcsize(self.fmt)
     2030        buff = readPrecisely(strio, l)
     2031        r = struct.unpack(self.fmt, buff)
     2032        self.key_tag, self.algo, self.digest_type = r
     2033        here = strio.tell()
     2034        self.digest.decode(strio, length + start - here if length else None)
     2035
     2036
     2037    def __hash__(self):
     2038        return hash((self.key_tag, self.algo, self.digest_type, self.digest))
     2039
     2040
     2041
     2042class Record_DNSKEY(tputil.FancyEqMixin, tputil.FancyStrMixin):
     2043    """
     2044    A DNSSEC DNSKEY record. Holds the public key for a signed RRset.
     2045
     2046    @type flags: C{int}
     2047    @ivar flags: Bit 7 is the Zone Key flag. If bit 7 has value 1, then the
     2048        DNSKEY record holds a DNS zone key and the DNSKEY RR's owner name is
     2049        the name of a zone.  If bit 7 has value 0, then the DNSKEY record
     2050        holds some other type of DNS public key and MUST NOT be used to
     2051        verify RRSIGs that cover RRsets.
     2052        Bit 15 is the Secure Entry Point flag. See RFC 3757.)
     2053        All other bits are reserved and must be zero.
     2054
     2055    @type protocol: C{int}
     2056    @ivar protocol: Must have value 3. The DNSKEY RR must be treated as invalid
     2057        if this field does not contain 3.
     2058
     2059    @type algo: C{int}
     2060    @ivar algo: Identifies the public key's cryptographic algorithm and
     2061        determines the format of the pub_key field.  See RFC 4034 App A.
     2062
     2063    @type pub_key: L{Sigstr}
     2064    @ivar pub_key: Holds the public key material.
     2065
     2066    @type ttl: C{int}
     2067    @ivar ttl: The maximum number of seconds which this record should be
     2068        cached.
     2069       
     2070    @since: 12.1
     2071    """
     2072    implements(IEncodable, IRecord)
     2073    TYPE = DNSKEY
     2074    fmt = '!HBB'
     2075
     2076    fancybasename = 'DNSKEY'
     2077    showAttributes = ('flags', 'protocol', 'algo',
     2078                      ('_pub_key', 'pub_key', '%s'), 'ttl')
     2079    compareAttributes = ('flags', 'protocol', 'algo', 'pub_key', 'ttl')
     2080
     2081    _pub_key = property(lambda self: self.pub_key.string)
     2082
     2083
     2084    def __init__(self, flags=0, protocol=0, algo=0, pub_key='', ttl=None):
     2085        self.flags = flags
     2086        self.protocol = protocol
     2087        self.algo = algo
     2088        self.pub_key = Sigstr(pub_key)
     2089        self.ttl = str2time(ttl)
     2090
     2091
     2092    def encode(self, strio, compDict = None):
     2093        strio.write(struct.pack(self.fmt,
     2094                                self.flags,
     2095                                self.protocol,
     2096                                self.algo))
     2097        self.pub_key.encode(strio, None)
     2098
     2099
     2100    def decode(self, strio, length=None):
     2101        start = strio.tell()
     2102        l = struct.calcsize(self.fmt)
     2103        buff = readPrecisely(strio, l)
     2104        r = struct.unpack(self.fmt, buff)
     2105        self.flags, self.protocol, self.algo = r
     2106        here = strio.tell()
     2107        self.pub_key.decode(strio, length + start - here if length else None)
     2108
     2109
     2110    def __hash__(self):
     2111        return hash((self.flags, self.protocol, self.algo, self.pub_key))
     2112
     2113
     2114
     2115class Record_NSEC(tputil.FancyEqMixin, tputil.FancyStrMixin):
     2116    """
     2117    A DNSSEC NSEC record provides authenticated denial of existance for DNS
     2118    data.
     2119
     2120    A DNSSEC NSEC record lists:
     2121
     2122        1) the next owner name in canonical ordering of the zone that contains
     2123           authoritative data or a delegation point NS RRset.
     2124
     2125        2) the set of RR types present at the NSEC RR's owner name.
     2126
     2127    @type nxt_name: L{Name}
     2128    @ivar nxt_name: The next owner name that has authoritative data or contains
     2129        a delegation point NS RRset.
     2130
     2131    @type type_bitmaps: L{TypeBitmaps}
     2132    @ivar type_bitmaps: Identifies the RRset types that exist at the NSEC RR's
     2133        owner name.
     2134
     2135    @type ttl: C{int}
     2136    @ivar ttl: The maximum number of seconds which this record should be
     2137        cached.
     2138       
     2139    @since: 12.1
     2140    """
     2141    implements(IEncodable, IRecord)
     2142    TYPE = NSEC
     2143
     2144    fancybasename = 'NSEC'
     2145    showAttributes = (('nxt_name', 'nxt_name', '%s'),
     2146                      ('_type_bitmaps', 'type_bitmaps', '%s'), 'ttl')
     2147    compareAttributes = ('nxt_name', 'type_bitmaps', 'ttl')
     2148
     2149    _type_bitmaps = property(lambda self: self.type_bitmaps.string)
     2150
     2151
     2152    def __init__(self, nxt_name='', type_bitmaps=None, ttl=None):
     2153        self.nxt_name = Name(nxt_name)
     2154        self.type_bitmaps = TypeBitmaps(type_bitmaps)
     2155        self.ttl = str2time(ttl)
     2156
     2157
     2158    def encode(self, strio, compDict = None):
     2159        self.nxt_name.encode(strio, None)
     2160        self.type_bitmaps.encode(strio, None)
     2161
     2162
     2163    def decode(self, strio, length=None):
     2164        start = strio.tell()
     2165        self.nxt_name.decode(strio, None)
     2166        here = strio.tell()
     2167        self.type_bitmaps.decode(strio, length + start - here if length
     2168                                 else None)
     2169
     2170
     2171    def __hash__(self):
     2172        return hash((self.nxt_name, self.type_bitmaps))
     2173
     2174
     2175
     2176class Record_NSEC3PARAM(tputil.FancyEqMixin, tputil.FancyStrMixin):
     2177    """
     2178    A DNSSEC NSEC3PARAM record contains the NSEC3 parameters (hash algorithm,
     2179    flags, iterations and salt) needed by authoritative servers to calculate
     2180    hashed owner names.  The presence of an NSEC3PARAM RR at a zone apex
     2181    indicates that the specified parameters may be used by authoritative
     2182    servers to choose an appropriate set of NSEC3 RRs for negative responses.
     2183
     2184    @type hash_algo: C{int}
     2185    @ivar hash_algo: Identifies the cryptographic hash algorithm used to
     2186        construct the hash value.
     2187
     2188    @type flags: C{int}
     2189    @ivar flags: Identifies 8 1-bit flags. The only flag presently defined is
     2190        the opt-out flag. If the opt-out flag is set, the NSEC3 record covers
     2191        zero or more unsigned delegations. If the opt-out flag is clear, the
     2192        NSEC3 record covers zero unsigned delegations.
     2193
     2194    @type iterations: C{int}
     2195    @ivar iterations: Defines the nubmer of additional times the hash algorithm
     2196        has been performed.
     2197
     2198    @type salt: L{Charset}
     2199    @ivar salt: Identifies the salt value provided to the hash.
     2200
     2201    @type ttl: C{int}
     2202    @ivar ttl: The maximum number of seconds which this record should be
     2203        cached.
     2204       
     2205    @since: 12.1
     2206    """
     2207    implements(IEncodable, IRecord)
     2208    TYPE = NSEC3
     2209    fmt = '!BBH'
     2210
     2211    fancybasename = 'NSEC3'
     2212    showAttributes = ('hash_algo', 'flags', 'iterations',
     2213                      ('_salt', 'salt', '%s'), 'ttl')
     2214    compareAttributes = ('hash_algo', 'flags', 'iterations', 'salt', 'ttl')
     2215
     2216    _salt = property(lambda self: self.salt.string)
     2217
     2218
     2219    def __init__(self, hash_algo=0, flags=0, iterations=0, salt='', ttl=None):
     2220        self.hash_algo = hash_algo
     2221        self.flags = flags
     2222        self.iterations = iterations
     2223        self.salt = Charstr(salt)
     2224        self.ttl = str2time(ttl)
     2225
     2226
     2227    def encode(self, strio, compDict = None):
     2228        strio.write(struct.pack(self.fmt,
     2229                                self.hash_algo,
     2230                                self.flags,
     2231                                self.iterations))
     2232        self.salt.encode(strio, None)
     2233
     2234
     2235    def decode(self, strio, length=None):
     2236        start = strio.tell()
     2237        l = struct.calcsize(self.fmt)
     2238        buff = readPrecisely(strio, l)
     2239        r = struct.unpack(self.fmt, buff)
     2240        self.hash_algo, self.flags, self.iterations = r
     2241        self.salt.decode(strio)
     2242        here = strio.tell()
     2243
     2244
     2245    def __hash__(self):
     2246        return hash((self.hash_algo, self.flags, self.iterations, self.salt))
     2247
     2248
     2249
     2250class Record_NSEC3(Record_NSEC3PARAM):
     2251    """
     2252    A DNSSEC NSEC3 record provides non-zone-enumerable authenticated denial of
     2253    existence for DNS data and permits a gradual expansion of delegation-centric
     2254    zones.
     2255
     2256    A DNSSEC NSEC3 record lists:
     2257
     2258        1) the set of RR types present at the original owner name of the NSEC
     2259           RR.
     2260
     2261        2) the next hashed owner name in the hash order of the zone.
     2262
     2263    @type hash_algo: C{int}
     2264    @ivar hash_algo: Identifies the cryptographic hash algorithm used to
     2265        construct the hash value.
     2266
     2267    @type flags: C{int}
     2268    @ivar flags: Identifies 8 1-bit flags. The only flag presently defined
     2269        is the opt-out flag. If the opt-out flag is set, the NSEC3 record
     2270        covers zero or more unsigned delegations. If the opt-out flag is
     2271        clear, the NSEC3 record covers zero unsigned delegations.
     2272
     2273    @type iterations: C{int}
     2274    @ivar iterations: Defines the nubmer of additional times the hash algorithm
     2275        has been performed.
     2276
     2277    @type salt: L{Charset}
     2278    @ivar salt: Identifies the salt value provided to the hash.
     2279
     2280    @type nxt_hash_owner_name: L{Charset}
     2281    @ivar nxt_hash_owner_name: Contains the next hashed owner name in the zone
     2282        in hash order.
     2283
     2284    @type type_bitmaps: L{TypeBitmaps}
     2285    @ivar type_bitmaps: Identifies the RRset types that exist at the NSEC3 RR's
     2286        original owner name.
     2287
     2288    @type ttl: C{int}
     2289    @ivar ttl: The maximum number of seconds which this record should be
     2290        cached.
     2291       
     2292    @since: 12.1
     2293    """
     2294    implements(IEncodable, IRecord)
     2295    TYPE = NSEC3
     2296    fmt = '!BBH'
     2297
     2298    fancybasename = 'NSEC3'
     2299    showAttributes = ('hash_algo', 'flags', 'iterations',
     2300                      ('_salt', 'salt', '%s'),
     2301                      ('nxt_hash_owner_name', 'nxt_hash_owner_name', '%s'),
     2302                      ('_type_bitmaps', 'type_bitmaps', '%s'), 'ttl')
     2303    compareAttributes = ('hash_algo', 'flags', 'iterations',
     2304                         'salt', 'nxt_hash_owner_name', 'type_bitmaps', 'ttl')
     2305
     2306    _salt = property(lambda self: self.salt.string)
     2307    _type_bitmaps = property(lambda self: self.type_bitmaps.string)
     2308
     2309
     2310    def __init__(self, hash_algo=0, flags=0, iterations=0, salt='',
     2311                 nxt_hash_owner='', type_bitmaps=None, ttl=None):
     2312        Record_NSEC3PARAM.__init__(self, hash_algo, flags,
     2313                                   iterations, salt, ttl)
     2314        self.nxt_hash_owner_name = Charstr(nxt_hash_owner)
     2315        self.type_bitmaps = TypeBitmaps(type_bitmaps)
     2316
     2317
     2318    def encode(self, strio, compDict = None):
     2319        Record_NSEC3PARAM.encode(self, strio, compDict)
     2320        self.nxt_hash_owner_name.encode(strio, None)
     2321        self.type_bitmaps.encode(strio, None)
     2322
     2323
     2324    def decode(self, strio, length=None):
     2325        start = strio.tell()
     2326        Record_NSEC3PARAM.decode(self, strio, compDict)
     2327        self.nxt_hash_owner_name.decode(strio)
     2328        here = strio.tell()
     2329        self.type_bitmaps.decode(strio, length + start - here if length
     2330                                 else None)
     2331
     2332
     2333    def __hash__(self):
     2334        return hash((self.hash_algo, self.flags, self.iterations, self.salt,
     2335                     self.nxt_hash_owner_name, self.type_bitmaps))
     2336
     2337
     2338
    14672339# This is a fallback record
    14682340class UnknownRecord(tputil.FancyEqMixin, tputil.FancyStrMixin, object):
    14692341    """
     
    14842356    compareAttributes = ('data', 'ttl')
    14852357    showAttributes = ('data', 'ttl')
    14862358
     2359
    14872360    def __init__(self, data='', ttl=None):
    14882361        self.data = data
    14892362        self.ttl = str2time(ttl)
     
    15402413    queries = answers = add = ns = None
    15412414
    15422415    def __init__(self, id=0, answer=0, opCode=0, recDes=0, recAv=0,
    1543                        auth=0, rCode=OK, trunc=0, maxSize=512):
     2416                 auth=0, rCode=OK, trunc=0, maxSize=512, authData=0, chkDis=0):
    15442417        self.maxSize = maxSize
    15452418        self.id = id
    15462419        self.answer = answer
    15472420        self.opCode = opCode
    1548         self.auth = auth
    1549         self.trunc = trunc
    1550         self.recDes = recDes
    1551         self.recAv = recAv
     2421        self.auth = auth            # AA - Authoritative Answer
     2422        self.trunc = trunc          # TC - TrunCated
     2423        self.recDes = recDes        # RD - Recursion Desired
     2424        self.recAv = recAv          # RA - Recursion Available
     2425        self.authData = authData    # AD - Authentic Data
     2426        self.chkDis = chkDis        # CD - Checking Disabled
    15522427        self.rCode = rCode
    15532428        self.queries = []
    15542429        self.answers = []
     
    15882463        if self.maxSize and size > self.maxSize:
    15892464            self.trunc = 1
    15902465            body = body[:self.maxSize - self.headerSize]
    1591         byte3 = (( ( self.answer & 1 ) << 7 )
    1592                  | ((self.opCode & 0xf ) << 3 )
    1593                  | ((self.auth & 1 ) << 2 )
    1594                  | ((self.trunc & 1 ) << 1 )
    1595                  | ( self.recDes & 1 ) )
    1596         byte4 = ( ( (self.recAv & 1 ) << 7 )
    1597                   | (self.rCode & 0xf ) )
     2466        byte3 = (((self.answer & 1) << 7)
     2467                 | ((self.opCode & 0xf) << 3)
     2468                 | ((self.auth & 1 ) << 2)
     2469                 | ((self.trunc & 1 ) << 1)
     2470                 | (self.recDes & 1))
     2471        byte4 = (((self.recAv & 1) << 7)
     2472                 | ((self.authData & 1) << 5)
     2473                 | ((self.chkDis & 1) << 4)
     2474                 | (self.rCode & 0xf))
    15982475
    15992476        strio.write(struct.pack(self.headerFmt, self.id, byte3, byte4,
    16002477                                len(self.queries), len(self.answers),
     
    16072484        header = readPrecisely(strio, self.headerSize)
    16082485        r = struct.unpack(self.headerFmt, header)
    16092486        self.id, byte3, byte4, nqueries, nans, nns, nadd = r
    1610         self.answer = ( byte3 >> 7 ) & 1
    1611         self.opCode = ( byte3 >> 3 ) & 0xf
    1612         self.auth = ( byte3 >> 2 ) & 1
    1613         self.trunc = ( byte3 >> 1 ) & 1
     2487        self.answer = (byte3 >> 7) & 1
     2488        self.opCode = (byte3 >> 3) & 0xf
     2489        self.auth = (byte3 >> 2) & 1
     2490        self.trunc = (byte3 >> 1) & 1
    16142491        self.recDes = byte3 & 1
    1615         self.recAv = ( byte4 >> 7 ) & 1
     2492        self.recAv = (byte4 >> 7) & 1
     2493        self.authData = (byte4 >> 5) & 1
     2494        self.chkDis = (byte4 >> 4) & 1
    16162495        self.rCode = byte4 & 0xf
    16172496
    16182497        self.queries = []
     
    16242503                return
    16252504            self.queries.append(q)
    16262505
    1627         items = ((self.answers, nans), (self.authority, nns), (self.additional, nadd))
     2506        items = ((self.answers, nans),
     2507                 (self.authority, nns),
     2508                 (self.additional, nadd))
    16282509        for (l, n) in items:
    16292510            self.parseRecords(l, n, strio)
    16302511
    16312512
    16322513    def parseRecords(self, list, num, strio):
    16332514        for i in range(num):
    1634             header = RRHeader(auth=self.auth)
    16352515            try:
    1636                 header.decode(strio)
     2516                header = OPTHeader._headerFactory(strio, auth=self.auth)
    16372517            except EOFError:
    16382518                return
    16392519            t = self.lookupRecordType(header.type)
     
    17432623            query, or errbacked with any errors that could happen (exceptions
    17442624            during writing of the query, timeout errors, ...).
    17452625        """
    1746         m = Message(id, recDes=1)
     2626        dnssecConfig = self.controller.dnssecConfig
     2627        chkDis = dnssecConfig.chkDis
     2628        m = Message(id, recDes=dnssecConfig.recDes, chkDis=chkDis)
    17472629        m.queries = queries
     2630        if dnssecConfig.ednsEnabled:
     2631            m.additional = [Record_OPT(payload_size = dnssecConfig.maxUdpPktSz,
     2632                                       version = dnssecConfig.version,
     2633                                       dnssecOk = dnssecConfig.dnssecOk)]
    17482634
    17492635        try:
    17502636            writeMessage(m)
     
    17982684        self.transport.write(message.toStr(), address)
    17992685
    18002686    def startListening(self):
    1801         self._reactor.listenUDP(0, self, maxPacketSize=512)
     2687        maxPacketSize = 512
     2688        if self.controller.dnssecConfig.ednsEnabled:
     2689            maxPacketSize = self.controller.dnssecConfig.maxUdpPktSz
     2690        self._reactor.listenUDP(0, self, maxPacketSize=maxPacketSize)
    18022691
    18032692    def datagramReceived(self, data, addr):
    18042693        """
  • twisted/names/secondary.py

     
    9090
    9191    @ivar _reactor: The reactor to use to perform the zone transfers, or C{None}
    9292        to use the global reactor.
     93
     94    @ivar dnssecConfig: a L{DnssecConfig} giving the DNSSEC configuration to set
     95        on the resolver.
    9396    """
    9497
    9598    transferring = False
     
    97100    _port = 53
    98101    _reactor = None
    99102
    100     def __init__(self, primaryIP, domain):
    101         common.ResolverBase.__init__(self)
     103    def __init__(self, primaryIP, domain, dnssecConfig=None):
     104        common.ResolverBase.__init__(self, dnssecConfig)
    102105        self.primary = primaryIP
    103106        self.domain = domain
    104107
     
    150153
    151154        return FileAuthority.__dict__['_lookup'](self, name, cls, type, timeout)
    152155
    153     #shouldn't we just subclass? :P
     156    # shouldn't we just subclass? :P
    154157
    155158    lookupZone = FileAuthority.__dict__['lookupZone']
    156159
     
    164167                r.setdefault(str(rec.name).lower(), []).append(rec.payload)
    165168
    166169    def _ebZone(self, failure):
    167         log.msg("Updating %s from %s failed during zone transfer" % (self.domain, self.primary))
     170        log.msg("Updating %s from %s failed during zone transfer"
     171                % (self.domain, self.primary))
    168172        log.err(failure)
    169173
    170174    def update(self):
     
    175179
    176180    def _ebTransferred(self, failure):
    177181        self.transferred = False
    178         log.msg("Transferring %s from %s failed after zone transfer" % (self.domain, self.primary))
     182        log.msg("Transferring %s from %s failed after zone transfer"
     183                % (self.domain, self.primary))
    179184        log.err(failure)
  • twisted/names/ser_num_arith.py

     
     1# -*- test-case-name: twisted.names.test.test_ser_num_arith -*-
     2# Copyright (c) Twisted Matrix Laboratories.
     3# See LICENSE for details.
     4
     5"""
     6Serial Number Arithmetic
     7
     8This module implements RFC 1982 DNS Serial Number Arithmetic
     9(see http://tools.ietf.org/pdf/rfc1982.pdf).
     10SNA is used in DNS and specifically in DNSSEC as defined in
     11RFC 4034 in the DNSSEC Signature Expiration and Inception Fields.
     12
     13@author: Bob Novas
     14
     15@since: 12.1
     16"""
     17
     18import calendar, time
     19
     20
     21
     22class SNA(object):
     23    """
     24    implements RFC 1982 - DNS Serial Number Arithmetic
     25    """
     26    SERIAL_BITS = 32
     27    MODULOVAL = 2**SERIAL_BITS
     28    HLFRNG = 2**(SERIAL_BITS-1)
     29    MAXADD = (2**(SERIAL_BITS-1)-1)
     30
     31
     32    def __init__(self, number):
     33        self._number = int(number)%self.MODULOVAL
     34
     35
     36    def __repr__(self):
     37        return str(self._number)
     38
     39
     40    def asInt(self):
     41        """
     42        return an integer representing the object
     43        """
     44        return self._number
     45
     46
     47    def __eq__(self, sna2):
     48        """
     49        define the equality operator
     50        """
     51        return sna2._number == self._number
     52
     53
     54    def __lt__(self, sna2):
     55        """
     56        define the less than operator
     57        """
     58        return ((self != sna2) and
     59               ((self._number < sna2._number) and
     60                ((sna2._number - self._number) < self.HLFRNG) or
     61               (self._number > sna2._number) and
     62                ((self._number - sna2._number) > self.HLFRNG)))
     63
     64
     65    def __gt__(self, sna2):
     66        """
     67        define the greater than operator
     68        """
     69        return ((self != sna2) and
     70               ((self._number < sna2._number) and
     71               ((sna2._number - self._number) > self.HLFRNG) or
     72               (self._number > sna2._number) and
     73               ((self._number - sna2._number) < self.HLFRNG)))
     74
     75
     76    def __le__(self, sna2):
     77        """
     78        define the less than or equal operator
     79        """
     80        return self == sna2 or self < sna2
     81
     82
     83    def __ge__(self, sna2):
     84        """
     85        define the greater than or equal operator
     86        """
     87        return self == sna2 or self > sna2
     88
     89
     90    def __add__(self, sna2):
     91        """
     92        define the addition operator
     93        """
     94        if sna2 <= SNA(self.MAXADD):
     95            return SNA( (self._number + sna2._number)%self.MODULOVAL )
     96        else:
     97            raise ArithmeticError
     98
     99
     100    def __hash__(self):
     101        """
     102        define a hash function
     103        """
     104        return hash(self._number)
     105
     106
     107
     108def max(snaList):
     109    """
     110    takes a list of sna's from which it will pick the one
     111    with the highest value
     112    """
     113    if len(snaList) == 0:
     114        return None
     115    trialMax = snaList[0]
     116    for s in snaList[1:]:
     117        if not trialMax:
     118            trialMax = s
     119        elif s and s > trialMax:
     120            trialMax = s
     121    return trialMax
     122
     123
     124
     125class DateSNA(SNA):
     126    """
     127    implements DNS Serial Number Arithmetic
     128    for dates 'YYYYMMDDHHMMSS' per RFC 4034 P3.1.5
     129    """
     130    fmt = '%Y%m%d%H%M%S'
     131
     132
     133    def __init__(self, utcDateTime=''):
     134        """
     135        accept a UTC date/time string as YYMMDDHHMMSS
     136        and convert it to seconds since the epoch
     137        """
     138        if not utcDateTime:
     139            utcDateTime = '19700101000000'
     140        dtstruct = time.strptime(utcDateTime, DateSNA.fmt)
     141        secondsSinceE = calendar.timegm(dtstruct)
     142        super(DateSNA, self).__init__(secondsSinceE)
     143
     144
     145    def __add__(self, sna2):
     146        """
     147        define the addition operator
     148        """
     149        if not isinstance(sna2, SNA):
     150            return NotImplemented
     151
     152        if (sna2 <= SNA(self.MAXADD) and
     153            (self._number + sna2._number < self.MODULOVAL)):
     154            sna = SNA((self._number + sna2._number)%self.MODULOVAL)
     155            return DateSNA.fromSNA(sna)
     156        else:
     157            raise ArithmeticError
     158
     159
     160    def asDate(self):
     161        """return a representation of the object as a date string"""
     162        dtstruct = time.gmtime(self._number)
     163        return time.strftime(DateSNA.fmt, dtstruct)
     164
     165
     166    @classmethod
     167    def fromSNA(cls, sna):
     168        """
     169        create an DateSNA object from an SNA
     170        """
     171        d = cls()
     172        d._number = sna._number
     173        return d
     174
     175
     176    @classmethod
     177    def fromInt(cls, i):
     178        """
     179        create an DateSNA object from an int
     180        """
     181        return cls.fromSNA(SNA(i))
     182
     183
     184    def __str__(self):
     185        """
     186        return a string representation of the object
     187        """
     188        return self.asDate()
     189
  • twisted/names/test/test_client.py

     
    88from twisted.names import client, dns
    99from twisted.names.error import DNSQueryTimeoutError
    1010from twisted.trial import unittest
    11 from twisted.names.common import ResolverBase
     11from twisted.names.common import ResolverBase, DnssecConfig
     12from twisted.names.dns import Query, Message
    1213from twisted.internet import defer, error
    1314from twisted.python import failure
    1415from twisted.python.deprecate import getWarningMethod, setWarningMethod
    1516from twisted.python.compat import set
     17from twisted.names.test.test_rootresolve import MemoryReactor
    1618
    1719
    1820class FakeResolver(ResolverBase):
     
    430432        self.assertNotIn(protocol, resolver.connections)
    431433
    432434
     435    def _edns0ConfigurationTest(self, reactor, resolver):
     436        """
     437        A Resolver created with edns0 sends an OPT record as part of the
     438        query indicating the maxUdpPktSz and EDNS version supported
     439        (only EDNS version 0 - the default - is defined by spec).
    433440
     441        In addition, check that the message flag bits (DO, CD, RC) agree
     442        with the flags set in the resolver.
     443       
     444        @since: 12.1
     445        """
     446        # make sure EDNS is enabled
     447        self.assertTrue(resolver.dnssecConfig.ednsEnabled)
     448
     449        d = resolver._query(('example.com', 53),
     450                            [Query('foo.example.com',dns.A, dns.IN)],
     451                            30)
     452
     453        # A UDP port should have been started
     454        portNumber, transport = reactor.udpPorts.popitem()
     455
     456        # and a DNS packet sent
     457        [(packet, address)] = transport._sentPackets
     458
     459        msg = Message()
     460        msg.fromStr(packet)
     461
     462        # the query should have an additional OPT Header with
     463        # version == 0 and payload_size as defined by the resolver's
     464        # dnssecConfig and DO should be set
     465        self.assertEqual(len(msg.additional), 1)
     466        additional = msg.additional[0]
     467        payload = additional.payload
     468        dnssecConfig = resolver.dnssecConfig
     469        self.assertEqual(additional.type, dns.OPT)
     470        self.assertEqual(payload.version, dnssecConfig.version)
     471        self.assertEqual(payload.payload_size, dnssecConfig.maxUdpPktSz)
     472        # payload.flags bit 15 is DO, should equal resolver.dnssecOk
     473        self.assertEqual(not not(payload.flags & 0x8000), dnssecConfig.dnssecOk)
     474
     475        # the rest of the query should be as above also
     476        self.assertEqual(msg.queries, [Query('foo.example.com', dns.A, dns.IN)])
     477        self.assertEqual(msg.answers, [])
     478        self.assertEqual(msg.authority, [])
     479
     480        # the message header flags should be set as per the resolver's
     481        # dnssecConfig settings
     482        self.assertEqual(msg.chkDis, dnssecConfig.chkDis)
     483        self.assertEqual(msg.recDes, dnssecConfig.recDes)
     484
     485        response = []
     486        d.addCallback(response.append)
     487        self.assertEqual(response, [])
     488
     489        # Once a reply is received, the Deferred should fire.
     490        # Make the flag bits in the message agree with what you asked for
     491        del msg.queries[:]
     492        msg.answer = 1
     493        msg.answers.append(dns.RRHeader('foo.example.com',
     494                                        payload=dns.Record_A('5.8.13.21')))
     495        # if you requested DO, say you got AD
     496        msg.authData = dnssecConfig.dnssecOk
     497        msg.chkDis = dnssecConfig.chkDis
     498        msg.recDes = dnssecConfig.recDes
     499        transport._protocol.datagramReceived(msg.toStr(), ('1.1.2.4', 1053))
     500        return response[0]
     501
     502
     503    def test_dnssecEnabled(self):
     504        """
     505        A query sent with DNSSEC Enabled has an additional OPT record with
     506        DO set. Such a query returns a 4-tuple as a result, with the 4th
     507        member of the tuple being the message with the header bits set as
     508        set in the original query.
     509       
     510        @since: 12.1
     511        """
     512        # Create a resolver with EDNS0, max packet size = 4096,
     513        # and dnssecOk, chkDis and recDes = True
     514        maxPacketSize = 4096
     515        dsc = DnssecConfig(ednsEnabled=True,
     516                           maxUdpPktSz=maxPacketSize,
     517                           dnssecOk=True,  # DO
     518                           chkDis=True,    # CD
     519                           recDes=True)    # RC
     520
     521        reactor = MemoryReactor()
     522        resolver = client.Resolver(servers=[('example.com', 53)],
     523                                   reactor=reactor,
     524                                   dnssecConfig=dsc)
     525
     526        message = self._edns0ConfigurationTest(reactor, resolver)
     527
     528        # check that a resolver with dnssecOk returns a 4-tuple with the
     529        # header set as in the query
     530        answer, authority, additional, message = resolver.filterAnswers(message)
     531        self.assertEqual(answer, [dns.RRHeader('foo.example.com',
     532                                  payload=dns.Record_A('5.8.13.21', ttl=0))])
     533        self.assertEqual(authority, [])
     534        self.assertEqual(additional, [])
     535        self.assertIsInstance(message, dns.Message)
     536        self.assertTrue(message.authData)
     537        self.assertTrue(message.chkDis)
     538        self.assertTrue(message.recDes)
     539
     540
     541    def test_dnssecDisabled(self):
     542        """
     543        A query sent with DNSSEC Disabled but EDNS enabled has an additional
     544        OPT record with DO clear. Such a query returns a 3-tuple as a result.
     545        """
     546
     547        # Create a resolver with EDNS0, max packet size = 4096,
     548        # and dnssecOk = False, but recDes = True
     549        maxPacketSize = 4096
     550        dsc = DnssecConfig(ednsEnabled=True,
     551                           maxUdpPktSz=maxPacketSize,
     552                           dnssecOk=False, # not DO
     553                           recDes=True)    # RC
     554
     555        reactor = MemoryReactor()
     556        resolver = client.Resolver(servers=[('example.com', 53)],
     557                                   reactor=reactor,
     558                                   dnssecConfig=dsc)
     559
     560        message = self._edns0ConfigurationTest(reactor, resolver)
     561
     562        # check that a resolver with dnssecOk == False returns a 3-tuple
     563        # containing the right stuff.
     564        # Note - no access to header info in this case
     565        answer, authority, additional = resolver.filterAnswers(message)
     566        self.assertEqual(answer,
     567                         [dns.RRHeader('foo.example.com',
     568                                    payload=dns.Record_A('5.8.13.21', ttl=0))])
     569        self.assertEqual(authority, [])
     570        self.assertEqual(additional, [])
     571
     572
    434573class ClientTestCase(unittest.TestCase):
    435574
    436575    def setUp(self):
     
    658797        return d
    659798
    660799
     800    def test_lookupDNSKey(self):
     801        """
     802        See L{test_lookupAddress}
     803        """
     804        d = client.lookupDNSKey(self.hostname)
     805        d.addCallback(self.checkResult, dns.DNSKEY)
     806        return d
     807
     808
     809    def test_lookupDS(self):
     810        """
     811        See L{test_lookupAddress}
     812       
     813        @since: 12.1
     814        """
     815        d = client.lookupDS(self.hostname)
     816        d.addCallback(self.checkResult, dns.DS)
     817        return d
     818
     819
     820    def test_lookupNSEC(self):
     821        """
     822        See L{test_lookupAddress}
     823       
     824        @since: 12.1
     825        """
     826        d = client.lookupNSEC(self.hostname)
     827        d.addCallback(self.checkResult, dns.NSEC)
     828        return d
     829
     830
     831    def test_lookupNSEC3(self):
     832        """
     833        See L{test_lookupAddress}
     834       
     835        @since: 12.1
     836        """
     837        d = client.lookupNSEC3(self.hostname)
     838        d.addCallback(self.checkResult, dns.NSEC3)
     839        return d
     840
     841
     842    def test_lookupNSEC3Param(self):
     843        """
     844        See L{test_lookupAddress}
     845       
     846        @since: 12.1
     847        """
     848        d = client.lookupNSEC3Param(self.hostname)
     849        d.addCallback(self.checkResult, dns.NSEC3PARAM)
     850        return d
     851
     852
     853    def test_lookupRRSIG(self):
     854        """
     855        See L{test_lookupAddress}
     856       
     857        @since: 12.1
     858        """
     859        d = client.lookupRRSIG(self.hostname)
     860        d.addCallback(self.checkResult, dns.RRSIG)
     861        return d
     862
     863
    661864class ThreadedResolverTests(unittest.TestCase):
    662865    """
    663866    Tests for L{client.ThreadedResolver}.
  • twisted/names/test/test_dns.py

     
    88
    99from cStringIO import StringIO
    1010
     11import re
    1112import struct
    1213
    1314from twisted.python.failure import Failure
    1415from twisted.internet import address, task
    1516from twisted.internet.error import CannotListenError, ConnectionDone
    1617from twisted.trial import unittest
    17 from twisted.names import dns
     18from twisted.names import dns, common
    1819
    1920from twisted.test import proto_helpers
    2021
     
    2526    dns.Record_WKS, dns.Record_SRV, dns.Record_AFSDB, dns.Record_RP,
    2627    dns.Record_HINFO, dns.Record_MINFO, dns.Record_MX, dns.Record_TXT,
    2728    dns.Record_AAAA, dns.Record_A6, dns.Record_NAPTR, dns.UnknownRecord,
     29    dns.Record_OPT, dns.Record_RRSIG, dns.Record_DS, dns.Record_DNSKEY,
     30    dns.Record_NSEC, dns.Record_NSEC3PARAM, dns.Record_NSEC3,
    2831    ]
    2932
    3033class NameTests(unittest.TestCase):
     
    189192    """Encoding and then decoding various objects."""
    190193
    191194    names = ["example.org", "go-away.fish.tv", "23strikesback.net"]
     195    sigs = ["Qm12VZVaZgKS0/DZx35SGECDwPiTTf3ngChb7OkgSv5iupVmJGhPWudm "
     196            "/18qBSXKyv9hxMlEXFFgpBieNqLfSBkP1bwKnlqPfr1Hx7ctDwDUpkT3 "
     197            "cS8u/ms9yo3Fu1ybpO4Hfsb1HbA2N3zzQnjWKnyk26AAQSz8KgjNTFzD "
     198            "tJM=",
     199            "ZH2kahMD1g2WOieIotAcBwB0e/o30Zq6YR//M/xwP1ktkYuclmcR56iv "
     200            "XiR3QFWqmN5Xz3YpgmM4tZkjIeSMp2doYa7XYORZ7OpzG7oyfo8IoXxc "
     201            "j1VGDeAn1CeNCpBtoSGapRABG1gjY7oeRj/smPQPp2Gkf79+WZfuzRom "
     202            "/t4=",
     203            "AZpaboyNQAmbnBO1K66QmZ0c+VCdY/wu9QpEdRnMpnIOLPD28pNVu6hk "
     204            "GQMz6eg5WYkPYDdJK+1D/oyAQkDmRgn10+O9EdeFDyLqYqq/htEAvDm4 "
     205            "CziMSOpD/mkg1bSWCZ2mdln/GBk8WooCeeM7LEHmRjmHMMj0xb6N4SKa "
     206            "MEc=",
     207            "AwEAAbi5VQa3x+R3WQouBDNts+ZX2zIKZNoj9pzl7ew446kI/2omv3j6 "
     208            "c/4RQ6VneYE3mK7r0fFIKhVagmiRroFO1rRUJ8sVidssZ35CldE0sju+ "
     209            "E7wymVg3tV+ZUUO/+5v6Sfj+tw3rlp6eKqm7EGKKM88t+KuXiGYMu0Vw "
     210            "Rm9OUO1n",
     211            "AwEAAbmTL+kuV45kAxGN//iBKz93Y6lutgxoptp+I1+PZZMsBkhm/dZj "
     212            "q57040Pz/Hr3f2zQX7z6fFu7/Ml3MHPH1eQDiVXDvOkeNq2x4IbCO7x+ "
     213            "0p6bGYj4fw/tEfh/8dUzyzvMwfuAMsOvXza8Kh+UP4jvFc95cUuGgYus "
     214            "uEjUOp40PsL7EtYvAks3UssA6/OZP4w/1Z5m/VFx4PzgY0dkEuc=",
     215            "VGPxa8A81eV1dtUxVhz9b9Jsp6FF4M5H6J0QhzbNCUTHTHjLNR2VHYfE "
     216            "fM+Akwo3/qKq3D6vzTfzqtyPAXP8CmGfdD8hfv0s7Hae9c7Is8usdlrk "
     217            "ZpoXEFMW+YVG8G9OieYViq6tBIpUvKgMVZ+oXKo63KJ/tC/yBW0H0VQP "
     218            "YwdzZ3ZvYRDmZDvrXoX7T0YNU+0HYHnb7g7nUECIJ/4HHg==",
     219            "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T/K+oeC7Y81HIOFE "
     220            "h9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q845Kya8Lmkp/ikJVe/9id2TC2hoffp "
     221            "Z9pbZRjIeBTAvdTboGmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8LJ55 "
     222            "Y1k="]
     223    type_bitmaps = ["A MX RRSIG NSEC TYPE1234",
     224                    "AAAA NS CNAME MX TXT RRSIG NSEC3 DNAME",
     225                    "NSEC3 A AAAA RRSIG DS NS "
     226                    "TYPE5000 TYPE1000 TYPE2000 TYPE3000 TYPE4000",
     227                    None]
    192228
    193229    def testName(self):
    194230        for n in self.names:
     
    276312            self.assertEqual(result.string, n)
    277313
    278314
     315    def test_Sigstr(self):
     316        """
     317        Test L{dns.Sigstr} encode and decode.
     318       
     319        @since: 12.1
     320        """
     321        for s in self.sigs:
     322            # encode the signature/key
     323            f = StringIO()
     324            dns.Sigstr(s).encode(f)
     325            l = f.tell()
     326
     327            # decode the signature/key
     328            f.seek(0, 0)
     329            result = dns.Sigstr()
     330            result.decode(f,l)
     331            # spaces are free, and dig sticks them in
     332            self.assertEqual(result.string, s.replace(' ', ''))
     333
     334
     335    def test_TypeBitmaps(self):
     336        """
     337        Test L{dns.TypeBitmaps} encode and decode.
     338       
     339        @since: 12.1
     340        """
     341        typeRegex = re.compile('TYPE(\d+)')
     342
     343        for b in self.type_bitmaps:
     344            # encode the type_bitmaps
     345            f = StringIO()
     346            dns.TypeBitmaps(b).encode(f)
     347            l = f.tell()
     348
     349            # decode the type_bitmaps
     350            f.seek(0, 0)
     351            result = dns.TypeBitmaps()
     352            result.decode(f,l)
     353
     354            def mnuVal(mnu):
     355                mnuVal = dns.REV_TYPES.get(mnu, None)
     356                if not mnuVal:
     357                    m = typeRegex.match(mnu)
     358                    if m.groups():
     359                        mnuVal = int(m.group(1))
     360                        assert mnuVal < 65536
     361                    else:
     362                        log.err("can't parse %s in %s" % (mnu, self.string, ))
     363                        mnuVal = 0
     364                return mnuVal
     365
     366            def sorttok(string):
     367                if not string:
     368                    return ''
     369
     370                toks = string.split(' ')
     371                toks.sort(key = mnuVal)
     372                return ' '.join(toks)
     373
     374            self.assertEqual(result.type_bitmaps, sorttok(b))
     375
     376
    279377    def test_NAPTR(self):
    280378        """
    281379        Test L{dns.Record_NAPTR} encode and decode.
     
    325423        msg = dns.Message()
    326424        msg.fromStr(
    327425            '\x01\x00' # Message ID
    328             '\x00' # answer bit, opCode nibble, auth bit, trunc bit, recursive bit
    329             '\x00' # recursion bit, empty bit, empty bit, empty bit, response code nibble
     426            '\x00' # answer bit, opCode nibble, auth, trunc, recursive bits
     427            '\x00' # recursion bit, 3 empty bits, response code nibble
    330428            '\x00\x00' # number of queries
    331429            '\x00\x00' # number of answers
    332430            '\x00\x00' # number of authorities
     
    439537        Initialize the controller: create a list of messages.
    440538        """
    441539        self.messages = []
     540        self.dnssecConfig = common.DnssecConfig()
    442541
    443542
    444543    def messageReceived(self, msg, proto, addr):
     
    893992            repr(dns.UnknownRecord("foo\x1fbar", 12)),
    894993            "<UNKNOWN data='foo\\x1fbar' ttl=12>")
    895994
     995    def test_dnskey(self):
     996        """
     997        The repr of a L{dns.DNSKEY} instance includes the flags, protocol,
     998        algo, pub_key and ttl fields of the record.
     999       
     1000        @since: 12.1
     1001        """
     1002        self.assertEqual(
     1003            repr(dns.Record_DNSKEY(10, 20, 30, "foo\x1fbar", ttl=20)),
     1004            "<DNSKEY flags=10 protocol=20 algo=30 pub_key=foo\x1fbar ttl=20>")
    8961005
     1006    def test_ds(self):
     1007        """
     1008        The repr of a L{dns.DS} instance includes the key_tag, algo, digest_type,
     1009        digest and ttl fields of the record.
     1010       
     1011        @since: 12.1
     1012        """
     1013        self.assertEqual(
     1014            repr(dns.Record_DS(11, 22, 33, "foo\x1fbar1", ttl=21)),
     1015            "<DS key_tag=11 algo=22 digest_type=33 digest=foo\x1fbar1 ttl=21>")
    8971016
     1017    def test_nsec(self):
     1018        """
     1019        The repr of a L{dns.NSEC} instance includes the nxt_name, type_bitmaps
     1020        and ttl fields of the record.
     1021       
     1022        @since: 12.1
     1023        """
     1024        self.assertEqual(
     1025            repr(dns.Record_NSEC('bob', "\x1fabcd", ttl=31)),
     1026            "<NSEC nxt_name=bob type_bitmaps=\x1fabcd ttl=31>")
     1027
     1028    def test_nsec3param(self):
     1029        """
     1030        The repr of a L{dns.NSEC3PARAM} instance includes the hash_algo, flags,
     1031        iterations, salt and ttl fields of the record.
     1032       
     1033        @since: 12.1
     1034        """
     1035        self.assertEqual(
     1036            repr(dns.Record_NSEC3PARAM(1, 2, 3, '\x12\x34', ttl=31)),
     1037            "<NSEC3 hash_algo=1 flags=2 iterations=3 salt=\x12\x34 ttl=31>")
     1038
     1039    def test_nsec3(self):
     1040        """
     1041        The repr of a L{dns.NSEC3} instance includes the hash_algo, flags,
     1042        iterations, salt, nxt_hash_owner_name, type_bitmaps and ttl fields
     1043        of the record.
     1044       
     1045        @since: 12.1
     1046        """
     1047        self.assertEqual(
     1048            repr(dns.Record_NSEC3(1, 2, 3, '\x12\x34', 'bob',
     1049                                  "\x1fabcd", ttl=31)),
     1050            "<NSEC3 hash_algo=1 flags=2 iterations=3 "
     1051            "salt=\x12\x34 nxt_hash_owner_name=bob "
     1052            "type_bitmaps=\x1fabcd ttl=31>")
     1053
     1054    def test_opt(self):
     1055        """
     1056        The repr of a L{dns.OPT} instance includes the payload_size, dnssecOk
     1057        flag, and version fields of the record.
     1058        (The OPT record has no ttl field.)
     1059       
     1060        @since: 12.1
     1061        """
     1062        self.assertEqual(
     1063            repr(dns.Record_OPT(payload_size=1492, dnssecOk=1, version=0)),
     1064                 "<OPT payload_size=1492 flags=0x8000 version=0>")
     1065
     1066    def test_rrsig(self):
     1067        """
     1068        The repr of a L{dns.RRSIG} instance includes the algo, labels,
     1069        original_ttl sig_expiration, sig_inception, key_tag, signers_name,
     1070        signature and ttl fields of the record.
     1071       
     1072        @since: 12.1
     1073        """
     1074        self.assertEqual(
     1075            repr(dns.Record_RRSIG(type_covered=dns.A,
     1076                                  algo=2,
     1077                                  labels=3,
     1078                                  original_ttl=30,
     1079                                  sig_expiration='20110101123456',
     1080                                  sig_inception= '20110202112233',
     1081                                  key_tag=60,
     1082                                  signers_name='bob',
     1083                                  signature='\x12\x34sig',
     1084                                  ttl=70)),
     1085        "<RRSIG type_covered=1 algo=2 labels=3 original_ttl=30"
     1086        " sig_expiration=20110101123456 sig_inception=20110202112233 key_tag=60"
     1087        " signers_name=bob signature=\x12\x34sig ttl=70>")
     1088
    8981089class _Equal(object):
    8991090    """
    9001091    A class the instances of which are equal to anything and everything.
     
    9611152            cls('example.com', 123),
    9621153            cls('example.org', 123))
    9631154
     1155    def test_optheader(self):
     1156        """
     1157        Two OptHeader instances comapare equal iff the have the same
     1158        (Record_OPT) payload and auth bit.
     1159        """
     1160        self._equalityTest(
     1161            dns.OPTHeader(payload=dns.Record_OPT(payload_size=1024,
     1162                                                 dnssecOk=True,
     1163                                                 version=0,
     1164                                                 ttl=30)),
     1165            dns.OPTHeader(payload=dns.Record_OPT(payload_size=1024,
     1166                                                 dnssecOk=True,
     1167                                                 version=0,
     1168                                                 ttl=30)),
     1169            dns.OPTHeader(payload=dns.Record_OPT(payload_size=1492,
     1170                                                 dnssecOk=False,
     1171                                                 version=0,
     1172                                                 ttl=40), auth=True))
    9641173
    9651174    def test_rrheader(self):
    9661175        """
     
    9681177        the same name, type, class, time to live, payload, and authoritative
    9691178        bit.
    9701179        """
     1180        aRec = dns.Record_A('1.2.3.4')
     1181
    9711182        # Vary the name
    9721183        self._equalityTest(
    973             dns.RRHeader('example.com', payload=dns.Record_A('1.2.3.4')),
    974             dns.RRHeader('example.com', payload=dns.Record_A('1.2.3.4')),
    975             dns.RRHeader('example.org', payload=dns.Record_A('1.2.3.4')))
     1184            dns.RRHeader('example.com', payload=aRec),
     1185            dns.RRHeader('example.com', payload=aRec),
     1186            dns.RRHeader('example.org', payload=aRec))
    9761187
    9771188        # Vary the payload
    9781189        self._equalityTest(
    979             dns.RRHeader('example.com', payload=dns.Record_A('1.2.3.4')),
    980             dns.RRHeader('example.com', payload=dns.Record_A('1.2.3.4')),
     1190            dns.RRHeader('example.com', payload=aRec),
     1191            dns.RRHeader('example.com', payload=aRec),
    9811192            dns.RRHeader('example.com', payload=dns.Record_A('1.2.3.5')))
    9821193
    9831194        # Vary the type.  Leave the payload as None so that we don't have to
     
    9891200
    9901201        # Probably not likely to come up.  Most people use the internet.
    9911202        self._equalityTest(
    992             dns.RRHeader('example.com', cls=dns.IN, payload=dns.Record_A('1.2.3.4')),
    993             dns.RRHeader('example.com', cls=dns.IN, payload=dns.Record_A('1.2.3.4')),
    994             dns.RRHeader('example.com', cls=dns.CS, payload=dns.Record_A('1.2.3.4')))
     1203            dns.RRHeader('example.com', cls=dns.IN, payload=aRec),
     1204            dns.RRHeader('example.com', cls=dns.IN, payload=aRec),
     1205            dns.RRHeader('example.com', cls=dns.CS, payload=aRec))
    9951206
    9961207        # Vary the ttl
    9971208        self._equalityTest(
    998             dns.RRHeader('example.com', ttl=60, payload=dns.Record_A('1.2.3.4')),
    999             dns.RRHeader('example.com', ttl=60, payload=dns.Record_A('1.2.3.4')),
    1000             dns.RRHeader('example.com', ttl=120, payload=dns.Record_A('1.2.3.4')))
     1209            dns.RRHeader('example.com', ttl=60, payload=aRec),
     1210            dns.RRHeader('example.com', ttl=60, payload=aRec),
     1211            dns.RRHeader('example.com', ttl=120, payload=aRec))
    10011212
    10021213        # Vary the auth bit
    10031214        self._equalityTest(
    1004             dns.RRHeader('example.com', auth=1, payload=dns.Record_A('1.2.3.4')),
    1005             dns.RRHeader('example.com', auth=1, payload=dns.Record_A('1.2.3.4')),
    1006             dns.RRHeader('example.com', auth=0, payload=dns.Record_A('1.2.3.4')))
     1215            dns.RRHeader('example.com', auth=1, payload=aRec),
     1216            dns.RRHeader('example.com', auth=1, payload=aRec),
     1217            dns.RRHeader('example.com', auth=0, payload=aRec))
    10071218
    10081219
    10091220    def test_ns(self):
     
    14831694            dns.UnknownRecord('foo', ttl=10),
    14841695            dns.UnknownRecord('foo', ttl=10),
    14851696            dns.UnknownRecord('foo', ttl=100))
     1697
     1698    def test_rrsig(self):
     1699        """
     1700        L(dns.RRSIG) instances compare equal iff they have the same
     1701        type_covered, algo, labels, original_ttl, sig_expiration, sig_inception,
     1702        key_tag, signers_name, signature, and ttl
     1703       
     1704        @since: 12.1
     1705        """
     1706        self._equalityTest(
     1707            dns.Record_RRSIG(type_covered=dns.A),
     1708            dns.Record_RRSIG(type_covered=dns.A),
     1709            dns.Record_RRSIG(type_covered=dns.AAAA))
     1710        self._equalityTest(
     1711            dns.Record_RRSIG(algo=1),
     1712            dns.Record_RRSIG(algo=1),
     1713            dns.Record_RRSIG(algo=2))
     1714        self._equalityTest(
     1715            dns.Record_RRSIG(labels=3),
     1716            dns.Record_RRSIG(labels=3),
     1717            dns.Record_RRSIG(labels=4))
     1718        self._equalityTest(
     1719            dns.Record_RRSIG(original_ttl=5),
     1720            dns.Record_RRSIG(original_ttl=5),
     1721            dns.Record_RRSIG(original_ttl=6))
     1722        self._equalityTest(
     1723            dns.Record_RRSIG(sig_expiration='20110101000000'),
     1724            dns.Record_RRSIG(sig_expiration='20110101000000'),
     1725            dns.Record_RRSIG(sig_expiration='20110101000001'))
     1726        self._equalityTest(
     1727            dns.Record_RRSIG(sig_inception='20120101000000'),
     1728            dns.Record_RRSIG(sig_inception='20120101000000'),
     1729            dns.Record_RRSIG(sig_inception='20120101000001'))
     1730        self._equalityTest(
     1731            dns.Record_RRSIG(key_tag=11),
     1732            dns.Record_RRSIG(key_tag=11),
     1733            dns.Record_RRSIG(key_tag=12))
     1734        self._equalityTest(
     1735            dns.Record_RRSIG(signers_name='bob'),
     1736            dns.Record_RRSIG(signers_name='bob'),
     1737            dns.Record_RRSIG(signers_name='joe'))
     1738        self._equalityTest(
     1739            dns.Record_RRSIG(signature='abcdef'),
     1740            dns.Record_RRSIG(signature='abcdef'),
     1741            dns.Record_RRSIG(signature='abcdefg'))
     1742        self._equalityTest(
     1743            dns.Record_RRSIG(ttl=10),
     1744            dns.Record_RRSIG(ttl=10),
     1745            dns.Record_RRSIG(ttl=20))
     1746
     1747    def test_ds(self):
     1748        """
     1749        L(dns.DS) instances compare equal iff they have the same
     1750        key_tag, algo, digest_type, digest and ttl
     1751       
     1752        @since: 12.1
     1753        """
     1754        self._equalityTest(
     1755            dns.Record_DS(key_tag=1),
     1756            dns.Record_DS(key_tag=1),
     1757            dns.Record_DS(key_tag=2))
     1758        self._equalityTest(
     1759            dns.Record_DS(algo=3),
     1760            dns.Record_DS(algo=3),
     1761            dns.Record_DS(algo=4))
     1762        self._equalityTest(
     1763            dns.Record_DS(digest_type=5),
     1764            dns.Record_DS(digest_type=5),
     1765            dns.Record_DS(digest_type=6))
     1766        self._equalityTest(
     1767            dns.Record_DS(digest='abcdef-digest'),
     1768            dns.Record_DS(digest='abcdef-digest'),
     1769            dns.Record_DS(digest='abcdef-digest-f'))
     1770        self._equalityTest(
     1771            dns.Record_DS(ttl=10),
     1772            dns.Record_DS(ttl=10),
     1773            dns.Record_DS(ttl=20))
     1774
     1775    def test_dnskey(self):
     1776        """
     1777        L(dns.DNSKEY) instances compare equal iff they have the same
     1778        flags, protocol, algo, pub_key and ttl
     1779       
     1780        @since: 12.1
     1781        """
     1782        self._equalityTest(
     1783            dns.Record_DNSKEY(flags=1),
     1784            dns.Record_DNSKEY(flags=1),
     1785            dns.Record_DNSKEY(flags=2))
     1786        self._equalityTest(
     1787            dns.Record_DNSKEY(protocol=3),
     1788            dns.Record_DNSKEY(protocol=3),
     1789            dns.Record_DNSKEY(protocol=4))
     1790        self._equalityTest(
     1791            dns.Record_DNSKEY(algo=5),
     1792            dns.Record_DNSKEY(algo=5),
     1793            dns.Record_DNSKEY(algo=6))
     1794        self._equalityTest(
     1795            dns.Record_DNSKEY(pub_key='abcdef-digest'),
     1796            dns.Record_DNSKEY(pub_key='abcdef-digest'),
     1797            dns.Record_DNSKEY(pub_key='abcdef-digest-f'))
     1798        self._equalityTest(
     1799            dns.Record_DNSKEY(ttl=10),
     1800            dns.Record_DNSKEY(ttl=10),
     1801            dns.Record_DNSKEY(ttl=20))
     1802
     1803    def test_nsec(self):
     1804        """
     1805        L(dns.DNSKEY) instances compare equal iff they have the same
     1806        nxt_name, type_bitmaps and ttl
     1807       
     1808        @since: 12.1
     1809        """
     1810        self._equalityTest(
     1811            dns.Record_NSEC(nxt_name="example.com"),
     1812            dns.Record_NSEC(nxt_name="example.com"),
     1813            dns.Record_NSEC(nxt_name="a.example.com"))
     1814        self._equalityTest(
     1815            dns.Record_NSEC(type_bitmaps="A AAAA NS NSEC3"),
     1816            dns.Record_NSEC(type_bitmaps="A AAAA NS NSEC3"),
     1817            dns.Record_NSEC(type_bitmaps="A AAAA NS NSEC3 RRSIG"))
     1818        self._equalityTest(
     1819            dns.Record_NSEC(ttl=5),
     1820            dns.Record_NSEC(ttl=5),
     1821            dns.Record_NSEC(ttl=6))
     1822
     1823    def test_nsec3param(self):
     1824        """
     1825        L(dns.DNSKEY) instances compare equal iff they have the same
     1826        hash_algo, flags, iterations, salt, nxt_hash_owner_name, type_bitmaps and ttl
     1827       
     1828        @since: 12.1
     1829        """
     1830        self._equalityTest(
     1831            dns.Record_NSEC3PARAM(hash_algo=1),
     1832            dns.Record_NSEC3PARAM(hash_algo=1),
     1833            dns.Record_NSEC3PARAM(hash_algo=2))
     1834        self._equalityTest(
     1835            dns.Record_NSEC3PARAM(flags=1),
     1836            dns.Record_NSEC3PARAM(flags=1),
     1837            dns.Record_NSEC3PARAM(flags=2))
     1838        self._equalityTest(
     1839            dns.Record_NSEC3PARAM(iterations=5),
     1840            dns.Record_NSEC3PARAM(iterations=5),
     1841            dns.Record_NSEC3PARAM(iterations=6))
     1842        self._equalityTest(
     1843            dns.Record_NSEC3PARAM(salt="abcdef"),
     1844            dns.Record_NSEC3PARAM(salt="abcdef"),
     1845            dns.Record_NSEC3PARAM(salt="abcdefg"))
     1846        self._equalityTest(
     1847            dns.Record_NSEC3PARAM(ttl=5),
     1848            dns.Record_NSEC3PARAM(ttl=5),
     1849            dns.Record_NSEC3PARAM(ttl=6))
     1850
     1851    def test_nsec3(self):
     1852        """
     1853        L(dns.DNSKEY) instances compare equal iff they have the same
     1854        hash_algo, flags, iterations, salt, nxt_hash_owner_name, type_bitmaps
     1855        and ttl
     1856       
     1857        @since: 12.1
     1858        """
     1859        self._equalityTest(
     1860            dns.Record_NSEC3(hash_algo=1),
     1861            dns.Record_NSEC3(hash_algo=1),
     1862            dns.Record_NSEC3(hash_algo=2))
     1863        self._equalityTest(
     1864            dns.Record_NSEC3(flags=1),
     1865            dns.Record_NSEC3(flags=1),
     1866            dns.Record_NSEC3(flags=2))
     1867        self._equalityTest(
     1868            dns.Record_NSEC3(iterations=5),
     1869            dns.Record_NSEC3(iterations=5),
     1870            dns.Record_NSEC3(iterations=6))
     1871        self._equalityTest(
     1872            dns.Record_NSEC3(salt="abcdef"),
     1873            dns.Record_NSEC3(salt="abcdef"),
     1874            dns.Record_NSEC3(salt="abcdefg"))
     1875        self._equalityTest(
     1876            dns.Record_NSEC3(nxt_hash_owner="example.com"),
     1877            dns.Record_NSEC3(nxt_hash_owner="example.com"),
     1878            dns.Record_NSEC3(nxt_hash_owner="a.example.com"))
     1879        self._equalityTest(
     1880            dns.Record_NSEC3(type_bitmaps="A AAAA NS NSEC3"),
     1881            dns.Record_NSEC3(type_bitmaps="A AAAA NS NSEC3"),
     1882            dns.Record_NSEC3(type_bitmaps="A AAAA NS NSEC3 RRSIG"))
     1883        self._equalityTest(
     1884            dns.Record_NSEC3(ttl=5),
     1885            dns.Record_NSEC3(ttl=5),
     1886            dns.Record_NSEC3(ttl=6))
     1887
     1888
  • twisted/names/test/test_names.py

     
    9898                           '\x12\x01\x16\xfe\xc1\x00\x01'),
    9999            dns.Record_NAPTR(100, 10, "u", "sip+E2U",
    100100                             "!^.*$!sip:information@domain.tld!"),
    101             dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF')],
     101            dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF'),
     102            dns.Record_DNSKEY(0x10, 3, 5,
     103                              "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T"
     104                              "/K+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q"
     105                              "845Kya8Lmkp/ikJVe/9id2TC2hoffpZ9pbZRjIeBT"
     106                              "AvdTboGmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8"
     107                              "LJ55Y1k="
     108                             )],
    102109        'http.tcp.test-domain.com': [
    103110            dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool')
    104111        ],
    105112        'host.test-domain.com': [
    106113            dns.Record_A('123.242.1.5'),
    107114            dns.Record_A('0.255.0.255'),
     115            dns.Record_RRSIG(dns.A, 5, 3, 86400,
     116                             '20120101000000', '20120201000000',
     117                             2642, 'test-domain.com',
     118                            "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T/K"
     119                            "+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q845K"
     120                            "ya8Lmkp/ikJVe/9id2TC2hoffpZ9pbZRjIeBTAvdTbo"
     121                            "GmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8LJ55Y1k=")
    108122        ],
    109123        'host-two.test-domain.com': [
    110124#
     
    224238        """Test DNS 'A' record queries with multiple answers"""
    225239        return self.namesTest(
    226240            self.resolver.lookupAddress('host.test-domain.com'),
    227             [dns.Record_A('123.242.1.5', ttl=19283784), dns.Record_A('0.255.0.255', ttl=19283784)]
     241            [dns.Record_A('123.242.1.5', ttl=19283784),
     242             dns.Record_A('0.255.0.255', ttl=19283784)]
    228243        )
    229244
    230245
     
    232247        """Test DNS 'A' record queries with edge cases"""
    233248        return self.namesTest(
    234249            self.resolver.lookupAddress('host-two.test-domain.com'),
    235             [dns.Record_A('255.255.255.254', ttl=19283784), dns.Record_A('0.0.0.0', ttl=19283784)]
     250            [dns.Record_A('255.255.255.254', ttl=19283784),
     251             dns.Record_A('0.0.0.0', ttl=19283784)]
    236252        )
    237253
    238254
     
    264280        """Test DNS 'HINFO' record queries"""
    265281        return self.namesTest(
    266282            self.resolver.lookupHostInfo('test-domain.com'),
    267             [dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know', ttl=19283784)]
     283            [dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know',
     284                              ttl=19283784)]
    268285        )
    269286
    270287    def testPTR(self):
     
    286303        """Test additional processing for CNAME records"""
    287304        return self.namesTest(
    288305        self.resolver.lookupAddress('cname.test-domain.com'),
    289         [dns.Record_CNAME('test-domain.com', ttl=19283784), dns.Record_A('127.0.0.1', ttl=19283784)]
     306        [dns.Record_CNAME('test-domain.com', ttl=19283784),
     307         dns.Record_A('127.0.0.1', ttl=19283784)]
    290308    )
    291309
    292310    def testMB(self):
     
    317335        """Test DNS 'MINFO' record queries"""
    318336        return self.namesTest(
    319337            self.resolver.lookupMailboxInfo('test-domain.com'),
    320             [dns.Record_MINFO(rmailbx='r mail box', emailbx='e mail box', ttl=19283784)]
     338            [dns.Record_MINFO(rmailbx='r mail box', emailbx='e mail box',
     339                              ttl=19283784)]
    321340        )
    322341
    323342
     
    325344        """Test DNS 'SRV' record queries"""
    326345        return self.namesTest(
    327346            self.resolver.lookupService('http.tcp.test-domain.com'),
    328             [dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl=19283784)]
     347            [dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool',
     348                            ttl=19283784)]
    329349        )
    330350
    331351    def testAFSDB(self):
    332352        """Test DNS 'AFSDB' record queries"""
    333353        return self.namesTest(
    334354            self.resolver.lookupAFSDatabase('test-domain.com'),
    335             [dns.Record_AFSDB(subtype=1, hostname='afsdb.test-domain.com', ttl=19283784)]
     355            [dns.Record_AFSDB(subtype=1, hostname='afsdb.test-domain.com',
     356                              ttl=19283784)]
    336357        )
    337358
    338359
     
    340361        """Test DNS 'RP' record queries"""
    341362        return self.namesTest(
    342363            self.resolver.lookupResponsibility('test-domain.com'),
    343             [dns.Record_RP(mbox='whatever.i.dunno', txt='some.more.text', ttl=19283784)]
     364            [dns.Record_RP(mbox='whatever.i.dunno', txt='some.more.text',
     365                           ttl=19283784)]
    344366        )
    345367
    346368
     
    348370        """Test DNS 'TXT' record queries"""
    349371        return self.namesTest(
    350372            self.resolver.lookupText('test-domain.com'),
    351             [dns.Record_TXT('A First piece of Text', 'a SecoNd piece', ttl=19283784),
    352              dns.Record_TXT('Some more text, haha!  Yes.  \0  Still here?', ttl=19283784)]
     373            [dns.Record_TXT('A First piece of Text', 'a SecoNd piece',
     374                            ttl=19283784),
     375             dns.Record_TXT('Some more text, haha!  Yes.  \0  Still here?',
     376                            ttl=19283784)]
    353377        )
    354378
    355379
     
    359383        """
    360384        return self.namesTest(
    361385            self.resolver.lookupSenderPolicy('test-domain.com'),
    362             [dns.Record_SPF('v=spf1 mx/30 mx:example.org/30 -all', ttl=19283784),
    363             dns.Record_SPF('v=spf1 +mx a:\0colo', '.example.com/28 -all not valid', ttl=19283784)]
     386            [dns.Record_SPF('v=spf1 mx/30 mx:example.org/30 -all',
     387                            ttl=19283784),
     388            dns.Record_SPF('v=spf1 +mx a:\0colo',
     389                           '.example.com/28 -all not valid',
     390                           ttl=19283784)]
    364391        )
    365392
    366393
     
    368395        """Test DNS 'WKS' record queries"""
    369396        return self.namesTest(
    370397            self.resolver.lookupWellKnownServices('test-domain.com'),
    371             [dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP, '\x12\x01\x16\xfe\xc1\x00\x01', ttl=19283784)]
     398            [dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP,
     399                            '\x12\x01\x16\xfe\xc1\x00\x01', ttl=19283784)]
    372400        )
    373401
    374402
     
    381409             dns.Record_A('1.2.3.4', ttl='1S'),
    382410             dns.Record_NS('ns1.domain', ttl='2M'),
    383411             dns.Record_NS('ns2.domain', ttl='3H'),
    384              dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl='4D')]
     412             dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool',
     413                            ttl='4D')]
    385414            )
    386415
    387416
     
    389418        """Test DNS 'AAAA' record queries (IPv6)"""
    390419        return self.namesTest(
    391420            self.resolver.lookupIPV6Address('test-domain.com'),
    392             [dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF', ttl=19283784)]
     421            [dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF',
     422                             ttl=19283784)]
    393423        )
    394424
    395425    def testA6(self):
     
    398428            self.resolver.lookupAddress6('test-domain.com'),
    399429            [dns.Record_A6(0, 'ABCD::4321', '', ttl=19283784),
    400430             dns.Record_A6(12, '0:0069::0', 'some.network.tld', ttl=19283784),
    401              dns.Record_A6(8, '0:5634:1294:AFCB:56AC:48EF:34C3:01FF', 'tra.la.la.net', ttl=19283784)]
     431             dns.Record_A6(8, '0:5634:1294:AFCB:56AC:48EF:34C3:01FF',
     432                           'tra.la.la.net', ttl=19283784)]
    402433         )
    403434
    404435
     
    407438        Test DNS 'AXFR' queries (Zone transfer)
    408439        """
    409440        default_ttl = soa_record.expire
    410         results = [copy.copy(r) for r in reduce(operator.add, test_domain_com.records.values())]
     441        results = [copy.copy(r)
     442                   for r in reduce(operator.add,
     443                                   test_domain_com.records.values())]
    411444        for r in results:
    412445            if r.ttl is None:
    413446                r.ttl = default_ttl
     
    435468                              "!^.*$!sip:information@domain.tld!",
    436469                              ttl=19283784)])
    437470
     471    def test_DNSKEY(self):
     472        """
     473        Test DNS 'DNSKEY' record queries.
     474       
     475        @since: 12.1
     476        """
     477        return self.namesTest(
     478            self.resolver.lookupDNSKey('test-domain.com'),
     479            [dns.Record_DNSKEY(0x10, 3, 5,
     480                               "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9"
     481                               "H4T/K+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5"
     482                               "Z/+0/Q845Kya8Lmkp/ikJVe/9id2TC2hoffpZ9"
     483                               "pbZRjIeBTAvdTboGmGuqG/ljnDLJrJpoF6g8g6"
     484                               "fHR9eekIWis8LJ55Y1k=",
     485                               ttl=19283784)])
    438486
    439 
    440487class DNSServerFactoryTests(unittest.TestCase):
    441488    """
    442489    Tests for L{server.DNSServerFactory}.
     
    523570        self.d.addCallback(self._gotResults)
    524571        self.controller = client.AXFRController('fooby.com', self.d)
    525572
    526         self.soa = dns.RRHeader(name='fooby.com', type=dns.SOA, cls=dns.IN, ttl=86400, auth=False,
     573        self.soa = dns.RRHeader(name='fooby.com', type=dns.SOA, cls=dns.IN,
     574                                ttl=86400, auth=False,
    527575                                payload=dns.Record_SOA(mname='fooby.com',
    528576                                                       rname='hooj.fooby.com',
    529577                                                       serial=100,
     
    535583
    536584        self.records = [
    537585            self.soa,
    538             dns.RRHeader(name='fooby.com', type=dns.NS, cls=dns.IN, ttl=700, auth=False,
    539                          payload=dns.Record_NS(name='ns.twistedmatrix.com', ttl=700)),
     586            dns.RRHeader(name='fooby.com', type=dns.NS, cls=dns.IN, ttl=700,
     587                         auth=False,
     588                         payload=dns.Record_NS(name='ns.twistedmatrix.com',
     589                                               ttl=700)),
    540590
    541             dns.RRHeader(name='fooby.com', type=dns.MX, cls=dns.IN, ttl=700, auth=False,
    542                          payload=dns.Record_MX(preference=10, exchange='mail.mv3d.com', ttl=700)),
     591            dns.RRHeader(name='fooby.com', type=dns.MX, cls=dns.IN, ttl=700,
     592                         auth=False,
     593                         payload=dns.Record_MX(preference=10,
     594                                               exchange='mail.mv3d.com',
     595                                               ttl=700)),
    543596
    544             dns.RRHeader(name='fooby.com', type=dns.A, cls=dns.IN, ttl=700, auth=False,
    545                          payload=dns.Record_A(address='64.123.27.105', ttl=700)),
     597            dns.RRHeader(name='fooby.com', type=dns.A, cls=dns.IN,
     598                         ttl=700, auth=False,
     599                         payload=dns.Record_A(address='64.123.27.105',
     600                                              ttl=700)),
    546601            self.soa
    547602            ]
    548603
    549604    def _makeMessage(self):
    550605        # hooray they all have the same message format
    551         return dns.Message(id=999, answer=1, opCode=0, recDes=0, recAv=1, auth=1, rCode=0, trunc=0, maxSize=0)
     606        return dns.Message(id=999, answer=1, opCode=0, recDes=0, recAv=1,
     607                           auth=1, rCode=0, trunc=0, maxSize=0)
    552608
    553609    def testBindAndTNamesStyle(self):
    554610        # Bind style = One big single message
     
    890946        self.assertEqual(service.domains[1].domain, 'example.edu')
    891947
    892948
    893 
    894949class SecondaryAuthorityTests(unittest.TestCase):
    895950    """
    896951    L{twisted.names.secondary.SecondaryAuthority} correctly constructs objects
  • twisted/names/test/test_ser_num_arith.py

     
     1# Copyright (c) Twisted Matrix Laboratories.
     2# See LICENSE for details.
     3
     4"""
     5Test cases for L{twisted.names.ser_num_arith}.
     6
     7@since: 12.1
     8"""
     9
     10from twisted.names.ser_num_arith import SNA, DateSNA
     11from twisted.names.ser_num_arith import max as sna_max
     12from twisted.trial import unittest
     13
     14class SNATest(unittest.TestCase):
     15
     16    def setUp(self):
     17        self.s1 = SNA(1)
     18        self.s1a = SNA(1)
     19        self.s2 = SNA(2)
     20        self.sMaxVal = SNA(SNA.HLFRNG+SNA.HLFRNG-1)
     21
     22    def test_equality(self):
     23        """
     24        Test SNA equality
     25        """
     26        self.assertEqual(self.s1, self.s1a)
     27        self.assertNotIdentical(self.s1, self.s1a)
     28        self.assertEqual(hash(self.s1), hash(self.s1a))
     29        self.assertNotEqual(hash(self.s1), hash(self.s2))
     30
     31    def test_le(self):
     32        """
     33        Test SNA less than or equal
     34        """
     35        self.assertTrue(self.s1 <= self.s1)
     36        self.assertTrue(self.s1 <= self.s1a)
     37        self.assertTrue(self.s1 <= self.s2)
     38        self.assertFalse(self.s2 <= self.s1)
     39
     40    def test_ge(self):
     41        """
     42        Test SNA greater than or equal
     43        """
     44        self.assertTrue(self.s1 >= self.s1)
     45        self.assertTrue(self.s1 >= self.s1a)
     46        self.assertFalse(self.s1 >= self.s2)
     47        self.assertTrue(self.s2 >= self.s1)
     48
     49
     50    def test_lt(self):
     51        """
     52        Test SNA less than
     53        """
     54        self.assertFalse(self.s1 < self.s1)
     55        self.assertFalse(self.s1 < self.s1a)
     56        self.assertTrue(self.s1 < self.s2)
     57        self.assertFalse(self.s2 < self.s1)
     58
     59    def test_gt(self):
     60        """
     61        Test SNA greater than
     62        """
     63        self.assertFalse(self.s1 > self.s1)
     64        self.assertFalse(self.s1 > self.s1a)
     65        self.assertFalse(self.s1 > self.s2)
     66        self.assertTrue(self.s2 > self.s1)
     67
     68    def test_add(self):
     69        """
     70        Test SNA addition
     71        """
     72        self.assertEqual(self.s1 + self.s1, self.s2)
     73        self.assertEqual(self.s1 + SNA(SNA.MAXADD), SNA(SNA.MAXADD + 1))
     74        self.assertEqual(SNA(SNA.MAXADD) + SNA(SNA.MAXADD) + SNA(2), SNA(0))
     75
     76    def test_maxval(self):
     77        """
     78        Test SNA maxval
     79        """
     80        smaxplus1 = self.sMaxVal + self.s1
     81        self.assertTrue(smaxplus1 > self.sMaxVal)
     82        self.assertEqual(smaxplus1, SNA(0))
     83
     84    def test_max(self):
     85        """
     86        Test the SNA max function
     87        """
     88        self.assertEqual(sna_max([None, self.s1]), self.s1)
     89        self.assertEqual(sna_max([self.s1, None]), self.s1)
     90        self.assertEqual(sna_max([self.s1, self.s1a]), self.s1)
     91        self.assertEqual(sna_max([self.s2, self.s1a, self.s1, None]), self.s2)
     92        self.assertEqual(sna_max([SNA(SNA.MAXADD), self.s2, self.s1a, self.s1, None]),
     93                          SNA(SNA.MAXADD))
     94        self.assertEqual(sna_max([self.s2, self.s1a, self.s1, None, self.sMaxVal]),
     95                          self.s2)
     96
     97    def test_dateSNA(self):
     98        """
     99        Test DateSNA construction and comparison
     100        """
     101        date1 = DateSNA('20120101000000')
     102        date2 = DateSNA('20130101000000')
     103        self.assertTrue(date1 < date2)
     104
     105    def test_dateAdd(self):
     106        """
     107        Test DateSNA addition
     108        """
     109        date3 = DateSNA('20370101000000')
     110        sna1  = SNA(365*24*60*60)
     111        date4 = date3 + sna1
     112        self.assertEqual(date4.asInt(),  date3.asInt() + sna1.asInt())
     113
     114    def test_asDate(self):
     115        """
     116        Test DateSNA conversion
     117        """
     118        date1 = '20120101000000'
     119        date1Sna = DateSNA(date1)
     120        self.assertEqual(date1Sna.asDate(), date1)
     121
     122    def test_roundTrip(self):
     123        """
     124        Test DateSNA conversion
     125        """
     126        date1 = '20370101000000'
     127        date1Sna = DateSNA(date1)
     128        intval = date1Sna.asInt()
     129        sna1a = SNA(intval)
     130
     131        dateSna1a = DateSNA.fromSNA(sna1a)
     132        self.assertEqual(date1Sna, dateSna1a)
     133
     134        dateSna2 = DateSNA.fromInt(intval)
     135        self.assertEqual(date1Sna, dateSna2)
  • twisted/names/topfiles/5454.feature

     
     1Features
     2--------
     3 - The main features introduced by this change are EDNS0 and support
     4   for DNSSEC. Enabling EDNS0 adds to a query an OPT record that carries
     5   two pieces of information. The first is the maximum size UDP packet
     6   that the network can handle (see RFC2671.) The second is the DNSSEC
     7   OK (DO) bit which tells an upstream resolver that the querying resolver
     8   is able to accept DNSSEC security data (See RFC3225.) Although this
     9   change fully implements EDNS0, it only enables twisted.names to
     10   ask for DNSSEC data and pass that data to the user. The user may
     11   check the Authentic Data (AD) bit in the message header to determine
     12   if the upstream resolver validates the data. (See RFC 4035, Section
     13   4.9.3 regarding only using the AD bit from a trusted resolver
     14   on a secure channel.)
     15 - Processing DNSSEC data and validating DNS answers requires doing
     16   cryptographic processing using a library such as openssl. This may
     17   be part of a future feature set but is not in the current feature.
     18 - The new class twisted.names.common.DnssecConfig allows configuring
     19   DNSSEC and EDNS0 parameters, as well as the recursion desired DNS
     20   parameter, for a resolver. DnssecConfig is an optional input argument
     21   to ResolverBase and is implemented for client.Resolver and
     22   secondaryAuthority.Resolver
     23 - ResolverBase now has new lookup methods for lookupDNSKey, lookupDS,
     24   lookupNSEC, lookupNSEC3, lookupNSEC3Param and lookupRRSIG records.
     25 - twisted.names.client.Resolver now returns a 4-tuple to a query when
     26   configured with dnssecOk. The 4-tuple includes a new reference
     27   to the message so that the caller can obtain the AD flag (and
     28   any of the other flags) in the message.
     29 - twisted.names.client now has new lookup methods for the six new
     30   DNSSEC record types.
     31 - twisted.names.dns now supports six new DNS record types - DNSKey,
     32   DS, NSEC, NSEC3, NSEC3Param and RRSIG.
     33 - The new twisted.names.dns.Sigstr class encodes/decodes signatures and keys.
     34 - The new twisted.names.dns.TypeBitmaps class encodes/decoes the type bitmap
     35   field in an NSEC3 resource record.
     36 - The new OptHeader and Record_OPT classes support the EDNS and DNSSEC
     37   options.
     38 - twisted.names.dns now has new classes added for the six DNSSEC record types.
     39 - The dns.Message class now has new AD and CD flags for AuthenticData and Checking Disabled.
     40 - twisted.names.dns now adds the OPT record to EDNS queries.
     41 - twisted.names.dns.Message.startListening now listens for larger UDP
     42   packets, as set by maxUdpPktSz.
     43 - The new twisted.names.ser_num_arith class implements DNS Serial Number
     44   Arithmetic (SNA) and Date SNA for handling numbers and dates that
     45   roll through a 32-bit integer.
     46 - New tests are added for all new features.
     47 No newline at end of file