-
-
Notifications
You must be signed in to change notification settings - Fork 51.1k
Expand file tree
/
Copy pathis_ipv6_address_valid.py
More file actions
127 lines (102 loc) · 3.81 KB
/
Copy pathis_ipv6_address_valid.py
File metadata and controls
127 lines (102 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
Validate IPv6 addresses (full and compressed forms).
References:
- Wikipedia: https://en.wikipedia.org/wiki/IPv6_address
- RFC 4291: https://tools.ietf.org/html/rfc4291
Rules implemented (subset of RFC 4291):
- Hex groups are 1-4 hex digits (0-9, a-f, A-F).
- Exactly 8 groups in total. A single '::' may compress one or more groups of
zeros. No more than one '::' is allowed.
- No leading/trailing single ':' (e.g., ':1:2::' is invalid).
- Does NOT support IPv4-mapped endings like '::ffff:192.0.2.128'.
Examples (doctests):
>>> is_ipv6_address_valid("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
True
>>> is_ipv6_address_valid("2001:db8:85a3:0:0:8A2E:0370:7334")
True
>>> is_ipv6_address_valid("2001:db8:85a3::8A2E:0370:7334") # '::' compresses two groups
True
>>> is_ipv6_address_valid("::1") # loopback, 7 groups compressed
True
>>> is_ipv6_address_valid("2001:db8::") # trailing compression is OK
True
>>> is_ipv6_address_valid("::") # all groups compressed
True
Invalids:
>>> is_ipv6_address_valid("") # empty
False
>>> is_ipv6_address_valid("2001:db8:85a3:::8a2e:370:7334") # multiple '::'
False
>>> is_ipv6_address_valid("2001:db8:85a3:0:0:8a2e:370:7334:abcd") # 9 groups
False
>>> is_ipv6_address_valid("2001:db8:85a3:0:0:8a2e:0370:g334") # non-hex char
False
>>> is_ipv6_address_valid("2001:db8:85a3:0:0:8a2e:0370:") # trailing ':'
False
>>> is_ipv6_address_valid(":2001:db8::1") # leading ':'
False
>>> is_ipv6_address_valid("a::b:c:d:e:f:g:h") # '::' compresses zero groups (already 8)
False
>>> is_ipv6_address_valid("1::1:1:1:1:1:1:1") # same idea; zero groups compressed
False
"""
_HEX_DIGITS = set("0123456789abcdefABCDEF")
def _is_valid_group(group: str) -> bool:
"""Return True iff group is 1-4 hex digits.
Args:
group (str): The hex group to validate.
Returns:
bool: True if the group is 1-4 hex digits, False otherwise.
Examples:
>>> _is_valid_group("1")
True
>>> _is_valid_group("abcd")
True
>>> _is_valid_group("12345") # too long
False
>>> _is_valid_group("g") # non-hex character
False
"""
return 1 <= len(group) <= 4 and all(ch in _HEX_DIGITS for ch in group)
def is_ipv6_address_valid(address: str) -> bool:
"""
Return True iff `address` is a valid IPv6 address in full or compressed form.
See module docstring for rules and doctests.
"""
# Early validation checks combined
if not address:
return False
if (address.startswith(":") and not address.startswith("::")) or (
address.endswith(":") and not address.endswith("::")
):
return False
# Check for invalid triple-colon or more
if ":::" in address:
return False
double_colon_count = address.count("::")
if double_colon_count > 1:
return False
if double_colon_count == 0:
# No compression: must be exactly 8 non-empty groups.
parts: list[str] = address.split(":")
return (
len(parts) == 8
and all(part != "" for part in parts)
and all(_is_valid_group(part) for part in parts)
)
# With compression: split once on '::' into left and right sides.
left, right = address.split("::", 1)
left_groups = [g for g in left.split(":") if g != ""] if left else []
right_groups = [g for g in right.split(":") if g != ""] if right else []
# Validate: valid hex groups, proper count, and actual compression
total_groups = len(left_groups) + len(right_groups)
return (
total_groups < 8 # '::' must compress at least one group
and all(_is_valid_group(g) for g in left_groups + right_groups)
)
if __name__ == "__main__":
from doctest import testmod
testmod()
ip = input().strip()
valid_or_invalid = "valid" if is_ipv6_address_valid(ip) else "invalid"
print(f"{ip} is a {valid_or_invalid} IPv6 address.")