We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Standalone should verify ports for suported challenges only
I ran the following commands and both complained that nginx was already using port 80.
`./letsencrypt-auto --standalone --dvsni-port 63443 --server https://acme-v01.api.letsencrypt.org/directory auth`
`./letsencrypt-auto --standalone --dvsni-port 63443 --server https://acme-v01.api.letsencrypt.org/directory --standalone-supported-challenges dvsni auth`

I was under the impression that using `--dvsni-port` would force dvsni only.

I had to supply a fake port with `--simple-http-port 9999` with the second commad to get a certificate.

I'm using haproxy in the front to redirect *.acme.invalid to port 63443.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `letsencrypt/plugins/standalone.py`
Content:
```
1 """Standalone Authenticator."""
2 import argparse
3 import collections
4 import logging
5 import random
6 import socket
7 import threading
8 
9 import OpenSSL
10 import six
11 import zope.interface
12 
13 from acme import challenges
14 from acme import crypto_util as acme_crypto_util
15 from acme import standalone as acme_standalone
16 
17 from letsencrypt import achallenges
18 from letsencrypt import errors
19 from letsencrypt import interfaces
20 
21 from letsencrypt.plugins import common
22 from letsencrypt.plugins import util
23 
24 logger = logging.getLogger(__name__)
25 
26 
27 class ServerManager(object):
28     """Standalone servers manager.
29 
30     Manager for `ACMEServer` and `ACMETLSServer` instances.
31 
32     `certs` and `simple_http_resources` correspond to
33     `acme.crypto_util.SSLSocket.certs` and
34     `acme.crypto_util.SSLSocket.simple_http_resources` respectively. All
35     created servers share the same certificates and resources, so if
36     you're running both TLS and non-TLS instances, SimpleHTTP handlers
37     will serve the same URLs!
38 
39     """
40     _Instance = collections.namedtuple("_Instance", "server thread")
41 
42     def __init__(self, certs, simple_http_resources):
43         self._instances = {}
44         self.certs = certs
45         self.simple_http_resources = simple_http_resources
46 
47     def run(self, port, challenge_type):
48         """Run ACME server on specified ``port``.
49 
50         This method is idempotent, i.e. all calls with the same pair of
51         ``(port, challenge_type)`` will reuse the same server.
52 
53         :param int port: Port to run the server on.
54         :param challenge_type: Subclass of `acme.challenges.Challenge`,
55             either `acme.challenge.SimpleHTTP` or `acme.challenges.DVSNI`.
56 
57         :returns: Server instance.
58         :rtype: ACMEServerMixin
59 
60         """
61         assert challenge_type in (challenges.DVSNI, challenges.SimpleHTTP)
62         if port in self._instances:
63             return self._instances[port].server
64 
65         address = ("", port)
66         try:
67             if challenge_type is challenges.DVSNI:
68                 server = acme_standalone.DVSNIServer(address, self.certs)
69             else:  # challenges.SimpleHTTP
70                 server = acme_standalone.SimpleHTTPServer(
71                     address, self.simple_http_resources)
72         except socket.error as error:
73             raise errors.StandaloneBindError(error, port)
74 
75         thread = threading.Thread(target=server.serve_forever2)
76         thread.start()
77 
78         # if port == 0, then random free port on OS is taken
79         # pylint: disable=no-member
80         real_port = server.socket.getsockname()[1]
81         self._instances[real_port] = self._Instance(server, thread)
82         return server
83 
84     def stop(self, port):
85         """Stop ACME server running on the specified ``port``.
86 
87         :param int port:
88 
89         """
90         instance = self._instances[port]
91         logger.debug("Stopping server at %s:%d...",
92                      *instance.server.socket.getsockname()[:2])
93         instance.server.shutdown2()
94         instance.thread.join()
95         del self._instances[port]
96 
97     def running(self):
98         """Return all running instances.
99 
100         Once the server is stopped using `stop`, it will not be
101         returned.
102 
103         :returns: Mapping from ``port`` to ``server``.
104         :rtype: tuple
105 
106         """
107         return dict((port, instance.server) for port, instance
108                     in six.iteritems(self._instances))
109 
110 
111 SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.SimpleHTTP])
112 
113 
114 def supported_challenges_validator(data):
115     """Supported challenges validator for the `argparse`.
116 
117     It should be passed as `type` argument to `add_argument`.
118 
119     """
120     challs = data.split(",")
121     unrecognized = [name for name in challs
122                     if name not in challenges.Challenge.TYPES]
123     if unrecognized:
124         raise argparse.ArgumentTypeError(
125             "Unrecognized challenges: {0}".format(", ".join(unrecognized)))
126 
127     choices = set(chall.typ for chall in SUPPORTED_CHALLENGES)
128     if not set(challs).issubset(choices):
129         raise argparse.ArgumentTypeError(
130             "Plugin does not support the following (valid) "
131             "challenges: {0}".format(", ".join(set(challs) - choices)))
132 
133     return data
134 
135 
136 class Authenticator(common.Plugin):
137     """Standalone Authenticator.
138 
139     This authenticator creates its own ephemeral TCP listener on the
140     necessary port in order to respond to incoming DVSNI and SimpleHTTP
141     challenges from the certificate authority. Therefore, it does not
142     rely on any existing server program.
143 
144     """
145     zope.interface.implements(interfaces.IAuthenticator)
146     zope.interface.classProvides(interfaces.IPluginFactory)
147 
148     description = "Standalone Authenticator"
149 
150     def __init__(self, *args, **kwargs):
151         super(Authenticator, self).__init__(*args, **kwargs)
152 
153         # one self-signed key for all DVSNI and SimpleHTTP certificates
154         self.key = OpenSSL.crypto.PKey()
155         self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)
156         # TODO: generate only when the first SimpleHTTP challenge is solved
157         self.simple_http_cert = acme_crypto_util.gen_ss_cert(
158             self.key, domains=["temp server"])
159 
160         self.served = collections.defaultdict(set)
161 
162         # Stuff below is shared across threads (i.e. servers read
163         # values, main thread writes). Due to the nature of CPython's
164         # GIL, the operations are safe, c.f.
165         # https://docs.python.org/2/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
166         self.certs = {}
167         self.simple_http_resources = set()
168 
169         self.servers = ServerManager(self.certs, self.simple_http_resources)
170 
171     @classmethod
172     def add_parser_arguments(cls, add):
173         add("supported-challenges", help="Supported challenges, "
174             "order preferences are randomly chosen.",
175             type=supported_challenges_validator, default=",".join(
176                 sorted(chall.typ for chall in SUPPORTED_CHALLENGES)))
177 
178     @property
179     def supported_challenges(self):
180         """Challenges supported by this plugin."""
181         return set(challenges.Challenge.TYPES[name] for name in
182                    self.conf("supported-challenges").split(","))
183 
184     def more_info(self):  # pylint: disable=missing-docstring
185         return self.__doc__
186 
187     def prepare(self):  # pylint: disable=missing-docstring
188         pass
189 
190     def get_chall_pref(self, domain):
191         # pylint: disable=unused-argument,missing-docstring
192         chall_pref = list(self.supported_challenges)
193         random.shuffle(chall_pref)  # 50% for each challenge
194         return chall_pref
195 
196     def perform(self, achalls):  # pylint: disable=missing-docstring
197         if any(util.already_listening(port) for port in
198                (self.config.dvsni_port, self.config.simple_http_port)):
199             raise errors.MisconfigurationError(
200                 "At least one of the (possibly) required ports is "
201                 "already taken.")
202 
203         try:
204             return self.perform2(achalls)
205         except errors.StandaloneBindError as error:
206             display = zope.component.getUtility(interfaces.IDisplay)
207 
208             if error.socket_error.errno == socket.errno.EACCES:
209                 display.notification(
210                     "Could not bind TCP port {0} because you don't have "
211                     "the appropriate permissions (for example, you "
212                     "aren't running this program as "
213                     "root).".format(error.port))
214             elif error.socket_error.errno == socket.errno.EADDRINUSE:
215                 display.notification(
216                     "Could not bind TCP port {0} because it is already in "
217                     "use by another process on this system (such as a web "
218                     "server). Please stop the program in question and then "
219                     "try again.".format(error.port))
220             else:
221                 raise  # XXX: How to handle unknown errors in binding?
222 
223     def perform2(self, achalls):
224         """Perform achallenges without IDisplay interaction."""
225         responses = []
226 
227         for achall in achalls:
228             if isinstance(achall, achallenges.SimpleHTTP):
229                 server = self.servers.run(
230                     self.config.simple_http_port, challenges.SimpleHTTP)
231                 response, validation = achall.gen_response_and_validation(
232                     tls=False)
233                 self.simple_http_resources.add(
234                     acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(
235                         chall=achall.chall, response=response,
236                         validation=validation))
237                 cert = self.simple_http_cert
238                 domain = achall.domain
239             else:  # DVSNI
240                 server = self.servers.run(self.config.dvsni_port, challenges.DVSNI)
241                 response, cert, _ = achall.gen_cert_and_response(self.key)
242                 domain = response.z_domain
243             self.certs[domain] = (self.key, cert)
244             self.served[server].add(achall)
245             responses.append(response)
246 
247         return responses
248 
249     def cleanup(self, achalls):  # pylint: disable=missing-docstring
250         # reduce self.served and close servers if none challenges are served
251         for server, server_achalls in self.served.items():
252             for achall in achalls:
253                 if achall in server_achalls:
254                     server_achalls.remove(achall)
255         for port, server in six.iteritems(self.servers.running()):
256             if not self.served[server]:
257                 self.servers.stop(port)
258
```
--- 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/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py
--- a/letsencrypt/plugins/standalone.py
+++ b/letsencrypt/plugins/standalone.py
@@ -181,6 +181,15 @@
         return set(challenges.Challenge.TYPES[name] for name in
                    self.conf("supported-challenges").split(","))
 
+    @property
+    def _necessary_ports(self):
+        necessary_ports = set()
+        if challenges.SimpleHTTP in self.supported_challenges:
+            necessary_ports.add(self.config.simple_http_port)
+        if challenges.DVSNI in self.supported_challenges:
+            necessary_ports.add(self.config.dvsni_port)
+        return necessary_ports
+
     def more_info(self):  # pylint: disable=missing-docstring
         return self.__doc__
 
@@ -194,8 +203,7 @@
         return chall_pref
 
     def perform(self, achalls):  # pylint: disable=missing-docstring
-        if any(util.already_listening(port) for port in
-               (self.config.dvsni_port, self.config.simple_http_port)):
+        if any(util.already_listening(port) for port in self._necessary_ports):
             raise errors.MisconfigurationError(
                 "At least one of the (possibly) required ports is "
                 "already taken.")

{"golden_diff": "diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py\n--- a/letsencrypt/plugins/standalone.py\n+++ b/letsencrypt/plugins/standalone.py\n@@ -181,6 +181,15 @@\n         return set(challenges.Challenge.TYPES[name] for name in\n                    self.conf(\"supported-challenges\").split(\",\"))\n \n+    @property\n+    def _necessary_ports(self):\n+        necessary_ports = set()\n+        if challenges.SimpleHTTP in self.supported_challenges:\n+            necessary_ports.add(self.config.simple_http_port)\n+        if challenges.DVSNI in self.supported_challenges:\n+            necessary_ports.add(self.config.dvsni_port)\n+        return necessary_ports\n+\n     def more_info(self):  # pylint: disable=missing-docstring\n         return self.__doc__\n \n@@ -194,8 +203,7 @@\n         return chall_pref\n \n     def perform(self, achalls):  # pylint: disable=missing-docstring\n-        if any(util.already_listening(port) for port in\n-               (self.config.dvsni_port, self.config.simple_http_port)):\n+        if any(util.already_listening(port) for port in self._necessary_ports):\n             raise errors.MisconfigurationError(\n                 \"At least one of the (possibly) required ports is \"\n                 \"already taken.\")\n", "issue": "Standalone should verify ports for suported challenges only\nI ran the following commands and both complained that nginx was already using port 80.\n`./letsencrypt-auto --standalone --dvsni-port 63443 --server https://acme-v01.api.letsencrypt.org/directory auth`\n`./letsencrypt-auto --standalone --dvsni-port 63443 --server https://acme-v01.api.letsencrypt.org/directory --standalone-supported-challenges dvsni auth`\n\nI was under the impression that using `--dvsni-port` would force dvsni only.\n\nI had to supply a fake port with `--simple-http-port 9999` with the second commad to get a certificate.\n\nI'm using haproxy in the front to redirect *.acme.invalid to port 63443.\n\n", "before_files": [{"content": "\"\"\"Standalone Authenticator.\"\"\"\nimport argparse\nimport collections\nimport logging\nimport random\nimport socket\nimport threading\n\nimport OpenSSL\nimport six\nimport zope.interface\n\nfrom acme import challenges\nfrom acme import crypto_util as acme_crypto_util\nfrom acme import standalone as acme_standalone\n\nfrom letsencrypt import achallenges\nfrom letsencrypt import errors\nfrom letsencrypt import interfaces\n\nfrom letsencrypt.plugins import common\nfrom letsencrypt.plugins import util\n\nlogger = logging.getLogger(__name__)\n\n\nclass ServerManager(object):\n    \"\"\"Standalone servers manager.\n\n    Manager for `ACMEServer` and `ACMETLSServer` instances.\n\n    `certs` and `simple_http_resources` correspond to\n    `acme.crypto_util.SSLSocket.certs` and\n    `acme.crypto_util.SSLSocket.simple_http_resources` respectively. All\n    created servers share the same certificates and resources, so if\n    you're running both TLS and non-TLS instances, SimpleHTTP handlers\n    will serve the same URLs!\n\n    \"\"\"\n    _Instance = collections.namedtuple(\"_Instance\", \"server thread\")\n\n    def __init__(self, certs, simple_http_resources):\n        self._instances = {}\n        self.certs = certs\n        self.simple_http_resources = simple_http_resources\n\n    def run(self, port, challenge_type):\n        \"\"\"Run ACME server on specified ``port``.\n\n        This method is idempotent, i.e. all calls with the same pair of\n        ``(port, challenge_type)`` will reuse the same server.\n\n        :param int port: Port to run the server on.\n        :param challenge_type: Subclass of `acme.challenges.Challenge`,\n            either `acme.challenge.SimpleHTTP` or `acme.challenges.DVSNI`.\n\n        :returns: Server instance.\n        :rtype: ACMEServerMixin\n\n        \"\"\"\n        assert challenge_type in (challenges.DVSNI, challenges.SimpleHTTP)\n        if port in self._instances:\n            return self._instances[port].server\n\n        address = (\"\", port)\n        try:\n            if challenge_type is challenges.DVSNI:\n                server = acme_standalone.DVSNIServer(address, self.certs)\n            else:  # challenges.SimpleHTTP\n                server = acme_standalone.SimpleHTTPServer(\n                    address, self.simple_http_resources)\n        except socket.error as error:\n            raise errors.StandaloneBindError(error, port)\n\n        thread = threading.Thread(target=server.serve_forever2)\n        thread.start()\n\n        # if port == 0, then random free port on OS is taken\n        # pylint: disable=no-member\n        real_port = server.socket.getsockname()[1]\n        self._instances[real_port] = self._Instance(server, thread)\n        return server\n\n    def stop(self, port):\n        \"\"\"Stop ACME server running on the specified ``port``.\n\n        :param int port:\n\n        \"\"\"\n        instance = self._instances[port]\n        logger.debug(\"Stopping server at %s:%d...\",\n                     *instance.server.socket.getsockname()[:2])\n        instance.server.shutdown2()\n        instance.thread.join()\n        del self._instances[port]\n\n    def running(self):\n        \"\"\"Return all running instances.\n\n        Once the server is stopped using `stop`, it will not be\n        returned.\n\n        :returns: Mapping from ``port`` to ``server``.\n        :rtype: tuple\n\n        \"\"\"\n        return dict((port, instance.server) for port, instance\n                    in six.iteritems(self._instances))\n\n\nSUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.SimpleHTTP])\n\n\ndef supported_challenges_validator(data):\n    \"\"\"Supported challenges validator for the `argparse`.\n\n    It should be passed as `type` argument to `add_argument`.\n\n    \"\"\"\n    challs = data.split(\",\")\n    unrecognized = [name for name in challs\n                    if name not in challenges.Challenge.TYPES]\n    if unrecognized:\n        raise argparse.ArgumentTypeError(\n            \"Unrecognized challenges: {0}\".format(\", \".join(unrecognized)))\n\n    choices = set(chall.typ for chall in SUPPORTED_CHALLENGES)\n    if not set(challs).issubset(choices):\n        raise argparse.ArgumentTypeError(\n            \"Plugin does not support the following (valid) \"\n            \"challenges: {0}\".format(\", \".join(set(challs) - choices)))\n\n    return data\n\n\nclass Authenticator(common.Plugin):\n    \"\"\"Standalone Authenticator.\n\n    This authenticator creates its own ephemeral TCP listener on the\n    necessary port in order to respond to incoming DVSNI and SimpleHTTP\n    challenges from the certificate authority. Therefore, it does not\n    rely on any existing server program.\n\n    \"\"\"\n    zope.interface.implements(interfaces.IAuthenticator)\n    zope.interface.classProvides(interfaces.IPluginFactory)\n\n    description = \"Standalone Authenticator\"\n\n    def __init__(self, *args, **kwargs):\n        super(Authenticator, self).__init__(*args, **kwargs)\n\n        # one self-signed key for all DVSNI and SimpleHTTP certificates\n        self.key = OpenSSL.crypto.PKey()\n        self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)\n        # TODO: generate only when the first SimpleHTTP challenge is solved\n        self.simple_http_cert = acme_crypto_util.gen_ss_cert(\n            self.key, domains=[\"temp server\"])\n\n        self.served = collections.defaultdict(set)\n\n        # Stuff below is shared across threads (i.e. servers read\n        # values, main thread writes). Due to the nature of CPython's\n        # GIL, the operations are safe, c.f.\n        # https://docs.python.org/2/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe\n        self.certs = {}\n        self.simple_http_resources = set()\n\n        self.servers = ServerManager(self.certs, self.simple_http_resources)\n\n    @classmethod\n    def add_parser_arguments(cls, add):\n        add(\"supported-challenges\", help=\"Supported challenges, \"\n            \"order preferences are randomly chosen.\",\n            type=supported_challenges_validator, default=\",\".join(\n                sorted(chall.typ for chall in SUPPORTED_CHALLENGES)))\n\n    @property\n    def supported_challenges(self):\n        \"\"\"Challenges supported by this plugin.\"\"\"\n        return set(challenges.Challenge.TYPES[name] for name in\n                   self.conf(\"supported-challenges\").split(\",\"))\n\n    def more_info(self):  # pylint: disable=missing-docstring\n        return self.__doc__\n\n    def prepare(self):  # pylint: disable=missing-docstring\n        pass\n\n    def get_chall_pref(self, domain):\n        # pylint: disable=unused-argument,missing-docstring\n        chall_pref = list(self.supported_challenges)\n        random.shuffle(chall_pref)  # 50% for each challenge\n        return chall_pref\n\n    def perform(self, achalls):  # pylint: disable=missing-docstring\n        if any(util.already_listening(port) for port in\n               (self.config.dvsni_port, self.config.simple_http_port)):\n            raise errors.MisconfigurationError(\n                \"At least one of the (possibly) required ports is \"\n                \"already taken.\")\n\n        try:\n            return self.perform2(achalls)\n        except errors.StandaloneBindError as error:\n            display = zope.component.getUtility(interfaces.IDisplay)\n\n            if error.socket_error.errno == socket.errno.EACCES:\n                display.notification(\n                    \"Could not bind TCP port {0} because you don't have \"\n                    \"the appropriate permissions (for example, you \"\n                    \"aren't running this program as \"\n                    \"root).\".format(error.port))\n            elif error.socket_error.errno == socket.errno.EADDRINUSE:\n                display.notification(\n                    \"Could not bind TCP port {0} because it is already in \"\n                    \"use by another process on this system (such as a web \"\n                    \"server). Please stop the program in question and then \"\n                    \"try again.\".format(error.port))\n            else:\n                raise  # XXX: How to handle unknown errors in binding?\n\n    def perform2(self, achalls):\n        \"\"\"Perform achallenges without IDisplay interaction.\"\"\"\n        responses = []\n\n        for achall in achalls:\n            if isinstance(achall, achallenges.SimpleHTTP):\n                server = self.servers.run(\n                    self.config.simple_http_port, challenges.SimpleHTTP)\n                response, validation = achall.gen_response_and_validation(\n                    tls=False)\n                self.simple_http_resources.add(\n                    acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(\n                        chall=achall.chall, response=response,\n                        validation=validation))\n                cert = self.simple_http_cert\n                domain = achall.domain\n            else:  # DVSNI\n                server = self.servers.run(self.config.dvsni_port, challenges.DVSNI)\n                response, cert, _ = achall.gen_cert_and_response(self.key)\n                domain = response.z_domain\n            self.certs[domain] = (self.key, cert)\n            self.served[server].add(achall)\n            responses.append(response)\n\n        return responses\n\n    def cleanup(self, achalls):  # pylint: disable=missing-docstring\n        # reduce self.served and close servers if none challenges are served\n        for server, server_achalls in self.served.items():\n            for achall in achalls:\n                if achall in server_achalls:\n                    server_achalls.remove(achall)\n        for port, server in six.iteritems(self.servers.running()):\n            if not self.served[server]:\n                self.servers.stop(port)\n", "path": "letsencrypt/plugins/standalone.py"}], "after_files": [{"content": "\"\"\"Standalone Authenticator.\"\"\"\nimport argparse\nimport collections\nimport logging\nimport random\nimport socket\nimport threading\n\nimport OpenSSL\nimport six\nimport zope.interface\n\nfrom acme import challenges\nfrom acme import crypto_util as acme_crypto_util\nfrom acme import standalone as acme_standalone\n\nfrom letsencrypt import achallenges\nfrom letsencrypt import errors\nfrom letsencrypt import interfaces\n\nfrom letsencrypt.plugins import common\nfrom letsencrypt.plugins import util\n\nlogger = logging.getLogger(__name__)\n\n\nclass ServerManager(object):\n    \"\"\"Standalone servers manager.\n\n    Manager for `ACMEServer` and `ACMETLSServer` instances.\n\n    `certs` and `simple_http_resources` correspond to\n    `acme.crypto_util.SSLSocket.certs` and\n    `acme.crypto_util.SSLSocket.simple_http_resources` respectively. All\n    created servers share the same certificates and resources, so if\n    you're running both TLS and non-TLS instances, SimpleHTTP handlers\n    will serve the same URLs!\n\n    \"\"\"\n    _Instance = collections.namedtuple(\"_Instance\", \"server thread\")\n\n    def __init__(self, certs, simple_http_resources):\n        self._instances = {}\n        self.certs = certs\n        self.simple_http_resources = simple_http_resources\n\n    def run(self, port, challenge_type):\n        \"\"\"Run ACME server on specified ``port``.\n\n        This method is idempotent, i.e. all calls with the same pair of\n        ``(port, challenge_type)`` will reuse the same server.\n\n        :param int port: Port to run the server on.\n        :param challenge_type: Subclass of `acme.challenges.Challenge`,\n            either `acme.challenge.SimpleHTTP` or `acme.challenges.DVSNI`.\n\n        :returns: Server instance.\n        :rtype: ACMEServerMixin\n\n        \"\"\"\n        assert challenge_type in (challenges.DVSNI, challenges.SimpleHTTP)\n        if port in self._instances:\n            return self._instances[port].server\n\n        address = (\"\", port)\n        try:\n            if challenge_type is challenges.DVSNI:\n                server = acme_standalone.DVSNIServer(address, self.certs)\n            else:  # challenges.SimpleHTTP\n                server = acme_standalone.SimpleHTTPServer(\n                    address, self.simple_http_resources)\n        except socket.error as error:\n            raise errors.StandaloneBindError(error, port)\n\n        thread = threading.Thread(target=server.serve_forever2)\n        thread.start()\n\n        # if port == 0, then random free port on OS is taken\n        # pylint: disable=no-member\n        real_port = server.socket.getsockname()[1]\n        self._instances[real_port] = self._Instance(server, thread)\n        return server\n\n    def stop(self, port):\n        \"\"\"Stop ACME server running on the specified ``port``.\n\n        :param int port:\n\n        \"\"\"\n        instance = self._instances[port]\n        logger.debug(\"Stopping server at %s:%d...\",\n                     *instance.server.socket.getsockname()[:2])\n        instance.server.shutdown2()\n        instance.thread.join()\n        del self._instances[port]\n\n    def running(self):\n        \"\"\"Return all running instances.\n\n        Once the server is stopped using `stop`, it will not be\n        returned.\n\n        :returns: Mapping from ``port`` to ``server``.\n        :rtype: tuple\n\n        \"\"\"\n        return dict((port, instance.server) for port, instance\n                    in six.iteritems(self._instances))\n\n\nSUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.SimpleHTTP])\n\n\ndef supported_challenges_validator(data):\n    \"\"\"Supported challenges validator for the `argparse`.\n\n    It should be passed as `type` argument to `add_argument`.\n\n    \"\"\"\n    challs = data.split(\",\")\n    unrecognized = [name for name in challs\n                    if name not in challenges.Challenge.TYPES]\n    if unrecognized:\n        raise argparse.ArgumentTypeError(\n            \"Unrecognized challenges: {0}\".format(\", \".join(unrecognized)))\n\n    choices = set(chall.typ for chall in SUPPORTED_CHALLENGES)\n    if not set(challs).issubset(choices):\n        raise argparse.ArgumentTypeError(\n            \"Plugin does not support the following (valid) \"\n            \"challenges: {0}\".format(\", \".join(set(challs) - choices)))\n\n    return data\n\n\nclass Authenticator(common.Plugin):\n    \"\"\"Standalone Authenticator.\n\n    This authenticator creates its own ephemeral TCP listener on the\n    necessary port in order to respond to incoming DVSNI and SimpleHTTP\n    challenges from the certificate authority. Therefore, it does not\n    rely on any existing server program.\n\n    \"\"\"\n    zope.interface.implements(interfaces.IAuthenticator)\n    zope.interface.classProvides(interfaces.IPluginFactory)\n\n    description = \"Standalone Authenticator\"\n\n    def __init__(self, *args, **kwargs):\n        super(Authenticator, self).__init__(*args, **kwargs)\n\n        # one self-signed key for all DVSNI and SimpleHTTP certificates\n        self.key = OpenSSL.crypto.PKey()\n        self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)\n        # TODO: generate only when the first SimpleHTTP challenge is solved\n        self.simple_http_cert = acme_crypto_util.gen_ss_cert(\n            self.key, domains=[\"temp server\"])\n\n        self.served = collections.defaultdict(set)\n\n        # Stuff below is shared across threads (i.e. servers read\n        # values, main thread writes). Due to the nature of CPython's\n        # GIL, the operations are safe, c.f.\n        # https://docs.python.org/2/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe\n        self.certs = {}\n        self.simple_http_resources = set()\n\n        self.servers = ServerManager(self.certs, self.simple_http_resources)\n\n    @classmethod\n    def add_parser_arguments(cls, add):\n        add(\"supported-challenges\", help=\"Supported challenges, \"\n            \"order preferences are randomly chosen.\",\n            type=supported_challenges_validator, default=\",\".join(\n                sorted(chall.typ for chall in SUPPORTED_CHALLENGES)))\n\n    @property\n    def supported_challenges(self):\n        \"\"\"Challenges supported by this plugin.\"\"\"\n        return set(challenges.Challenge.TYPES[name] for name in\n                   self.conf(\"supported-challenges\").split(\",\"))\n\n    @property\n    def _necessary_ports(self):\n        necessary_ports = set()\n        if challenges.SimpleHTTP in self.supported_challenges:\n            necessary_ports.add(self.config.simple_http_port)\n        if challenges.DVSNI in self.supported_challenges:\n            necessary_ports.add(self.config.dvsni_port)\n        return necessary_ports\n\n    def more_info(self):  # pylint: disable=missing-docstring\n        return self.__doc__\n\n    def prepare(self):  # pylint: disable=missing-docstring\n        pass\n\n    def get_chall_pref(self, domain):\n        # pylint: disable=unused-argument,missing-docstring\n        chall_pref = list(self.supported_challenges)\n        random.shuffle(chall_pref)  # 50% for each challenge\n        return chall_pref\n\n    def perform(self, achalls):  # pylint: disable=missing-docstring\n        if any(util.already_listening(port) for port in self._necessary_ports):\n            raise errors.MisconfigurationError(\n                \"At least one of the (possibly) required ports is \"\n                \"already taken.\")\n\n        try:\n            return self.perform2(achalls)\n        except errors.StandaloneBindError as error:\n            display = zope.component.getUtility(interfaces.IDisplay)\n\n            if error.socket_error.errno == socket.errno.EACCES:\n                display.notification(\n                    \"Could not bind TCP port {0} because you don't have \"\n                    \"the appropriate permissions (for example, you \"\n                    \"aren't running this program as \"\n                    \"root).\".format(error.port))\n            elif error.socket_error.errno == socket.errno.EADDRINUSE:\n                display.notification(\n                    \"Could not bind TCP port {0} because it is already in \"\n                    \"use by another process on this system (such as a web \"\n                    \"server). Please stop the program in question and then \"\n                    \"try again.\".format(error.port))\n            else:\n                raise  # XXX: How to handle unknown errors in binding?\n\n    def perform2(self, achalls):\n        \"\"\"Perform achallenges without IDisplay interaction.\"\"\"\n        responses = []\n\n        for achall in achalls:\n            if isinstance(achall, achallenges.SimpleHTTP):\n                server = self.servers.run(\n                    self.config.simple_http_port, challenges.SimpleHTTP)\n                response, validation = achall.gen_response_and_validation(\n                    tls=False)\n                self.simple_http_resources.add(\n                    acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(\n                        chall=achall.chall, response=response,\n                        validation=validation))\n                cert = self.simple_http_cert\n                domain = achall.domain\n            else:  # DVSNI\n                server = self.servers.run(self.config.dvsni_port, challenges.DVSNI)\n                response, cert, _ = achall.gen_cert_and_response(self.key)\n                domain = response.z_domain\n            self.certs[domain] = (self.key, cert)\n            self.served[server].add(achall)\n            responses.append(response)\n\n        return responses\n\n    def cleanup(self, achalls):  # pylint: disable=missing-docstring\n        # reduce self.served and close servers if none challenges are served\n        for server, server_achalls in self.served.items():\n            for achall in achalls:\n                if achall in server_achalls:\n                    server_achalls.remove(achall)\n        for port, server in six.iteritems(self.servers.running()):\n            if not self.served[server]:\n                self.servers.stop(port)\n", "path": "letsencrypt/plugins/standalone.py"}]}