Merge branch 'master' of github.com:asmaps/nsupdate.info

This commit is contained in:
Arne Schauf 2013-09-29 03:13:16 +02:00
commit 3e87efa7e7
4 changed files with 67 additions and 12 deletions

View File

@ -6,8 +6,8 @@ logger = logging.getLogger(__name__)
from django.http import HttpResponse from django.http import HttpResponse
from django.conf import settings from django.conf import settings
from django.contrib.auth.hashers import check_password from django.contrib.auth.hashers import check_password
from django.contrib.auth.decorators import login_required
from main.forms import *
from main.models import Host from main.models import Host
import dns.inet import dns.inet
import os import os
@ -19,12 +19,13 @@ def MyIpView(request):
return HttpResponse(request.META['REMOTE_ADDR'], content_type="text/plain") return HttpResponse(request.META['REMOTE_ADDR'], content_type="text/plain")
def UpdateIpView(request): def DetectIpView(request):
ipaddr = request.META['REMOTE_ADDR'] ipaddr = request.META['REMOTE_ADDR']
af = dns.inet.af_for_address(ipaddr) af = dns.inet.af_for_address(ipaddr)
key = 'ipv4' if af == dns.inet.AF_INET else 'ipv6' key = 'ipv4' if af == dns.inet.AF_INET else 'ipv6'
request.session[key] = ipaddr request.session[key] = ipaddr
image_data = open(os.path.join(settings.STATIC_ROOT, "1px.gif"), "rb").read() with open(os.path.join(settings.STATIC_ROOT, "1px.gif"), "rb") as f:
image_data = f.read()
return HttpResponse(image_data, mimetype="image/png") return HttpResponse(image_data, mimetype="image/png")
@ -57,7 +58,7 @@ def basic_authenticate(auth):
return username, password return username, password
def check_auth(username, password): def check_api_auth(username, password):
""" """
Check username and password against our database. Check username and password against our database.
@ -77,6 +78,24 @@ def check_auth(username, password):
return check_password(password, password_hash) return check_password(password, password_hash)
def check_session_auth(user, hostname):
"""
Check our database whether the hostname is owned by the user.
:param user: django user object
:param hostname: fqdn
:return: True if hostname is owned by this user, False otherwise.
"""
hosts = Host.objects.filter(fqdn=hostname, created_by=user)
num_hosts = len(hosts)
if num_hosts == 0:
return False
if num_hosts > 1:
logging.error("fqdn %s has multiple entries" % fqdn)
return False
return True
def Response(content): def Response(content):
return HttpResponse(content, content_type='text/plain') return HttpResponse(content, content_type='text/plain')
@ -106,7 +125,7 @@ def NicUpdateView(request):
logger.warning('%s - received no auth' % (hostname, )) logger.warning('%s - received no auth' % (hostname, ))
return basic_challenge("authenticate to update DNS", 'noauth') return basic_challenge("authenticate to update DNS", 'noauth')
username, password = basic_authenticate(auth) username, password = basic_authenticate(auth)
if not check_auth(username, password): if not check_api_auth(username, password):
logger.info('%s - received bad credentials, username: %s' % (hostname, username, )) logger.info('%s - received bad credentials, username: %s' % (hostname, username, ))
return basic_challenge("authenticate to update DNS", 'badauth') return basic_challenge("authenticate to update DNS", 'badauth')
if hostname is None: if hostname is None:
@ -119,6 +138,32 @@ def NicUpdateView(request):
if agent in settings.BAD_AGENTS: if agent in settings.BAD_AGENTS:
logger.info('%s - received update from bad user agent %s' % (hostname, agent, )) logger.info('%s - received update from bad user agent %s' % (hostname, agent, ))
return Response('badagent') return Response('badagent')
return _update(hostname, ipaddr)
@login_required
def AuthorizedNicUpdateView(request):
"""
similar to NicUpdateView, but the client is not a router or other dyndns client,
but the admin browser who is currently logged into the nsupdate.info site.
Example URLs:
https://supdate.info/nic/update?hostname=fqdn&myip=1.2.3.4
"""
hostname = request.GET.get('hostname')
if hostname is None:
return Response('nohost')
if not check_session_auth(request.user, hostname):
logger.info('%s - is not owned by user: %s' % (hostname, request.user.username, ))
return Response('nohost')
ipaddr = request.GET.get('myip')
if ipaddr is None:
ipaddr = request.META.get('REMOTE_ADDR')
return _update(hostname, ipaddr)
def _update(hostname, ipaddr):
ipaddr = str(ipaddr) # XXX bug in dnspython: crashes if ipaddr is unicode, wants a str! ipaddr = str(ipaddr) # XXX bug in dnspython: crashes if ipaddr is unicode, wants a str!
try: try:
update(hostname, ipaddr) update(hostname, ipaddr)

View File

@ -21,6 +21,15 @@ class SameIpError(ValueError):
def update(fqdn, ipaddr, ttl=60): def update(fqdn, ipaddr, ttl=60):
"""
intelligent dns updater - first does a lookup on the master server to find
the current ip and only sends a dynamic update if we have a different ip.
:param fqdn: fully qualified domain name (str)
:param ipaddr: new ip address
:param ttl: time to live, default 60s (int)
:raises: SameIpError if new and old IP is the same
"""
af = dns.inet.af_for_address(ipaddr) af = dns.inet.af_for_address(ipaddr)
rdtype = 'A' if af == dns.inet.AF_INET else 'AAAA' rdtype = 'A' if af == dns.inet.AF_INET else 'AAAA'
try: try:

View File

@ -1,6 +1,6 @@
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url
from main.views import HomeView, OverviewView, HostView, DeleteHostView from main.views import HomeView, OverviewView, HostView, DeleteHostView
from api.views import MyIpView, UpdateIpView, NicUpdateView from api.views import MyIpView, DetectIpView, NicUpdateView, AuthorizedNicUpdateView
urlpatterns = patterns( urlpatterns = patterns(
@ -10,6 +10,7 @@ urlpatterns = patterns(
url(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'), url(r'^host/(?P<pk>\d+)/$', HostView.as_view(), name='host_view'),
url(r'^host/(?P<pk>\d+)/delete/$', DeleteHostView.as_view(), name='delete_host'), url(r'^host/(?P<pk>\d+)/delete/$', DeleteHostView.as_view(), name='delete_host'),
url(r'^myip$', MyIpView), url(r'^myip$', MyIpView),
url(r'^updateip$', UpdateIpView), url(r'^detectip/$', DetectIpView),
url(r'^nic/update$', NicUpdateView), url(r'^nic/update$', NicUpdateView),
url(r'^nic/update_authorized$', AuthorizedNicUpdateView),
) )

View File

@ -25,7 +25,7 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="{% url 'home' %}">nsupdate.info</a> <a class="navbar-brand" href="{% url 'home' %}"><span style="color: #00ba00"></span> nsupdate.info</a>
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
@ -68,7 +68,7 @@
<div class="row" style="background-color: #DDDDDD"> <div class="row" style="background-color: #DDDDDD">
<div class="col-sm-4" style="text-align: center"> <div class="col-sm-4" style="text-align: center">
<h1><i class="icon-globe icon-4x"></i></h1> <h1><i class="icon-globe icon-4x"></i></h1>
<h3>World wide web</h3> <h3>World Wide Web</h3>
<p> <p>
nsupdate.info is a free and <a href="https://github.com/asmaps/nsupdate.info">open-source</a> dynamic DNS service with IPv4 and IPv6 support. nsupdate.info is a free and <a href="https://github.com/asmaps/nsupdate.info">open-source</a> dynamic DNS service with IPv4 and IPv6 support.
</p> </p>
@ -77,7 +77,7 @@
<h1><i class="icon-cogs icon-4x"></i></h1> <h1><i class="icon-cogs icon-4x"></i></h1>
<h3>Awesomeness</h3> <h3>Awesomeness</h3>
<p> <p>
Powered by python, the magical pony django and a stupid three columned icon landing page. Powered by python, the magical django pony and a stupid three columned icon landing page.
</p> </p>
</div> </div>
<div class="col-sm-4" style="text-align: center"> <div class="col-sm-4" style="text-align: center">
@ -124,8 +124,8 @@
</div> </div>
<div style="display: none"> <div style="display: none">
<img src="//{{ WWW_IPV4_HOST }}/updateip" /> <img src="//{{ WWW_IPV4_HOST }}/detectip" />
<img src="//{{ WWW_IPV6_HOST }}/updateip" /> <img src="//{{ WWW_IPV6_HOST }}/detectip" />
</div> </div>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>