From 6c3e066115d0000a13f2c0819fbf0dab5b9054e3 Mon Sep 17 00:00:00 2001
From: Moul <moul@moul.re>
Date: Mon, 14 Jun 2021 18:10:12 +0200
Subject: [PATCH] [fix] #170: endpoint: Fix ipv4/host attributes mix up

In case only an IPv4 is present, the latter ends up in server/host attribute
Since HOST_REGEX is using dots, the IPv4 is matched as a host

Only happening to BMA(S) and GVA since the other endpoints' regex are
matiching one among (host|ipv6|ipv4)

Use ipaddress as a trick after regex matching to
switch ipv4 and server/host attributes
---
 duniterpy/api/endpoint.py   | 24 +++++++++++++++++-------
 tests/api/test_endpoints.py | 12 ++++++++++++
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/duniterpy/api/endpoint.py b/duniterpy/api/endpoint.py
index 3ef14708..7d57eb0d 100644
--- a/duniterpy/api/endpoint.py
+++ b/duniterpy/api/endpoint.py
@@ -14,7 +14,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import re
-from typing import Any, Dict, Optional, Type, TypeVar
+from ipaddress import ip_address
+from typing import Any, Dict, Optional, Tuple, Type, TypeVar
 
 from duniterpy import constants as const
 
@@ -171,10 +172,10 @@ class BMAEndpoint(Endpoint):
         m = BMAEndpoint.re_inline.match(inline)
         if m is None:
             raise MalformedDocumentError(BMAEndpoint.API)
-        server = m["host"]
-        ipv4 = m["ipv4"]
+        server, ipv4 = fix_host_ipv4_mix_up(m["host"], m["ipv4"])
         ipv6 = m["ipv6"]
         port = int(m["port"])
+
         return cls(server, ipv4, ipv6, port)
 
     def inline(self) -> str:
@@ -265,11 +266,11 @@ class SecuredBMAEndpoint(BMAEndpoint):
         m = SecuredBMAEndpoint.re_inline.match(inline)
         if m is None:
             raise MalformedDocumentError(SecuredBMAEndpoint.API)
-        server = m["host"]
-        ipv4 = m["ipv4"]
+        server, ipv4 = fix_host_ipv4_mix_up(m["host"], m["ipv4"])
         ipv6 = m["ipv6"]
         port = int(m["port"])
         path = m["path"]
+
         if not path:
             path = ""
         return cls(server, ipv4, ipv6, port, path)
@@ -619,11 +620,11 @@ class GVAEndpoint(Endpoint):
         if m is None:
             raise MalformedDocumentError(cls.API)
         flags = m["flags"]
-        server = m["host"]
-        ipv4 = m["ipv4"]
+        server, ipv4 = fix_host_ipv4_mix_up(m["host"], m["ipv4"])
         ipv6 = m["ipv6"]
         port = int(m["port"])
         path = m["path"]
+
         if not flags:
             flags = ""
         if not path:
@@ -744,3 +745,12 @@ def endpoint(value: Any) -> Any:
         raise TypeError("Cannot convert {0} to endpoint".format(value))
 
     return result
+
+
+def fix_host_ipv4_mix_up(host: str, ipv4: str) -> Tuple[str, str]:
+    mixed_up = False
+    try:
+        mixed_up = ip_address(host).version == 4 and not ipv4
+    except ValueError:
+        pass
+    return ("", host) if mixed_up else (host, ipv4)
diff --git a/tests/api/test_endpoints.py b/tests/api/test_endpoints.py
index 75dc7a61..7198759e 100644
--- a/tests/api/test_endpoints.py
+++ b/tests/api/test_endpoints.py
@@ -87,3 +87,15 @@ class TestEndpoint(unittest.TestCase):
         self.assertEqual(gvasub_endpoint.path, "gva")
 
         assert gvasub_endpoint.inline(), endpoint_str
+
+    def test_gva_host_ipv4_mix_up(self):
+        endpoint_str = "GVA S 127.0.0.1 443 gva"
+        gva_endpoint = endpoint.GVAEndpoint.from_inline(endpoint_str)
+        self.assertEqual(gva_endpoint.server, "")
+        self.assertEqual(gva_endpoint.ipv4, "127.0.0.1")
+
+    def test_bmas_host_ipv4_mix_up(self):
+        endpoint_str = "BMAS 127.0.0.1 443 bma"
+        bmas_endpoint = endpoint.SecuredBMAEndpoint.from_inline(endpoint_str)
+        self.assertEqual(bmas_endpoint.server, "")
+        self.assertEqual(bmas_endpoint.ipv4, "127.0.0.1")
-- 
GitLab