ipv6.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import ipaddress
  2. from django.core.exceptions import ValidationError
  3. from django.utils.translation import gettext_lazy as _
  4. MAX_IPV6_ADDRESS_LENGTH = 39
  5. def _ipv6_address_from_str(ip_str, max_length=MAX_IPV6_ADDRESS_LENGTH):
  6. if len(ip_str) > max_length:
  7. raise ValueError(
  8. f"Unable to convert {ip_str} to an IPv6 address (value too long)."
  9. )
  10. return ipaddress.IPv6Address(int(ipaddress.IPv6Address(ip_str)))
  11. def clean_ipv6_address(
  12. ip_str,
  13. unpack_ipv4=False,
  14. error_message=_("This is not a valid IPv6 address."),
  15. max_length=MAX_IPV6_ADDRESS_LENGTH,
  16. ):
  17. """
  18. Clean an IPv6 address string.
  19. Raise ValidationError if the address is invalid.
  20. Replace the longest continuous zero-sequence with "::", remove leading
  21. zeroes, and make sure all hextets are lowercase.
  22. Args:
  23. ip_str: A valid IPv6 address.
  24. unpack_ipv4: if an IPv4-mapped address is found,
  25. return the plain IPv4 address (default=False).
  26. error_message: An error message used in the ValidationError.
  27. Return a compressed IPv6 address or the same value.
  28. """
  29. try:
  30. addr = _ipv6_address_from_str(ip_str, max_length)
  31. except ValueError:
  32. raise ValidationError(error_message, code="invalid")
  33. if unpack_ipv4 and addr.ipv4_mapped:
  34. return str(addr.ipv4_mapped)
  35. elif addr.ipv4_mapped:
  36. return "::ffff:%s" % str(addr.ipv4_mapped)
  37. return str(addr)
  38. def is_valid_ipv6_address(ip_addr):
  39. """
  40. Return whether the `ip_addr` object is a valid IPv6 address.
  41. """
  42. if isinstance(ip_addr, ipaddress.IPv6Address):
  43. return True
  44. try:
  45. _ipv6_address_from_str(ip_addr)
  46. except (TypeError, ValueError):
  47. return False
  48. return True