atomicshop 2.15.13__py3-none-any.whl → 2.16.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.

Files changed (66) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/a_installs/ubuntu/pycharm.py +7 -0
  3. atomicshop/a_installs/win/pycharm.py +2 -2
  4. atomicshop/{addons/mains/install_wsl_ubuntu_lts_admin.py → a_installs/win/wsl_ubuntu_lts.py} +1 -0
  5. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  6. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  7. atomicshop/basics/booleans.py +14 -5
  8. atomicshop/dns.py +104 -0
  9. atomicshop/file_io/docxs.py +8 -0
  10. atomicshop/file_io/tomls.py +133 -0
  11. atomicshop/filesystem.py +5 -4
  12. atomicshop/get_process_list.py +3 -3
  13. atomicshop/mitm/config_static.py +195 -0
  14. atomicshop/mitm/config_toml_editor.py +55 -0
  15. atomicshop/mitm/connection_thread_worker.py +54 -90
  16. atomicshop/mitm/import_config.py +148 -139
  17. atomicshop/mitm/initialize_engines.py +7 -2
  18. atomicshop/mitm/initialize_mitm_server.py +162 -107
  19. atomicshop/mitm/shared_functions.py +0 -1
  20. atomicshop/mitm/statistic_analyzer.py +13 -1
  21. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +54 -14
  22. atomicshop/permissions/__init__.py +0 -0
  23. atomicshop/permissions/permissions.py +22 -0
  24. atomicshop/{permissions.py → permissions/ubuntu_permissions.py} +4 -54
  25. atomicshop/permissions/win_permissions.py +33 -0
  26. atomicshop/script_as_string_processor.py +5 -1
  27. atomicshop/wrappers/cryptographyw.py +3 -3
  28. atomicshop/wrappers/dockerw/install_docker.py +6 -5
  29. atomicshop/wrappers/elasticsearchw/install_elastic.py +2 -1
  30. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -4
  31. atomicshop/wrappers/mongodbw/install_mongodb.py +2 -1
  32. atomicshop/wrappers/msiw.py +2 -3
  33. atomicshop/wrappers/psutilw/networks.py +25 -1
  34. atomicshop/wrappers/pycharmw/__init__.py +0 -0
  35. atomicshop/wrappers/pycharmw/ubuntu.py +38 -0
  36. atomicshop/wrappers/{pycharmw.py → pycharmw/win.py} +2 -2
  37. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  38. atomicshop/wrappers/pywin32w/wmis/helpers.py +127 -0
  39. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +167 -0
  40. atomicshop/wrappers/socketw/accepter.py +8 -8
  41. atomicshop/wrappers/socketw/base.py +13 -0
  42. atomicshop/wrappers/socketw/certificator.py +202 -149
  43. atomicshop/wrappers/socketw/creator.py +15 -35
  44. atomicshop/wrappers/socketw/dns_server.py +155 -102
  45. atomicshop/wrappers/socketw/exception_wrapper.py +8 -27
  46. atomicshop/wrappers/socketw/get_process.py +115 -95
  47. atomicshop/wrappers/socketw/sni.py +298 -164
  48. atomicshop/wrappers/socketw/socket_client.py +5 -12
  49. atomicshop/wrappers/socketw/socket_server_tester.py +1 -1
  50. atomicshop/wrappers/socketw/socket_wrapper.py +328 -72
  51. atomicshop/wrappers/socketw/statistics_csv.py +94 -16
  52. atomicshop/wrappers/ubuntu_terminal.py +6 -6
  53. atomicshop/wrappers/wslw.py +1 -0
  54. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/METADATA +1 -1
  55. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/RECORD +63 -54
  56. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  57. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  58. atomicshop/mitm/config_editor.py +0 -37
  59. /atomicshop/{addons/mains/install_docker_rootless_ubuntu.py → a_installs/ubuntu/docker_rootless.py} +0 -0
  60. /atomicshop/{addons/mains/install_docker_ubuntu_main_sudo.py → a_installs/ubuntu/docker_sudo.py} +0 -0
  61. /atomicshop/{addons/mains/install_elastic_search_and_kibana_ubuntu.py → a_installs/ubuntu/elastic_search_and_kibana.py} +0 -0
  62. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  63. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  64. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/LICENSE.txt +0 -0
  65. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/WHEEL +0 -0
  66. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/top_level.txt +0 -0
@@ -1,34 +1,180 @@
1
1
  import threading
2
2
  import select
3
+ from typing import Literal, Union
3
4
 
4
- from . import base, creator, get_process, accepter, statistics_csv, ssl_base
5
+ from ..psutilw import networks
5
6
  from ...script_as_string_processor import ScriptAsStringProcessor
6
- from ... import queues
7
+ from ... import queues, filesystem
8
+ from ...basics import booleans
7
9
  from ...print_api import print_api
8
10
 
11
+ from . import base, creator, get_process, accepter, statistics_csv, ssl_base, sni
12
+
13
+
14
+ class SocketWrapperPortInUseError(Exception):
15
+ pass
16
+
17
+
18
+ class SocketWrapperConfigurationValuesError(Exception):
19
+ pass
20
+
9
21
 
10
22
  SNI_QUEUE = queues.NonBlockQueue()
11
23
 
12
24
 
13
- # === Socket Wrapper ===================================================================================================
14
25
  class SocketWrapper:
15
26
  def __init__(
16
27
  self,
28
+ listening_interface: str,
29
+ listening_port_list: list[int],
30
+ forwarding_dns_service_ipv4_list___only_for_localhost: list = None,
31
+ ca_certificate_name: str = None,
32
+ ca_certificate_filepath: str = None,
33
+ default_server_certificate_usage: bool = False,
34
+ default_server_certificate_name: str = None,
35
+ default_certificate_domain_list: list = None,
36
+ default_server_certificate_directory: str = None,
37
+ sni_custom_callback_function: callable = None,
38
+ sni_use_default_callback_function: bool = False,
39
+ sni_use_default_callback_function_extended: bool = False,
40
+ sni_add_new_domains_to_default_server_certificate: bool = False,
41
+ sni_create_server_certificate_for_each_domain: bool = False,
42
+ sni_server_certificates_cache_directory: str = None,
43
+ sni_get_server_certificate_from_server_socket: bool = False,
44
+ sni_server_certificate_from_server_socket_download_directory: str = None,
45
+ skip_extension_id_list: list = None,
46
+ custom_server_certificate_usage: bool = False,
47
+ custom_server_certificate_path: str = None,
48
+ custom_private_key_path: str = None,
49
+ get_process_name: bool = False,
50
+ ssh_user: str = None,
51
+ ssh_pass: str = None,
52
+ ssh_script_to_execute: Union[
53
+ Literal[
54
+ 'process_from_port',
55
+ 'process_from_ipv4'
56
+ ],
57
+ None
58
+ ] = None,
17
59
  logger=None,
18
- statistics_logger=None,
19
- config=None,
20
- domains_list: list = None
60
+ statistics_logs_directory: str = None,
61
+ request_domain_queue: queues.NonBlockQueue = None
21
62
  ):
63
+ """
64
+ Socket Wrapper class that will be used to create sockets, listen on them, accept connections and send them to
65
+ new threads.
66
+
67
+ :param listening_interface: string, interface that will be listened on.
68
+ Example: '0.0.0.0'. For all interfaces.
69
+ :param listening_port_list: list, of ports that will be listened on.
70
+ :param ca_certificate_name: CA certificate name.
71
+ :param ca_certificate_filepath: CA certificate file path.
72
+ :param default_server_certificate_usage: boolean, if True, default server certificate will be used
73
+ for each incoming socket.
74
+ :param sni_custom_callback_function: callable, custom callback function that will be executed when
75
+ there is a SNI present in the request.
76
+
77
+ Example: custom callback function to set the 'server_hostname' for the socket with the domain name from SNI:
78
+ def sni_handle(
79
+ sni_ssl_socket: ssl.SSLSocket,
80
+ sni_destination_name: str,
81
+ sni_ssl_context: ssl.SSLContext):
82
+ # Set 'server_hostname' for the socket.
83
+ sni_ssl_socket.server_hostname = sni_destination_name
84
+
85
+ return sni_handle
86
+
87
+ The function should accept 3 arguments:
88
+ sni_ssl_socket: ssl.SSLSocket, SSL socket object.
89
+ sni_destination_name: string, domain name from SNI.
90
+ sni_ssl_context: ssl.SSLContext, SSL context object.
91
+
92
+ These parameters are default for any SNI handler function, so you can use them in your custom function.
93
+
94
+ :param sni_use_default_callback_function: boolean, if True, default callback function will be used.
95
+ The function will set the 'server_hostname' for the socket with the domain name from SNI.
96
+ The example in 'sni_custom_callback_function' parameter is the function that will be used.
97
+ :param sni_use_default_callback_function_extended: boolean, if True, default callback function will be used
98
+ with extended functionality. This feature will handle all the features and parameters that are set in
99
+ the SocketWrapper object that are related to SNI. THis includes certificate management for each domain,
100
+ adding new domains to the default certificate, creating new certificates for each domain, etc.
101
+ This feature also utilizes the 'request_domain_queue' parameter to get the domain name that was requested
102
+ from the DNS server (atomicshop.wrappers.socketw.dns_server).
103
+ :param sni_add_new_domains_to_default_server_certificate: boolean, if True, new domains that hit the tcp
104
+ server will be added to default server certificate.
105
+ :param sni_create_server_certificate_for_each_domain: boolean, if True, server certificate will be
106
+ created and used for each domain that hit the tcp server.
107
+ :param sni_get_server_certificate_from_server_socket: boolean, if True, server certificate will be
108
+ downloaded from the server socket.
109
+ :param sni_server_certificate_from_server_socket_download_directory: string, path to directory where
110
+ server certificate will be downloaded from the server socket.
111
+ :param default_server_certificate_name: default server certificate name.
112
+ :param default_certificate_domain_list: list of string, domains to create the default certificate with.
113
+ :param default_server_certificate_directory: string, path to directory where default certificate file
114
+ will be stored.
115
+ :param sni_server_certificates_cache_directory: string, path to directory where all server certificates for
116
+ each domain will be created.
117
+ :param skip_extension_id_list: list of string, list of extension IDs that will be skipped when processing
118
+ the certificate from the server socket.
119
+ Example: ['1.3.6.1.5.5.7.3.2', '2.5.29.31', '1.3.6.1.5.5.7.1.1']
120
+ :param custom_server_certificate_usage: boolean, if True, custom server certificate will be used.
121
+ :param custom_server_certificate_path: string, path to custom server certificate.
122
+ :param custom_private_key_path: string, path to custom private key.
123
+ server certificates from the server socket.
124
+ :param get_process_name: boolean, if the process name and command line should be gathered from the socket.
125
+ If the socket came from remote host we will try ti get the process name from the remote host by SSH.
126
+ By default, we don't get the process name, because we're using psutil to get the process name and command
127
+ line, but if the process is protected by the system, then command line will be empty.
128
+ It's up to user to decide if to run the script with root privileges or not, this is only relevant if
129
+ the script is running on the same host.
130
+ :param ssh_user: string, SSH username that will be used to connect to remote host.
131
+ :param ssh_pass: string, SSH password that will be used to connect to remote host.
132
+ :param ssh_script_to_execute: string, script that will be executed to get the process name on ssh remote host.
133
+ :param logger: logging.Logger object, logger object that will be used to log messages.
134
+ :param statistics_logs_directory: string, path to directory where daily statistics.csv files will be stored.
135
+ After you initialize the SocketWrapper object, you can get the statistics_writer object from it and use it
136
+ to write statistics to the file in a worker thread.
137
+
138
+ socket_wrapper_instance = SocketWrapper(...)
139
+ statistics_writer = socket_wrapper_instance.statistics_writer
140
+
141
+ statistics_writer: statistics_csv.StatisticsCSVWriter object, there is a logger object that
142
+ will be used to write the statistics file.
143
+ :param request_domain_queue: queues.NonBlockQueue object, non-blocking queue that will be used to get
144
+ the domain name that was requested from the DNS server (atomicshop.wrappers.socketw.dns_server).
145
+ This is used to get the domain name that got to the DNS server and set it to the socket in case SNI
146
+ was empty (in the SNIHandler class to set the 'server_hostname' for the socket).
147
+ """
22
148
 
149
+ self.listening_interface: str = listening_interface
150
+ self.listening_port_list: list[int] = listening_port_list
151
+ self.ca_certificate_name: str = ca_certificate_name
152
+ self.ca_certificate_filepath: str = ca_certificate_filepath
153
+ self.default_server_certificate_usage: bool = default_server_certificate_usage
154
+ self.default_server_certificate_name: str = default_server_certificate_name
155
+ self.default_certificate_domain_list: list = default_certificate_domain_list
156
+ self.default_server_certificate_directory: str = default_server_certificate_directory
157
+ self.sni_custom_callback_function: callable = sni_custom_callback_function
158
+ self.sni_use_default_callback_function: bool = sni_use_default_callback_function
159
+ self.sni_use_default_callback_function_extended: bool = sni_use_default_callback_function_extended
160
+ self.sni_add_new_domains_to_default_server_certificate: bool = sni_add_new_domains_to_default_server_certificate
161
+ self.sni_create_server_certificate_for_each_domain: bool = sni_create_server_certificate_for_each_domain
162
+ self.sni_server_certificates_cache_directory: str = sni_server_certificates_cache_directory
163
+ self.sni_get_server_certificate_from_server_socket: bool = sni_get_server_certificate_from_server_socket
164
+ self.sni_server_certificate_from_server_socket_download_directory: str = \
165
+ sni_server_certificate_from_server_socket_download_directory
166
+ self.skip_extension_id_list: list = skip_extension_id_list
167
+ self.custom_server_certificate_usage: bool = custom_server_certificate_usage
168
+ self.custom_server_certificate_path: str = custom_server_certificate_path
169
+ self.custom_private_key_path: str = custom_private_key_path
170
+ self.get_process_name: bool = get_process_name
171
+ self.ssh_user: str = ssh_user
172
+ self.ssh_pass: str = ssh_pass
173
+ self.ssh_script_to_execute = ssh_script_to_execute
23
174
  self.logger = logger
24
- self.statistics = statistics_logger
25
- self.config: dict = config
26
-
27
- # If 'domains_list' wasn't passed, but 'config' did.
28
- if not domains_list and config:
29
- self.domains_list: list = config['certificates']['domains_all_times']
30
- else:
31
- self.domains_list: list = domains_list
175
+ self.statistics_logs_directory: str = statistics_logs_directory
176
+ self.forwarding_dns_service_ipv4_list___only_for_localhost = (
177
+ forwarding_dns_service_ipv4_list___only_for_localhost)
32
178
 
33
179
  self.socket_object = None
34
180
 
@@ -49,9 +195,80 @@ class SocketWrapper:
49
195
 
50
196
  # Defining 'ssh_script_processor' variable, which will be used to process SSH scripts.
51
197
  self.ssh_script_processor = None
52
- if self.config['ssh']['get_process_name']:
198
+ if self.get_process_name:
199
+ # noinspection PyTypeChecker
53
200
  self.ssh_script_processor = \
54
- ScriptAsStringProcessor().read_script_to_string(self.config['ssh']['script_to_execute'])
201
+ ScriptAsStringProcessor().read_script_to_string(self.ssh_script_to_execute)
202
+
203
+ self.statistics_writer = statistics_csv.StatisticsCSVWriter(
204
+ statistics_directory_path=self.statistics_logs_directory)
205
+
206
+ self.test_config()
207
+
208
+ def test_config(self):
209
+ if self.sni_custom_callback_function and (
210
+ self.sni_use_default_callback_function or self.sni_use_default_callback_function_extended):
211
+ message = "You can't use both custom and default SNI function at the same time."
212
+ raise SocketWrapperConfigurationValuesError(message)
213
+
214
+ if self.sni_use_default_callback_function_extended and not self.sni_use_default_callback_function:
215
+ message = "You can't use extended SNI function without default SNI function."
216
+ raise SocketWrapperConfigurationValuesError(message)
217
+
218
+ if self.sni_use_default_callback_function and self.sni_custom_callback_function:
219
+ message = \
220
+ "You can't set both [sni_use_default_callback_function = True] and [sni_custom_callback_function]."
221
+ raise SocketWrapperConfigurationValuesError(message)
222
+
223
+ try:
224
+ booleans.check_3_booleans_when_only_1_can_be_true(
225
+ (self.default_server_certificate_usage, 'default_server_certificate_usage'),
226
+ (self.sni_create_server_certificate_for_each_domain,
227
+ 'sni_create_server_certificate_for_each_domain'),
228
+ (self.custom_server_certificate_usage, 'custom_server_certificate_usage'))
229
+ except ValueError as e:
230
+ raise SocketWrapperConfigurationValuesError(str(e))
231
+
232
+ if not self.default_server_certificate_usage and \
233
+ self.sni_add_new_domains_to_default_server_certificate:
234
+ message = "No point setting [sni_add_new_domains_to_default_server_certificate = True]\n" \
235
+ "If you're not going to use default certificates [default_server_certificate_usage = False]"
236
+ raise SocketWrapperConfigurationValuesError(message)
237
+
238
+ if self.sni_get_server_certificate_from_server_socket and \
239
+ not self.sni_create_server_certificate_for_each_domain:
240
+ message = "You set [sni_get_server_certificate_from_server_socket = True],\n" \
241
+ "But you didn't set [sni_create_server_certificate_for_each_domain = True]."
242
+ raise SocketWrapperConfigurationValuesError(message)
243
+
244
+ if self.custom_server_certificate_usage and \
245
+ not self.custom_server_certificate_path:
246
+ message = "You set [custom_server_certificate_usage = True],\n" \
247
+ "But you didn't set [custom_server_certificate_path]."
248
+ raise SocketWrapperConfigurationValuesError(message)
249
+
250
+ # If 'custom_certificate_usage' was set to 'True'.
251
+ if self.custom_server_certificate_usage:
252
+ # Check file existence.
253
+ if not filesystem.is_file_exists(file_path=self.custom_server_certificate_path):
254
+ message = f"File not found: {self.custom_server_certificate_path}"
255
+ print_api(message, color='red')
256
+ return 1
257
+
258
+ # And if 'custom_private_key_path' field was populated in [advanced] section, we'll check its existence.
259
+ if self.custom_private_key_path:
260
+ # Check private key file existence.
261
+ if not filesystem.is_file_exists(file_path=self.custom_private_key_path):
262
+ message = f"File not found: {self.custom_private_key_path}"
263
+ print_api(message, color='red')
264
+ return 1
265
+
266
+ port_in_use = networks.get_processes_using_port_list(self.listening_port_list)
267
+ if port_in_use:
268
+ error_messages: list = list()
269
+ for port, process_info in port_in_use.items():
270
+ error_messages.append(f"Port [{port}] is already in use by process: {process_info}")
271
+ raise SocketWrapperPortInUseError("\n".join(error_messages))
55
272
 
56
273
  # Creating listening sockets.
57
274
  def create_socket_ipv4_tcp(self, ip_address: str, port: int):
@@ -61,9 +278,6 @@ class SocketWrapper:
61
278
  creator.bind_socket_with_ip_port(self.socket_object, ip_address, port, logger=self.logger)
62
279
  creator.set_listen_on_socket(self.socket_object, logger=self.logger)
63
280
 
64
- # self.socket_object, accept_error_message = creator.wrap_socket_with_ssl_context_server_sni_extended(
65
- # self.socket_object, config=self.config, print_kwargs={'logger': self.logger})
66
-
67
281
  return self.socket_object
68
282
 
69
283
  def create_tcp_listening_socket_list(self, overwrite_list: bool = False):
@@ -73,41 +287,41 @@ class SocketWrapper:
73
287
  self.listening_sockets = list()
74
288
 
75
289
  # Creating a socket for each port in the list set in configuration file
76
- for port in self.config['tcp']['listening_port_list']:
290
+ for port in self.listening_port_list:
77
291
  socket_by_port = self.create_socket_ipv4_tcp(
78
- self.config['tcp']['listening_interface'], port)
292
+ self.listening_interface, port)
79
293
 
80
294
  self.listening_sockets.append(socket_by_port)
81
295
 
82
- def send_accepted_socket_to_thread(self, thread_function_name, reference_args=()):
83
- # Creating thread for each socket
84
- thread_current = threading.Thread(target=thread_function_name, args=(*reference_args,))
85
- thread_current.daemon = True
86
- thread_current.start()
87
- # Append to list of threads, so they can be "joined" later
88
- self.threads_list.append(thread_current)
89
-
90
- # 'reference_args[0]' is the client socket.
91
- client_address = base.get_source_address_from_socket(reference_args[0])
92
-
93
- self.logger.info(f"Accepted connection, thread created {client_address}. Continue listening...")
94
-
95
296
  def loop_for_incoming_sockets(
96
- self, function_reference, listening_socket_list: list = None,
97
- pass_function_reference_to_thread: bool = True, reference_args=(), *args, **kwargs):
297
+ self,
298
+ reference_function_name,
299
+ reference_function_args=(),
300
+ listening_socket_list: list = None,
301
+ pass_function_reference_to_thread: bool = True
302
+ ):
98
303
  """
99
304
  Loop to wait for new connections, accept them and send to new threads.
100
305
  The boolean variable was declared True in the beginning of the script and will be set to False if the process
101
306
  will be killed or closed.
102
307
 
103
- :param function_reference: callable, function reference that you want to execute when client
104
- socket received by 'accept()' and connection been made.
308
+ :param reference_function_name: callable, function reference that you want to execute when client
309
+ socket received by 'accept()' and connection has been made.
310
+ :param reference_function_args: tuple, that will be passed to 'function_reference' when it will be called.
311
+ Your function should be able to accept these arguments before the 'reference_function_args' tuple:
312
+ (client_socket, process_name, is_tls, domain_from_dns_server).
313
+ Meaning that 'reference_function_args' will be added to the end of the arguments tuple like so:
314
+ (client_socket, process_name, is_tls, tls_type, tls_version, domain_from_dns_server,
315
+ *reference_function_args).
316
+
317
+ client_socket: socket, client socket that was accepted.
318
+ process_name: string, process name that was gathered from the socket.
319
+ is_tls: boolean, if the socket is SSL/TLS.
320
+ domain_from_dns_server: string, domain that was requested from DNS server.
105
321
  :param listening_socket_list: list, of sockets that you want to listen on.
106
322
  :param pass_function_reference_to_thread: boolean, that sets if 'function_reference' will be
107
323
  executed as is, or passed to thread. 'function_reference' can include passing to a thread,
108
324
  but you don't have to use it, since SocketWrapper can do it for you.
109
- :param reference_args: tuple, that will be passed to 'function_reference' when it will be called.
110
- :param kwargs:
111
325
  :return:
112
326
  """
113
327
 
@@ -116,10 +330,8 @@ class SocketWrapper:
116
330
  # Then assign 'self.listening_sockets'.
117
331
  listening_socket_list = self.listening_sockets
118
332
 
119
- # Socket accept infinite loop run variable. When the process is closed, the loop will break and the threads will
120
- # be joined and garbage collection cleaned if there is any
121
- socket_infinite_loop_run: bool = True
122
- while socket_infinite_loop_run:
333
+ while True:
334
+ # noinspection PyBroadException
123
335
  try:
124
336
  # Using "select.select" which is currently the only API function that works on all
125
337
  # operating system types: Windows / Linux / BSD.
@@ -138,17 +350,20 @@ class SocketWrapper:
138
350
  # Wait from any connection on "accept()".
139
351
  # 'client_socket' is socket or ssl socket, 'client_address' is a tuple (ip_address, port).
140
352
  client_socket, client_address, accept_error_message = accepter.accept_connection_with_error(
141
- listening_socket_object, dns_domain=domain_from_dns_server, print_kwargs={'logger': self.logger})
353
+ listening_socket_object, domain_from_dns_server=domain_from_dns_server, print_kwargs={'logger': self.logger})
142
354
 
143
355
  # This is the earliest stage to ask for process name.
144
356
  # SSH Remote / LOCALHOST script execution to identify process section.
145
- # If 'config.tcp['get_process_name']' was set to True in 'config.ini', then this will be executed.
357
+ # If 'get_process_name' was set to True, then this will be executed.
146
358
  process_name = None
147
- if self.config['ssh']['get_process_name']:
359
+ if self.get_process_name:
148
360
  # Get the process name from the socket.
149
- process_name = get_process.get_process_name(
150
- client_socket=client_socket, config=self.config, ssh_script_processor=self.ssh_script_processor,
151
- print_kwargs={'logger': self.logger})
361
+ get_command_instance = get_process.GetCommandLine(
362
+ client_socket=client_socket,
363
+ ssh_script_processor=self.ssh_script_processor,
364
+ ssh_user=self.ssh_user,
365
+ ssh_pass=self.ssh_pass)
366
+ process_name = get_command_instance.get_process_name(print_kwargs={'logger': self.logger})
152
367
 
153
368
  # If 'accept()' function worked well, SSL worked well, then 'client_socket' won't be empty.
154
369
  if client_socket:
@@ -157,58 +372,99 @@ class SocketWrapper:
157
372
  tls_properties = ssl_base.is_tls(client_socket)
158
373
  if tls_properties:
159
374
  is_tls = True
375
+ tls_type, tls_version = tls_properties
376
+ else:
377
+ tls_type, tls_version = None, None
160
378
 
161
379
  # If 'is_tls' is True.
162
380
  ssl_client_socket = None
163
381
  if is_tls:
382
+ sni_handler = sni.SNISetup(
383
+ default_server_certificate_usage=self.default_server_certificate_usage,
384
+ default_server_certificate_name=self.default_server_certificate_name,
385
+ default_certificate_domain_list=self.default_certificate_domain_list,
386
+ default_server_certificate_directory=self.default_server_certificate_directory,
387
+ sni_custom_callback_function=self.sni_custom_callback_function,
388
+ sni_use_default_callback_function=self.sni_use_default_callback_function,
389
+ sni_use_default_callback_function_extended=self.sni_use_default_callback_function_extended,
390
+ sni_add_new_domains_to_default_server_certificate=(
391
+ self.sni_add_new_domains_to_default_server_certificate),
392
+ sni_server_certificates_cache_directory=self.sni_server_certificates_cache_directory,
393
+ sni_create_server_certificate_for_each_domain=(
394
+ self.sni_create_server_certificate_for_each_domain),
395
+ sni_get_server_certificate_from_server_socket=(
396
+ self.sni_get_server_certificate_from_server_socket),
397
+ sni_server_certificate_from_server_socket_download_directory=(
398
+ self.sni_server_certificate_from_server_socket_download_directory),
399
+ skip_extension_id_list=self.skip_extension_id_list,
400
+ ca_certificate_name=self.ca_certificate_name,
401
+ ca_certificate_filepath=self.ca_certificate_filepath,
402
+ custom_server_certificate_usage=self.custom_server_certificate_usage,
403
+ custom_server_certificate_path=self.custom_server_certificate_path,
404
+ custom_private_key_path=self.custom_private_key_path,
405
+ domain_from_dns_server=domain_from_dns_server,
406
+ forwarding_dns_service_ipv4_list___only_for_localhost=(
407
+ self.forwarding_dns_service_ipv4_list___only_for_localhost),
408
+ tls=is_tls
409
+ )
410
+
164
411
  ssl_client_socket, accept_error_message = \
165
- creator.wrap_socket_with_ssl_context_server_sni_extended(
166
- client_socket, config=self.config, dns_domain=domain_from_dns_server,
167
- print_kwargs={'logger': self.logger})
412
+ sni_handler.wrap_socket_with_ssl_context_server_sni_extended(
413
+ client_socket,
414
+ print_kwargs={'logger': self.logger}
415
+ )
168
416
 
169
417
  if accept_error_message:
170
418
  # Write statistics after wrap is there was an error.
171
- statistics_csv.write_accept_error(
172
- error_message=accept_error_message, host=domain_from_dns_server,
173
- process_name=process_name,
174
- statistics_logger=self.statistics, print_kwargs={'logger': self.logger})
419
+ self.statistics_writer.write_accept_error(
420
+ error_message=accept_error_message,
421
+ host=domain_from_dns_server,
422
+ process_name=process_name)
175
423
 
176
424
  continue
177
425
 
178
- # ready_to_read, _, _ = select.select([client_socket], [], [])
179
- # if ready_to_read:
180
- # try:
181
- # # self.socket_object.do_handshake()
182
- # self.socket_object.accept()
183
- # except Exception:
184
- # raise
185
-
186
426
  # Create new arguments tuple that will be passed, since client socket and process_name
187
427
  # are gathered from SocketWrapper.
188
428
  if ssl_client_socket:
189
429
  # In order to use the same object, it needs to get nullified first, since the old instance
190
430
  # will not get overwritten. Though it still will show in the memory as SSLSocket, it will not
191
431
  # be handled as such, but as regular raw socket.
432
+ # noinspection PyUnusedLocal
192
433
  client_socket = None
193
434
  client_socket = ssl_client_socket
194
435
  thread_args = \
195
- (client_socket, process_name, is_tls, domain_from_dns_server) + reference_args
436
+ ((client_socket, process_name, is_tls, tls_type, tls_version, domain_from_dns_server) +
437
+ reference_function_args)
196
438
  # If 'pass_function_reference_to_thread' was set to 'False', execute the callable passed function
197
439
  # as is.
198
440
  if not pass_function_reference_to_thread:
199
- function_reference(thread_args, *args, **kwargs)
441
+ reference_function_name(thread_args)
200
442
  # If 'pass_function_reference_to_thread' was set to 'True', execute the callable function reference
201
443
  # in a new thread.
202
444
  else:
203
- self.send_accepted_socket_to_thread(function_reference, thread_args)
445
+ self._send_accepted_socket_to_thread(reference_function_name, thread_args)
204
446
  # Else, if no client_socket was opened during, accept, then print the error.
205
447
  else:
206
448
  # Write statistics after accept.
207
- statistics_csv.write_accept_error(
208
- error_message=accept_error_message, host=domain_from_dns_server, process_name=process_name,
209
- statistics_logger=self.statistics, print_kwargs={'logger': self.logger})
449
+ self.statistics_writer.write_accept_error(
450
+ error_message=accept_error_message,
451
+ host=domain_from_dns_server,
452
+ process_name=process_name)
210
453
  except Exception:
211
454
  print_api("Undocumented exception in while loop of listening sockets.", error_type=True,
212
- logger_method="error", traceback_string=True, oneline=True, logger=self.logger)
455
+ logger_method="error", traceback_string=True, logger=self.logger)
213
456
  pass
214
457
  continue
458
+
459
+ def _send_accepted_socket_to_thread(self, thread_function_name, reference_args=()):
460
+ # Creating thread for each socket
461
+ thread_current = threading.Thread(target=thread_function_name, args=(*reference_args,))
462
+ thread_current.daemon = True
463
+ thread_current.start()
464
+ # Append to list of threads, so they can be "joined" later
465
+ self.threads_list.append(thread_current)
466
+
467
+ # 'reference_args[0]' is the client socket.
468
+ client_address = base.get_source_address_from_socket(reference_args[0])
469
+
470
+ self.logger.info(f"Accepted connection, thread created {client_address}. Continue listening...")
@@ -1,23 +1,101 @@
1
1
  import datetime
2
2
 
3
3
  from ...print_api import print_api
4
+ from ...file_io import csvs
5
+ from ..loggingw import loggingw
4
6
 
5
7
 
6
- def write_accept_error(error_message: str, host: str, process_name: str, statistics_logger, print_kwargs: dict = None):
7
- request_time_sent = datetime.datetime.now()
8
+ LOGGER_NAME: str = 'statistics'
9
+ STATISTICS_HEADER: str = \
10
+ ('request_time_sent,tls,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,process_cmd,'
11
+ 'error')
8
12
 
9
- # If 'message' is not defined, it means there was no execution and there is no need for statistics.
10
- try:
11
- statistics_logger.info(
12
- f"{request_time_sent},"
13
- f"{host},"
14
- f",,,,,,"
15
- f"\"{process_name}\","
16
- f"{error_message}"
13
+
14
+ class StatisticsCSVWriter:
15
+ """
16
+ Class to write statistics to CSV file.
17
+ This can be initiated at the main, and then passed to the thread worker function.
18
+ """
19
+ def __init__(
20
+ self,
21
+ statistics_directory_path: str
22
+ ):
23
+ self.statistics_directory_path = statistics_directory_path
24
+
25
+ self.csv_logger = loggingw.create_logger(
26
+ logger_name=LOGGER_NAME,
27
+ directory_path=statistics_directory_path,
28
+ add_timedfile=True,
29
+ formatter_filehandler='MESSAGE',
30
+ file_type='csv',
31
+ header=STATISTICS_HEADER
32
+ )
33
+
34
+ def write_row(
35
+ self,
36
+ host: str,
37
+ tls_type: str,
38
+ tls_version: str,
39
+ path: str,
40
+ status_code: str,
41
+ command: str,
42
+ request_size_bytes: str,
43
+ response_size_bytes: str,
44
+ recorded_file_path: str = None,
45
+ process_cmd: str = None,
46
+ error: str = None,
47
+ request_time_sent=None,
48
+ ):
49
+ if not request_time_sent:
50
+ request_time_sent = datetime.datetime.now()
51
+
52
+ if not tls_type and not tls_version:
53
+ tls_info = ''
54
+ else:
55
+ tls_info = f'{tls_type}|{tls_version}'
56
+
57
+ escaped_line_string: str = csvs.escape_csv_line_to_string([
58
+ request_time_sent,
59
+ tls_info,
60
+ host,
61
+ path,
62
+ command,
63
+ status_code,
64
+ request_size_bytes,
65
+ response_size_bytes,
66
+ recorded_file_path,
67
+ process_cmd,
68
+ error
69
+ ])
70
+
71
+ self.csv_logger.info(escaped_line_string)
72
+
73
+ def write_accept_error(
74
+ self,
75
+ error_message: str,
76
+ host: str,
77
+ process_name: str
78
+ ):
79
+ """
80
+ Write the error message to the statistics CSV file.
81
+ This is used for easier execution, since most of the parameters will be empty on accept.
82
+
83
+ :param error_message: string, error message.
84
+ :param host: string, host, the domain or IP address.
85
+ :param process_name: process name, the command line of the process.
86
+ :return:
87
+ """
88
+
89
+ self.write_row(
90
+ host=host,
91
+ tls_type='',
92
+ tls_version='',
93
+ path='',
94
+ status_code='',
95
+ command='',
96
+ request_size_bytes='',
97
+ response_size_bytes='',
98
+ recorded_file_path='',
99
+ process_cmd=process_name,
100
+ error=error_message
17
101
  )
18
- except UnboundLocalError:
19
- pass
20
- except Exception:
21
- message = "Undocumented exception after accept on building statistics."
22
- print_api(message, error_type=True, logger_method='error', traceback_string=True, oneline=True, **print_kwargs)
23
- pass