diff --git a/README.md b/README.md
index 3e918e7..7f226ee 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ SSRFProtect.validate(url)  # Raise SSRFProtectException
 requests.get(url)  # request is not processed
 ``` 
 
-### Whitelisted ip addresses
+### Whitelisted IP addresses
 ```python
 import requests
 from ssrf_protect.ssrf_protect import SSRFProtect
@@ -41,7 +41,7 @@ SSRFProtect.validate(url, options=options)
 requests.get(url) # request is processed
 ``` 
 
-### Blacklisted ip addresses
+### Blacklisted IP addresses
 ```python
 import requests
 from ssrf_protect.ssrf_protect import SSRFProtect
diff --git a/setup.py b/setup.py
index 75a87e4..b5762dc 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ dep_links = [
 
 
 setup(name='ssrf-protect',
-      version='0.1',
+      version='0.2',
       description='Basic library to validate URLs again SSRF attacks',
       author='the ssrf-protect contributors (https://github.com/kobotoolbox/ssrf-protect/graphs/contributors)',
       url='https://github.com/kobotoolbox/ssrf-protect/',
diff --git a/src/ssrf_protect/exceptions.py b/src/ssrf_protect/exceptions.py
index 524520a..5b565bc 100644
--- a/src/ssrf_protect/exceptions.py
+++ b/src/ssrf_protect/exceptions.py
@@ -4,5 +4,5 @@ from __future__ import absolute_import, unicode_literals
 
 class SSRFProtectException(Exception):
     """
-    Basic exception to be raised when ip address is not allowed.
+    Basic exception to be raised when a URL is not allowed.
     """
diff --git a/src/ssrf_protect/ssrf_protect.py b/src/ssrf_protect/ssrf_protect.py
index fd98638..f2056ee 100644
--- a/src/ssrf_protect/ssrf_protect.py
+++ b/src/ssrf_protect/ssrf_protect.py
@@ -38,8 +38,18 @@ class SSRFProtect(object):
         try:
             return ip_address(text_type(url))
         except ValueError:
-            host = urlparse(url).hostname
-            return ip_address(text_type(socket.gethostbyname(host)))
+            try:
+                host = urlparse(url).hostname
+                return ip_address(text_type(socket.gethostbyname(host)))
+            except AttributeError:
+                # `urlparse` receives an invalid parameter
+                raise SSRFProtectException(
+                    'Invalid URL `{url}`'.format(url=url))
+            except (ValueError, TypeError, socket.gaierror):
+                # `TypeError`, `socket.gaierror: `socket.gethostbyname` receives an invalid parameter
+                # `ValueError`: `ip_address` receives an invalid parameter
+                raise SSRFProtectException(
+                    'Cannot resolve IP address for `{host}`'.format(host=host))
 
     @classmethod
     def validate(cls, url, options={}):
@@ -58,6 +68,12 @@ class SSRFProtect(object):
         """
 
         ip_address_ = cls._get_ip_address(url)
+        denied_ip_addresses = options.get('denied_ip_addresses', [])
+        if len(denied_ip_addresses) > 0 and \
+                text_type(ip_address_) in denied_ip_addresses:
+            raise SSRFProtectException('URL {url} is not allowed because it resolves '
+                                       'to a denied IP address'.format(url=url))
+
         allowed_ip_addresses = options.get('allowed_ip_addresses', [])
         if len(allowed_ip_addresses) > 0 and \
                 text_type(ip_address_) in allowed_ip_addresses:
@@ -67,10 +83,4 @@ class SSRFProtect(object):
             raise SSRFProtectException('URL {url} is not allowed because it resolves '
                                        'to a private IP address'.format(url=url))
 
-        denied_ip_addresses = options.get('denied_ip_addresses', [])
-        if len(denied_ip_addresses) > 0 and \
-                text_type(ip_address_) in denied_ip_addresses:
-            raise SSRFProtectException('URL {url} is not allowed because it resolves '
-                                       'to a denied ip address'.format(url=url))
-
         return

suggestion: have deny take precedence over allow
right now, it looks like the order of operations is:

1. is the ip on the allow list? allow.
1. is the ip private? deny.
1. is the ip on the deny list? deny.

i would suggest:

1. is the ip on the deny list? deny.
1. is the ip on the allow list? allow.
1. is the ip private? deny.
diff --git a/tests/test_ip_addresses.py b/tests/test_ip_addresses.py
index 5ebcaa5..d7d7e0e 100644
--- a/tests/test_ip_addresses.py
+++ b/tests/test_ip_addresses.py
@@ -27,6 +27,23 @@ def test_allowed_ips():
     }
     assert SSRFProtect.validate(url, options=options) is None
 
+
+def test_allowed_and_denied_ips():
+    # Because denial comes first, if an ip is allowed and denied
+    # an exception should be raised
+    url = 'http://127.0.0.1/test'
+    options = {
+        'denied_ip_addresses': ['127.0.0.1'],
+        'allowed_ip_addresses': ['127.0.0.1']
+    }
+
+    with pytest.raises(SSRFProtectException) as excinfo:
+        SSRFProtect.validate(url, options)
+
+    assert 'URL {url} is not allowed because it resolves to ' \
+           'a denied IP address'.format(url=url) == str(excinfo.value)
+
+
 @patch('ssrf_protect.ssrf_protect.SSRFProtect._get_ip_address',
        new=MockSSRFProtect._get_ip_address)
 def test_denied_ips():
@@ -39,4 +56,4 @@ def test_denied_ips():
         SSRFProtect.validate(url, options)
 
     assert 'URL {url} is not allowed because it resolves to ' \
-           'a denied ip address'.format(url=url) == str(excinfo.value)
+           'a denied IP address'.format(url=url) == str(excinfo.value)

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest mock
apt-get update apt-get install -y gcc
3.9
dev_requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
exceptiongroup==1.2.2
iniconfig==2.1.0
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
six==1.17.0
-e git+https://github.com/kobotoolbox/ssrf-protect.git@755efe16694273ce66060a51e04f973dc034ca4e#egg=ssrf_protect
tomli==2.2.1

name: ssrf-protect
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - six==1.17.0
      - tomli==2.2.1
prefix: /opt/conda/envs/ssrf-protect

tests/test_ip_addresses.py::test_allowed_and_denied_ips tests/test_ip_addresses.py::test_denied_ips
tests/test_ip_addresses.py::test_no_options tests/test_ip_addresses.py::test_allowed_ips
setup.py README.md src/ssrf_protect/exceptions.py src/ssrf_protect/ssrf_protect.py
setup.py README.md src/ssrf_protect/exceptions.py src/ssrf_protect/ssrf_protect.py