atomicshop 3.3.8__py3-none-any.whl → 3.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/basics/strings.py +1 -1
- atomicshop/certificates.py +2 -2
- atomicshop/dns.py +26 -28
- atomicshop/etws/traces/trace_tcp.py +1 -2
- atomicshop/mitm/centered_settings.py +133 -0
- atomicshop/mitm/config_static.py +22 -44
- atomicshop/mitm/connection_thread_worker.py +383 -165
- atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
- atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
- atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
- atomicshop/mitm/engines/create_module_template.py +1 -2
- atomicshop/mitm/import_config.py +91 -89
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +238 -122
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +33 -12
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +104 -31
- atomicshop/networks.py +160 -92
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/process.py +11 -4
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +140 -164
- atomicshop/web.py +63 -22
- atomicshop/web_apis/google_llm.py +22 -14
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
- atomicshop/wrappers/dockerw/dockerw.py +2 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +0 -190
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +180 -68
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +20 -4
- atomicshop/wrappers/loggingw/reading.py +18 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +0 -38
- atomicshop/wrappers/netshw.py +124 -3
- atomicshop/wrappers/playwrightw/scenarios.py +1 -1
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
- atomicshop/wrappers/socketw/certificator.py +19 -9
- atomicshop/wrappers/socketw/creator.py +101 -14
- atomicshop/wrappers/socketw/dns_server.py +17 -5
- atomicshop/wrappers/socketw/exception_wrapper.py +21 -16
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +29 -9
- atomicshop/wrappers/socketw/sender.py +10 -9
- atomicshop/wrappers/socketw/sni.py +31 -10
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +11 -10
- atomicshop/wrappers/socketw/socket_wrapper.py +125 -32
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/ubuntu_terminal.py +21 -18
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/RECORD +83 -109
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
- atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
- atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py +0 -10
- atomicshop/a_installs/ubuntu/mongodb.py +0 -12
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/wsl_ubuntu_lts.py +0 -10
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/__init__.py +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/search_in_archive.py +0 -284
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -73
- atomicshop/archiver/shutils.py +0 -34
- atomicshop/archiver/zips.py +0 -353
- atomicshop/file_types.py +0 -24
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/script_as_string_processor.py +0 -42
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -449
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -233
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/__init__.py +0 -0
- atomicshop/wrappers/fibratusw/install.py +0 -80
- atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py +0 -100
- atomicshop/wrappers/mongodbw/install_mongodb_win.py +0 -244
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- atomicshop/wrappers/wslw.py +0 -192
- atomicshop-3.3.8.dist-info/entry_points.txt +0 -2
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import socket
|
|
3
3
|
import ssl
|
|
4
4
|
|
|
5
|
-
from . import
|
|
5
|
+
from . import socket_base, exception_wrapper
|
|
6
6
|
from ...print_api import print_api
|
|
7
7
|
|
|
8
8
|
|
|
@@ -25,28 +25,63 @@ def add_reusable_address_option(socket_instance):
|
|
|
25
25
|
socket_instance.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def create_ssl_context_for_server(
|
|
28
|
+
def create_ssl_context_for_server(
|
|
29
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
30
|
+
sslkeylog_file_path: str = None,
|
|
31
|
+
allow_legacy: bool = False
|
|
32
|
+
) -> ssl.SSLContext:
|
|
33
|
+
"""
|
|
34
|
+
This function creates the SSL context for the server.
|
|
35
|
+
Meaning that your script will act like a server, and the client will connect to it.
|
|
36
|
+
"""
|
|
29
37
|
# Creating context with SSL certificate and the private key before the socket
|
|
30
38
|
# https://docs.python.org/3/library/ssl.html
|
|
31
39
|
# Creating context for SSL wrapper, specifying "PROTOCOL_TLS_SERVER" will pick the best TLS version protocol for
|
|
32
40
|
# the server.
|
|
33
41
|
|
|
34
|
-
ssl_context: ssl.SSLContext = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
42
|
+
# ssl_context: ssl.SSLContext = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
35
43
|
|
|
36
44
|
# # Enforce the use of TLS 1.2 only (disable TLS 1.0, TLS 1.1, and TLS 1.3)
|
|
37
45
|
# ssl_context.options |= ssl.OP_NO_TLSv1 # Disable TLS 1.0
|
|
38
46
|
# ssl_context.options |= ssl.OP_NO_TLSv1_1 # Disable TLS 1.1
|
|
39
47
|
# ssl_context.options |= ssl.OP_NO_TLSv1_3 # Disable TLS 1.3
|
|
40
48
|
|
|
41
|
-
#
|
|
49
|
+
# Correct factory for servers
|
|
50
|
+
ssl_context: ssl.SSLContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
51
|
+
|
|
52
|
+
# Modern default; relax only if you must
|
|
53
|
+
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
54
|
+
|
|
55
|
+
# Don't verify client certificates.
|
|
56
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
57
|
+
ssl_context.check_hostname = False
|
|
58
|
+
|
|
59
|
+
if enable_sslkeylogfile_env_to_client_ssl_context:
|
|
60
|
+
if sslkeylog_file_path is None:
|
|
61
|
+
sslkeylog_file_path = os.environ.get('SSLKEYLOGFILE')
|
|
62
|
+
|
|
63
|
+
if not os.path.exists(sslkeylog_file_path):
|
|
64
|
+
open(sslkeylog_file_path, "a").close()
|
|
65
|
+
|
|
66
|
+
ssl_context.keylog_filename = sslkeylog_file_path
|
|
67
|
+
|
|
68
|
+
# If you must support old clients that only offer TLS_RSA_* suites under OpenSSL 3:
|
|
69
|
+
if allow_legacy:
|
|
70
|
+
# This enables RSA key exchange and other legacy bits at security level 1
|
|
71
|
+
ssl_context.set_ciphers('DEFAULT:@SECLEVEL=1')
|
|
72
|
+
# If you truly have TLS 1.0/1.1 clients, uncomment the next line (not recommended):
|
|
73
|
+
ssl_context.minimum_version = ssl.TLSVersion.TLSv1
|
|
74
|
+
|
|
42
75
|
return ssl_context
|
|
43
76
|
|
|
44
77
|
|
|
45
78
|
def create_ssl_context_for_client(
|
|
46
|
-
enable_sslkeylogfile_env_to_client_ssl_context: bool = False
|
|
79
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
80
|
+
sslkeylog_file_path: str = None
|
|
47
81
|
) -> ssl.SSLContext:
|
|
48
82
|
"""
|
|
49
83
|
This function creates the SSL context for the client.
|
|
84
|
+
This means that your script will act like a client, and will connect to a server.
|
|
50
85
|
The SSL context is created with the "PROTOCOL_TLS_CLIENT" protocol.
|
|
51
86
|
|
|
52
87
|
:param enable_sslkeylogfile_env_to_client_ssl_context: boolean, enables the SSLKEYLOGFILE environment variable
|
|
@@ -56,15 +91,23 @@ def create_ssl_context_for_client(
|
|
|
56
91
|
This is useful for debugging SSL/TLS connections with WireShark.
|
|
57
92
|
Since WireShark also uses this environment variable to read the key log file and apply to the SSL/TLS
|
|
58
93
|
connections, so you can see the decrypted traffic.
|
|
94
|
+
:param sslkeylog_file_path: string, full file path for the SSL key log file. Default is None.
|
|
59
95
|
|
|
60
96
|
:return: ssl.SSLContext
|
|
61
97
|
"""
|
|
62
98
|
ssl_context: ssl.SSLContext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
63
99
|
|
|
64
100
|
if enable_sslkeylogfile_env_to_client_ssl_context:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
101
|
+
if sslkeylog_file_path is None:
|
|
102
|
+
sslkeylog_file_path = os.environ.get('SSLKEYLOGFILE')
|
|
103
|
+
|
|
104
|
+
if not os.path.exists(sslkeylog_file_path):
|
|
105
|
+
open(sslkeylog_file_path, "a").close()
|
|
106
|
+
|
|
107
|
+
ssl_context.keylog_filename = sslkeylog_file_path
|
|
108
|
+
|
|
109
|
+
current_ciphers = 'AES256-GCM-SHA384:' + ssl._DEFAULT_CIPHERS
|
|
110
|
+
ssl_context.set_ciphers(current_ciphers)
|
|
68
111
|
|
|
69
112
|
return ssl_context
|
|
70
113
|
|
|
@@ -138,9 +181,48 @@ def load_certificate_and_key_into_server_ssl_context(
|
|
|
138
181
|
print_api(message, error_type=True, logger_method="critical", **print_kwargs)
|
|
139
182
|
|
|
140
183
|
|
|
141
|
-
def
|
|
184
|
+
def copy_server_ctx_settings(src: ssl.SSLContext, dst: ssl.SSLContext) -> None:
|
|
185
|
+
# Versions & options
|
|
186
|
+
try: dst.minimum_version = src.minimum_version
|
|
187
|
+
except Exception: pass
|
|
188
|
+
try: dst.maximum_version = src.maximum_version
|
|
189
|
+
except Exception: pass
|
|
190
|
+
try: dst.options = src.options
|
|
191
|
+
except Exception: pass
|
|
192
|
+
|
|
193
|
+
# Verification knobs (server usually CERT_NONE unless you do mTLS)
|
|
194
|
+
try: dst.verify_mode = src.verify_mode
|
|
195
|
+
except Exception: pass
|
|
196
|
+
try: dst.check_hostname = src.check_hostname
|
|
197
|
+
except Exception: pass
|
|
198
|
+
|
|
199
|
+
# Cipher policy – replicate current enabled list
|
|
200
|
+
try:
|
|
201
|
+
cipher_names = ':'.join(c['name'] for c in src.get_ciphers())
|
|
202
|
+
if cipher_names:
|
|
203
|
+
dst.set_ciphers(cipher_names)
|
|
204
|
+
except Exception:
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
# (ALPN/curves/etc. don’t have public getters; set them the same way you set them on src, if applicable)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def create_server_ssl_context___load_certificate_and_key(
|
|
211
|
+
certificate_file_path: str,
|
|
212
|
+
key_file_path: str | None,
|
|
213
|
+
inherit_from: ssl.SSLContext | None = None,
|
|
214
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
215
|
+
sslkeylog_file_path: str = None,
|
|
216
|
+
) -> ssl.SSLContext:
|
|
142
217
|
# Create and set ssl context for server.
|
|
143
|
-
ssl_context: ssl.SSLContext = create_ssl_context_for_server(
|
|
218
|
+
ssl_context: ssl.SSLContext = create_ssl_context_for_server(
|
|
219
|
+
allow_legacy=True, enable_sslkeylogfile_env_to_client_ssl_context=enable_sslkeylogfile_env_to_client_ssl_context,
|
|
220
|
+
sslkeylog_file_path=sslkeylog_file_path)
|
|
221
|
+
|
|
222
|
+
# If you replaced contexts during SNI, copy policy from the old one
|
|
223
|
+
if inherit_from is not None:
|
|
224
|
+
copy_server_ctx_settings(inherit_from, ssl_context)
|
|
225
|
+
|
|
144
226
|
# Load certificate into context.
|
|
145
227
|
load_certificate_and_key_into_server_ssl_context(ssl_context, certificate_file_path, key_file_path)
|
|
146
228
|
# Return ssl context only.
|
|
@@ -231,7 +313,7 @@ def set_listen_on_socket(socket_object, **kwargs):
|
|
|
231
313
|
# To determine the maximum listening sockets, you may use the 'socket' library and 'SOMAXCONN' parameter
|
|
232
314
|
# from it.
|
|
233
315
|
socket_object.listen(socket.SOMAXCONN)
|
|
234
|
-
ip_address, port =
|
|
316
|
+
ip_address, port = socket_base.get_destination_address_from_socket(socket_object)
|
|
235
317
|
|
|
236
318
|
print_api(f"Listening for new connections on: {ip_address}:{port}", **kwargs)
|
|
237
319
|
|
|
@@ -243,8 +325,9 @@ def wrap_socket_with_ssl_context_client___default_certs___ignore_verification(
|
|
|
243
325
|
socket_object,
|
|
244
326
|
server_hostname: str = None,
|
|
245
327
|
custom_pem_client_certificate_file_path: str = None,
|
|
246
|
-
enable_sslkeylogfile_env_to_client_ssl_context: bool = False
|
|
247
|
-
|
|
328
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
329
|
+
sslkeylog_file_path: str = None
|
|
330
|
+
) -> ssl.SSLSocket:
|
|
248
331
|
"""
|
|
249
332
|
This function is a preset for wrapping the socket with SSL context for the client.
|
|
250
333
|
It sets the CA default certificates, and ignores the server's certificate verification.
|
|
@@ -255,9 +338,13 @@ def wrap_socket_with_ssl_context_client___default_certs___ignore_verification(
|
|
|
255
338
|
Default is None.
|
|
256
339
|
:param enable_sslkeylogfile_env_to_client_ssl_context: boolean, enables the SSLKEYLOGFILE environment variable
|
|
257
340
|
to the SSL context. Default is False.
|
|
341
|
+
:param sslkeylog_file_path: string, full file path for the SSL key log file. Default is None.
|
|
342
|
+
|
|
343
|
+
:return: ssl.SSLSocket object
|
|
258
344
|
"""
|
|
259
345
|
ssl_context: ssl.SSLContext = create_ssl_context_for_client(
|
|
260
|
-
enable_sslkeylogfile_env_to_client_ssl_context=enable_sslkeylogfile_env_to_client_ssl_context
|
|
346
|
+
enable_sslkeylogfile_env_to_client_ssl_context=enable_sslkeylogfile_env_to_client_ssl_context
|
|
347
|
+
,sslkeylog_file_path=sslkeylog_file_path)
|
|
261
348
|
set_client_ssl_context_ca_default_certs(ssl_context)
|
|
262
349
|
set_client_ssl_context_certificate_verification_ignore(ssl_context)
|
|
263
350
|
|
|
@@ -13,6 +13,7 @@ from ..loggingw import loggingw
|
|
|
13
13
|
from ..psutilw import psutil_networks
|
|
14
14
|
from ...basics import booleans, tracebacks
|
|
15
15
|
from ...file_io import csvs
|
|
16
|
+
from ... import networks
|
|
16
17
|
|
|
17
18
|
# noinspection PyPackageRequirements
|
|
18
19
|
import dnslib
|
|
@@ -302,6 +303,17 @@ class DnsServer:
|
|
|
302
303
|
time.sleep(1)
|
|
303
304
|
raise DnsConfigurationValuesError(e)
|
|
304
305
|
|
|
306
|
+
# If the listening interface is not localhost, check if the interface can be bound to.
|
|
307
|
+
if not self.listening_interface.startswith('127.'):
|
|
308
|
+
host_ips: list[str] = networks.get_host_ips_psutil(ipv6=False)
|
|
309
|
+
if self.listening_interface not in host_ips:
|
|
310
|
+
message = (f"Listening interface [{self.listening_interface}] is not assigned to any of the host "
|
|
311
|
+
f"network interfaces. Current host IPv4 addresses: {host_ips}")
|
|
312
|
+
print_api(f'DnsConfigurationValuesError: {str(message)}', error_type=True, color="red", logger=self.logger)
|
|
313
|
+
# Wait for the message to be printed and saved to file.
|
|
314
|
+
time.sleep(1)
|
|
315
|
+
raise DnsConfigurationValuesError(message)
|
|
316
|
+
|
|
305
317
|
ips_ports: list[str] = [f'{self.listening_interface}:{self.listening_port}']
|
|
306
318
|
port_in_use = psutil_networks.get_processes_using_port_list(ips_ports)
|
|
307
319
|
if port_in_use:
|
|
@@ -466,7 +478,7 @@ class DnsServer:
|
|
|
466
478
|
dns_cached_request = False
|
|
467
479
|
# Check if the received data request from client is already in the cache
|
|
468
480
|
if client_data in self.dns_questions_to_answers_cache:
|
|
469
|
-
# message = "!!!
|
|
481
|
+
# message = "!!! Request / Response is already in the dictionary..."
|
|
470
482
|
# self.logger.info(message)
|
|
471
483
|
|
|
472
484
|
# Get the response from the cached answers list
|
|
@@ -549,7 +561,7 @@ class DnsServer:
|
|
|
549
561
|
f'{self.offline_route_ipv6}')
|
|
550
562
|
)
|
|
551
563
|
|
|
552
|
-
message = f"!!!
|
|
564
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
553
565
|
f"{self.offline_route_ipv6}."
|
|
554
566
|
self.logger.info(message)
|
|
555
567
|
|
|
@@ -565,7 +577,7 @@ class DnsServer:
|
|
|
565
577
|
elif qtype_string == "SRV" or qtype_string == "SOA" or qtype_string == "HTTPS":
|
|
566
578
|
dns_built_response.add_answer(*RR.fromZone(self.offline_srv_answer))
|
|
567
579
|
|
|
568
|
-
message = f"!!!
|
|
580
|
+
message = f"!!! Request / Response is in offline mode returning: " \
|
|
569
581
|
f"{self.offline_srv_answer}."
|
|
570
582
|
self.logger.info(message)
|
|
571
583
|
elif qtype_string == "ANY":
|
|
@@ -574,7 +586,7 @@ class DnsServer:
|
|
|
574
586
|
self.offline_route_domain)
|
|
575
587
|
)
|
|
576
588
|
|
|
577
|
-
message = f"!!!
|
|
589
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
578
590
|
f"{self.offline_route_domain}."
|
|
579
591
|
self.logger.info(message)
|
|
580
592
|
else:
|
|
@@ -584,7 +596,7 @@ class DnsServer:
|
|
|
584
596
|
" " + self.offline_route_ipv4)
|
|
585
597
|
)
|
|
586
598
|
|
|
587
|
-
message = f"!!!
|
|
599
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
588
600
|
f"{self.offline_route_ipv4}."
|
|
589
601
|
self.logger.info(message)
|
|
590
602
|
# Values error means in most cases that you create wrong response
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ssl
|
|
2
2
|
import functools
|
|
3
3
|
|
|
4
|
-
from . import
|
|
4
|
+
from . import socket_base
|
|
5
5
|
from ...print_api import print_api
|
|
6
6
|
from ...inspect_wrapper import get_target_function_default_args_and_combine_with_current
|
|
7
7
|
|
|
@@ -75,18 +75,25 @@ def connection_exception_decorator(function_name):
|
|
|
75
75
|
pass
|
|
76
76
|
pass
|
|
77
77
|
except ssl.SSLError as exception_object:
|
|
78
|
-
|
|
79
|
-
if exception_object
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
excepted: bool = False
|
|
79
|
+
if getattr(exception_object, "reason", None):
|
|
80
|
+
# Getting the exact reason of "ssl.SSLError"
|
|
81
|
+
if exception_object.reason == "HTTP_REQUEST":
|
|
82
|
+
message = f"Socket Accept: HTTP Request on SSL Socket: " \
|
|
83
|
+
f"{socket_base.get_source_destination(kwargs['socket_object'])}"
|
|
84
|
+
wrapper_handle_connection_exceptions.message = message
|
|
85
|
+
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
86
|
+
|
|
87
|
+
excepted = True
|
|
88
|
+
elif exception_object.reason == "TSV1_ALERT_UNKNOWN_CA":
|
|
89
|
+
message = f"Socket Accept: Check CA certificate on the client " \
|
|
90
|
+
f"{socket_base.get_source_destination(kwargs['socket_object'])}"
|
|
91
|
+
wrapper_handle_connection_exceptions.message = message
|
|
92
|
+
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
93
|
+
|
|
94
|
+
excepted = True
|
|
95
|
+
|
|
96
|
+
if not excepted:
|
|
90
97
|
# Not all requests have the server name passed through Client Hello.
|
|
91
98
|
# If it is not passed an error of undefined variable will be raised.
|
|
92
99
|
# So, we'll check if the variable as a string is in the "locals()" variable pool.
|
|
@@ -102,21 +109,19 @@ def connection_exception_decorator(function_name):
|
|
|
102
109
|
f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
103
110
|
wrapper_handle_connection_exceptions.message = message
|
|
104
111
|
print_api(message, logger_method='error', oneline=True, **kwargs['print_kwargs'])
|
|
105
|
-
|
|
112
|
+
|
|
106
113
|
except FileNotFoundError:
|
|
107
114
|
message = "'SSLSocket.accept()' crashed: 'FileNotFoundError'. Some problem with SSL during Handshake - " \
|
|
108
115
|
"Could be certificate, client, or server."
|
|
109
116
|
message = f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
110
117
|
wrapper_handle_connection_exceptions.message = message
|
|
111
118
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
112
|
-
pass
|
|
113
119
|
except Exception as e:
|
|
114
120
|
_ = e
|
|
115
121
|
message = "Undocumented exception on accept."
|
|
116
122
|
message = f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
117
123
|
wrapper_handle_connection_exceptions.message = message
|
|
118
124
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
119
|
-
pass
|
|
120
125
|
|
|
121
126
|
wrapper_handle_connection_exceptions.message = None
|
|
122
127
|
return wrapper_handle_connection_exceptions
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from . import socket_base
|
|
4
|
+
from ...print_api import print_api
|
|
5
|
+
from ...ssh_remote import SSHRemote
|
|
6
|
+
from ... import package_mains_processor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
GET_LOCALHOST_FUNCTION_NAME = 'find_cmdline_by_port'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GetCommandLine:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
client_ip: str,
|
|
16
|
+
client_port: int = None,
|
|
17
|
+
package_processor: package_mains_processor.PackageMainsProcessor = None,
|
|
18
|
+
ssh_client: SSHRemote = None,
|
|
19
|
+
logger: logging.Logger = None
|
|
20
|
+
):
|
|
21
|
+
self.client_ip: str = client_ip
|
|
22
|
+
self.client_port: int = client_port
|
|
23
|
+
self.package_processor: package_mains_processor.PackageMainsProcessor = package_processor
|
|
24
|
+
self.ssh_client: SSHRemote = ssh_client
|
|
25
|
+
self.logger: logging.Logger = logger
|
|
26
|
+
|
|
27
|
+
def get_process_name(self, print_kwargs: dict = None):
|
|
28
|
+
if print_kwargs is None:
|
|
29
|
+
print_kwargs = {}
|
|
30
|
+
|
|
31
|
+
# Checking if we're on localhost. If not, we'll execute SSH connection to get calling process name.
|
|
32
|
+
if self.client_ip not in socket_base.THIS_DEVICE_IP_LIST:
|
|
33
|
+
# Tried using paramiko SSH concurrently within threads, but with bigger loads it just breaks.
|
|
34
|
+
# So, better using it separately for each thread.
|
|
35
|
+
|
|
36
|
+
print_api(f"Initializing SSH connection to [{self.client_ip}]", **print_kwargs)
|
|
37
|
+
|
|
38
|
+
script_string: str = self.package_processor.read_script_file_to_string()
|
|
39
|
+
|
|
40
|
+
execution_output, execution_error = self.ssh_client.connect_get_client_commandline(port=self.client_port, script_string=script_string)
|
|
41
|
+
# Else, if we're on localhost, then execute the script directly without SSH.
|
|
42
|
+
else:
|
|
43
|
+
print_api(f"Executing LOCALHOST command to get the calling process.", **print_kwargs)
|
|
44
|
+
# execution_output, execution_error, rc = self.package_processor.execute_script_with_subprocess(arguments=[str(client_port)])
|
|
45
|
+
execution_output = self.package_processor.execute_script_file(
|
|
46
|
+
function_name=GET_LOCALHOST_FUNCTION_NAME, args=(self.client_port,))
|
|
47
|
+
execution_error = None
|
|
48
|
+
|
|
49
|
+
# This section is generic for both remote SSH and localhost executions of the script.
|
|
50
|
+
process_name = self.get_commandline_and_error(execution_output, execution_error, print_kwargs=print_kwargs)
|
|
51
|
+
|
|
52
|
+
return process_name
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def get_commandline_and_error(
|
|
56
|
+
execution_output,
|
|
57
|
+
execution_error,
|
|
58
|
+
print_kwargs: dict = None
|
|
59
|
+
):
|
|
60
|
+
# If there was known error on localhost / known error on remote or any kind of error on remote, it was
|
|
61
|
+
# already logged, so we'll just put the error into 'process_name'.
|
|
62
|
+
if execution_error:
|
|
63
|
+
process_name = execution_error
|
|
64
|
+
print_api(
|
|
65
|
+
f"Error During Command Execution: {process_name}", error_type=True,
|
|
66
|
+
logger_method='error', **(print_kwargs or {}))
|
|
67
|
+
# If there wasn't any error of above types, then we can put the output from either local or remote script
|
|
68
|
+
# execution into 'process_name' and log it / output to console.
|
|
69
|
+
else:
|
|
70
|
+
# If the output that was returned is not empty.
|
|
71
|
+
if execution_output:
|
|
72
|
+
# Replacing '\r\n' escape lines with string, so that the line will not be escaped in logs.
|
|
73
|
+
if '\r\n' in execution_output:
|
|
74
|
+
execution_output = execution_output.replace('\r\n', '')
|
|
75
|
+
elif '\n' in execution_output:
|
|
76
|
+
execution_output = execution_output.replace('\n', '')
|
|
77
|
+
|
|
78
|
+
process_name = execution_output
|
|
79
|
+
print_api(f"Client Process Command Line: {process_name}", **(print_kwargs or {}))
|
|
80
|
+
# Else if the script output came back empty.
|
|
81
|
+
else:
|
|
82
|
+
process_name = ''
|
|
83
|
+
message = "Client Process Command Line came back empty after script execution."
|
|
84
|
+
print_api(message, error_type=True, logger_method='error', **(print_kwargs or {}))
|
|
85
|
+
|
|
86
|
+
return process_name
|
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import socket
|
|
3
3
|
import ssl
|
|
4
|
-
import time
|
|
5
4
|
|
|
6
5
|
import select
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
|
|
9
8
|
from ...print_api import print_api
|
|
9
|
+
from ...basics import tracebacks
|
|
10
10
|
from ..loggingw import loggingw
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def peek_first_bytes(
|
|
13
|
+
def peek_first_bytes(
|
|
14
|
+
client_socket,
|
|
15
|
+
bytes_amount: int = 1,
|
|
16
|
+
timeout: float = None
|
|
17
|
+
) -> bytes:
|
|
14
18
|
"""
|
|
15
19
|
Peek first byte from the socket without removing it from the buffer.
|
|
16
20
|
|
|
17
21
|
:param client_socket: Socket object.
|
|
18
22
|
:param bytes_amount: Amount of bytes to peek.
|
|
23
|
+
:param timeout: float, Timeout in seconds.
|
|
19
24
|
|
|
20
25
|
:return: the first X bytes from the socket buffer.
|
|
21
26
|
"""
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
error: bool = False
|
|
29
|
+
client_socket.settimeout(timeout)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
peek_a_bytes: bytes = client_socket.recv(bytes_amount, socket.MSG_PEEK)
|
|
33
|
+
except socket.timeout:
|
|
34
|
+
error = True
|
|
35
|
+
finally:
|
|
36
|
+
client_socket.settimeout(None)
|
|
37
|
+
|
|
38
|
+
if error:
|
|
39
|
+
raise TimeoutError
|
|
40
|
+
|
|
41
|
+
return peek_a_bytes
|
|
24
42
|
|
|
25
43
|
|
|
26
44
|
def is_socket_ready_for_read(socket_instance, timeout: float = 0) -> bool:
|
|
@@ -93,14 +111,16 @@ class Receiver:
|
|
|
93
111
|
# A signal to close connection will be empty bytes string: b''.
|
|
94
112
|
received_data = self.ssl_socket.recv(self.buffer_size_receive)
|
|
95
113
|
except ConnectionAbortedError:
|
|
96
|
-
error_message = "ConnectionAbortedError:
|
|
97
|
-
print_api(error_message, logger=self.logger, logger_method='critical', traceback_string=False)
|
|
114
|
+
error_message = "ConnectionAbortedError: Connection was aborted by local TCP stack (not remote close)..."
|
|
98
115
|
except ConnectionResetError:
|
|
99
|
-
error_message = "ConnectionResetError:
|
|
100
|
-
|
|
116
|
+
error_message = "ConnectionResetError: Connection was forcibly closed by the other side..."
|
|
117
|
+
except TimeoutError as e:
|
|
118
|
+
if e.errno == 10060:
|
|
119
|
+
error_message = "TimeoutError: [WinError 10060] Socket receive operation timed out..."
|
|
120
|
+
else:
|
|
121
|
+
raise e
|
|
101
122
|
except ssl.SSLError:
|
|
102
|
-
error_message = "ssl.SSLError:
|
|
103
|
-
print_api(error_message, logger=self.logger, logger_method='critical', traceback_string=True)
|
|
123
|
+
error_message = f"ssl.SSLError: Encountered SSL error on receive...\n{tracebacks.get_as_string()}"
|
|
104
124
|
|
|
105
125
|
if received_data == b'':
|
|
106
126
|
self.logger.info("Empty message received, socket closed on the other side.")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import socket
|
|
1
2
|
import ssl
|
|
2
3
|
import logging
|
|
3
4
|
from pathlib import Path
|
|
@@ -6,18 +7,18 @@ from ...print_api import print_api
|
|
|
6
7
|
from ..loggingw import loggingw
|
|
7
8
|
from ...basics import tracebacks
|
|
8
9
|
|
|
9
|
-
from . import
|
|
10
|
+
from . import socket_base
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Sender:
|
|
13
14
|
def __init__(
|
|
14
15
|
self,
|
|
15
|
-
ssl_socket: ssl.SSLSocket,
|
|
16
|
-
|
|
16
|
+
ssl_socket: ssl.SSLSocket | socket.socket,
|
|
17
|
+
bytes_to_send: bytes,
|
|
17
18
|
logger: logging.Logger = None
|
|
18
19
|
):
|
|
19
|
-
self.
|
|
20
|
-
self.ssl_socket: ssl.SSLSocket = ssl_socket
|
|
20
|
+
self.bytes_to_send: bytes = bytes_to_send
|
|
21
|
+
self.ssl_socket: ssl.SSLSocket | socket.socket = ssl_socket
|
|
21
22
|
|
|
22
23
|
if logger:
|
|
23
24
|
# Create child logger for the provided logger with the module's name.
|
|
@@ -42,7 +43,7 @@ class Sender:
|
|
|
42
43
|
|
|
43
44
|
try:
|
|
44
45
|
# Getting byte length of current message
|
|
45
|
-
current_message_length = len(self.
|
|
46
|
+
current_message_length = len(self.bytes_to_send)
|
|
46
47
|
|
|
47
48
|
self.logger.info(
|
|
48
49
|
f"Sending message to "
|
|
@@ -51,7 +52,7 @@ class Sender:
|
|
|
51
52
|
# Looping through "socket.send()" method while total sent bytes are less than message length
|
|
52
53
|
while total_sent_bytes < current_message_length:
|
|
53
54
|
# Sending the message and getting the amount of bytes sent
|
|
54
|
-
sent_bytes = self.ssl_socket.send(self.
|
|
55
|
+
sent_bytes = self.ssl_socket.send(self.bytes_to_send[total_sent_bytes:])
|
|
55
56
|
# If there were only "0" bytes sent, then connection on the other side was terminated
|
|
56
57
|
if sent_bytes == 0:
|
|
57
58
|
error_message = (
|
|
@@ -61,13 +62,13 @@ class Sender:
|
|
|
61
62
|
break
|
|
62
63
|
|
|
63
64
|
# Adding amount of currently sent bytes to the total amount of bytes sent
|
|
64
|
-
total_sent_bytes
|
|
65
|
+
total_sent_bytes += sent_bytes
|
|
65
66
|
self.logger.info(f"Sent {total_sent_bytes} bytes out of {current_message_length}")
|
|
66
67
|
|
|
67
68
|
# At this point the sending loop finished successfully
|
|
68
69
|
self.logger.info(f"Sent the message to destination.")
|
|
69
70
|
except Exception as e:
|
|
70
|
-
source_tuple, destination_tuple =
|
|
71
|
+
source_tuple, destination_tuple = socket_base.get_source_destination(self.ssl_socket)
|
|
71
72
|
source_address, source_port = source_tuple
|
|
72
73
|
destination_address, destination_port = destination_tuple
|
|
73
74
|
if self.ssl_socket.server_hostname:
|
|
@@ -47,7 +47,9 @@ class SNISetup:
|
|
|
47
47
|
tls: bool,
|
|
48
48
|
domain_from_dns_server: str = None,
|
|
49
49
|
skip_extension_id_list: list = None,
|
|
50
|
-
exceptions_logger: loggingw.ExceptionCsvLogger = None
|
|
50
|
+
exceptions_logger: loggingw.ExceptionCsvLogger = None,
|
|
51
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
52
|
+
sslkeylog_file_path: str = None
|
|
51
53
|
):
|
|
52
54
|
self.ca_certificate_name = ca_certificate_name
|
|
53
55
|
self.ca_certificate_filepath = ca_certificate_filepath
|
|
@@ -73,8 +75,9 @@ class SNISetup:
|
|
|
73
75
|
self.skip_extension_id_list = skip_extension_id_list
|
|
74
76
|
self.tls = tls
|
|
75
77
|
self.exceptions_logger = exceptions_logger
|
|
76
|
-
|
|
77
78
|
self.certificator_instance = None
|
|
79
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = enable_sslkeylogfile_env_to_client_ssl_context
|
|
80
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
78
81
|
|
|
79
82
|
def wrap_socket_with_ssl_context_server_sni_extended(
|
|
80
83
|
self,
|
|
@@ -83,7 +86,9 @@ class SNISetup:
|
|
|
83
86
|
):
|
|
84
87
|
|
|
85
88
|
# Create SSL Socket to wrap the raw socket with.
|
|
86
|
-
ssl_context: ssl.SSLContext = creator.create_ssl_context_for_server(
|
|
89
|
+
ssl_context: ssl.SSLContext = creator.create_ssl_context_for_server(
|
|
90
|
+
allow_legacy=True, enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
91
|
+
sslkeylog_file_path=self.sslkeylog_file_path)
|
|
87
92
|
|
|
88
93
|
self.certificator_instance = certificator.Certificator(
|
|
89
94
|
ca_certificate_name=self.ca_certificate_name,
|
|
@@ -102,7 +107,9 @@ class SNISetup:
|
|
|
102
107
|
forwarding_dns_service_ipv4_list___only_for_localhost=(
|
|
103
108
|
self.forwarding_dns_service_ipv4_list___only_for_localhost),
|
|
104
109
|
skip_extension_id_list=self.skip_extension_id_list,
|
|
105
|
-
tls=self.tls
|
|
110
|
+
tls=self.tls,
|
|
111
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
112
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
106
113
|
)
|
|
107
114
|
|
|
108
115
|
# Add SNI callback function to the SSL context.
|
|
@@ -150,7 +157,8 @@ class SNISetup:
|
|
|
150
157
|
# The function is actually called at "accept()" method of the "ssl.SSLSocket"
|
|
151
158
|
# This needs to be set only once on the listening socket
|
|
152
159
|
if self.sni_custom_callback_function:
|
|
153
|
-
ssl_context.sni_callback = self.sni_custom_callback_function
|
|
160
|
+
# ssl_context.sni_callback = self.sni_custom_callback_function
|
|
161
|
+
ssl_context.set_servername_callback(self.sni_custom_callback_function)
|
|
154
162
|
|
|
155
163
|
if self.sni_use_default_callback_function:
|
|
156
164
|
sni_handler_instance = SNIHandler(
|
|
@@ -160,8 +168,10 @@ class SNISetup:
|
|
|
160
168
|
certificator_instance=self.certificator_instance,
|
|
161
169
|
domain_from_dns_server=self.domain_from_dns_server,
|
|
162
170
|
default_certificate_domain_list=self.default_certificate_domain_list,
|
|
163
|
-
exceptions_logger=self.exceptions_logger
|
|
164
|
-
|
|
171
|
+
exceptions_logger=self.exceptions_logger,
|
|
172
|
+
enable_sslkeylogfile_env_to_client_ssl_context=(
|
|
173
|
+
self.certificator_instance.enable_sslkeylogfile_env_to_client_ssl_context),
|
|
174
|
+
sslkeylog_file_path=self.certificator_instance.sslkeylog_file_path)
|
|
165
175
|
ssl_context.set_servername_callback(
|
|
166
176
|
sni_handler_instance.setup_sni_callback(print_kwargs=print_kwargs))
|
|
167
177
|
|
|
@@ -179,7 +189,9 @@ class SNIHandler:
|
|
|
179
189
|
certificator_instance: certificator.Certificator,
|
|
180
190
|
domain_from_dns_server: str,
|
|
181
191
|
default_certificate_domain_list: list,
|
|
182
|
-
exceptions_logger: loggingw.ExceptionCsvLogger
|
|
192
|
+
exceptions_logger: loggingw.ExceptionCsvLogger,
|
|
193
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool,
|
|
194
|
+
sslkeylog_file_path: str
|
|
183
195
|
):
|
|
184
196
|
self.sni_use_default_callback_function_extended = sni_use_default_callback_function_extended
|
|
185
197
|
self.sni_add_new_domains_to_default_server_certificate = sni_add_new_domains_to_default_server_certificate
|
|
@@ -188,6 +200,8 @@ class SNIHandler:
|
|
|
188
200
|
self.domain_from_dns_server: str = domain_from_dns_server
|
|
189
201
|
self.default_certificate_domain_list = default_certificate_domain_list
|
|
190
202
|
self.exceptions_logger = exceptions_logger
|
|
203
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = enable_sslkeylogfile_env_to_client_ssl_context
|
|
204
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
191
205
|
|
|
192
206
|
# noinspection PyTypeChecker
|
|
193
207
|
self.sni_received_parameters: SNIReceivedParameters = None
|
|
@@ -322,8 +336,15 @@ class SNIHandler:
|
|
|
322
336
|
# Since new default certificate was created we need to create new SSLContext and add the certificate.
|
|
323
337
|
# You need to build new context and exchange the context that being inherited from the main socket,
|
|
324
338
|
# or else the context will receive previous certificate each time.
|
|
325
|
-
self.sni_received_parameters.ssl_socket.context =
|
|
326
|
-
creator.create_server_ssl_context___load_certificate_and_key(
|
|
339
|
+
self.sni_received_parameters.ssl_socket.context = (
|
|
340
|
+
creator.create_server_ssl_context___load_certificate_and_key(
|
|
341
|
+
default_server_certificate_path,
|
|
342
|
+
None,
|
|
343
|
+
inherit_from=self.sni_received_parameters.ssl_socket.context,
|
|
344
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
345
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
346
|
+
)
|
|
347
|
+
)
|
|
327
348
|
else:
|
|
328
349
|
message = f"Couldn't create / overwrite Default Server Certificate: {default_server_certificate_path}"
|
|
329
350
|
raise SNIDefaultCertificateCreationError(message)
|