atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -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/a_mains/update_config_toml.py +38 -0
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/strings.py +72 -3
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -3
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +11 -6
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +1 -1
- atomicshop/diff_check.py +3 -3
- atomicshop/dns.py +128 -3
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/trace.py +40 -42
- atomicshop/etws/traces/trace_dns.py +56 -44
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +27 -5
- atomicshop/file_io/docxs.py +34 -17
- atomicshop/file_io/file_io.py +31 -17
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +616 -291
- atomicshop/get_process_list.py +3 -3
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -80
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +136 -40
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +39 -9
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -42
- atomicshop/process.py +24 -6
- atomicshop/process_poller/process_pool.py +0 -1
- atomicshop/process_poller/simple_process_pool.py +204 -5
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/system_resource_monitor.py +61 -47
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/urls.py +6 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +98 -27
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/githubw.py +537 -54
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +214 -107
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +860 -22
- atomicshop/wrappers/loggingw/reading.py +134 -112
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +37 -1
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/cert_store.py +116 -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/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +491 -182
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +1 -1
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/pycharm.py +0 -9
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
- atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- 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/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/config_editor.py +0 -37
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -268
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -151
- atomicshop/script_as_string_processor.py +0 -38
- 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 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/install.py +0 -81
- atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
- atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
- atomicshop/wrappers/msiw.py +0 -149
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/psutilw/networks.py +0 -45
- atomicshop/wrappers/pycharmw.py +0 -81
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.15.11.dist-info/RECORD +0 -302
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /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/{archiver → permissions}/__init__.py +0 -0
- /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
- /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
|
@@ -14,7 +14,13 @@ def connection_exception_decorator(function_name):
|
|
|
14
14
|
args, kwargs = get_target_function_default_args_and_combine_with_current(function_name, *args, **kwargs)
|
|
15
15
|
|
|
16
16
|
wrapper_handle_connection_exceptions.message = None
|
|
17
|
-
port = kwargs['socket_object'].getsockname()
|
|
17
|
+
listen_ipv4, port = kwargs['socket_object'].getsockname()
|
|
18
|
+
|
|
19
|
+
domain_from_dns_server = kwargs['domain_from_dns_server']
|
|
20
|
+
if not domain_from_dns_server:
|
|
21
|
+
# If the domain is not passed, we will use the TCP data.
|
|
22
|
+
# This is needed for the decorator to work properly.
|
|
23
|
+
domain_from_dns_server = listen_ipv4
|
|
18
24
|
|
|
19
25
|
try:
|
|
20
26
|
# Since our 'kwargs' has already all the needed arguments, we don't need 'args'.
|
|
@@ -24,17 +30,15 @@ def connection_exception_decorator(function_name):
|
|
|
24
30
|
# After that second exception will be "pass"-ed. This is an exception inside an exception handling.
|
|
25
31
|
# Looks like was introduced in Python 3 in PEP 3134.
|
|
26
32
|
except ConnectionAbortedError:
|
|
27
|
-
message = f"Socket Accept: {
|
|
33
|
+
message = f"Socket Accept: {domain_from_dns_server}:{port}: " \
|
|
28
34
|
f"* Established connection was aborted by software on the host..."
|
|
29
35
|
wrapper_handle_connection_exceptions.message = message
|
|
30
36
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
31
|
-
pass
|
|
32
37
|
except ConnectionResetError:
|
|
33
|
-
message = f"Socket Accept: {
|
|
38
|
+
message = f"Socket Accept: {domain_from_dns_server}:{port}: " \
|
|
34
39
|
f"* An existing connection was forcibly closed by the remote host..."
|
|
35
40
|
wrapper_handle_connection_exceptions.message = message
|
|
36
41
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
37
|
-
pass
|
|
38
42
|
except ssl.SSLEOFError as e:
|
|
39
43
|
# A subclass of SSLError raised when the SSL connection has been terminated abruptly. Generally, you
|
|
40
44
|
# shouldn't try to reuse the underlying transport when this error is encountered.
|
|
@@ -45,10 +49,11 @@ def connection_exception_decorator(function_name):
|
|
|
45
49
|
wrapper_handle_connection_exceptions.message = message
|
|
46
50
|
try:
|
|
47
51
|
message = \
|
|
48
|
-
f"Socket Accept: {
|
|
52
|
+
f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
49
53
|
wrapper_handle_connection_exceptions.message = message
|
|
50
|
-
print_api(message, error_type=True, logger_method='error', **kwargs['print_kwargs'])
|
|
51
|
-
except Exception:
|
|
54
|
+
print_api(message, error_type=True, logger_method='error', oneline=True, **kwargs['print_kwargs'])
|
|
55
|
+
except Exception as e:
|
|
56
|
+
_ = e
|
|
52
57
|
message = f"Socket Accept: port {port}: {message}"
|
|
53
58
|
wrapper_handle_connection_exceptions.message = message
|
|
54
59
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
@@ -59,36 +64,36 @@ def connection_exception_decorator(function_name):
|
|
|
59
64
|
wrapper_handle_connection_exceptions.message = message
|
|
60
65
|
try:
|
|
61
66
|
message = \
|
|
62
|
-
f"Socket Accept: {
|
|
67
|
+
f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
63
68
|
wrapper_handle_connection_exceptions.message = message
|
|
64
|
-
print_api(message, logger_method='error', **kwargs['print_kwargs'])
|
|
65
|
-
except Exception:
|
|
69
|
+
print_api(message, logger_method='error', oneline=True, **kwargs['print_kwargs'])
|
|
70
|
+
except Exception as e:
|
|
71
|
+
_ = e
|
|
66
72
|
message = f"Socket Accept: port {port}: {message}"
|
|
67
73
|
wrapper_handle_connection_exceptions.message = message
|
|
68
74
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
69
75
|
pass
|
|
70
76
|
pass
|
|
71
77
|
except ssl.SSLError as exception_object:
|
|
72
|
-
|
|
73
|
-
if exception_object
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else:
|
|
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:
|
|
92
97
|
# Not all requests have the server name passed through Client Hello.
|
|
93
98
|
# If it is not passed an error of undefined variable will be raised.
|
|
94
99
|
# So, we'll check if the variable as a string is in the "locals()" variable pool.
|
|
@@ -96,39 +101,27 @@ def connection_exception_decorator(function_name):
|
|
|
96
101
|
|
|
97
102
|
message = "SSLError on accept. Not documented..."
|
|
98
103
|
wrapper_handle_connection_exceptions.message = message
|
|
99
|
-
print_api(message, logger_method='error', **kwargs['print_kwargs'])
|
|
100
|
-
# try:
|
|
101
|
-
# message = f"Socket Accept: {domain_name}:{socket_object.getsockname()[1]}: {message}"
|
|
102
|
-
# print_api(message, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
103
|
-
# except Exception:
|
|
104
|
-
# message = f"Socket Accept: port {socket_object.getsockname()[1]}: {message}"
|
|
105
|
-
# print_api(message, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
106
|
-
# pass
|
|
104
|
+
print_api(message, logger_method='error', oneline=True, **kwargs['print_kwargs'])
|
|
107
105
|
|
|
108
106
|
message = f'ssl.SSLError:{exception_object}'
|
|
109
107
|
wrapper_handle_connection_exceptions.message = message
|
|
110
108
|
message = \
|
|
111
|
-
f"Socket Accept: {
|
|
109
|
+
f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
112
110
|
wrapper_handle_connection_exceptions.message = message
|
|
113
|
-
print_api(message, logger_method='error', **kwargs['print_kwargs'])
|
|
114
|
-
|
|
111
|
+
print_api(message, logger_method='error', oneline=True, **kwargs['print_kwargs'])
|
|
112
|
+
|
|
115
113
|
except FileNotFoundError:
|
|
116
114
|
message = "'SSLSocket.accept()' crashed: 'FileNotFoundError'. Some problem with SSL during Handshake - " \
|
|
117
115
|
"Could be certificate, client, or server."
|
|
118
|
-
message = f"Socket Accept: {
|
|
116
|
+
message = f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
119
117
|
wrapper_handle_connection_exceptions.message = message
|
|
120
118
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# print_api(message, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
124
|
-
# pass
|
|
125
|
-
pass
|
|
126
|
-
except Exception:
|
|
119
|
+
except Exception as e:
|
|
120
|
+
_ = e
|
|
127
121
|
message = "Undocumented exception on accept."
|
|
128
|
-
message = f"Socket Accept: {
|
|
122
|
+
message = f"Socket Accept: {domain_from_dns_server}:{port}: {message}"
|
|
129
123
|
wrapper_handle_connection_exceptions.message = message
|
|
130
124
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
131
|
-
pass
|
|
132
125
|
|
|
133
126
|
wrapper_handle_connection_exceptions.message = None
|
|
134
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,28 +1,75 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import socket
|
|
2
3
|
import ssl
|
|
3
4
|
|
|
5
|
+
import select
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
4
8
|
from ...print_api import print_api
|
|
9
|
+
from ...basics import tracebacks
|
|
5
10
|
from ..loggingw import loggingw
|
|
6
11
|
|
|
7
12
|
|
|
8
|
-
def peek_first_bytes(
|
|
13
|
+
def peek_first_bytes(
|
|
14
|
+
client_socket,
|
|
15
|
+
bytes_amount: int = 1,
|
|
16
|
+
timeout: float = None
|
|
17
|
+
) -> bytes:
|
|
9
18
|
"""
|
|
10
19
|
Peek first byte from the socket without removing it from the buffer.
|
|
11
20
|
|
|
12
21
|
:param client_socket: Socket object.
|
|
13
22
|
:param bytes_amount: Amount of bytes to peek.
|
|
23
|
+
:param timeout: float, Timeout in seconds.
|
|
14
24
|
|
|
15
25
|
:return: the first X bytes from the socket buffer.
|
|
16
26
|
"""
|
|
17
27
|
|
|
18
|
-
|
|
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
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_socket_ready_for_read(socket_instance, timeout: float = 0) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Check if socket is ready for read.
|
|
47
|
+
|
|
48
|
+
:param socket_instance: Socket object.
|
|
49
|
+
:param timeout: Timeout in seconds. The default is no timeout.
|
|
50
|
+
|
|
51
|
+
:return: True if socket is ready for read, False otherwise.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Check if the socket is closed.
|
|
55
|
+
if socket_instance.fileno() == -1:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
# Use select to check if the socket is ready for reading.
|
|
59
|
+
# 'readable' returns a list of sockets that are ready for reading.
|
|
60
|
+
# Since we use only one socket, it will return a list with one element if the socket is ready for reading,
|
|
61
|
+
# or an empty list if the socket is not ready for reading.
|
|
62
|
+
readable, _, _ = select.select([socket_instance], [], [], timeout)
|
|
63
|
+
return bool(readable)
|
|
19
64
|
|
|
20
65
|
|
|
21
66
|
class Receiver:
|
|
22
67
|
""" Receiver Class is responsible for receiving the message from socket and populate the message class """
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
ssl_socket: ssl.SSLSocket,
|
|
71
|
+
logger: logging.Logger = None
|
|
72
|
+
):
|
|
26
73
|
self.ssl_socket: ssl.SSLSocket = ssl_socket
|
|
27
74
|
self.buffer_size_receive: int = 16384
|
|
28
75
|
# Timeout of 2 is enough for regular HTTP sessions`.
|
|
@@ -39,130 +86,125 @@ class Receiver:
|
|
|
39
86
|
# Will get client Local port from the socket
|
|
40
87
|
self.class_client_local_port: int = int()
|
|
41
88
|
|
|
89
|
+
if logger:
|
|
90
|
+
# Create child logger for the provided logger with the module's name.
|
|
91
|
+
self.logger: logging.Logger = loggingw.get_logger_with_level(f'{logger.name}.{Path(__file__).stem}')
|
|
92
|
+
else:
|
|
93
|
+
self.logger: logging.Logger = logger
|
|
94
|
+
|
|
42
95
|
# Function to receive only the buffer, with error handling
|
|
43
|
-
def
|
|
96
|
+
def chunk_from_buffer(self) -> tuple[bytes, str]:
|
|
97
|
+
"""
|
|
98
|
+
Receive a chunk from the socket buffer.
|
|
99
|
+
|
|
100
|
+
:return: Tuple(received chunk binary bytes data, error message string).
|
|
101
|
+
"""
|
|
44
102
|
# Defining the data variable
|
|
45
|
-
|
|
103
|
+
# noinspection PyTypeChecker
|
|
104
|
+
received_data: bytes = None
|
|
105
|
+
# noinspection PyTypeChecker
|
|
106
|
+
error_message: str = None
|
|
46
107
|
|
|
108
|
+
# All excepts will be treated as empty message, indicate that socket was closed and will be handled properly.
|
|
47
109
|
try:
|
|
48
110
|
# "recv(byte buffer size)" to read the server's response.
|
|
49
|
-
|
|
111
|
+
# A signal to close connection will be empty bytes string: b''.
|
|
112
|
+
received_data = self.ssl_socket.recv(self.buffer_size_receive)
|
|
50
113
|
except ConnectionAbortedError:
|
|
51
|
-
|
|
52
|
-
print_api(message, logger=self.logger, logger_method='critical', traceback_string=True, oneline=True)
|
|
53
|
-
# This will be treated as empty message - indicate that socket was closed and will be handled properly.
|
|
54
|
-
pass
|
|
114
|
+
error_message = "ConnectionAbortedError: Connection was aborted by local TCP stack (not remote close)..."
|
|
55
115
|
except ConnectionResetError:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
60
122
|
except ssl.SSLError:
|
|
61
|
-
|
|
62
|
-
print_api(message, logger=self.logger, logger_method='critical', traceback_string=True, oneline=True)
|
|
63
|
-
# This will be treated as empty message - indicate that socket was closed and will be handled properly.
|
|
64
|
-
pass
|
|
123
|
+
error_message = f"ssl.SSLError: Encountered SSL error on receive...\n{tracebacks.get_as_string()}"
|
|
65
124
|
|
|
66
|
-
if
|
|
125
|
+
if received_data == b'':
|
|
67
126
|
self.logger.info("Empty message received, socket closed on the other side.")
|
|
68
127
|
|
|
69
|
-
return
|
|
128
|
+
return received_data, error_message
|
|
70
129
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# ended and the message is longer than the receiving buffer.
|
|
75
|
-
# So we need to set the timeout for the client socket.
|
|
76
|
-
# If you set "timeout" on the listening main socket, it is not inherited to the child socket when client
|
|
77
|
-
# connected, so you need to set it for the new socket as well.
|
|
78
|
-
# Each function need to set the timeout independently
|
|
79
|
-
self.ssl_socket.settimeout(self.socket_timeout)
|
|
80
|
-
# variable that is responsible to retry over the same receive session if packet is less than buffer
|
|
81
|
-
partial_data_received: bool = False
|
|
130
|
+
def socket_receive_message_full(self) -> tuple[bytes, bool, str]:
|
|
131
|
+
"""
|
|
132
|
+
Receive the full message from the socket.
|
|
82
133
|
|
|
134
|
+
:return: Tuple(full data binary bytes, is socket closed boolean, error message string).
|
|
135
|
+
"""
|
|
83
136
|
# Define the variable that is going to aggregate the whole data received
|
|
84
|
-
|
|
85
|
-
#
|
|
86
|
-
|
|
137
|
+
full_data: bytes = bytes()
|
|
138
|
+
# noinspection PyTypeChecker
|
|
139
|
+
error_message: str = None
|
|
87
140
|
|
|
88
141
|
# Infinite loop to accept data from the client
|
|
142
|
+
# We'll skip the 'is_socket_ready_for_read' check on the first run, since we want to read the data anyway,
|
|
143
|
+
# to leave the socket in the blocking mode.
|
|
144
|
+
first_run: bool = True
|
|
89
145
|
while True:
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
146
|
+
# Check if there is data to be read from the socket.
|
|
147
|
+
is_there_data: bool = is_socket_ready_for_read(self.ssl_socket, timeout=0.5)
|
|
148
|
+
|
|
149
|
+
# noinspection PyTypeChecker
|
|
150
|
+
if is_there_data or first_run:
|
|
151
|
+
first_run = False
|
|
152
|
+
# Receive the data from the socket.
|
|
153
|
+
received_chunk, error_message = self.chunk_from_buffer()
|
|
154
|
+
received_chunk: bytes
|
|
155
|
+
error_message: str
|
|
156
|
+
|
|
157
|
+
# And if the message received is not empty then aggregate it to the main "data received" variable
|
|
158
|
+
if received_chunk != b'' and received_chunk is not None:
|
|
159
|
+
full_data += received_chunk
|
|
160
|
+
|
|
161
|
+
self.logger.info(f"Received packet bytes: [{len(received_chunk)}] | "
|
|
162
|
+
f"Total aggregated bytes: [{len(full_data)}]")
|
|
163
|
+
|
|
164
|
+
elif received_chunk == b'' or received_chunk is None:
|
|
165
|
+
# If there received_chunk is None, this means that the socket was closed,
|
|
166
|
+
# since it is a connection error.
|
|
167
|
+
# Same goes for the empty message.
|
|
168
|
+
is_socket_closed = True
|
|
107
169
|
break
|
|
108
|
-
else:
|
|
109
|
-
self.logger.info(
|
|
110
|
-
# Dividing number of seconds by 60 to get minutes
|
|
111
|
-
f"{self.socket_timeout/60} minutes timeout reached on 'socket.recv()' - no data received from "
|
|
112
|
-
f"{self.class_client_address}:{self.class_client_local_port}. Still waiting...")
|
|
113
|
-
# Pass the exception
|
|
114
|
-
pass
|
|
115
|
-
# Changing the socket timeout back to none since receiving operation has been finished.
|
|
116
|
-
self.ssl_socket.settimeout(None)
|
|
117
|
-
# Continue to the next iteration inside while
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
# And if the message received is not empty then aggregate it to the main "data received" variable
|
|
121
|
-
if class_data_received:
|
|
122
|
-
class_data.extend(class_data_received)
|
|
123
|
-
|
|
124
|
-
self.logger.info(f"Received packet with {len(class_data_received)} bytes. "
|
|
125
|
-
f"Current aggregated request of total: {len(class_data)} bytes")
|
|
126
|
-
|
|
127
|
-
# If the first received session is less than the buffer size, then the full message was received
|
|
128
|
-
if len(class_data_received) < self.buffer_size_receive:
|
|
129
|
-
# Since we already received some data from the other side, the retried variable will be true
|
|
130
|
-
partial_data_received = True
|
|
131
|
-
self.logger.info(f"Receiving the buffer again...")
|
|
132
|
-
|
|
133
|
-
# In this case the socket timeout will be 2 seconds to wait for more packets.
|
|
134
|
-
# If there are no more packets, receiver will end its activity and pass the message to
|
|
135
|
-
# the rest of the components in the network chain
|
|
136
|
-
if self.socket_timeout != 0.5:
|
|
137
|
-
self.socket_timeout = 0.5
|
|
138
|
-
self.ssl_socket.settimeout(self.socket_timeout)
|
|
139
|
-
self.logger.info(f"Timeout changed to {self.socket_timeout} seconds")
|
|
140
|
-
|
|
141
|
-
# Continue to the next receive on the socket
|
|
142
|
-
continue
|
|
143
170
|
else:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
f"client in the next cycle - empty message will be received again, and current "
|
|
148
|
-
f"socket will be finally closed.")
|
|
171
|
+
# If there is no data to be read from the socket, it doesn't mean that the socket is closed.
|
|
172
|
+
is_socket_closed = False
|
|
173
|
+
received_chunk = None
|
|
149
174
|
break
|
|
150
175
|
|
|
151
|
-
|
|
176
|
+
if full_data:
|
|
177
|
+
self.logger.info(f"Received total: [{len(full_data)}] bytes")
|
|
178
|
+
|
|
179
|
+
# In case the full data is empty, and the received chunk is None, it doesn't mean that the socket is closed.
|
|
180
|
+
# But it means that there was no data to be read from the socket, because of error or timeout.
|
|
181
|
+
if full_data == b'' and received_chunk is None:
|
|
182
|
+
full_data = None
|
|
183
|
+
|
|
184
|
+
return full_data, is_socket_closed, error_message
|
|
185
|
+
|
|
186
|
+
def receive(self) -> tuple[bytes, bool, str]:
|
|
187
|
+
"""
|
|
188
|
+
Receive the message from the socket.
|
|
152
189
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
190
|
+
:return: Tuple(
|
|
191
|
+
data binary bytes,
|
|
192
|
+
is socket closed boolean,
|
|
193
|
+
error message string if there was a connection exception).
|
|
194
|
+
"""
|
|
195
|
+
# Getting client address and Local port from the socket
|
|
156
196
|
self.class_client_address = self.ssl_socket.getpeername()[0]
|
|
157
|
-
# Getting client Local port from the socket
|
|
158
197
|
self.class_client_local_port = self.ssl_socket.getpeername()[1]
|
|
159
198
|
|
|
160
199
|
# Receiving data from the socket and closing the socket if send is finished.
|
|
161
200
|
self.logger.info(f"Waiting for data from {self.class_client_address}:{self.class_client_local_port}")
|
|
162
|
-
|
|
201
|
+
socket_data_bytes, is_socket_closed, error_message = self.socket_receive_message_full()
|
|
202
|
+
socket_data_bytes: bytes
|
|
203
|
+
is_socket_closed: bool
|
|
204
|
+
error_message: str
|
|
163
205
|
|
|
164
|
-
if
|
|
206
|
+
if socket_data_bytes:
|
|
165
207
|
# Put only 100 characters to the log, since we record the message any way in full - later.
|
|
166
|
-
self.logger.info(f"Received: {
|
|
208
|
+
self.logger.info(f"Received: {socket_data_bytes[0: 100]}...")
|
|
167
209
|
|
|
168
|
-
return
|
|
210
|
+
return socket_data_bytes, is_socket_closed, error_message
|