#!/usr/bin/python2.6 # # Univention IP Calculator """helper script: base on IP adress and network mask the script calculates brodcast address, network address and DNS entries""" # # Copyright 2004-2012 Univention GmbH # # http://www.univention.de/ # # All rights reserved. # # The source code of this program is made available # under the terms of the GNU Affero General Public License version 3 # (GNU AGPL V3) as published by the Free Software Foundation. # # Binary versions of this program provided by Univention to you as # well as other copyrighted, protected or trademarked materials like # Logos, graphics, fonts, specific documentations and configurations, # cryptographic keys etc. are subject to a license agreement between # you and Univention and not subject to the GNU AGPL V3. # # In the case you use this program under the terms of the GNU AGPL V3, # the program is provided in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public # License with the Debian GNU/Linux or Univention distribution in file # /usr/share/common-licenses/AGPL-3; if not, see # . import optparse import sys import copy def parse_ipv4(addr): """Parse IPv4 address. >>> parse_ipv4('1.2.3.4') [1, 2, 3, 4] >>> parse_ipv4('1.2.3') Traceback (most recent call last): ... ValueError: need 4 octets >>> parse_ipv4('1.2.3.4.5') Traceback (most recent call last): ... ValueError: need 4 octets >>> parse_ipv4('1.2.3.-4') Traceback (most recent call last): ... ValueError: need 4 octets >>> parse_ipv4('1.2.3.256') Traceback (most recent call last): ... ValueError: need 4 octets """ nibbles = addr.split('.') nibbles = [int(_) for _ in nibbles] if len(nibbles) != 4: raise ValueError('need 4 octets') if min(nibbles) < 0 or max(nibbles) > 255: raise ValueError('need 4 octets') return nibbles def format_ipv4(addr, extend=None): """Format IPv4 address. >>> format_ipv4([1, 2, 3, 4]) '1.2.3.4' >>> format_ipv4([1, 2, 3], extend=0) '1.2.3.0' """ if extend is not None: addr = (addr + [extend, extend, extend, extend])[:4] return '.'.join(('%d' % (_,) for _ in addr)) def parse_options(): """Parse command line options.""" usage = '%prog [options] --ip --netmask [--output ]' epilog = 'Calculate network values from network address for DNS records.' parser = optparse.OptionParser(usage=usage, epilog=epilog) parser.add_option('--ip', dest='ip', help='IPv4 or IPv6 address') parser.add_option('--netmask', dest='netmask', help='Netmask or prefix length') parser.add_option('--output', dest='output', default='all', choices=('network', 'reverse', 'pointer', 'all'), help='Specify requested output typ') parser.add_option('--calcdns', dest='calcdns', action='store_true', help='Request to calcuale DNS record entries') parser.add_option('--quiet', dest='quiet', action='store_true', help='Supress error output') parser.add_option('--full', dest='full', action='store_true', help='Extend network address to 4-tuple') parser.add_option('--test-internal', action='callback', callback=__test, help=optparse.SUPPRESS_HELP) (options, _args) = parser.parse_args() if not options.ip or not options.netmask: if not options.quiet: parser.error("Needed --ip and --netmask") sys.exit(1) try: addr = parse_ipv4(options.ip) except ValueError: if not options.quiet: parser.error('Invalid ip') sys.exit(1) try: netmask = parse_ipv4(options.netmask) except ValueError: if not options.quiet: parser.error('Invalid netmask') sys.exit(1) return addr, netmask, options def calc_dns(addr, netmask): """Calculate values for use in DNS (in nibble resolution). >>> calc_dns([170,85,170,85],[0,0,0,0]) ([], [255, 255, 255, 255], [85, 170, 85, 170]) >>> calc_dns([170,85,170,85],[192,0,0,0]) ([], [255, 255, 255, 255], [85, 170, 85, 170]) >>> calc_dns([170,85,170,85],[255,0,0,0]) ([170], [170, 255, 255, 255], [85, 170, 85]) >>> calc_dns([170,85,170,85],[255,255,255,0]) ([170, 85, 170], [170, 85, 170, 255], [85]) >>> calc_dns([170,85,170,85],[255,255,255,128]) ([170, 85, 170], [170, 85, 170, 255], [85]) >>> calc_dns([170,85,170,85],[255,255,255,255]) ([170, 85, 170], [170, 85, 170, 255], [85]) """ for i in range(0, 4): if netmask[i] < 255: break else: i = 4 network = addr[:i] broadcast = (addr[:i] + [255, 255, 255, 255])[:4] pointer = addr[min(i, 3):] pointer.reverse() return network, broadcast, pointer def calc_full(addr, netmask): """Calculate values in full (bit) resolution. >>> calc_full([170,85,170,85],[0,0,0,0]) ([0, 0, 0, 0], [255, 255, 255, 255], [85, 170, 85, 170]) >>> calc_full([170,85,170,85],[192,0,0,0]) ([128, 0, 0, 0], [191, 255, 255, 255], [85, 170, 85, 170]) >>> calc_full([170,85,170,85],[255,0,0,0]) ([170, 0, 0, 0], [170, 255, 255, 255], [85, 170, 85]) >>> calc_full([170,85,170,85],[255,255,255,0]) ([170, 85, 170, 0], [170, 85, 170, 255], [85]) >>> calc_full([170,85,170,85],[255,255,255,128]) ([170, 85, 170, 0], [170, 85, 170, 127], [85]) >>> calc_full([170,85,170,85],[255,255,255,255]) ([170, 85, 170, 85], [170, 85, 170, 85], [85]) """ network = [a & m for (a, m) in zip(addr, netmask)] broadcast = [a | ~m & 255 for (a, m) in zip(addr, netmask)] for i in range(4): if netmask[i] < 255: pointer = addr[i:] break else: pointer = addr[3:] pointer.reverse() return network, broadcast, pointer def main(): """Calculates brodcast address, network address and DNS entries.""" addr, netmask, options = parse_options() if options.calcdns: network, broadcast, pointer = calc_dns(addr, netmask) else: network, broadcast, pointer = calc_full(addr, netmask) if options.netmask == "255.255.255.255": reverse = network[:-1] else: reverse = network[:] reverse.reverse() if options.output == 'all': print 'Network: %s' % format_ipv4(network) print 'Reverse: %s' % format_ipv4(reverse) print 'Pointer: %s' % format_ipv4(pointer) if options.full: print 'Network full: %s' % format_ipv4(network, extend=0) print 'Broadcast: %s' % format_ipv4(broadcast) elif options.output == 'network': print format_ipv4(network, extend=0 if options.full else None) elif options.output == 'reverse': print format_ipv4(reverse) elif options.output == 'pointer': print format_ipv4(pointer) elif options.output == 'broadcast': print format_ipv4(broadcast) def __test(_option, _opt_str, _value, _parser): """Run internal test suite.""" import doctest res = doctest.testmod() sys.exit(int(bool(res[0]))) if __name__ == '__main__': main()