185

What's the best way to validate that an IP entered by the user is valid? It comes in as a string.

chills42
  • 14,201
  • 3
  • 42
  • 77
krupan
  • 3,920
  • 5
  • 27
  • 24
  • I just want to point out that if a broadcast address is not considered a valid address, then every single one of the solutions suggested so far fail. You have to test against a subnet mask to see if it is a broadcast address. – Bitt Oct 10 '14 at 10:12
  • `import ipaddress; ipaddress.ipaddress(your_input_text)` and catch the `ValueError`. It's in the stdlib. – Boris Verkhovskiy Nov 11 '19 at 19:33

11 Answers11

195

Don't parse it. Just ask.

import socket

try:
    socket.inet_aton(addr)
    # legal
except socket.error:
    # Not legal
Dustin
  • 89,080
  • 21
  • 111
  • 133
  • 23
    Hmm, seems to accept things like "4" and "192.168" and silently pads the rest with zeros. Technically valid, I'm sure, but not quite what I expected. – krupan Nov 25 '08 at 23:58
  • 1
    Those are valid representations of IP addresses. 127.1 is localhost, 1172703390 is my web server. If you want to ensure it's in dotted quad, you can also verify that len(addr.split('.')) == 4 – Dustin Nov 26 '08 at 00:05
  • 1
    @krupan: you could combine the above with a test for "len(addr.split(".")) == 4" if you would like to reject shorter addresses. – Greg Hewgill Nov 26 '08 at 00:13
  • 6
    No, does not work with all legal IP addresses: >>> socket.inet_aton("2001:660::1") Traceback (most recent call last): File "", line 1, in socket.error: illegal IP address string passed to inet_aton – bortzmeyer Dec 01 '08 at 10:53
  • 26
    @bortzmeyer: socket.inet_pton(socket_family, address) is what you want if you want to support ip6. You still need to specify the family. inet_aton specifically does not support anything but ip4. – richo May 26 '10 at 05:15
  • 9
    It looks like Richo's response solves krupan and bortzmeyer's questions. Use socket.inet_pton with socket.AF_INET or socket.AF_INET6 as family to validate ipv4 and ipv6 without incomplete addresses being accepted. – freb Nov 23 '11 at 01:26
  • Now, there is an official way to manipulate IPv4 and Ipv6 address in python [PEP 3144](http://www.python.org/dev/peps/pep-3144/). See my answer above. – Yohann May 28 '12 at 10:41
  • 9
    inet_aton() isn't accepting "invalid" IPs when it accepts "4", "192.168" and "127.1", it is merely using the underlying C behaviour - see the documentation. 127.1 puts 127 in the top octet and parses the 1 as a 24-bit number that it splits across the remaining three octets. The idea is to support /16 ranges that increment IPs, so you can go 172.16.1...172.16.255 and then 172.16.256, rather than having alter your maths to go to 172.16.1.0. – IBBoard Dec 07 '12 at 15:32
  • 1
    @richo - `inet_pton` is only available on some variations of Unix, and it's not available on Windows at all. – ArtOfWarfare Jun 22 '15 at 18:07
  • This doesn't allow to check IP against RFC790. – Jury Feb 17 '16 at 15:19
  • 1
    It is worth noting that there are security concerns with the socket module, which utilises the glibc inet_aton() function which "accepts trailing garbage for historical reasons", as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=1347549. Red Had Product Security has rated this issue as having medium security impact and, as such, it's not likely to be addressed any time soon. Given this, I'd contend that a good regex is the the best tool for this. – aitch-hat Apr 04 '17 at 16:17
  • Why is `4` or `192.168` a valid IP address? – Martin Thoma Mar 26 '20 at 15:37
  • @MartinThoma from the [inet](https://man7.org/linux/man-pages/man3/inet.3.html) man page, "The value a is interpreted as a 32-bit value that is stored directly into the binary address without any byte rearrangement" – Brad Solomon Nov 01 '20 at 16:26
  • you can use iptools. – shobhit_mittal Jul 30 '21 at 08:12
103

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

Yohann
  • 6,047
  • 3
  • 26
  • 33
  • I'm getting this error `C:\Python\Codes>check_ip.py File "C:\Python\Codes\check_ip.py", line 8 print '%s is a correct IP%s address.' % (ip, ip.version) ^ SyntaxError: invalid syntax C:\Python\Codes>` –  Mar 10 '18 at 03:49
  • 2
    Thanks @Yohann. For Python 3.5, parenthesis is required on print. Else, the code will produce an error. Since this space is limited, I'll update the code on answer section below. Hope this will helps others too. –  Mar 10 '18 at 05:45
  • This will return incorrect response for `test.example.com`. I got `IPv6Address(u'7465:7374:2e65:7861:6d70:6c65:2e63:6f6d')` – SharkIng Jun 22 '20 at 21:02
  • Sorry, newb here. What role does `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](https://www.geeksforgeeks.org/how-to-use-sys-argv-in-python/), seems like they are just test params. – ericOnline Feb 17 '21 at 19:48
81
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
Danilo Bargen
  • 18,626
  • 15
  • 91
  • 127
tzot
  • 92,761
  • 29
  • 141
  • 204
  • 1
    Why the line: "return address.count('.') == 3" ?? Is that left over from your debugging? – quux Dec 15 '10 at 15:32
  • 21
    @quux: no. It's a long discussion, and people don't like the fact that at least on Linux and Windows shortened addresses are considered acceptable. For example, `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. – tzot Dec 15 '10 at 19:44
  • 2
    How about on windows? – towry Apr 06 '13 at 04:36
  • Note that this is a unix-only answer – cowlinator Feb 23 '18 at 20:40
  • 2
    @cowlinator `inet_pton` exists only in Unix, and `inet_aton` exists in all platforms, so this is a “unix-mostly” answer. – tzot Feb 26 '18 at 10:11
68

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').

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
Samat Jain
  • 15,758
  • 4
  • 20
  • 13
  • Excellent idea. The only solution presented until now which works with all IP addresses. >>> from IPy import IP >>> IP("2001:660::1") IP('2001:660::1') – bortzmeyer Dec 01 '08 at 10:56
  • For python 2, pip install ipaddress and you get almost the same API :) – radtek Oct 07 '15 at 16:11
  • Regarding `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? – Aakash Goyal Jun 15 '16 at 07:39
  • Use the exact same steps and got `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. – rbaleksandar Mar 05 '18 at 09:38
  • 1
    The solution is as simple as this: `try: return bool(ipaddress.ip_address(ip)) except ValueError: return False` – Hett Dec 25 '19 at 15:14
46
def 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:

Edit3:

Finally managed to write a pattern that passes all tests, and that I am also happy with.

Community
  • 1
  • 1
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
18

I 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()))
Grzegorz Luczywo
  • 9,962
  • 1
  • 33
  • 22
  • you are repiting the same pattern 3 times... – warfares Mar 04 '15 at 23:33
  • 8
    ;-) ^(\d{1,3}\.){3}\d{1,3}$ – warfares Mar 04 '15 at 23:39
  • 2
    @warfaresthat is for a reason, it needs the groups separated in order to check if the values are in the range 0 and 255, that's what the second part of the return is doing, if you want to use your regex use return bool(m) only. – alsotoes May 29 '19 at 23:11
11

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
chills42
  • 14,201
  • 3
  • 42
  • 77
8

Consider IPv4 address as "ip".

if re.match(r'^((\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])$', ip):  
    print("Valid IP")  
else:
    print("Invalid IP")
Rajesh
  • 179
  • 2
  • 3
6

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!

blag
  • 119
  • 2
  • 6
  • 4
    Although I admire your effort as well, I think there's a huge design flaw in your construct: It's way too big! I would never trust a regex of this size that was not used by thousands of people for years. – erikbstack Apr 16 '14 at 12:05
  • 1
    @erikb85: Take a look at the script I posted to Pastebin. It contains 1154 tests of different IPv6 formats, and it passes **every single one** of them. If you think more tests are necessary, feel free to modify my script, add tests, and post the results. – blag May 24 '14 at 09:36
  • I found out that simply doing `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... – user9645 Nov 29 '22 at 18:16
3

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)
def_0101
  • 783
  • 1
  • 7
  • 9
0

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 )
hsar
  • 1