Add a ``strict`` flag allowing users to restrict validation of IPv4 format

Add a ``strict`` flag allowing users to restrict validation to IP
addresses in presentation format (``a.b.c.d``) as opposed to address
format (``a.b.c.d``, ``a.b.c``, ``a.b``, ``a``).

https://github.com/netaddr/netaddr/issues/186
https://man7.org/linux/man-pages/man3/inet_pton.3.html
https://bugzilla.redhat.com/show_bug.cgi?id=1924436

Change-Id: I10fed16dad77ac17691a5d175c42b25916dc8bc4
Closes-Bug: #1914386
This commit is contained in:
Hervé Beraud 2021-02-03 10:50:50 +01:00
parent 870ab370c5
commit 3288539a0b
3 changed files with 75 additions and 3 deletions

View File

@ -24,6 +24,7 @@ import socket
from urllib import parse from urllib import parse
import netaddr import netaddr
from netaddr.core import INET_PTON
import netifaces import netifaces
from oslo_utils._i18n import _ from oslo_utils._i18n import _
@ -81,17 +82,43 @@ def parse_host_port(address, default_port=None):
return (host, None if port is None else int(port)) return (host, None if port is None else int(port))
def is_valid_ipv4(address): def is_valid_ipv4(address, strict=None):
"""Verify that address represents a valid IPv4 address. """Verify that address represents a valid IPv4 address.
:param address: Value to verify :param address: Value to verify
:type address: string :type address: string
:param strict: flag allowing users to restrict validation
to IP addresses in presentation format (``a.b.c.d``) as opposed to
address format (``a.b.c.d``, ``a.b.c``, ``a.b``, ``a``).
:type flags: bool
:returns: bool :returns: bool
.. versionadded:: 1.1 .. versionadded:: 1.1
.. versionchanged:: 4.8.0
Allow to restrict validation to IP addresses in presentation format
(``a.b.c.d``) as opposed to address format
(``a.b.c.d``, ``a.b.c``, ``a.b``, ``a``).
""" """
if strict is not None:
flag = INET_PTON if strict else 0
try:
return netaddr.valid_ipv4(address, flags=flag)
except netaddr.AddrFormatError:
return False
# non strict mode
try: try:
return netaddr.valid_ipv4(address) if netaddr.valid_ipv4(address, flags=INET_PTON):
return True
else:
if netaddr.valid_ipv4(address):
LOG.warn(
'Converting in non strict mode is deprecated. '
'You should pass strict=False if you want to '
'preserve legacy behavior')
return True
else:
return False
except netaddr.AddrFormatError: except netaddr.AddrFormatError:
return False return False

View File

@ -154,13 +154,52 @@ class NetworkUtilsTest(test_base.BaseTestCase):
netutils.set_tcp_keepalive(mock_sock, False) netutils.set_tcp_keepalive(mock_sock, False)
self.assertEqual(1, len(mock_sock.mock_calls)) self.assertEqual(1, len(mock_sock.mock_calls))
def test_is_valid_ipv4(self): @mock.patch.object(netutils, 'LOG', autospec=True)
def test_is_valid_ipv4(self, mock_log):
expected_log = 'Converting in non strict mode is deprecated. ' \
'You should pass strict=False if you want to preserve ' \
'legacy behavior'
self.assertTrue(netutils.is_valid_ipv4('42.42.42.42')) self.assertTrue(netutils.is_valid_ipv4('42.42.42.42'))
self.assertFalse(netutils.is_valid_ipv4('-1.11.11.11')) self.assertFalse(netutils.is_valid_ipv4('-1.11.11.11'))
self.assertFalse(netutils.is_valid_ipv4('')) self.assertFalse(netutils.is_valid_ipv4(''))
self.assertTrue(netutils.is_valid_ipv4('10'))
mock_log.warn.assert_called_with(expected_log)
mock_log.reset_mock()
self.assertTrue(netutils.is_valid_ipv4('10.10'))
mock_log.warn.assert_called_with(expected_log)
mock_log.reset_mock()
self.assertTrue(netutils.is_valid_ipv4('10.10.10'))
mock_log.warn.assert_called_with(expected_log)
mock_log.reset_mock()
self.assertTrue(netutils.is_valid_ipv4('10.10.10.10'))
mock_log.warn.assert_not_called()
mock_log.reset_mock()
self.assertFalse(
netutils.is_valid_ipv4('10', strict=True)
)
self.assertFalse(
netutils.is_valid_ipv4('10.10', strict=True)
)
self.assertFalse(
netutils.is_valid_ipv4('10.10.10', strict=True)
)
mock_log.warn.assert_not_called()
mock_log.reset_mock()
self.assertTrue(
netutils.is_valid_ipv4('10', strict=False)
)
self.assertTrue(
netutils.is_valid_ipv4('10.10', strict=False)
)
self.assertTrue(
netutils.is_valid_ipv4('10.10.10', strict=False)
)
mock_log.warn.assert_not_called()
mock_log.reset_mock()
def test_is_valid_ipv6(self): def test_is_valid_ipv6(self):
self.assertTrue(netutils.is_valid_ipv6('::1')) self.assertTrue(netutils.is_valid_ipv6('::1'))

View File

@ -0,0 +1,6 @@
---
features:
- |
Add a ``strict`` flag to ``netutils.is_valid_ipv4`` to allowing users to
restrict validation to IP addresses in presentation format (``a.b.c.d``)
as opposed to address format (``a.b.c.d``, ``a.b.c``, ``a.b``, ``a``).