# Third party modules
import six
+import requests
+
+from six.moves.urllib.parse import urlunsplit
# Own modules
-from .common import pp, to_bool
+from .common import pp, to_bool, to_bytes
from .cfg_app import PpCfgAppError, PpConfigApplication
-__version__ = '0.2.1'
+__version__ = '0.3.0'
LOG = logging.getLogger(__name__)
default_pdns_api_host = 'systemshare.pixelpark.com'
default_pdns_api_port = 8081
- default_pdns_api_root_path = '/api/v1/servers/localhost'
+ default_pdns_api_root_path = '/api/v1'
+ default_pdns_api_server_id = 'localhost'
default_named_conf = '/etc/named.conf'
default_named_zones_cfg_dir = '/etc/named'
default_named_basedir = '/var/named'
re_split_addresses = re.compile(r'[,;\s]+')
re_integer = re.compile(r'^\s*(\d+)\s*$')
+ re_ipv4_zone = re.compile(r'^((?:\d+\.)+)in-addr\.arpa\.$')
+ re_ipv6_zone = re.compile(r'^((?:[\da-f]\.)+)ip6\.arpa\.$')
+
# -------------------------------------------------------------------------
def __init__(self, appname=None, version=__version__):
self.pdns_api_host = self.default_pdns_api_host
self.pdns_api_port = self.default_pdns_api_port
self.pdns_api_root_path = self.default_pdns_api_root_path
+ self.pdns_api_server_id = self.default_pdns_api_server_id
self.pdns_api_key = None
self.is_internal = False
self.named_show_bind_version = False
self.named_version2show = self.default_named_version2show
+ self.zones = {}
+
description = textwrap.dedent('''\
Generation of configuration of named (the BIND 9 name daemon).
''').strip()
section, section_name, 'root_path',
'pdns_api_root_path', True, 'root path of the PowerDNS')
+ if 'server_id' in section and section['server_id'].strip():
+ self.pdns_api_server_id = section['server_id'].strip().lower()
+
if 'key' in section:
key = section['key'].strip()
self.pdns_api_key = key
if os.geteuid():
LOG.error("You must be root to execute this script.")
self.exit(1)
- LOG.info("Jetzt geht's looos")
+
+ self.get_api_zones()
+
+ # -------------------------------------------------------------------------
+ def get_api_zones(self):
+
+ LOG.info("Trying to get all zones from PDNS API ...")
+
+ headers = {}
+ if self.pdns_api_key:
+ headers['X-API-Key'] = self.pdns_api_key
+
+ path = os.path.join(
+ self.pdns_api_root_path, 'servers', self.pdns_api_server_id, 'zones')
+ server = self.pdns_api_host
+ if self.pdns_api_port != 80:
+ server = '{}:{}'.format(server, self.pdns_api_port)
+ url = urlunsplit(('http', server, path, None, None))
+ LOG.debug("URL to send API call: {!r}.".format(url))
+ if self.verbose > 1:
+ LOG.debug("Headers:\n%s", pp(headers))
+ session = requests.Session()
+ response = session.request(
+ 'GET', url, headers=headers, timeout=10)
+ if self.verbose > 1:
+ LOG.debug("Response status code: {}".format(response.status_code))
+ if not response.ok:
+ try:
+ err = response.json()
+ code = err['httpStatus']
+ msg = err['messages']
+ LOG.error("Got an error from API ({}) with status {}: {}".format(
+ url, code, msg))
+ self.exit(6)
+ except ValueError:
+ msg = 'Failed to parse the response from {!r}: {}'.format(
+ url, response.text)
+ LOG.error(msg)
+ self.exit(6)
+
+ json_response = response.json()
+ if self.verbose > 3:
+ LOG.debug("Got a response:\n{}".format(pp(json_response)))
+
+ for entry in json_response:
+
+# { 'account': '',
+# 'dnssec': False,
+# 'id': '56.66.217.in-addr.arpa.',
+# 'kind': 'Master',
+# 'last_check': 0,
+# 'masters': [],
+# 'name': '56.66.217.in-addr.arpa.',
+# 'notified_serial': 2017080202,
+# 'serial': 2017080202,
+# 'url': 'api/v1/servers/localhost/zones/56.66.217.in-addr.arpa.'},
+
+ zone_name = entry['name']
+ zone = {
+ 'account': entry['account'],
+ 'kind': entry['kind'],
+ 'serial': entry['serial'],
+ }
+
+ if entry['dnssec']:
+ self.named_dnssec = True
+ if self.verbose > 1:
+ LOG.debug("Found zone {!r}.".format(zone_name))
+
+ uni_name = None
+ match = self.re_ipv4_zone.search(zone_name)
+ if match:
+ prefix = self._get_ipv4_prefix(match.group(1))
+ if prefix:
+ uni_name = 'rev.' + prefix
+
+ match = self.re_ipv6_zone.search(zone_name)
+ if match:
+ prefix = self._get_ipv6_prefix(match.group(1))
+ if prefix:
+ uni_name = 'rev.' + prefix
+
+ if not uni_name:
+ uni_name = zone_name.encode('utf-8').decode('idna')
+
+ zone['canoniical_name'] = uni_name
+
+ self.zones[zone_name] = zone
+
+ if self.verbose > 2:
+ LOG.debug("Got zones:\n{}".format(pp(self.zones)))
+
+ # -------------------------------------------------------------------------
+ def _get_ipv4_prefix(self, match):
+
+ tuples = []
+ for t in match.split('.'):
+ if t:
+ tuples.insert(0, t)
+ LOG.debug("Got IPv4 tuples: {}".format(pp(tuples)))
+ return '.'.join(tuples)
+
+ # -------------------------------------------------------------------------
+ def _get_ipv6_prefix(self, match):
+
+ tuples = []
+ for t in match.split('.'):
+ if t:
+ tuples.insert(0, t)
+ LOG.debug("Got IPv6 tuples: {}".format(pp(tuples)))
+
+ tokens = []
+ while len(tuples):
+ token = ''.join(tuples[0:4]).ljust(4, '0')
+ if token.startswith('000'):
+ token = token[3:]
+ elif token.startswith('00'):
+ token = token[2:]
+ elif token.startswith('0'):
+ token = token[1:]
+ tokens.append(token)
+ del tuples[0:4]
+
+ return ':'.join(tokens)
# =============================================================================