{"id":25077390,"url":"https://github.com/egberts/bind9_parser","last_synced_at":"2025-04-15T02:52:17.996Z","repository":{"id":39901917,"uuid":"206634194","full_name":"egberts/bind9_parser","owner":"egberts","description":"Bind9 Parser in Python that can process all of ISC Bind configuration files","archived":false,"fork":false,"pushed_at":"2023-02-03T20:39:49.000Z","size":40243,"stargazers_count":23,"open_issues_count":9,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-15T02:52:09.319Z","etag":null,"topics":["bind9","bind9-configuration","pyparsing","python","python3"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/egberts.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-05T18:45:10.000Z","updated_at":"2025-01-19T22:10:56.000Z","dependencies_parsed_at":"2023-02-18T10:46:04.942Z","dependency_job_id":null,"html_url":"https://github.com/egberts/bind9_parser","commit_stats":null,"previous_names":["egberts/bind9-parser"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egberts%2Fbind9_parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egberts%2Fbind9_parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egberts%2Fbind9_parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egberts%2Fbind9_parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/egberts","download_url":"https://codeload.github.com/egberts/bind9_parser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248997086,"owners_count":21195797,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bind9","bind9-configuration","pyparsing","python","python3"],"created_at":"2025-02-07T02:24:57.754Z","updated_at":"2025-04-15T02:52:17.979Z","avatar_url":"https://github.com/egberts.png","language":"Python","readme":"# bind9-parser\n\nYou got `named.conf`?  Itching to read it and work with it ... in Python?\n\nNow we can parse `named.conf` with relative ease using Python.  Could even output this as JSON so ANY language can read `named.conf`.\n\n[PyParsing](https://github.com/pyparsing/pyparsing) is our friend, and there are some 2,400 BNF syntax elements for `named.conf` ... in Python3!\n\n# Features\n\n* Pythonized `named.conf` settings (DONE)\n* JSON output (DONE)\n* Schema lookup (DONE)\n* offline local search engine on all Bind9 clauses, statements, and keywords. (DONE)\n* Python chaining the setters/getters of `view`/`zone` clauses (DONE)\n* Outputting `named.conf` (IN-PROGRES)\n\n# Introduction\n\nbind9_parser is a pythonized token constructor of `named.conf` configuration file \nused in ISC Bind9 DNS name server daemon.\n\nbind9_parser parses a text-based `named.conf` containing ISC Bind9 configuration settings, such as:\n\n```nginx\noptions {\n    recursion yes;\n    };\nzone example.test IN {\n    file \"db.example.test.master\";\n    };\n```\n\nbind9_parser constructs a token tree of all settings found in the `named.conf`.\n\n```python\nnamed_conf_tokens = {\n  'options': [{'recursion': 'yes'}],\n  'zone': [\n    {\n      'zone_name': 'example.test',\n      'class': 'IN',\n      'file': 'db.example.test.master'\n    } ] }\n```\n\nTokenize `named.conf` variable consists of `dict` and `list` to ameliorate and preserve the many `1:1`\n, `1:M`, `1:*`, and `N:M` relationships that are defined between its clause and statement keywords.\n\n# Design\n\nToken parser is chosen here for the primary purpose of performing automated checking of its valid settings. No concrete \nsyntax tree (and certainly no abstract syntax tree either).\n\nThis is about as simple as getting and setting configurations with like an `.INI` file, but with the complexity of `named.conf`\ninstead.\n\n# Token Parser Design\n\n`PyParsing` library (v2.9+) is used to ensure accurate token extractions of `named.conf` settings and generates a single\nvariable containing the entire file.\n\nLatest (and some 2,500-odd) EBNFs for ISC Bind9 `named.conf` have been incorporated and all of its clauses and\nstatements each have their own independently-usable `ParseElement` (an aspect of EBNF but in `PyParsing` parlance) that can be used to analyze even\nsmaller text portion of its configuration file.\n\nSince there are no abstraction within `named.conf` (except maybe for the `addresses_match_list`), there is no need for\nan AST.\n\nNo support for concrete syntax tree (CST) means no saving of comment lines, formatting, nor extraneous whitespaces. Sole\npurpose of this design is to get all the raw `named.conf` settings. No CST support also means no way to reconstruct the\noriginal file containing such annotation.\n\nIt is all about extracting the settings. Writing it back out into a `named.conf`-style file has become a secondary goal\nhere because too many passive security tools awaits this (pending) outputter() feature.\n\n# Python  Design\n\nThe token tree consists of a Pythonized `dict`/`list` that is fully readable by various built-in JSON APIs.\n\nThere is a work-in-progress DESIGN document that will:\n\n* tokenize `named.conf` (DONE)\n* Python chaining the setters/getters of `view`/`zone` clauses (DONE)\n* Outputting `named.conf` from its tokenized Python variable (IN-PROGRES)\n\n[DESIGN-work-in-progress.md](DESIGN-work-in-progress.md)\n\n# Examples\n\nA working example of:\n\n```nginx\n    acl MY_BASTION_HOSTS { 4.4.4.4; 3.3.3.3; 2.2.2.2; 1.1.1.1; };\n    controls { inet 128.0.0.9 port 8006 allow { 128.0.0.10; 128.0.0.11;} read-only yes; };\n    dlz your_IBM_2 { database \"RSDMS\"; search no; };\n    dyndb \"example-ldap\" \"/usr/lib64/bind/ldap.so\" { \n        uri \"ldap://ldap.example.com\"; \n        base \"cn=dns, dc=example,dc=com\"; \n        auth_method \"none\"; };\n    key dyndns { algorithm hmac-sha512; secret ABCDEFG; };\n    logging { channel salesfolks { file \"/tmp/sales.log\" size 5M; severity info; print-time no;};\n              channel accounting { file \"/tmp/acct.log\" size 30M; severity info; print-time no; };\n              channel badguys { file \"/tmp/alert\" size 255G; severity debug 77; print-time yes;}; };\n    managed-keys { www1.www.example.com initial-key 1 1 1 \"ASBASDASD\"; };\n    masters bastion_host_group { bastion_hosts22; hidden_bastion; };\n    zone red { file \"/var/lib/bind9/public/masters/db.example.com\"; };\n    server 3.4.5.6 { bogus yes; edns no; edns-udp-size 102; edns-version 2;\n                     keys my_key_name_to_private_dns; max-udp-size 32768; notify-source *; notify-source-v6 *;\n                     padding 53; provide-ixfr yes; query-source *; query-source address *; query-source-v6 *;\n                     request-expire yes; request-ixfr yes; request-nsid yes; send-cookie yes; tcp-keepalive yes;\n                     tcp-only yes; transfer-format one-answer; transfer-source *; transfer-source-v6 *; transfers 36; };\n    trusted-keys { abc 1 1 1 \"ASBASDASD\";};\n    zone green { file \"/var/lib/bind9/public/masters/db.green.com\"; };\n    masters dmz_masters port 7553 dscp 5 { 10.0.0.1 key priv_dns_chan_key5; };\n```\n\nresults in producing this variable:\n\n```python\nnamed_conf_elements = {\n    'acl': [{'acl_name': 'MY_BASTION_HOSTS',\n             'aml_series': [{'aml': [{'ip4_addr': '4.4.4.4'},\n                                     {'ip4_addr': '3.3.3.3'},\n                                     {'ip4_addr': '2.2.2.2'},\n                                     {'ip4_addr': '1.1.1.1'}]}]}],\n    'controls': [{'inet': {'allow': {'aml': [{'ip4_addr': '128.0.0.10'},\n                                             {'ip4_addr': '128.0.0.11'}]},\n                           'control_server_addr': '128.0.0.9',\n                           'ip_port_w': '8006',\n                           'read-only': 'yes'}}],\n    'dlz': [{'db_args': 'RSDMS',\n             'dlz_name': 'your_IBM_2',\n             'search': 'no'}],\n    'dyndb': [{'db_name': '\"example-ldap\"',\n               'driver_parameters': 'uri '\n                                    '\"ldap://ldap.example.com\"; \\n'\n                                    '        base \"cn=dns, '\n                                    'dc=example,dc=com\"; \\n'\n                                    '        auth_method '\n                                    '\"none\"; ',\n               'module_filename': '/usr/lib64/bind/ldap.so'}],\n    'key': [{'algorithm': 'hmac-sha512',\n             'key_id': 'dyndns',\n             'secret': 'ABCDEFG'}],\n    'logging': {'channels': [{'channel_name': 'salesfolks',\n                              'path_name': '/tmp/sales.log',\n                              'print_time': 'no',\n                              'severity': ['info'],\n                              'size_spec': [5, 'M']},\n                             {'channel_name': 'accounting',\n                              'path_name': '/tmp/acct.log',\n                              'print_time': 'no',\n                              'severity': ['info'],\n                              'size_spec': [30, 'M']},\n                             {'channel_name': 'badguys',\n                              'path_name': '/tmp/alert',\n                              'print_time': 'yes',\n                              'severity': {'debug': {'debug_level': 77}},\n                              'size_spec': [255, 'G']}]},\n    'managed_keys': [{'algorithm_id': 1,\n                      'flags': 1,\n                      'key_secret': '\"ASBASDASD\"',\n                      'protocol_id': 1,\n                      'rr_domain': 'www1.www.example.com'}],\n    'primaries': [{'dscp_port': 5,\n                   'ip_port': '7553',\n                   'primary_id': 'dmz_masters',\n                   'primary_list': [{'ip4_addr': '10.0.0.1',\n                                     'key_id': 'priv_dns_chan_key5'}]}],\n    'server': [{'configs': {'bogus': 'yes',\n                            'edns': 'no',\n                            'edns_udp_size': 102,\n                            'edns_version': 2,\n                            'keys': 'my_key_name_to_private_dns',\n                            'max_udp_size': 32768,\n                            'notify_source': {'ip4_addr': '*'},\n                            'notify_source_v6': {'ip6_addr_w': '*'},\n                            'padding': 53,\n                            'provide_ixfr': 'yes',\n                            'query_source': {'ip4_addr_w': '*'},\n                            'query_source_v6': {'ip6_addr_w': '*'},\n                            'request_expire': 'yes',\n                            'request_ixfr': 'yes',\n                            'request_nsid': 'yes',\n                            'send_cookie': 'yes',\n                            'tcp_keepalive': 'yes',\n                            'tcp_only': 'yes',\n                            'transfer_format': 'one-answer',\n                            'transfer_source': {'ip4_addr_w': '*'},\n                            'transfer_source_v6': {'ip6_addr': '*'},\n                            'transfers': 36},\n                'ip_addr': '3.4.5.6'}],\n    'trusted_keys': [{'algorithm_id': '1',\n                      'domain': 'abc',\n                      'key_id': '1',\n                      'protocol_type': '1',\n                      'pubkey_base64': 'ASBASDASD'}],\n    'zones': [{'file': '/var/lib/bind9/public/masters/db.example.com',\n               'zone_name': 'red'},\n              {'file': '/var/lib/bind9/public/masters/db.green.com',\n               'zone_name': 'green'}]}\n)\n```\n\n# Full-Blown Examples\n\n```python\nparse_result_named_conf = {\n    'acl': [\n        {'acl_name': 'MY_BASTION_HOSTS',\n         'aml_series': [{'aml': [{'ip4_addr': '4.4.4.4'},\n                                 {'ip4_addr': '3.3.3.3'},\n                                 {'ip4_addr': '2.2.2.2'},\n                                 {'ip4_addr': '1.1.1.1'}]}]}],\n    'controls': [{'inet': {'allow': {'aml': [{'ip4_addr': '128.0.0.10'},\n                                             {'ip4_addr': '128.0.0.11'}]},\n                           'control_server_addr': '128.0.0.9',\n                           'ip_port_w': '8006',\n                           'read-only': 'yes'}}],\n    'dlz': [{'db_args': 'RSDMS',\n             'dlz_name': 'your_IBM_2',\n             'search': 'no'}],\n    'dyndb': [{'db_name': '\"example-ldap\"',\n               'driver_parameters': 'uri '\n                                    '\"ldap://ldap.example.com\"; '\n                                    'base \"cn=dns, '\n                                    'dc=example,dc=com\"; '\n                                    'auth_method \"none\"; ',\n               'module_filename': '/usr/lib64/bind/ldap.so'}],\n    'key': [{'algorithm': 'hmac-sha512',\n             'key_id': 'dyndns',\n             'secret': 'ABCDEFG'}],\n    'logging': [{'channel': [{'channel_name': 'salesfolks',\n                              'path_name': '/tmp/sales.log',\n                              'print_time': 'no',\n                              'severity': ['info'],\n                              'size_spec': [5, 'M']}]},\n                {'channel': [{'channel_name': 'accounting',\n                              'path_name': '/tmp/acct.log',\n                              'print_time': 'no',\n                              'severity': ['info'],\n                              'size_spec': [30, 'M']}]},\n                {'channel': [{'channel_name': 'badguys',\n                              'path_name': '/tmp/alert',\n                              'print_time': 'yes',\n                              'severity': {'debug': [77]},\n                              'size_spec': [255, 'G']}]}],\n    'managed_keys': [{'algorithm_id': 1,\n                      'flags': 1,\n                      'key_secret': '\"ASBASDASD\"',\n                      'protocol_id': 1,\n                      'rr_domain': 'www1.www.example.com'}],\n    'options': [{\n        'action': 'drop',\n        'allow-recursion': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow-recursion-on': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow_new_zones': 'yes',\n        'allow_notify': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow_query': {'aml': [{'keyword': 'any'}]},\n        'allow_query_cache': {'aml': [{'keyword': 'none'}]},\n        'allow_query_cache_on': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow_query_on': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow_transfer': {'aml': [{'ip4_addr': '127.0.0.1'}],\n                           'ip_port': '855'},\n        'allow_update': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'allow_update_forwarding': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'also-notify': {'port': '856',\n                        'remote': [{'ip_addr': '127.0.0.1',\n                                    'key_id': 'ABC_KEY',\n                                    'tls_algorithm_name': 'SSLv3'}]},\n        'alt_transfer_source': {'dscp_port': 1, 'ip_port_w': '*'},\n        'alt_transfer_source_v6': {'dscp_port': 2, 'ip_port_w': '*'},\n        'answer-cookie': 'no',\n        'attach_cache': 'ABC_CACHE',\n        'auth_nxdomain': 'no',\n        'auto_dnssec': 'off',\n        'automatic_interface_scan': 'no',\n        'avoid_v4_udp_ports': ['1', '2', '3'],\n        'avoid_v6_udp_ports': ['4', '5', '6'],\n        'bindkeys_file': 'dir/file',\n        'blackhole': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'check_dup_records': 'warn',\n        'check_integrity': 'no',\n        'check_mx': 'fail',\n        'check_mx_cname': 'ignore',\n        'check_names': [{'result_status': 'ignore',\n                         'zone_type': 'primary'}],\n        'check_sibling': 'fail',\n        'check_spf': 'warn',\n        'check_srv_cname': 'fail',\n        'check_wildcard': 'no',\n        'clients_per_query': 10,\n        'cookie_algorithm': 'aes',\n        'cookie_secret': '\"cookie_secret\"',\n        'coresize': ['default'],\n        'datasize': [1, 'G'],\n        'deny_answer_addresses': {'aml': [{'ip4_addr': '127.0.0.1'}],\n                                  'except_from': [{'fqdn': '172.in-addr.arpa.'}]},\n        'deny_answer_aliases': {'except_from': [{'fqdn': '172.in-addr.arpa.'}],\n                                'name_list': ['example.test',\n                                              'test.example']},\n        'dialup': 'notify-passive',\n        'directory': 'dir/file',\n        'disable_algorithms': {'algorithms': ['AES512', 'SHA512'],\n                               'domain_name': 'aaaaaaaaaaaaaaaaa'},\n        'disable_ds_digests': [{'algorithm_name': ['RSASHA512'],\n                                'domain_name': '.'}],\n        'disable_empty_zone': [{'zone_name': '127.in-addr.arpa'}],\n        'dns64': [{'aml': [{'ip4_addr': '127.0.0.1'}],\n                   'break_dnssec': 'yes',\n                   'clients': [{'ip4_addr': '127.0.0.1'}],\n                   'exclude': [{'ip4_addr': '127.0.0.1'}],\n                   'mapped': [{'ip4_addr': '127.0.0.1'}],\n                   'netprefix': {'ip6_addr': '64:ff9b::',\n                                 'prefix': '96'},\n                   'recursive_only': 'no'}],\n        'dns64_contact': {'soa_rname': 'dns64.contact.string.content'},\n        'dns64_server': {'soa_rname': 'dns64.server.string.content'},\n        'dnskey_sig_validity': 3,\n        'dnsrps_enable': 'no',\n        'dnssec_accept_expired': 'no',\n        'dnssec_dnskey_kskonly': 'no',\n        'dnssec_loadkeys_interval': 1,\n        'dnssec_must_be_secure': [{'dnssec_secured': 'yes',\n                                   'fqdn': '\"home.arpa.\"'},\n                                  {'dnssec_secured': 'yes',\n                                   'fqdn': '\"example.test.\"'}],\n        'dnssec_policy': 'my_policy',\n        'dnssec_secure_to_insecure': 'no',\n        'dnssec_update_mode': 'no-resign',\n        'dnssec_validation': 'auto',\n        'dnstap': ['all', 'response'],\n        'dnstap-output': {'path': 'dir/file',\n                          'size': 'unlimited',\n                          'versions': 5},\n        'dnstap-version': 'none',\n        'dscp': 14,\n        'dump_file': 'dir/file',\n        'edns_udp_size': 512,\n        'empty_contact': {'soa_contact_name': 'empty-contact-string-content'},\n        'empty_server': {'soa_contact_name': 'empty-server-string-content'},\n        'empty_zones_enable': 'no',\n        'fetch_quota_params': {'high_threshold': 1.0,\n                               'low_threshold': 1.0,\n                               'moving_average_discount_rate': 1.0,\n                               'moving_avg_recalculate_interval': 5},\n        'fetches_per_server': 5,\n        'fetches_per_zone': 4,\n        'files': {'files_count': 'unlimited'},\n        'flush_zones_on_shutdown': 'no',\n        'forward': 'only',\n        'forwarders': {'forwarder': [{'ip_addr': '127.0.0.1'}],\n                       'ip_port': '753'},\n        'heartbeat_interval': 60,\n        'hostname': {'none': 'none'},\n        'http_listener_clients': 5,\n        'http_port': 80,\n        'http_streams_per_connection': 5,\n        'https_port': 443,\n        'interface_interval': 60,\n        'ip_port': '53',\n        'ipv4only_contact': {'soa_rname': 'ipv4only-contact-string-content'},\n        'ipv4only_enable': 'no',\n        'ipv4only_server': {'soa_rname': 'ipv4only-contact-string-content'},\n        'ixfr_from_differences': 'primary',\n        'keep-response-order': {'aml': [{'ip4_addr': '127.0.0.1'}]},\n        'key_directory': 'dir/file',\n        'lame_ttl': 60,\n        'listen_on': [{'aml': [{'ip4_addr': '127.0.0.1'}],\n                       'http_port': 'HTTP_NAME',\n                       'ip_port': '53',\n                       'tls_port': 'TLS_NAME'}],\n        'listen_on_v6': [{'aml': [{'ip6_addr': '::1'}],\n                          'http_port': 'HTTP_NAME',\n                          'ip_port': '53',\n                          'tls_port': 'TLS_NAME'}],\n        'lmdb_mapsize': {'amount': 1, 'unit': 'M'},\n        'lock_file': 'dir/file',\n        'managed_keys_directory': 'dir/file',\n        'masterfile_format': 'text',\n        'masterfile_style': 'relative',\n        'match_mapped_addresses': 'no',\n        'max-ixfr-ratio': 'unlimited',\n        'max-zone-ttl': 'unlimited',\n        'max_cache_size': ['unlimited'],\n        'max_cache_ttl': '1H',\n        'max_clients_per_query': 60,\n        'max_journal_size': [11, 'M'],\n        'max_ncache_ttl': '1H',\n        'max_records': 5,\n        'max_recursion_depth': 3,\n        'max_recursion_queries': 4,\n        'max_refresh_time': 60,\n        'max_retry_time': 60,\n        'max_rsa_exponent_size': 512,\n        'max_stale_ttl': '16',\n        'max_transfer_idle_in': 5,\n        'max_transfer_idle_out': 5,\n        'max_transfer_time_in': 5,\n        'max_transfer_time_out': 5,\n        'max_udp_size': 5,\n        'memstatistics': 'no',\n        'memstatistics_file': 'dir/file',\n        'message_compression': 'no',\n        'min_cache_ttl': '1D',\n        'min_ncache_ttl': '2d',\n        'min_refresh_time': '1W',\n        'min_retry_time': 1,\n        'minimal_any': 'no',\n        'multi_master': 'no',\n        'new_zones_directory': 'dir/file',\n        'no_case_compress': [{'acl_name': 'example.test'}],\n        'nocookie_udp_size': 512,\n        'notify': 'primary-only',\n        'notify_delay': 60,\n        'notify_rate': 60,\n        'notify_source': {'dscp_port': 4,\n                          'ip4_addr-w': '*',\n                          'ip4_port_w': '*'},\n        'notify_source_v6': {'dscp_port': 5,\n                             'ip6_addr': '*',\n                             'ip_port_w': '*'},\n        'notify_to_soa': 'no',\n        'nsec3_test_zone': 'no',\n        'nta_lifetime': '60m',\n        'nta_recheck': '24h',\n        'nxdomain_redirect': 'redirect.example.test.',\n        'parental_source': {'ip4_addr_w': '127.0.0.1',\n                            'ip_port_w': '12388'},\n        'parental_source_v6': {'ip6_addr_w': 'ffe2::1',\n                               'ip_port_w': '12389'},\n        'preferred_glue': 'AAAA',\n        'prefetch': {'expiry_ttl': 30, 'threshold_ttl': 60},\n        'provide_ixfr': 'no',\n        'qname_minimization': 'relaxed',\n        'query_source': {'ip4_addr': '127.0.0.1'},\n        'query_source_v6': {'ip6_addr': 'fec2::1'},\n        'querylog_boolean': 'no',\n        'rate_limit': [{'all_per_second': 60}],\n        'recursing_file': 'dir/file',\n        'recursion': 'no',\n        'recursive_clients': 60,\n        'request_expire': 'no',\n        'request_ixfr': 'no',\n        'request_nsid': 'no',\n        'require_server_cookie': 'no',\n        'reserved_sockets': 30,\n        'resolver_nonbackoff_tries': 25,\n        'resolver_query_timeout': 24,\n        'resolver_retry_interval': 23,\n        'response-padding': {'aml': [{'ip4_addr': '127.0.0.1'}],\n                             'fqdn': 512},\n        'response_policy': {'add_soa': 'no',\n                            'break_dnssec': 'no',\n                            'dnsrps_enable': 'yes',\n                            'max_policy_ttl': '30S',\n                            'min_ns_dots': 2,\n                            'min_update_interval': '4w',\n                            'nsdname_enable': 'yes',\n                            'nsdname_wait_recurse': 'yes',\n                            'nsip_enable': 'yes',\n                            'nsip_wait_recurse': 'yes',\n                            'qname_wait_recurse': 'yes',\n                            'recursive_only': 'yes',\n                            'zone': [{'add_soa': 'no',\n                                      'log': 'no',\n                                      'max_policy_ttl': '4Y',\n                                      'min_update_interval': '30S',\n                                      'nsdname_enable': 'no',\n                                      'nsip_enable': 'no',\n                                      'policy': ['no-op'],\n                                      'recursive_only': 'no',\n                                      'zone_name': '172.in-addr.arpa.'},\n                                     {'add_soa': 'yes',\n                                      'log': 'yes',\n                                      'max_policy_ttl': '3Y',\n                                      'min_update_interval': '20S',\n                                      'nsdname_enable': 'yes',\n                                      'nsip_enable': 'yes',\n                                      'policy': [[]],\n                                      'recursive_only': 'yes',\n                                      'zone_name': '168.192.in-addr.arpa.'},\n                                     {'add_soa': 'no',\n                                      'log': 'yes',\n                                      'max_policy_ttl': '4Y',\n                                      'min_update_interval': '30S',\n                                      'nsdname_enable': 'no',\n                                      'nsip_enable': 'yes',\n                                      'policy': ['no-op'],\n                                      'recursive_only': 'yes',\n                                      'zone_name': 'example.test.'},\n                                     {'add_soa': 'yes',\n                                      'log': 'yes',\n                                      'max_policy_ttl': '4Y',\n                                      'min_update_interval': '30S',\n                                      'nsdname_enable': 'no',\n                                      'nsip_enable': 'yes',\n                                      'policy': ['no-op'],\n                                      'recursive_only': 'yes',\n                                      'zone_name': 'example2.test.'},\n                                     {'add_soa': 'no',\n                                      'log': 'yes',\n                                      'max_policy_ttl': '4Y',\n                                      'min_update_interval': '30S',\n                                      'nsdname_enable': 'no',\n                                      'nsip_enable': 'yes',\n                                      'policy': ['no-op'],\n                                      'recursive_only': 'yes',\n                                      'zone_name': '172.in-addr.arpa.'}]},\n        'reuseport': 'no',\n        'root_delegation_only': {'domains': ['name1',\n                                             'name2',\n                                             'name3']},\n        'root_key_sentinel': 'no',\n        'rrset_order': [{'name': 'fixed.example', 'order': 'fixed'},\n                        {'name': 'random.example', 'order': 'random'},\n                        {'name': 'cyclic.example', 'order': 'cyclic'},\n                        {'name': 'none.example', 'order': 'none'},\n                        {'order': 'random', 'type': 'NS'},\n                        {'order': 'cyclic'}],\n        'secroots_file': 'dir/file',\n        'send_cookie': 'no',\n        'serial_query_rate': 5,\n        'serial_update_method': 'unixtime',\n        'server_id_name': 'hostname',\n        'servfail_ttl': 1,\n        'session_keyalg': 'hmac-md5',\n        'session_keyfile': 'dir/file',\n        'session_keyname': '\"session_keyname\"',\n        'sig_signing_nodes': 5,\n        'sig_signing_signatures': 5,\n        'sig_signing_type': 6,\n        'sig_validity_interval': 5,\n        'sortlist': {'aml': [{'aml': [{'keyword': 'localhost'},\n                                      {'aml': [{'keyword': 'localnets'},\n                                               {'ip4_addr': '192.168.1.0',\n                                                'prefix': '24'},\n                                               {'aml': [{'ip4_addr': '192.168.2.0',\n                                                         'prefix': '24'},\n                                                        {'ip4_addr': '192.168.3.0',\n                                                         'prefix': '24'}]}]}]},\n                             {'aml': [{'ip4_addr': '192.168.1.0',\n                                       'prefix': '24'},\n                                      {'aml': [{'ip4_addr': '192.168.1.0',\n                                                'prefix': '24'},\n                                               {'aml': [{'ip4_addr': '192.168.2.0',\n                                                         'prefix': '24'},\n                                                        {'ip4_addr': '192.168.3.0',\n                                                         'prefix': '24'}]}]}]}]},\n        'stacksize': ['default'],\n        'stale_answer_client_timeout': 'disabled',\n        'stale_answer_enable': 'no',\n        'stale_answer_ttl': 60,\n        'stale_cache_enable': 'no',\n        'stale_refresh_time': 8,\n        'startup_notify_rate': 5,\n        'statistics_file': 'dir/file',\n        'suppress_initial_notify': 'no',\n        'synth_from_dnssec': 'no',\n        'tcp_advertised_timeout': 60,\n        'tcp_clients': 60,\n        'tcp_idle_timeout': 60,\n        'tcp_initial_timeout': 60,\n        'tcp_keepalive_timeout': 60,\n        'tcp_listen_queue': 60,\n        'tcp_receive_buffer': 60,\n        'tcp_send_buffer': 60,\n        'tkey_dhkey': [{'host_name': 'dhkey_string_content',\n                        'key_tag': 60}],\n        'tkey_domain': '172.in-addr.arpa.',\n        'tkey_gssapi_credential': {'instance': 'kdc1.example.test',\n                                   'primary': 'kadmin',\n                                   'principal': 'kadmin/kdc1.example.test@EXAMPLE.TEST',\n                                   'realm': 'EXAMPLE.TEST'},\n        'tkey_gssapi_keytab': 'directory/file',\n        'tls_port': 60,\n        'transfer_format': 'many-answers',\n        'transfer_message_size': 60,\n        'transfer_source': {'dscp_port': 12,\n                            'ip4_addr': '127.0.0.1',\n                            'ip_port_w': '60'},\n        'transfer_source_v6': {'dscp_port': 11,\n                               'ip6_addr': 'ffec::1',\n                               'ip_port_w': '60'},\n        'transfers_in': 60,\n        'transfers_out': 60,\n        'transfers_per_ns': 60,\n        'trust_anchor_telemetry': 'no',\n        'try_tcp_refresh': 'no',\n        'udp_receive_buffer': 60,\n        'udp_send_buffer': 60,\n        'update_check_ksk': 'no',\n        'use_alt_transfer_source': 'no',\n        'use_v4_udp_ports': {'port_end': 1024, 'port_start': 1},\n        'use_v6_udp_ports': {'port_end': 44315, 'port_start': 1025},\n        'v6_bias': 60,\n        'validate_except': ['168.192.in-addr.arpa.'],\n        'version_string': 'funky dns server, uh?',\n        'zero_no_soa_ttl': 'no',\n        'zero_no_soa_ttl_cache': 'no',\n        'zone_statistics': 'terse'}],\n    'primaries': [{'dscp_port': 5,\n                   'ip_port': '7553',\n                   'primary_id': 'dmz_masters',\n                   'primary_list': [{'ip4_addr': '10.0.0.1',\n                                     'key_id': 'priv_dns_chan_key5'}]}],\n    'server': [{'configs': {'bogus': 'yes',\n                            'edns': 'no',\n                            'edns_udp_size': 102,\n                            'edns_version': 2,\n                            'keys': 'my_key_name_to_private_dns',\n                            'max_udp_size': 32768,\n                            'notify_source': {'ip4_addr': '*'},\n                            'notify_source_v6': {'ip6_addr_w': '*'},\n                            'padding': 53,\n                            'provide_ixfr': 'yes',\n                            'query_source': {'ip4_addr_w': '*'},\n                            'query_source_v6': {'ip6_addr_w': '*'},\n                            'request_expire': 'yes',\n                            'request_ixfr': 'yes',\n                            'request_nsid': 'yes',\n                            'send_cookie': 'yes',\n                            'tcp_keepalive': 'yes',\n                            'tcp_only': 'yes',\n                            'transfer_format': 'one-answer',\n                            'transfer_source': {'ip4_addr_w': '*'},\n                            'transfer_source_v6': {'ip6_addr': '*'},\n                            'transfers': 36},\n                'ip_addr': '3.4.5.6'}],\n    'trusted_keys': parser_result_trusted_keys,\n    'zones': [{'file': '/var/lib/bind9/public/masters/db.example.com',\n               'zone_name': 'red'},\n              {'file': '/var/lib/bind9/public/masters/db.green.com',\n               'zone_name': 'green'}]}\n```\n\n# Quick Demo\n\nWhat does the Python variable name look like if I parsed [`named-zytrax.conf`](https://github.com/egberts/bind9_parser/blob/master/examples/named-conf/named-zytrax.conf).\n\n```command\n$ ./dump-named-conf.py examples/named-conf/named-zytrax.conf\n```\n\n```python\nprint(result.asDict()):\n{'logging': [{'channel': [{'channel_name': 'example_log',\n                           'path_name': '/var/log/named/example.log',\n                           'print_category': 'yes',\n                           'print_severity': 'yes',\n                           'print_time': 'yes',\n                           'severity': ['info'],\n                           'size_spec': [2,\n                                         'm'],\n                           'versions': 3}]},\n             {'category_group': [{'categories': ['example_log'],\n                                  'category_group_name': 'default'}]}],\n 'options': [{'allow-recursion': {'aml': [{'ip4_addr': '192.168.3.0',\n                                           'prefix': '24'}]},\n              'allow_transfer': {'aml': [{'acl_name': '\"none\"'}]},\n              'directory': '/var/named',\n              'version_string': 'get '\n                                'lost'}],\n 'zones': [{'file': 'root.servers',\n            'type': 'hint',\n            'zone_name': '.'},\n           {'allow_transfer': {'aml': [{'ip4_addr': '192.168.23.1'},\n                                       {'ip4_addr': '192.168.23.2'}]},\n            'class': 'in',\n            'file': 'master/master.example.com',\n            'type': 'master',\n            'zone_name': 'example.com'},\n           {'allow_update': {'aml': [{'keyword': 'none'}]},\n            'class': 'in',\n            'file': 'master.localhost',\n            'type': 'master',\n            'zone_name': 'localhost'},\n           {'allow_update': {'aml': [{'keyword': 'none'}]},\n            'class': 'in',\n            'file': 'localhost.rev',\n            'type': 'master',\n            'zone_name': '0.0.127.in-addr.arpa'},\n           {'class': 'in',\n            'file': '192.168.0.rev',\n            'type': 'master',\n            'zone_name': '0.168.192.IN-ADDR.ARPA'}]}\n```\n\n\n\n# Quick HOWTO\n\nTo take your `named.conf` file and output a Pythonized variable containing ALL\nof the settings found:\n\n```shell\n./dump-named-conf.py examples/named-conf/named-oracle.conf\n```\nand the output of the Python array variable is:\n```console\n{'logging': [{'category_group': [{'categories': ['default_syslog'],\n                                  'category_group_name': 'queries'}]}],\n 'options': [{'allow_transfer': {'aml': [{'addr': '127.0.1.1/24'}]},\n              'datasize': [2098],\n              'directory': '\"/var/named\"',\n              'forward': 'only',\n              'forwarders': {'forwarders_list': [{'addr': '99.11.33.44'}]},\n              'recursion': 'no',\n              'transfers_in': 10,\n              'transfers_per_ns': 2}],\n 'zones': [{'file': '\"db.cities.zn\"',\n            'type': 'master',\n            'zone_name': '\"cities.zn\"'},\n           {'file': '\"db.127.cities.zn\"',\n            'type': 'master',\n            'zone_name': '\"0.0.127.in-addr.arpa\"'},\n           {'file': '\"db.cities.zn.rev\"',\n            'type': 'master',\n            'zone_name': '\"168.192.in-addr.arpa\"'},\n           {'file': '\"slave/db.sales.doc\"',\n            'masters_zone': {'zone_master_list': [{'ip4': '192.168.1.151'}]},\n            'type': 'slave',\n            'zone_name': '\"sales.doc.com\"'},\n           {'file': '\"slave/db.sales.doc.rev\"',\n            'masters_zone': {'zone_master_list': [{'ip4': '192.168.1.151'}]},\n            'type': 'slave',\n            'zone_name': '\"168.192.in-addr.arpa\"'}]}\n```\n\nTo install this package, consult README.install.md\n\n\n# Features\n\nFeatures:\n* 'include' statements are also folded into the parser\n* Relative directory support (not stuck on /etc/bind or /var/lib/bind)\n  * Useful for testing many config files in their respective local subdirectory(s).\n* Support for Bind 4.8 to v9.15.1 (working on Bind10)\n* ISC config files are used in ISC Bind9 server, as well as both ISC DHCP server and client.\n\nbind9-parser make it so easy to do all of that, and now easier for you.\n\n# Introduction\nHere is a program to parse ``\"options { server-id 'example.invalid'; };\"`` :\n\n```python\n\n    from bind9_parser import *\n    test_named_conf_text = \"options { server-id 'example.invalid'; };\"\n    result = clause_statements.parseString(test_named_conf_text, parseAll=True)\n    print(result.asDict())\n```\n\nThe program outputs the following::\n\n```python\n    {'options': [{'server_id_name': \"'example.invalid'\"}]}\n```\n\n\n\n# Unit Tests\nA massive unit tests files are supplied (under `tests/` subdirectory) to ensure that future breakage does not occur.\n\nI use JetBrain PyCharm to unittest these all these modules.  However, you can also do it from a command line:\n```console\npython3 -munittest tests/test_*.py\n```\n\n# JSON \n\n```console\n$ ./dump-named-conf-json.py examples/named-conf/named-oracle.conf \n```\n\n```console\nprint(result.asDict()):\n{'logging': [{'category_group': [{'categories': ['default_syslog'],\n                                  'category_group_name': 'queries'}]}],\n 'options': [{'allow_transfer': {'aml': [{'ip4_addr': '127.0.1.1',\n                                          'prefix': '24'}]},\n              'datasize': [2098],\n              'directory': '/var/named',\n              'forward': 'only',\n              'forwarders': {'forwarder': [{'ip_addr': '99.11.33.44'}]},\n              'recursion': 'no',\n              'transfers_in': 10,\n              'transfers_per_ns': 2}],\n 'zones': [{'file': 'db.cities.zn',\n            'type': 'master',\n            'zone_name': 'cities.zn'},\n           {'file': 'db.127.cities.zn',\n            'type': 'master',\n            'zone_name': '0.0.127.in-addr.arpa'},\n           {'file': 'db.cities.zn.rev',\n            'type': 'master',\n            'zone_name': '168.192.in-addr.arpa'},\n           {'file': 'slave/db.sales.doc',\n            'masters_zone': {'zone_master_list': [{'ip4': '192.168.1.151'}]},\n            'type': 'slave',\n            'zone_name': 'sales.doc.com'},\n           {'file': 'slave/db.sales.doc.rev',\n            'masters_zone': {'zone_master_list': [{'ip4': '192.168.1.151'}]},\n            'type': 'slave',\n            'zone_name': '168.192.in-addr.arpa'}]}\n\nJSON dump:\n\njson-pretty:  {\n    \"options\": [\n        {\n            \"directory\": \"/var/named\",\n            \"datasize\": [\n                2098\n            ],\n            \"forward\": \"only\",\n            \"forwarders\": {\n                \"forwarder\": [\n                    {\n                        \"ip_addr\": \"99.11.33.44\"\n                    }\n                ]\n            },\n            \"recursion\": \"no\",\n            \"transfers_in\": 10,\n            \"transfers_per_ns\": 2,\n            \"allow_transfer\": {\n                \"aml\": [\n                    {\n                        \"ip4_addr\": \"127.0.1.1\",\n                        \"prefix\": \"24\"\n                    }\n                ]\n            }\n        }\n    ],\n    \"logging\": [\n        {\n            \"category_group\": [\n                {\n                    \"category_group_name\": \"queries\",\n                    \"categories\": [\n                        \"default_syslog\"\n                    ]\n                }\n            ]\n        }\n    ],\n    \"zones\": [\n        {\n            \"zone_name\": \"cities.zn\",\n            \"type\": \"master\",\n            \"file\": \"db.cities.zn\"\n        },\n        {\n            \"zone_name\": \"0.0.127.in-addr.arpa\",\n            \"type\": \"master\",\n            \"file\": \"db.127.cities.zn\"\n        },\n        {\n            \"zone_name\": \"168.192.in-addr.arpa\",\n            \"type\": \"master\",\n            \"file\": \"db.cities.zn.rev\"\n        },\n        {\n            \"zone_name\": \"sales.doc.com\",\n            \"type\": \"slave\",\n            \"file\": \"slave/db.sales.doc\",\n            \"masters_zone\": {\n                \"zone_master_list\": [\n                    {\n                        \"ip4\": \"192.168.1.151\"\n                    }\n                ]\n            }\n        },\n        {\n            \"zone_name\": \"168.192.in-addr.arpa\",\n            \"type\": \"slave\",\n            \"file\": \"slave/db.sales.doc.rev\",\n            \"masters_zone\": {\n                \"zone_master_list\": [\n                    {\n                        \"ip4\": \"192.168.1.151\"\n                    }\n                ]\n            }\n        }\n    ]\n}\nend of result.\n```\n# Status\n\nAt the moment, my focus is on the remaining breakage of just the unittesting scripts for  top-level 'options' clause where I'm busy doing unit-testing, but the EBNF is largely deployed and ready\nto go and should work for a large percentage of deployed `named.conf`. It takes time to validate each clause and statement.\n\nIn the future, I do expect some minor tweaks for conversion to integer from strings, perhaps some argument validation.  Might be some forgotten aspect of EBNF like (1:N, or 1:1, or even 1:*).\n\nEnjoy the parser.\n\n# Why Did I Do This?\n\nI see lots of Python scripts for ISC Bind Zone files, but not its configuration.  This Bind9 Parser (in Python) has to do or at least pave the way for the following:\n\n* verification of settings against actual environment setting\n* security audit\n* massive unit testing of Bind 9 using pre-canned configurations\n* implement CISecurity against Bind 9 \n\nClosest cousin of Bind configuration format is NGINX config.\n\nClosest Python (and configuration file) parser that I could find was\n[liuyangc3/nginx_config_parser](https://github.com/liuyangc3/nginx_config_parser) on GitHub here.\n\nLots of generator, beautifier, lint, builder, change detector for Bind9 everywhere, but not a Python parser for Bind9 configuration file.\n\nWorks for Bind 4.9 to latest v9.19.1.\n\n# Bonus Tools\n\n## Offline Search Engine\n\nWe do offer a Python utility to annotate default ISC description for each clause/statement as an ease-of-use for a\nbudding DNS administrator. \n\nAt the moment, this clause/statement keyword CLI utility is a simple dictionary lookup from a\nstatic flat-file Python array database for we later plan to fold this into the `named.conf` outputter stage, as an\noption.\n\nThis tool will help find related clauses or statements or even keywords related to your specific topic.  \n\nTake **ANSWER** as a topic, let us search for this keyword, oh in Bind9 version 9.8 (kinda old, uh, but it goes up to ***v9.19.1*** **!!!**:\n\n```console\n$ python3 examples/rough-draft/namedconfglobal.py  -w topic -k answer -v9.19.1\nVersion: 9.19.1\nPattern: answer\n----------------\nsortlist\n      comment:\n \nThe response to a DNS query may consist of multiple resource records\n(RRs) forming a resource record set (RRset). The name server\nnormally returns the RRs within the RRset in an indeterminate order (but\nsee the ``rrset-order`` statement in :ref:`rrset_ordering`). The client resolver code should\nrearrange the RRs as appropriate: that is, using any addresses on the\nlocal net in preference to other addresses. However, not all resolvers\ncan do this or are correctly configured. When a client is using a local\nserver, the sorting can be performed in the server, based on the\nclient's address. This only requires configuring the name servers, not\nall the clients.\n\nThe ``sortlist`` statement (see below) takes an ``address_match_list`` and\ninterprets it in a special way. Each top-level statement in the ``sortlist``\nmust itself be an explicit ``address_match_list`` with one or two elements. The\nfirst element (which may be an IP address, an IP prefix, an ACL name, or a nested\n``address_match_list``) of each top-level list is checked against the source\naddress of the query until a match is found. When the addresses in the first\nelement overlap, the first rule to match is selected.\n\nOnce the source address of the query has been matched, if the top-level\nstatement contains only one element, the actual primitive element that\nmatched the source address is used to select the address in the response\nto move to the beginning of the response. If the statement is a list of\ntwo elements, then the second element is interpreted as a topology\npreference list. Each top-level element is assigned a distance, and the\naddress in the response with the minimum distance is moved to the\nbeginning of the response.\n\nIn the following example, any queries received from any of the addresses\nof the host itself get responses preferring addresses on any of the\nlocally connected networks. Next most preferred are addresses on the\n192.168.1/24 network, and after that either the 192.168.2/24 or\n192.168.3/24 network, with no preference shown between these two\nnetworks. Queries received from a host on the 192.168.1/24 network\nprefer other addresses on that network to the 192.168.2/24 and\n192.168.3/24 networks. Queries received from a host on the 192.168.4/24\nor the 192.168.5/24 network only prefer other addresses on their\ndirectly connected networks.\n\n\n----------------\nstale-answer-enable\n      comment:\n \nIf ``yes``, enable the returning of \"stale\" cached answers when the name\nservers for a zone are not answering and the ``stale-cache-enable`` option is\nalso enabled. The default is not to return stale answers.\n\nStale answers can also be enabled or disabled at runtime via\n:option:`rndc serve-stale on \u003crndc serve-stale\u003e` or :option:`rndc serve-stale off \u003crndc serve-stale\u003e`; these override \nthe configured setting. :option:`rndc serve-stale reset \u003crndc serve-stale\u003e` restores the\nsetting to the one specified in :iscman:`named.conf`. Note that if stale\nanswers have been disabled by :iscman:`rndc`, they cannot be\nre-enabled by reloading or reconfiguring :iscman:`named`; they must be\nre-enabled with :option:`rndc serve-stale on \u003crndc serve-stale\u003e`, or the server must be\nrestarted.\n\nInformation about stale answers is logged under the ``serve-stale``\nlog category.\n\n\n----------------\nstale-answer-ttl\n      comment:\n \nThis specifies the TTL to be returned on stale answers. The default is 30\nseconds. The minimum allowed is 1 second; a value of 0 is updated silently\nto 1 second.\n\nFor stale answers to be returned, they must be enabled, either in the\nconfiguration file using ``stale-answer-enable`` or via\n:option:`rndc serve-stale on \u003crndc serve-stale\u003e`.\n\n\nEND\n```\n\n# Coverages\n[![build status](https://api.travis-ci.org/egberts/bind9_parser.svg)](https://travis-ci.org/egberts/bind9_parser)\n[![coverage status](https://coveralls.io/repos/github/egberts/bind9_parser/badge.svg)](https://coveralls.io/github/egberts/bind9_parser)  \n|  |license| |[![GitHub version](https://badge.fury.io/gh/egberts%2Fbind9_parser.svg)](https://badge.fury.io/gh/egberts%2Fbind9_parser)| |status|\n|  |ci-status| |win-ci-status| |docs| | [![codecov](https://codecov.io/gh/egberts/bind9_parser/branch/master/graph/badge.svg?token=V8RieceAFx)](https://codecov.io/gh/egberts/bind9_parser) |\n[![star this repo](http://githubbadges.com/star.svg?user=egberts\u0026repo=bind9_parser)](http://github.com/egberts/bind9_parser/star)\n[![fork this repo](http://githubbadges.com/fork.svg?user=egberts\u0026repo=bind9_parser)](http://github.com/egberts/bind9_parser/fork)\n|  |kit| |format| |repos| |downloads|\n|| |contributors|\n|  |tidelift| |twitter-coveragepy| |twitter-nedbat|\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegberts%2Fbind9_parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fegberts%2Fbind9_parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegberts%2Fbind9_parser/lists"}