diff --git a/binder/bcommon/forms.py b/binder/bcommon/forms.py index d0220c9..9f49bf8 100644 --- a/binder/bcommon/forms.py +++ b/binder/bcommon/forms.py @@ -2,10 +2,18 @@ from django import forms from bcommon.models import BindServer, Key +RECORD_TYPE_CHOICES = (("A", "A"), ("AAAA", "AAAA")) +TTL_CHOICES = ((300, "5 minutes"), + (1800, "30 minutes"), + (3600, "1 hour"), + (43200, "12 hours"), + (86400, "1 day")) + class FormAddRecord(forms.Form): - dns_hostname = forms.CharField(max_length=100) - rr_domain = forms.CharField(max_length=100) - rr_name = forms.CharField(max_length=256) - rr_type = forms.ChoiceField(choices=(("A", "A"), ("MX", "MX"), ("CNAME", "CNAME"), ("AAAA", "AAAA"))) - rr_data = forms.CharField(max_length=256) - tsig_key = forms.ModelChoiceField(queryset=Key.objects.all(), empty_label=None) + dns_server = forms.CharField(max_length=100, label="Hostname of DNS Server", widget=forms.TextInput(attrs={'readonly':'readonly'})) + name = forms.CharField(max_length=100, label="Record Name (FQDN)") + record_type = forms.ChoiceField(choices=RECORD_TYPE_CHOICES, label="Record Type") + ttl = forms.ChoiceField(choices=TTL_CHOICES, label="TTL", initial=86400) + create_reverse = forms.BooleanField(label="Create Reverse Record (PTR)?", required=False) + data = forms.CharField(max_length=256, label="Record Data (IP/Hostname)") + key_name = forms.ModelChoiceField(queryset=Key.objects.all(), empty_label=None, label="TSIG Key") diff --git a/binder/bcommon/helpers.py b/binder/bcommon/helpers.py index d325dba..7481586 100644 --- a/binder/bcommon/helpers.py +++ b/binder/bcommon/helpers.py @@ -1,12 +1,13 @@ -import urllib2 -from BeautifulSoup import BeautifulStoneSoup as BS -import re +from bcommon.keyutils import create_keyring -from bcommon.models import Key, BindServer +import re import dns.query +import dns.reversename +import dns.update def list_zone_records(dns_hostname, zone_name): - """ Take a DNS zone, and list all the records it contains. """ + """Take a DNS server and a zone name, + and return an array of its records.""" # Need to move most of this logic into a helper method. try: zone = dns.zone.from_xfr(dns.query.xfr(dns_hostname, zone_name)) @@ -31,16 +32,48 @@ def list_zone_records(dns_hostname, zone_name): 'rr_data' : split_record.split(" ")[4]}) return record_array -def add_record(clean_data): - key_name = Key.objects.get(name=(clean_data['tsig_key'])).name - key_data = Key.objects.get(name=(clean_data['tsig_key'])).data - key_algorithm = Key.objects.get(name=(clean_data['tsig_key'])).algorithm - keyring = dns.tsigkeyring.from_text({ key_name : key_data }) - dns_update = dns.update.Update(clean_data['rr_domain'], keyring = keyring, keyalgorithm=key_algorithm) - dns_update.replace(str(clean_data['rr_name']), 86400, str(clean_data['rr_type']), str(clean_data['rr_data'])) - try: - response = dns.query.tcp(dns_update, clean_data['dns_hostname']) - except dns.tsig.PeerBadKey: - return {'errors' : "There was a problem adding your record due to a TSIG key issue. Please resolve that first." } +def add_forward_record(form_data, zone_keyring): + """Take in data from FormAddRecord and a keyring object, + return a response from the DNS server about adding the record.""" - return {'response' : response } + re_form_data = re.search(r"(\w+).(.*)", form_data["name"]) + hostname = re_form_data.group(1) + domain = re_form_data.group(2) + + dns_update = dns.update.Update(domain, keyring = zone_keyring) + dns_update.replace(hostname, int(form_data["ttl"]), str(form_data["record_type"]), str(form_data["data"])) + + try: + response = dns.query.tcp(dns_update, form_data["dns_server"]) + except dns.tsig.BadPeerKey: + response = "There was a problem adding your forward record due to a TSIG key issue." + + return response + +def add_reverse_record(form_data, zone_keyring): + + reverse_ip_fqdn = str(dns.reversename.from_address(form_data["data"])) + reverse_ip = re.search(r"([0-9]+).(.*).$", reverse_ip_fqdn).group(1) + reverse_domain = re.search(r"([0-9]+).(.*).$", reverse_ip_fqdn).group(2) + + dns_update = dns.update.Update(reverse_domain, keyring = zone_keyring) + dns_update.replace(reverse_ip, int(form_data["ttl"]), "PTR", str(form_data["name"]) + ".") + + response = dns.query.tcp(dns_update, form_data["dns_server"]) + + return response + +def add_record(form_data, key_dict): + """Add a DNS record with data from a FormAddRecord object. + If a reverse PTR record is requested, this will be added too.""" + + keyring = create_keyring(key_dict) + response = {} + forward_response = add_forward_record(form_data, keyring) + response["forward_response"] = forward_response + + if form_data["create_reverse"]: + reverse_response = add_reverse_record(form_data, keyring) + response["reverse_response"] = reverse_response + + return response diff --git a/binder/bcommon/keyutils.py b/binder/bcommon/keyutils.py new file mode 100755 index 0000000..5ecb47d --- /dev/null +++ b/binder/bcommon/keyutils.py @@ -0,0 +1,13 @@ +import dns.tsigkeyring +import re +import sys + +def create_keyring(key_dict): + """Accept a TSIG keyfile and a key name to retrieve. + Return a keyring object with the key name and TSIG secret.""" + + keyring = dns.tsigkeyring.from_text({ + key_dict.name : key_dict.data + }) + + return keyring diff --git a/binder/bcommon/views.py b/binder/bcommon/views.py index 84a1d25..1869a7d 100644 --- a/binder/bcommon/views.py +++ b/binder/bcommon/views.py @@ -3,17 +3,11 @@ from bcommon.models import BindServer, Key from django.template import Context from django.shortcuts import render_to_response, redirect -from bcommon.helpers import list_server_zones, list_zone_records, add_record +from bcommon.helpers import list_zone_records, add_record #, delete_record from bcommon.forms import FormAddRecord from django.template import RequestContext - -import dns.update -import dns.tsigkeyring - -import socket - def home_index(request): return render_to_response('index.htm') @@ -55,37 +49,40 @@ def view_zone_records(request, dns_hostname, zone_name): 'rr_domain' : zone_name}, context_instance=RequestContext(request)) -def view_add_record(request, dns_hostname, zone_name): +def view_add_record(request, dns_server, zone): """ View to provide form to add a DNS record. """ - form = FormAddRecord(initial={ 'dns_hostname' : dns_hostname, - 'rr_domain' : zone_name }) + form = FormAddRecord(initial={ 'dns_server' : dns_server, + 'zone' : zone }) return render_to_response('bcommon/add_record_form.htm', { 'form' : form }, context_instance=RequestContext(request)) -def add_record_result(request): +def view_add_record_result(request): """ Process the input given to add a DNS record. """ if request.method == "GET": - # Return home. You shouldn't be accessing the result - # via a GET. + # Return home. You shouldn't be accessing this url via a GET. return redirect('/') - # We got a POST to add the result. form = FormAddRecord(request.POST) if form.is_valid(): cd = form.cleaned_data - response = add_record(cd) - - if 'errors' in response: - return render_to_response('bcommon/add_record_result.htm', - { 'errors' : response['errors'] }, - context_instance=RequestContext(request)) else: - return render_to_response('bcommon/add_record_result.htm', - { 'response' : response['response'] }, + form = FormAddRecord(request.POST) + return render_to_response('bcommon/add_record_form.htm', + { 'form' : form }, context_instance=RequestContext(request)) -def view_delete_record(request): + + key_dict = Key.objects.get(name=cd["key_name"]) + add_record_response = add_record(cd, key_dict) + + return render_to_response('bcommon/add_record_result.htm', + { 'response' : add_record_response }, + context_instance=RequestContext(request)) + + +### WORK ON BELOW +def confirm_delete_record(request): if request.method == "GET": # Return home. You shouldn't trying to directly acces # the url for deleting records. @@ -95,20 +92,33 @@ def view_delete_record(request): rr_domain = request.POST['rr_domain'] rr_array = request.POST.getlist('rr_array') - if request.POST['delete_step'] == "initial": - """ We need to confirm they really want to delete the items. """ - return render_to_response('bcommon/delete_record_initial.htm', - { 'rr_server' : rr_server, - 'rr_domain' : rr_domain, - 'rr_array' : rr_array }, - context_instance=RequestContext(request)) - - if request.POST['delete_step'] == "finalize": - # TODO: Instrument - """ Time to actually delete the records requested """ - pass + ## TODO(jforman): We need to handle the case where the POST data + ## is somehow bad. + return render_to_response('bcommon/delete_record_initial.htm', + { 'rr_server' : rr_server, + 'rr_domain' : rr_domain, + 'rr_array' : rr_array, + 'tsig_keys' : Key.objects.all() }, + context_instance=RequestContext(request)) # If we hit a case where we don't know what's going on. - return render_to_response('bcommon/index.htm', - { 'errors' : "We hit an unhandled exception in deleting your requested records." }, - context_instance=RequestContext(request)) + # return render_to_response('bcommon/index.htm', + # { 'errors' : "We hit an unhandled exception in deleting your requested records." }, + # context_instance=RequestContext(request)) + +def delete_result(request): + if request.method == "GET": + # Return home. You shouldn't trying to directly acces + # the url for deleting records. + return redirect('/') + + to_delete_array = {} + to_delete_array['rr_server'] = request.POST['rr_server'] + to_delete_array['rr_domain'] = request.POST['rr_domain'] + to_delete_array['rr_array'] = eval(request.POST.getlist('rr_array')[0]) + for current in to_delete_array['rr_array']: + print "current: %s" % current + + to_delete_array['key_name'] = request.POST['key_name'] + to_delete_array['key_data'] = Key.objects.get(name=(to_delete_array['key_name'])).data + delete_result = delete_record(to_delete_array) diff --git a/binder/templates/bcommon/add_record_result.htm b/binder/templates/bcommon/add_record_result.htm index 94c729e..9684919 100644 --- a/binder/templates/bcommon/add_record_result.htm +++ b/binder/templates/bcommon/add_record_result.htm @@ -6,10 +6,19 @@ {% endblock pageheader %} {% block body %} -{% if not errors %} - Result: -
-    {{ response }}
-  
+{% if response.forward_response %} +Forward Record Creation Output +
+{{ response.forward_response }}
+
{% endif %} + +

+{% if response.reverse_response %} +Reverse Record Creation Output +

+{{ response.reverse_response }}
+
+{% endif %} + {% endblock body %} diff --git a/binder/templates/bcommon/delete_record_initial.htm b/binder/templates/bcommon/delete_record_initial.htm index 698f26c..b750e1a 100644 --- a/binder/templates/bcommon/delete_record_initial.htm +++ b/binder/templates/bcommon/delete_record_initial.htm @@ -2,18 +2,23 @@ {% block body %} -
{% csrf_token %} +{% csrf_token %} - + Do you really want to delete the following records? - +
{% endblock body %} diff --git a/binder/templates/bcommon/list_server_zones.htm b/binder/templates/bcommon/list_server_zones.htm index 6b177b0..c9bcdb0 100644 --- a/binder/templates/bcommon/list_server_zones.htm +++ b/binder/templates/bcommon/list_server_zones.htm @@ -2,7 +2,7 @@ {% block body %} {% if not errors %} -DNS Server Zone List: +DNS Server Zone List for {{ dns_hostname }}: