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 Reckimport socket
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport struct
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.ipv4
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.rdata
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_proto_tcp = socket.getprotobyname('tcp')
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_proto_udp = socket.getprotobyname('udp')
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass WKS(dns.rdata.Rdata):
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """WKS record
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar address: the address
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type address: string
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar protocol: the protocol
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type protocol: int
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @ivar bitmap: the bitmap
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type bitmap: string
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @see: RFC 1035"""
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    __slots__ = ['address', 'protocol', 'bitmap']
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def __init__(self, rdclass, rdtype, address, protocol, bitmap):
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        super(WKS, self).__init__(rdclass, rdtype)
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.address = address
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.protocol = protocol
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self.bitmap = bitmap
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_text(self, origin=None, relativize=True, **kw):
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        bits = []
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        for i in xrange(0, len(self.bitmap)):
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            byte = ord(self.bitmap[i])
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            for j in xrange(0, 8):
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                if byte & (0x80 >> j):
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    bits.append(str(i * 8 + j))
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        text = ' '.join(bits)
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return '%s %d %s' % (self.address, self.protocol, text)
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        address = tok.get_string()
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        protocol = tok.get_string()
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if protocol.isdigit():
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            protocol = int(protocol)
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        else:
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            protocol = socket.getprotobyname(protocol)
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        bitmap = []
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        while 1:
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            token = tok.get().unescape()
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if token.is_eol_or_eof():
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                break
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if token.value.isdigit():
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                serv = int(token.value)
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            else:
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                if protocol != _proto_udp and protocol != _proto_tcp:
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    raise NotImplementedError("protocol must be TCP or UDP")
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                if protocol == _proto_udp:
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    protocol_text = "udp"
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                else:
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    protocol_text = "tcp"
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                serv = socket.getservbyname(token.value, protocol_text)
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            i = serv // 8
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            l = len(bitmap)
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if l < i + 1:
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                for j in xrange(l, i + 1):
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    bitmap.append('\x00')
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8)))
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        bitmap = dns.rdata._truncate_bitmap(bitmap)
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, address, protocol, bitmap)
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_text = classmethod(from_text)
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def to_wire(self, file, compress = None, origin = None):
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(dns.ipv4.inet_aton(self.address))
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        protocol = struct.pack('!B', self.protocol)
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(protocol)
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        file.write(self.bitmap)
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        address = dns.ipv4.inet_ntoa(wire[current : current + 4])
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        protocol, = struct.unpack('!B', wire[current + 4 : current + 5])
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current += 5
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        rdlen -= 5
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        bitmap = wire[current : current + rdlen]
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return cls(rdclass, rdtype, address, protocol, bitmap)
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    from_wire = classmethod(from_wire)
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    def _cmp(self, other):
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        sa = dns.ipv4.inet_aton(self.address)
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        oa = dns.ipv4.inet_aton(other.address)
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        v = cmp(sa, oa)
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if v == 0:
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            sp = struct.pack('!B', self.protocol)
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            op = struct.pack('!B', other.protocol)
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            v = cmp(sp, op)
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if v == 0:
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                v = cmp(self.bitmap, other.bitmap)
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return v
114