We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[nginx] Cert for two domains in one virtaulhost fails
When trying to obtain or renew a cert for two domains which is listed in `server_name` in one virtualhost the operation fails.
### Fails

**00-default.conf**
`server_name   _ example.com sub.example.com;`

```
# ./certbot-auto renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.com.conf
-------------------------------------------------------------------------------
Cert not due for renewal, but simulating renewal for dry run
Starting new HTTPS connection (1): acme-staging.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for example.com
tls-sni-01 challenge for sub.example.com
Cleaning up challenges
Attempting to renew cert from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: nginx restart failed:

nginx: [emerg] a duplicate default server for 0.0.0.0:443 in /etc/letsencrypt/le_tls_sni_01_cert_challenge.conf:36
. Skipping.
```
### Works

**00-default.conf**
`server_name   _ example.com;`

**sub.conf**
`server_name   sub.example.com;`
### Info

```
# ./certbot-auto --version
letsencrypt 0.9.3
# cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `certbot-nginx/certbot_nginx/tls_sni_01.py`
Content:
```
1 """A class that performs TLS-SNI-01 challenges for Nginx"""
2 
3 import itertools
4 import logging
5 import os
6 
7 from certbot import errors
8 from certbot.plugins import common
9 
10 from certbot_nginx import obj
11 from certbot_nginx import nginxparser
12 
13 
14 logger = logging.getLogger(__name__)
15 
16 
17 class NginxTlsSni01(common.TLSSNI01):
18     """TLS-SNI-01 authenticator for Nginx
19 
20     :ivar configurator: NginxConfigurator object
21     :type configurator: :class:`~nginx.configurator.NginxConfigurator`
22 
23     :ivar list achalls: Annotated
24         class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
25         challenges
26 
27     :param list indices: Meant to hold indices of challenges in a
28         larger array. NginxTlsSni01 is capable of solving many challenges
29         at once which causes an indexing issue within NginxConfigurator
30         who must return all responses in order.  Imagine NginxConfigurator
31         maintaining state about where all of the http-01 Challenges,
32         TLS-SNI-01 Challenges belong in the response array.  This is an
33         optional utility.
34 
35     :param str challenge_conf: location of the challenge config file
36 
37     """
38 
39     def perform(self):
40         """Perform a challenge on Nginx.
41 
42         :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response`
43         :rtype: list
44 
45         """
46         if not self.achalls:
47             return []
48 
49         addresses = []
50         default_addr = "{0} default_server ssl".format(
51             self.configurator.config.tls_sni_01_port)
52 
53         for achall in self.achalls:
54             vhost = self.configurator.choose_vhost(achall.domain)
55             if vhost is None:
56                 logger.error(
57                     "No nginx vhost exists with server_name matching: %s. "
58                     "Please specify server_names in the Nginx config.",
59                     achall.domain)
60                 return None
61 
62             for addr in vhost.addrs:
63                 if addr.default:
64                     addresses.append([obj.Addr.fromstring(default_addr)])
65                     break
66             else:
67                 addresses.append(list(vhost.addrs))
68 
69         # Create challenge certs
70         responses = [self._setup_challenge_cert(x) for x in self.achalls]
71 
72         # Set up the configuration
73         self._mod_config(addresses)
74 
75         # Save reversible changes
76         self.configurator.save("SNI Challenge", True)
77 
78         return responses
79 
80     def _mod_config(self, ll_addrs):
81         """Modifies Nginx config to include challenge server blocks.
82 
83         :param list ll_addrs: list of lists of
84             :class:`certbot_nginx.obj.Addr` to apply
85 
86         :raises .MisconfigurationError:
87             Unable to find a suitable HTTP block in which to include
88             authenticator hosts.
89 
90         """
91         # Add the 'include' statement for the challenges if it doesn't exist
92         # already in the main config
93         included = False
94         include_directive = ['\n', 'include', ' ', self.challenge_conf]
95         root = self.configurator.parser.loc["root"]
96 
97         bucket_directive = ['\n', 'server_names_hash_bucket_size', ' ', '128']
98 
99         main = self.configurator.parser.parsed[root]
100         for key, body in main:
101             if key == ['http']:
102                 found_bucket = False
103                 for k, _ in body:
104                     if k == bucket_directive[1]:
105                         found_bucket = True
106                 if not found_bucket:
107                     body.insert(0, bucket_directive)
108                 if include_directive not in body:
109                     body.insert(0, include_directive)
110                 included = True
111                 break
112         if not included:
113             raise errors.MisconfigurationError(
114                 'LetsEncrypt could not find an HTTP block to include '
115                 'TLS-SNI-01 challenges in %s.' % root)
116 
117         config = [self._make_server_block(pair[0], pair[1])
118                   for pair in itertools.izip(self.achalls, ll_addrs)]
119         config = nginxparser.UnspacedList(config)
120 
121         self.configurator.reverter.register_file_creation(
122             True, self.challenge_conf)
123 
124         with open(self.challenge_conf, "w") as new_conf:
125             nginxparser.dump(config, new_conf)
126 
127     def _make_server_block(self, achall, addrs):
128         """Creates a server block for a challenge.
129 
130         :param achall: Annotated TLS-SNI-01 challenge
131         :type achall:
132             :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
133 
134         :param list addrs: addresses of challenged domain
135             :class:`list` of type :class:`~nginx.obj.Addr`
136 
137         :returns: server block for the challenge host
138         :rtype: list
139 
140         """
141         document_root = os.path.join(
142             self.configurator.config.work_dir, "tls_sni_01_page")
143 
144         block = [['listen', ' ', str(addr)] for addr in addrs]
145 
146         block.extend([['server_name', ' ',
147                        achall.response(achall.account_key).z_domain],
148                       # access and error logs necessary for
149                       # integration testing (non-root)
150                       ['access_log', ' ', os.path.join(
151                           self.configurator.config.work_dir, 'access.log')],
152                       ['error_log', ' ', os.path.join(
153                           self.configurator.config.work_dir, 'error.log')],
154                       ['ssl_certificate', ' ', self.get_cert_path(achall)],
155                       ['ssl_certificate_key', ' ', self.get_key_path(achall)],
156                       [['location', ' ', '/'], [['root', ' ', document_root]]]] +
157                      self.configurator.parser.loc["ssl_options"])
158 
159         return [['server'], block]
160
```

Path: `certbot-nginx/certbot_nginx/obj.py`
Content:
```
1 """Module contains classes used by the Nginx Configurator."""
2 import re
3 
4 from certbot.plugins import common
5 
6 
7 class Addr(common.Addr):
8     r"""Represents an Nginx address, i.e. what comes after the 'listen'
9     directive.
10 
11     According to the `documentation`_, this may be address[:port], port,
12     or unix:path. The latter is ignored here.
13 
14     The default value if no directive is specified is \*:80 (superuser)
15     or \*:8000 (otherwise). If no port is specified, the default is
16     80. If no address is specified, listen on all addresses.
17 
18     .. _documentation:
19        http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
20 
21     .. todo:: Old-style nginx configs define SSL vhosts in a separate
22               block instead of using 'ssl' in the listen directive.
23 
24     :param str addr: addr part of vhost address, may be hostname, IPv4, IPv6,
25         "", or "\*"
26     :param str port: port number or "\*" or ""
27     :param bool ssl: Whether the directive includes 'ssl'
28     :param bool default: Whether the directive includes 'default_server'
29 
30     """
31     def __init__(self, host, port, ssl, default):
32         super(Addr, self).__init__((host, port))
33         self.ssl = ssl
34         self.default = default
35 
36     @classmethod
37     def fromstring(cls, str_addr):
38         """Initialize Addr from string."""
39         parts = str_addr.split(' ')
40         ssl = False
41         default = False
42         host = ''
43         port = ''
44 
45         # The first part must be the address
46         addr = parts.pop(0)
47 
48         # Ignore UNIX-domain sockets
49         if addr.startswith('unix:'):
50             return None
51 
52         tup = addr.partition(':')
53         if re.match(r'^\d+$', tup[0]):
54             # This is a bare port, not a hostname. E.g. listen 80
55             host = ''
56             port = tup[0]
57         else:
58             # This is a host-port tuple. E.g. listen 127.0.0.1:*
59             host = tup[0]
60             port = tup[2]
61 
62         # The rest of the parts are options; we only care about ssl and default
63         while len(parts) > 0:
64             nextpart = parts.pop()
65             if nextpart == 'ssl':
66                 ssl = True
67             elif nextpart == 'default_server':
68                 default = True
69 
70         return cls(host, port, ssl, default)
71 
72     def __str__(self):
73         parts = ''
74         if self.tup[0] and self.tup[1]:
75             parts = "%s:%s" % self.tup
76         elif self.tup[0]:
77             parts = self.tup[0]
78         else:
79             parts = self.tup[1]
80 
81         if self.default:
82             parts += ' default_server'
83         if self.ssl:
84             parts += ' ssl'
85 
86         return parts
87 
88     def __repr__(self):
89         return "Addr(" + self.__str__() + ")"
90 
91     def __eq__(self, other):
92         if isinstance(other, self.__class__):
93             return (self.tup == other.tup and
94                     self.ssl == other.ssl and
95                     self.default == other.default)
96         return False
97 
98 
99 class VirtualHost(object):  # pylint: disable=too-few-public-methods
100     """Represents an Nginx Virtualhost.
101 
102     :ivar str filep: file path of VH
103     :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`)
104     :ivar set names: Server names/aliases of vhost
105         (:class:`list` of :class:`str`)
106     :ivar list raw: The raw form of the parsed server block
107 
108     :ivar bool ssl: SSLEngine on in vhost
109     :ivar bool enabled: Virtual host is enabled
110     :ivar list path: The indices into the parsed file used to access
111         the server block defining the vhost
112 
113     """
114 
115     def __init__(self, filep, addrs, ssl, enabled, names, raw, path):
116         # pylint: disable=too-many-arguments
117         """Initialize a VH."""
118         self.filep = filep
119         self.addrs = addrs
120         self.names = names
121         self.ssl = ssl
122         self.enabled = enabled
123         self.raw = raw
124         self.path = path
125 
126     def __str__(self):
127         addr_str = ", ".join(str(addr) for addr in self.addrs)
128         return ("file: %s\n"
129                 "addrs: %s\n"
130                 "names: %s\n"
131                 "ssl: %s\n"
132                 "enabled: %s" % (self.filep, addr_str,
133                                  self.names, self.ssl, self.enabled))
134 
135     def __repr__(self):
136         return "VirtualHost(" + self.__str__().replace("\n", ", ") + ")\n"
137 
138     def __eq__(self, other):
139         if isinstance(other, self.__class__):
140             return (self.filep == other.filep and
141                     list(self.addrs) == list(other.addrs) and
142                     self.names == other.names and
143                     self.ssl == other.ssl and
144                     self.enabled == other.enabled and
145                     self.path == other.path)
146 
147         return False
148
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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
@@ -69,7 +69,8 @@
 
         return cls(host, port, ssl, default)
 
-    def __str__(self):
+    def to_string(self, include_default=True):
+        """Return string representation of Addr"""
         parts = ''
         if self.tup[0] and self.tup[1]:
             parts = "%s:%s" % self.tup
@@ -78,13 +79,16 @@
         else:
             parts = self.tup[1]
 
-        if self.default:
+        if self.default and include_default:
             parts += ' default_server'
         if self.ssl:
             parts += ' ssl'
 
         return parts
 
+    def __str__(self):
+        return self.to_string()
+
     def __repr__(self):
         return "Addr(" + self.__str__() + ")"
 
diff --git a/certbot-nginx/certbot_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py
--- a/certbot-nginx/certbot_nginx/tls_sni_01.py
+++ b/certbot-nginx/certbot_nginx/tls_sni_01.py
@@ -47,7 +47,7 @@
             return []
 
         addresses = []
-        default_addr = "{0} default_server ssl".format(
+        default_addr = "{0} ssl".format(
             self.configurator.config.tls_sni_01_port)
 
         for achall in self.achalls:
@@ -59,12 +59,10 @@
                     achall.domain)
                 return None
 
-            for addr in vhost.addrs:
-                if addr.default:
-                    addresses.append([obj.Addr.fromstring(default_addr)])
-                    break
-            else:
+            if vhost.addrs:
                 addresses.append(list(vhost.addrs))
+            else:
+                addresses.append([obj.Addr.fromstring(default_addr)])
 
         # Create challenge certs
         responses = [self._setup_challenge_cert(x) for x in self.achalls]
@@ -141,7 +139,7 @@
         document_root = os.path.join(
             self.configurator.config.work_dir, "tls_sni_01_page")
 
-        block = [['listen', ' ', str(addr)] for addr in addrs]
+        block = [['listen', ' ', addr.to_string(include_default=False)] for addr in addrs]
 
         block.extend([['server_name', ' ',
                        achall.response(achall.account_key).z_domain],
@@ -155,5 +153,4 @@
                       ['ssl_certificate_key', ' ', self.get_key_path(achall)],
                       [['location', ' ', '/'], [['root', ' ', document_root]]]] +
                      self.configurator.parser.loc["ssl_options"])
-
         return [['server'], block]

{"golden_diff": "diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py\n--- a/certbot-nginx/certbot_nginx/obj.py\n+++ b/certbot-nginx/certbot_nginx/obj.py\n@@ -69,7 +69,8 @@\n \n         return cls(host, port, ssl, default)\n \n-    def __str__(self):\n+    def to_string(self, include_default=True):\n+        \"\"\"Return string representation of Addr\"\"\"\n         parts = ''\n         if self.tup[0] and self.tup[1]:\n             parts = \"%s:%s\" % self.tup\n@@ -78,13 +79,16 @@\n         else:\n             parts = self.tup[1]\n \n-        if self.default:\n+        if self.default and include_default:\n             parts += ' default_server'\n         if self.ssl:\n             parts += ' ssl'\n \n         return parts\n \n+    def __str__(self):\n+        return self.to_string()\n+\n     def __repr__(self):\n         return \"Addr(\" + self.__str__() + \")\"\n \ndiff --git a/certbot-nginx/certbot_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py\n--- a/certbot-nginx/certbot_nginx/tls_sni_01.py\n+++ b/certbot-nginx/certbot_nginx/tls_sni_01.py\n@@ -47,7 +47,7 @@\n             return []\n \n         addresses = []\n-        default_addr = \"{0} default_server ssl\".format(\n+        default_addr = \"{0} ssl\".format(\n             self.configurator.config.tls_sni_01_port)\n \n         for achall in self.achalls:\n@@ -59,12 +59,10 @@\n                     achall.domain)\n                 return None\n \n-            for addr in vhost.addrs:\n-                if addr.default:\n-                    addresses.append([obj.Addr.fromstring(default_addr)])\n-                    break\n-            else:\n+            if vhost.addrs:\n                 addresses.append(list(vhost.addrs))\n+            else:\n+                addresses.append([obj.Addr.fromstring(default_addr)])\n \n         # Create challenge certs\n         responses = [self._setup_challenge_cert(x) for x in self.achalls]\n@@ -141,7 +139,7 @@\n         document_root = os.path.join(\n             self.configurator.config.work_dir, \"tls_sni_01_page\")\n \n-        block = [['listen', ' ', str(addr)] for addr in addrs]\n+        block = [['listen', ' ', addr.to_string(include_default=False)] for addr in addrs]\n \n         block.extend([['server_name', ' ',\n                        achall.response(achall.account_key).z_domain],\n@@ -155,5 +153,4 @@\n                       ['ssl_certificate_key', ' ', self.get_key_path(achall)],\n                       [['location', ' ', '/'], [['root', ' ', document_root]]]] +\n                      self.configurator.parser.loc[\"ssl_options\"])\n-\n         return [['server'], block]\n", "issue": "[nginx] Cert for two domains in one virtaulhost fails\nWhen trying to obtain or renew a cert for two domains which is listed in `server_name` in one virtualhost the operation fails.\n### Fails\n\n**00-default.conf**\n`server_name   _ example.com sub.example.com;`\n\n```\n# ./certbot-auto renew --dry-run\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\n\n-------------------------------------------------------------------------------\nProcessing /etc/letsencrypt/renewal/example.com.conf\n-------------------------------------------------------------------------------\nCert not due for renewal, but simulating renewal for dry run\nStarting new HTTPS connection (1): acme-staging.api.letsencrypt.org\nRenewing an existing certificate\nPerforming the following challenges:\ntls-sni-01 challenge for example.com\ntls-sni-01 challenge for sub.example.com\nCleaning up challenges\nAttempting to renew cert from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: nginx restart failed:\n\nnginx: [emerg] a duplicate default server for 0.0.0.0:443 in /etc/letsencrypt/le_tls_sni_01_cert_challenge.conf:36\n. Skipping.\n```\n### Works\n\n**00-default.conf**\n`server_name   _ example.com;`\n\n**sub.conf**\n`server_name   sub.example.com;`\n### Info\n\n```\n# ./certbot-auto --version\nletsencrypt 0.9.3\n# cat /etc/redhat-release\nCentOS Linux release 7.2.1511 (Core)\n```\n\n", "before_files": [{"content": "\"\"\"A class that performs TLS-SNI-01 challenges for Nginx\"\"\"\n\nimport itertools\nimport logging\nimport os\n\nfrom certbot import errors\nfrom certbot.plugins import common\n\nfrom certbot_nginx import obj\nfrom certbot_nginx import nginxparser\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NginxTlsSni01(common.TLSSNI01):\n    \"\"\"TLS-SNI-01 authenticator for Nginx\n\n    :ivar configurator: NginxConfigurator object\n    :type configurator: :class:`~nginx.configurator.NginxConfigurator`\n\n    :ivar list achalls: Annotated\n        class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge`\n        challenges\n\n    :param list indices: Meant to hold indices of challenges in a\n        larger array. NginxTlsSni01 is capable of solving many challenges\n        at once which causes an indexing issue within NginxConfigurator\n        who must return all responses in order.  Imagine NginxConfigurator\n        maintaining state about where all of the http-01 Challenges,\n        TLS-SNI-01 Challenges belong in the response array.  This is an\n        optional utility.\n\n    :param str challenge_conf: location of the challenge config file\n\n    \"\"\"\n\n    def perform(self):\n        \"\"\"Perform a challenge on Nginx.\n\n        :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response`\n        :rtype: list\n\n        \"\"\"\n        if not self.achalls:\n            return []\n\n        addresses = []\n        default_addr = \"{0} default_server ssl\".format(\n            self.configurator.config.tls_sni_01_port)\n\n        for achall in self.achalls:\n            vhost = self.configurator.choose_vhost(achall.domain)\n            if vhost is None:\n                logger.error(\n                    \"No nginx vhost exists with server_name matching: %s. \"\n                    \"Please specify server_names in the Nginx config.\",\n                    achall.domain)\n                return None\n\n            for addr in vhost.addrs:\n                if addr.default:\n                    addresses.append([obj.Addr.fromstring(default_addr)])\n                    break\n            else:\n                addresses.append(list(vhost.addrs))\n\n        # Create challenge certs\n        responses = [self._setup_challenge_cert(x) for x in self.achalls]\n\n        # Set up the configuration\n        self._mod_config(addresses)\n\n        # Save reversible changes\n        self.configurator.save(\"SNI Challenge\", True)\n\n        return responses\n\n    def _mod_config(self, ll_addrs):\n        \"\"\"Modifies Nginx config to include challenge server blocks.\n\n        :param list ll_addrs: list of lists of\n            :class:`certbot_nginx.obj.Addr` to apply\n\n        :raises .MisconfigurationError:\n            Unable to find a suitable HTTP block in which to include\n            authenticator hosts.\n\n        \"\"\"\n        # Add the 'include' statement for the challenges if it doesn't exist\n        # already in the main config\n        included = False\n        include_directive = ['\\n', 'include', ' ', self.challenge_conf]\n        root = self.configurator.parser.loc[\"root\"]\n\n        bucket_directive = ['\\n', 'server_names_hash_bucket_size', ' ', '128']\n\n        main = self.configurator.parser.parsed[root]\n        for key, body in main:\n            if key == ['http']:\n                found_bucket = False\n                for k, _ in body:\n                    if k == bucket_directive[1]:\n                        found_bucket = True\n                if not found_bucket:\n                    body.insert(0, bucket_directive)\n                if include_directive not in body:\n                    body.insert(0, include_directive)\n                included = True\n                break\n        if not included:\n            raise errors.MisconfigurationError(\n                'LetsEncrypt could not find an HTTP block to include '\n                'TLS-SNI-01 challenges in %s.' % root)\n\n        config = [self._make_server_block(pair[0], pair[1])\n                  for pair in itertools.izip(self.achalls, ll_addrs)]\n        config = nginxparser.UnspacedList(config)\n\n        self.configurator.reverter.register_file_creation(\n            True, self.challenge_conf)\n\n        with open(self.challenge_conf, \"w\") as new_conf:\n            nginxparser.dump(config, new_conf)\n\n    def _make_server_block(self, achall, addrs):\n        \"\"\"Creates a server block for a challenge.\n\n        :param achall: Annotated TLS-SNI-01 challenge\n        :type achall:\n            :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`\n\n        :param list addrs: addresses of challenged domain\n            :class:`list` of type :class:`~nginx.obj.Addr`\n\n        :returns: server block for the challenge host\n        :rtype: list\n\n        \"\"\"\n        document_root = os.path.join(\n            self.configurator.config.work_dir, \"tls_sni_01_page\")\n\n        block = [['listen', ' ', str(addr)] for addr in addrs]\n\n        block.extend([['server_name', ' ',\n                       achall.response(achall.account_key).z_domain],\n                      # access and error logs necessary for\n                      # integration testing (non-root)\n                      ['access_log', ' ', os.path.join(\n                          self.configurator.config.work_dir, 'access.log')],\n                      ['error_log', ' ', os.path.join(\n                          self.configurator.config.work_dir, 'error.log')],\n                      ['ssl_certificate', ' ', self.get_cert_path(achall)],\n                      ['ssl_certificate_key', ' ', self.get_key_path(achall)],\n                      [['location', ' ', '/'], [['root', ' ', document_root]]]] +\n                     self.configurator.parser.loc[\"ssl_options\"])\n\n        return [['server'], block]\n", "path": "certbot-nginx/certbot_nginx/tls_sni_01.py"}, {"content": "\"\"\"Module contains classes used by the Nginx Configurator.\"\"\"\nimport re\n\nfrom certbot.plugins import common\n\n\nclass Addr(common.Addr):\n    r\"\"\"Represents an Nginx address, i.e. what comes after the 'listen'\n    directive.\n\n    According to the `documentation`_, this may be address[:port], port,\n    or unix:path. The latter is ignored here.\n\n    The default value if no directive is specified is \\*:80 (superuser)\n    or \\*:8000 (otherwise). If no port is specified, the default is\n    80. If no address is specified, listen on all addresses.\n\n    .. _documentation:\n       http://nginx.org/en/docs/http/ngx_http_core_module.html#listen\n\n    .. todo:: Old-style nginx configs define SSL vhosts in a separate\n              block instead of using 'ssl' in the listen directive.\n\n    :param str addr: addr part of vhost address, may be hostname, IPv4, IPv6,\n        \"\", or \"\\*\"\n    :param str port: port number or \"\\*\" or \"\"\n    :param bool ssl: Whether the directive includes 'ssl'\n    :param bool default: Whether the directive includes 'default_server'\n\n    \"\"\"\n    def __init__(self, host, port, ssl, default):\n        super(Addr, self).__init__((host, port))\n        self.ssl = ssl\n        self.default = default\n\n    @classmethod\n    def fromstring(cls, str_addr):\n        \"\"\"Initialize Addr from string.\"\"\"\n        parts = str_addr.split(' ')\n        ssl = False\n        default = False\n        host = ''\n        port = ''\n\n        # The first part must be the address\n        addr = parts.pop(0)\n\n        # Ignore UNIX-domain sockets\n        if addr.startswith('unix:'):\n            return None\n\n        tup = addr.partition(':')\n        if re.match(r'^\\d+$', tup[0]):\n            # This is a bare port, not a hostname. E.g. listen 80\n            host = ''\n            port = tup[0]\n        else:\n            # This is a host-port tuple. E.g. listen 127.0.0.1:*\n            host = tup[0]\n            port = tup[2]\n\n        # The rest of the parts are options; we only care about ssl and default\n        while len(parts) > 0:\n            nextpart = parts.pop()\n            if nextpart == 'ssl':\n                ssl = True\n            elif nextpart == 'default_server':\n                default = True\n\n        return cls(host, port, ssl, default)\n\n    def __str__(self):\n        parts = ''\n        if self.tup[0] and self.tup[1]:\n            parts = \"%s:%s\" % self.tup\n        elif self.tup[0]:\n            parts = self.tup[0]\n        else:\n            parts = self.tup[1]\n\n        if self.default:\n            parts += ' default_server'\n        if self.ssl:\n            parts += ' ssl'\n\n        return parts\n\n    def __repr__(self):\n        return \"Addr(\" + self.__str__() + \")\"\n\n    def __eq__(self, other):\n        if isinstance(other, self.__class__):\n            return (self.tup == other.tup and\n                    self.ssl == other.ssl and\n                    self.default == other.default)\n        return False\n\n\nclass VirtualHost(object):  # pylint: disable=too-few-public-methods\n    \"\"\"Represents an Nginx Virtualhost.\n\n    :ivar str filep: file path of VH\n    :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`)\n    :ivar set names: Server names/aliases of vhost\n        (:class:`list` of :class:`str`)\n    :ivar list raw: The raw form of the parsed server block\n\n    :ivar bool ssl: SSLEngine on in vhost\n    :ivar bool enabled: Virtual host is enabled\n    :ivar list path: The indices into the parsed file used to access\n        the server block defining the vhost\n\n    \"\"\"\n\n    def __init__(self, filep, addrs, ssl, enabled, names, raw, path):\n        # pylint: disable=too-many-arguments\n        \"\"\"Initialize a VH.\"\"\"\n        self.filep = filep\n        self.addrs = addrs\n        self.names = names\n        self.ssl = ssl\n        self.enabled = enabled\n        self.raw = raw\n        self.path = path\n\n    def __str__(self):\n        addr_str = \", \".join(str(addr) for addr in self.addrs)\n        return (\"file: %s\\n\"\n                \"addrs: %s\\n\"\n                \"names: %s\\n\"\n                \"ssl: %s\\n\"\n                \"enabled: %s\" % (self.filep, addr_str,\n                                 self.names, self.ssl, self.enabled))\n\n    def __repr__(self):\n        return \"VirtualHost(\" + self.__str__().replace(\"\\n\", \", \") + \")\\n\"\n\n    def __eq__(self, other):\n        if isinstance(other, self.__class__):\n            return (self.filep == other.filep and\n                    list(self.addrs) == list(other.addrs) and\n                    self.names == other.names and\n                    self.ssl == other.ssl and\n                    self.enabled == other.enabled and\n                    self.path == other.path)\n\n        return False\n", "path": "certbot-nginx/certbot_nginx/obj.py"}], "after_files": [{"content": "\"\"\"A class that performs TLS-SNI-01 challenges for Nginx\"\"\"\n\nimport itertools\nimport logging\nimport os\n\nfrom certbot import errors\nfrom certbot.plugins import common\n\nfrom certbot_nginx import obj\nfrom certbot_nginx import nginxparser\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NginxTlsSni01(common.TLSSNI01):\n    \"\"\"TLS-SNI-01 authenticator for Nginx\n\n    :ivar configurator: NginxConfigurator object\n    :type configurator: :class:`~nginx.configurator.NginxConfigurator`\n\n    :ivar list achalls: Annotated\n        class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge`\n        challenges\n\n    :param list indices: Meant to hold indices of challenges in a\n        larger array. NginxTlsSni01 is capable of solving many challenges\n        at once which causes an indexing issue within NginxConfigurator\n        who must return all responses in order.  Imagine NginxConfigurator\n        maintaining state about where all of the http-01 Challenges,\n        TLS-SNI-01 Challenges belong in the response array.  This is an\n        optional utility.\n\n    :param str challenge_conf: location of the challenge config file\n\n    \"\"\"\n\n    def perform(self):\n        \"\"\"Perform a challenge on Nginx.\n\n        :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response`\n        :rtype: list\n\n        \"\"\"\n        if not self.achalls:\n            return []\n\n        addresses = []\n        default_addr = \"{0} ssl\".format(\n            self.configurator.config.tls_sni_01_port)\n\n        for achall in self.achalls:\n            vhost = self.configurator.choose_vhost(achall.domain)\n            if vhost is None:\n                logger.error(\n                    \"No nginx vhost exists with server_name matching: %s. \"\n                    \"Please specify server_names in the Nginx config.\",\n                    achall.domain)\n                return None\n\n            if vhost.addrs:\n                addresses.append(list(vhost.addrs))\n            else:\n                addresses.append([obj.Addr.fromstring(default_addr)])\n\n        # Create challenge certs\n        responses = [self._setup_challenge_cert(x) for x in self.achalls]\n\n        # Set up the configuration\n        self._mod_config(addresses)\n\n        # Save reversible changes\n        self.configurator.save(\"SNI Challenge\", True)\n\n        return responses\n\n    def _mod_config(self, ll_addrs):\n        \"\"\"Modifies Nginx config to include challenge server blocks.\n\n        :param list ll_addrs: list of lists of\n            :class:`certbot_nginx.obj.Addr` to apply\n\n        :raises .MisconfigurationError:\n            Unable to find a suitable HTTP block in which to include\n            authenticator hosts.\n\n        \"\"\"\n        # Add the 'include' statement for the challenges if it doesn't exist\n        # already in the main config\n        included = False\n        include_directive = ['\\n', 'include', ' ', self.challenge_conf]\n        root = self.configurator.parser.loc[\"root\"]\n\n        bucket_directive = ['\\n', 'server_names_hash_bucket_size', ' ', '128']\n\n        main = self.configurator.parser.parsed[root]\n        for key, body in main:\n            if key == ['http']:\n                found_bucket = False\n                for k, _ in body:\n                    if k == bucket_directive[1]:\n                        found_bucket = True\n                if not found_bucket:\n                    body.insert(0, bucket_directive)\n                if include_directive not in body:\n                    body.insert(0, include_directive)\n                included = True\n                break\n        if not included:\n            raise errors.MisconfigurationError(\n                'LetsEncrypt could not find an HTTP block to include '\n                'TLS-SNI-01 challenges in %s.' % root)\n\n        config = [self._make_server_block(pair[0], pair[1])\n                  for pair in itertools.izip(self.achalls, ll_addrs)]\n        config = nginxparser.UnspacedList(config)\n\n        self.configurator.reverter.register_file_creation(\n            True, self.challenge_conf)\n\n        with open(self.challenge_conf, \"w\") as new_conf:\n            nginxparser.dump(config, new_conf)\n\n    def _make_server_block(self, achall, addrs):\n        \"\"\"Creates a server block for a challenge.\n\n        :param achall: Annotated TLS-SNI-01 challenge\n        :type achall:\n            :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`\n\n        :param list addrs: addresses of challenged domain\n            :class:`list` of type :class:`~nginx.obj.Addr`\n\n        :returns: server block for the challenge host\n        :rtype: list\n\n        \"\"\"\n        document_root = os.path.join(\n            self.configurator.config.work_dir, \"tls_sni_01_page\")\n\n        block = [['listen', ' ', addr.to_string(include_default=False)] for addr in addrs]\n\n        block.extend([['server_name', ' ',\n                       achall.response(achall.account_key).z_domain],\n                      # access and error logs necessary for\n                      # integration testing (non-root)\n                      ['access_log', ' ', os.path.join(\n                          self.configurator.config.work_dir, 'access.log')],\n                      ['error_log', ' ', os.path.join(\n                          self.configurator.config.work_dir, 'error.log')],\n                      ['ssl_certificate', ' ', self.get_cert_path(achall)],\n                      ['ssl_certificate_key', ' ', self.get_key_path(achall)],\n                      [['location', ' ', '/'], [['root', ' ', document_root]]]] +\n                     self.configurator.parser.loc[\"ssl_options\"])\n        return [['server'], block]\n", "path": "certbot-nginx/certbot_nginx/tls_sni_01.py"}, {"content": "\"\"\"Module contains classes used by the Nginx Configurator.\"\"\"\nimport re\n\nfrom certbot.plugins import common\n\n\nclass Addr(common.Addr):\n    r\"\"\"Represents an Nginx address, i.e. what comes after the 'listen'\n    directive.\n\n    According to the `documentation`_, this may be address[:port], port,\n    or unix:path. The latter is ignored here.\n\n    The default value if no directive is specified is \\*:80 (superuser)\n    or \\*:8000 (otherwise). If no port is specified, the default is\n    80. If no address is specified, listen on all addresses.\n\n    .. _documentation:\n       http://nginx.org/en/docs/http/ngx_http_core_module.html#listen\n\n    .. todo:: Old-style nginx configs define SSL vhosts in a separate\n              block instead of using 'ssl' in the listen directive.\n\n    :param str addr: addr part of vhost address, may be hostname, IPv4, IPv6,\n        \"\", or \"\\*\"\n    :param str port: port number or \"\\*\" or \"\"\n    :param bool ssl: Whether the directive includes 'ssl'\n    :param bool default: Whether the directive includes 'default_server'\n\n    \"\"\"\n    def __init__(self, host, port, ssl, default):\n        super(Addr, self).__init__((host, port))\n        self.ssl = ssl\n        self.default = default\n\n    @classmethod\n    def fromstring(cls, str_addr):\n        \"\"\"Initialize Addr from string.\"\"\"\n        parts = str_addr.split(' ')\n        ssl = False\n        default = False\n        host = ''\n        port = ''\n\n        # The first part must be the address\n        addr = parts.pop(0)\n\n        # Ignore UNIX-domain sockets\n        if addr.startswith('unix:'):\n            return None\n\n        tup = addr.partition(':')\n        if re.match(r'^\\d+$', tup[0]):\n            # This is a bare port, not a hostname. E.g. listen 80\n            host = ''\n            port = tup[0]\n        else:\n            # This is a host-port tuple. E.g. listen 127.0.0.1:*\n            host = tup[0]\n            port = tup[2]\n\n        # The rest of the parts are options; we only care about ssl and default\n        while len(parts) > 0:\n            nextpart = parts.pop()\n            if nextpart == 'ssl':\n                ssl = True\n            elif nextpart == 'default_server':\n                default = True\n\n        return cls(host, port, ssl, default)\n\n    def to_string(self, include_default=True):\n        \"\"\"Return string representation of Addr\"\"\"\n        parts = ''\n        if self.tup[0] and self.tup[1]:\n            parts = \"%s:%s\" % self.tup\n        elif self.tup[0]:\n            parts = self.tup[0]\n        else:\n            parts = self.tup[1]\n\n        if self.default and include_default:\n            parts += ' default_server'\n        if self.ssl:\n            parts += ' ssl'\n\n        return parts\n\n    def __str__(self):\n        return self.to_string()\n\n    def __repr__(self):\n        return \"Addr(\" + self.__str__() + \")\"\n\n    def __eq__(self, other):\n        if isinstance(other, self.__class__):\n            return (self.tup == other.tup and\n                    self.ssl == other.ssl and\n                    self.default == other.default)\n        return False\n\n\nclass VirtualHost(object):  # pylint: disable=too-few-public-methods\n    \"\"\"Represents an Nginx Virtualhost.\n\n    :ivar str filep: file path of VH\n    :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`)\n    :ivar set names: Server names/aliases of vhost\n        (:class:`list` of :class:`str`)\n    :ivar list raw: The raw form of the parsed server block\n\n    :ivar bool ssl: SSLEngine on in vhost\n    :ivar bool enabled: Virtual host is enabled\n    :ivar list path: The indices into the parsed file used to access\n        the server block defining the vhost\n\n    \"\"\"\n\n    def __init__(self, filep, addrs, ssl, enabled, names, raw, path):\n        # pylint: disable=too-many-arguments\n        \"\"\"Initialize a VH.\"\"\"\n        self.filep = filep\n        self.addrs = addrs\n        self.names = names\n        self.ssl = ssl\n        self.enabled = enabled\n        self.raw = raw\n        self.path = path\n\n    def __str__(self):\n        addr_str = \", \".join(str(addr) for addr in self.addrs)\n        return (\"file: %s\\n\"\n                \"addrs: %s\\n\"\n                \"names: %s\\n\"\n                \"ssl: %s\\n\"\n                \"enabled: %s\" % (self.filep, addr_str,\n                                 self.names, self.ssl, self.enabled))\n\n    def __repr__(self):\n        return \"VirtualHost(\" + self.__str__().replace(\"\\n\", \", \") + \")\\n\"\n\n    def __eq__(self, other):\n        if isinstance(other, self.__class__):\n            return (self.filep == other.filep and\n                    list(self.addrs) == list(other.addrs) and\n                    self.names == other.names and\n                    self.ssl == other.ssl and\n                    self.enabled == other.enabled and\n                    self.path == other.path)\n\n        return False\n", "path": "certbot-nginx/certbot_nginx/obj.py"}]}