Ticket #811: genericipaddressfield.2.diff
File genericipaddressfield.2.diff, 54.2 KB (added by , 13 years ago) |
---|
-
docs/topics/forms/modelforms.txt
83 83 84 84 ``IPAddressField`` ``IPAddressField`` 85 85 86 ``GenericIPAddressField`` ``GenericIPAddressField`` 87 86 88 ``ManyToManyField`` ``ModelMultipleChoiceField`` (see 87 89 below) 88 90 -
docs/ref/models/fields.txt
760 760 An IP address, in string format (e.g. "192.0.2.30"). The admin represents this 761 761 as an ``<input type="text">`` (a single-line input). 762 762 763 ``GenericIPAddressField`` 764 ------------------------- 765 766 .. class:: GenericIPAddressField([protocols=both, unpack_ipv4_mapped=False, **options]) 767 768 .. versionadded:: 1.4 769 770 An IPv4 or IPv6 address, in string format (e.g. "192.0.2.30" or "2a02:42fe::4") 771 The admin represents this as an ``<input type="text">`` (a single-line input). 772 773 The IPv6 address normalization follows `RFC4291 section 2.2`_, including using 774 the IPv4 format suggested in paragraph 3 of that section, like ``::ffff:192.0.2.0``. 775 For example, ``2001:0::0:01`` would be normalized to ``2001::1``, and ``::ffff:0a0a:0a0a`` 776 to ``::ffff:10.10.10.10``. All characters are converted to lowercase. 777 778 .. _RFC4291 section 2.2: http://tools.ietf.org/html/rfc4291#section-2.2 779 780 .. attribute:: GenericIPAddressField.protocol 781 782 Limits valid inputs to the specified protocol. 783 Accepted values are ``both`` (default), ``IPv4`` 784 or ``IPv6``. Matching is case insensitive. 785 786 .. attribute:: GenericIPAddressField.unpack_ipv4_mapped 787 788 Unpack IPv4 mapped addresses, like ``::ffff::192.0.2.1``. 789 If this option is enabled, that address would be unpacked to 790 ``192.0.2.1``. Default is disabled. Can only be used 791 when ``protocol`` is set to ``both``. 792 763 793 ``NullBooleanField`` 764 794 -------------------- 765 795 -
docs/ref/forms/fields.txt
622 622 expression. 623 623 * Error message keys: ``required``, ``invalid`` 624 624 625 ``GenericIPAddressField`` 626 ~~~~~~~~~~~~~~~~~~~~~~~~~ 627 628 .. class:: GenericIPAddressField(**kwargs) 629 630 .. versionadded:: 1.4 631 632 A field containing either an IPv4 or an IPv6 address. 633 634 * Default widget: ``TextInput`` 635 * Empty value: ``''`` (an empty string) 636 * Normalizes to: A Unicode object. IPv6 addresses are 637 normalized as described below. 638 * Validates that the given value is a valid IP address, using regular 639 expressions. 640 * Error message keys: ``required``, ``invalid`` 641 642 The IPv6 address normalization follows `RFC4291 section 2.2`_, including using 643 the IPv4 format suggested in paragraph 3 of that section, like ``::ffff:192.0.2.0``. 644 For example, ``2001:0::0:01`` would be normalized to ``2001::1``, and ``::ffff:0a0a:0a0a`` 645 to ``::ffff:10.10.10.10``. All characters are converted to lowercase. 646 647 .. _RFC4291 section 2.2: http://tools.ietf.org/html/rfc4291#section-2.2 648 649 Takes two optional arguments: 650 651 .. attribute:: GenericIPAddressField.protocol 652 653 Limits valid inputs to the specified protocol. 654 Accepted values are ``both`` (default), ``IPv4`` 655 or ``IPv6``. Matching is case insensitive. 656 657 .. attribute:: GenericIPAddressField.unpack_ipv4_mapped 658 659 Unpack IPv4 mapped addresses, like ``::ffff::192.0.2.1``. 660 If this option is enabled, that address would be unpacked to 661 ``192.0.2.1``. Default is disabled. Can only be used 662 when ``protocol`` is set to ``both``. 663 625 664 ``MultipleChoiceField`` 626 665 ~~~~~~~~~~~~~~~~~~~~~~~ 627 666 -
docs/ref/validators.txt
130 130 A :class:`RegexValidator` instance that ensures a value looks like an IPv4 131 131 address. 132 132 133 ``validate_ipv6_address`` 134 ------------------------- 135 .. versionadded:: 1.4 136 137 .. data:: validate_ipv6_address 138 139 Uses :mod:`django.utils.ipv6` to check the validity of an IPv6 address. 140 141 ``validate_ipv46_address`` 142 -------------------------- 143 .. versionadded:: 1.4 144 145 .. data:: validate_ipv46_address 146 147 Uses both ``validate_ipv4_address`` and ``validate_ipv6_address`` to ensure 148 a value is either a valid IPv4 or IPv6 address. 149 133 150 ``validate_comma_separated_integer_list`` 134 151 ----------------------------------------- 135 152 .. data:: validate_comma_separated_integer_list -
django/db/models/fields/__init__.py
17 17 from django.utils.translation import ugettext_lazy as _ 18 18 from django.utils.encoding import smart_unicode, force_unicode, smart_str 19 19 from django.utils import datetime_safe 20 from django.utils.ipv6 import (clean_ipv6_address, is_valid_ipv6_address, 21 validate_configure_ipaddressfield_settings) 20 22 21 23 class NOT_PROVIDED: 22 24 pass … … 920 922 921 923 class IPAddressField(Field): 922 924 empty_strings_allowed = False 923 description = _("IP address")925 description = _("IPv4 address") 924 926 def __init__(self, *args, **kwargs): 925 927 kwargs['max_length'] = 15 926 928 Field.__init__(self, *args, **kwargs) … … 933 935 defaults.update(kwargs) 934 936 return super(IPAddressField, self).formfield(**defaults) 935 937 938 class GenericIPAddressField(Field): 939 empty_strings_allowed = True 940 description = _("IP address") 941 default_error_messages = {} 942 943 def __init__(self, protocol='both', unpack_ipv4_mapped=False, *args, **kwargs): 944 self.unpack_ipv4_mapped = unpack_ipv4_mapped 945 946 self.default_validators = validate_configure_ipaddressfield_settings( 947 protocol, unpack_ipv4_mapped, self.default_error_messages) 948 949 kwargs['max_length'] = 39 950 Field.__init__(self, *args, **kwargs) 951 952 def get_internal_type(self): 953 return "GenericIPAddressField" 954 955 def to_python(self, value): 956 if value and ':' in value: 957 return clean_ipv6_address(value, self.unpack_ipv4_mapped, self.default_error_messages['invalid']) 958 959 return value 960 961 def get_prep_value(self, value): 962 if value and ':' in value: 963 try: 964 return clean_ipv6_address(value, self.unpack_ipv4_mapped) 965 except ValidationError: 966 pass 967 968 return value 969 970 def formfield(self, **kwargs): 971 defaults = {'form_class': forms.GenericIPAddressField} 972 defaults.update(kwargs) 973 return super(GenericIPAddressField, self).formfield(**defaults) 974 975 936 976 class NullBooleanField(Field): 937 977 empty_strings_allowed = False 938 978 default_error_messages = { -
django/db/backends/sqlite3/creation.py
20 20 'IntegerField': 'integer', 21 21 'BigIntegerField': 'bigint', 22 22 'IPAddressField': 'char(15)', 23 'GenericIPAddressField': 'char(39)', 23 24 'NullBooleanField': 'bool', 24 25 'OneToOneField': 'integer', 25 26 'PositiveIntegerField': 'integer unsigned', -
django/db/backends/mysql/creation.py
19 19 'IntegerField': 'integer', 20 20 'BigIntegerField': 'bigint', 21 21 'IPAddressField': 'char(15)', 22 'GenericIPAddressField': 'char(39)', 22 23 'NullBooleanField': 'bool', 23 24 'OneToOneField': 'integer', 24 25 'PositiveIntegerField': 'integer UNSIGNED', -
django/db/backends/oracle/creation.py
27 27 'IntegerField': 'NUMBER(11)', 28 28 'BigIntegerField': 'NUMBER(19)', 29 29 'IPAddressField': 'VARCHAR2(15)', 30 'GenericIPAddressField': 'VARCHAR2(39)', 30 31 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))', 31 32 'OneToOneField': 'NUMBER(11)', 32 33 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', -
django/db/backends/postgresql_psycopg2/introspection.py
12 12 700: 'FloatField', 13 13 701: 'FloatField', 14 14 869: 'IPAddressField', 15 869: 'GenericIPAddressField', 15 16 1043: 'CharField', 16 17 1082: 'DateField', 17 18 1083: 'TimeField', -
django/db/backends/postgresql_psycopg2/creation.py
21 21 'IntegerField': 'integer', 22 22 'BigIntegerField': 'bigint', 23 23 'IPAddressField': 'inet', 24 'GenericIPAddressField': 'inet', 24 25 'NullBooleanField': 'boolean', 25 26 'OneToOneField': 'integer', 26 27 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', -
django/forms/fields.py
9 9 import time 10 10 import urlparse 11 11 import warnings 12 import socket 12 13 from decimal import Decimal, DecimalException 13 14 try: 14 15 from cStringIO import StringIO … … 21 22 from django.utils.translation import ugettext_lazy as _ 22 23 from django.utils.encoding import smart_unicode, smart_str, force_unicode 23 24 from django.utils.functional import lazy 25 from django.utils.ipv6 import clean_ipv6_address, validate_configure_ipaddressfield_settings 24 26 25 27 # Provide this import for backwards compatibility. 26 28 from django.core.validators import EMPTY_VALUES … … 37 39 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 38 40 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 39 41 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 40 'SplitDateTimeField', 'IPAddressField', ' FilePathField', 'SlugField',41 ' TypedChoiceField', 'TypedMultipleChoiceField'42 'SplitDateTimeField', 'IPAddressField', 'GenericIPAddressField', 'FilePathField', 43 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField' 42 44 ) 43 45 44 46 … … 955 957 default_validators = [validators.validate_ipv4_address] 956 958 957 959 960 class GenericIPAddressField(CharField): 961 default_error_messages = {} 962 963 def __init__(self, protocol='both', unpack_ipv4_mapped=False, *args, **kwargs): 964 self.unpack_ipv4_mapped = unpack_ipv4_mapped 965 966 self.default_validators = validate_configure_ipaddressfield_settings( 967 protocol, unpack_ipv4_mapped, self.default_error_messages) 968 969 super(GenericIPAddressField, self).__init__(*args, **kwargs) 970 971 def to_python(self, value): 972 if not value: 973 return '' 974 975 if value and ':' in value: 976 return clean_ipv6_address(value, self.unpack_ipv4_mapped, self.default_error_messages['invalid']) 977 978 return value 979 980 958 981 class SlugField(CharField): 959 982 default_error_messages = { 960 983 'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers," -
django/core/validators.py
5 5 from django.core.exceptions import ValidationError 6 6 from django.utils.translation import ugettext_lazy as _ 7 7 from django.utils.encoding import smart_unicode 8 from django.utils.ipv6 import is_valid_ipv6_address 8 9 9 10 # These values, if given to validate(), will trigger the self.required check. 10 11 EMPTY_VALUES = (None, '', [], (), {}) … … 145 146 ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') 146 147 validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid') 147 148 149 def validate_ipv6_address(value): 150 if not is_valid_ipv6_address(value): 151 raise ValidationError(_(u'Enter a valid IPv6 address.'), code='invalid') 152 153 def validate_ipv46_address(value): 154 try: 155 validate_ipv4_address(value) 156 except ValidationError: 157 try: 158 validate_ipv6_address(value) 159 except ValidationError: 160 raise ValidationError(_(u'Enter a valid IPv4 or IPv6 address.'), code='invalid') 161 148 162 comma_separated_int_list_re = re.compile('^[\d,]+$') 149 163 validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid') 150 164 -
django/utils/ipv6.py
1 # This code was mostly based on ipaddr-py 2 # Copyright 2007 Google Inc. http://code.google.com/p/ipaddr-py/ 3 # Licensed under the Apache License, Version 2.0 (the "License"). 4 5 from django.core.exceptions import ValidationError 6 from django.utils.translation import ugettext_lazy as _ 7 8 def clean_ipv6_address(ip_str, unpack_ipv4_mapped=False, error_message="This is not a valid IPv6 address"): 9 """Cleans a IPv6 address string. 10 11 Validity is checked by calling is_valid_ipv6_address() - if an 12 invalid address is passed, ValidationError is raised. 13 14 Replaces the longest continious zero-sequence with "::" and 15 removes leading zeroes and makes sure all hextets are lowercase. 16 17 Args: 18 ip_str: A valid IPv6 address. 19 unpack_ipv4_mapped: if an IPv4-mapped address is found, 20 return the plain IPv4 address (default=False). 21 error_message: A error message for in the ValidationError. 22 23 Returns: 24 A compressed IPv6 address, or the same value 25 26 """ 27 best_doublecolon_start = -1 28 best_doublecolon_len = 0 29 doublecolon_start = -1 30 doublecolon_len = 0 31 32 if not is_valid_ipv6_address(ip_str): 33 raise ValidationError(error_message) 34 35 # This algorithm can only handle fully exploded 36 # IP strings 37 ip_str = _explode_shorthand_ip_string(ip_str) 38 39 ip_str = _sanitize_ipv4_mapping(ip_str) 40 41 # If needed, unpack the IPv4 and return straight away 42 # - no need in running the rest of the algorithm 43 if unpack_ipv4_mapped: 44 ipv4_unpacked = _unpack_ipv4(ip_str) 45 46 if ipv4_unpacked: 47 return ipv4_unpacked 48 49 hextets = ip_str.split(":") 50 51 for index in range(len(hextets)): 52 # Remove leading zeroes 53 hextets[index] = hextets[index].lstrip('0') 54 if not hextets[index]: 55 hextets[index] = '0' 56 57 # Determine best hextet to compress 58 if hextets[index] == '0': 59 doublecolon_len += 1 60 if doublecolon_start == -1: 61 # Start of a sequence of zeros. 62 doublecolon_start = index 63 if doublecolon_len > best_doublecolon_len: 64 # This is the longest sequence of zeros so far. 65 best_doublecolon_len = doublecolon_len 66 best_doublecolon_start = doublecolon_start 67 else: 68 doublecolon_len = 0 69 doublecolon_start = -1 70 71 # Compress the most suitable hextet 72 if best_doublecolon_len > 1: 73 best_doublecolon_end = (best_doublecolon_start + 74 best_doublecolon_len) 75 # For zeros at the end of the address. 76 if best_doublecolon_end == len(hextets): 77 hextets += [''] 78 hextets[best_doublecolon_start:best_doublecolon_end] = [''] 79 # For zeros at the beginning of the address. 80 if best_doublecolon_start == 0: 81 hextets = [''] + hextets 82 83 result = ":".join(hextets) 84 85 return result.lower() 86 87 88 def _sanitize_ipv4_mapping(ip_str): 89 """Sanitize IPv4 mapping in a expanded IPv6 address. 90 91 This converts ::ffff:0a0a:0a0a to ::ffff:10.10.10.10. 92 If there is nothing to sanitize, returns an unchanged 93 string. 94 95 Args: 96 ip_str: A string, the expanded IPv6 address. 97 98 Returns: 99 The sanitized output string, if applicable. 100 """ 101 if not ip_str.lower().startswith('0000:0000:0000:0000:0000:ffff:'): 102 # not an ipv4 mapping 103 return ip_str 104 105 hextets = ip_str.split(':') 106 107 if '.' in hextets[-1]: 108 # already sanitized 109 return ip_str 110 111 ipv4_address = "%d.%d.%d.%d" % ( 112 int(hextets[6][0:2], 16), 113 int(hextets[6][2:4], 16), 114 int(hextets[7][0:2], 16), 115 int(hextets[7][2:4], 16), 116 ) 117 118 result = ':'.join(hextets[0:6]) 119 result += ':' + ipv4_address 120 121 return result 122 123 def _unpack_ipv4(ip_str): 124 """Unpack an IPv4 address that was mapped in a compressed IPv6 address. 125 126 This converts 0000:0000:0000:0000:0000:ffff:10.10.10.10 to 10.10.10.10. 127 If there is nothing to sanitize, returns None. 128 129 Args: 130 ip_str: A string, the expanded IPv6 address. 131 132 Returns: 133 The unpacked IPv4 address, or None if there was nothing to unpack. 134 """ 135 if not ip_str.lower().startswith('0000:0000:0000:0000:0000:ffff:'): 136 return None 137 138 hextets = ip_str.split(':') 139 return hextets[-1] 140 141 def is_valid_ipv6_address(ip_str): 142 """Ensure we have a valid IPv6 address. 143 144 Args: 145 ip_str: A string, the IPv6 address. 146 147 Returns: 148 A boolean, True if this is a valid IPv6 address. 149 150 """ 151 from django.core.validators import validate_ipv4_address 152 153 # We need to have at least one ':'. 154 if ':' not in ip_str: 155 return False 156 157 # We can only have one '::' shortener. 158 if ip_str.count('::') > 1: 159 return False 160 161 # '::' should be encompassed by start, digits or end. 162 if ':::' in ip_str: 163 return False 164 165 # A single colon can neither start nor end an address. 166 if ((ip_str.startswith(':') and not ip_str.startswith('::')) or 167 (ip_str.endswith(':') and not ip_str.endswith('::'))): 168 return False 169 170 # We can never have more than 7 ':' (1::2:3:4:5:6:7:8 is invalid) 171 if ip_str.count(':') > 7: 172 return False 173 174 # If we have no concatenation, we need to have 8 fields with 7 ':'. 175 if '::' not in ip_str and ip_str.count(':') != 7: 176 # We might have an IPv4 mapped address. 177 if ip_str.count('.') != 3: 178 return False 179 180 ip_str = _explode_shorthand_ip_string(ip_str) 181 182 # Now that we have that all squared away, let's check that each of the 183 # hextets are between 0x0 and 0xFFFF. 184 for hextet in ip_str.split(':'): 185 if hextet.count('.') == 3: 186 # If we have an IPv4 mapped address, the IPv4 portion has to 187 # be at the end of the IPv6 portion. 188 if not ip_str.split(':')[-1] == hextet: 189 return False 190 try: 191 validate_ipv4_address(hextet) 192 except ValidationError: 193 return False 194 else: 195 try: 196 # a value error here means that we got a bad hextet, 197 # something like 0xzzzz 198 if int(hextet, 16) < 0x0 or int(hextet, 16) > 0xFFFF: 199 return False 200 except ValueError: 201 return False 202 return True 203 204 205 def _explode_shorthand_ip_string(ip_str): 206 """Expand a shortened IPv6 address. 207 208 Args: 209 ip_str: A string, the IPv6 address. 210 211 Returns: 212 A string, the expanded IPv6 address. 213 214 """ 215 if not _is_shorthand_ip(ip_str): 216 # We've already got a longhand ip_str. 217 return ip_str 218 219 new_ip = [] 220 hextet = ip_str.split('::') 221 222 # If there is a ::, we need to expand it with zeroes 223 # to get to 8 hextets - unless there is a dot in the last hextet, 224 # meaning we're doing v4-mapping 225 if '.' in ip_str.split(':')[-1]: 226 fill_to = 7 227 else: 228 fill_to = 8 229 230 if len(hextet) > 1: 231 sep = len(hextet[0].split(':')) + len(hextet[1].split(':')) 232 new_ip = hextet[0].split(':') 233 234 for _ in xrange(fill_to - sep): 235 new_ip.append('0000') 236 new_ip += hextet[1].split(':') 237 238 else: 239 new_ip = ip_str.split(':') 240 241 # Now need to make sure every hextet is 4 lower case characters. 242 # If a hextet is < 4 characters, we've got missing leading 0's. 243 ret_ip = [] 244 for hextet in new_ip: 245 ret_ip.append(('0' * (4 - len(hextet)) + hextet).lower()) 246 return ':'.join(ret_ip) 247 248 249 def _is_shorthand_ip(ip_str): 250 """Determine if the address is shortened. 251 252 Args: 253 ip_str: A string, the IPv6 address. 254 255 Returns: 256 A boolean, True if the address is shortened. 257 258 """ 259 if ip_str.count('::') == 1: 260 return True 261 if filter(lambda x: len(x) < 4, ip_str.split(':')): 262 return True 263 return False 264 265 266 def validate_configure_ipaddressfield_settings(protocol, unpack_ipv4_mapped, default_error_messages): 267 """Set up an GenericIPAddressField instance and validate it's inputs. 268 269 This code is here, because it is exactly the same for the model and the form field. 270 """ 271 from django.core import validators 272 273 if protocol != 'both' and unpack_ipv4_mapped: 274 raise ValueError( 275 "You can only use `unpack_ipv4_mapped` if `protocol` is set to 'both'") 276 277 if protocol.lower() == "both": 278 default_validators = [validators.validate_ipv46_address] 279 default_error_messages['invalid'] = _(u'Enter a valid IPv4 or IPv6 address.') 280 281 elif protocol.lower() == "ipv4": 282 default_validators = [validators.validate_ipv4_address] 283 default_error_messages['invalid'] = _(u'Enter a valid IPv4 address.') 284 285 elif protocol.lower() == "ipv6": 286 default_validators = [validators.validate_ipv6_address] 287 default_error_messages['invalid'] = _(u'Enter a valid IPv6 address.') 288 289 else: 290 raise ValueError( 291 "The protocol '%s' is not a known protocol for GenericIPAddressField" 292 % protocol) 293 294 return default_validators 295 296 # This code was mostly based on ipaddr-py 297 # Copyright 2007 Google Inc. http://code.google.com/p/ipaddr-py/ 298 # Licensed under the Apache License, Version 2.0 (the "License"). 299 300 from django.core.exceptions import ValidationError 301 from django.utils.translation import ugettext_lazy as _ 302 303 def clean_ipv6_address(ip_str, unpack_ipv4_mapped=False, error_message="This is not a valid IPv6 address"): 304 """Cleans a IPv6 address string. 305 306 Validity is checked by calling is_valid_ipv6_address() - if an 307 invalid address is passed, ValidationError is raised. 308 309 Replaces the longest continious zero-sequence with "::" and 310 removes leading zeroes and makes sure all hextets are lowercase. 311 312 Args: 313 ip_str: A valid IPv6 address. 314 unpack_ipv4_mapped: if an IPv4-mapped address is found, 315 return the plain IPv4 address (default=False). 316 error_message: A error message for in the ValidationError. 317 318 Returns: 319 A compressed IPv6 address, or the same value 320 321 """ 322 best_doublecolon_start = -1 323 best_doublecolon_len = 0 324 doublecolon_start = -1 325 doublecolon_len = 0 326 327 if not is_valid_ipv6_address(ip_str): 328 raise ValidationError(error_message) 329 330 # This algorithm can only handle fully exploded 331 # IP strings 332 ip_str = _explode_shorthand_ip_string(ip_str) 333 334 ip_str = _sanitize_ipv4_mapping(ip_str) 335 336 # If needed, unpack the IPv4 and return straight away 337 # - no need in running the rest of the algorithm 338 if unpack_ipv4_mapped: 339 ipv4_unpacked = _unpack_ipv4(ip_str) 340 341 if ipv4_unpacked: 342 return ipv4_unpacked 343 344 hextets = ip_str.split(":") 345 346 for index in range(len(hextets)): 347 # Remove leading zeroes 348 hextets[index] = hextets[index].lstrip('0') 349 if not hextets[index]: 350 hextets[index] = '0' 351 352 # Determine best hextet to compress 353 if hextets[index] == '0': 354 doublecolon_len += 1 355 if doublecolon_start == -1: 356 # Start of a sequence of zeros. 357 doublecolon_start = index 358 if doublecolon_len > best_doublecolon_len: 359 # This is the longest sequence of zeros so far. 360 best_doublecolon_len = doublecolon_len 361 best_doublecolon_start = doublecolon_start 362 else: 363 doublecolon_len = 0 364 doublecolon_start = -1 365 366 # Compress the most suitable hextet 367 if best_doublecolon_len > 1: 368 best_doublecolon_end = (best_doublecolon_start + 369 best_doublecolon_len) 370 # For zeros at the end of the address. 371 if best_doublecolon_end == len(hextets): 372 hextets += [''] 373 hextets[best_doublecolon_start:best_doublecolon_end] = [''] 374 # For zeros at the beginning of the address. 375 if best_doublecolon_start == 0: 376 hextets = [''] + hextets 377 378 result = ":".join(hextets) 379 380 return result.lower() 381 382 383 def _sanitize_ipv4_mapping(ip_str): 384 """Sanitize IPv4 mapping in a expanded IPv6 address. 385 386 This converts ::ffff:0a0a:0a0a to ::ffff:10.10.10.10. 387 If there is nothing to sanitize, returns an unchanged 388 string. 389 390 Args: 391 ip_str: A string, the expanded IPv6 address. 392 393 Returns: 394 The sanitized output string, if applicable. 395 """ 396 if not ip_str.lower().startswith('0000:0000:0000:0000:0000:ffff:'): 397 # not an ipv4 mapping 398 return ip_str 399 400 hextets = ip_str.split(':') 401 402 if '.' in hextets[-1]: 403 # already sanitized 404 return ip_str 405 406 ipv4_address = "%d.%d.%d.%d" % ( 407 int(hextets[6][0:2], 16), 408 int(hextets[6][2:4], 16), 409 int(hextets[7][0:2], 16), 410 int(hextets[7][2:4], 16), 411 ) 412 413 result = ':'.join(hextets[0:6]) 414 result += ':' + ipv4_address 415 416 return result 417 418 def _unpack_ipv4(ip_str): 419 """Unpack an IPv4 address that was mapped in a compressed IPv6 address. 420 421 This converts 0000:0000:0000:0000:0000:ffff:10.10.10.10 to 10.10.10.10. 422 If there is nothing to sanitize, returns None. 423 424 Args: 425 ip_str: A string, the expanded IPv6 address. 426 427 Returns: 428 The unpacked IPv4 address, or None if there was nothing to unpack. 429 """ 430 if not ip_str.lower().startswith('0000:0000:0000:0000:0000:ffff:'): 431 return None 432 433 hextets = ip_str.split(':') 434 return hextets[-1] 435 436 def is_valid_ipv6_address(ip_str): 437 """Ensure we have a valid IPv6 address. 438 439 Args: 440 ip_str: A string, the IPv6 address. 441 442 Returns: 443 A boolean, True if this is a valid IPv6 address. 444 445 """ 446 from django.core.validators import validate_ipv4_address 447 448 # We need to have at least one ':'. 449 if ':' not in ip_str: 450 return False 451 452 # We can only have one '::' shortener. 453 if ip_str.count('::') > 1: 454 return False 455 456 # '::' should be encompassed by start, digits or end. 457 if ':::' in ip_str: 458 return False 459 460 # A single colon can neither start nor end an address. 461 if ((ip_str.startswith(':') and not ip_str.startswith('::')) or 462 (ip_str.endswith(':') and not ip_str.endswith('::'))): 463 return False 464 465 # We can never have more than 7 ':' (1::2:3:4:5:6:7:8 is invalid) 466 if ip_str.count(':') > 7: 467 return False 468 469 # If we have no concatenation, we need to have 8 fields with 7 ':'. 470 if '::' not in ip_str and ip_str.count(':') != 7: 471 # We might have an IPv4 mapped address. 472 if ip_str.count('.') != 3: 473 return False 474 475 ip_str = _explode_shorthand_ip_string(ip_str) 476 477 # Now that we have that all squared away, let's check that each of the 478 # hextets are between 0x0 and 0xFFFF. 479 for hextet in ip_str.split(':'): 480 if hextet.count('.') == 3: 481 # If we have an IPv4 mapped address, the IPv4 portion has to 482 # be at the end of the IPv6 portion. 483 if not ip_str.split(':')[-1] == hextet: 484 return False 485 try: 486 validate_ipv4_address(hextet) 487 except ValidationError: 488 return False 489 else: 490 try: 491 # a value error here means that we got a bad hextet, 492 # something like 0xzzzz 493 if int(hextet, 16) < 0x0 or int(hextet, 16) > 0xFFFF: 494 return False 495 except ValueError: 496 return False 497 return True 498 499 500 def _explode_shorthand_ip_string(ip_str): 501 """Expand a shortened IPv6 address. 502 503 Args: 504 ip_str: A string, the IPv6 address. 505 506 Returns: 507 A string, the expanded IPv6 address. 508 509 """ 510 if not _is_shorthand_ip(ip_str): 511 # We've already got a longhand ip_str. 512 return ip_str 513 514 new_ip = [] 515 hextet = ip_str.split('::') 516 517 # If there is a ::, we need to expand it with zeroes 518 # to get to 8 hextets - unless there is a dot in the last hextet, 519 # meaning we're doing v4-mapping 520 if '.' in ip_str.split(':')[-1]: 521 fill_to = 7 522 else: 523 fill_to = 8 524 525 if len(hextet) > 1: 526 sep = len(hextet[0].split(':')) + len(hextet[1].split(':')) 527 new_ip = hextet[0].split(':') 528 529 for _ in xrange(fill_to - sep): 530 new_ip.append('0000') 531 new_ip += hextet[1].split(':') 532 533 else: 534 new_ip = ip_str.split(':') 535 536 # Now need to make sure every hextet is 4 lower case characters. 537 # If a hextet is < 4 characters, we've got missing leading 0's. 538 ret_ip = [] 539 for hextet in new_ip: 540 ret_ip.append(('0' * (4 - len(hextet)) + hextet).lower()) 541 return ':'.join(ret_ip) 542 543 544 def _is_shorthand_ip(ip_str): 545 """Determine if the address is shortened. 546 547 Args: 548 ip_str: A string, the IPv6 address. 549 550 Returns: 551 A boolean, True if the address is shortened. 552 553 """ 554 if ip_str.count('::') == 1: 555 return True 556 if filter(lambda x: len(x) < 4, ip_str.split(':')): 557 return True 558 return False 559 560 561 def validate_configure_ipaddressfield_settings(protocol, unpack_ipv4_mapped, default_error_messages): 562 """Set up an GenericIPAddressField instance and validate it's inputs. 563 564 This code is here, because it is exactly the same for the model and the form field. 565 """ 566 from django.core import validators 567 568 if protocol != 'both' and unpack_ipv4_mapped: 569 raise ValueError( 570 "You can only use `unpack_ipv4_mapped` if `protocol` is set to 'both'") 571 572 if protocol.lower() == "both": 573 default_validators = [validators.validate_ipv46_address] 574 default_error_messages['invalid'] = _(u'Enter a valid IPv4 or IPv6 address.') 575 576 elif protocol.lower() == "ipv4": 577 default_validators = [validators.validate_ipv4_address] 578 default_error_messages['invalid'] = _(u'Enter a valid IPv4 address.') 579 580 elif protocol.lower() == "ipv6": 581 default_validators = [validators.validate_ipv6_address] 582 default_error_messages['invalid'] = _(u'Enter a valid IPv6 address.') 583 584 else: 585 raise ValueError( 586 "The protocol '%s' is not a known protocol for GenericIPAddressField" 587 % protocol) 588 589 return default_validators 590 -
tests/modeltests/validators/tests.py
52 52 (validate_ipv4_address, '25,1,1,1', ValidationError), 53 53 (validate_ipv4_address, '25.1 .1.1', ValidationError), 54 54 55 # validate_ipv6_address uses django.utils.ipv6, which 56 # is tested in much greater detail in it's own testcase 57 (validate_ipv6_address, 'fe80::1', None), 58 (validate_ipv6_address, '::1', None), 59 (validate_ipv6_address, '1:2:3:4:5:6:7:8', None), 60 61 (validate_ipv6_address, '1:2', ValidationError), 62 (validate_ipv6_address, '::zzz', ValidationError), 63 (validate_ipv6_address, '12345::', ValidationError), 64 65 (validate_ipv46_address, '1.1.1.1', None), 66 (validate_ipv46_address, '255.0.0.0', None), 67 (validate_ipv46_address, '0.0.0.0', None), 68 (validate_ipv46_address, 'fe80::1', None), 69 (validate_ipv46_address, '::1', None), 70 (validate_ipv46_address, '1:2:3:4:5:6:7:8', None), 71 72 (validate_ipv46_address, '256.1.1.1', ValidationError), 73 (validate_ipv46_address, '25.1.1.', ValidationError), 74 (validate_ipv46_address, '25,1,1,1', ValidationError), 75 (validate_ipv46_address, '25.1 .1.1', ValidationError), 76 (validate_ipv46_address, '1:2', ValidationError), 77 (validate_ipv46_address, '::zzz', ValidationError), 78 (validate_ipv46_address, '12345::', ValidationError), 79 55 80 (validate_comma_separated_integer_list, '1', None), 56 81 (validate_comma_separated_integer_list, '1,2,3', None), 57 82 (validate_comma_separated_integer_list, '1,2,3,', None), -
tests/modeltests/validation/tests.py
2 2 from django.test import TestCase 3 3 from django.core.exceptions import NON_FIELD_ERRORS 4 4 from modeltests.validation import ValidationTestCase 5 from modeltests.validation.models import Author, Article, ModelToValidate 5 from modeltests.validation.models import (Author, Article, ModelToValidate, 6 GenericIPAddressTestModel, GenericIPAddressWithUnpackUniqueTestModel) 6 7 7 8 # Import other tests for this package. 8 9 from modeltests.validation.validators import TestModelsWithValidators … … 77 78 mtv = ModelToValidate(number=10, name='Some Name'*100) 78 79 self.assertFailsValidation(mtv.full_clean, ['name',]) 79 80 81 80 82 class ArticleForm(forms.ModelForm): 81 83 class Meta: 82 84 model = Article … … 124 126 article = Article(author_id=self.author.id) 125 127 form = ArticleForm(data, instance=article) 126 128 self.assertEqual(form.errors.keys(), ['pub_date']) 129 130 131 class GenericIPAddressFieldTests(ValidationTestCase): 132 133 def test_correct_generic_ip_passes(self): 134 giptm = GenericIPAddressTestModel(generic_ip="1.2.3.4") 135 self.assertEqual(None, giptm.full_clean()) 136 giptm = GenericIPAddressTestModel(generic_ip="2001::2") 137 self.assertEqual(None, giptm.full_clean()) 138 139 def test_invalid_generic_ip_raises_error(self): 140 giptm = GenericIPAddressTestModel(generic_ip="294.4.2.1") 141 self.assertFailsValidation(giptm.full_clean, ['generic_ip',]) 142 giptm = GenericIPAddressTestModel(generic_ip="1:2") 143 self.assertFailsValidation(giptm.full_clean, ['generic_ip',]) 144 145 def test_correct_v4_ip_passes(self): 146 giptm = GenericIPAddressTestModel(v4_ip="1.2.3.4") 147 self.assertEqual(None, giptm.full_clean()) 148 149 def test_invalid_v4_ip_raises_error(self): 150 giptm = GenericIPAddressTestModel(v4_ip="294.4.2.1") 151 self.assertFailsValidation(giptm.full_clean, ['v4_ip',]) 152 giptm = GenericIPAddressTestModel(v4_ip="2001::2") 153 self.assertFailsValidation(giptm.full_clean, ['v4_ip',]) 154 155 def test_correct_v6_ip_passes(self): 156 giptm = GenericIPAddressTestModel(v6_ip="2001::2") 157 self.assertEqual(None, giptm.full_clean()) 158 159 def test_invalid_v6_ip_raises_error(self): 160 giptm = GenericIPAddressTestModel(v6_ip="1.2.3.4") 161 self.assertFailsValidation(giptm.full_clean, ['v6_ip',]) 162 giptm = GenericIPAddressTestModel(v6_ip="1:2") 163 self.assertFailsValidation(giptm.full_clean, ['v6_ip',]) 164 165 def test_v6_uniqueness_detection(self): 166 # These two addresses are the same with different syntax 167 giptm = GenericIPAddressTestModel(generic_ip="2001::1:0:0:0:0:2") 168 giptm.save() 169 giptm = GenericIPAddressTestModel(generic_ip="2001:0:1:2") 170 self.assertFailsValidation(giptm.full_clean, ['generic_ip',]) 171 172 def test_v4_unpack_uniqueness_detection(self): 173 # These two are different, because we are not doing IPv4 unpacking 174 giptm = GenericIPAddressTestModel(generic_ip="::ffff:10.10.10.10") 175 giptm.save() 176 giptm = GenericIPAddressTestModel(generic_ip="10.10.10.10") 177 self.assertEqual(None, giptm.full_clean()) 178 179 # These two are the same, because we are doing IPv4 unpacking 180 giptm = GenericIPAddressWithUnpackUniqueTestModel(generic_v4unpack_ip="::ffff:18.52.18.52") 181 giptm.save() 182 giptm = GenericIPAddressWithUnpackUniqueTestModel(generic_v4unpack_ip="18.52.18.52") 183 self.assertFailsValidation(giptm.full_clean, ['generic_v4unpack_ip',]) 184 185 No newline at end of file -
tests/modeltests/validation/models.py
81 81 82 82 class UniqueErrorsModel(models.Model): 83 83 name = models.CharField(max_length=100, unique=True, error_messages={'unique': u'Custom unique name message.'}) 84 number = models.IntegerField(unique=True, error_messages={'unique': u'Custom unique number message.'}) 85 No newline at end of file 84 number = models.IntegerField(unique=True, error_messages={'unique': u'Custom unique number message.'}) 85 86 class GenericIPAddressTestModel(models.Model): 87 generic_ip = models.GenericIPAddressField(blank=True, unique=True) 88 v4_ip = models.GenericIPAddressField(blank=True, protocol="ipv4") 89 v6_ip = models.GenericIPAddressField(blank=True, protocol="ipv6") 90 91 class GenericIPAddressWithUnpackUniqueTestModel(models.Model): 92 generic_v4unpack_ip = models.GenericIPAddressField(blank=True, unique=True, unpack_ipv4_mapped=True) 93 94 No newline at end of file -
tests/regressiontests/forms/tests/error_messages.py
196 196 self.assertFormErrors([u'REQUIRED'], f.clean, '') 197 197 self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0') 198 198 199 def test_generic_ipaddressfield(self): 200 e = { 201 'required': 'REQUIRED', 202 'invalid': 'INVALID IP ADDRESS', 203 } 204 f = GenericIPAddressField(error_messages=e) 205 self.assertFormErrors([u'REQUIRED'], f.clean, '') 206 self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0') 207 199 208 def test_subclassing_errorlist(self): 200 209 class TestForm(Form): 201 210 first_name = CharField() -
tests/regressiontests/forms/tests/extra.py
460 460 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5') 461 461 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5') 462 462 463 464 def test_generic_ipaddress_invalid_arguments(self): 465 self.assertRaises(ValueError, GenericIPAddressField, protocol="hamster") 466 self.assertRaises(ValueError, GenericIPAddressField, protocol="ipv4", unpack_ipv4_mapped=True) 467 468 def test_generic_ipaddress_as_generic(self): 469 # The edges of the IPv6 validation code are not deeply tested here, 470 # they are covered in the tests for django.utils.ipv6 471 472 f = GenericIPAddressField() 473 self.assertFormErrors([u'This field is required.'], f.clean, '') 474 self.assertFormErrors([u'This field is required.'], f.clean, None) 475 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 476 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo') 477 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.') 478 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5') 479 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5') 480 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 481 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 482 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '12345:2:3:4') 483 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3::4') 484 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 485 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') 486 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1:2') 487 488 def test_generic_ipaddress_as_ipv4_only(self): 489 f = GenericIPAddressField(protocol="IPv4") 490 self.assertFormErrors([u'This field is required.'], f.clean, '') 491 self.assertFormErrors([u'This field is required.'], f.clean, None) 492 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 493 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo') 494 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.') 495 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5') 496 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5') 497 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'fe80::223:6cff:fe8a:2e8a') 498 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '2a02::223:6cff:fe8a:2e8a') 499 500 def test_generic_ipaddress_as_ipv4_only(self): 501 f = GenericIPAddressField(protocol="IPv6") 502 self.assertFormErrors([u'This field is required.'], f.clean, '') 503 self.assertFormErrors([u'This field is required.'], f.clean, None) 504 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.1') 505 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo') 506 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.') 507 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1.2.3.4.5') 508 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '256.125.1.5') 509 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 510 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 511 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '12345:2:3:4') 512 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3::4') 513 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 514 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') 515 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1:2') 516 517 def test_generic_ipaddress_as_generic_not_required(self): 518 f = GenericIPAddressField(required=False) 519 self.assertEqual(f.clean(''), u'') 520 self.assertEqual(f.clean(None), u'') 521 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 522 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo') 523 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.') 524 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5') 525 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5') 526 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 527 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 528 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '12345:2:3:4') 529 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3::4') 530 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 531 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') 532 self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1:2') 533 534 def test_generic_ipaddress_normalisation(self): 535 # Test the normalising code 536 f = GenericIPAddressField() 537 self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'::ffff:10.10.10.10') 538 self.assertEqual(f.clean('::ffff:10.10.10.10'), u'::ffff:10.10.10.10') 539 self.assertEqual(f.clean('2001:000:a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 540 self.assertEqual(f.clean('2001::a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 541 542 f = GenericIPAddressField(unpack_ipv4_mapped=True) 543 self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'10.10.10.10') 544 545 463 546 def test_smart_unicode(self): 464 547 class Test: 465 548 def __str__(self): -
tests/regressiontests/serializers_regress/tests.py
196 196 #(XX, ImageData 197 197 (data_obj, 90, IPAddressData, "127.0.0.1"), 198 198 (data_obj, 91, IPAddressData, None), 199 (data_obj, 95, GenericIPAddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), 200 (data_obj, 96, GenericIPAddressData, None), 199 201 (data_obj, 100, NullBooleanData, True), 200 202 (data_obj, 101, NullBooleanData, False), 201 203 (data_obj, 102, NullBooleanData, None), … … 298 300 (pk_obj, 682, IntegerPKData, 0), 299 301 # (XX, ImagePKData 300 302 (pk_obj, 690, IPAddressPKData, "127.0.0.1"), 303 (pk_obj, 695, GenericIPAddressPKData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), 301 304 # (pk_obj, 700, NullBooleanPKData, True), 302 305 # (pk_obj, 701, NullBooleanPKData, False), 303 306 (pk_obj, 710, PhonePKData, "212-634-5789"), -
tests/regressiontests/serializers_regress/models.py
52 52 class IPAddressData(models.Model): 53 53 data = models.IPAddressField(null=True) 54 54 55 class GenericIPAddressData(models.Model): 56 data = models.GenericIPAddressField(null=True) 57 55 58 class NullBooleanData(models.Model): 56 59 data = models.NullBooleanField(null=True) 57 60 … … 187 190 class IPAddressPKData(models.Model): 188 191 data = models.IPAddressField(primary_key=True) 189 192 193 class GenericIPAddressPKData(models.Model): 194 data = models.GenericIPAddressField(primary_key=True) 195 190 196 # This is just a Boolean field with null=True, and we can't test a PK value of NULL. 191 197 # class NullBooleanPKData(models.Model): 192 198 # data = models.NullBooleanField(primary_key=True) -
tests/regressiontests/utils/tests.py
19 19 from datetime_safe import * 20 20 from baseconv import * 21 21 from jslex import * 22 from ipv6 import * -
tests/regressiontests/utils/ipv6.py
1 from django.utils.ipv6 import is_valid_ipv6_address, clean_ipv6_address 2 from django.utils import unittest 3 4 class TestUtilsIPv6(unittest.TestCase): 5 6 def test_validates_correct_plain_address(self): 7 self.assertTrue(is_valid_ipv6_address('fe80::223:6cff:fe8a:2e8a')) 8 self.assertTrue(is_valid_ipv6_address('2a02::223:6cff:fe8a:2e8a')) 9 self.assertTrue(is_valid_ipv6_address('1::2:3:4:5:6:7')) 10 self.assertTrue(is_valid_ipv6_address('::')) 11 self.assertTrue(is_valid_ipv6_address('::a')) 12 self.assertTrue(is_valid_ipv6_address('2::')) 13 14 def test_validates_correct_with_v4mapping(self): 15 self.assertTrue(is_valid_ipv6_address('::ffff:254.42.16.14')) 16 self.assertTrue(is_valid_ipv6_address('::ffff:0a0a:0a0a')) 17 18 def test_validates_incorrect_plain_address(self): 19 self.assertFalse(is_valid_ipv6_address('foo')) 20 self.assertFalse(is_valid_ipv6_address('127.0.0.1')) 21 self.assertFalse(is_valid_ipv6_address('12345::')) 22 self.assertFalse(is_valid_ipv6_address('1::2:3::4')) 23 self.assertFalse(is_valid_ipv6_address('1::zzz')) 24 self.assertFalse(is_valid_ipv6_address('1::2:3:4:5:6:7:8')) 25 self.assertFalse(is_valid_ipv6_address('1:2')) 26 self.assertFalse(is_valid_ipv6_address('1:::2')) 27 28 def test_validates_incorrect_with_v4mapping(self): 29 self.assertFalse(is_valid_ipv6_address('::ffff:999.42.16.14')) 30 self.assertFalse(is_valid_ipv6_address('::ffff:zzzz:0a0a')) 31 # The ::1.2.3.4 format used to be valid but was deprecated 32 # in rfc4291 section 2.5.5.1 33 self.assertTrue(is_valid_ipv6_address('::254.42.16.14')) 34 self.assertTrue(is_valid_ipv6_address('::0a0a:0a0a')) 35 self.assertFalse(is_valid_ipv6_address('::999.42.16.14')) 36 self.assertFalse(is_valid_ipv6_address('::zzzz:0a0a')) 37 38 def test_cleanes_plain_address(self): 39 self.assertEqual(clean_ipv6_address('DEAD::0:BEEF'), u'dead::beef') 40 self.assertEqual(clean_ipv6_address('2001:000:a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 41 self.assertEqual(clean_ipv6_address('2001::a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 42 43 def test_cleanes_with_v4_mapping(self): 44 self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a'), u'::ffff:10.10.10.10') 45 self.assertEqual(clean_ipv6_address('::ffff:1234:1234'), u'::ffff:18.52.18.52') 46 self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52'), u'::ffff:18.52.18.52') 47 48 def test_unpacks_ipv4(self): 49 self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a', unpack_ipv4_mapped=True), u'10.10.10.10') 50 self.assertEqual(clean_ipv6_address('::ffff:1234:1234', unpack_ipv4_mapped=True), u'18.52.18.52') 51 self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52', unpack_ipv4_mapped=True), u'18.52.18.52') 52 from django.utils.ipv6 import is_valid_ipv6_address, clean_ipv6_address 53 from django.utils import unittest 54 55 class TestUtilsIPv6(unittest.TestCase): 56 57 def test_validates_correct_plain_address(self): 58 self.assertTrue(is_valid_ipv6_address('fe80::223:6cff:fe8a:2e8a')) 59 self.assertTrue(is_valid_ipv6_address('2a02::223:6cff:fe8a:2e8a')) 60 self.assertTrue(is_valid_ipv6_address('1::2:3:4:5:6:7')) 61 self.assertTrue(is_valid_ipv6_address('::')) 62 self.assertTrue(is_valid_ipv6_address('::a')) 63 self.assertTrue(is_valid_ipv6_address('2::')) 64 65 def test_validates_correct_with_v4mapping(self): 66 self.assertTrue(is_valid_ipv6_address('::ffff:254.42.16.14')) 67 self.assertTrue(is_valid_ipv6_address('::ffff:0a0a:0a0a')) 68 69 def test_validates_incorrect_plain_address(self): 70 self.assertFalse(is_valid_ipv6_address('foo')) 71 self.assertFalse(is_valid_ipv6_address('127.0.0.1')) 72 self.assertFalse(is_valid_ipv6_address('12345::')) 73 self.assertFalse(is_valid_ipv6_address('1::2:3::4')) 74 self.assertFalse(is_valid_ipv6_address('1::zzz')) 75 self.assertFalse(is_valid_ipv6_address('1::2:3:4:5:6:7:8')) 76 self.assertFalse(is_valid_ipv6_address('1:2')) 77 self.assertFalse(is_valid_ipv6_address('1:::2')) 78 79 def test_validates_incorrect_with_v4mapping(self): 80 self.assertFalse(is_valid_ipv6_address('::ffff:999.42.16.14')) 81 self.assertFalse(is_valid_ipv6_address('::ffff:zzzz:0a0a')) 82 # The ::1.2.3.4 format used to be valid but was deprecated 83 # in rfc4291 section 2.5.5.1 84 self.assertTrue(is_valid_ipv6_address('::254.42.16.14')) 85 self.assertTrue(is_valid_ipv6_address('::0a0a:0a0a')) 86 self.assertFalse(is_valid_ipv6_address('::999.42.16.14')) 87 self.assertFalse(is_valid_ipv6_address('::zzzz:0a0a')) 88 89 def test_cleanes_plain_address(self): 90 self.assertEqual(clean_ipv6_address('DEAD::0:BEEF'), u'dead::beef') 91 self.assertEqual(clean_ipv6_address('2001:000:a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 92 self.assertEqual(clean_ipv6_address('2001::a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 93 94 def test_cleanes_with_v4_mapping(self): 95 self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a'), u'::ffff:10.10.10.10') 96 self.assertEqual(clean_ipv6_address('::ffff:1234:1234'), u'::ffff:18.52.18.52') 97 self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52'), u'::ffff:18.52.18.52') 98 99 def test_unpacks_ipv4(self): 100 self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a', unpack_ipv4_mapped=True), u'10.10.10.10') 101 self.assertEqual(clean_ipv6_address('::ffff:1234:1234', unpack_ipv4_mapped=True), u'18.52.18.52') 102 self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52', unpack_ipv4_mapped=True), u'18.52.18.52')