From 5e637b14c9a41c84a2eed6fbefdd0a9aa4156495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonne=20Ha=C3=9F?= Date: Mon, 29 Sep 2014 06:16:52 +0200 Subject: [PATCH] Handle IPv4-mapped IPv6 addresses Some reverse proxy configurations pass REMOTE_ADDR as a IPv4-mapped IPv6 address when listening on a IPv6 socket. This patch converts such a mapped address into a IPv4 address at all usages of REMOTE_ADDR. It handles both, the ::ffff:192.0.2.128 format as well as the deprecated ::192.0.2.128 format. --- nsupdate/api/views.py | 9 +++++---- nsupdate/context_processors.py | 3 ++- nsupdate/main/iptools.py | 17 +++++++++++++++++ nsupdate/main/views.py | 5 +++-- 4 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 nsupdate/main/iptools.py diff --git a/nsupdate/api/views.py b/nsupdate/api/views.py index 9f4d40b..ce2d81a8 100644 --- a/nsupdate/api/views.py +++ b/nsupdate/api/views.py @@ -22,6 +22,7 @@ from ..utils import log, ddns_client from ..main.models import Host from ..main.dnstools import (FQDN, update, delete, check_ip, put_ip_into_session, SameIpError, DnsUpdateError, NameServerNotAvailable) +from ..main.iptools import normalize_ip def Response(content): @@ -44,7 +45,7 @@ def myip_view(request, logger=None): """ # Note: keeping this as a function-based view, as it is frequently used - # maybe it is slightly more efficient than class-based. - ipaddr = request.META['REMOTE_ADDR'] + ipaddr = normalize_ip(request.META['REMOTE_ADDR']) logger.debug("detected remote ip address: %s" % ipaddr) return Response(ipaddr) @@ -64,7 +65,7 @@ class DetectIpView(View): # so the session cookie is not received here - thus we access it via # the sessionid: s = SessionStore(session_key=sessionid) - ipaddr = request.META['REMOTE_ADDR'] + ipaddr = normalize_ip(request.META['REMOTE_ADDR']) # as this is NOT the session automatically established and # also saved by the framework, we need to use save=True here put_ip_into_session(s, ipaddr, save=True) @@ -225,7 +226,7 @@ class NicUpdateView(View): return Response('badagent') ipaddr = request.GET.get('myip') if not ipaddr: # None or '' - ipaddr = request.META.get('REMOTE_ADDR') + ipaddr = normalize_ip(request.META.get('REMOTE_ADDR')) secure = request.is_secure() if delete: return _delete(host, ipaddr, secure, logger=logger) @@ -283,7 +284,7 @@ class AuthorizedNicUpdateView(View): # and logged-in usage - thus misbehaved user agents are no problem. ipaddr = request.GET.get('myip') if not ipaddr: # None or empty string - ipaddr = request.META.get('REMOTE_ADDR') + ipaddr = normalize_ip(request.META.get('REMOTE_ADDR')) secure = request.is_secure() if delete: return _delete(host, ipaddr, secure, logger=logger) diff --git a/nsupdate/context_processors.py b/nsupdate/context_processors.py index 33f0efa..acf022f 100644 --- a/nsupdate/context_processors.py +++ b/nsupdate/context_processors.py @@ -10,6 +10,7 @@ logger = logging.getLogger(__name__) import time from .main.dnstools import put_ip_into_session +from .main.iptools import normalize_ip from django.conf import settings @@ -36,7 +37,7 @@ def update_ips(request): s = request.session t_now = int(time.time()) # update and keep fresh using info from the request we have anyway: - ipaddr = request.META['REMOTE_ADDR'] + ipaddr = normalize_ip(request.META['REMOTE_ADDR']) put_ip_into_session(s, ipaddr, max_age=MAX_IP_AGE / 2) # remove stale data to not show outdated IPs (e.g. after losing IPv6 connectivity): for key in ['ipv4', 'ipv6', ]: diff --git a/nsupdate/main/iptools.py b/nsupdate/main/iptools.py new file mode 100644 index 0000000..015fa60 --- /dev/null +++ b/nsupdate/main/iptools.py @@ -0,0 +1,17 @@ +""" +Misc. IP tools: normalize, handle mapped addresses +""" + +from netaddr import IPAddress + +def normalize_ip(ipaddr): + ipaddr = normalize_mapped_address(ipaddr) + return ipaddr + +def normalize_mapped_address(ipaddr): + ipaddr = IPAddress(ipaddr) + + if ipaddr.is_ipv4_compat() or ipaddr.is_ipv4_mapped(): + ipaddr = ipaddr.ipv4() + + return str(ipaddr) diff --git a/nsupdate/main/views.py b/nsupdate/main/views.py index 0fc1f6e..a0770a6 100644 --- a/nsupdate/main/views.py +++ b/nsupdate/main/views.py @@ -19,6 +19,7 @@ from django.core.exceptions import PermissionDenied from django.utils.timezone import now from . import dnstools +from .iptools import normalize_ip from .forms import (CreateHostForm, EditHostForm, CreateRelatedHostForm, EditRelatedHostForm, CreateDomainForm, EditDomainForm, CreateUpdaterHostConfigForm, EditUpdaterHostConfigForm) @@ -210,7 +211,7 @@ class AddHostView(CreateView): def form_valid(self, form): self.object = form.save(commit=False) try: - dnstools.add(self.object.get_fqdn(), self.request.META['REMOTE_ADDR']) + dnstools.add(self.object.get_fqdn(), normalize_ip(self.request.META['REMOTE_ADDR'])) except dnstools.Timeout: success, level, msg = False, messages.ERROR, 'Timeout - communicating to name server failed.' except dnstools.NameServerNotAvailable: @@ -265,7 +266,7 @@ class HostView(UpdateView): def get_context_data(self, *args, **kwargs): context = super(HostView, self).get_context_data(*args, **kwargs) context['nav_overview'] = True - context['remote_addr'] = self.request.META['REMOTE_ADDR'] + context['remote_addr'] = normalize_ip(self.request.META['REMOTE_ADDR']) return context