Repository: certbot/certbot

Issue:
Fully check for Nginx address equality
`"0.0.0.0"`, `"*"`, and `""` are equivalent to Nginx but not to the parser. This will manifest as a failure to correctly label server blocks as `ssl`, or anywhere else address equality is tested. This should probably be fixed in the Nginx `Addr` object when testing equality.


Discussion:
Assigning it to Joona or Erica, whoever gets there first :)

Resolving patch:
diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py
--- a/certbot-nginx/certbot_nginx/obj.py
+++ b/certbot-nginx/certbot_nginx/obj.py
@@ -29,10 +29,14 @@ class Addr(common.Addr):
     :param bool default: Whether the directive includes 'default_server'
 
     """
+    UNSPECIFIED_IPV4_ADDRESSES = ('', '*', '0.0.0.0')
+    CANONICAL_UNSPECIFIED_ADDRESS = UNSPECIFIED_IPV4_ADDRESSES[0]
+
     def __init__(self, host, port, ssl, default):
         super(Addr, self).__init__((host, port))
         self.ssl = ssl
         self.default = default
+        self.unspecified_address = host in self.UNSPECIFIED_IPV4_ADDRESSES
 
     @classmethod
     def fromstring(cls, str_addr):
@@ -96,6 +100,13 @@ def __repr__(self):
     def super_eq(self, other):
         """Check ip/port equality, with IPv6 support.
         """
+        # If both addresses got an unspecified address, then make sure the
+        # host representation in each match when doing the comparison.
+        if self.unspecified_address and other.unspecified_address:
+            return common.Addr((self.CANONICAL_UNSPECIFIED_ADDRESS,
+                                self.tup[1]), self.ipv6) == \
+                   common.Addr((other.CANONICAL_UNSPECIFIED_ADDRESS,
+                                other.tup[1]), other.ipv6)
         # Nginx plugin currently doesn't support IPv6 but this will
         # future-proof it
         return super(Addr, self).__eq__(other)


Test patch:
diff --git a/certbot-nginx/certbot_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py
--- a/certbot-nginx/certbot_nginx/tests/obj_test.py
+++ b/certbot-nginx/certbot_nginx/tests/obj_test.py
@@ -1,5 +1,6 @@
 """Test the helper objects in certbot_nginx.obj."""
 import unittest
+import itertools
 
 
 class AddrTest(unittest.TestCase):
@@ -72,6 +73,24 @@ def test_eq(self):
         self.assertNotEqual(self.addr1, self.addr2)
         self.assertFalse(self.addr1 == 3333)
 
+    def test_equivalent_any_addresses(self):
+        from certbot_nginx.obj import Addr
+        any_addresses = ("0.0.0.0:80 default_server ssl",
+                         "80 default_server ssl",
+                         "*:80 default_server ssl")
+        for first, second in itertools.combinations(any_addresses, 2):
+            self.assertEqual(Addr.fromstring(first), Addr.fromstring(second))
+
+        # Also, make sure ports are checked.
+        self.assertNotEqual(Addr.fromstring(any_addresses[0]),
+                            Addr.fromstring("0.0.0.0:443 default_server ssl"))
+
+        # And they aren't equivalent to a specified address.
+        for any_address in any_addresses:
+            self.assertNotEqual(
+                Addr.fromstring("192.168.1.2:80 default_server ssl"),
+                Addr.fromstring(any_address))
+
     def test_set_inclusion(self):
         from certbot_nginx.obj import Addr
         set_a = set([self.addr1, self.addr2])