133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright (C) 2003-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 Reck"""MX-like base classes."""
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport cStringIO
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport struct
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.exception
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.rdata
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.name
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass MXBase(dns.rdata.Rdata):
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Base class for rdata that is like an MX record.
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar preference: the preference value
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type preference: int
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar exchange: the exchange name
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type exchange: dns.name.Name object"""
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    __slots__ = ['preference', 'exchange']
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def __init__(self, rdclass, rdtype, preference, exchange):
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(MXBase, self).__init__(rdclass, rdtype)
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.preference = preference
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.exchange = exchange
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_text(self, origin=None, relativize=True, **kw):
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        exchange = self.exchange.choose_relativity(origin, relativize)
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return '%d %s' % (self.preference, exchange)
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        preference = tok.get_uint16()
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        exchange = tok.get_name()
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        exchange = exchange.choose_relativity(origin, relativize)
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        tok.get_eol()
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, preference, exchange)
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_text = classmethod(from_text)
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        pref = struct.pack("!H", self.preference)
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(pref)
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.exchange.to_wire(file, compress, origin)
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_digestable(self, origin = None):
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return struct.pack("!H", self.preference) + \
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            self.exchange.to_digestable(origin)
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (preference, ) = struct.unpack('!H', wire[current : current + 2])
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += 2
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= 2
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (exchange, cused) = dns.name.from_wire(wire[: current + rdlen],
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                               current)
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if cused != rdlen:
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            raise dns.exception.FormError
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if not origin is None:
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            exchange = exchange.relativize(origin)
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, preference, exchange)
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_wire = classmethod(from_wire)
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def choose_relativity(self, origin = None, relativize = True):
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.exchange = self.exchange.choose_relativity(origin, relativize)
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def _cmp(self, other):
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        sp = struct.pack("!H", self.preference)
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        op = struct.pack("!H", other.preference)
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        v = cmp(sp, op)
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if v == 0:
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            v = cmp(self.exchange, other.exchange)
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return v
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass UncompressedMX(MXBase):
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Base class for rdata that is like an MX record, but whose name
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    is not compressed when converted to DNS wire format, and whose
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    digestable form is not downcased."""
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(UncompressedMX, self).to_wire(file, None, origin)
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_digestable(self, origin = None):
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        f = cStringIO.StringIO()
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.to_wire(f, None, origin)
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return f.getvalue()
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass UncompressedDowncasingMX(MXBase):
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Base class for rdata that is like an MX record, but whose name
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    is not compressed when convert to DNS wire format."""
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(UncompressedDowncasingMX, self).to_wire(file, None, origin)
106