bin/process-zone
changeset 603 b58236f9ea7b
parent 558 840092ee4d97
equal deleted inserted replaced
602:87c5570a205f 603:b58236f9ea7b
     9 import optparse
     9 import optparse
    10 import codecs
    10 import codecs
    11 from datetime import datetime
    11 from datetime import datetime
    12 import logging
    12 import logging
    13 
    13 
       
    14 import ipaddr
       
    15 
    14 log = logging.getLogger('main')
    16 log = logging.getLogger('main')
    15 
    17 
    16 # command-line options, global state
    18 # command-line options, global state
    17 options = None
    19 options = None
    18 
    20 
    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 
   242 
   244 
   243         if not parts :
   245         if not parts :
   244             # skip
   246             # skip
   245             return
   247             return
   246         
   248         
   247         # indented lines don't have name
   249         # XXX: indented lines keep name from previous record
   248         if line.indent :
   250         if line.indent :
   249             name = None
   251             name = None
   250 
   252 
   251         else :
   253         else :
   252             name = parts.pop(0)
   254             name = parts.pop(0)
   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