Exemple #1
0
def get_algorithm(algorithm):
    """Returns the wire format string and the hash module to use for the
    specified TSIG algorithm

    @rtype: (string, hash constructor)
    @raises NotImplementedError: I{algorithm} is not supported
    """

    global _hashes
    if _hashes is None:
        _setup_hashes()

    if isinstance(algorithm, (str, unicode)):
        algorithm = name.from_text(algorithm)

    if sys.hexversion < 0x02050200 and \
       (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512):
        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
                                  " requires Python 2.5.2 or later")

    try:
        return (algorithm.to_digestable(), _hashes[algorithm])
    except KeyError:
        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
                                  " is not supported")
Exemple #2
0
    def _question_line(self, section):
        """Process one line from the text format question section."""

        token = self.tok.get(want_leading=True)
        if not token.is_whitespace():
            self.last_name = mname.from_text(token.value, None)
        name = self.last_name
        token = self.tok.get()
        if not token.is_identifier():
            raise exception.SyntaxError
        # Class
        try:
            rdclass = rdataclass.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            rdclass = rdataclass.IN
        # Type
        rdtype = rdatatype.from_text(token.value)
        self.message.find_rrset(self.message.question,
                                name,
                                rdclass,
                                rdtype,
                                create=True,
                                force_unique=True)
        if self.updating:
            self.zone_rdclass = rdclass
        self.tok.get_eol()
Exemple #3
0
    def _question_line(self, section):
        """Process one line from the text format question section."""

        token = self.tok.get(want_leading = True)
        if not token.is_whitespace():
            self.last_name = mname.from_text(token.value, None)
        name = self.last_name
        token = self.tok.get()
        if not token.is_identifier():
            raise exception.SyntaxError
        # Class
        try:
            rdclass = rdataclass.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            rdclass = rdataclass.IN
        # Type
        rdtype = rdatatype.from_text(token.value)
        self.message.find_rrset(self.message.question, name,
                                rdclass, rdtype, create=True,
                                force_unique=True)
        if self.updating:
            self.zone_rdclass = rdclass
        self.tok.get_eol()
Exemple #4
0
    def present(self, name, *args):
        """Require that an owner name (and optionally an rdata type,
        or specific rdataset) exists as a prerequisite to the
        execution of the update.  The first argument is always a name.
        The other arguments can be:

                - rdataset...

                - rdata...

                - rdtype, string..."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if len(args) == 0:
            rrset = self.find_rrset(self.answer, name,
                                    rdataclass.ANY, rdatatype.ANY,
                                    rdatatype.NONE, None,
                                    True, True)
        elif isinstance(args[0], rdataset.Rdataset) or \
             isinstance(args[0], rdata.Rdata) or \
             len(args) > 1:
            if not isinstance(args[0], rdataset.Rdataset):
                # Add a 0 TTL
                args = list(args)
                args.insert(0, 0)
            self._add(False, self.answer, name, *args)
        else:
            rdtype = args[0]
            if isinstance(rdtype, (str, unicode)):
                rdtype = rdatatype.from_text(rdtype)
            rrset = self.find_rrset(self.answer, name,
                                    rdataclass.ANY, rdtype,
                                    rdatatype.NONE, None,
                                    True, True)
Exemple #5
0
def zone_for_name(name, rdclass=rdataclass.IN, tcp=False, resolver=None):
    """Find the name of the zone which contains the specified name.

    @param name: the query name
    @type name: absolute name.Name object or string
    @param rdclass: The query class
    @type rdclass: int
    @param tcp: use TCP to make the query (default is False).
    @type tcp: bool
    @param resolver: the resolver to use
    @type resolver: resolver.Resolver object or None
    @rtype: name.Name"""

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, name.root)
    if resolver is None:
        resolver = get_default_resolver()
    if not name.is_absolute():
        raise NotAbsolute(name)
    while 1:
        try:
            answer = resolver.query(name, rdatatype.SOA, rdclass, tcp)
            if answer.rrset.name == name:
                return name
            # otherwise we were CNAMEd or DNAMEd and need to look higher
        except (resolver.NXDOMAIN, resolver.NoAnswer):
            pass
        try:
            name = name.parent()
        except name.NoParent:
            raise NoRootSOA
Exemple #6
0
    def present(self, name, *args):
        """Require that an owner name (and optionally an rdata type,
        or specific rdataset) exists as a prerequisite to the
        execution of the update.  The first argument is always a name.
        The other arguments can be:

                - rdataset...

                - rdata...

                - rdtype, string..."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if len(args) == 0:
            rrset = self.find_rrset(self.answer, name, rdataclass.ANY,
                                    rdatatype.ANY, rdatatype.NONE, None, True,
                                    True)
        elif isinstance(args[0], rdataset.Rdataset) or \
             isinstance(args[0], rdata.Rdata) or \
             len(args) > 1:
            if not isinstance(args[0], rdataset.Rdataset):
                # Add a 0 TTL
                args = list(args)
                args.insert(0, 0)
            self._add(False, self.answer, name, *args)
        else:
            rdtype = args[0]
            if isinstance(rdtype, (str, unicode)):
                rdtype = rdatatype.from_text(rdtype)
            rrset = self.find_rrset(self.answer, name, rdataclass.ANY, rdtype,
                                    rdatatype.NONE, None, True, True)
Exemple #7
0
def get_algorithm(algorithm):
    """Returns the wire format string and the hash module to use for the
    specified TSIG algorithm

    @rtype: (string, hash constructor)
    @raises NotImplementedError: I{algorithm} is not supported
    """

    global _hashes
    if _hashes is None:
        _setup_hashes()

    if isinstance(algorithm, (str, unicode)):
        algorithm = name.from_text(algorithm)

    if sys.hexversion < 0x02050200 and \
       (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512):
        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
                                  " requires Python 2.5.2 or later")

    try:
        return (algorithm.to_digestable(), _hashes[algorithm])
    except KeyError:
        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
                                  " is not supported")
Exemple #8
0
def make_query(qname,
               rdtype,
               rdclass=rdataclass.IN,
               use_edns=None,
               want_dnssec=False,
               ednsflags=0,
               payload=1280,
               request_payload=None,
               options=None):
    """Make a query message.

    The query name, type, and class may all be specified either
    as objects of the appropriate type, or as strings.

    The query will have a randomly choosen query id, and its DNS flags
    will be set to flags.RD.

    @param qname: The query name.
    @type qname: mname.Name object or string
    @param rdtype: The desired rdata type.
    @type rdtype: int
    @param rdclass: The desired rdata class; the default is class IN.
    @type rdclass: int
    @param use_edns: The EDNS level to use; the default is None (no EDNS).
    See the description of message.Message.use_edns() for the possible
    values for use_edns and their meanings.
    @type use_edns: int or bool or None
    @param want_dnssec: Should the query indicate that DNSSEC is desired?
    @type want_dnssec: bool
    @param ednsflags: EDNS flag values.
    @type ednsflags: int
    @param payload: The EDNS sender's payload field, which is the maximum
    size of UDP datagram the sender can handle.
    @type payload: int
    @param request_payload: The EDNS payload size to use when sending
    this message.  If not specified, defaults to the value of payload.
    @type request_payload: int or None
    @param options: The EDNS options
    @type options: None or list of edns.Option objects
    @see: RFC 2671
    @rtype: message.Message object"""

    if isinstance(qname, (str, unicode)):
        qname = mname.from_text(qname)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    if isinstance(rdclass, (str, unicode)):
        rdclass = rdataclass.from_text(rdclass)
    m = Message()
    m.flags |= flags.RD
    m.find_rrset(m.question,
                 qname,
                 rdclass,
                 rdtype,
                 create=True,
                 force_unique=True)
    m.use_edns(use_edns, ednsflags, payload, request_payload, options)
    m.want_dnssec(want_dnssec)
    return m
Exemple #9
0
 def _config_win32_search(self, search):
     """Configure a Search registry entry."""
     # we call str() on search to convert it from unicode to ascii
     search = str(search)
     split_char = self._determine_split_char(search)
     search_list = search.split(split_char)
     for s in search_list:
         if not s in self.search:
             self.search.append(name.from_text(s))
Exemple #10
0
    def get_name(self, origin=None):
        """Read the next token and interpret it as a DNS name.

        @raises exception.SyntaxError:
        @rtype: name.Name object"""

        token = self.get()
        if not token.is_identifier():
            raise exception.SyntaxError('expecting an identifier')
        return name.from_text(token.value, origin)
Exemple #11
0
def from_text(textring):
    """Convert a dictionary containing (textual DNS name, base64 secret) pairs
    into a binary keyring which has (name.Name, binary secret) pairs.
    @rtype: dict"""
    
    keyring = {}
    for keytext in textring:
        keyname = name.from_text(keytext)
        secret = base64.decodestring(textring[keytext])
        keyring[keyname] = secret
    return keyring
Exemple #12
0
 def _validate_name(self, name):
     if isinstance(name, (str, unicode)):
         name = name.from_text(name, None)
     elif not isinstance(name, name.Name):
         raise KeyError("name parameter must be convertable to a DNS name")
     if name.is_absolute():
         if not name.is_subdomain(self.origin):
             raise KeyError("name parameter must be a subdomain of the zone origin")
         if self.relativize:
             name = name.relativize(self.origin)
     return name
Exemple #13
0
 def _validate_name(self, name):
     if isinstance(name, (str, unicode)):
         name = name.from_text(name, None)
     elif not isinstance(name, name.Name):
         raise KeyError("name parameter must be convertable to a DNS name")
     if name.is_absolute():
         if not name.is_subdomain(self.origin):
             raise KeyError(
                 "name parameter must be a subdomain of the zone origin")
         if self.relativize:
             name = name.relativize(self.origin)
     return name
Exemple #14
0
    def _rr_line(self, section):
        """Process one line from the text format answer, authority, or
        additional data sections.
        """

        deleting = None
        # Name
        token = self.tok.get(want_leading = True)
        if not token.is_whitespace():
            self.last_name = mname.from_text(token.value, None)
        name = self.last_name
        token = self.tok.get()
        if not token.is_identifier():
            raise exception.SyntaxError
        # TTL
        try:
            ttl = int(token.value, 0)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            ttl = 0
        # Class
        try:
            rdclass = rdataclass.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
            if rdclass == rdataclass.ANY or rdclass == rdataclass.NONE:
                deleting = rdclass
                rdclass = self.zone_rdclass
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            rdclass = rdataclass.IN
        # Type
        rdtype = rdatatype.from_text(token.value)
        token = self.tok.get()
        if not token.is_eol_or_eof():
            self.tok.unget(token)
            rd = rdata.from_text(rdclass, rdtype, self.tok, None)
            covers = rd.covers()
        else:
            rd = None
            covers = rdatatype.NONE
        rrset = self.message.find_rrset(section, name,
                                        rdclass, rdtype, covers,
                                        deleting, True, self.updating)
        if not rd is None:
            rrset.add(rd, ttl)
Exemple #15
0
def from_e164(text, origin=public_enum_domain):
    """Convert an E.164 number in textual form into a Name object whose
    value is the ENUM domain name for that number.
    @param text: an E.164 number in textual form.
    @type text: str
    @param origin: The domain in which the number should be constructed.
    The default is e164.arpa.
    @type origin: name.Name object or None
    @rtype: name.Name object
    """
    parts = [d for d in text if d.isdigit()]
    parts.reverse()
    return name.from_text('.'.join(parts), origin=origin)
Exemple #16
0
    def _rr_line(self, section):
        """Process one line from the text format answer, authority, or
        additional data sections.
        """

        deleting = None
        # Name
        token = self.tok.get(want_leading=True)
        if not token.is_whitespace():
            self.last_name = mname.from_text(token.value, None)
        name = self.last_name
        token = self.tok.get()
        if not token.is_identifier():
            raise exception.SyntaxError
        # TTL
        try:
            ttl = int(token.value, 0)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            ttl = 0
        # Class
        try:
            rdclass = rdataclass.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
            if rdclass == rdataclass.ANY or rdclass == rdataclass.NONE:
                deleting = rdclass
                rdclass = self.zone_rdclass
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            rdclass = rdataclass.IN
        # Type
        rdtype = rdatatype.from_text(token.value)
        token = self.tok.get()
        if not token.is_eol_or_eof():
            self.tok.unget(token)
            rd = rdata.from_text(rdclass, rdtype, self.tok, None)
            covers = rd.covers()
        else:
            rd = None
            covers = rdatatype.NONE
        rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers,
                                        deleting, True, self.updating)
        if not rd is None:
            rrset.add(rd, ttl)
Exemple #17
0
 def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone,
              allow_include=False, check_origin=True):
     if isinstance(origin, (str, unicode)):
         origin = name.from_text(origin)
     self.tok = tok
     self.current_origin = origin
     self.relativize = relativize
     self.ttl = 0
     self.last_name = self.current_origin
     self.zone = zone_factory(origin, rdclass, relativize=relativize)
     self.saved_state = []
     self.current_file = None
     self.allow_include = allow_include
     self.check_origin = check_origin
Exemple #18
0
 def read_resolv_conf(self, f):
     """Process f as a file in the /etc/resolv.conf format.  If f is
     a string, it is used as the name of the file to open; otherwise it
     is treated as the file itself."""
     if isinstance(f, str) or isinstance(f, unicode):
         try:
             f = open(f, 'r')
         except IOError:
             # /etc/resolv.conf doesn't exist, can't be read, etc.
             # We'll just use the default resolver configuration.
             self.nameservers = ['127.0.0.1']
             return
         want_close = True
     else:
         want_close = False
     try:
         for l in f:
             if len(l) == 0 or l[0] == '#' or l[0] == ';':
                 continue
             tokens = l.split()
             if len(tokens) == 0:
                 continue
             if tokens[0] == 'nameserver':
                 self.nameservers.append(tokens[1])
             elif tokens[0] == 'domain':
                 self.domain = name.from_text(tokens[1])
             elif tokens[0] == 'search':
                 for suffix in tokens[1:]:
                     self.search.append(name.from_text(suffix))
             elif tokens[0] == 'options':
                 if 'rotate' in tokens[1:]:
                     self.rotate = True
     finally:
         if want_close:
             f.close()
     if len(self.nameservers) == 0:
         self.nameservers.append('127.0.0.1')
Exemple #19
0
def make_query(qname, rdtype, rdclass = rdataclass.IN, use_edns=None,
               want_dnssec=False, ednsflags=0, payload=1280,
               request_payload=None, options=None):
    """Make a query message.

    The query name, type, and class may all be specified either
    as objects of the appropriate type, or as strings.

    The query will have a randomly choosen query id, and its DNS flags
    will be set to flags.RD.

    @param qname: The query name.
    @type qname: mname.Name object or string
    @param rdtype: The desired rdata type.
    @type rdtype: int
    @param rdclass: The desired rdata class; the default is class IN.
    @type rdclass: int
    @param use_edns: The EDNS level to use; the default is None (no EDNS).
    See the description of message.Message.use_edns() for the possible
    values for use_edns and their meanings.
    @type use_edns: int or bool or None
    @param want_dnssec: Should the query indicate that DNSSEC is desired?
    @type want_dnssec: bool
    @param ednsflags: EDNS flag values.
    @type ednsflags: int
    @param payload: The EDNS sender's payload field, which is the maximum
    size of UDP datagram the sender can handle.
    @type payload: int
    @param request_payload: The EDNS payload size to use when sending
    this message.  If not specified, defaults to the value of payload.
    @type request_payload: int or None
    @param options: The EDNS options
    @type options: None or list of edns.Option objects
    @see: RFC 2671
    @rtype: message.Message object"""

    if isinstance(qname, (str, unicode)):
        qname = mname.from_text(qname)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    if isinstance(rdclass, (str, unicode)):
        rdclass = rdataclass.from_text(rdclass)
    m = Message()
    m.flags |= flags.RD
    m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
                 force_unique=True)
    m.use_edns(use_edns, ednsflags, payload, request_payload, options)
    m.want_dnssec(want_dnssec)
    return m
Exemple #20
0
    def absent(self, name, rdtype=None):
        """Require that an owner name (and optionally an rdata type) does
        not exist as a prerequisite to the execution of the update."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if rdtype is None:
            rrset = self.find_rrset(self.answer, name, rdataclass.NONE,
                                    rdatatype.ANY, rdatatype.NONE, None, True,
                                    True)
        else:
            if isinstance(rdtype, (str, unicode)):
                rdtype = rdatatype.from_text(rdtype)
            rrset = self.find_rrset(self.answer, name, rdataclass.NONE, rdtype,
                                    rdatatype.NONE, None, True, True)
Exemple #21
0
def query(number, domains, resolver=None):
    """Look for NAPTR RRs for the specified number in the specified domains.

    e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.'])
    """
    if resolver is None:
        resolver = resolver.get_default_resolver()
    for domain in domains:
        if isinstance(domain, (str, unicode)):
            domain = name.from_text(domain)
        qname = e164.from_e164(number, domain)
        try:
            return resolver.query(qname, 'NAPTR')
        except resolver.NXDOMAIN:
            pass
    raise resolver.NXDOMAIN
Exemple #22
0
    def use_tsig(self,
                 keyring,
                 keyname=None,
                 fudge=300,
                 original_id=None,
                 tsig_error=0,
                 other_data='',
                 algorithm=tsig.default_algorithm):
        """When sending, a TSIG signature using the specified keyring
        and keyname should be added.

        @param keyring: The TSIG keyring to use; defaults to None.
        @type keyring: dict
        @param keyname: The name of the TSIG key to use; defaults to None.
        The key must be defined in the keyring.  If a keyring is specified
        but a keyname is not, then the key used will be the first key in the
        keyring.  Note that the order of keys in a dictionary is not defined,
        so applications should supply a keyname when a keyring is used, unless
        they know the keyring contains only one key.
        @type keyname: mname.Name or string
        @param fudge: TSIG time fudge; default is 300 seconds.
        @type fudge: int
        @param original_id: TSIG original id; defaults to the message's id
        @type original_id: int
        @param tsig_error: TSIG error code; default is 0.
        @type tsig_error: int
        @param other_data: TSIG other data.
        @type other_data: string
        @param algorithm: The TSIG algorithm to use; defaults to
        tsig.default_algorithm
        """

        self.keyring = keyring
        if keyname is None:
            self.keyname = self.keyring.keys()[0]
        else:
            if isinstance(keyname, (str, unicode)):
                keyname = mname.from_text(keyname)
            self.keyname = keyname
        self.keyalgorithm = algorithm
        self.fudge = fudge
        if original_id is None:
            self.original_id = self.id
        else:
            self.original_id = original_id
        self.tsig_error = tsig_error
        self.other_data = other_data
Exemple #23
0
    def __init__(self,
                 zone,
                 rdclass=rdataclass.IN,
                 keyring=None,
                 keyname=None,
                 keyalgorithm=tsig.default_algorithm):
        """Initialize a new DNS Update object.

        @param zone: The zone which is being updated.
        @type zone: A name.Name or string
        @param rdclass: The class of the zone; defaults to rdataclass.IN.
        @type rdclass: An int designating the class, or a string whose value
        is the name of a class.
        @param keyring: The TSIG keyring to use; defaults to None.
        @type keyring: dict
        @param keyname: The name of the TSIG key to use; defaults to None.
        The key must be defined in the keyring.  If a keyring is specified
        but a keyname is not, then the key used will be the first key in the
        keyring.  Note that the order of keys in a dictionary is not defined,
        so applications should supply a keyname when a keyring is used, unless
        they know the keyring contains only one key.
        @type keyname: name.Name or string
        @param keyalgorithm: The TSIG algorithm to use; defaults to
        tsig.default_algorithm.  Constants for TSIG algorithms are defined
        in tsig, and the currently implemented algorithms are
        HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
        HMAC_SHA512.
        @type keyalgorithm: string
        """
        super(Update, self).__init__()
        self.flags |= opcode.to_flags(opcode.UPDATE)
        if isinstance(zone, (str, unicode)):
            zone = name.from_text(zone)
        self.origin = zone
        if isinstance(rdclass, str):
            rdclass = rdataclass.from_text(rdclass)
        self.zone_rdclass = rdclass
        self.find_rrset(self.question,
                        self.origin,
                        rdclass,
                        rdatatype.SOA,
                        create=True,
                        force_unique=True)
        if not keyring is None:
            self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
Exemple #24
0
    def absent(self, name, rdtype=None):
        """Require that an owner name (and optionally an rdata type) does
        not exist as a prerequisite to the execution of the update."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if rdtype is None:
            rrset = self.find_rrset(self.answer, name,
                                    rdataclass.NONE, rdatatype.ANY,
                                    rdatatype.NONE, None,
                                    True, True)
        else:
            if isinstance(rdtype, (str, unicode)):
                rdtype = rdatatype.from_text(rdtype)
            rrset = self.find_rrset(self.answer, name,
                                    rdataclass.NONE, rdtype,
                                    rdatatype.NONE, None,
                                    True, True)
Exemple #25
0
def _validate(rrset, rrsigset, keys, origin=None, now=None):
    """Validate an RRset

    @param rrset: The RRset to validate
    @type rrset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param rrsigset: The signature RRset
    @type rrsigset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param keys: The key dictionary.
    @type keys: a dictionary keyed by name.Name with node or rdataset values
    @param origin: The origin to use for relative names
    @type origin: name.Name or None
    @param now: The time to use when validating the signatures.  The default
    is the current time.
    @type now: int
    """

    if isinstance(origin, (str, unicode)):
        origin = name.from_text(origin, name.root)

    if isinstance(rrset, tuple):
        rrname = rrset[0]
    else:
        rrname = rrset.name

    if isinstance(rrsigset, tuple):
        rrsigname = rrsigset[0]
        rrsigrdataset = rrsigset[1]
    else:
        rrsigname = rrsigset.name
        rrsigrdataset = rrsigset

    rrname = rrname.choose_relativity(origin)
    rrsigname = rrname.choose_relativity(origin)
    if rrname != rrsigname:
        raise ValidationFailure, "owner names do not match"

    for rrsig in rrsigrdataset:
        try:
            _validate_rrsig(rrset, rrsig, keys, origin, now)
            return
        except ValidationFailure, e:
            pass
Exemple #26
0
def _validate(rrset, rrsigset, keys, origin=None, now=None):
    """Validate an RRset

    @param rrset: The RRset to validate
    @type rrset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param rrsigset: The signature RRset
    @type rrsigset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param keys: The key dictionary.
    @type keys: a dictionary keyed by name.Name with node or rdataset values
    @param origin: The origin to use for relative names
    @type origin: name.Name or None
    @param now: The time to use when validating the signatures.  The default
    is the current time.
    @type now: int
    """

    if isinstance(origin, (str, unicode)):
        origin = name.from_text(origin, name.root)

    if isinstance(rrset, tuple):
        rrname = rrset[0]
    else:
        rrname = rrset.name

    if isinstance(rrsigset, tuple):
        rrsigname = rrsigset[0]
        rrsigrdataset = rrsigset[1]
    else:
        rrsigname = rrsigset.name
        rrsigrdataset = rrsigset

    rrname = rrname.choose_relativity(origin)
    rrsigname = rrname.choose_relativity(origin)
    if rrname != rrsigname:
        raise ValidationFailure, "owner names do not match"

    for rrsig in rrsigrdataset:
        try:
            _validate_rrsig(rrset, rrsig, keys, origin, now)
            return
        except ValidationFailure, e:
            pass
Exemple #27
0
def from_text_list(name, ttl, rdclass, rdtype, text_rdatas):
    """Create an RRset with the specified name, TTL, class, and type, and with
    the specified list of rdatas in text format.

    @rtype: rrset.RRset object
    """

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, None)
    if isinstance(rdclass, (str, unicode)):
        rdclass = rdataclass.from_text(rdclass)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    r = RRset(name, rdclass, rdtype)
    r.update_ttl(ttl)
    for t in text_rdatas:
        rd = rdata.from_text(r.rdclass, r.rdtype, t)
        r.add(rd)
    return r
Exemple #28
0
def make_ds(name, key, algorithm, origin=None):
    if algorithm.upper() == 'SHA1':
        dsalg = 1
        hash = hash.get('SHA1')()
    elif algorithm.upper() == 'SHA256':
        dsalg = 2
        hash = hash.get('SHA256')()
    else:
        raise UnsupportedAlgorithm, 'unsupported algorithm "%s"' % algorithm

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, origin)
    hash.update(name.canonicalize().to_wire())
    hash.update(_to_rdata(key, origin))
    digest = hash.digest()

    dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest
    return rdata.from_wire(rdataclass.IN, rdatatype.DS, dsrdata, 0,
                           len(dsrdata))
Exemple #29
0
def from_text_list(name, ttl, rdclass, rdtype, text_rdatas):
    """Create an RRset with the specified name, TTL, class, and type, and with
    the specified list of rdatas in text format.

    @rtype: rrset.RRset object
    """

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, None)
    if isinstance(rdclass, (str, unicode)):
        rdclass = rdataclass.from_text(rdclass)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    r = RRset(name, rdclass, rdtype)
    r.update_ttl(ttl)
    for t in text_rdatas:
        rd = rdata.from_text(r.rdclass, r.rdtype, t)
        r.add(rd)
    return r
Exemple #30
0
def make_ds(name, key, algorithm, origin=None):
    if algorithm.upper() == 'SHA1':
        dsalg = 1
        hash = hash.get('SHA1')()
    elif algorithm.upper() == 'SHA256':
        dsalg = 2
        hash = hash.get('SHA256')()
    else:
        raise UnsupportedAlgorithm, 'unsupported algorithm "%s"' % algorithm

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, origin)
    hash.update(name.canonicalize().to_wire())
    hash.update(_to_rdata(key, origin))
    digest = hash.digest()

    dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest
    return rdata.from_wire(rdataclass.IN, rdatatype.DS, dsrdata, 0,
                               len(dsrdata))
Exemple #31
0
    def delete(self, name, *args):
        """Delete records.  The first argument is always a name.  The other
        arguments can be:

                - I{nothing}

                - rdataset...

                - rdata...

                - rdtype, [string...]"""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if len(args) == 0:
            rrset = self.find_rrset(self.authority, name, rdataclass.ANY,
                                    rdatatype.ANY, rdatatype.NONE,
                                    rdatatype.ANY, True, True)
        elif isinstance(args[0], rdataset.Rdataset):
            for rds in args:
                for rd in rds:
                    self._add_rr(name, 0, rd, rdataclass.NONE)
        else:
            args = list(args)
            if isinstance(args[0], rdata.Rdata):
                for rd in args:
                    self._add_rr(name, 0, rd, rdataclass.NONE)
            else:
                rdtype = args.pop(0)
                if isinstance(rdtype, (str, unicode)):
                    rdtype = rdatatype.from_text(rdtype)
                if len(args) == 0:
                    rrset = self.find_rrset(self.authority, name,
                                            self.zone_rdclass, rdtype,
                                            rdatatype.NONE,
                                            rdataclass.ANY,
                                            True, True)
                else:
                    for s in args:
                        rd = rdata.from_text(self.zone_rdclass, rdtype, s,
                                                 self.origin)
                        self._add_rr(name, 0, rd, rdataclass.NONE)
Exemple #32
0
    def use_tsig(self, keyring, keyname=None, fudge=300,
                 original_id=None, tsig_error=0, other_data='',
                 algorithm=tsig.default_algorithm):
        """When sending, a TSIG signature using the specified keyring
        and keyname should be added.

        @param keyring: The TSIG keyring to use; defaults to None.
        @type keyring: dict
        @param keyname: The name of the TSIG key to use; defaults to None.
        The key must be defined in the keyring.  If a keyring is specified
        but a keyname is not, then the key used will be the first key in the
        keyring.  Note that the order of keys in a dictionary is not defined,
        so applications should supply a keyname when a keyring is used, unless
        they know the keyring contains only one key.
        @type keyname: mname.Name or string
        @param fudge: TSIG time fudge; default is 300 seconds.
        @type fudge: int
        @param original_id: TSIG original id; defaults to the message's id
        @type original_id: int
        @param tsig_error: TSIG error code; default is 0.
        @type tsig_error: int
        @param other_data: TSIG other data.
        @type other_data: string
        @param algorithm: The TSIG algorithm to use; defaults to
        tsig.default_algorithm
        """

        self.keyring = keyring
        if keyname is None:
            self.keyname = self.keyring.keys()[0]
        else:
            if isinstance(keyname, (str, unicode)):
                keyname = mname.from_text(keyname)
            self.keyname = keyname
        self.keyalgorithm = algorithm
        self.fudge = fudge
        if original_id is None:
            self.original_id = self.id
        else:
            self.original_id = original_id
        self.tsig_error = tsig_error
        self.other_data = other_data
Exemple #33
0
def from_rdata_list(name, ttl, rdatas):
    """Create an RRset with the specified name and TTL, and with
    the specified list of rdata objects.

    @rtype: rrset.RRset object
    """

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, None)

    if len(rdatas) == 0:
        raise ValueError("rdata list must not be empty")
    r = None
    for rd in rdatas:
        if r is None:
            r = RRset(name, rd.rdclass, rd.rdtype)
            r.update_ttl(ttl)
            first_time = False
        r.add(rd)
    return r
Exemple #34
0
def from_rdata_list(name, ttl, rdatas):
    """Create an RRset with the specified name and TTL, and with
    the specified list of rdata objects.

    @rtype: rrset.RRset object
    """

    if isinstance(name, (str, unicode)):
        name = name.from_text(name, None)

    if len(rdatas) == 0:
        raise ValueError("rdata list must not be empty")
    r = None
    for rd in rdatas:
        if r is None:
            r = RRset(name, rd.rdclass, rd.rdtype)
            r.update_ttl(ttl)
            first_time = False
        r.add(rd)
    return r
Exemple #35
0
 def __init__(self,
              tok,
              origin,
              rdclass,
              relativize,
              zone_factory=Zone,
              allow_include=False,
              check_origin=True):
     if isinstance(origin, (str, unicode)):
         origin = name.from_text(origin)
     self.tok = tok
     self.current_origin = origin
     self.relativize = relativize
     self.ttl = 0
     self.last_name = self.current_origin
     self.zone = zone_factory(origin, rdclass, relativize=relativize)
     self.saved_state = []
     self.current_file = None
     self.allow_include = allow_include
     self.check_origin = check_origin
Exemple #36
0
    def delete(self, name, *args):
        """Delete records.  The first argument is always a name.  The other
        arguments can be:

                - I{nothing}

                - rdataset...

                - rdata...

                - rdtype, [string...]"""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if len(args) == 0:
            rrset = self.find_rrset(self.authority, name, rdataclass.ANY,
                                    rdatatype.ANY, rdatatype.NONE,
                                    rdatatype.ANY, True, True)
        elif isinstance(args[0], rdataset.Rdataset):
            for rds in args:
                for rd in rds:
                    self._add_rr(name, 0, rd, rdataclass.NONE)
        else:
            args = list(args)
            if isinstance(args[0], rdata.Rdata):
                for rd in args:
                    self._add_rr(name, 0, rd, rdataclass.NONE)
            else:
                rdtype = args.pop(0)
                if isinstance(rdtype, (str, unicode)):
                    rdtype = rdatatype.from_text(rdtype)
                if len(args) == 0:
                    rrset = self.find_rrset(self.authority, name,
                                            self.zone_rdclass, rdtype,
                                            rdatatype.NONE, rdataclass.ANY,
                                            True, True)
                else:
                    for s in args:
                        rd = rdata.from_text(self.zone_rdclass, rdtype, s,
                                             self.origin)
                        self._add_rr(name, 0, rd, rdataclass.NONE)
Exemple #37
0
 def reset(self):
     """Reset all resolver configuration to the defaults."""
     self.domain = \
         name.Name(name.from_text(socket.gethostname())[1:])
     if len(self.domain) == 0:
         self.domain = name.root
     self.nameservers = []
     self.search = []
     self.port = 53
     self.timeout = 2.0
     self.lifetime = 30.0
     self.keyring = None
     self.keyname = None
     self.keyalgorithm = tsig.default_algorithm
     self.edns = -1
     self.ednsflags = 0
     self.payload = 0
     self.cache = None
     self.flags = None
     self.retry_servfail = False
     self.rotate = False
Exemple #38
0
def from_address(text):
    """Convert an IPv4 or IPv6 address in textual form into a Name object whose
    value is the reverse-map domain name of the address.
    @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1',
    '::1')
    @type text: str
    @rtype: name.Name object
    """
    try:
        v6 = ipv6.inet_aton(text)
        if ipv6.is_mapped(v6):
            parts = ['%d' % ord(byte) for byte in v6[12:]]
            origin = ipv4_reverse_domain
        else:
            parts = list(v6.encode('hex_codec'))
            origin = ipv6_reverse_domain
    except:
        parts = ['%d' % ord(byte) for byte in ipv4.inet_aton(text)]
        origin = ipv4_reverse_domain
    parts.reverse()
    return name.from_text('.'.join(parts), origin=origin)
Exemple #39
0
    def _add(self, replace, section, name, *args):
        """Add records.  The first argument is the replace mode.  If
        false, RRs are added to an existing RRset; if true, the RRset
        is replaced with the specified contents.  The second
        argument is the section to add to.  The third argument
        is always a name.  The other arguments can be:

                - rdataset...

                - ttl, rdata...

                - ttl, rdtype, string..."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if isinstance(args[0], rdataset.Rdataset):
            for rds in args:
                if replace:
                    self.delete(name, rds.rdtype)
                for rd in rds:
                    self._add_rr(name, rds.ttl, rd, section=section)
        else:
            args = list(args)
            ttl = int(args.pop(0))
            if isinstance(args[0], rdata.Rdata):
                if replace:
                    self.delete(name, args[0].rdtype)
                for rd in args:
                    self._add_rr(name, ttl, rd, section=section)
            else:
                rdtype = args.pop(0)
                if isinstance(rdtype, str):
                    rdtype = rdatatype.from_text(rdtype)
                if replace:
                    self.delete(name, rdtype)
                for s in args:
                    rd = rdata.from_text(self.zone_rdclass, rdtype, s,
                                         self.origin)
                    self._add_rr(name, ttl, rd, section=section)
Exemple #40
0
    def _add(self, replace, section, name, *args):
        """Add records.  The first argument is the replace mode.  If
        false, RRs are added to an existing RRset; if true, the RRset
        is replaced with the specified contents.  The second
        argument is the section to add to.  The third argument
        is always a name.  The other arguments can be:

                - rdataset...

                - ttl, rdata...

                - ttl, rdtype, string..."""

        if isinstance(name, (str, unicode)):
            name = name.from_text(name, None)
        if isinstance(args[0], rdataset.Rdataset):
            for rds in args:
                if replace:
                    self.delete(name, rds.rdtype)
                for rd in rds:
                    self._add_rr(name, rds.ttl, rd, section=section)
        else:
            args = list(args)
            ttl = int(args.pop(0))
            if isinstance(args[0], rdata.Rdata):
                if replace:
                    self.delete(name, args[0].rdtype)
                for rd in args:
                    self._add_rr(name, ttl, rd, section=section)
            else:
                rdtype = args.pop(0)
                if isinstance(rdtype, str):
                    rdtype = rdatatype.from_text(rdtype)
                if replace:
                    self.delete(name, rdtype)
                for s in args:
                    rd = rdata.from_text(self.zone_rdclass, rdtype, s,
                                             self.origin)
                    self._add_rr(name, ttl, rd, section=section)
Exemple #41
0
    def __init__(self, zone, rdclass=rdataclass.IN, keyring=None,
                 keyname=None, keyalgorithm=tsig.default_algorithm):
        """Initialize a new DNS Update object.

        @param zone: The zone which is being updated.
        @type zone: A name.Name or string
        @param rdclass: The class of the zone; defaults to rdataclass.IN.
        @type rdclass: An int designating the class, or a string whose value
        is the name of a class.
        @param keyring: The TSIG keyring to use; defaults to None.
        @type keyring: dict
        @param keyname: The name of the TSIG key to use; defaults to None.
        The key must be defined in the keyring.  If a keyring is specified
        but a keyname is not, then the key used will be the first key in the
        keyring.  Note that the order of keys in a dictionary is not defined,
        so applications should supply a keyname when a keyring is used, unless
        they know the keyring contains only one key.
        @type keyname: name.Name or string
        @param keyalgorithm: The TSIG algorithm to use; defaults to
        tsig.default_algorithm.  Constants for TSIG algorithms are defined
        in tsig, and the currently implemented algorithms are
        HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
        HMAC_SHA512.
        @type keyalgorithm: string
        """
        super(Update, self).__init__()
        self.flags |= opcode.to_flags(opcode.UPDATE)
        if isinstance(zone, (str, unicode)):
            zone = name.from_text(zone)
        self.origin = zone
        if isinstance(rdclass, str):
            rdclass = rdataclass.from_text(rdclass)
        self.zone_rdclass = rdclass
        self.find_rrset(self.question, self.origin, rdclass, rdatatype.SOA,
                        create=True, force_unique=True)
        if not keyring is None:
            self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
Exemple #42
0
    def read(self):
        """Read a DNS master file and build a zone object.

        @raises zone.NoSOA: No SOA RR was found at the zone origin
        @raises zone.NoNS: No NS RRset was found at the zone origin
        """

        try:
            while 1:
                token = self.tok.get(True, True)
                if token.is_eof():
                    if not self.current_file is None:
                        self.current_file.close()
                    if len(self.saved_state) > 0:
                        (self.tok, self.current_origin, self.last_name,
                         self.current_file,
                         self.ttl) = self.saved_state.pop(-1)
                        continue
                    break
                elif token.is_eol():
                    continue
                elif token.is_comment():
                    self.tok.get_eol()
                    continue
                elif token.value[0] == '$':
                    u = token.value.upper()
                    if u == '$TTL':
                        token = self.tok.get()
                        if not token.is_identifier():
                            raise exception.SyntaxError("bad $TTL")
                        self.ttl = ttl.from_text(token.value)
                        self.tok.get_eol()
                    elif u == '$ORIGIN':
                        self.current_origin = self.tok.get_name()
                        self.tok.get_eol()
                        if self.zone.origin is None:
                            self.zone.origin = self.current_origin
                    elif u == '$INCLUDE' and self.allow_include:
                        token = self.tok.get()
                        filename = token.value
                        token = self.tok.get()
                        if token.is_identifier():
                            new_origin = name.from_text(token.value, \
                                                            self.current_origin)
                            self.tok.get_eol()
                        elif not token.is_eol_or_eof():
                            raise exception.SyntaxError(
                                "bad origin in $INCLUDE")
                        else:
                            new_origin = self.current_origin
                        self.saved_state.append(
                            (self.tok, self.current_origin, self.last_name,
                             self.current_file, self.ttl))
                        self.current_file = file(filename, 'r')
                        self.tok = tokenizer.Tokenizer(self.current_file,
                                                       filename)
                        self.current_origin = new_origin
                    elif u == '$GENERATE':
                        self._generate_line()
                    else:
                        raise exception.SyntaxError(
                            "Unknown master file directive '" + u + "'")
                    continue
                self.tok.unget(token)
                self._rr_line()
        except exception.SyntaxError, detail:
            (filename, line_number) = self.tok.where()
            if detail is None:
                detail = "syntax error"
            raise exception.SyntaxError("%s:%d: %s" %
                                        (filename, line_number, detail))
Exemple #43
0
def xfr(where, zone, rdtype=rdatatype.AXFR, rdclass=rdataclass.IN,
        timeout=None, port=53, keyring=None, keyname=None, relativize=True,
        af=None, lifetime=None, source=None, source_port=0, serial=0,
        use_udp=False, keyalgorithm=tsig.default_algorithm):
    """Return a generator for the responses to a zone transfer.

    @param where: where to send the message
    @type where: string containing an IPv4 or IPv6 address
    @param zone: The name of the zone to transfer
    @type zone: name.Name object or string
    @param rdtype: The type of zone transfer.  The default is
    rdatatype.AXFR.
    @type rdtype: int or string
    @param rdclass: The class of the zone transfer.  The default is
    rdataclass.IN.
    @type rdclass: int or string
    @param timeout: The number of seconds to wait for each response message.
    If None, the default, wait forever.
    @type timeout: float
    @param port: The port to which to send the message.  The default is 53.
    @type port: int
    @param keyring: The TSIG keyring to use
    @type keyring: dict
    @param keyname: The name of the TSIG key to use
    @type keyname: name.Name object or string
    @param relativize: If True, all names in the zone will be relativized to
    the zone origin.  It is essential that the relativize setting matches
    the one specified to zone.from_xfr().
    @type relativize: bool
    @param af: the address family to use.  The default is None, which
    causes the address family to use to be inferred from the form of of where.
    If the inference attempt fails, AF_INET is used.
    @type af: int
    @param lifetime: The total number of seconds to spend doing the transfer.
    If None, the default, then there is no limit on the time the transfer may
    take.
    @type lifetime: float
    @rtype: generator of message.Message objects.
    @param source: source address.  The default is the wildcard address.
    @type source: string
    @param source_port: The port from which to send the message.
    The default is 0.
    @type source_port: int
    @param serial: The SOA serial number to use as the base for an IXFR diff
    sequence (only meaningful if rdtype == rdatatype.IXFR).
    @type serial: int
    @param use_udp: Use UDP (only meaningful for IXFR)
    @type use_udp: bool
    @param keyalgorithm: The TSIG algorithm to use; defaults to
    tsig.default_algorithm
    @type keyalgorithm: string
    """

    if isinstance(zone, (str, unicode)):
        zone = name.from_text(zone)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    q = message.make_query(zone, rdtype, rdclass)
    if rdtype == rdatatype.IXFR:
        rrset = rrset.from_text(zone, 0, 'IN', 'SOA',
                                    '. . %u 0 0 0 0' % serial)
        q.authority.append(rrset)
    if not keyring is None:
        q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
    wire = q.to_wire()
    (af, destination, source) = _destination_and_source(af, where, port, source,
                                                        source_port)
    if use_udp:
        if rdtype != rdatatype.IXFR:
            raise ValueError('cannot do a UDP AXFR')
        s = socket.socket(af, socket.SOCK_DGRAM, 0)
    else:
        s = socket.socket(af, socket.SOCK_STREAM, 0)
    s.setblocking(0)
    if source is not None:
        s.bind(source)
    expiration = _compute_expiration(lifetime)
    _connect(s, destination)
    l = len(wire)
    if use_udp:
        _wait_for_writable(s, expiration)
        s.send(wire)
    else:
        tcpmsg = struct.pack("!H", l) + wire
        _net_write(s, tcpmsg, expiration)
    done = False
    delete_mode = True
    expecting_SOA = False
    soa_rrset = None
    soa_count = 0
    if relativize:
        origin = zone
        oname = name.empty
    else:
        origin = None
        oname = zone
    tsig_ctx = None
    first = True
    while not done:
        mexpiration = _compute_expiration(timeout)
        if mexpiration is None or mexpiration > expiration:
            mexpiration = expiration
        if use_udp:
            _wait_for_readable(s, expiration)
            (wire, from_address) = s.recvfrom(65535)
        else:
            ldata = _net_read(s, 2, mexpiration)
            (l,) = struct.unpack("!H", ldata)
            wire = _net_read(s, l, mexpiration)
        r = message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
                                  xfr=True, origin=origin, tsig_ctx=tsig_ctx,
                                  multi=True, first=first,
                                  one_rr_per_rrset=(rdtype==rdatatype.IXFR))
        tsig_ctx = r.tsig_ctx
        first = False
        answer_index = 0
        if soa_rrset is None:
            if not r.answer or r.answer[0].name != oname:
                raise exception.FormError("No answer or RRset not for qname")
            rrset = r.answer[0]
            if rrset.rdtype != rdatatype.SOA:
                raise exception.FormError("first RRset is not an SOA")
            answer_index = 1
            soa_rrset = rrset.copy()
            if rdtype == rdatatype.IXFR:
                if soa_rrset[0].serial <= serial:
                    #
                    # We're already up-to-date.
                    #
                    done = True
                else:
                    expecting_SOA = True
        #
        # Process SOAs in the answer section (other than the initial
        # SOA in the first message).
        #
        for rrset in r.answer[answer_index:]:
            if done:
                raise exception.FormError("answers after final SOA")
            if rrset.rdtype == rdatatype.SOA and rrset.name == oname:
                if expecting_SOA:
                    if rrset[0].serial != serial:
                        raise exception.FormError("IXFR base serial mismatch")
                    expecting_SOA = False
                elif rdtype == rdatatype.IXFR:
                    delete_mode = not delete_mode
                #
                # If this SOA RRset is equal to the first we saw then we're
                # finished. If this is an IXFR we also check that we're seeing
                # the record in the expected part of the response.
                #
                if rrset == soa_rrset and \
                        (rdtype == rdatatype.AXFR or \
                        (rdtype == rdatatype.IXFR and delete_mode)):
                    done = True
            elif expecting_SOA:
                #
                # We made an IXFR request and are expecting another
                # SOA RR, but saw something else, so this must be an
                # AXFR response.
                #
                rdtype = rdatatype.AXFR
                expecting_SOA = False
        if done and q.keyring and not r.had_tsig:
            raise exception.FormError("missing TSIG")
        yield r
    s.close()
Exemple #44
0
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
    """Validate an RRset against a single signature rdata

    The owner name of the rrsig is assumed to be the same as the owner name
    of the rrset.

    @param rrset: The RRset to validate
    @type rrset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param rrsig: The signature rdata
    @type rrsig: rrset.Rdata
    @param keys: The key dictionary.
    @type keys: a dictionary keyed by name.Name with node or rdataset values
    @param origin: The origin to use for relative names
    @type origin: name.Name or None
    @param now: The time to use when validating the signatures.  The default
    is the current time.
    @type now: int
    """

    if isinstance(origin, (str, unicode)):
        origin = name.from_text(origin, name.root)

    for candidate_key in _find_candidate_keys(keys, rrsig):
        if not candidate_key:
            raise ValidationFailure, 'unknown key'

        # For convenience, allow the rrset to be specified as a (name, rdataset)
        # tuple as well as a proper rrset
        if isinstance(rrset, tuple):
            rrname = rrset[0]
            rdataset = rrset[1]
        else:
            rrname = rrset.name
            rdataset = rrset

        if now is None:
            now = time.time()
        if rrsig.expiration < now:
            raise ValidationFailure, 'expired'
        if rrsig.inception > now:
            raise ValidationFailure, 'not yet valid'

        hash = _make_hash(rrsig.algorithm)

        if _is_rsa(rrsig.algorithm):
            keyptr = candidate_key.key
            (bytes, ) = struct.unpack('!B', keyptr[0:1])
            keyptr = keyptr[1:]
            if bytes == 0:
                (bytes, ) = struct.unpack('!H', keyptr[0:2])
                keyptr = keyptr[2:]
            rsa_e = keyptr[0:bytes]
            rsa_n = keyptr[bytes:]
            keylen = len(rsa_n) * 8
            pubkey = Crypto.PublicKey.RSA.construct(
                (Crypto.Util.number.bytes_to_long(rsa_n),
                 Crypto.Util.number.bytes_to_long(rsa_e)))
            sig = (Crypto.Util.number.bytes_to_long(rrsig.signature), )
        elif _is_dsa(rrsig.algorithm):
            keyptr = candidate_key.key
            (t, ) = struct.unpack('!B', keyptr[0:1])
            keyptr = keyptr[1:]
            octets = 64 + t * 8
            dsa_q = keyptr[0:20]
            keyptr = keyptr[20:]
            dsa_p = keyptr[0:octets]
            keyptr = keyptr[octets:]
            dsa_g = keyptr[0:octets]
            keyptr = keyptr[octets:]
            dsa_y = keyptr[0:octets]
            pubkey = Crypto.PublicKey.DSA.construct(
                (Crypto.Util.number.bytes_to_long(dsa_y),
                 Crypto.Util.number.bytes_to_long(dsa_g),
                 Crypto.Util.number.bytes_to_long(dsa_p),
                 Crypto.Util.number.bytes_to_long(dsa_q)))
            (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:])
            sig = (Crypto.Util.number.bytes_to_long(dsa_r),
                   Crypto.Util.number.bytes_to_long(dsa_s))
        elif _is_ecdsa(rrsig.algorithm):
            if rrsig.algorithm == ECDSAP256SHA256:
                curve = ecdsa.curves.NIST256p
                key_len = 32
                digest_len = 32
            elif rrsig.algorithm == ECDSAP384SHA384:
                curve = ecdsa.curves.NIST384p
                key_len = 48
                digest_len = 48
            else:
                # shouldn't happen
                raise ValidationFailure, 'unknown ECDSA curve'
            keyptr = candidate_key.key
            x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len])
            y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2])
            assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
            point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
            verifying_key = ecdsa.keys.VerifyingKey.from_public_point(
                point, curve)
            pubkey = ECKeyWrapper(verifying_key, key_len)
            r = rrsig.signature[:key_len]
            s = rrsig.signature[key_len:]
            sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r),
                                        Crypto.Util.number.bytes_to_long(s))
        else:
            raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm

        hash.update(_to_rdata(rrsig, origin)[:18])
        hash.update(rrsig.signer.to_digestable(origin))

        if rrsig.labels < len(rrname) - 1:
            suffix = rrname.split(rrsig.labels + 1)[1]
            rrname = name.from_text('*', suffix)
        rrnamebuf = rrname.to_digestable(origin)
        rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
                              rrsig.original_ttl)
        rrlist = sorted(rdataset)
        for rr in rrlist:
            hash.update(rrnamebuf)
            hash.update(rrfixed)
            rrdata = rr.to_digestable(origin)
            rrlen = struct.pack('!H', len(rrdata))
            hash.update(rrlen)
            hash.update(rrdata)

        digest = hash.digest()

        if _is_rsa(rrsig.algorithm):
            # PKCS1 algorithm identifier goop
            digest = _make_algorithm_id(rrsig.algorithm) + digest
            padlen = keylen // 8 - len(digest) - 3
            digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest
        elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm):
            pass
        else:
            # Raise here for code clarity; this won't actually ever happen
            # since if the algorithm is really unknown we'd already have
            # raised an exception above
            raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm

        if pubkey.verify(digest, sig):
            return
    raise ValidationFailure, 'verify failure'
Exemple #45
0
class PeerBadSignature(PeerError):
    """Raised if the peer didn't like the signature we sent"""
    pass

class PeerBadTime(PeerError):
    """Raised if the peer didn't like the time we sent"""
    pass

class PeerBadTruncation(PeerError):
    """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
    pass

# TSIG Algorithms

HMAC_MD5 = name.from_text("HMAC-MD5.SIG-ALG.REG.INT")
HMAC_SHA1 = name.from_text("hmac-sha1")
HMAC_SHA224 = name.from_text("hmac-sha224")
HMAC_SHA256 = name.from_text("hmac-sha256")
HMAC_SHA384 = name.from_text("hmac-sha384")
HMAC_SHA512 = name.from_text("hmac-sha512")

default_algorithm = HMAC_MD5

BADSIG = 16
BADKEY = 17
BADTIME = 18
BADTRUNC = 22

def sign(wire, keyname, secret, time, fudge, original_id, error,
         other_data, request_mac, ctx=None, multi=False, first=True,
Exemple #46
0
    def _generate_line(self):
        # range lhs [ttl] [class] type rhs [ comment ]
        """Process one line containing the GENERATE statement from a DNS
        master file."""
        if self.current_origin is None:
            raise UnknownOrigin

        token = self.tok.get()
        # Range (required)
        try:
            start, stop, step = grange.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except:
            raise exception.SyntaxError

        # lhs (required)
        try:
            lhs = token.value
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except:
            raise exception.SyntaxError

        # TTL
        try:
            ttl = ttl.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except ttl.BadTTL:
            ttl = self.ttl
        # Class
        try:
            rdclass = rdataclass.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except exception.SyntaxError:
            raise exception.SyntaxError
        except:
            rdclass = self.zone.rdclass
        if rdclass != self.zone.rdclass:
            raise exception.SyntaxError("RR class is not zone's class")
        # Type
        try:
            rdtype = rdatatype.from_text(token.value)
            token = self.tok.get()
            if not token.is_identifier():
                raise exception.SyntaxError
        except:
            raise exception.SyntaxError("unknown rdatatype '%s'" % token.value)

        # lhs (required)
        try:
            rhs = token.value
        except:
            raise exception.SyntaxError

        lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs)
        rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs)
        for i in range(start, stop + 1, step):
            # +1 because bind is inclusive and python is exclusive

            if lsign == '+':
                lindex = i + int(loffset)
            elif lsign == '-':
                lindex = i - int(loffset)

            if rsign == '-':
                rindex = i - int(roffset)
            elif rsign == '+':
                rindex = i + int(roffset)

            lzfindex = str(lindex).zfill(int(lwidth))
            rzfindex = str(rindex).zfill(int(rwidth))

            name = lhs.replace('$%s' % (lmod), lzfindex)
            rdata = rhs.replace('$%s' % (rmod), rzfindex)

            self.last_name = name.from_text(name, self.current_origin)
            name = self.last_name
            if not name.is_subdomain(self.zone.origin):
                self._eat_line()
                return
            if self.relativize:
                name = name.relativize(self.zone.origin)

            n = self.zone.nodes.get(name)
            if n is None:
                n = self.zone.node_factory()
                self.zone.nodes[name] = n
            try:
                rd = rdata.from_text(rdclass, rdtype, rdata,
                                     self.current_origin, False)
            except exception.SyntaxError:
                # Catch and reraise.
                (ty, va) = sys.exc_info()[:2]
                raise va
            except:
                # All exceptions that occur in the processing of rdata
                # are treated as syntax errors.  This is not strictly
                # correct, but it is correct almost all of the time.
                # We convert them to syntax errors so that we can emit
                # helpful filename:line info.
                (ty, va) = sys.exc_info()[:2]
                raise exception.SyntaxError("caught exception %s: %s" %
                                            (str(ty), str(va)))

            rd.choose_relativity(self.zone.origin, self.relativize)
            covers = rd.covers()
            rds = n.find_rdataset(rdclass, rdtype, covers, True)
            rds.add(rd, ttl)
Exemple #47
0
    def query(self, qname, rdtype=rdatatype.A, rdclass=rdataclass.IN,
              tcp=False, source=None, raise_on_no_answer=True, source_port=0):
        """Query nameservers to find the answer to the question.

        The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
        of the appropriate type, or strings that can be converted into objects
        of the appropriate type.  E.g. For I{rdtype} the integer 2 and the
        the string 'NS' both mean to query for records with DNS rdata type NS.

        @param qname: the query name
        @type qname: name.Name object or string
        @param rdtype: the query type
        @type rdtype: int or string
        @param rdclass: the query class
        @type rdclass: int or string
        @param tcp: use TCP to make the query (default is False).
        @type tcp: bool
        @param source: bind to this IP address (defaults to machine default IP).
        @type source: IP address in dotted quad notation
        @param raise_on_no_answer: raise NoAnswer if there's no answer
        (defaults is True).
        @type raise_on_no_answer: bool
        @param source_port: The port from which to send the message.
        The default is 0.
        @type source_port: int
        @rtype: resolver.Answer instance
        @raises Timeout: no answers could be found in the specified lifetime
        @raises NXDOMAIN: the query name does not exist
        @raises YXDOMAIN: the query name is too long after DNAME substitution
        @raises NoAnswer: the response did not contain an answer and
        raise_on_no_answer is True.
        @raises NoNameservers: no non-broken nameservers are available to
        answer the question."""
        #import pdb
        #pdb.set_trace()
        if isinstance(qname, (str, unicode)):
            qname = name.from_text(qname, None)
        if isinstance(rdtype, (str, unicode)):
            rdtype = rdatatype.from_text(rdtype)
        if rdatatype.is_metatype(rdtype):
            raise NoMetaqueries
        if isinstance(rdclass, (str, unicode)):
            rdclass = rdataclass.from_text(rdclass)
        if rdataclass.is_metaclass(rdclass):
            raise NoMetaqueries
        qnames_to_try = []
        if qname.is_absolute():
            qnames_to_try.append(qname)
        else:
            if len(qname) > 1:
                qnames_to_try.append(qname.concatenate(name.root))
            if self.search:
                for suffix in self.search:
                    qnames_to_try.append(qname.concatenate(suffix))
            else:
                qnames_to_try.append(qname.concatenate(self.domain))
        all_nxdomain = True
        start = time.time()
        for qname in qnames_to_try:
            if self.cache:
                answer = self.cache.get((qname, rdtype, rdclass))
                if not answer is None:
                    if answer.rrset is None and raise_on_no_answer:
                        raise NoAnswer
                    else:
                        return answer
            request = message.make_query(qname, rdtype, rdclass)
            if not self.keyname is None:
                request.use_tsig(self.keyring, self.keyname,
                                 algorithm=self.keyalgorithm)
            request.use_edns(self.edns, self.ednsflags, self.payload)
            if self.flags is not None:
                request.flags = self.flags
            response = None
            #
            # make a copy of the servers list so we can alter it later.
            #
            nameservers = self.nameservers[:]
            if self.rotate:
                random.shuffle(nameservers)
            backoff = 0.10
            while response is None:
                if len(nameservers) == 0:
                    raise NoNameservers
                for nameserver in nameservers[:]:
                    timeout = self._compute_timeout(start)
                    try:
                        if tcp:
                            response = mquery.tcp(request, nameserver,
                                                     timeout, self.port,
                                                     source=source,
                                                     source_port=source_port)
                        else:
                            response = mquery.udp(request, nameserver,
                                                     timeout, self.port,
                                                     source=source,
                                                     source_port=source_port)
                            if response.flags & flags.TC:
                                # Response truncated; retry with TCP.
                                timeout = self._compute_timeout(start)
                                response = mquery.tcp(request, nameserver,
                                                       timeout, self.port,
                                                       source=source,
                                                       source_port=source_port)
                    except (socket.error, exception.Timeout):
                        #
                        # Communication failure or timeout.  Go to the
                        # next server
                        #
                        response = None
                        continue
                    except mquery.UnexpectedSource:
                        #
                        # Who knows?  Keep going.
                        #
                        response = None
                        continue
                    except exception.FormError:
                        #
                        # We don't understand what this server is
                        # saying.  Take it out of the mix and
                        # continue.
                        #
                        nameservers.remove(nameserver)
                        response = None
                        continue
                    except EOFError:
                        #
                        # We're using TCP and they hung up on us.
                        # Probably they don't support TCP (though
                        # they're supposed to!).  Take it out of the
                        # mix and continue.
                        #
                        nameservers.remove(nameserver)
                        response = None
                        continue
                    rcode = response.rcode()
                    if rcode == mrcode.YXDOMAIN:
                        raise YXDOMAIN
                    if rcode == mrcode.NOERROR or \
                           rcode == mrcode.NXDOMAIN:
                        break
                    #
                    # We got a response, but we're not happy with the
                    # rcode in it.  Remove the server from the mix if
                    # the rcode isn't SERVFAIL.
                    #
                    if rcode != mrcode.SERVFAIL or not self.retry_servfail:
                        nameservers.remove(nameserver)
                    response = None
                if not response is None:
                    break
                #
                # All nameservers failed!
                #
                if len(nameservers) > 0:
                    #
                    # But we still have servers to try.  Sleep a bit
                    # so we don't pound them!
                    #
                    timeout = self._compute_timeout(start)
                    sleep_time = min(timeout, backoff)
                    backoff *= 2
                    time.sleep(sleep_time)
            if response.rcode() == mrcode.NXDOMAIN:
                continue
            all_nxdomain = False
            break
        if all_nxdomain:
            raise NXDOMAIN
        answer = Answer(qname, rdtype, rdclass, response,
                        raise_on_no_answer)
        if self.cache:
            self.cache.put((qname, rdtype, rdclass), answer)
        return answer
Exemple #48
0
def xfr(where,
        zone,
        rdtype=rdatatype.AXFR,
        rdclass=rdataclass.IN,
        timeout=None,
        port=53,
        keyring=None,
        keyname=None,
        relativize=True,
        af=None,
        lifetime=None,
        source=None,
        source_port=0,
        serial=0,
        use_udp=False,
        keyalgorithm=tsig.default_algorithm):
    """Return a generator for the responses to a zone transfer.

    @param where: where to send the message
    @type where: string containing an IPv4 or IPv6 address
    @param zone: The name of the zone to transfer
    @type zone: name.Name object or string
    @param rdtype: The type of zone transfer.  The default is
    rdatatype.AXFR.
    @type rdtype: int or string
    @param rdclass: The class of the zone transfer.  The default is
    rdataclass.IN.
    @type rdclass: int or string
    @param timeout: The number of seconds to wait for each response message.
    If None, the default, wait forever.
    @type timeout: float
    @param port: The port to which to send the message.  The default is 53.
    @type port: int
    @param keyring: The TSIG keyring to use
    @type keyring: dict
    @param keyname: The name of the TSIG key to use
    @type keyname: name.Name object or string
    @param relativize: If True, all names in the zone will be relativized to
    the zone origin.  It is essential that the relativize setting matches
    the one specified to zone.from_xfr().
    @type relativize: bool
    @param af: the address family to use.  The default is None, which
    causes the address family to use to be inferred from the form of of where.
    If the inference attempt fails, AF_INET is used.
    @type af: int
    @param lifetime: The total number of seconds to spend doing the transfer.
    If None, the default, then there is no limit on the time the transfer may
    take.
    @type lifetime: float
    @rtype: generator of message.Message objects.
    @param source: source address.  The default is the wildcard address.
    @type source: string
    @param source_port: The port from which to send the message.
    The default is 0.
    @type source_port: int
    @param serial: The SOA serial number to use as the base for an IXFR diff
    sequence (only meaningful if rdtype == rdatatype.IXFR).
    @type serial: int
    @param use_udp: Use UDP (only meaningful for IXFR)
    @type use_udp: bool
    @param keyalgorithm: The TSIG algorithm to use; defaults to
    tsig.default_algorithm
    @type keyalgorithm: string
    """

    if isinstance(zone, (str, unicode)):
        zone = name.from_text(zone)
    if isinstance(rdtype, (str, unicode)):
        rdtype = rdatatype.from_text(rdtype)
    q = message.make_query(zone, rdtype, rdclass)
    if rdtype == rdatatype.IXFR:
        rrset = rrset.from_text(zone, 0, 'IN', 'SOA',
                                '. . %u 0 0 0 0' % serial)
        q.authority.append(rrset)
    if not keyring is None:
        q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
    wire = q.to_wire()
    (af, destination,
     source) = _destination_and_source(af, where, port, source, source_port)
    if use_udp:
        if rdtype != rdatatype.IXFR:
            raise ValueError('cannot do a UDP AXFR')
        s = socket.socket(af, socket.SOCK_DGRAM, 0)
    else:
        s = socket.socket(af, socket.SOCK_STREAM, 0)
    s.setblocking(0)
    if source is not None:
        s.bind(source)
    expiration = _compute_expiration(lifetime)
    _connect(s, destination)
    l = len(wire)
    if use_udp:
        _wait_for_writable(s, expiration)
        s.send(wire)
    else:
        tcpmsg = struct.pack("!H", l) + wire
        _net_write(s, tcpmsg, expiration)
    done = False
    delete_mode = True
    expecting_SOA = False
    soa_rrset = None
    soa_count = 0
    if relativize:
        origin = zone
        oname = name.empty
    else:
        origin = None
        oname = zone
    tsig_ctx = None
    first = True
    while not done:
        mexpiration = _compute_expiration(timeout)
        if mexpiration is None or mexpiration > expiration:
            mexpiration = expiration
        if use_udp:
            _wait_for_readable(s, expiration)
            (wire, from_address) = s.recvfrom(65535)
        else:
            ldata = _net_read(s, 2, mexpiration)
            (l, ) = struct.unpack("!H", ldata)
            wire = _net_read(s, l, mexpiration)
        r = message.from_wire(wire,
                              keyring=q.keyring,
                              request_mac=q.mac,
                              xfr=True,
                              origin=origin,
                              tsig_ctx=tsig_ctx,
                              multi=True,
                              first=first,
                              one_rr_per_rrset=(rdtype == rdatatype.IXFR))
        tsig_ctx = r.tsig_ctx
        first = False
        answer_index = 0
        if soa_rrset is None:
            if not r.answer or r.answer[0].name != oname:
                raise exception.FormError("No answer or RRset not for qname")
            rrset = r.answer[0]
            if rrset.rdtype != rdatatype.SOA:
                raise exception.FormError("first RRset is not an SOA")
            answer_index = 1
            soa_rrset = rrset.copy()
            if rdtype == rdatatype.IXFR:
                if soa_rrset[0].serial <= serial:
                    #
                    # We're already up-to-date.
                    #
                    done = True
                else:
                    expecting_SOA = True
        #
        # Process SOAs in the answer section (other than the initial
        # SOA in the first message).
        #
        for rrset in r.answer[answer_index:]:
            if done:
                raise exception.FormError("answers after final SOA")
            if rrset.rdtype == rdatatype.SOA and rrset.name == oname:
                if expecting_SOA:
                    if rrset[0].serial != serial:
                        raise exception.FormError("IXFR base serial mismatch")
                    expecting_SOA = False
                elif rdtype == rdatatype.IXFR:
                    delete_mode = not delete_mode
                #
                # If this SOA RRset is equal to the first we saw then we're
                # finished. If this is an IXFR we also check that we're seeing
                # the record in the expected part of the response.
                #
                if rrset == soa_rrset and \
                        (rdtype == rdatatype.AXFR or \
                        (rdtype == rdatatype.IXFR and delete_mode)):
                    done = True
            elif expecting_SOA:
                #
                # We made an IXFR request and are expecting another
                # SOA RR, but saw something else, so this must be an
                # AXFR response.
                #
                rdtype = rdatatype.AXFR
                expecting_SOA = False
        if done and q.keyring and not r.had_tsig:
            raise exception.FormError("missing TSIG")
        yield r
    s.close()
Exemple #49
0
 def _config_win32_domain(self, domain):
     """Configure a Domain registry entry."""
     # we call str() on domain to convert it from unicode to ascii
     self.domain = name.from_text(str(domain))
Exemple #50
0
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
    """Validate an RRset against a single signature rdata

    The owner name of the rrsig is assumed to be the same as the owner name
    of the rrset.

    @param rrset: The RRset to validate
    @type rrset: rrset.RRset or (name.Name, rdataset.Rdataset)
    tuple
    @param rrsig: The signature rdata
    @type rrsig: rrset.Rdata
    @param keys: The key dictionary.
    @type keys: a dictionary keyed by name.Name with node or rdataset values
    @param origin: The origin to use for relative names
    @type origin: name.Name or None
    @param now: The time to use when validating the signatures.  The default
    is the current time.
    @type now: int
    """

    if isinstance(origin, (str, unicode)):
        origin = name.from_text(origin, name.root)

    for candidate_key in _find_candidate_keys(keys, rrsig):
        if not candidate_key:
            raise ValidationFailure, 'unknown key'

        # For convenience, allow the rrset to be specified as a (name, rdataset)
        # tuple as well as a proper rrset
        if isinstance(rrset, tuple):
            rrname = rrset[0]
            rdataset = rrset[1]
        else:
            rrname = rrset.name
            rdataset = rrset

        if now is None:
            now = time.time()
        if rrsig.expiration < now:
            raise ValidationFailure, 'expired'
        if rrsig.inception > now:
            raise ValidationFailure, 'not yet valid'

        hash = _make_hash(rrsig.algorithm)

        if _is_rsa(rrsig.algorithm):
            keyptr = candidate_key.key
            (bytes,) = struct.unpack('!B', keyptr[0:1])
            keyptr = keyptr[1:]
            if bytes == 0:
                (bytes,) = struct.unpack('!H', keyptr[0:2])
                keyptr = keyptr[2:]
            rsa_e = keyptr[0:bytes]
            rsa_n = keyptr[bytes:]
            keylen = len(rsa_n) * 8
            pubkey = Crypto.PublicKey.RSA.construct(
                (Crypto.Util.number.bytes_to_long(rsa_n),
                 Crypto.Util.number.bytes_to_long(rsa_e)))
            sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),)
        elif _is_dsa(rrsig.algorithm):
            keyptr = candidate_key.key
            (t,) = struct.unpack('!B', keyptr[0:1])
            keyptr = keyptr[1:]
            octets = 64 + t * 8
            dsa_q = keyptr[0:20]
            keyptr = keyptr[20:]
            dsa_p = keyptr[0:octets]
            keyptr = keyptr[octets:]
            dsa_g = keyptr[0:octets]
            keyptr = keyptr[octets:]
            dsa_y = keyptr[0:octets]
            pubkey = Crypto.PublicKey.DSA.construct(
                (Crypto.Util.number.bytes_to_long(dsa_y),
                 Crypto.Util.number.bytes_to_long(dsa_g),
                 Crypto.Util.number.bytes_to_long(dsa_p),
                 Crypto.Util.number.bytes_to_long(dsa_q)))
            (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:])
            sig = (Crypto.Util.number.bytes_to_long(dsa_r),
                   Crypto.Util.number.bytes_to_long(dsa_s))
        elif _is_ecdsa(rrsig.algorithm):
            if rrsig.algorithm == ECDSAP256SHA256:
                curve = ecdsa.curves.NIST256p
                key_len = 32
                digest_len = 32
            elif rrsig.algorithm == ECDSAP384SHA384:
                curve = ecdsa.curves.NIST384p
                key_len = 48
                digest_len = 48
            else:
                # shouldn't happen
                raise ValidationFailure, 'unknown ECDSA curve'
            keyptr = candidate_key.key
            x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len])
            y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2])
            assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
            point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
            verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
                                                                      curve)
            pubkey = ECKeyWrapper(verifying_key, key_len)
            r = rrsig.signature[:key_len]
            s = rrsig.signature[key_len:]
            sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r),
                                        Crypto.Util.number.bytes_to_long(s))
        else:
            raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm

        hash.update(_to_rdata(rrsig, origin)[:18])
        hash.update(rrsig.signer.to_digestable(origin))

        if rrsig.labels < len(rrname) - 1:
            suffix = rrname.split(rrsig.labels + 1)[1]
            rrname = name.from_text('*', suffix)
        rrnamebuf = rrname.to_digestable(origin)
        rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
                              rrsig.original_ttl)
        rrlist = sorted(rdataset);
        for rr in rrlist:
            hash.update(rrnamebuf)
            hash.update(rrfixed)
            rrdata = rr.to_digestable(origin)
            rrlen = struct.pack('!H', len(rrdata))
            hash.update(rrlen)
            hash.update(rrdata)

        digest = hash.digest()

        if _is_rsa(rrsig.algorithm):
            # PKCS1 algorithm identifier goop
            digest = _make_algorithm_id(rrsig.algorithm) + digest
            padlen = keylen // 8 - len(digest) - 3
            digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest
        elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm):
            pass
        else:
            # Raise here for code clarity; this won't actually ever happen
            # since if the algorithm is really unknown we'd already have
            # raised an exception above
            raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm

        if pubkey.verify(digest, sig):
            return
    raise ValidationFailure, 'verify failure'