atomicshop 2.16.48__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.48'
4
+ __version__ = '2.17.0'
@@ -198,6 +198,14 @@ def thread_worker_main(
198
198
  responder.logger.info(f"{response_raw_bytes_single[0: 100]}...")
199
199
 
200
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
+
201
209
  # If we're on localhost, then use external services list in order to resolve the domain:
202
210
  # config['tcp']['forwarding_dns_service_ipv4_list___only_for_localhost']
203
211
  if client_message.client_ip in base.THIS_DEVICE_IP_LIST:
@@ -206,13 +214,18 @@ def thread_worker_main(
206
214
  tls=is_tls,
207
215
  dns_servers_list=(
208
216
  config_static.TCPServer.forwarding_dns_service_ipv4_list___only_for_localhost),
209
- logger=network_logger
217
+ logger=network_logger,
218
+ custom_pem_client_certificate_file_path=custom_client_pem_certificate_path
210
219
  )
211
220
  # If we're not on localhost, then connect to domain directly.
212
221
  else:
213
222
  service_client_instance = socket_client.SocketClient(
214
- service_name=client_message.server_name, service_port=client_message.destination_port,
215
- tls=is_tls, logger=network_logger)
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
+ )
216
229
 
217
230
  return service_client_instance
218
231
 
@@ -277,7 +290,7 @@ def thread_worker_main(
277
290
 
278
291
  # Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
279
292
  # These should be outside any loop and initialized only once entering the thread.
280
- parser, responder, recorder = assign_class_by_domain(
293
+ parser, responder, recorder, mtls_dict = assign_class_by_domain(
281
294
  engines_usage=config_static.TCPServer.engines_usage,
282
295
  engines_list=engines_list,
283
296
  message_domain_name=server_name,
@@ -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,7 +2,6 @@ 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
7
  from .engines.__reference_general import parser___reference_general, responder___reference_general, \
@@ -26,6 +25,8 @@ class ModuleCategory:
26
25
  # The instance of the recorder class that will be initiated once in the script start
27
26
  self.responder_instance = None
28
27
 
28
+ self.mtls: dict = dict()
29
+
29
30
  def fill_engine_fields_from_general_reference(self, engines_fullpath: str):
30
31
  # Reference module variables.
31
32
  self.engine_name = '__reference_general'
@@ -44,6 +45,7 @@ class ModuleCategory:
44
45
 
45
46
  # Getting the parameters from engine config file
46
47
  self.domain_list = configuration_data['domains']
48
+ self.mtls = configuration_data['mtls']
47
49
 
48
50
  # If there's module configuration file, but no domains in it, there's no point to continue.
49
51
  # Since, each engine is based on domains.
@@ -51,12 +53,12 @@ class ModuleCategory:
51
53
  raise ValueError(f"Engine Configuration file doesn't contain any domains: {engine_config_file_path}")
52
54
 
53
55
  # Full path to file
54
- self.parser_file_path = filesystem.get_paths_from_directory(
55
- engine_directory_path, get_file=True, file_name_check_pattern=configuration_data['parser_file'])[0].path
56
- self.responder_file_path = filesystem.get_paths_from_directory(
57
- engine_directory_path, get_file=True, file_name_check_pattern=configuration_data['responder_file'])[0].path
58
- self.recorder_file_path = filesystem.get_paths_from_directory(
59
- 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}'
60
62
 
61
63
  def initialize_engine(self, logs_path: str, logger=None, reference_general: bool = False, **kwargs):
62
64
  # Initiating logger for each engine by its name
@@ -105,6 +107,7 @@ def assign_class_by_domain(
105
107
  function_parser = None
106
108
  function_responder = None
107
109
  function_recorder = None
110
+ mtls_data: dict = dict()
108
111
 
109
112
  # In case SNI came empty in the request from client, then there's no point in iterating through engine domains.
110
113
  if message_domain_name:
@@ -127,6 +130,8 @@ def assign_class_by_domain(
127
130
  function_recorder = function_module.recorder_class_object
128
131
  # Since the responder is being initiated only once, we're assigning only the instance
129
132
  function_responder = function_module.responder_instance
133
+ mtls_data = function_module.mtls
134
+
130
135
 
131
136
  logger.info(f"Assigned Modules for [{message_domain_name}]: "
132
137
  f"{function_module.parser_class_object.__name__}, "
@@ -148,4 +153,4 @@ def assign_class_by_domain(
148
153
  function_responder = reference_module.responder_instance
149
154
 
150
155
  # Return all the initiated modules
151
- return function_parser, function_responder, function_recorder
156
+ return function_parser, function_responder, function_recorder, mtls_data
@@ -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
 
@@ -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.
@@ -165,14 +171,14 @@ class SocketClient:
165
171
  if exception_type in ['ConnectionRefusedError', 'ConnectionAbortedError', 'ConnectionResetError',
166
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
@@ -370,7 +376,7 @@ class SocketClient:
370
376
  server_socket_for_certificate, error_message = self.service_connection()
371
377
  # Get the DER byte certificate from the socket.
372
378
  certificate_from_socket_der_bytes = ssl_base.get_certificate_from_socket(server_socket_for_certificate)
373
- print_api('Fetched certificate from socket.', logger=self.logger, **kwargs)
379
+ print_api.print_api('Fetched certificate from socket.', logger=self.logger, **kwargs)
374
380
  # Close the socket.
375
381
  self.close_socket()
376
382
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.48
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=g4si8ZVmv-cmrXDPNTeEfgvWGFAXNNlfF5E-_j8xNMo,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
@@ -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=vfFClzwDfaoT4zHCOfky4DnEDoSonp9N1MCj62dS9Nk,20451
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=Naseof9JGY7oxDz9ueOyW7-KYTu7QDlFxv2ABqq9DD4,8219
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
134
  atomicshop/mitm/recs_files.py,sha256=gzFuTonqcXkMvhpOj1Nwse3E8umFGrKN2H5AleMjJ3w,3051
135
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
@@ -303,23 +303,23 @@ atomicshop/wrappers/pywin32w/wmis/win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0n
303
303
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
304
304
  atomicshop/wrappers/socketw/accepter.py,sha256=hZZKVYlF3LOHQJsSIEKXZUf6QXXWm-AtqXZevvaYigE,1732
305
305
  atomicshop/wrappers/socketw/base.py,sha256=DV6BWao9kiLAWimhVDKGEi3ISVaWk5iPHXtBHrO3uwc,2264
306
- atomicshop/wrappers/socketw/certificator.py,sha256=3CpQKtcW68FSbH6LVSEZTqWBS6Yg_-3K0x4nFkId4UY,12236
307
- 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
308
308
  atomicshop/wrappers/socketw/dns_server.py,sha256=RklzINNuoMQn4PGGQEI5hiAldprbVwwvikY6u9X-jTY,49067
309
309
  atomicshop/wrappers/socketw/exception_wrapper.py,sha256=B-X5SHLSUIWToihH2MKnOB1F4A81_X0DpLLfnYKYbEc,7067
310
310
  atomicshop/wrappers/socketw/get_process.py,sha256=aJC-_qFUv3NgWCSUzDI72E4z8_-VTZE9NVZ0CwUoNlM,5698
311
311
  atomicshop/wrappers/socketw/receiver.py,sha256=-QtKK0T_lmoAIypTYaIKOD3pgB1npWGPxcVEN37y_gk,10060
312
312
  atomicshop/wrappers/socketw/sender.py,sha256=gwSzF51QD5paeeFav6fpbQpO8KgBO5lNztHYQyN5id0,4959
313
313
  atomicshop/wrappers/socketw/sni.py,sha256=Nc8WMZZR21o5GXILQLVWbf7OzNPXAfE8trJY153e9Qk,17591
314
- atomicshop/wrappers/socketw/socket_client.py,sha256=9_VXXo8r4upP5v0Rhzx7dJIblM23_0Ggh2PfktYj-fE,20489
314
+ atomicshop/wrappers/socketw/socket_client.py,sha256=YUlwbasxYQqz1xUlPWxwEnSDSCYKGdxWb2CFAogefr8,21131
315
315
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
316
316
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9Exd4j3qhDXcFc74-E,35567
317
317
  atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
318
318
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=SDYI1cN0oaapvPeLxSXiJrelTy6xbZl-bopR0jAjVGE,3149
319
319
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
320
320
  atomicshop/wrappers/winregw/winreg_network.py,sha256=zZQfps-CdODQaTUADbHAwKHr5RUg7BLafnKWBbKaLN4,8728
321
- atomicshop-2.16.48.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
322
- atomicshop-2.16.48.dist-info/METADATA,sha256=zxLRU6GxTBO675CZCx48ARUCy2CYk9CTkD-0jhyE3AQ,10500
323
- atomicshop-2.16.48.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
324
- atomicshop-2.16.48.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
325
- atomicshop-2.16.48.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,,