atomicshop 3.3.28__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 +18 -43
- atomicshop/mitm/connection_thread_worker.py +376 -162
- 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 +79 -88
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +222 -121
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/networks.py +108 -93
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +115 -51
- atomicshop/web.py +20 -7
- 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/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +175 -63
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +17 -1
- 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 +30 -7
- atomicshop/wrappers/socketw/dns_server.py +6 -6
- atomicshop/wrappers/socketw/exception_wrapper.py +3 -3
- 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 +23 -6
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +6 -8
- atomicshop/wrappers/socketw/socket_wrapper.py +82 -21
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/RECORD +74 -88
- 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/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/ffmpegw.py +0 -125
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- /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.28.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -305,7 +305,7 @@ class DnsServer:
|
|
|
305
305
|
|
|
306
306
|
# If the listening interface is not localhost, check if the interface can be bound to.
|
|
307
307
|
if not self.listening_interface.startswith('127.'):
|
|
308
|
-
host_ips: list[str] = networks.
|
|
308
|
+
host_ips: list[str] = networks.get_host_ips_psutil(ipv6=False)
|
|
309
309
|
if self.listening_interface not in host_ips:
|
|
310
310
|
message = (f"Listening interface [{self.listening_interface}] is not assigned to any of the host "
|
|
311
311
|
f"network interfaces. Current host IPv4 addresses: {host_ips}")
|
|
@@ -478,7 +478,7 @@ class DnsServer:
|
|
|
478
478
|
dns_cached_request = False
|
|
479
479
|
# Check if the received data request from client is already in the cache
|
|
480
480
|
if client_data in self.dns_questions_to_answers_cache:
|
|
481
|
-
# message = "!!!
|
|
481
|
+
# message = "!!! Request / Response is already in the dictionary..."
|
|
482
482
|
# self.logger.info(message)
|
|
483
483
|
|
|
484
484
|
# Get the response from the cached answers list
|
|
@@ -561,7 +561,7 @@ class DnsServer:
|
|
|
561
561
|
f'{self.offline_route_ipv6}')
|
|
562
562
|
)
|
|
563
563
|
|
|
564
|
-
message = f"!!!
|
|
564
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
565
565
|
f"{self.offline_route_ipv6}."
|
|
566
566
|
self.logger.info(message)
|
|
567
567
|
|
|
@@ -577,7 +577,7 @@ class DnsServer:
|
|
|
577
577
|
elif qtype_string == "SRV" or qtype_string == "SOA" or qtype_string == "HTTPS":
|
|
578
578
|
dns_built_response.add_answer(*RR.fromZone(self.offline_srv_answer))
|
|
579
579
|
|
|
580
|
-
message = f"!!!
|
|
580
|
+
message = f"!!! Request / Response is in offline mode returning: " \
|
|
581
581
|
f"{self.offline_srv_answer}."
|
|
582
582
|
self.logger.info(message)
|
|
583
583
|
elif qtype_string == "ANY":
|
|
@@ -586,7 +586,7 @@ class DnsServer:
|
|
|
586
586
|
self.offline_route_domain)
|
|
587
587
|
)
|
|
588
588
|
|
|
589
|
-
message = f"!!!
|
|
589
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
590
590
|
f"{self.offline_route_domain}."
|
|
591
591
|
self.logger.info(message)
|
|
592
592
|
else:
|
|
@@ -596,7 +596,7 @@ class DnsServer:
|
|
|
596
596
|
" " + self.offline_route_ipv4)
|
|
597
597
|
)
|
|
598
598
|
|
|
599
|
-
message = f"!!!
|
|
599
|
+
message = f"!!! Request / Response is in offline mode returning " \
|
|
600
600
|
f"{self.offline_route_ipv4}."
|
|
601
601
|
self.logger.info(message)
|
|
602
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
|
|
|
@@ -80,14 +80,14 @@ def connection_exception_decorator(function_name):
|
|
|
80
80
|
# Getting the exact reason of "ssl.SSLError"
|
|
81
81
|
if exception_object.reason == "HTTP_REQUEST":
|
|
82
82
|
message = f"Socket Accept: HTTP Request on SSL Socket: " \
|
|
83
|
-
f"{
|
|
83
|
+
f"{socket_base.get_source_destination(kwargs['socket_object'])}"
|
|
84
84
|
wrapper_handle_connection_exceptions.message = message
|
|
85
85
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
86
86
|
|
|
87
87
|
excepted = True
|
|
88
88
|
elif exception_object.reason == "TSV1_ALERT_UNKNOWN_CA":
|
|
89
89
|
message = f"Socket Accept: Check CA certificate on the client " \
|
|
90
|
-
f"{
|
|
90
|
+
f"{socket_base.get_source_destination(kwargs['socket_object'])}"
|
|
91
91
|
wrapper_handle_connection_exceptions.message = message
|
|
92
92
|
print_api(message, logger_method='error', traceback_string=True, oneline=True, **kwargs['print_kwargs'])
|
|
93
93
|
|
|
@@ -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
|
|
@@ -74,6 +76,8 @@ class SNISetup:
|
|
|
74
76
|
self.tls = tls
|
|
75
77
|
self.exceptions_logger = exceptions_logger
|
|
76
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
|
|
77
81
|
|
|
78
82
|
def wrap_socket_with_ssl_context_server_sni_extended(
|
|
79
83
|
self,
|
|
@@ -82,7 +86,9 @@ class SNISetup:
|
|
|
82
86
|
):
|
|
83
87
|
|
|
84
88
|
# Create SSL Socket to wrap the raw socket with.
|
|
85
|
-
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)
|
|
86
92
|
|
|
87
93
|
self.certificator_instance = certificator.Certificator(
|
|
88
94
|
ca_certificate_name=self.ca_certificate_name,
|
|
@@ -101,7 +107,9 @@ class SNISetup:
|
|
|
101
107
|
forwarding_dns_service_ipv4_list___only_for_localhost=(
|
|
102
108
|
self.forwarding_dns_service_ipv4_list___only_for_localhost),
|
|
103
109
|
skip_extension_id_list=self.skip_extension_id_list,
|
|
104
|
-
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
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
# Add SNI callback function to the SSL context.
|
|
@@ -160,7 +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
|
|
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)
|
|
164
175
|
ssl_context.set_servername_callback(
|
|
165
176
|
sni_handler_instance.setup_sni_callback(print_kwargs=print_kwargs))
|
|
166
177
|
|
|
@@ -178,7 +189,9 @@ class SNIHandler:
|
|
|
178
189
|
certificator_instance: certificator.Certificator,
|
|
179
190
|
domain_from_dns_server: str,
|
|
180
191
|
default_certificate_domain_list: list,
|
|
181
|
-
exceptions_logger: loggingw.ExceptionCsvLogger
|
|
192
|
+
exceptions_logger: loggingw.ExceptionCsvLogger,
|
|
193
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool,
|
|
194
|
+
sslkeylog_file_path: str
|
|
182
195
|
):
|
|
183
196
|
self.sni_use_default_callback_function_extended = sni_use_default_callback_function_extended
|
|
184
197
|
self.sni_add_new_domains_to_default_server_certificate = sni_add_new_domains_to_default_server_certificate
|
|
@@ -187,6 +200,8 @@ class SNIHandler:
|
|
|
187
200
|
self.domain_from_dns_server: str = domain_from_dns_server
|
|
188
201
|
self.default_certificate_domain_list = default_certificate_domain_list
|
|
189
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
|
|
190
205
|
|
|
191
206
|
# noinspection PyTypeChecker
|
|
192
207
|
self.sni_received_parameters: SNIReceivedParameters = None
|
|
@@ -325,7 +340,9 @@ class SNIHandler:
|
|
|
325
340
|
creator.create_server_ssl_context___load_certificate_and_key(
|
|
326
341
|
default_server_certificate_path,
|
|
327
342
|
None,
|
|
328
|
-
inherit_from=self.sni_received_parameters.ssl_socket.context
|
|
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
|
|
329
346
|
)
|
|
330
347
|
)
|
|
331
348
|
else:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import socket
|
|
2
|
+
import time
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
LOCALHOST_IPV4: str = '127.0.0.1'
|
|
@@ -99,4 +100,35 @@ def get_host_name_from_ip_address(ip_address: str) -> str:
|
|
|
99
100
|
host_name, alias_list, ipaddr_list = socket.gethostbyaddr(ip_address)
|
|
100
101
|
_ = alias_list, ipaddr_list
|
|
101
102
|
|
|
102
|
-
return host_name
|
|
103
|
+
return host_name
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def wait_for_ip_bindable(
|
|
107
|
+
ip: str,
|
|
108
|
+
port: int = 0,
|
|
109
|
+
timeout: float = 15.0,
|
|
110
|
+
interval: float = 0.5,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Wait until a single IP is bindable (or timeout).
|
|
114
|
+
|
|
115
|
+
Raises TimeoutError if the IP cannot be bound within 'timeout' seconds.
|
|
116
|
+
"""
|
|
117
|
+
deadline = time.time() + timeout
|
|
118
|
+
last_err: OSError | None = None
|
|
119
|
+
|
|
120
|
+
while time.time() < deadline:
|
|
121
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
122
|
+
try:
|
|
123
|
+
s.bind((ip, port))
|
|
124
|
+
s.close()
|
|
125
|
+
return # success
|
|
126
|
+
except OSError as e:
|
|
127
|
+
last_err = e
|
|
128
|
+
s.close()
|
|
129
|
+
time.sleep(interval)
|
|
130
|
+
|
|
131
|
+
raise TimeoutError(
|
|
132
|
+
f"IP {ip} not bindable within {timeout} seconds; "
|
|
133
|
+
f"last error: {last_err}"
|
|
134
|
+
)
|
|
@@ -218,7 +218,6 @@ class SocketClient:
|
|
|
218
218
|
self.socket_instance = None
|
|
219
219
|
self.logger.info(f"Closed socket to service server [{self.service_name}:{self.service_port}]")
|
|
220
220
|
|
|
221
|
-
# noinspection PyUnusedLocal
|
|
222
221
|
def send_receive_to_service(
|
|
223
222
|
self,
|
|
224
223
|
request_bytes: Union[bytearray, bytes],
|
|
@@ -231,9 +230,8 @@ class SocketClient:
|
|
|
231
230
|
:param skip_send: If True, the data will not be sent to the service server. After the connection is established,
|
|
232
231
|
the function will wait for the response only.
|
|
233
232
|
"""
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
error_message = None
|
|
233
|
+
|
|
234
|
+
origin_data: bytes | None = None
|
|
237
235
|
|
|
238
236
|
service_socket, error_message = self.service_connection()
|
|
239
237
|
# If connection to service server wasn't successful
|
|
@@ -257,7 +255,7 @@ class SocketClient:
|
|
|
257
255
|
if not skip_send:
|
|
258
256
|
# Send the data received from the client to the service over socket
|
|
259
257
|
error_on_send = Sender(
|
|
260
|
-
ssl_socket=self.socket_instance,
|
|
258
|
+
ssl_socket=self.socket_instance, bytes_to_send=request_bytes, logger=self.logger).send()
|
|
261
259
|
|
|
262
260
|
# If the socket disconnected on data send
|
|
263
261
|
if error_on_send:
|
|
@@ -268,17 +266,17 @@ class SocketClient:
|
|
|
268
266
|
|
|
269
267
|
# Else if send was successful
|
|
270
268
|
if not error_on_send:
|
|
271
|
-
|
|
269
|
+
origin_data, is_socket_closed, error_message = Receiver(
|
|
272
270
|
ssl_socket=self.socket_instance, logger=self.logger).receive()
|
|
273
271
|
|
|
274
272
|
# If data received is empty meaning the socket was closed on the other side
|
|
275
|
-
if not
|
|
273
|
+
if not origin_data:
|
|
276
274
|
error_message = "Service server closed the connection on receive"
|
|
277
275
|
|
|
278
276
|
# We'll close the socket and nullify the object
|
|
279
277
|
self.close_socket()
|
|
280
278
|
|
|
281
|
-
return
|
|
279
|
+
return origin_data, error_message, self.connection_ip, self.socket_instance
|
|
282
280
|
|
|
283
281
|
def send_receive_message_list_with_interval(
|
|
284
282
|
self, requests_bytes_list: list, intervals_list: list, intervals_defaults: int, cycles: int = 1):
|