From 88f0faa73beeafc30248210c4c6b99b7a9ccbdba Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Tue, 15 Nov 2016 11:01:10 +0100 Subject: [PATCH 01/27] Add AUTOMATIC_REVERSE_PTR option to cfg We'll use this option to create reverse lookup domains and PTR records inside them when creating A or AAAA records in any domain. --- config_template.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config_template.py b/config_template.py index 288ff47..4eecace 100644 --- a/config_template.py +++ b/config_template.py @@ -79,3 +79,7 @@ RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT'] # EXPERIMENTAL FEATURES PRETTY_IPV6_PTR = False + +# Create reverse lookup domain if not exists and PTR record from +# A and AAAA records +AUTOMATIC_REVERSE_PTR = False From bbb71f401c7fe0d83da593c62daeed9c316e4336 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Wed, 16 Nov 2016 14:02:43 +0100 Subject: [PATCH 02/27] Add try block into domain.get_id_by_name function If we try to check if a domain exists, and we fetch with get_id_by_name() function it will return with None if the domain not exists, and return the id when yes. --- app/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models.py b/app/models.py index 7f14d40..f111e06 100644 --- a/app/models.py +++ b/app/models.py @@ -477,8 +477,11 @@ class Domain(db.Model): """ Return domain id """ - domain = Domain.query.filter(Domain.name==name).first() - return domain.id + try: + domain = Domain.query.filter(Domain.name==name).first() + return domain.id + except: + return None def update(self): """ From 562b7e2053637b35b8320e53ff0ab0238f1d1a67 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Wed, 16 Nov 2016 14:09:13 +0100 Subject: [PATCH 03/27] Add create_reverse_domain function to Domain This function will create automatically the reverse lookup domain for the applied record of a Domain. And also grant the privileges from the original Domain. --- app/models.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/models.py b/app/models.py index f111e06..76a9181 100644 --- a/app/models.py +++ b/app/models.py @@ -7,6 +7,7 @@ import urlparse import itertools import traceback import pyotp +import re from datetime import datetime from distutils.version import StrictVersion @@ -599,6 +600,34 @@ class Domain(db.Model): logging.debug(str(e)) return {'status': 'error', 'msg': 'Cannot add this domain.'} + def create_reverse_domain(self, domain_name, domain_reverse_name): + """ + Check the existing reverse lookup domain, + if not exists create a new one automatically + """ + self.name = domain_name + domain_id = self.get_id_by_name(domain_reverse_name) + if None == domain_id and app.config['AUTOMATIC_REVERSE_PTR']: + result = self.add(domain_reverse_name, 'Master', 'INCEPTION_INCREMENT', '', '') + self.update() + if result['status'] == 'ok': + history = History(msg='Add reverse lookup domain %s' % domain_reverse_name, detail=str({'domain_type': 'Master', 'domain_master_ips': ''}), created_by='System') + history.add() + else: + return {'status': 'error', 'msg': 'Adding reverse lookup domain failed'} + domain_user_ids = self.get_user() + domain_users = [] + u = User() + for uid in domain_user_ids: + u.id = uid + tmp = u.get_user_info_by_id() + domain_users.append(tmp.username) + if 0 != len(domain_users): + self.name = domain_reverse_name + self.grant_privielges(domain_users) + return {'status': 'ok', 'msg': 'New reverse lookup domain created with granted privilages'} + return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'} + return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'} def delete(self, domain_name): """ From f430ed014bf18e36f7f92d85a64b747d48e9283b Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Wed, 16 Nov 2016 14:12:40 +0100 Subject: [PATCH 04/27] Add reverse domain creation into Record.apply() When a record successefully added to a domain, it will try to create a reverse lookup domain for that record. In this point we aren't create the records yet... --- app/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models.py b/app/models.py index 76a9181..8a8c89d 100644 --- a/app/models.py +++ b/app/models.py @@ -1017,6 +1017,14 @@ class Record(object): return {'status': 'error', 'msg': jdata2['error']} else: logging.info('Record was applied successfully.') + for r in new_records: + r_name = r['name'] + '.' + if r['type'] in ['A', 'AAAA']: + r_content = r['content'] + temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) + domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') + d = Domain() + d.create_reverse_domain(domain, domain_reverse_name) return {'status': 'ok', 'msg': 'Record was applied successfully'} except Exception, e: logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) From 0e8a41f58e0ace467d65a23a71fecaab41112825 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Wed, 16 Nov 2016 15:13:02 +0100 Subject: [PATCH 05/27] Move dns.reversename import to head of models.py It is necessary because we use this function it the pretty_ipv6_ptr don't turned on. --- app/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 8a8c89d..1f7015f 100644 --- a/app/models.py +++ b/app/models.py @@ -8,6 +8,7 @@ import itertools import traceback import pyotp import re +import dns.reversename from datetime import datetime from distutils.version import StrictVersion @@ -32,7 +33,6 @@ else: if 'PRETTY_IPV6_PTR' in app.config.keys(): import dns.inet import dns.name - import dns.reversename PRETTY_IPV6_PTR = app.config['PRETTY_IPV6_PTR'] else: PRETTY_IPV6_PTR = False From 3d7511f0131c479c95db13e029ecea456e8d0fb9 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Wed, 16 Nov 2016 15:15:35 +0100 Subject: [PATCH 06/27] Add reverse PTR record adding to reverse domain At this point we just create the new records and we don't care about the record updates, so now this is a little bit buggy. --- app/models.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models.py b/app/models.py index 1f7015f..67864d2 100644 --- a/app/models.py +++ b/app/models.py @@ -1017,14 +1017,20 @@ class Record(object): return {'status': 'error', 'msg': jdata2['error']} else: logging.info('Record was applied successfully.') + d = Domain() for r in new_records: - r_name = r['name'] + '.' if r['type'] in ['A', 'AAAA']: + r_name = r['name'] + '.' r_content = r['content'] temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) - domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') - d = Domain() + domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') d.create_reverse_domain(domain, domain_reverse_name) + self.name = dns.reversename.from_address(r_content).to_text().rstrip('.') + self.type = 'PTR' + self.status = r['disabled'] + self.ttl = r['ttl'] + self.data = r_name + self.add(domain_reverse_name) return {'status': 'ok', 'msg': 'Record was applied successfully'} except Exception, e: logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) From d224bd679850189964959c7a9d55c826fb02ebff Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Thu, 17 Nov 2016 11:28:49 +0100 Subject: [PATCH 07/27] Fix typo error --- app/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 67864d2..38b8f88 100644 --- a/app/models.py +++ b/app/models.py @@ -447,7 +447,7 @@ class Domain(db.Model): db.session.commit() return True except Exception, e: - logging.error('Can not create settting %s for domain %s. %s' % (setting, self.name, str(e))) + logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, str(e))) return False def get_domains(self): From 43f1289b98ff4bf1fa4d69945db7ef013eb30250 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Thu, 17 Nov 2016 11:32:28 +0100 Subject: [PATCH 08/27] Remove unnecessary record field in Record.delete() refferring to pdns api documentation the fields inside the of the entry delete json is not necessary. --- app/models.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models.py b/app/models.py index 38b8f88..753085d 100644 --- a/app/models.py +++ b/app/models.py @@ -1049,10 +1049,6 @@ class Record(object): "type": self.type, "changetype": "DELETE", "records": [ - { - "name": self.name, - "type": self.type - } ] } ] From 58ef114f7f762d6c67ba66f54e513c93be71359f Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Thu, 17 Nov 2016 11:35:09 +0100 Subject: [PATCH 09/27] Move auto-ptr functionality into a new function --- app/models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 753085d..fdcc9c1 100644 --- a/app/models.py +++ b/app/models.py @@ -1016,7 +1016,16 @@ class Record(object): logging.debug(jdata2['error']) return {'status': 'error', 'msg': jdata2['error']} else: + self.auto_ptr(domain, new_records, deleted_records) logging.info('Record was applied successfully.') + return {'status': 'ok', 'msg': 'Record was applied successfully'} + except Exception, e: + logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) + return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} + + def auto_ptr(self, domain, new_records, deleted_records): + if app.config['AUTOMATIC_REVERSE_PTR']: + try: d = Domain() for r in new_records: if r['type'] in ['A', 'AAAA']: @@ -1025,7 +1034,7 @@ class Record(object): temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') d.create_reverse_domain(domain, domain_reverse_name) - self.name = dns.reversename.from_address(r_content).to_text().rstrip('.') + self.name = dns.reversename.from_address(r_content).to_text() self.type = 'PTR' self.status = r['disabled'] self.ttl = r['ttl'] From 3dbbfc16ce64658ee8ccdff276c1d4cb9c9f0c51 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Thu, 17 Nov 2016 11:37:09 +0100 Subject: [PATCH 10/27] Implement auto-ptr deleting functionality this way we safely remove the corresponding auto created reverse ptr --- app/models.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/models.py b/app/models.py index fdcc9c1..bcb2490 100644 --- a/app/models.py +++ b/app/models.py @@ -1040,11 +1040,20 @@ class Record(object): self.ttl = r['ttl'] self.data = r_name self.add(domain_reverse_name) - return {'status': 'ok', 'msg': 'Record was applied successfully'} - except Exception, e: - logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) - return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} - + for r in deleted_records: + if r['type'] in ['A', 'AAAA']: + r_name = r['name'] + '.' + r_content = r['content'] + temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) + domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') + self.name = dns.reversename.from_address(r_content).to_text() + self.type = 'PTR' + self.data = r_content + self.delete(domain_reverse_name) + return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'} + except Exception as e: + logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (str(e), domain)) + return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'} def delete(self, domain): """ From 7d72cf6088bba688c603523358dec5bcc82c90dd Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Thu, 17 Nov 2016 15:04:07 +0100 Subject: [PATCH 11/27] Put a "." char in a safe way to the records name fields end First of all we cut all of dot char at the end of the rstring and than we put one there. this way we make sure that our string contains just one dot at its end. --- app/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models.py b/app/models.py index bcb2490..b805125 100644 --- a/app/models.py +++ b/app/models.py @@ -797,7 +797,7 @@ class Record(object): if NEW_SCHEMA: data = {"rrsets": [ { - "name": self.name + '.', + "name": self.name.rstrip('.') + '.', "type": self.type, "changetype": "REPLACE", "ttl": self.ttl, @@ -889,7 +889,7 @@ class Record(object): records = [] for r in deleted_records: - r_name = r['name'] + '.' if NEW_SCHEMA else r['name'] + r_name = r['name'].rstrip('.') + '.' if NEW_SCHEMA else r['name'] r_type = r['type'] if PRETTY_IPV6_PTR: # only if activated if NEW_SCHEMA: # only if new schema @@ -911,7 +911,7 @@ class Record(object): records = [] for r in new_records: if NEW_SCHEMA: - r_name = r['name'] + '.' + r_name = r['name'].rstrip('.') + '.' r_type = r['type'] if PRETTY_IPV6_PTR: # only if activated if r_type == 'PTR': # only ptr @@ -1034,7 +1034,7 @@ class Record(object): temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') d.create_reverse_domain(domain, domain_reverse_name) - self.name = dns.reversename.from_address(r_content).to_text() + self.name = dns.reversename.from_address(r_content).to_text().rstrip('.') self.type = 'PTR' self.status = r['disabled'] self.ttl = r['ttl'] @@ -1063,7 +1063,7 @@ class Record(object): headers['X-API-Key'] = PDNS_API_KEY data = {"rrsets": [ { - "name": self.name, + "name": self.name.rstrip('.') + '.', "type": self.type, "changetype": "DELETE", "records": [ From c81deb0044e6f02b8f04dd8dca15e29a62616ae5 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Fri, 18 Nov 2016 08:30:24 +0100 Subject: [PATCH 12/27] Fix SOE-EDIT-API value in reverse-domain creation --- app/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models.py b/app/models.py index b805125..533e701 100644 --- a/app/models.py +++ b/app/models.py @@ -609,6 +609,7 @@ class Domain(db.Model): domain_id = self.get_id_by_name(domain_reverse_name) if None == domain_id and app.config['AUTOMATIC_REVERSE_PTR']: result = self.add(domain_reverse_name, 'Master', 'INCEPTION_INCREMENT', '', '') + result = self.add(domain_reverse_name, 'Master', 'INCEPTION-INCREMENT', '', '') self.update() if result['status'] == 'ok': history = History(msg='Add reverse lookup domain %s' % domain_reverse_name, detail=str({'domain_type': 'Master', 'domain_master_ips': ''}), created_by='System') From 94b0d261425673a5738b6565952a555916a51f42 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:30:16 +0100 Subject: [PATCH 13/27] Delete settings related to domain on domain deleting Because this bug domain deleting isn't possible when a domain specific attribute is set (eg. dyndns). This modification delete domain settings before domain deleting. --- app/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models.py b/app/models.py index 533e701..bfa57c4 100644 --- a/app/models.py +++ b/app/models.py @@ -507,6 +507,10 @@ class Domain(db.Model): if domain_user: domain_user.delete() db.session.commit() + domain_setting = DomainSetting.query.filter(DomainSetting.domain_id==domain.id) + if domain_setting: + domain_setting.delete() + db.session.commit() # then remove domain Domain.query.filter(Domain.name == d).delete() From 5df67ed76ed1ceed5fd4ecfdb2490420c101cbbb Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:35:40 +0100 Subject: [PATCH 14/27] Remove auto-ptr config from config_template.py Because of the feaute modifications this isn't necesarry anymore. --- config_template.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config_template.py b/config_template.py index 4eecace..288ff47 100644 --- a/config_template.py +++ b/config_template.py @@ -79,7 +79,3 @@ RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT'] # EXPERIMENTAL FEATURES PRETTY_IPV6_PTR = False - -# Create reverse lookup domain if not exists and PTR record from -# A and AAAA records -AUTOMATIC_REVERSE_PTR = False From 85eaa8dd691bab90437968ce1f5e99e46d04bc5f Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:38:45 +0100 Subject: [PATCH 15/27] Add domain specific auto-dns preference to domain_management.html It uses exactly the same method as the dyndns preferences. just copy-paste --- app/templates/domain_management.html | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/templates/domain_management.html b/app/templates/domain_management.html index 03cd9b0..a02caa9 100644 --- a/app/templates/domain_management.html +++ b/app/templates/domain_management.html @@ -55,6 +55,22 @@ +
+
+
+
+

Auto PTR creation

+
+
+

+  Allow automatic reverse pointer creation on record updates?{% if + auto_ptr_setting %}
Auto-ptr is enabled globally on the PDA system!{% endif %}

+ +
+
+
+
@@ -94,6 +110,10 @@ $('.dyndns_on_demand_toggle').iCheck({ checkboxClass : 'icheckbox_square-blue', increaseArea : '20%' // optional }); +$('.auto_ptr_toggle').iCheck({ + checkboxClass : 'icheckbox_square-blue', + increaseArea : '20%' // optional +}); $("#domain_multi_user").multiSelect(); @@ -110,6 +130,18 @@ $('.dyndns_on_demand_toggle').on('ifToggled', function(event) { }; applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true); }); +$('.auto_ptr_toggle').on('ifToggled', function(event) { + var is_checked = $(this).prop('checked'); + var domain = $(this).prop('id'); + postdata = { + 'action' : 'set_setting', + 'data' : { + 'setting' : 'auto_ptr', + 'value' : is_checked + } + }; + applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true); +}); // handle deletion of domain $(document.body).on('click', '.delete_domain', function() { From 2d61c56e7b08fa7ec6bf1afb5d884b0973e4cc66 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:42:00 +0100 Subject: [PATCH 16/27] Add auto-ptr setting injection --- app/views.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views.py b/app/views.py index 1a37d53..0b236de 100644 --- a/app/views.py +++ b/app/views.py @@ -58,6 +58,11 @@ def inject_default_domain_table_size_setting(): default_domain_table_size_setting = Setting.query.filter(Setting.name == 'default_domain_table_size').first() return dict(default_domain_table_size_setting=default_domain_table_size_setting.value) +@app.context_processor +def inject_auto_ptr_setting(): + auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first() + return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value)) + # START USER AUTHENTICATION HANDLER @app.before_request def before_request(): From 791b7656ca29991f580f2920f6938e251f807c95 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:43:55 +0100 Subject: [PATCH 17/27] Modify create_db.py, add auto-ptr setting inserting It will globally modify the auto-ptr function in system --- create_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/create_db.py b/create_db.py index 8768ab7..41c874d 100755 --- a/create_db.py +++ b/create_db.py @@ -77,7 +77,8 @@ def init_records(): Setting('record_helper', 'True'), Setting('login_ldap_first', 'True'), Setting('default_record_table_size', '15'), - Setting('default_domain_table_size', '10') + Setting('default_domain_table_size', '10'), + Setting('auto_ptr','False') ]) db_commit = db.session.commit() From 3911935e3be4faa2b6e602303fdb408e0035d049 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:44:47 +0100 Subject: [PATCH 18/27] Add an extra check into reverse domain creation and also import strtobool --- app/models.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/models.py b/app/models.py index bfa57c4..f81a529 100644 --- a/app/models.py +++ b/app/models.py @@ -11,6 +11,7 @@ import re import dns.reversename from datetime import datetime +from distutils.util import strtobool from distutils.version import StrictVersion from flask_login import AnonymousUserMixin @@ -609,10 +610,18 @@ class Domain(db.Model): Check the existing reverse lookup domain, if not exists create a new one automatically """ + domain_obj = Domain.query.filter(Domain.name == domain).first() + domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first() + domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False + system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first() + system_auto_ptr = strtobool(system_auto_ptr.value) self.name = domain_name domain_id = self.get_id_by_name(domain_reverse_name) - if None == domain_id and app.config['AUTOMATIC_REVERSE_PTR']: - result = self.add(domain_reverse_name, 'Master', 'INCEPTION_INCREMENT', '', '') + if None == domain_id and \ + ( + system_auto_ptr or \ + domain_auto_ptr + ): result = self.add(domain_reverse_name, 'Master', 'INCEPTION-INCREMENT', '', '') self.update() if result['status'] == 'ok': From cc1a3def5dfd442962cd7708e75c31fb435acebd Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 13:46:54 +0100 Subject: [PATCH 19/27] Add setting read and extra check to adding an auto-ptr record It is using domain sepcific or global auto-ptr setting to determine the using of auto-ptr creation. --- app/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index f81a529..9a0d6bf 100644 --- a/app/models.py +++ b/app/models.py @@ -1038,7 +1038,15 @@ class Record(object): return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} def auto_ptr(self, domain, new_records, deleted_records): - if app.config['AUTOMATIC_REVERSE_PTR']: + """ + Add auto-ptr records + """ + domain_obj = Domain.query.filter(Domain.name == domain).first() + domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first() + domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False + system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first() + system_auto_ptr = strtobool(system_auto_ptr.value) + if system_auto_ptr or domain_auto_ptr: try: d = Domain() for r in new_records: From bbfbe3683e1c406fb4fc0c5dfd8fdd12b7ba5e80 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 14:50:22 +0100 Subject: [PATCH 20/27] Make my record modal inputs more clear Add "eg." before the placeholder texts. Some user missed to fill out the priority field, and then they got errors. --- app/templates/domain.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/domain.html b/app/templates/domain.html index 4b19e7c..e834ae1 100644 --- a/app/templates/domain.html +++ b/app/templates/domain.html @@ -268,16 +268,16 @@ var modal = $("#modal_custom_record"); if (record_data.val() == "") { var form = " \ - \ + \ \ - \ + \ "; } else { var parts = record_data.val().split(" "); var form = " \ - \ + \ \ - \ + \ "; } modal.find('.modal-body p').html(form); From 4ec70f41438d775c2c594629236e9129225de7f5 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 14:51:36 +0100 Subject: [PATCH 21/27] Change serial displaying in dashboard When pdns not give us serial just notified serial, we need to display that. --- app/templates/dashboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 8c9a7e0..dee30fc 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -168,7 +168,7 @@ {{ domain.type }} - {{ domain.serial }} + {% if domain.serial == 0 %}{{ domain.notified_serial }}{% else %}{{domain.serial}}{% endif %} {% if domain.master == '[]'%}N/A {% else %}{{ domain.master|display_master_name }}{% endif %} From 1538cf023991f6214ce0f969f21d08b5d8b3acd0 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 15:52:07 +0100 Subject: [PATCH 22/27] Limit record selection in reverse lookup domain to PTR And also fix the default type selection of a new record --- app/templates/domain.html | 3 ++- app/views.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/templates/domain.html b/app/templates/domain.html index e834ae1..eeb4c27 100644 --- a/app/templates/domain.html +++ b/app/templates/domain.html @@ -223,7 +223,8 @@ $("#tbl_records").DataTable().search('').columns().search('').draw(); // add new row - var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', 'A', 'Active', 3600, '', '', '', '0']); + var default_type = records_allow_edit[0] + var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '0']); editRow($("#tbl_records").DataTable(), nRow); document.getElementById("edit-row-focus").focus(); nEditing = nRow; diff --git a/app/views.py b/app/views.py index 0b236de..8cc8761 100644 --- a/app/views.py +++ b/app/views.py @@ -2,6 +2,7 @@ import base64 import json import os import traceback +import re from distutils.util import strtobool from distutils.version import StrictVersion from functools import wraps @@ -317,8 +318,11 @@ def domain(domain_name): if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: record = Record(name=jr['name'], type=jr['type'], status='Disabled' if jr['disabled'] else 'Active', ttl=jr['ttl'], data=jr['content']) records.append(record) - - return render_template('domain.html', domain=domain, records=records, editable_records=app.config['RECORDS_ALLOW_EDIT']) + if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name): + editable_records = app.config['RECORDS_ALLOW_EDIT'] + else: + editable_records = ['PTR'] + return render_template('domain.html', domain=domain, records=records, editable_records=editable_records) else: return redirect(url_for('error', code=404)) From c53d9ace8985a460dc123832d234d92703276bd1 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 16:52:54 +0100 Subject: [PATCH 23/27] Extend reverse domain regexp with classes With this modification it can be possible, to detect custom IP classes for domains. It just need to modify the multipler in regexp {4} or {1}. In the future it will works automaticly, but not now --- app/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models.py b/app/models.py index 9a0d6bf..85bfa4d 100644 --- a/app/models.py +++ b/app/models.py @@ -1044,8 +1044,10 @@ class Record(object): domain_obj = Domain.query.filter(Domain.name == domain).first() domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first() domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False + system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first() system_auto_ptr = strtobool(system_auto_ptr.value) + if system_auto_ptr or domain_auto_ptr: try: d = Domain() @@ -1053,7 +1055,7 @@ class Record(object): if r['type'] in ['A', 'AAAA']: r_name = r['name'] + '.' r_content = r['content'] - temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) + temp = re.search('^((([a-f0-9]\.){4})(?P.+6.arpa)\.?)|((([0-9]+\.){1})(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') d.create_reverse_domain(domain, domain_reverse_name) self.name = dns.reversename.from_address(r_content).to_text().rstrip('.') @@ -1066,7 +1068,7 @@ class Record(object): if r['type'] in ['A', 'AAAA']: r_name = r['name'] + '.' r_content = r['content'] - temp = re.search('^(([a-f0-9]\.){4}(?P.+6.arpa)\.?)|(\.(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) + temp = re.search('^((([a-f0-9]\.){4})(?P.+6.arpa)\.?)|((([0-9]+\.){1})(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') self.name = dns.reversename.from_address(r_content).to_text() self.type = 'PTR' From d7db0d5e7aff7cddb3e1cc0d6dd579b0d721b5e0 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 19:36:43 +0100 Subject: [PATCH 24/27] Fix create reverse domain function Using of wrong variable --- app/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 85bfa4d..e867f00 100644 --- a/app/models.py +++ b/app/models.py @@ -610,7 +610,7 @@ class Domain(db.Model): Check the existing reverse lookup domain, if not exists create a new one automatically """ - domain_obj = Domain.query.filter(Domain.name == domain).first() + domain_obj = Domain.query.filter(Domain.name == domain_name).first() domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first() domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first() From b9f95da906eda8b2073d603a11e69d66b2c81f97 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 19:40:43 +0100 Subject: [PATCH 25/27] Implement of checking existing higher class ip reverse zones iteratively checking of existing domains with higher IP classes. When this function find an existing higher class domain return with that reverse address. eg. 192.in-addr.arpa If it is not find any existing higher class domain it returns with the lowest class domain reverse domain name. eg, 39.168.192.in-addr.arpa --- app/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/models.py b/app/models.py index e867f00..15f33cd 100644 --- a/app/models.py +++ b/app/models.py @@ -643,6 +643,19 @@ class Domain(db.Model): return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'} return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'} + def get_reverse_domain_name(self, reverse_host_address): + if re.search('ip6.arpa', reverse_host_address): + for i in range(31,3,-1): + address = re.search('((([a-f0-9]\.){'+ str(i) +'})(?P.+6.arpa)\.?)', reverse_host_address) + if None != self.get_id_by_name(address.group('ipname')): + break + else: + for i in range(3,0,-1): + address = re.search('((([0-9]+\.){'+ str(i) +'})(?P.+r.arpa)\.?)', reverse_host_address) + if None != self.get_id_by_name(address.group('ipname')): + break + return address.group('ipname') + def delete(self, domain_name): """ Delete a single domain name from powerdns From e6e3c397787bb5e43e4bb3adbbedcfe620800c04 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 21 Nov 2016 19:44:10 +0100 Subject: [PATCH 26/27] Add get_reverse_domain_name functionality In this way the reverse it is possible to create auto-ptr records in higher ip classes (eg. class A in IPv4). Only works with existing higher class domain. If is isn't find higher class domain, create a lowest class domain, and add there the reverse PTRs. Also works with IPv6! --- app/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models.py b/app/models.py index 15f33cd..a3a802a 100644 --- a/app/models.py +++ b/app/models.py @@ -1060,7 +1060,7 @@ class Record(object): system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first() system_auto_ptr = strtobool(system_auto_ptr.value) - + if system_auto_ptr or domain_auto_ptr: try: d = Domain() @@ -1068,8 +1068,8 @@ class Record(object): if r['type'] in ['A', 'AAAA']: r_name = r['name'] + '.' r_content = r['content'] - temp = re.search('^((([a-f0-9]\.){4})(?P.+6.arpa)\.?)|((([0-9]+\.){1})(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) - domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') + reverse_host_address = dns.reversename.from_address(r_content).to_text() + domain_reverse_name = d.get_reverse_domain_name(reverse_host_address) d.create_reverse_domain(domain, domain_reverse_name) self.name = dns.reversename.from_address(r_content).to_text().rstrip('.') self.type = 'PTR' @@ -1081,9 +1081,9 @@ class Record(object): if r['type'] in ['A', 'AAAA']: r_name = r['name'] + '.' r_content = r['content'] - temp = re.search('^((([a-f0-9]\.){4})(?P.+6.arpa)\.?)|((([0-9]+\.){1})(?P.+r.arpa)\.?)', dns.reversename.from_address(r_content).to_text()) - domain_reverse_name = temp.group('ipv6name') if temp.group('ipv6name') != None else temp.group('ipv4name') - self.name = dns.reversename.from_address(r_content).to_text() + reverse_host_address = dns.reversename.from_address(r_content).to_text() + domain_reverse_name = d.get_reverse_domain_name(reverse_host_address) + self.name = reverse_host_address self.type = 'PTR' self.data = r_content self.delete(domain_reverse_name) From 72e3a82e9ecb13033de2f8448f744e411cd4dd55 Mon Sep 17 00:00:00 2001 From: "SIPOS, Peter" Date: Mon, 28 Nov 2016 08:39:07 +0100 Subject: [PATCH 27/27] Change reverse domain creation order With refactoring the get_reverse_domain_name function, we change the reverse domain checking to a reverse order. In this way we check the lowest class (more specific) reverse zone first. When an existing domain found we use it to create the reverse PTR records. If no one existing can be find, The most specific address will be used. --- app/models.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models.py b/app/models.py index a3a802a..6878938 100644 --- a/app/models.py +++ b/app/models.py @@ -644,17 +644,21 @@ class Domain(db.Model): return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'} def get_reverse_domain_name(self, reverse_host_address): + c = 1 if re.search('ip6.arpa', reverse_host_address): - for i in range(31,3,-1): + for i in range(1,32,1): address = re.search('((([a-f0-9]\.){'+ str(i) +'})(?P.+6.arpa)\.?)', reverse_host_address) if None != self.get_id_by_name(address.group('ipname')): + c = i break + return re.search('((([a-f0-9]\.){'+ str(c) +'})(?P.+6.arpa)\.?)', reverse_host_address).group('ipname') else: - for i in range(3,0,-1): + for i in range(1,4,1): address = re.search('((([0-9]+\.){'+ str(i) +'})(?P.+r.arpa)\.?)', reverse_host_address) if None != self.get_id_by_name(address.group('ipname')): + c = i break - return address.group('ipname') + return re.search('((([0-9]+\.){'+ str(c) +'})(?P.+r.arpa)\.?)', reverse_host_address).group('ipname') def delete(self, domain_name): """