Repository: sabnzbd/sabnzbd (Python)

Issue:
RFC: probablyipv4() and probablyipv6() to use module "ipaddress" instead of regexp?
In the past I have introduced probablyipv4() and probablyipv6(). See https://github.com/sabnzbd/sabnzbd/blob/develop/sabnzbd/misc.py#L845-L858

Inside those function: a regexp which probably works. Therefore the function name.

I could rewrite the functions to depend on the python module `ipaddress`, which can recognize addresses:


>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.10').version
4
>>> ipaddress.ip_address('2001:db8::').version
6
>>> ipaddress.ip_address('blabla').version
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/ipaddress.py", line 53, in ip_address
    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
ValueError: 'blabla' does not appear to be an IPv4 or IPv6 address
>>>

That would mean:
- extra module to import
- get rid of the regexps
- "probably" turns into "100% sure"

Comments? Useful? Worth it? @Safihre @jcfp 




Discussion:
Go ahead, it's already part of the standard library so no problem for extra import.
Write some unit tests so we can be sure it works for us. 
Seems to come with nice methods for determining if an address is loopback, local, multicast, etc. Probably makes having a LOCALHOSTS definition in sabnzbd.constants obsolete as well.
> Seems to come with nice methods for determining if an address is loopback, local, multicast, etc. Probably makes having a LOCALHOSTS definition in sabnzbd.constants obsolete as well.




```
>>> ipaddress.ip_address('127.0.0.1').is_loopback
True
>>> ipaddress.ip_address('::1').is_loopback
True
>>> ipaddress.ip_address('localhost').is_loopback
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/ipaddress.py", line 53, in ip_address
    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
ValueError: 'localhost' does not appear to be an IPv4 or IPv6 address
>>> 
```





Patch:
diff --git a/sabnzbd/misc.py b/sabnzbd/misc.py
index dd4b2cf55e3..10773af6d3b 100644
--- a/sabnzbd/misc.py
+++ b/sabnzbd/misc.py
@@ -30,6 +30,7 @@
 import datetime
 import inspect
 import ctypes
+import ipaddress
 from typing import Union, Tuple, Any, Optional
 
 import sabnzbd
@@ -843,19 +844,26 @@ def find_on_path(targets):
 
 
 def probablyipv4(ip):
-    if ip.count(".") == 3 and re.sub("[0123456789.]", "", ip) == "":
-        return True
-    else:
+    try:
+        return ipaddress.ip_address(ip).version == 4
+    except:
         return False
 
 
 def probablyipv6(ip):
     # Returns True if the given input is probably an IPv6 address
     # Square Brackets like '[2001::1]' are OK
-    if ip.count(":") >= 2 and re.sub(r"[0123456789abcdefABCDEF:\[\]]", "", ip) == "":
-        return True
-    else:
-        return False
+    try:
+        # Check for plain IPv6 address
+        return ipaddress.ip_address(ip).version == 6
+    except:
+        try:
+            # Remove '[' and ']' and test again:
+            ip = re.search(r"^\[(.*)\]$", ip).group(1)
+            return ipaddress.ip_address(ip).version == 6
+        except:
+            # No, not an IPv6 address
+            return False
 
 
 def ip_extract():


Test patch:
diff --git a/tests/test_probablyip.py b/tests/test_probablyip.py
new file mode 100644
index 00000000000..e5bb6deda83
--- /dev/null
+++ b/tests/test_probablyip.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python3 -OO
+# Copyright 2007-2020 The SABnzbd-Team <team@sabnzbd.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+tests.test_utils.test_probablyip - Testing SABnzbd's probablyipX() functions
+"""
+
+from sabnzbd.misc import *
+
+
+class TestProbablyIP:
+    def test_probablyipv4(self):
+        # Positive testing
+        assert probablyipv4("1.2.3.4")
+        assert probablyipv4("255.255.255.255")
+        assert probablyipv4("0.0.0.0")
+        # Negative testing
+        assert not probablyipv4("400.500.600.700")
+        assert not probablyipv4("blabla")
+        assert not probablyipv4("2001::1")
+
+    def test_probablyipv6(self):
+        # Positive testing
+        assert probablyipv6("2001::1")
+        assert probablyipv6("[2001::1]")
+        assert probablyipv6("fdd6:5a2d:3f20:0:14b0:d8f4:ccb9:fab6")
+        # Negative testing
+        assert not probablyipv6("blabla")
+        assert not probablyipv6("1.2.3.4")
+        assert not probablyipv6("[1.2.3.4]")
+        assert not probablyipv6("2001:1")
+        assert not probablyipv6("2001::[2001::1]")