atomicshop 2.16.46__py3-none-any.whl → 2.17.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 CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.16.46'
4
+ __version__ = '2.17.0'
atomicshop/diff_check.py CHANGED
@@ -54,9 +54,9 @@ class DiffChecker:
54
54
  function input for that object. So, not always you know what your object type during class initialization.
55
55
  :param check_object_display_name: string, name of the object to display in the message.
56
56
  If not specified, the provided 'check_object' will be displayed.
57
- :param aggregation: boolean, if True, the object will be aggregated with other objects in the list of objects.
58
- Meaning, that the object will be checked against the existing objects in the list, and if it is not
59
- in the list, it will be added to the list. If it is in the list, it will be ignored.
57
+ #:param aggregation: boolean, if True, the object will be aggregated with other objects in the list of objects.
58
+ # Meaning, that the object will be checked against the existing objects in the list, and if it is not
59
+ # in the list, it will be added to the list. If it is in the list, it will be ignored.
60
60
  :param input_file_path: string, full file path for storing input file for current state of objects,
61
61
  to check later if this state isn't updated. If this variable is left empty, all the content will be saved
62
62
  in memory and input file will not be used.
@@ -143,6 +143,124 @@ def thread_worker_main(
143
143
 
144
144
  network_logger.info("Thread Finished. Will continue listening on the Main thread")
145
145
 
146
+ def process_client_raw_data_request() -> bool:
147
+ """
148
+ Process the client raw data request.
149
+
150
+ :return: True if the socket should be closed, False if not.
151
+ """
152
+
153
+ # If the message is empty, then the connection was closed already by the other side,
154
+ # so we can close the socket as well.
155
+ # If the received message from the client is not empty, then continue.
156
+ if not client_received_raw_data:
157
+ return True
158
+
159
+ # Putting the received message to the aggregating message class.
160
+ client_message.request_raw_bytes = client_received_raw_data
161
+
162
+ parse_http()
163
+ if protocol != '':
164
+ client_message.protocol = protocol
165
+
166
+ # Parse websocket frames only if it is not the first protocol upgrade request.
167
+ if protocol == 'Websocket' and cycle_count != 0:
168
+ client_message.request_raw_decoded = parse_websocket(client_message.request_raw_bytes)
169
+
170
+ # Custom parser, should parse HTTP body or the whole message if not HTTP.
171
+ parser_instance = parser(client_message)
172
+ parser_instance.parse()
173
+
174
+ # Converting body parsed to string on logging, since there is no strict rule for the parameter
175
+ # to be string.
176
+ parser_instance.logger.info(f"{str(client_message.request_body_parsed)[0: 100]}...")
177
+
178
+ return False
179
+
180
+ def create_responder_response():
181
+ # Since we're in response mode, we'll record the request anyway, after the responder did its job.
182
+ client_message.info = "In Server Response Mode"
183
+
184
+ # Re-initiate the 'client_message.response_list_of_raw_bytes' list, since we'll be appending
185
+ # new entries for empty list.
186
+ client_message.response_list_of_raw_bytes = list()
187
+
188
+ # If it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
189
+ # response automatically.
190
+ if protocol == 'Websocket' and cycle_count == 0:
191
+ client_message.response_list_of_raw_bytes.append(
192
+ websocket_parse.create_byte_http_response(client_message.request_raw_bytes))
193
+ # Creating response for parsed message and printing
194
+ responder.create_response(client_message)
195
+
196
+ # Output first 100 characters of all the responses in the list.
197
+ for response_raw_bytes_single in client_message.response_list_of_raw_bytes:
198
+ responder.logger.info(f"{response_raw_bytes_single[0: 100]}...")
199
+
200
+ def create_client_socket():
201
+ # If there is a custom certificate for the client for this domain, then we'll use it.
202
+ # noinspection PyTypeChecker
203
+ custom_client_pem_certificate_path: str = None
204
+ for subdomain, pem_file_path in mtls_dict.items():
205
+ if subdomain == client_message.server_name:
206
+ custom_client_pem_certificate_path = pem_file_path
207
+ break
208
+
209
+ # If we're on localhost, then use external services list in order to resolve the domain:
210
+ # config['tcp']['forwarding_dns_service_ipv4_list___only_for_localhost']
211
+ if client_message.client_ip in base.THIS_DEVICE_IP_LIST:
212
+ service_client_instance = socket_client.SocketClient(
213
+ service_name=client_message.server_name, service_port=client_message.destination_port,
214
+ tls=is_tls,
215
+ dns_servers_list=(
216
+ config_static.TCPServer.forwarding_dns_service_ipv4_list___only_for_localhost),
217
+ logger=network_logger,
218
+ custom_pem_client_certificate_file_path=custom_client_pem_certificate_path
219
+ )
220
+ # If we're not on localhost, then connect to domain directly.
221
+ else:
222
+ service_client_instance = socket_client.SocketClient(
223
+ service_name=client_message.server_name,
224
+ service_port=client_message.destination_port,
225
+ tls=is_tls,
226
+ logger=network_logger,
227
+ custom_pem_client_certificate_file_path=custom_client_pem_certificate_path
228
+ )
229
+
230
+ return service_client_instance
231
+
232
+ def process_received_response_from_service_client():
233
+ if client_message.error is not None:
234
+ statistics_error_list.append(client_message.error)
235
+
236
+ # Since we need a list for raw bytes, we'll add the 'response_raw_bytes' to our list object.
237
+ # But we need to re-initiate it first.
238
+ client_message.response_list_of_raw_bytes = list()
239
+ # If there was error during send or receive from the service and response was None,
240
+ # It means that there was no response at all because of the error.
241
+ if client_message.error and response_raw_bytes is None:
242
+ client_message.response_list_of_raw_bytes.append(None)
243
+ # If there was no error, but response came empty, it means that the service has closed the
244
+ # socket after it received the request, without sending any data.
245
+ elif client_message.error is None and response_raw_bytes is None:
246
+ client_message.response_list_of_raw_bytes.append("")
247
+ else:
248
+ client_message.response_list_of_raw_bytes.append(response_raw_bytes)
249
+
250
+ client_message.response_list_of_raw_decoded = list()
251
+ # Make HTTP Response parsing only if there was response at all.
252
+ if response_raw_bytes:
253
+ response_raw_decoded, is_http_response, response_parsing_error = (
254
+ HTTPResponseParse(response_raw_bytes).parse())
255
+
256
+ if is_http_response:
257
+ client_message.response_list_of_raw_decoded.append(response_raw_decoded)
258
+ elif protocol == 'Websocket' and cycle_count != 0:
259
+ response_decoded = parse_websocket(response_raw_bytes)
260
+ client_message.response_list_of_raw_decoded.append(response_decoded)
261
+ else:
262
+ client_message.response_list_of_raw_decoded.append(None)
263
+
146
264
  # Building client message object before the loop only for any exception to occurs, since we write it to
147
265
  # recording file in its current state.
148
266
  client_message: ClientMessage = ClientMessage()
@@ -172,7 +290,7 @@ def thread_worker_main(
172
290
 
173
291
  # Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
174
292
  # These should be outside any loop and initialized only once entering the thread.
175
- parser, responder, recorder = assign_class_by_domain(
293
+ parser, responder, recorder, mtls_dict = assign_class_by_domain(
176
294
  engines_usage=config_static.TCPServer.engines_usage,
177
295
  engines_list=engines_list,
178
296
  message_domain_name=server_name,
@@ -191,6 +309,7 @@ def thread_worker_main(
191
309
  network_logger.info(f"Thread Created - Client [{client_ip}:{source_port}] | "
192
310
  f"Destination service: [{server_name}:{destination_port}]")
193
311
 
312
+ end_socket: bool = False
194
313
  service_client = None
195
314
  # Loop while received message is not empty, if so, close socket, since other side already closed.
196
315
  # noinspection PyTypeChecker
@@ -217,79 +336,34 @@ def thread_worker_main(
217
336
  # Getting current time of message received from client.
218
337
  client_message.request_time_received = datetime.now()
219
338
 
220
- network_logger.info(f"Initializing Receiver on cycle: {str(cycle_count+1)}")
221
- # Getting message from the client over the socket using specific class.
222
- client_received_raw_data = receiver.Receiver(
223
- ssl_socket=client_socket, logger=network_logger).receive()
224
-
225
- # If the message is empty, then the connection was closed already by the other side,
226
- # so we can close the socket as well.
227
- # If the received message from the client is not empty, then continue.
228
- if client_received_raw_data:
229
- # Putting the received message to the aggregating message class.
230
- client_message.request_raw_bytes = client_received_raw_data
339
+ # Peek if there is some data in the socket.
340
+ # This is needed to check if the client just connects without sending data, if so we need to try and
341
+ # receive data from the server and send it to the client.
342
+ # We will do it only on the first cycle, after that the connection should work as usual.
343
+ # Sometimes the client will execute connection without sending data, just for the server to send response.
344
+ is_socket_ready: bool = True
345
+ if cycle_count == 0:
346
+ is_socket_ready = receiver.is_socket_ready_for_read(client_socket)
231
347
 
232
- parse_http()
233
- if protocol != '':
234
- client_message.protocol = protocol
348
+ if is_socket_ready:
349
+ network_logger.info(f"Initializing Receiver on cycle: {str(cycle_count+1)}")
350
+ # Getting message from the client over the socket using specific class.
351
+ client_received_raw_data = receiver.Receiver(
352
+ ssl_socket=client_socket, logger=network_logger).receive()
235
353
 
236
- # Parse websocket frames only if it is not the first protocol upgrade request.
237
- if protocol == 'Websocket' and cycle_count != 0:
238
- client_message.request_raw_decoded = parse_websocket(client_message.request_raw_bytes)
239
-
240
- # Custom parser, should parse HTTP body or the whole message if not HTTP.
241
- parser_instance = parser(client_message)
242
- parser_instance.parse()
243
-
244
- # Converting body parsed to string on logging, since there is no strict rule for the parameter
245
- # to be string.
246
- parser_instance.logger.info(f"{str(client_message.request_body_parsed)[0: 100]}...")
354
+ end_socket = process_client_raw_data_request()
247
355
 
356
+ if not end_socket:
248
357
  # If we're in response mode, execute responder.
249
358
  response_raw_bytes = None
250
359
  if config_static.TCPServer.server_response_mode:
251
- # Since we're in response mode, we'll record the request anyway, after the responder did its job.
252
- client_message.info = "In Server Response Mode"
253
-
254
- # Re-initiate the 'client_message.response_list_of_raw_bytes' list, since we'll be appending
255
- # new entries for empty list.
256
- client_message.response_list_of_raw_bytes = list()
257
-
258
- # If it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
259
- # response automatically.
260
- if protocol == 'Websocket' and cycle_count == 0:
261
- client_message.response_list_of_raw_bytes.append(
262
- websocket_parse.create_byte_http_response(client_message.request_raw_bytes))
263
- # Creating response for parsed message and printing
264
- responder.create_response(client_message)
265
-
266
- # Output first 100 characters of all the responses in the list.
267
- for response_raw_bytes in client_message.response_list_of_raw_bytes:
268
- if response_raw_bytes:
269
- responder.logger.info(f"{response_raw_bytes[0: 100]}...")
270
- else:
271
- responder.logger.info(f"Response empty...")
360
+ create_responder_response()
272
361
  # Else, we're not in response mode, then execute client connect and record section.
273
362
  else:
274
363
  # If "service_client" object is not defined, we'll define it.
275
- # If it's defined, then it means there's still active "ssl_socket" with connection to the service
276
- # domain.
364
+ # If it's defined, then there's still active "ssl_socket" with connection to the service domain.
277
365
  if not service_client:
278
- # If we're on localhost, then use external services list in order to resolve the domain:
279
- # config['tcp']['forwarding_dns_service_ipv4_list___only_for_localhost']
280
- if client_message.client_ip in base.THIS_DEVICE_IP_LIST:
281
- service_client = socket_client.SocketClient(
282
- service_name=client_message.server_name, service_port=client_message.destination_port,
283
- tls=is_tls,
284
- dns_servers_list=(
285
- config_static.TCPServer.forwarding_dns_service_ipv4_list___only_for_localhost),
286
- logger=network_logger
287
- )
288
- # If we're not on localhost, then connect to domain directly.
289
- else:
290
- service_client = socket_client.SocketClient(
291
- service_name=client_message.server_name, service_port=client_message.destination_port,
292
- tls=is_tls, logger=network_logger)
366
+ service_client = create_client_socket()
293
367
 
294
368
  # Sending current client message and receiving a response.
295
369
  # If there was an error it will be passed to "client_message" object class and if not, "None" will
@@ -297,40 +371,9 @@ def thread_worker_main(
297
371
  # If there was connection error or socket close, then "ssl_socket" of the "service_client"
298
372
  # will be empty.
299
373
  response_raw_bytes, client_message.error, client_message.server_ip, service_ssl_socket = (
300
- service_client.send_receive_to_service(client_message.request_raw_bytes))
301
-
302
- if client_message.error is not None:
303
- statistics_error_list.append(client_message.error)
304
-
305
- # Since we need a list for raw bytes, we'll add the 'response_raw_bytes' to our list object.
306
- # But we need to re-initiate it first.
307
- client_message.response_list_of_raw_bytes = list()
308
- # If there was error during send or receive from the service and response was None,
309
- # It means that there was no response at all because of the error.
310
- if client_message.error and response_raw_bytes is None:
311
- client_message.response_list_of_raw_bytes.append(None)
312
- # If there was no error, but response came empty, it means that the service has closed the
313
- # socket after it received the request, without sending any data.
314
- elif client_message.error is None and response_raw_bytes is None:
315
- client_message.response_list_of_raw_bytes.append("")
316
- else:
317
- client_message.response_list_of_raw_bytes.append(response_raw_bytes)
318
-
319
- client_message.response_list_of_raw_decoded = list()
320
- # Make HTTP Response parsing only if there was response at all.
321
- if response_raw_bytes:
322
- response_raw_decoded, is_http_response, response_parsing_error = (
323
- HTTPResponseParse(response_raw_bytes).parse())
324
-
325
- if is_http_response:
326
- client_message.response_list_of_raw_decoded.append(response_raw_decoded)
327
- elif protocol == 'Websocket' and cycle_count != 0:
328
- response_decoded = parse_websocket(response_raw_bytes)
329
- client_message.response_list_of_raw_decoded.append(response_decoded)
330
- else:
331
- client_message.response_list_of_raw_decoded.append(None)
332
-
374
+ service_client.send_receive_to_service(client_message.request_raw_bytes, (not is_socket_ready)))
333
375
 
376
+ process_received_response_from_service_client()
334
377
 
335
378
  # So if the socket was closed and there was an error we can break the loop
336
379
  if not service_ssl_socket:
@@ -338,8 +381,8 @@ def thread_worker_main(
338
381
  recorded = True
339
382
  break
340
383
 
341
- # If there is a response, then send it.
342
- if response_raw_bytes:
384
+ # If there is a response(s), then send it.
385
+ if client_message.response_list_of_raw_bytes:
343
386
  # Sending response/s to client no matter if in record mode or not.
344
387
  network_logger.info(
345
388
  f"Sending messages to client: {len(client_message.response_list_of_raw_bytes)}")
@@ -362,7 +405,10 @@ def thread_worker_main(
362
405
 
363
406
  record_and_statistics_write()
364
407
  recorded = True
365
- else:
408
+
409
+ # If the message is empty, then the connection was closed already by the other side, also if there will
410
+ # be empty response from the server, so we can close the socket as well and exceptions will be raised.
411
+ if end_socket:
366
412
  # If it's the first cycle we will record the message from the client if it came empty.
367
413
  if cycle_count == 0:
368
414
  record_and_statistics_write()
@@ -370,6 +416,7 @@ def thread_worker_main(
370
416
  # In other cases, we'll just break the loop, since empty message means that the other side closed the
371
417
  # connection.
372
418
  recorded = True
419
+
373
420
  break
374
421
 
375
422
  finish_thread()
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import argparse
2
3
  from typing import Literal
3
4
 
4
5
  from ... import filesystem
@@ -19,11 +20,17 @@ SCRIPT_DIRECTORY: str = filesystem.get_file_directory(__file__)
19
20
  ENGINES_DIRECTORY_PATH: str = filesystem.get_working_directory() + os.sep + ENGINES_DIRECTORY_NAME
20
21
 
21
22
 
23
+ def parse_arguments():
24
+ parser = argparse.ArgumentParser(description='Create a new engine module template.')
25
+ parser.add_argument('engine_name', type=str, help='The name of the new engine.')
26
+ return parser.parse_args()
27
+
28
+
22
29
  class CreateModuleTemplate:
23
- def __init__(self, engine_name: str, domains: list):
30
+ def __init__(self):
24
31
  # === Get input variables. ===
25
- self.engine_name: str = engine_name
26
- self.domains: list = domains
32
+ self.engine_name: str = parse_arguments().engine_name
33
+ self.domains: list = ['example.com']
27
34
 
28
35
  # New engine's directory.
29
36
  self.new_engine_directory: str = ENGINES_DIRECTORY_PATH + os.sep + self.engine_name
@@ -66,10 +73,14 @@ class CreateModuleTemplate:
66
73
 
67
74
  # Add "" to each domain.
68
75
  domains_with_quotes: list = [f'"{domain}"' for domain in self.domains]
69
- config_lines_list.append(f'domains = [{", ".join(domains_with_quotes)}]\n')
70
- config_lines_list.append(f'parser_file = "{self.parser_file_name}"')
71
- config_lines_list.append(f'responder_file = "{self.responder_file_name}"')
72
- config_lines_list.append(f'recorder_file = "{self.recorder_file_name}"')
76
+ config_lines_list.append(f'"domains" = [{", ".join(domains_with_quotes)}]\n')
77
+ # config_lines_list.append(f'\n')
78
+ config_lines_list.append(f'"parser_file" = "{self.parser_file_name}"')
79
+ config_lines_list.append(f'"responder_file" = "{self.responder_file_name}"')
80
+ config_lines_list.append(f'"recorder_file" = "{self.recorder_file_name}"\n')
81
+ # config_lines_list.append(f'\n')
82
+ config_lines_list.append(f'[mtls]')
83
+ config_lines_list.append(f'# "subdomain.domain.com" = "file_name_in_current_dir.pem"')
73
84
 
74
85
  config_file_path = self.new_engine_directory + os.sep + CONFIG_FILE_NAME
75
86
 
@@ -2,15 +2,11 @@ import os
2
2
  import sys
3
3
  from pathlib import Path
4
4
 
5
- from .. import filesystem
6
5
  from ..file_io import tomls
7
6
  from ..basics.classes import import_first_class_name_from_file_path
8
- from ..wrappers.loggingw import loggingw
9
7
  from .engines.__reference_general import parser___reference_general, responder___reference_general, \
10
8
  recorder___reference_general
11
9
 
12
- from . import config_static
13
-
14
10
 
15
11
  class ModuleCategory:
16
12
  def __init__(self, script_directory: str):
@@ -29,6 +25,8 @@ class ModuleCategory:
29
25
  # The instance of the recorder class that will be initiated once in the script start
30
26
  self.responder_instance = None
31
27
 
28
+ self.mtls: dict = dict()
29
+
32
30
  def fill_engine_fields_from_general_reference(self, engines_fullpath: str):
33
31
  # Reference module variables.
34
32
  self.engine_name = '__reference_general'
@@ -47,6 +45,7 @@ class ModuleCategory:
47
45
 
48
46
  # Getting the parameters from engine config file
49
47
  self.domain_list = configuration_data['domains']
48
+ self.mtls = configuration_data['mtls']
50
49
 
51
50
  # If there's module configuration file, but no domains in it, there's no point to continue.
52
51
  # Since, each engine is based on domains.
@@ -54,12 +53,12 @@ class ModuleCategory:
54
53
  raise ValueError(f"Engine Configuration file doesn't contain any domains: {engine_config_file_path}")
55
54
 
56
55
  # Full path to file
57
- self.parser_file_path = filesystem.get_paths_from_directory(
58
- engine_directory_path, get_file=True, file_name_check_pattern=configuration_data['parser_file'])[0].path
59
- self.responder_file_path = filesystem.get_paths_from_directory(
60
- engine_directory_path, get_file=True, file_name_check_pattern=configuration_data['responder_file'])[0].path
61
- self.recorder_file_path = filesystem.get_paths_from_directory(
62
- engine_directory_path, get_file=True, file_name_check_pattern=configuration_data['recorder_file'])[0].path
56
+ self.parser_file_path = f'{engine_directory_path}{os.sep}{configuration_data['parser_file']}'
57
+ self.responder_file_path = f'{engine_directory_path}{os.sep}{configuration_data['responder_file']}'
58
+ self.recorder_file_path = f'{engine_directory_path}{os.sep}{configuration_data['recorder_file']}'
59
+
60
+ for subdomain, file_name in self.mtls.items():
61
+ self.mtls[subdomain] = f'{engine_directory_path}{os.sep}{file_name}'
63
62
 
64
63
  def initialize_engine(self, logs_path: str, logger=None, reference_general: bool = False, **kwargs):
65
64
  # Initiating logger for each engine by its name
@@ -108,6 +107,7 @@ def assign_class_by_domain(
108
107
  function_parser = None
109
108
  function_responder = None
110
109
  function_recorder = None
110
+ mtls_data: dict = dict()
111
111
 
112
112
  # In case SNI came empty in the request from client, then there's no point in iterating through engine domains.
113
113
  if message_domain_name:
@@ -130,6 +130,8 @@ def assign_class_by_domain(
130
130
  function_recorder = function_module.recorder_class_object
131
131
  # Since the responder is being initiated only once, we're assigning only the instance
132
132
  function_responder = function_module.responder_instance
133
+ mtls_data = function_module.mtls
134
+
133
135
 
134
136
  logger.info(f"Assigned Modules for [{message_domain_name}]: "
135
137
  f"{function_module.parser_class_object.__name__}, "
@@ -151,4 +153,4 @@ def assign_class_by_domain(
151
153
  function_responder = reference_module.responder_instance
152
154
 
153
155
  # Return all the initiated modules
154
- return function_parser, function_responder, function_recorder
156
+ return function_parser, function_responder, function_recorder, mtls_data
@@ -1,7 +1,6 @@
1
1
  import datetime
2
2
  import os
3
3
  import multiprocessing
4
- from pathlib import Path
5
4
 
6
5
  from ..archiver import zips
7
6
  from .. import filesystem
@@ -1,8 +1,6 @@
1
- import json
2
1
  import inspect
3
2
 
4
3
  from ..wrappers.loggingw import loggingw
5
- from ..basics import dicts
6
4
 
7
5
  from . import config_static
8
6
 
File without changes
File without changes
@@ -0,0 +1,42 @@
1
+ import os
2
+
3
+ import google.generativeai as genai
4
+
5
+
6
+ class GoogleLLM:
7
+ def __init__(
8
+ self,
9
+ llm_api_key: str
10
+ ) -> None:
11
+ self.genai = genai
12
+
13
+ os.environ["API_KEY"] = llm_api_key
14
+ genai.configure(api_key=os.environ["API_KEY"])
15
+
16
+ def get_current_models(self) -> list[str]:
17
+ """ Function to get the current models available in the Gemini API """
18
+ result_list: list[str] = []
19
+ for model in self.genai.list_models():
20
+ result_list.append(model.name)
21
+
22
+ return result_list
23
+
24
+ def get_answer_online(
25
+ self,
26
+ search_query: str,
27
+ additional_llm_instructions: str,
28
+ number_of_top_links: int = 2,
29
+ number_of_characters_per_link: int = 15000,
30
+ temperature: float = 0,
31
+ max_output_tokens: int = 4096
32
+ ):
33
+ """
34
+ Function to get the answer to a question by searching Google Custom Console API and processing the content using Gemini API.
35
+ :param search_query:
36
+ :param additional_llm_instructions:
37
+ :param number_of_top_links:
38
+ :param number_of_characters_per_link:
39
+ :param temperature:
40
+ :param max_output_tokens:
41
+ :return:
42
+ """
@@ -40,8 +40,7 @@ def install_fibratus(
40
40
  exclude_string='slim')
41
41
 
42
42
  # Install the MSI file
43
- msiw.install_msi(
44
- msi_path=fibratus_setup_file_path, exit_on_error=True, as_admin=True)
43
+ msiw.install_msi(msi_path=fibratus_setup_file_path)
45
44
 
46
45
  count = 0
47
46
  while count != WAIT_SECONDS_FOR_EXECUTABLE_TO_APPEAR_AFTER_INSTALLATION:
@@ -154,13 +154,16 @@ class Certificator:
154
154
  service_name=sni_received_parameters.destination_name,
155
155
  service_port=base.get_destination_address_from_socket(sni_received_parameters.ssl_socket)[1],
156
156
  tls=self.tls,
157
- dns_servers_list=self.forwarding_dns_service_ipv4_list___only_for_localhost)
157
+ dns_servers_list=self.forwarding_dns_service_ipv4_list___only_for_localhost,
158
+ logger=print_kwargs.get('logger') if print_kwargs else None
159
+ )
158
160
  # If we're not on localhost, then connect to domain directly.
159
161
  else:
160
162
  service_client = socket_client.SocketClient(
161
163
  service_name=sni_received_parameters.destination_name,
162
164
  service_port=base.get_destination_address_from_socket(sni_received_parameters.ssl_socket)[1],
163
- tls=self.tls
165
+ tls=self.tls,
166
+ logger=print_kwargs.get('logger') if print_kwargs else None
164
167
  )
165
168
 
166
169
  # If certificate from socket exists, then we don't need to get it from the socket and write to file.
@@ -37,11 +37,26 @@ def create_ssl_context_for_client():
37
37
  return ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
38
38
 
39
39
 
40
- def set_client_ssl_context_default_certs(ssl_context):
41
- # "load_default_certs" method is telling the client to check the local certificate storage on the system for the
42
- # needed certificate of the server. Without this line you will get an error from the server that the client
43
- # is using self-signed certificate. Which is partly true, since you used the SLL wrapper,
44
- # but didn't specify the certificate at all.
40
+ def set_client_ssl_context_ca_default_certs(ssl_context):
41
+ """
42
+ "load_default_certs" method is telling the client to check the local certificate storage on the system for the
43
+ needed certificate of the server. Without this line you will get an error from the server that the client
44
+ is using self-signed certificate. Which is partly true, since you used the SLL wrapper,
45
+ but didn't specify the certificate at all.
46
+ -----------------------------------------
47
+ https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_default_certs
48
+ Load a set of default “certification authority” (CA) certificates from default locations.
49
+ On Windows it loads CA certs from the CA and ROOT system stores.
50
+ On all systems it calls SSLContext.set_default_verify_paths().
51
+ In the future the method may load CA certificates from other locations, too.
52
+
53
+ The purpose flag specifies what kind of CA certificates are loaded.
54
+ The default settings Purpose.SERVER_AUTH loads certificates, that are flagged and trusted for
55
+ TLS web server authentication (client side sockets). Purpose.CLIENT_AUTH loads CA certificates for
56
+ client certificate verification on the server side.
57
+ -----------------------------------------
58
+ """
59
+
45
60
  # The purpose of the certificate is to authenticate on the server
46
61
  # context.load_default_certs(Purpose.SERVER_AUTH)
47
62
  # You don't have to specify the purpose to connect, but if you get a purpose error, you know where to find it
@@ -187,10 +202,26 @@ def set_listen_on_socket(socket_object, **kwargs):
187
202
  # Socket Creator Presets
188
203
 
189
204
  def wrap_socket_with_ssl_context_client___default_certs___ignore_verification(
190
- socket_object, server_hostname: str = None):
205
+ socket_object,
206
+ server_hostname: str = None,
207
+ custom_pem_client_certificate_file_path: str = None
208
+ ):
209
+ """
210
+ This function is a preset for wrapping the socket with SSL context for the client.
211
+ It sets the CA default certificates, and ignores the server's certificate verification.
212
+
213
+ :param socket_object: socket.socket object
214
+ :param server_hostname: string, hostname of the server. Default is None.
215
+ :param custom_pem_client_certificate_file_path: string, full file path for the client certificate PWM file.
216
+ Default is None.
217
+ """
191
218
  ssl_context: ssl.SSLContext = create_ssl_context_for_client()
192
- set_client_ssl_context_default_certs(ssl_context)
219
+ set_client_ssl_context_ca_default_certs(ssl_context)
193
220
  set_client_ssl_context_certificate_verification_ignore(ssl_context)
221
+
222
+ if custom_pem_client_certificate_file_path:
223
+ ssl_context.load_cert_chain(certfile=custom_pem_client_certificate_file_path, keyfile=None)
224
+
194
225
  ssl_socket: ssl.SSLSocket = wrap_socket_with_ssl_context_client(
195
226
  socket_object, ssl_context, server_hostname=server_hostname)
196
227
 
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import socket
3
3
  import ssl
4
+ import select
4
5
  from pathlib import Path
5
6
 
6
7
  from ...print_api import print_api
@@ -20,6 +21,24 @@ def peek_first_bytes(client_socket, bytes_amount: int = 1) -> bytes:
20
21
  return client_socket.recv(bytes_amount, socket.MSG_PEEK)
21
22
 
22
23
 
24
+ def is_socket_ready_for_read(client_socket, timeout: int = 0) -> bool:
25
+ """
26
+ Check if socket is ready for read.
27
+
28
+ :param client_socket: Socket object.
29
+ :param timeout: Timeout in seconds. The default is no timeout.
30
+
31
+ :return: True if socket is ready for read, False otherwise.
32
+ """
33
+
34
+ # Use select to check if the socket is ready for reading.
35
+ # 'readable' returns a list of sockets that are ready for reading.
36
+ # Since we use only one socket, it will return a list with one element if the socket is ready for reading,
37
+ # or an empty list if the socket is not ready for reading.
38
+ readable, _, _ = select.select([client_socket], [], [], timeout)
39
+ return bool(readable)
40
+
41
+
23
42
  class Receiver:
24
43
  """ Receiver Class is responsible for receiving the message from socket and populate the message class """
25
44
  def __init__(
@@ -6,7 +6,7 @@ from ...print_api import print_api
6
6
  from ..loggingw import loggingw
7
7
  from ...basics import tracebacks
8
8
 
9
- from . import base, ssl_base
9
+ from . import base
10
10
 
11
11
 
12
12
  class Sender:
@@ -254,15 +254,13 @@ class SNIHandler:
254
254
 
255
255
  # Try on general settings in the SNI function.
256
256
  try:
257
- # Check if SNI was passed.
258
- if self.sni_received_parameters.destination_name:
259
- service_name_from_sni = self.sni_received_parameters.destination_name
260
- # If no SNI was passed.
261
- else:
257
+ # Check if SNI was passed. If no SNI was passed.
258
+ if not self.sni_received_parameters.destination_name:
262
259
  # If DNS server is enabled we'll get the domain from dns server.
263
260
  if self.domain_from_dns_server:
264
- service_name_from_sni = self.domain_from_dns_server
265
- message = f"SNI Handler: No SNI was passed, using domain from DNS Server: {service_name_from_sni}"
261
+ self.sni_received_parameters.destination_name = self.domain_from_dns_server
262
+ message = \
263
+ f"SNI Handler: No SNI was passed, using domain from DNS Server: {self.domain_from_dns_server}"
266
264
  print_api(message, **(print_kwargs or {}))
267
265
  # If DNS server is disabled, the domain from dns server will be empty.
268
266
  else:
@@ -271,7 +269,7 @@ class SNIHandler:
271
269
  print_api(message, **(print_kwargs or {}))
272
270
 
273
271
  # Setting "server_hostname" as a domain.
274
- self.sni_received_parameters.ssl_socket.server_hostname = service_name_from_sni
272
+ self.sni_received_parameters.ssl_socket.server_hostname = self.sni_received_parameters.destination_name
275
273
  message = \
276
274
  f"SNI Handler: port {self.sni_received_parameters.ssl_socket.getsockname()[1]}: " \
277
275
  f"Incoming connection for [{self.sni_received_parameters.ssl_socket.server_hostname}]"
@@ -16,7 +16,7 @@ from .sender import Sender
16
16
  from . import ssl_base
17
17
  from .. import cryptographyw
18
18
  from ..loggingw import loggingw
19
- from ...print_api import print_api
19
+ from ... import print_api
20
20
  from ...file_io import file_io
21
21
  from ...basics import tracebacks
22
22
 
@@ -29,7 +29,8 @@ class SocketClient:
29
29
  tls: bool = False,
30
30
  connection_ip=None,
31
31
  dns_servers_list=None,
32
- logger: logging.Logger = None
32
+ logger: logging.Logger = None,
33
+ custom_pem_client_certificate_file_path: str = None
33
34
  ):
34
35
  """
35
36
  If you have a certificate for domain, but not for the IPv4 address, the SSL Socket context can be created for
@@ -47,6 +48,8 @@ class SocketClient:
47
48
  :param dns_servers_list: (Optional) List object with dns IPv4 addresses that 'service_name' will be resolved
48
49
  with, using 'dnspython' module. 'connection_ip' will be populated with first resolved IP.
49
50
  :param logger: (Optional) Logger object. If not provided, the default logger will be used.
51
+ :param custom_pem_client_certificate_file_path: (Optional) If specified, the SSL Socket will be created with
52
+ custom client certificate. The path to the file with the certificate should be provided.
50
53
 
51
54
  If both 'connection_ip' and 'dns_servers_list' specified, ValueException with raise.
52
55
  """
@@ -55,6 +58,7 @@ class SocketClient:
55
58
  self.tls: bool = tls
56
59
  self.connection_ip = connection_ip
57
60
  self.dns_servers_list = dns_servers_list
61
+ self.custom_pem_client_certificate_file_path: str = custom_pem_client_certificate_file_path
58
62
 
59
63
  if logger:
60
64
  # Create child logger for the provided logger with the module's name.
@@ -79,13 +83,15 @@ class SocketClient:
79
83
  def create_service_socket(self):
80
84
  # If TLS is enabled.
81
85
  if not self.tls:
82
- self.logger.info(f"Creating non-SSL socket to [{self.service_name}:{self.service_port}]")
86
+ log_message: str = f"Creating non-SSL socket to [{self.service_name}:{self.service_port}]"
87
+ print_api.print_api(log_message, logger=self.logger, logger_method='info')
83
88
  return creator.create_socket_ipv4_tcp()
84
89
  else:
85
- self.logger.info(f"Creating SSL socket to [{self.service_name}:{self.service_port}]")
90
+ log_message: str = f"Creating SSL socket to [{self.service_name}:{self.service_port}]"
91
+ print_api.print_api(log_message, logger=self.logger, logger_method='info')
86
92
  socket_object = creator.create_socket_ipv4_tcp()
87
93
  return creator.wrap_socket_with_ssl_context_client___default_certs___ignore_verification(
88
- socket_object, self.service_name)
94
+ socket_object, self.service_name, self.custom_pem_client_certificate_file_path)
89
95
 
90
96
  def service_connection(
91
97
  self
@@ -141,7 +147,7 @@ class SocketClient:
141
147
  error_string = (
142
148
  f"Socket Client Connect: {exception_type}: "
143
149
  f"Domain {self.service_name} doesn't exist - Couldn't resolve with {self.dns_servers_list}.")
144
- print_api(error_string, logger=self.logger, logger_method='error')
150
+ print_api.print_api(error_string, logger=self.logger, logger_method='error')
145
151
  return None, error_string
146
152
 
147
153
  # If DNS was resolved correctly or DNS servers weren't specified - we can try connecting.
@@ -163,16 +169,16 @@ class SocketClient:
163
169
  error_string: str = f"Socket Client Connect: {destination}: {exception_type}"
164
170
 
165
171
  if exception_type in ['ConnectionRefusedError', 'ConnectionAbortedError', 'ConnectionResetError',
166
- 'ssl.SSLError', 'TimeoutError']:
172
+ 'TimeoutError'] or 'ssl' in exception_type.lower():
167
173
  error_message: str = f"{error_string}: {exception_error}"
168
- print_api(error_message, logger=self.logger, logger_method='error')
174
+ print_api.print_api(error_message, logger=self.logger, logger_method='error')
169
175
  return None, error_message
170
176
  elif exception_type == 'socket.gaierror':
171
177
  custom_error_message: str = (
172
178
  f"Couldn't resolve [{self.service_name}] to IP using default methods. "
173
179
  f"Domain doesn't exist or there's no IP assigned to it.")
174
180
  error_message: str = f"{error_string}: {custom_error_message}"
175
- print_api(error_message, logger=self.logger, logger_method='error')
181
+ print_api.print_api(error_message, logger=self.logger, logger_method='error')
176
182
  return None, error_message
177
183
  else:
178
184
  raise e
@@ -192,7 +198,18 @@ class SocketClient:
192
198
  self.logger.info(f"Closed socket to service server [{self.service_name}:{self.service_port}]")
193
199
 
194
200
  # noinspection PyUnusedLocal
195
- def send_receive_to_service(self, request_bytes: bytearray):
201
+ def send_receive_to_service(
202
+ self,
203
+ request_bytes: Union[bytearray, bytes],
204
+ skip_send: bool = False
205
+ ):
206
+ """
207
+ Function to send data to service server and receive response.
208
+
209
+ :param request_bytes: The data that will be sent to the service server.
210
+ :param skip_send: If True, the data will not be sent to the service server. After the connection is established,
211
+ the function will wait for the response only.
212
+ """
196
213
  # Define variables
197
214
  function_service_data = None
198
215
  error_message = None
@@ -214,18 +231,22 @@ class SocketClient:
214
231
  self.logger.info(
215
232
  f"[{self.service_name}] resolves to ip: [{self.connection_ip}]. Pulled IP from the socket.")
216
233
 
217
- # Send the data received from the client to the service over socket
218
- error_on_send: str = Sender(
219
- ssl_socket=self.socket_instance, class_message=request_bytes, logger=self.logger).send()
234
+ # noinspection PyTypeChecker
235
+ error_on_send: str = None
236
+ if not skip_send:
237
+ # Send the data received from the client to the service over socket
238
+ error_on_send = Sender(
239
+ ssl_socket=self.socket_instance, class_message=request_bytes, logger=self.logger).send()
220
240
 
221
- # If the socket disconnected on data send
222
- if error_on_send:
223
- error_message = f"Service socket closed on data send: {error_on_send}"
241
+ # If the socket disconnected on data send
242
+ if error_on_send:
243
+ error_message = f"Service socket closed on data send: {error_on_send}"
244
+
245
+ # We'll close the socket and nullify the object
246
+ self.close_socket()
224
247
 
225
- # We'll close the socket and nullify the object
226
- self.close_socket()
227
248
  # Else if send was successful
228
- else:
249
+ if not error_on_send:
229
250
  function_service_data = Receiver(
230
251
  ssl_socket=self.socket_instance, logger=self.logger).receive()
231
252
 
@@ -355,7 +376,7 @@ class SocketClient:
355
376
  server_socket_for_certificate, error_message = self.service_connection()
356
377
  # Get the DER byte certificate from the socket.
357
378
  certificate_from_socket_der_bytes = ssl_base.get_certificate_from_socket(server_socket_for_certificate)
358
- print_api('Fetched certificate from socket.', logger=self.logger, **kwargs)
379
+ print_api.print_api('Fetched certificate from socket.', logger=self.logger, **kwargs)
359
380
  # Close the socket.
360
381
  self.close_socket()
361
382
 
@@ -49,7 +49,7 @@ def is_sysmon_running():
49
49
  """
50
50
 
51
51
  process_list: list = process.get_running_processes_by_cmdline_pattern(
52
- pattern=SYSMON_FILE_NAME, first=True, process_name_case_insensitive=True)
52
+ pattern=SYSMON_FILE_NAME, first=True, cmdline_case_insensitive=True)
53
53
 
54
54
  if process_list:
55
55
  return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.46
3
+ Version: 2.17.0
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=lwyRrx1298W0LP5WyV3pYJw7KJMlCtYSIh5JN2AhC58,124
1
+ atomicshop/__init__.py,sha256=JaX16ugkhtjjNo6Te9NZk6rsS2IsN9ETiFc6wSyNBes,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -9,7 +9,7 @@ atomicshop/config_init.py,sha256=50kD2lXP8sgwPekcmAbfADcY46YvXkF-6XIdA7W_638,250
9
9
  atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,4667
10
10
  atomicshop/console_user_response.py,sha256=31HIy9QGXa7f-GVR8MzJauQ79E_ZqAeagF3Ks4GGdDU,3234
11
11
  atomicshop/datetimes.py,sha256=IQZ66lmta-ZqxYbyHzm_9eugbJFSilXK1e0kfMgoXGg,18371
12
- atomicshop/diff_check.py,sha256=Q9RCqRa-jEgo7Fujx08_JTuZ6qcgttMI6aNYB6zN9Ik,27173
12
+ atomicshop/diff_check.py,sha256=vxTDccVbGZHEge6Ja9_ArLWwslOUgIoJAdYPylh4cZg,27176
13
13
  atomicshop/dns.py,sha256=5Gimq_WY2arqg7BeGmR7P--fGfnH0Dsh8lrOt_H0jRY,6817
14
14
  atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
15
15
  atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
@@ -126,16 +126,16 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
126
126
  atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
127
  atomicshop/mitm/config_static.py,sha256=ROAtbibSWSsF3BraUbhu-QO3MPIFqYY5KUKgsQbiSkk,7813
128
128
  atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
129
- atomicshop/mitm/connection_thread_worker.py,sha256=ZIlGoi-uN8YxT_-f6xYO9w2IbOAg0qBa7m4T87Uu58I,19769
129
+ atomicshop/mitm/connection_thread_worker.py,sha256=EtEp6aymfQ-btZzeZDmxLJdIortB4Gf5MOaHwEFSShI,21095
130
130
  atomicshop/mitm/import_config.py,sha256=0Ij14aISTllTOiWYJpIUMOWobQqGofD6uafui5uWllE,9272
131
- atomicshop/mitm/initialize_engines.py,sha256=VyJE8QnzlgD3QbX5inz5o6rC3zQ3is9CeTL7-B10g1w,8292
131
+ atomicshop/mitm/initialize_engines.py,sha256=NWz0yBErSrYBn0xWkJDBcHStBJ-kcsv9VtorcSP9x5M,8258
132
132
  atomicshop/mitm/message.py,sha256=URR5JKSuAT8XmGIkyprEjlPW2GW4ef_gfUz_GgcFseE,2184
133
133
  atomicshop/mitm/mitm_main.py,sha256=5c-9oxBiLueTbZr4Dyd4EEOorEUix5vSWxX9p5O1fBs,23375
134
- atomicshop/mitm/recs_files.py,sha256=mMyO1kPB-VkS_pbWCDhZHKdbWzlPbYSout61QuzHOao,3077
135
- atomicshop/mitm/shared_functions.py,sha256=l6oEyv4ug5D_03V3QLADYoocbcL2Ml_dYVW2WKM21l4,1818
134
+ atomicshop/mitm/recs_files.py,sha256=gzFuTonqcXkMvhpOj1Nwse3E8umFGrKN2H5AleMjJ3w,3051
135
+ atomicshop/mitm/shared_functions.py,sha256=0lzeyINd44sVEfFbahJxQmz6KAMWbYrW5ou3UYfItvw,1777
136
136
  atomicshop/mitm/statistic_analyzer.py,sha256=5_sAYGX2Xunzo_pS2W5WijNCwr_BlGJbbOO462y_wN4,27533
137
137
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
- atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvita0qsusdFGm8NZLsZ-XMs,4853
138
+ atomicshop/mitm/engines/create_module_template.py,sha256=TAzsA4eLD2wYr7auuL4Nf_71iXqn-BOBXlSkNVrnYD4,5336
139
139
  atomicshop/mitm/engines/create_module_template_example.py,sha256=X5xhvbV6-g9jU_bQVhf_crZmaH50LRWz3bS-faQ18ds,489
140
140
  atomicshop/mitm/engines/__parent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  atomicshop/mitm/engines/__parent/parser___parent.py,sha256=RK2wviepP0oeq7zuLpgkvqvTJtc0r0a7hDGWdV0dGc4,657
@@ -175,6 +175,9 @@ atomicshop/startup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
175
175
  atomicshop/startup/win/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
176
  atomicshop/startup/win/startup_folder.py,sha256=2RZEyF-Mf8eWPlt_-OaoGKKnMs6YhELEzJZ376EI0E0,1891
177
177
  atomicshop/startup/win/task_scheduler.py,sha256=qALe-8sfthYxsdCViH2r8OsH3x-WauDqteg5RzElPdk,4348
178
+ atomicshop/web_apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
+ atomicshop/web_apis/google_custom_search.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
+ atomicshop/web_apis/google_llm.py,sha256=WVLqyfZHFIGEncxdBvrHCv2FbvQw40z75uMGzq9lxB4,1291
178
181
  atomicshop/wrappers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
182
  atomicshop/wrappers/_process_wrapper_curl.py,sha256=XkZZXYl7D0Q6UfdWqy-18AvpU0yVp9i2BVD2qRcXlkk,841
180
183
  atomicshop/wrappers/_process_wrapper_tar.py,sha256=WUMZFKNrlG4nJP9tWZ51W7BR1j_pIjsjgyAStmWjRGs,655
@@ -189,7 +192,7 @@ atomicshop/wrappers/olefilew.py,sha256=biD5m58rogifCYmYhJBrAFb9O_Bn_spLek_9HofLe
189
192
  atomicshop/wrappers/pipw.py,sha256=mu4jnHkSaYNfpBiLZKMZxEX_E2LqW5BVthMZkblPB_c,1317
190
193
  atomicshop/wrappers/process_wrapper_pbtk.py,sha256=ycPmBRnv627RWks6N8OhxJQe8Gu3h3Vwj-4HswPOw0k,599
191
194
  atomicshop/wrappers/pyopensslw.py,sha256=OBWxA6EJ2vU_Qlf4M8m6ilcG3hyYB4yB0EsXUf7NhEU,6804
192
- atomicshop/wrappers/sysmonw.py,sha256=MFF8ts0gHbXn2_QeH196UncOUtm4MnM2cQBzTOnfrnk,5351
195
+ atomicshop/wrappers/sysmonw.py,sha256=CdawuWuy_uUi3ALCm6lKP7pSyKeTk1MXyzOaTMbBSO8,5346
193
196
  atomicshop/wrappers/ubuntu_terminal.py,sha256=3UJaje_Ke5G9xEyj3b37XZ_KjR_FSSnb4gupdCyI-jE,11965
194
197
  atomicshop/wrappers/wslw.py,sha256=2Z7X0j5M2hoRZjbHfm_vqwNXZeptsdkNCdhdcM_S9vo,6998
195
198
  atomicshop/wrappers/certauthw/certauth.py,sha256=hKedW0DOWlEigSNm8wu4SqHkCQsGJ1tJfH7s4nr3Bk0,12223
@@ -246,7 +249,7 @@ atomicshop/wrappers/factw/rest/router.py,sha256=fdGok5ESBxcZHIBgM93l4yTPRGoeooQN
246
249
  atomicshop/wrappers/factw/rest/statistics.py,sha256=vznwzKP1gEF7uXz3HsuV66BU9wrp73N_eFqpFpye9Qw,653
247
250
  atomicshop/wrappers/factw/rest/status.py,sha256=4O3xS1poafwyUiLDkhyx4oMMe4PBwABuRPpOMnMKgIU,641
248
251
  atomicshop/wrappers/fibratusw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
- atomicshop/wrappers/fibratusw/install.py,sha256=PLVymDe0HuOvU0r2lje8BkQAgtiOWEeRO7n-1zKuL7A,3287
252
+ atomicshop/wrappers/fibratusw/install.py,sha256=GnaAAqcXRhovxZ3x5uB9RAXTMCh5xd5k1niCKTzh4Z0,3242
250
253
  atomicshop/wrappers/loggingw/consts.py,sha256=JWiUJEydjhwatBxtIJsGTmDUSTLbmIRidtR6qRLMaIY,1608
251
254
  atomicshop/wrappers/loggingw/filters.py,sha256=48UVhJHemCS0agXmQP8dHvAHM8r9DFphJ1TNEBP3Dlg,3545
252
255
  atomicshop/wrappers/loggingw/formatters.py,sha256=ZY12IokVY1G_Wzn2Zlv9qjK-e8CtIK6yUgUfPHvH2BU,5802
@@ -300,23 +303,23 @@ atomicshop/wrappers/pywin32w/wmis/win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0n
300
303
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
301
304
  atomicshop/wrappers/socketw/accepter.py,sha256=hZZKVYlF3LOHQJsSIEKXZUf6QXXWm-AtqXZevvaYigE,1732
302
305
  atomicshop/wrappers/socketw/base.py,sha256=DV6BWao9kiLAWimhVDKGEi3ISVaWk5iPHXtBHrO3uwc,2264
303
- atomicshop/wrappers/socketw/certificator.py,sha256=3CpQKtcW68FSbH6LVSEZTqWBS6Yg_-3K0x4nFkId4UY,12236
304
- atomicshop/wrappers/socketw/creator.py,sha256=3_OraDkw2DAWZfoSdY3svCGMOIxpjLEEY7NxWd7M5P4,9873
306
+ atomicshop/wrappers/socketw/certificator.py,sha256=mtWPJ_ew3OSwt0-1W4jaoco1VIY4NRCrMv3mDUxb_Cc,12418
307
+ atomicshop/wrappers/socketw/creator.py,sha256=OLcd7FyUk_k8iyoL-xYnCnNmolPSgio6OeQXth7NdLg,11414
305
308
  atomicshop/wrappers/socketw/dns_server.py,sha256=RklzINNuoMQn4PGGQEI5hiAldprbVwwvikY6u9X-jTY,49067
306
309
  atomicshop/wrappers/socketw/exception_wrapper.py,sha256=B-X5SHLSUIWToihH2MKnOB1F4A81_X0DpLLfnYKYbEc,7067
307
310
  atomicshop/wrappers/socketw/get_process.py,sha256=aJC-_qFUv3NgWCSUzDI72E4z8_-VTZE9NVZ0CwUoNlM,5698
308
- atomicshop/wrappers/socketw/receiver.py,sha256=XVvWOoeCo3vA0O5p19ryi-hcDIyx382WNG7WzMNVeYk,9322
309
- atomicshop/wrappers/socketw/sender.py,sha256=vjgU1TaADJjaYiZOkLzfxcdCbmkvjhEhVjSV5mmIbw8,4969
310
- atomicshop/wrappers/socketw/sni.py,sha256=J1kPnQ77XwKN1pO5aOI1c_VfhuivCm95OOaQxMpPuZ0,17627
311
- atomicshop/wrappers/socketw/socket_client.py,sha256=zDX7M3KfUOn0tXR92gZ119HVFgA8N_0ppQxfxVqNvKc,19867
311
+ atomicshop/wrappers/socketw/receiver.py,sha256=-QtKK0T_lmoAIypTYaIKOD3pgB1npWGPxcVEN37y_gk,10060
312
+ atomicshop/wrappers/socketw/sender.py,sha256=gwSzF51QD5paeeFav6fpbQpO8KgBO5lNztHYQyN5id0,4959
313
+ atomicshop/wrappers/socketw/sni.py,sha256=Nc8WMZZR21o5GXILQLVWbf7OzNPXAfE8trJY153e9Qk,17591
314
+ atomicshop/wrappers/socketw/socket_client.py,sha256=YUlwbasxYQqz1xUlPWxwEnSDSCYKGdxWb2CFAogefr8,21131
312
315
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
313
316
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9Exd4j3qhDXcFc74-E,35567
314
317
  atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
315
318
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=SDYI1cN0oaapvPeLxSXiJrelTy6xbZl-bopR0jAjVGE,3149
316
319
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
317
320
  atomicshop/wrappers/winregw/winreg_network.py,sha256=zZQfps-CdODQaTUADbHAwKHr5RUg7BLafnKWBbKaLN4,8728
318
- atomicshop-2.16.46.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
319
- atomicshop-2.16.46.dist-info/METADATA,sha256=omUbYa_iNUdkQVw-R3HI2c0QglteZBXwvixzYRx2jD4,10500
320
- atomicshop-2.16.46.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
321
- atomicshop-2.16.46.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
322
- atomicshop-2.16.46.dist-info/RECORD,,
321
+ atomicshop-2.17.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
322
+ atomicshop-2.17.0.dist-info/METADATA,sha256=MG763mizxXrtHLWqgLymqhAomm0BYskwwRsHU-mvJpY,10499
323
+ atomicshop-2.17.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
324
+ atomicshop-2.17.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
325
+ atomicshop-2.17.0.dist-info/RECORD,,