133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Permission to use, copy, modify, and distribute this software and its
433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# documentation for any purpose with or without fee is hereby granted,
533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# provided that the above copyright notice and this permission notice
633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# appear in all copies.
733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport struct
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.exception
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.dnssec
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.rdata
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_flags_from_text = {
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'NOCONF': (0x4000, 0xC000),
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'NOAUTH': (0x8000, 0xC000),
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'NOKEY': (0xC000, 0xC000),
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG2': (0x2000, 0x2000),
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'EXTEND': (0x1000, 0x1000),
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG4': (0x0800, 0x0800),
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG5': (0x0400, 0x0400),
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'USER': (0x0000, 0x0300),
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'ZONE': (0x0100, 0x0300),
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'HOST': (0x0200, 0x0300),
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'NTYP3': (0x0300, 0x0300),
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG8': (0x0080, 0x0080),
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG9': (0x0040, 0x0040),
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG10': (0x0020, 0x0020),
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'FLAG11': (0x0010, 0x0010),
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG0': (0x0000, 0x000f),
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG1': (0x0001, 0x000f),
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG2': (0x0002, 0x000f),
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG3': (0x0003, 0x000f),
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG4': (0x0004, 0x000f),
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG5': (0x0005, 0x000f),
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG6': (0x0006, 0x000f),
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG7': (0x0007, 0x000f),
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG8': (0x0008, 0x000f),
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG9': (0x0009, 0x000f),
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG10': (0x000a, 0x000f),
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG11': (0x000b, 0x000f),
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG12': (0x000c, 0x000f),
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG13': (0x000d, 0x000f),
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG14': (0x000e, 0x000f),
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'SIG15': (0x000f, 0x000f),
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    }
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_protocol_from_text = {
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'NONE' : 0,
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'TLS' : 1,
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'EMAIL' : 2,
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'DNSSEC' : 3,
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'IPSEC' : 4,
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    'ALL' : 255,
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    }
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass KEYBase(dns.rdata.Rdata):
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """KEY-like record base
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar flags: the key flags
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type flags: int
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar protocol: the protocol for which this key may be used
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type protocol: int
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar algorithm: the algorithm used for the key
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type algorithm: int
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar key: the public key
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type key: string"""
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    __slots__ = ['flags', 'protocol', 'algorithm', 'key']
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(KEYBase, self).__init__(rdclass, rdtype)
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.flags = flags
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.protocol = protocol
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.algorithm = algorithm
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.key = key
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_text(self, origin=None, relativize=True, **kw):
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm,
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                dns.rdata._base64ify(self.key))
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        flags = tok.get_string()
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if flags.isdigit():
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            flags = int(flags)
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        else:
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            flag_names = flags.split('|')
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            flags = 0
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            for flag in flag_names:
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                v = _flags_from_text.get(flag)
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                if v is None:
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    raise dns.exception.SyntaxError('unknown flag %s' % flag)
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                flags &= ~v[1]
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                flags |= v[0]
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        protocol = tok.get_string()
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if protocol.isdigit():
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            protocol = int(protocol)
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        else:
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            protocol = _protocol_from_text.get(protocol)
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if protocol is None:
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                raise dns.exception.SyntaxError('unknown protocol %s' % protocol)
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        chunks = []
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        while 1:
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            t = tok.get().unescape()
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if t.is_eol_or_eof():
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                break
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if not t.is_identifier():
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                raise dns.exception.SyntaxError
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            chunks.append(t.value)
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b64 = ''.join(chunks)
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        key = b64.decode('base64_codec')
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, flags, protocol, algorithm, key)
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_text = classmethod(from_text)
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(header)
12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(self.key)
13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if rdlen < 4:
13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            raise dns.exception.FormError
13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        header = struct.unpack('!HBB', wire[current : current + 4])
13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += 4
13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= 4
13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        key = wire[current : current + rdlen]
13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, header[0], header[1], header[2],
13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                   key)
14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
14133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_wire = classmethod(from_wire)
14233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
14333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def _cmp(self, other):
14433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        hs = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
14533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        ho = struct.pack("!HBB", other.flags, other.protocol, other.algorithm)
14633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        v = cmp(hs, ho)
14733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if v == 0:
14833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            v = cmp(self.key, other.key)
14933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return v
150