atomicshop 3.2.13__py3-none-any.whl → 3.3.1__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/basics/multiprocesses.py +67 -0
- atomicshop/config_init.py +7 -2
- atomicshop/mitm/config_static.py +6 -3
- atomicshop/mitm/connection_thread_worker.py +11 -4
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +21 -0
- atomicshop/mitm/import_config.py +12 -6
- atomicshop/mitm/initialize_engines.py +15 -7
- atomicshop/mitm/mitm_main.py +268 -150
- atomicshop/wrappers/loggingw/loggingw.py +464 -33
- atomicshop/wrappers/socketw/dns_server.py +28 -10
- atomicshop/wrappers/socketw/socket_client.py +7 -0
- atomicshop/wrappers/socketw/socket_wrapper.py +164 -172
- atomicshop/wrappers/socketw/statistics_csv.py +32 -13
- atomicshop/wrappers/ubuntu_terminal.py +11 -2
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/METADATA +1 -1
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/RECORD +21 -21
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/LICENSE.txt +0 -0
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/WHEEL +0 -0
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/entry_points.txt +0 -0
- {atomicshop-3.2.13.dist-info → atomicshop-3.3.1.dist-info}/top_level.txt +0 -0
|
@@ -160,6 +160,13 @@ class SocketClient:
|
|
|
160
160
|
f"Domain {self.service_name} doesn't exist - Couldn't resolve with {self.dns_servers_list}.")
|
|
161
161
|
print_api.print_api(error_string, logger=self.logger, logger_method='error')
|
|
162
162
|
return None, error_string
|
|
163
|
+
except dns.resolver.LifetimeTimeout as e:
|
|
164
|
+
exception_type: str = type(e).__name__
|
|
165
|
+
error_string = (
|
|
166
|
+
f"Socket Client Connect: {exception_type}: "
|
|
167
|
+
f"Timeout while resolving domain {self.service_name} with {self.dns_servers_list}.")
|
|
168
|
+
print_api.print_api(error_string, logger=self.logger, logger_method='error')
|
|
169
|
+
return None, error_string
|
|
163
170
|
|
|
164
171
|
# If DNS was resolved correctly or DNS servers weren't specified - we can try connecting.
|
|
165
172
|
# If 'connection_ip' was manually specified or resolved with 'dnspython' - the connection
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import multiprocessing
|
|
2
2
|
import threading
|
|
3
3
|
import select
|
|
4
4
|
from typing import Literal, Union
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
import logging
|
|
7
6
|
import socket
|
|
8
7
|
|
|
9
8
|
from ...mitm import initialize_engines
|
|
@@ -12,7 +11,7 @@ from ..certauthw import certauthw
|
|
|
12
11
|
from ..loggingw import loggingw
|
|
13
12
|
from ...script_as_string_processor import ScriptAsStringProcessor
|
|
14
13
|
from ...permissions import permissions
|
|
15
|
-
from ... import
|
|
14
|
+
from ... import filesystem, certificates
|
|
16
15
|
from ...basics import booleans
|
|
17
16
|
from ...print_api import print_api
|
|
18
17
|
|
|
@@ -27,12 +26,17 @@ class SocketWrapperConfigurationValuesError(Exception):
|
|
|
27
26
|
pass
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
# from ... import queues
|
|
30
|
+
# SNI_QUEUE = queues.NonBlockQueue()
|
|
31
|
+
LOGS_DIRECTORY_NAME: str = 'logs'
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class SocketWrapper:
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
37
|
+
ip_address: str,
|
|
38
|
+
port: int,
|
|
39
|
+
engine: initialize_engines.ModuleCategory = None,
|
|
36
40
|
forwarding_dns_service_ipv4_list___only_for_localhost: list = None,
|
|
37
41
|
ca_certificate_name: str = None,
|
|
38
42
|
ca_certificate_filepath: str = None,
|
|
@@ -65,9 +69,13 @@ class SocketWrapper:
|
|
|
65
69
|
],
|
|
66
70
|
None
|
|
67
71
|
] = None,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
logs_directory: str = None,
|
|
73
|
+
logger_name: str = 'SocketWrapper',
|
|
74
|
+
logger_queue: multiprocessing.Queue = None,
|
|
75
|
+
statistics_logger_name: str = 'statistics',
|
|
76
|
+
statistics_logger_queue: multiprocessing.Queue = None,
|
|
77
|
+
exceptions_logger_name: str = 'SocketWrapperExceptions',
|
|
78
|
+
exceptions_logger_queue: multiprocessing.Queue = None,
|
|
71
79
|
no_engine_usage_enable: bool = False,
|
|
72
80
|
no_engines_listening_address_list: list[str] = None,
|
|
73
81
|
engines_list: list[initialize_engines.ModuleCategory] = None
|
|
@@ -145,21 +153,24 @@ class SocketWrapper:
|
|
|
145
153
|
:param ssh_user: string, SSH username that will be used to connect to remote host.
|
|
146
154
|
:param ssh_pass: string, SSH password that will be used to connect to remote host.
|
|
147
155
|
:param ssh_script_to_execute: string, script that will be executed to get the process name on ssh remote host.
|
|
148
|
-
:param
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
:param exceptions_logger: loggingw.ExceptionCsvLogger object, logger object that will be used to log exceptions.
|
|
152
|
-
If not provided, logger will be created with default settings and will save exceptions to the
|
|
153
|
-
'statistics_logs_directory'.
|
|
154
|
-
:param statistics_logs_directory: string, path to directory where daily statistics.csv files will be stored.
|
|
155
|
-
After you initialize the SocketWrapper object, you can get the statistics_writer object from it and use it
|
|
156
|
-
to write statistics to the file in a worker thread.
|
|
156
|
+
:param logs_directory: string, path to directory where daily statistics.csv files and all the other logger
|
|
157
|
+
files will be stored. After you initialize the SocketWrapper object, you can get the statistics_writer
|
|
158
|
+
object from it and use it to write statistics to the file in a worker thread.
|
|
157
159
|
|
|
158
160
|
socket_wrapper_instance = SocketWrapper(...)
|
|
159
161
|
statistics_writer = socket_wrapper_instance.statistics_writer
|
|
160
162
|
|
|
161
163
|
statistics_writer: statistics_csv.StatisticsCSVWriter object, there is a logger object that
|
|
162
164
|
will be used to write the statistics file.
|
|
165
|
+
:param logger_name: string, name of the logger that will be used to log messages.
|
|
166
|
+
:param logger_queue: multiprocessing.Queue, queue that will be used to log messages in multiprocessing.
|
|
167
|
+
You need to start the logger listener in the main process to handle the queue.
|
|
168
|
+
:param statistics_logger_name: string, name of the logger that will be used to log statistics.
|
|
169
|
+
:param statistics_logger_queue: multiprocessing.Queue, queue that will be used to log statistics in
|
|
170
|
+
multiprocessing. You need to start the logger listener in the main process to handle the queue.
|
|
171
|
+
:param exceptions_logger_name: string, name of the logger that will be used to log exceptions.
|
|
172
|
+
:param exceptions_logger_queue: multiprocessing.Queue, queue that will be used to log exceptions in
|
|
173
|
+
multiprocessing. You need to start the logger listener in the main process to handle the queue.
|
|
163
174
|
:param no_engine_usage_enable: boolean, if True, 'engines_list' will be used to listen on the addresses,
|
|
164
175
|
but the "no_engines_listening_address_list" parameter will be used instead.
|
|
165
176
|
:param no_engines_listening_address_list: list, of ips+ports that will be listened on.
|
|
@@ -178,6 +189,9 @@ class SocketWrapper:
|
|
|
178
189
|
#"domain" = "example.com"
|
|
179
190
|
"""
|
|
180
191
|
|
|
192
|
+
self.ip_address: str = ip_address
|
|
193
|
+
self.port: int = port
|
|
194
|
+
self.engine: initialize_engines.ModuleCategory = engine
|
|
181
195
|
self.ca_certificate_name: str = ca_certificate_name
|
|
182
196
|
self.ca_certificate_filepath: str = ca_certificate_filepath
|
|
183
197
|
self.ca_certificate_crt_filepath: str = ca_certificate_crt_filepath
|
|
@@ -205,13 +219,11 @@ class SocketWrapper:
|
|
|
205
219
|
self.ssh_user: str = ssh_user
|
|
206
220
|
self.ssh_pass: str = ssh_pass
|
|
207
221
|
self.ssh_script_to_execute = ssh_script_to_execute
|
|
208
|
-
self.logger = logger
|
|
209
|
-
self.statistics_logs_directory: str = statistics_logs_directory
|
|
210
222
|
self.forwarding_dns_service_ipv4_list___only_for_localhost = (
|
|
211
223
|
forwarding_dns_service_ipv4_list___only_for_localhost)
|
|
212
|
-
self.no_engine_usage_enable: bool = no_engine_usage_enable
|
|
213
|
-
self.no_engines_listening_address_list: list[str] = no_engines_listening_address_list
|
|
214
|
-
self.engines_list: list[initialize_engines.ModuleCategory] = engines_list
|
|
224
|
+
# self.no_engine_usage_enable: bool = no_engine_usage_enable
|
|
225
|
+
# self.no_engines_listening_address_list: list[str] = no_engines_listening_address_list
|
|
226
|
+
# self.engines_list: list[initialize_engines.ModuleCategory] = engines_list
|
|
215
227
|
|
|
216
228
|
self.socket_object = None
|
|
217
229
|
|
|
@@ -236,26 +248,53 @@ class SocketWrapper:
|
|
|
236
248
|
self.ssh_script_processor = \
|
|
237
249
|
ScriptAsStringProcessor().read_script_to_string(self.ssh_script_to_execute)
|
|
238
250
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
251
|
+
# If logs directory was not set, we will use the working directory.
|
|
252
|
+
if not logs_directory:
|
|
253
|
+
logs_directory = str(Path.cwd() / LOGS_DIRECTORY_NAME)
|
|
254
|
+
self.logs_directory: str = logs_directory
|
|
255
|
+
|
|
256
|
+
if not logger_name:
|
|
257
|
+
logger_name = 'SocketWrapper'
|
|
258
|
+
self.logger_name: str = logger_name
|
|
259
|
+
self.logger_name_listener: str = f"{logger_name}.listener"
|
|
260
|
+
|
|
261
|
+
if loggingw.is_logger_exists(self.logger_name_listener):
|
|
262
|
+
self.logger = loggingw.get_logger_with_level(self.logger_name_listener)
|
|
263
|
+
elif not logger_queue:
|
|
264
|
+
_ = loggingw.create_logger(
|
|
265
|
+
logger_name=logger_name,
|
|
266
|
+
directory_path=self.logs_directory,
|
|
246
267
|
add_stream=True,
|
|
247
268
|
add_timedfile_with_internal_queue=True,
|
|
248
269
|
formatter_streamhandler='DEFAULT',
|
|
249
270
|
formatter_filehandler='DEFAULT'
|
|
250
271
|
)
|
|
251
272
|
|
|
252
|
-
|
|
253
|
-
self.exceptions_logger = loggingw.ExceptionCsvLogger(
|
|
254
|
-
logger_name='SocketWrapperExceptions',
|
|
255
|
-
directory_path=self.statistics_logs_directory
|
|
256
|
-
)
|
|
273
|
+
self.logger = loggingw.get_logger_with_level(self.logger_name_listener)
|
|
257
274
|
else:
|
|
258
|
-
|
|
275
|
+
_ = loggingw.create_logger(
|
|
276
|
+
logger_name=logger_name,
|
|
277
|
+
add_queue_handler=True,
|
|
278
|
+
log_queue=logger_queue
|
|
279
|
+
)
|
|
280
|
+
self.logger = loggingw.get_logger_with_level(self.logger_name_listener)
|
|
281
|
+
|
|
282
|
+
self.statistics_writer = statistics_csv.StatisticsCSVWriter(
|
|
283
|
+
logger_name=statistics_logger_name,
|
|
284
|
+
directory_path=self.logs_directory,
|
|
285
|
+
log_queue=statistics_logger_queue,
|
|
286
|
+
add_queue_handler_no_listener_multiprocessing=True
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if not exceptions_logger_name:
|
|
290
|
+
exceptions_logger_name = 'SocketWrapperExceptions'
|
|
291
|
+
|
|
292
|
+
self.exceptions_logger = loggingw.ExceptionCsvLogger(
|
|
293
|
+
logger_name=exceptions_logger_name,
|
|
294
|
+
directory_path=self.logs_directory,
|
|
295
|
+
log_queue=exceptions_logger_queue,
|
|
296
|
+
add_queue_handler_no_listener_multiprocessing=True
|
|
297
|
+
)
|
|
259
298
|
|
|
260
299
|
self.test_config()
|
|
261
300
|
|
|
@@ -274,12 +313,12 @@ class SocketWrapper:
|
|
|
274
313
|
"You can't set both [sni_use_default_callback_function = True] and [sni_custom_callback_function]."
|
|
275
314
|
raise SocketWrapperConfigurationValuesError(message)
|
|
276
315
|
|
|
277
|
-
if self.no_engine_usage_enable and not self.no_engines_listening_address_list:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
elif not self.no_engine_usage_enable and not self.engines_list:
|
|
281
|
-
|
|
282
|
-
|
|
316
|
+
# if self.no_engine_usage_enable and not self.no_engines_listening_address_list:
|
|
317
|
+
# message = "You set [no_engine_usage_enable = True], but you didn't set [no_engines_listening_address_list]."
|
|
318
|
+
# raise SocketWrapperConfigurationValuesError(message)
|
|
319
|
+
# elif not self.no_engine_usage_enable and not self.engines_list:
|
|
320
|
+
# message = "You set [no_engine_usage_enable = False], but you didn't set [engines_list]."
|
|
321
|
+
# raise SocketWrapperConfigurationValuesError(message)
|
|
283
322
|
|
|
284
323
|
try:
|
|
285
324
|
booleans.is_only_1_true_in_list(
|
|
@@ -329,15 +368,7 @@ class SocketWrapper:
|
|
|
329
368
|
return 1
|
|
330
369
|
|
|
331
370
|
# Checking if listening address is in use.
|
|
332
|
-
listening_check_list
|
|
333
|
-
if self.engines_list:
|
|
334
|
-
for engine in self.engines_list:
|
|
335
|
-
for _, ip_port_dict in engine.domain_target_dict.items():
|
|
336
|
-
address: str = f"{ip_port_dict['ip']}:{ip_port_dict['port']}"
|
|
337
|
-
if address not in listening_check_list:
|
|
338
|
-
listening_check_list.append(address)
|
|
339
|
-
else:
|
|
340
|
-
listening_check_list = self.no_engines_listening_address_list
|
|
371
|
+
listening_check_list = [f"{self.ip_address}:{self.port}"]
|
|
341
372
|
port_in_use = psutil_networks.get_processes_using_port_list(listening_check_list)
|
|
342
373
|
if port_in_use:
|
|
343
374
|
error_messages: list = list()
|
|
@@ -415,64 +446,40 @@ class SocketWrapper:
|
|
|
415
446
|
|
|
416
447
|
return self.socket_object
|
|
417
448
|
|
|
418
|
-
def
|
|
449
|
+
def start_listening_socket(
|
|
419
450
|
self,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
pass_function_reference_to_thread: bool = True
|
|
451
|
+
callable_function: callable,
|
|
452
|
+
callable_args: tuple = ()
|
|
423
453
|
):
|
|
424
454
|
"""
|
|
425
|
-
Start listening
|
|
455
|
+
Start listening on a single socket with given IP address and port.
|
|
456
|
+
This function is used to start listening on a single socket, for example, when you want to listen on a specific
|
|
457
|
+
IP address and port.
|
|
458
|
+
|
|
459
|
+
:param callable_function: callable, function that you want to execute when client
|
|
460
|
+
socket received by 'accept()' and connection has been made.
|
|
461
|
+
:param callable_args: tuple, that will be passed to 'callable_function' when it will be called.
|
|
462
|
+
:return: None
|
|
426
463
|
"""
|
|
427
464
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
for engine in self.engines_list:
|
|
431
|
-
# Combine the domain and port dicts.
|
|
432
|
-
connection_dict: dict = {**engine.domain_target_dict, **engine.port_target_dict}
|
|
433
|
-
|
|
434
|
-
# Start all the regular listening interfaces.
|
|
435
|
-
for domain_or_port, ip_port_dict in connection_dict.items():
|
|
436
|
-
ip_address: str = ip_port_dict['ip']
|
|
437
|
-
port = int(ip_port_dict['port'])
|
|
438
|
-
socket_by_port = self.create_socket_ipv4_tcp(ip_address, port)
|
|
439
|
-
threading.Thread(
|
|
440
|
-
target=self.listening_socket_loop,
|
|
441
|
-
args=(socket_by_port, engine, reference_function_name,
|
|
442
|
-
reference_function_args, pass_function_reference_to_thread),
|
|
443
|
-
name=f"acceptor-{engine.engine_name}-{ip_address}:{port}",
|
|
444
|
-
daemon=True
|
|
445
|
-
).start()
|
|
465
|
+
if self.engine:
|
|
466
|
+
acceptor_name: str = f"acceptor-{self.engine.engine_name}-{self.ip_address}:{self.port}"
|
|
446
467
|
else:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
name=f"acceptor-{ip_address}:{port}",
|
|
457
|
-
daemon=True
|
|
458
|
-
).start()
|
|
459
|
-
|
|
460
|
-
# # Creating a socket for each port in the list set in configuration file
|
|
461
|
-
# for address in self.listening_address_list:
|
|
462
|
-
# ip_address, port_str = address.split(':')
|
|
463
|
-
# port = int(port_str)
|
|
464
|
-
# socket_by_port = self.create_socket_ipv4_tcp(
|
|
465
|
-
# ip_address, port)
|
|
466
|
-
#
|
|
467
|
-
# self.listening_sockets.append(socket_by_port)
|
|
468
|
+
acceptor_name: str = f"acceptor-{self.ip_address}:{self.port}"
|
|
469
|
+
|
|
470
|
+
socket_by_port = self.create_socket_ipv4_tcp(self.ip_address, self.port)
|
|
471
|
+
threading.Thread(
|
|
472
|
+
target=self.listening_socket_loop,
|
|
473
|
+
args=(socket_by_port, callable_function, callable_args),
|
|
474
|
+
name=acceptor_name,
|
|
475
|
+
daemon=True
|
|
476
|
+
).start()
|
|
468
477
|
|
|
469
478
|
def listening_socket_loop(
|
|
470
479
|
self,
|
|
471
480
|
listening_socket_object: socket.socket,
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
reference_function_args=(),
|
|
475
|
-
pass_function_reference_to_thread: bool = True
|
|
481
|
+
callable_function: callable,
|
|
482
|
+
callable_args=()
|
|
476
483
|
):
|
|
477
484
|
"""
|
|
478
485
|
Loop to wait for new connections, accept them and send to new threads.
|
|
@@ -480,23 +487,19 @@ class SocketWrapper:
|
|
|
480
487
|
will be killed or closed.
|
|
481
488
|
|
|
482
489
|
:param listening_socket_object: listening socket that was created with bind.
|
|
483
|
-
:param
|
|
484
|
-
:param reference_function_name: callable, function reference that you want to execute when client
|
|
490
|
+
:param callable_function: callable, function that you want to execute when client
|
|
485
491
|
socket received by 'accept()' and connection has been made.
|
|
486
|
-
:param
|
|
487
|
-
Your function should be able to accept these arguments before the '
|
|
492
|
+
:param callable_args: tuple, that will be passed to 'function_reference' when it will be called.
|
|
493
|
+
Your function should be able to accept these arguments before the 'callable_args' tuple:
|
|
488
494
|
(client_socket, process_name, is_tls, domain_from_dns_server).
|
|
489
|
-
Meaning that '
|
|
495
|
+
Meaning that 'callable_args' will be added to the end of the arguments tuple like so:
|
|
490
496
|
(client_socket, process_name, is_tls, tls_type, tls_version, domain_from_dns_server,
|
|
491
|
-
*
|
|
497
|
+
*callable_args).
|
|
492
498
|
|
|
493
499
|
client_socket: socket, client socket that was accepted.
|
|
494
500
|
process_name: string, process name that was gathered from the socket.
|
|
495
501
|
is_tls: boolean, if the socket is SSL/TLS.
|
|
496
502
|
domain_from_dns_server: string, domain that was requested from DNS server.
|
|
497
|
-
:param pass_function_reference_to_thread: boolean, that sets if 'function_reference' will be
|
|
498
|
-
executed as is, or passed to thread. 'function_reference' can include passing to a thread,
|
|
499
|
-
but you don't have to use it, since SocketWrapper can do it for you.
|
|
500
503
|
:return:
|
|
501
504
|
"""
|
|
502
505
|
|
|
@@ -514,31 +517,30 @@ class SocketWrapper:
|
|
|
514
517
|
listening_ip, listening_port = listening_socket_object.getsockname()
|
|
515
518
|
|
|
516
519
|
domain_from_engine = None
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
520
|
+
# Get the domain to connect on this process in case on no SNI provided.
|
|
521
|
+
for domain, ip_port_dict in self.engine.domain_target_dict.items():
|
|
522
|
+
if ip_port_dict['ip'] == listening_ip:
|
|
523
|
+
domain_from_engine = domain
|
|
524
|
+
break
|
|
525
|
+
# If there was no domain found, try to find the IP address for port.
|
|
526
|
+
if not domain_from_engine:
|
|
527
|
+
for port, file_or_ip in self.engine.port_target_dict.items():
|
|
528
|
+
if file_or_ip['ip'] == listening_ip:
|
|
529
|
+
# Get the value from the 'on_port_connect' dictionary.
|
|
530
|
+
address_or_file_path: str = self.engine.on_port_connect[str(listening_port)]
|
|
531
|
+
ip_port_address_from_config: tuple = initialize_engines.get_ipv4_from_engine_on_connect_port(
|
|
532
|
+
address_or_file_path)
|
|
533
|
+
if not ip_port_address_from_config:
|
|
534
|
+
raise ValueError(
|
|
535
|
+
f"Invalid IP address or file path in 'on_port_connect' for port "
|
|
536
|
+
f"{listening_port}: {address_or_file_path}"
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
domain_from_engine = ip_port_address_from_config[0]
|
|
540
|
+
|
|
522
541
|
break
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
for port, file_or_ip in engine.port_target_dict.items():
|
|
526
|
-
if file_or_ip['ip'] == listening_ip:
|
|
527
|
-
# Get the value from the 'on_port_connect' dictionary.
|
|
528
|
-
address_or_file_path: str = engine.on_port_connect[str(listening_port)]
|
|
529
|
-
ip_port_address_from_config: tuple = initialize_engines.get_ipv4_from_engine_on_connect_port(
|
|
530
|
-
address_or_file_path)
|
|
531
|
-
if not ip_port_address_from_config:
|
|
532
|
-
raise ValueError(
|
|
533
|
-
f"Invalid IP address or file path in 'on_port_connect' for port "
|
|
534
|
-
f"{listening_port}: {address_or_file_path}"
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
domain_from_engine = ip_port_address_from_config[0]
|
|
538
|
-
|
|
539
|
-
break
|
|
540
|
-
|
|
541
|
-
self.logger.info(f"Requested domain setting: {domain_from_engine}")
|
|
542
|
+
|
|
543
|
+
self.logger.info(f"Requested domain setting: {domain_from_engine}")
|
|
542
544
|
|
|
543
545
|
# Wait from any connection on "accept()".
|
|
544
546
|
# 'client_socket' is socket or ssl socket, 'client_address' is a tuple (ip_address, port).
|
|
@@ -561,7 +563,7 @@ class SocketWrapper:
|
|
|
561
563
|
process_name = get_command_instance.get_process_name(print_kwargs={'logger': self.logger})
|
|
562
564
|
|
|
563
565
|
source_ip: str = client_address[0]
|
|
564
|
-
engine_name: str = get_engine_name(domain_from_engine, self.
|
|
566
|
+
engine_name: str = get_engine_name(domain_from_engine, [self.engine])
|
|
565
567
|
dest_port: int = listening_socket_object.getsockname()[1]
|
|
566
568
|
|
|
567
569
|
# Not always there will be a hostname resolved by the IP address, so we will leave it empty if it fails.
|
|
@@ -620,16 +622,6 @@ class SocketWrapper:
|
|
|
620
622
|
print_kwargs={'logger': self.logger}
|
|
621
623
|
)
|
|
622
624
|
|
|
623
|
-
# Get the real tls version after connection is wrapped.
|
|
624
|
-
tls_version = ssl_client_socket.version()
|
|
625
|
-
|
|
626
|
-
# If the 'domain_from_dns_server' is empty, it means that the 'engine_name' is not set.
|
|
627
|
-
# In this case we will set the 'engine_name' to from the SNI.
|
|
628
|
-
if engine_name == '':
|
|
629
|
-
sni_hostname: str = ssl_client_socket.server_hostname
|
|
630
|
-
if sni_hostname:
|
|
631
|
-
engine_name = get_engine_name(sni_hostname, self.engines_list)
|
|
632
|
-
|
|
633
625
|
if accept_error_message:
|
|
634
626
|
# Write statistics after wrap is there was an error.
|
|
635
627
|
self.statistics_writer.write_accept_error(
|
|
@@ -643,6 +635,16 @@ class SocketWrapper:
|
|
|
643
635
|
|
|
644
636
|
continue
|
|
645
637
|
|
|
638
|
+
# Get the real tls version after connection is wrapped.
|
|
639
|
+
tls_version = ssl_client_socket.version()
|
|
640
|
+
|
|
641
|
+
# If the 'domain_from_dns_server' is empty, it means that the 'engine_name' is not set.
|
|
642
|
+
# In this case we will set the 'engine_name' to from the SNI.
|
|
643
|
+
if engine_name == '':
|
|
644
|
+
sni_hostname: str = ssl_client_socket.server_hostname
|
|
645
|
+
if sni_hostname:
|
|
646
|
+
engine_name = get_engine_name(sni_hostname, [self.engine])
|
|
647
|
+
|
|
646
648
|
# Create new arguments tuple that will be passed, since client socket and process_name
|
|
647
649
|
# are gathered from SocketWrapper.
|
|
648
650
|
if ssl_client_socket:
|
|
@@ -652,21 +654,24 @@ class SocketWrapper:
|
|
|
652
654
|
# noinspection PyUnusedLocal
|
|
653
655
|
client_socket = None
|
|
654
656
|
client_socket = ssl_client_socket
|
|
655
|
-
thread_args =
|
|
656
|
-
(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
#
|
|
660
|
-
|
|
661
|
-
before_socket_thread_worker
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
657
|
+
thread_args = (
|
|
658
|
+
(client_socket, process_name, is_tls, tls_type, tls_version, domain_from_engine, self.statistics_writer, [self.engine]) +
|
|
659
|
+
callable_args)
|
|
660
|
+
|
|
661
|
+
# Creating thread for each socket
|
|
662
|
+
thread_current = threading.Thread(
|
|
663
|
+
target=before_socket_thread_worker,
|
|
664
|
+
args=(callable_function, thread_args, self.exceptions_logger),
|
|
665
|
+
daemon=True
|
|
666
|
+
)
|
|
667
|
+
thread_current.start()
|
|
668
|
+
# Append to list of threads, so they can be "joined" later
|
|
669
|
+
self.threads_list.append(thread_current)
|
|
670
|
+
|
|
671
|
+
# 'thread_callable_args[1][0]' is the client socket.
|
|
672
|
+
client_address = base.get_source_address_from_socket(client_socket)
|
|
673
|
+
|
|
674
|
+
self.logger.info(f"Accepted connection, thread created {client_address}. Continue listening...")
|
|
670
675
|
# Else, if no client_socket was opened during, accept, then print the error.
|
|
671
676
|
else:
|
|
672
677
|
# Write statistics after accept.
|
|
@@ -681,35 +686,22 @@ class SocketWrapper:
|
|
|
681
686
|
except Exception as e:
|
|
682
687
|
self.exceptions_logger.write(e)
|
|
683
688
|
|
|
684
|
-
def _send_accepted_socket_to_thread(self, thread_function_name, reference_args=()):
|
|
685
|
-
# Creating thread for each socket
|
|
686
|
-
thread_current = threading.Thread(target=thread_function_name, args=(*reference_args,))
|
|
687
|
-
thread_current.daemon = True
|
|
688
|
-
thread_current.start()
|
|
689
|
-
# Append to list of threads, so they can be "joined" later
|
|
690
|
-
self.threads_list.append(thread_current)
|
|
691
|
-
|
|
692
|
-
# 'reference_args[1][0]' is the client socket.
|
|
693
|
-
client_address = base.get_source_address_from_socket(reference_args[1][0])
|
|
694
|
-
|
|
695
|
-
self.logger.info(f"Accepted connection, thread created {client_address}. Continue listening...")
|
|
696
|
-
|
|
697
689
|
|
|
698
690
|
def before_socket_thread_worker(
|
|
699
691
|
callable_function: callable,
|
|
700
|
-
|
|
692
|
+
callable_args: tuple,
|
|
701
693
|
exceptions_logger: loggingw.ExceptionCsvLogger = None
|
|
702
694
|
):
|
|
703
695
|
"""
|
|
704
696
|
Function that will be executed before the thread is started.
|
|
705
697
|
:param callable_function: callable, function that will be executed in the thread.
|
|
706
|
-
:param
|
|
698
|
+
:param callable_args: tuple, arguments that will be passed to the function.
|
|
707
699
|
:param exceptions_logger: loggingw.ExceptionCsvLogger, logger object that will be used to log exceptions.
|
|
708
700
|
:return:
|
|
709
701
|
"""
|
|
710
702
|
|
|
711
703
|
try:
|
|
712
|
-
callable_function(*
|
|
704
|
+
callable_function(*callable_args)
|
|
713
705
|
except Exception as e:
|
|
714
706
|
exceptions_logger.write(e)
|
|
715
707
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import multiprocessing
|
|
2
3
|
|
|
3
|
-
from ...file_io import csvs
|
|
4
4
|
from ..loggingw import loggingw
|
|
5
5
|
|
|
6
6
|
|
|
@@ -10,22 +10,41 @@ STATISTICS_HEADER: str = (
|
|
|
10
10
|
'response_size_bytes,file_path,process_cmd,action,error')
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class StatisticsCSVWriter:
|
|
13
|
+
class StatisticsCSVWriter(loggingw.CsvLogger):
|
|
14
14
|
"""
|
|
15
15
|
Class to write statistics to CSV file.
|
|
16
16
|
This can be initiated at the main, and then passed to the thread worker function.
|
|
17
17
|
"""
|
|
18
18
|
def __init__(
|
|
19
19
|
self,
|
|
20
|
-
|
|
20
|
+
logger_name: str = LOGGER_NAME,
|
|
21
|
+
directory_path: str = None,
|
|
22
|
+
log_queue: multiprocessing.Queue = None,
|
|
23
|
+
add_queue_handler_start_listener_multiprocessing: bool = False,
|
|
24
|
+
add_queue_handler_no_listener_multiprocessing: bool = False
|
|
21
25
|
):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
"""
|
|
27
|
+
Initialize the StatisticsCSVWriter with the directory path for the statistics CSV file.
|
|
28
|
+
:param directory_path: str, the directory path where the statistics CSV file will be created.
|
|
29
|
+
:param log_queue: multiprocessing.Queue, the queue to use for logging in multiprocessing.
|
|
30
|
+
:param add_queue_handler_start_listener_multiprocessing: bool, whether to add a queue handler that will use
|
|
31
|
+
the 'logger_queue' and start the queue listener with the same 'logger_queue' for multiprocessing.
|
|
32
|
+
:param add_queue_handler_no_listener_multiprocessing: bool, whether to add a queue handler that will use
|
|
33
|
+
the 'logger_queue' but will not start the queue listener for multiprocessing. This is useful when you
|
|
34
|
+
already started the queue listener and want to add more handlers to the logger without
|
|
35
|
+
starting a new listener.
|
|
36
|
+
|
|
37
|
+
If you don't set any of 'add_queue_handler_start_listener_multiprocessing' or
|
|
38
|
+
'add_queue_handler_no_listener_multiprocessing', the logger will be created without a queue handler.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
super().__init__(
|
|
42
|
+
logger_name=logger_name,
|
|
43
|
+
directory_path=directory_path,
|
|
44
|
+
log_queue=log_queue,
|
|
45
|
+
add_queue_handler_start_listener_multiprocessing=add_queue_handler_start_listener_multiprocessing,
|
|
46
|
+
add_queue_handler_no_listener_multiprocessing=add_queue_handler_no_listener_multiprocessing,
|
|
47
|
+
custom_header=STATISTICS_HEADER
|
|
29
48
|
)
|
|
30
49
|
|
|
31
50
|
def write_row(
|
|
@@ -60,7 +79,7 @@ class StatisticsCSVWriter:
|
|
|
60
79
|
else:
|
|
61
80
|
tls_info = f'{tls_type}|{tls_version}'
|
|
62
81
|
|
|
63
|
-
|
|
82
|
+
row_of_cols: list = [
|
|
64
83
|
timestamp,
|
|
65
84
|
thread_id,
|
|
66
85
|
engine,
|
|
@@ -81,9 +100,9 @@ class StatisticsCSVWriter:
|
|
|
81
100
|
process_cmd,
|
|
82
101
|
action,
|
|
83
102
|
error
|
|
84
|
-
]
|
|
103
|
+
]
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
super().write(row_of_cols)
|
|
87
106
|
|
|
88
107
|
def write_accept_error(
|
|
89
108
|
self,
|
|
@@ -8,15 +8,24 @@ from ..print_api import print_api
|
|
|
8
8
|
from ..permissions import ubuntu_permissions
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def install_packages(
|
|
11
|
+
def install_packages(
|
|
12
|
+
package_list: list[str],
|
|
13
|
+
timeout_seconds: int = 0,
|
|
14
|
+
):
|
|
12
15
|
"""
|
|
13
16
|
Function installs a package using apt-get.
|
|
14
17
|
:param package_list: list of strings, package names to install.
|
|
18
|
+
:param timeout_seconds: int, if the 'apt-get' command is busy at the moment, the function will wait for
|
|
19
|
+
'timeout_seconds' seconds before raising an error.
|
|
20
|
+
'-1' means wait indefinitely.
|
|
15
21
|
:return:
|
|
16
22
|
"""
|
|
17
23
|
|
|
18
24
|
# Construct the command with the package list
|
|
19
|
-
command = ["sudo", "apt
|
|
25
|
+
command = ["sudo", "apt", "install", "-y"] + package_list
|
|
26
|
+
|
|
27
|
+
if timeout_seconds != 0:
|
|
28
|
+
command.extend(["-o", f"DPkg::Lock::Timeout={str(timeout_seconds)}"])
|
|
20
29
|
|
|
21
30
|
subprocess.check_call(command)
|
|
22
31
|
|