blob: ac2b536502fd2366304b35b5c92d93b1f4bf74c8 [file] [log] [blame]
Wei-Yu Chenad55cb82022-02-15 20:07:01 +08001# SPDX-FileCopyrightText: 2020 The Magma Authors.
2# SPDX-FileCopyrightText: 2022 Open Networking Foundation <support@opennetworking.org>
3#
4# SPDX-License-Identifier: BSD-3-Clause
Wei-Yu Chen49950b92021-11-08 19:19:18 +08005
6Util module to distinguish between the reasons checkins stop working: network
7is down or cert is invalid.
8"""
9
10import asyncio
11import logging
12import os
13import ssl
14
15
16class TCPClientProtocol(asyncio.Protocol):
17 """
18 Implementation of TCP Protocol to create and immediately close the
19 connection
20 """
21
22 def connection_made(self, transport):
23 transport.close()
24
25
26@asyncio.coroutine
27def create_tcp_connection(host, port, loop):
28 """
29 Creates tcp connection
30 """
31 tcp_conn = yield from loop.create_connection(
32 TCPClientProtocol,
33 host,
34 port,
35 )
36 return tcp_conn
37
38
39@asyncio.coroutine
40def create_ssl_connection(host, port, certfile, keyfile, loop):
41 """
42 Creates ssl connection.
43 """
44 context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
45 context.load_cert_chain(
46 certfile,
47 keyfile=keyfile,
48 )
49
50 ssl_conn = yield from loop.create_connection(
51 TCPClientProtocol,
52 host,
53 port,
54 ssl=context,
55 )
56 return ssl_conn
57
58
59@asyncio.coroutine
60def cert_is_invalid(host, port, certfile, keyfile, loop):
61 """
62 Asynchronously test if both a TCP and SSL connection can be made to host
63 on port. If the TCP connection is successful, but the SSL connection fails,
64 we assume this is due to an invalid cert.
65
66 Args:
67 host: host to connect to
68 port: port to connect to on host
69 certfile: path to a PEM encoded certificate
70 keyfile: path to the corresponding key to the certificate
71 loop: asyncio event loop
72 Returns:
73 True if the cert is invalid
74 False otherwise
75 """
76 # Create connections
77 tcp_coro = create_tcp_connection(host, port, loop)
78 ssl_coro = create_ssl_connection(host, port, certfile, keyfile, loop)
79
80 coros = tcp_coro, ssl_coro
81 asyncio.set_event_loop(loop)
82 res = yield from asyncio.gather(*coros, return_exceptions=True)
83 tcp_res, ssl_res = res
84
85 if isinstance(tcp_res, Exception):
86 logging.error(
87 'Error making TCP connection: %s, %s',
88 'errno==None' if tcp_res.errno is None
89 else os.strerror(tcp_res.errno),
90 tcp_res,
91 )
92 return False
93
94 # Invalid cert only when tcp succeeds and ssl fails
95 if isinstance(ssl_res, Exception):
96 logging.error(
97 'Error making SSL connection: %s, %s',
98 'errno==None' if ssl_res.errno is None
99 else os.strerror(ssl_res.errno),
100 ssl_res,
101 )
102 return True
103
104 return False