133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright (C) 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 cStringIO
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport string
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport struct
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.exception
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.rdata
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.rdatatype
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass HIP(dns.rdata.Rdata):
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """HIP record
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar hit: the host identity tag
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type hit: string
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar algorithm: the public key cryptographic algorithm
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type algorithm: int
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar key: the public key
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type key: string
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar servers: the rendezvous servers
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type servers: list of dns.name.Name objects
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @see: RFC 5205"""
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    __slots__ = ['hit', 'algorithm', 'key', 'servers']
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def __init__(self, rdclass, rdtype, hit, algorithm, key, servers):
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(HIP, self).__init__(rdclass, rdtype)
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.hit = hit
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.algorithm = algorithm
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.key = key
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.servers = servers
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_text(self, origin=None, relativize=True, **kw):
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        hit = self.hit.encode('hex-codec')
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        key = self.key.encode('base64-codec').replace('\n', '')
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        text = ''
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        servers = []
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        for server in self.servers:
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            servers.append(str(server.choose_relativity(origin, relativize)))
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if len(servers) > 0:
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            text += (' ' + ' '.join(servers))
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return '%u %s %s%s' % (self.algorithm, hit, key, text)
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        algorithm = tok.get_uint8()
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        hit = tok.get_string().decode('hex-codec')
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if len(hit) > 255:
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            raise dns.exception.SyntaxError("HIT too long")
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        key = tok.get_string().decode('base64-codec')
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        servers = []
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        while 1:
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            token = tok.get()
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if token.is_eol_or_eof():
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                break
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            server = dns.name.from_text(token.value, origin)
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            server.choose_relativity(origin, relativize)
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            servers.append(server)
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, hit, algorithm, key, servers)
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_text = classmethod(from_text)
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lh = len(self.hit)
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lk = len(self.key)
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(struct.pack("!BBH", lh, self.algorithm, lk))
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(self.hit)
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(self.key)
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        for server in self.servers:
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            server.to_wire(file, None, origin)
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (lh, algorithm, lk) = struct.unpack('!BBH',
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                            wire[current : current + 4])
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += 4
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= 4
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        hit = wire[current : current + lh]
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += lh
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= lh
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        key = wire[current : current + lk]
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += lk
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= lk
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        servers = []
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        while rdlen > 0:
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            (server, cused) = dns.name.from_wire(wire[: current + rdlen],
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                                 current)
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            current += cused
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            rdlen -= cused
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if not origin is None:
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                server = server.relativize(origin)
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            servers.append(server)
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, hit, algorithm, key, servers)
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_wire = classmethod(from_wire)
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def choose_relativity(self, origin = None, relativize = True):
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        servers = []
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        for server in self.servers:
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            server = server.choose_relativity(origin, relativize)
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            servers.append(server)
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.servers = servers
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def _cmp(self, other):
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b1 = cStringIO.StringIO()
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lh = len(self.hit)
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lk = len(self.key)
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b1.write(struct.pack("!BBH", lh, self.algorithm, lk))
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b1.write(self.hit)
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b1.write(self.key)
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b2 = cStringIO.StringIO()
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lh = len(other.hit)
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lk = len(other.key)
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b2.write(struct.pack("!BBH", lh, other.algorithm, lk))
12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b2.write(other.hit)
12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        b2.write(other.key)
12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        v = cmp(b1.getvalue(), b2.getvalue())
12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if v != 0:
13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            return v
13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        ls = len(self.servers)
13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        lo = len(other.servers)
13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        count = min(ls, lo)
13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        i = 0
13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        while i < count:
13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            v = cmp(self.servers[i], other.servers[i])
13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if v != 0:
13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                return v
13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            i += 1
14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return ls - lo
141