blob: 83a765cae23c4346454ba13f3000acd1feab70d2 [file] [log] [blame]
Wei-Yu Chen49950b92021-11-08 19:19:18 +08001"""
2Copyright 2020 The Magma Authors.
3
4This source code is licensed under the BSD-style license found in the
5LICENSE file in the root directory of this source tree.
6
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12
13Util module to distinguish between the reasons checkins stop working: network
14is down or cert is invalid.
15"""
16
17import asyncio
18import logging
19import os
20import ssl
21
22
23class TCPClientProtocol(asyncio.Protocol):
24 """
25 Implementation of TCP Protocol to create and immediately close the
26 connection
27 """
28
29 def connection_made(self, transport):
30 transport.close()
31
32
33@asyncio.coroutine
34def create_tcp_connection(host, port, loop):
35 """
36 Creates tcp connection
37 """
38 tcp_conn = yield from loop.create_connection(
39 TCPClientProtocol,
40 host,
41 port,
42 )
43 return tcp_conn
44
45
46@asyncio.coroutine
47def create_ssl_connection(host, port, certfile, keyfile, loop):
48 """
49 Creates ssl connection.
50 """
51 context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
52 context.load_cert_chain(
53 certfile,
54 keyfile=keyfile,
55 )
56
57 ssl_conn = yield from loop.create_connection(
58 TCPClientProtocol,
59 host,
60 port,
61 ssl=context,
62 )
63 return ssl_conn
64
65
66@asyncio.coroutine
67def cert_is_invalid(host, port, certfile, keyfile, loop):
68 """
69 Asynchronously test if both a TCP and SSL connection can be made to host
70 on port. If the TCP connection is successful, but the SSL connection fails,
71 we assume this is due to an invalid cert.
72
73 Args:
74 host: host to connect to
75 port: port to connect to on host
76 certfile: path to a PEM encoded certificate
77 keyfile: path to the corresponding key to the certificate
78 loop: asyncio event loop
79 Returns:
80 True if the cert is invalid
81 False otherwise
82 """
83 # Create connections
84 tcp_coro = create_tcp_connection(host, port, loop)
85 ssl_coro = create_ssl_connection(host, port, certfile, keyfile, loop)
86
87 coros = tcp_coro, ssl_coro
88 asyncio.set_event_loop(loop)
89 res = yield from asyncio.gather(*coros, return_exceptions=True)
90 tcp_res, ssl_res = res
91
92 if isinstance(tcp_res, Exception):
93 logging.error(
94 'Error making TCP connection: %s, %s',
95 'errno==None' if tcp_res.errno is None
96 else os.strerror(tcp_res.errno),
97 tcp_res,
98 )
99 return False
100
101 # Invalid cert only when tcp succeeds and ssl fails
102 if isinstance(ssl_res, Exception):
103 logging.error(
104 'Error making SSL connection: %s, %s',
105 'errno==None' if ssl_res.errno is None
106 else os.strerror(ssl_res.errno),
107 ssl_res,
108 )
109 return True
110
111 return False