81 # reverse stage |
83 # reverse stage |
82 parser.add_option('--reverse-domain', metavar='DOMAIN', |
84 parser.add_option('--reverse-domain', metavar='DOMAIN', |
83 help="Domain to use for hosts in reverse zone") |
85 help="Domain to use for hosts in reverse zone") |
84 |
86 |
85 parser.add_option('--reverse-zone', metavar='NET', |
87 parser.add_option('--reverse-zone', metavar='NET', |
86 help="Generate forward zone for given subnet (x.z.y)") |
88 help="Generate forward zone for given subnet (x.z.y | a:b:c:d)") |
87 |
89 |
88 # |
90 # |
89 parser.add_option('--doctest', action='store_true', |
91 parser.add_option('--doctest', action='store_true', |
90 help="Run module doctests") |
92 help="Run module doctests") |
91 |
93 |
643 timestamp = r.line.timestamp |
645 timestamp = r.line.timestamp |
644 |
646 |
645 if timestamp : |
647 if timestamp : |
646 yield TXTRecord(r.name, timestamp.strftime(TIMESTAMP_FORMAT), ttl=r.ttl) |
648 yield TXTRecord(r.name, timestamp.strftime(TIMESTAMP_FORMAT), ttl=r.ttl) |
647 |
649 |
648 def reverse_addr (ip) : |
650 def reverse_ipv4 (ip) : |
649 """ |
651 """ |
650 Return in-addr.arpa reverse for given IPv4 IP. |
652 Return in-addr.arpa reverse for given IPv4 prefix. |
651 """ |
653 """ |
652 |
654 |
653 # parse |
655 # parse |
654 octets = tuple(int(part) for part in ip.split('.')) |
656 octets = tuple(int(part) for part in ip.split('.')) |
655 |
657 |
656 for octet in octets : |
658 for octet in octets : |
657 assert 0 <= octet <= 255 |
659 assert 0 <= octet <= 255 |
658 |
660 |
659 return '.'.join([str(octet) for octet in reversed(octets)] + ['in-addr', 'arpa']) |
661 return '.'.join([str(octet) for octet in reversed(octets)] + ['in-addr', 'arpa']) |
|
662 |
|
663 def reverse_ipv6 (ip6) : |
|
664 """ |
|
665 Return ip6.arpa reverse for given IPv6 prefix. |
|
666 """ |
|
667 |
|
668 parts = [int(part, 16) for part in ip6.split(':')] |
|
669 parts = ['{0:04x}'.format(part) for part in parts] |
|
670 parts = ''.join(parts) |
|
671 |
|
672 return '.'.join(tuple(reversed(parts)) + ( 'ip6', 'arpa')) |
660 |
673 |
661 def fqdn (*parts) : |
674 def fqdn (*parts) : |
662 fqdn = '.'.join(parts) |
675 fqdn = '.'.join(parts) |
663 |
676 |
664 # we may be given an fqdn in parts |
677 # we may be given an fqdn in parts |
670 def process_zone_reverse (zone, origin, domain) : |
683 def process_zone_reverse (zone, origin, domain) : |
671 """ |
684 """ |
672 Process zone data -> reverse zone data. |
685 Process zone data -> reverse zone data. |
673 """ |
686 """ |
674 |
687 |
|
688 name = None |
|
689 |
675 for r in zone : |
690 for r in zone : |
676 if r.type != 'A' : |
691 # keep name from previous.. |
|
692 if r.name : |
|
693 name = r.name |
|
694 |
|
695 if r.type == 'A' : |
|
696 ip, = r.data |
|
697 ptr = reverse_ipv4(ip) |
|
698 |
|
699 elif r.type == 'AAAA' : |
|
700 ip, = r.data |
|
701 ptr = reverse_ipv6(ip) |
|
702 |
|
703 else : |
677 continue |
704 continue |
678 |
705 |
679 ip, = r.data |
|
680 |
|
681 # generate reverse-addr |
|
682 reverse = reverse_addr(ip) |
|
683 |
|
684 # verify |
706 # verify |
685 if zone and reverse.endswith(origin) : |
707 if zone and ptr.endswith(origin) : |
686 reverse = reverse[:-(len(origin) + 1)] |
708 ptr = ptr[:-(len(origin) + 1)] |
687 |
709 |
688 else : |
710 else : |
689 log.warning("Reverse does not match zone origin, skipping: (%s) -> %s <-> %s", ip, reverse, origin) |
711 log.warning("Reverse does not match zone origin, skipping: (%s) -> %s <-> %s", ip, ptr, origin) |
690 continue |
712 continue |
691 |
713 |
692 # domain to use |
714 # domain to use |
693 host_domain = r.origin or domain |
715 host_domain = r.origin or domain |
694 host_fqdn = fqdn(r.name, host_domain) |
716 host_fqdn = fqdn(name, host_domain) |
695 |
717 |
696 yield ZoneRecord(reverse, 'PTR', [host_fqdn]) |
718 yield ZoneRecord(ptr, 'PTR', [host_fqdn]) |
697 |
719 |
698 def write_zone_records (file, zone) : |
720 def write_zone_records (file, zone) : |
699 for r in zone : |
721 for r in zone : |
700 file.write(r.build_line() + u'\n') |
722 file.write(r.build_line() + u'\n') |
701 |
723 |
775 return 1 |
797 return 1 |
776 |
798 |
777 zone = list(process_zone_meta(zone, ignore=set(options.meta_ignore))) |
799 zone = list(process_zone_meta(zone, ignore=set(options.meta_ignore))) |
778 |
800 |
779 elif options.reverse_zone : |
801 elif options.reverse_zone : |
780 origin = reverse_addr(options.reverse_zone) |
802 if ':' in options.reverse_zone : |
|
803 # IPv6 |
|
804 origin = reverse_ipv6(options.reverse_zone) |
|
805 |
|
806 else : |
|
807 # IPv4 |
|
808 origin = reverse_ipv4(options.reverse_zone) |
|
809 |
781 domain = options.reverse_domain |
810 domain = options.reverse_domain |
782 |
811 |
783 if not domain : |
812 if not domain : |
784 log.error("--reverse-zone requires --reverse-domain") |
813 log.error("--reverse-zone requires --reverse-domain") |
785 return 1 |
814 return 1 |