DNS checking API
This commit is contained in:
parent
278a44988e
commit
10580c4b4b
|
@ -0,0 +1,129 @@
|
|||
import dns.name
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.resolver
|
||||
|
||||
#from collections import defaultdict
|
||||
# FIXME: DNS timeouts
|
||||
|
||||
def compareRecords(rrset, expected):
|
||||
result = {
|
||||
"nameMissing": [],
|
||||
"rrMissing": [],
|
||||
"rrExtra": [],
|
||||
}
|
||||
|
||||
for domain, rrtype, content in expected:
|
||||
for rrrec in rrset:
|
||||
if domain == rrrec.name.to_text() and dns.rdatatype.from_text(rrtype) == rrrec.rdtype:
|
||||
for name in content:
|
||||
if name not in map(lambda _x: _x.to_text(), rrrec.items):
|
||||
# record missing
|
||||
result["rrMissing"].append((domain, rrtype, name))
|
||||
|
||||
for item in rrrec.items:
|
||||
if item.to_text() not in content:
|
||||
# superfluous record
|
||||
result["rrExtra"].append((domain, rrtype, item.to_text()))
|
||||
|
||||
break
|
||||
else:
|
||||
# domain + rr nicht in nameserver
|
||||
result["nameMissing"].append((domain, rrtype))
|
||||
|
||||
success = not any(len(_x) > 0 for _x in result.values())
|
||||
print("NARF", success, result)
|
||||
|
||||
return success, result
|
||||
|
||||
|
||||
def dnsQuery(domain, rrType, nameserverIp):
|
||||
dname = dns.name.from_text(domain)
|
||||
req = dns.message.make_query(dname, dns.rdatatype.from_text(rrType))
|
||||
resp = dns.query.udp(req, nameserverIp, timeout=2.0)
|
||||
|
||||
if resp.rcode() != dns.rcode.NXDOMAIN:
|
||||
rrset = resp.answer + resp.authority + resp.additional
|
||||
return True, rrset
|
||||
else:
|
||||
return False, []
|
||||
|
||||
|
||||
def checkDomain(domain, tldNameserver, nameservers):
|
||||
print(domain, tldNameserver, nameservers)
|
||||
result = []
|
||||
|
||||
# build record set
|
||||
nsRecords = [(domain, "NS", list(ns.name for ns in nameservers))]
|
||||
glueRecords = []
|
||||
for ns in nameservers:
|
||||
if ns.name.endswith("." + domain):
|
||||
if ns.glueIPv4 or ns.glueIPv6:
|
||||
if ns.glueIPv4:
|
||||
glueRecords.append((ns.name, "A", [ns.glueIPv4]))
|
||||
if ns.glueIPv6:
|
||||
glueRecords.append((ns.name, "AAAA", [ns.glueIPv6]))
|
||||
else:
|
||||
result.append(("err", "Nameserver %s is under domain %s, but has no glue entries." % (ns.name, domain)))
|
||||
|
||||
print(nsRecords, glueRecords)
|
||||
|
||||
# 1. TLD nameserver
|
||||
try:
|
||||
found, rrset = dnsQuery(domain, "ANY", tldNameserver)
|
||||
if found:
|
||||
#print(rrset, nsRecords, glueRecords)
|
||||
success, errors = compareRecords(rrset, nsRecords + glueRecords)
|
||||
if success:
|
||||
result.append(("succ", "All records present in TLD nameserver"))
|
||||
else:
|
||||
result.append(("err", "Record mismatch between TLD nameserver and WHOIS database", errors))
|
||||
else:
|
||||
result.append(("err", "Domain %s not found in TLD nameserver" % (domain,)))
|
||||
except (dns.exception.Timeout, OSError):
|
||||
result.append(("err", "TLD nameserver is currently not reachable"))
|
||||
|
||||
# find other records...
|
||||
|
||||
# 2. your nameservers
|
||||
for ns in nameservers:
|
||||
addr = None
|
||||
if ns.glueIPv4:
|
||||
addr = ns.glueIPv4
|
||||
elif ns.glueIPv6:
|
||||
addr = ns.glueIPv6
|
||||
else:
|
||||
for rrType in ("A", "AAAA"):
|
||||
try:
|
||||
r = dns.resolver.Resolver()
|
||||
r.timeout = 2.0
|
||||
q = r.query(ns.name, rdtype=dns.rdatatype.from_text(rrType))
|
||||
addr = q.response.answer[0].items[0].address
|
||||
except (dns.exception.DNSException, OSError):
|
||||
pass
|
||||
|
||||
if addr:
|
||||
err = False
|
||||
errDict = {"nameMissing": [], "rrMissing": [], "rrExtra": []}
|
||||
try:
|
||||
for rec in (nsRecords + glueRecords):
|
||||
found, rrset = dnsQuery(rec[0], rec[1], addr)
|
||||
|
||||
success, errors = compareRecords(rrset, nsRecords + glueRecords)
|
||||
if not success:
|
||||
err = True
|
||||
for k in errors.keys():
|
||||
errDict[k].extend(errors[k])
|
||||
|
||||
if not err:
|
||||
result.append(("succ", "Nameserver %s is configured correctly" % ns.name))
|
||||
else:
|
||||
print(" ==> ", errDict, addr)
|
||||
result.append(("err", "Nameserver %s recordset does not match the database" % (ns.name,), errDict))
|
||||
except (dns.exception.DNSException, OSError):
|
||||
result.append(("err", "Nameserver %s is not reachable (via %s)" % (ns.name, addr)))
|
||||
|
||||
else:
|
||||
result.append(("err", "Can't resolv an ip address for nameserver %s" % ns.name))
|
||||
|
||||
return result
|
|
@ -7,6 +7,7 @@ from django.db.models import Q
|
|||
from whoisdb.models import ASBlock, ASNumber, InetNum
|
||||
from domains.models import Domain, ReverseZone
|
||||
from dnmgmt.settings import TLD_NAMESERVERS
|
||||
from .dnshelper import checkDomain as helperCheckDomain
|
||||
|
||||
import ipaddress
|
||||
|
||||
|
@ -187,7 +188,8 @@ def checkDomain(request):
|
|||
|
||||
ret["success"] = True
|
||||
ret["domain"] = domain.name
|
||||
ret["result"] = checkDomain(domain.name, TLD_NAMESERVERS, domain.nameservers.all())
|
||||
# FIXME: change this if we ever have more than one...
|
||||
ret["result"] = helperCheckDomain(domain.name, TLD_NAMESERVERS[0], domain.nameservers.all())
|
||||
except Domain.DoesNotExist:
|
||||
ret["errorMsg"] = "Domain does not exist"
|
||||
|
||||
|
@ -210,7 +212,8 @@ def checkRzone(request):
|
|||
raise ReverseZone.DoesNotExist()
|
||||
|
||||
ret["success"] = True
|
||||
ret["result"] = checkDomain(rzone.name, TLD_NAMESERVERS, rzone.nameservers.all())
|
||||
# FIXME: change this if we ever have more than one...
|
||||
ret["result"] = helperCheckDomain(rzone.name, TLD_NAMESERVERS[0], rzone.nameservers.all())
|
||||
except Domain.DoesNotExist:
|
||||
ret["errorMsg"] = "ReverseZone does not exist"
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ urlpatterns = [
|
|||
url(r'^$', domains_views.overview, name='overview'),
|
||||
|
||||
url(r'domain/create/$', domains_views.DomainCreate.as_view(), name='domain-create'),
|
||||
url(r'domain/check/(?P<domain>[a-z0-9.-]+)/$', domains_views.DomainCheck.as_view(), name='domain-check'),
|
||||
url(r'domain/show/(?P<domain>[a-z0-9.-]+)/$', domains_views.DomainDetail.as_view(), name='domain-show'),
|
||||
url(r'domain/edit/(?P<domain>[a-z0-9.-]+)/$', domains_views.DomainEdit.as_view(), name='domain-edit'),
|
||||
url(r'domain/delete/(?P<domain>[a-z0-9.-]+)/$', domains_views.DomainDelete.as_view(), name='domain-delete'),
|
||||
|
|
|
@ -34,6 +34,14 @@ class DomainCreate(LoginRequiredMixin, CreateView):
|
|||
return kwargs
|
||||
|
||||
|
||||
class DomainCheck(LoginRequiredMixin, DetailView):
|
||||
model = Domain
|
||||
slug_field = "name"
|
||||
slug_url_kwarg = "domain"
|
||||
context_object_name = "domain"
|
||||
|
||||
template_name = "domains/domain_check.html"
|
||||
|
||||
class DomainDetail(LoginRequiredMixin, DetailView):
|
||||
model = Domain
|
||||
slug_field = "name"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,79 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Checking domain {{ domain.name }}</div>
|
||||
<div class="panel-body">
|
||||
<div id="dnscontent">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function addMessage(div, level, message) {
|
||||
$(div).html($(div).html() + ' <div role="alert" class="alert alert-'+level+' alert-dismissible"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> '+message+'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button></div>');
|
||||
}
|
||||
|
||||
function addRecord(d, key, title) {
|
||||
var msg = "";
|
||||
if(d[key].length > 0) {
|
||||
msg += "\n<p>" + title + ":<ul>";
|
||||
for(var i=0; i<d[key].length; i++)
|
||||
msg += "<li>" + d[key][i].join(" ") + "</li>";
|
||||
msg += "</ul></p>";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#dnscontent").text("Running API request...");
|
||||
$.get({
|
||||
url: "{% url "api:domain-check" %}",
|
||||
data: {domain: "{{ domain.name }}"},
|
||||
beforeSend: function() {
|
||||
$("#dnscontent").html('<img src="{% static "img/loader.gif" %}">');
|
||||
},
|
||||
success: function(data) {
|
||||
$("#dnscontent").html("<p>Checked domain: " + data.domain + "</p>");
|
||||
if(data.success) {
|
||||
for(message of data.result) {
|
||||
var level = "success";
|
||||
if(message[0] == "err")
|
||||
level = "danger";
|
||||
|
||||
var msg = message[1];
|
||||
if(message.length > 2) {
|
||||
var d = message[2];
|
||||
|
||||
|
||||
msg += addRecord(d, "nameMissing", "Missing names");
|
||||
msg += addRecord(d, "rrExtra", "Extra records");
|
||||
msg += addRecord(d, "rrMissing", "Missing records");
|
||||
|
||||
//console.log(d);
|
||||
//if(d["rrMissing"].length > 0) {
|
||||
// msg += "\n<p>Missing records:<ul>";
|
||||
// for(var i=0; i<d["rrMissing"].length; i++)
|
||||
// msg += "<li>" + d["rrMissing"][i].join(" ") + "</li>";
|
||||
// msg += "</ul></p>";
|
||||
//}
|
||||
}
|
||||
addMessage("#dnscontent", level, msg);
|
||||
}
|
||||
} else {
|
||||
addMessage("#dnscontent", "danger", "Could not get DNS data from API!");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue