What's the best way to validate that an IP entered by the user is valid? It comes in as a string.
11 Answers
Don't parse it. Just ask.
import socket
try:
socket.inet_aton(addr)
# legal
except socket.error:
# Not legal
14 Comments
From Python 3.4 on, the best way to check if an IPv6 or IPv4 address is correct, is to use the Python Standard Library module ipaddress - IPv4/IPv6 manipulation library s.a. https://docs.python.org/3/library/ipaddress.html for complete documentation.
Example :
#!/usr/bin/env python
import ipaddress
import sys
try:
ip = ipaddress.ip_address(sys.argv[1])
print('%s is a correct IP%s address.' % (ip, ip.version))
except ValueError:
print('address/netmask is invalid: %s' % sys.argv[1])
except:
print('Usage : %s ip' % sys.argv[0])
For other versions: Github, phihag / Philipp Hagemeister,"Python 3.3's ipaddress for older Python versions", https://github.com/phihag/ipaddress
The backport from phihag is available e.g. in Anaconda Python 2.7 & is included in Installer. s.a. https://docs.continuum.io/anaconda/pkg-docs
To install with pip:
pip install ipaddress
s.a.: ipaddress 1.0.17, "IPv4/IPv6 manipulation library", "Port of the 3.3+ ipaddress module", https://pypi.python.org/pypi/ipaddress/1.0.17
4 Comments
test.example.com. I got IPv6Address(u'7465:7374:2e65:7861:6d70:6c65:2e63:6f6d')sys.argv[0]/[1] play in this example? Are these just test parameters (sample IP addresses) you are passing when firing the script or are they required when implementing? From reading here, seems like they are just test params.import socket
def is_valid_ipv4_address(address):
try:
socket.inet_pton(socket.AF_INET, address)
except AttributeError: # no inet_pton here, sorry
try:
socket.inet_aton(address)
except socket.error:
return False
return address.count('.') == 3
except socket.error: # not a valid address
return False
return True
def is_valid_ipv6_address(address):
try:
socket.inet_pton(socket.AF_INET6, address)
except socket.error: # not a valid address
return False
return True
5 Comments
socket.inet_aton('127.1') evaluates to '\x7f\x00\x00\x01' (i.e. exactly like '127.0.0.1' does). I've had this tiresome and lengthy discussion elsewhere on SO, can't bother to remember where, though.inet_pton exists only in Unix, and inet_aton exists in all platforms, so this is a “unix-mostly” answer.The IPy module (a module designed for dealing with IP addresses) will throw a ValueError exception for invalid addresses.
>>> from IPy import IP
>>> IP('127.0.0.1')
IP('127.0.0.1')
>>> IP('277.0.0.1')
Traceback (most recent call last):
...
ValueError: '277.0.0.1': single byte must be 0 <= byte < 256
>>> IP('foobar')
Traceback (most recent call last):
...
ValueError: invalid literal for long() with base 10: 'foobar'
However, like Dustin's answer, it will accept things like "4" and "192.168" since, as mentioned, these are valid representations of IP addresses.
If you're using Python 3.3 or later, it now includes the ipaddress module:
>>> import ipaddress
>>> ipaddress.ip_address('127.0.0.1')
IPv4Address('127.0.0.1')
>>> ipaddress.ip_address('277.0.0.1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.3/ipaddress.py", line 54, in ip_address
address)
ValueError: '277.0.0.1' does not appear to be an IPv4 or IPv6 address
>>> ipaddress.ip_address('foobar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.3/ipaddress.py", line 54, in ip_address
address)
ValueError: 'foobar' does not appear to be an IPv4 or IPv6 address
For Python 2, you can get the same functionality using ipaddress if you install python-ipaddress:
pip install ipaddress
This module is compatible with Python 2 and provides a very similar API to that of the ipaddress module included in the Python Standard Library since Python 3.3. More details here. In Python 2 you will need to explicitly convert the IP address string to unicode: ipaddress.ip_address(u'127.0.0.1').
5 Comments
import ipaddress, when I passed an IPv4 address, I got output like IPv4Address('127.0.0.1'). But when I tried to convert it to string to check if it contains IPv4 or IPv6, I just got the IP. How can I know in code if it is a IPv4 or IPv6? Is if "IPv4" in str(type(val)): a good idea?ipaddress.AddressValueError: '127.0.0.1' does not appear to be a n IPv4 or IPv6 address. Did you pass in a bytes (str in Python 2) instead of a unicode object?. The help presented in the error message helped me fix the issue. You need u'...' in Python 2.try: return bool(ipaddress.ip_address(ip)) except ValueError: return Falsedef is_valid_ip(ip):
"""Validates IP addresses.
"""
return is_valid_ipv4(ip) or is_valid_ipv6(ip)
IPv4:
def is_valid_ipv4(ip):
"""Validates IPv4 addresses.
"""
pattern = re.compile(r"""
^
(?:
# Dotted variants:
(?:
# Decimal 1-255 (no leading 0's)
[3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2}
|
0x0*[0-9a-f]{1,2} # Hexadecimal 0x0 - 0xFF (possible leading 0's)
|
0+[1-3]?[0-7]{0,2} # Octal 0 - 0377 (possible leading 0's)
)
(?: # Repeat 0-3 times, separated by a dot
\.
(?:
[3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2}
|
0x0*[0-9a-f]{1,2}
|
0+[1-3]?[0-7]{0,2}
)
){0,3}
|
0x0*[0-9a-f]{1,8} # Hexadecimal notation, 0x0 - 0xffffffff
|
0+[0-3]?[0-7]{0,10} # Octal notation, 0 - 037777777777
|
# Decimal notation, 1-4294967295:
429496729[0-5]|42949672[0-8]\d|4294967[01]\d\d|429496[0-6]\d{3}|
42949[0-5]\d{4}|4294[0-8]\d{5}|429[0-3]\d{6}|42[0-8]\d{7}|
4[01]\d{8}|[1-3]\d{0,9}|[4-9]\d{0,8}
)
$
""", re.VERBOSE | re.IGNORECASE)
return pattern.match(ip) is not None
IPv6:
def is_valid_ipv6(ip):
"""Validates IPv6 addresses.
"""
pattern = re.compile(r"""
^
\s* # Leading whitespace
(?!.*::.*::) # Only a single whildcard allowed
(?:(?!:)|:(?=:)) # Colon iff it would be part of a wildcard
(?: # Repeat 6 times:
[0-9a-f]{0,4} # A group of at most four hexadecimal digits
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
){6} #
(?: # Either
[0-9a-f]{0,4} # Another group
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
[0-9a-f]{0,4} # Last group
(?: (?<=::) # Colon iff preceeded by exacly one colon
| (?<!:) #
| (?<=:) (?<!::) : #
) # OR
| # A v4 address with NO leading zeros
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
(?: \.
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
){3}
)
\s* # Trailing whitespace
$
""", re.VERBOSE | re.IGNORECASE | re.DOTALL)
return pattern.match(ip) is not None
The IPv6 version uses "(?:(?<=::)|(?<!::):)", which could be replaced with "(?(?<!::):)" on regex engines that support conditionals with look-arounds. (i.e. PCRE, .NET)
Edit:
- Dropped the native variant.
- Expanded the regex to comply with the RFC.
- Added another regex for IPv6 addresses.
Edit2:
I found some links discussing how to parse IPv6 addresses with regex:
- A Regular Expression for IPv6 Addresses - InterMapper Forums
- Working IPv6 regular expression - Patrick’s playground blog
- test-ipv6-regex.pl - Perl script with tons of test-cases. It seems my regex fails on a few of those tests.
Edit3:
Finally managed to write a pattern that passes all tests, and that I am also happy with.
4 Comments
[2001:4860:4860::8888]:80I hope it's simple and pythonic enough:
def is_valid_ip(ip):
m = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip)
return bool(m) and all(map(lambda n: 0 <= int(n) <= 255, m.groups()))
3 Comments
I think this would do it...
def validIP(address):
parts = address.split(".")
if len(parts) != 4:
return False
for item in parts:
if not 0 <= int(item) <= 255:
return False
return True
5 Comments
192.168.178.0030 would be valid.I have to give a great deal of credit to Markus Jarderot for his post - the majority of my post is inspired from his.
I found that Markus' answer still fails some of the IPv6 examples in the Perl script referenced by his answer.
Here is my regex that passes all of the examples in that Perl script:
r"""^
\s* # Leading whitespace
# Zero-width lookaheads to reject too many quartets
(?:
# 6 quartets, ending IPv4 address; no wildcards
(?:[0-9a-f]{1,4}(?::(?!:))){6}
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 0-5 quartets, wildcard, ending IPv4 address
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})?
(?:::(?!:))
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 0-4 quartets, wildcard, 0-1 quartets, ending IPv4 address
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})?
(?:::(?!:))
(?:[0-9a-f]{1,4}(?::(?!:)))?
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 0-3 quartets, wildcard, 0-2 quartets, ending IPv4 address
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})?
(?:::(?!:))
(?:[0-9a-f]{1,4}(?::(?!:))){0,2}
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 0-2 quartets, wildcard, 0-3 quartets, ending IPv4 address
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})?
(?:::(?!:))
(?:[0-9a-f]{1,4}(?::(?!:))){0,3}
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 0-1 quartets, wildcard, 0-4 quartets, ending IPv4 address
(?:[0-9a-f]{1,4}){0,1}
(?:::(?!:))
(?:[0-9a-f]{1,4}(?::(?!:))){0,4}
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# wildcard, 0-5 quartets, ending IPv4 address
(?:::(?!:))
(?:[0-9a-f]{1,4}(?::(?!:))){0,5}
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)
(?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}
|
# 8 quartets; no wildcards
(?:[0-9a-f]{1,4}(?::(?!:))){7}[0-9a-f]{1,4}
|
# 0-7 quartets, wildcard
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,6}[0-9a-f]{1,4})?
(?:::(?!:))
|
# 0-6 quartets, wildcard, 0-1 quartets
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,5}[0-9a-f]{1,4})?
(?:::(?!:))
(?:[0-9a-f]{1,4})?
|
# 0-5 quartets, wildcard, 0-2 quartets
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})?
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})?
|
# 0-4 quartets, wildcard, 0-3 quartets
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})?
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})?
|
# 0-3 quartets, wildcard, 0-4 quartets
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})?
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})?
|
# 0-2 quartets, wildcard, 0-5 quartets
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})?
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})?
|
# 0-1 quartets, wildcard, 0-6 quartets
(?:[0-9a-f]{1,4})?
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,5}[0-9a-f]{1,4})?
|
# wildcard, 0-7 quartets
(?:::(?!:))
(?:(?:[0-9a-f]{1,4}(?::(?!:))){0,6}[0-9a-f]{1,4})?
)
(?:/(?:1(?:2[0-7]|[01]\d)|\d\d?))? # With an optional CIDR routing prefix (0-128)
\s* # Trailing whitespace
$"""
I also put together a Python script to test all of those IPv6 examples; it's here on Pastebin because it was too large to post here.
You can run the script with test result and example arguments in the form of "[result]=[example]", so like:
python script.py Fail=::1.2.3.4: pass=::127.0.0.1 false=::: True=::1
or you can simply run all of the tests by specifying no arguments, so like:
python script.py
Anyway, I hope this helps somebody else!
3 Comments
ipaddress.IPv6Address(my_addr) also matches every test case result you posted so if you have Python 3.3+ you can use that... note that the corresponding IPv4 function was not so great and failed a number of (different) test cases involving leading zeros...I came up with this simple version
def ip_checkv4(ip):
parts=ip.split(".")
if len(parts)<4 or len(parts)>4:
return "invalid IP length should be 4 not greater or less than 4"
else:
while len(parts)== 4:
a=int(parts[0])
b=int(parts[1])
c=int(parts[2])
d=int(parts[3])
if a<= 0 or a == 127 :
return "invalid IP address"
elif d == 0:
return "host id should not be 0 or less than zero "
elif a>=255:
return "should not be 255 or greater than 255 or less than 0 A"
elif b>=255 or b<0:
return "should not be 255 or greater than 255 or less than 0 B"
elif c>=255 or c<0:
return "should not be 255 or greater than 255 or less than 0 C"
elif d>=255 or c<0:
return "should not be 255 or greater than 255 or less than 0 D"
else:
return "Valid IP address ", ip
p=raw_input("Enter IP address")
print ip_checkv4(p)
Comments
I only needed to parse IP v4 addresses. My solution based on Chills strategy follows:
def getIP():
valid = False
while not valid :
octets = raw_input( "Remote Machine IP Address:" ).strip().split(".")
try: valid=len( filter( lambda(item):0<=int(item)<256, octets) ) == 4
except: valid = False
return ".".join( octets )
import ipaddress; ipaddress.ipaddress(your_input_text)and catch theValueError. It's in the stdlib.