atomicshop 2.16.13__py3-none-any.whl → 2.16.15__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.13'
4
+ __version__ = '2.16.15'
@@ -5,8 +5,10 @@ https://oidref.com/1.3.6.1.5.5.7.3.1
5
5
 
6
6
 
7
7
  import ssl
8
+ from typing import Literal
8
9
 
9
10
  from .wrappers import cryptographyw
11
+ from .wrappers.pywin32w import cert_store
10
12
  from .print_api import print_api
11
13
 
12
14
 
@@ -16,72 +18,165 @@ from .print_api import print_api
16
18
  SECONDS_NOT_AFTER_3_YEARS = 3 * 365 * 24 * 60 * 60
17
19
 
18
20
 
19
- def is_certificate_in_store(certificate, issuer_only: bool = False, thumbprint_only: bool = False):
21
+ def get_pem_certificate_from_string(certificate: str) -> str:
20
22
  """
21
- The function will check if the certificate is installed in the Windows certificate store.
22
-
23
- :param certificate: x509 object, certificate to check.
24
- :param issuer_only: bool, if True, will check only by the certificate issuer common name is installed in the store.
25
- The problem that the issuer common name is not unique, so it can be installed multiple times.
26
- :param thumbprint_only: bool, if True, will check only by the certificate thumbprint is installed in the store.
27
- The problem that searching by the thumbprint will not tell you if there are multiple certificates with the same
28
- issuer name.
29
- :return: bool, True if certificate is installed, False if not.
23
+ Some PEM certificates can contain a private key. This function will return only the certificate part.
24
+
25
+ :param certificate: string, PEM certificate.
26
+ :return: string, certificate part.
30
27
  """
31
28
 
32
- # Make sure the certificate is x509.Certificate object.
33
- certificate = cryptographyw.convert_object_to_x509(certificate)
34
- # Get the certificate thumbprint.
35
- thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(certificate)
36
- issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(certificate)
29
+ certificate_lines = certificate.split('\n')
30
+ certificate_part = ''
31
+ start = False
32
+ for line in certificate_lines:
33
+ if 'BEGIN CERTIFICATE' in line:
34
+ start = True
35
+ if start:
36
+ certificate_part += line + '\n'
37
+ if 'END CERTIFICATE' in line:
38
+ break
37
39
 
38
- # for store in ["CA", "ROOT", "MY"]:
39
- for cert, encoding, trust in ssl.enum_certificates("ROOT"):
40
- store_certificate = cryptographyw.convert_object_to_x509(cert)
41
- store_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(store_certificate)
42
- store_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(store_certificate)
43
-
44
- if issuer_only:
45
- if store_issuer_common_name == issuer_common_name:
46
- return True, certificate
47
- elif thumbprint_only:
48
- if store_thumbprint == thumbprint:
49
- return True, certificate
50
- elif not issuer_only and not thumbprint_only:
51
- if store_thumbprint == thumbprint and store_issuer_common_name == issuer_common_name:
52
- return True, certificate
40
+ return certificate_part
53
41
 
54
42
 
55
- def get_certificates_by_issuer_name(issuer_name: str, print_kwargs: dict = None):
43
+ def write_crt_certificate_file_in_pem_format_from_pem_file(
44
+ pem_file_path: str,
45
+ crt_file_path: str
46
+ ):
56
47
  """
57
- The function will return all certificates with the specified issuer name.
48
+ The function will read the PEM certificate file and write it to the CRT file in PEM format.
49
+ The function is used to convert the PEM certificate file to the CRT file.
58
50
 
59
- :param issuer_name: string, issuer name to search for.
60
- :param print_kwargs: dict, that contains all the arguments for 'print_api' function.
51
+ Basically the point here is that the CRT file is the same as the PEM file, but the extension is different,
52
+ and it doesn't support integrated private key.
61
53
 
62
- :return: list, of certificates with the specified issuer name.
54
+ :param pem_file_path: string, path to the PEM certificate file.
55
+ :param crt_file_path: string, path to the CRT certificate file.
63
56
  """
64
57
 
65
- if not print_kwargs:
66
- print_kwargs = {}
58
+ with open(pem_file_path, 'r') as f:
59
+ certificate_string = f.read()
60
+
61
+ certificate_pem = get_pem_certificate_from_string(certificate_string)
62
+
63
+ with open(crt_file_path, 'w') as f:
64
+ f.write(certificate_pem)
65
+
66
+
67
+ def is_certificate_in_store(
68
+ certificate: any = None,
69
+ by_cert_issuer: bool = True,
70
+ by_cert_thumbprint: bool = True,
71
+ issuer_name: str = None,
72
+ store_location: str = "ROOT"
73
+ ) -> tuple[bool, list]:
74
+ """
75
+ The function will check if the CA certificate is installed in the Windows certificate Trusted Root store.
76
+ NO ADMIN RIGHTS NEEDED.
77
+
78
+ :param certificate: x509 object, certificate to check. You can search by certificate or by issuer name.
79
+ Supported types:
80
+ string that is path to file will be imported as bytes object abd converted to x509.Certificate
81
+ After check if it's PEM or DER format.
82
+ string that is PEM certificate will be converted to bytes, then x509.Certificate
83
+ bytes of PEM or DER will be converted to x509.Certificate.
84
+ x509.Certificate will be returned as is.
85
+ :param by_cert_issuer: bool, if True, will check only by the certificate issuer common name is installed in the store.
86
+ The problem if the search will be by issuer alone, that the issuer common name is not unique,
87
+ so it can be installed multiple times.
88
+ :param by_cert_thumbprint: bool, if True, will check only by the certificate thumbprint is installed in the store.
89
+ The problem that searching by the thumbprint alone will not tell you if there are multiple
90
+ certificates with the same issuer name.
91
+ :param issuer_name: string, issuer name to search for. You can search by certificate or by issuer name.
92
+ :param store_location: string, store location to search in. Default is "ROOT".
93
+ :return: tuple(bool - True if certificate is installed and False if not, list of certificates found)
94
+ """
67
95
 
68
- certificates_list = []
96
+ if not by_cert_issuer and not by_cert_thumbprint:
97
+ raise ValueError('At least one of the parameters "by_issuer" or "by_thumbprint" must be True.')
98
+
99
+ if not certificate and not issuer_name:
100
+ raise ValueError('At least one of the parameters "certificate" or "issuer_name" must be provided.')
101
+ elif certificate and issuer_name:
102
+ raise ValueError('Only one of the parameters "certificate" or "issuer_name" must be provided.')
103
+
104
+ if certificate:
105
+ # Make sure the certificate is x509.Certificate object.
106
+ certificate_x509 = cryptographyw.convert_object_to_x509(certificate)
107
+ # Get the certificate thumbprint.
108
+ provided_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(certificate_x509)
109
+ provided_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(certificate_x509)
110
+ elif issuer_name:
111
+ provided_thumbprint = None
112
+ provided_issuer_common_name = issuer_name
113
+ else:
114
+ raise ValueError('At least one of the parameters "certificate" or "issuer_name" must be provided.')
69
115
 
70
- for cert, encoding, trust in ssl.enum_certificates("ROOT"):
116
+ # Iterate over all certificates in the store specifically in the ROOT.
117
+ # for store in ["CA", "ROOT", "MY"]:
118
+ result_found_list: list = []
119
+ found: bool = False
120
+ for cert, encoding, trust in ssl.enum_certificates(store_location):
71
121
  store_certificate = cryptographyw.convert_object_to_x509(cert)
72
122
  store_issuer_common_name: str = cryptographyw.get_issuer_common_name_from_x509(store_certificate)
123
+ store_thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(store_certificate)
73
124
 
74
- if store_issuer_common_name == issuer_name:
75
- certificates_list.append(store_certificate)
125
+ if certificate:
126
+ if by_cert_issuer and not by_cert_thumbprint:
127
+ if store_issuer_common_name == provided_issuer_common_name:
128
+ result_found_list.append(store_certificate)
129
+ found = True
130
+ elif by_cert_thumbprint and not by_cert_issuer:
131
+ if store_thumbprint == provided_thumbprint:
132
+ result_found_list.append(store_certificate)
133
+ found = True
134
+ elif by_cert_issuer and by_cert_thumbprint:
135
+ if store_thumbprint == provided_thumbprint and store_issuer_common_name == provided_issuer_common_name:
136
+ result_found_list.append(store_certificate)
137
+ found = True
138
+ elif issuer_name:
139
+ if store_issuer_common_name == provided_issuer_common_name:
140
+ result_found_list.append(store_certificate)
141
+ found = True
142
+
143
+ return found, result_found_list
144
+
145
+
146
+ def delete_certificate_by_issuer_name(
147
+ issuer_name: str,
148
+ store_location: Literal[
149
+ "ROOT",
150
+ "CA",
151
+ "MY"] = "ROOT",
152
+ print_kwargs: dict = None
153
+ ):
154
+ """
155
+ NEED ADMIN RIGHTS.
156
+ The function will remove all certificates with the specified issuer name.
157
+ There can be several certificates with this name.
76
158
 
77
- if certificates_list:
78
- for certificate_single in certificates_list:
79
- issuer_name = cryptographyw.get_issuer_common_name_from_x509(certificate_single)
80
- thumbprint = cryptographyw.get_sha1_thumbprint_from_x509(certificate_single)
81
- message = f'Issuer name: {issuer_name} | Thumbprint: {thumbprint}'
82
- print_api(message, **print_kwargs)
83
- else:
84
- message = f'No certificates with issuer name: {issuer_name}'
85
- print_api(message, **print_kwargs)
159
+ :param issuer_name: string, issuer name to search for.
160
+ :param store_location: string, store location to search in. Default is "ROOT".
161
+ :param print_kwargs: dict, print_api kwargs.
162
+ """
163
+
164
+ cert_store.delete_certificate_by_issuer_name(issuer_name, store_location, print_kwargs)
165
+
166
+
167
+ def install_certificate_file(
168
+ file_path: str,
169
+ store_location: Literal[
170
+ "ROOT", "CA", "MY"] = "ROOT",
171
+ print_kwargs: dict = None
172
+ ):
173
+ """
174
+ The function will install the certificate from the file to the specified store location.
175
+ NEED ADMIN RIGHTS.
176
+
177
+ :param file_path: string, full file path to the certificate file.
178
+ :param store_location: string, store location to install the certificate. Default is "ROOT".
179
+ :param print_kwargs: dict, print_api kwargs.
180
+ """
86
181
 
87
- return certificates_list
182
+ cert_store.install_certificate_file(file_path, store_location, print_kwargs)
@@ -4,7 +4,10 @@ from dataclasses import dataclass
4
4
  from . import import_config
5
5
 
6
6
 
7
- SCRIPT_VERSION: str = '1.7.4'
7
+ SCRIPT_VERSION: str = '1.7.6'
8
+ """
9
+ added ca cert check and installation
10
+ """
8
11
 
9
12
 
10
13
  # CONFIG = None
@@ -20,6 +23,8 @@ LIST_OF_BOOLEANS: list = [
20
23
  ('tcp', 'engines_usage'),
21
24
  ('tcp', 'server_response_mode'),
22
25
  ('logrec', 'enable_request_response_recordings_in_logs'),
26
+ ('certificates', 'install_ca_certificate_to_root_store'),
27
+ ('certificates', 'uninstall_unused_ca_certificates_with_mitm_ca_name'),
23
28
  ('certificates', 'default_server_certificate_usage'),
24
29
  ('certificates', 'sni_add_new_domains_to_default_server_certificate'),
25
30
  ('certificates', 'custom_server_certificate_usage'),
@@ -52,8 +57,11 @@ class MainConfig:
52
57
  # Certificates.
53
58
  default_server_certificate_name: str = 'default'
54
59
  ca_certificate_name: str = 'ElaborateCA'
60
+ ca_certificate_pem_filename: str = f'{ca_certificate_name}.pem'
61
+ ca_certificate_crt_filename: str = f'{ca_certificate_name}_for_manual_installation_not_used_by_script.crt'
55
62
  # CA Certificate name and file name without extension.
56
63
  ca_certificate_filepath: str = None
64
+ ca_certificate_crt_filepath: str = None
57
65
  # Default server certificate file name and path.
58
66
  default_server_certificate_filename = f'{default_server_certificate_name}.pem'
59
67
  default_server_certificate_filepath: str = None
@@ -62,7 +70,8 @@ class MainConfig:
62
70
  def update(cls):
63
71
  # This runs after the dataclass is initialized
64
72
  cls.ENGINES_DIRECTORY_PATH = cls.SCRIPT_DIRECTORY + os.sep + cls.ENGINES_DIRECTORY_NAME
65
- cls.ca_certificate_filepath = f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.ca_certificate_name}.pem'
73
+ cls.ca_certificate_filepath = f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.ca_certificate_pem_filename}'
74
+ cls.ca_certificate_crt_filepath = f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.ca_certificate_crt_filename}'
66
75
  cls.default_server_certificate_filepath = \
67
76
  f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.default_server_certificate_filename}'
68
77
 
@@ -105,12 +114,16 @@ class LogRec:
105
114
  logs_path: str
106
115
  recordings_path: str
107
116
  enable_request_response_recordings_in_logs: bool
117
+ store_logs_for_x_days: int
108
118
 
109
119
  recordings_directory_name: str = 'recs'
110
120
 
111
121
 
112
122
  @dataclass
113
123
  class Certificates:
124
+ install_ca_certificate_to_root_store: bool
125
+ uninstall_unused_ca_certificates_with_mitm_ca_name: bool
126
+
114
127
  default_server_certificate_usage: bool
115
128
  sni_add_new_domains_to_default_server_certificate: bool
116
129
 
@@ -117,6 +117,14 @@ def check_configurations() -> int:
117
117
  print_api(message, color='red')
118
118
  return 1
119
119
 
120
+ # This is checked directly in the SocketWrapper.
121
+ # if (config_static.Certificates.install_ca_certificate_to_root_store and not is_admin) or \
122
+ # (config_static.Certificates.uninstall_unused_ca_certificates_with_mitm_ca_name and not is_admin):
123
+ # message: str = \
124
+ # "Need to run the script with administrative rights to install or uninstall CA certificate.\nExiting..."
125
+ # print_api(message, color='red')
126
+ # return 1
127
+
120
128
  return 0
121
129
 
122
130
 
@@ -9,6 +9,8 @@ from ..wrappers.loggingw import loggingw
9
9
  from .engines.__reference_general import parser___reference_general, responder___reference_general, \
10
10
  recorder___reference_general
11
11
 
12
+ from . import config_static
13
+
12
14
 
13
15
  class ModuleCategory:
14
16
  def __init__(self, script_directory: str):
@@ -89,7 +91,8 @@ class ModuleCategory:
89
91
  add_stream=True,
90
92
  add_timedfile=True,
91
93
  formatter_streamhandler='DEFAULT',
92
- formatter_filehandler='DEFAULT'
94
+ formatter_filehandler='DEFAULT',
95
+ backupCount=config_static.LogRec.store_logs_for_x_days
93
96
  )
94
97
 
95
98
 
@@ -65,7 +65,8 @@ def mitm_server_main(config_file_path: str):
65
65
  add_stream=True,
66
66
  add_timedfile=True,
67
67
  formatter_streamhandler='DEFAULT',
68
- formatter_filehandler='DEFAULT'
68
+ formatter_filehandler='DEFAULT',
69
+ backupCount=config_static.LogRec.store_logs_for_x_days
69
70
  )
70
71
 
71
72
  # Writing first log.
@@ -208,7 +209,8 @@ def mitm_server_main(config_file_path: str):
208
209
  add_stream=True,
209
210
  add_timedfile=True,
210
211
  formatter_streamhandler='DEFAULT',
211
- formatter_filehandler='DEFAULT'
212
+ formatter_filehandler='DEFAULT',
213
+ backupCount=config_static.LogRec.store_logs_for_x_days
212
214
  )
213
215
  system_logger.info(f"Loaded network logger: {network_logger}")
214
216
 
@@ -227,12 +229,13 @@ def mitm_server_main(config_file_path: str):
227
229
  dns_server_instance = dns_server.DnsServer(
228
230
  listening_interface=config_static.DNSServer.listening_interface,
229
231
  listening_port=config_static.DNSServer.listening_port,
232
+ log_directory_path=config_static.LogRec.logs_path,
233
+ backupCount_log_files_x_days=config_static.LogRec.store_logs_for_x_days,
230
234
  forwarding_dns_service_ipv4=config_static.DNSServer.forwarding_dns_service_ipv4,
231
235
  tcp_target_server_ipv4=config_static.DNSServer.target_tcp_server_ipv4,
232
236
  # Passing the engine domain list to DNS server to work with.
233
237
  # 'list' function re-initializes the current list, or else it will be the same instance object.
234
238
  tcp_resolve_domain_list=list(config_static.Certificates.domains_all_times),
235
- log_directory_path=config_static.LogRec.logs_path,
236
239
  offline_mode=config_static.DNSServer.offline_mode,
237
240
  resolve_to_tcp_server_only_tcp_resolve_domains=(
238
241
  config_static.DNSServer.resolve_to_tcp_server_only_engine_domains),
@@ -260,6 +263,10 @@ def mitm_server_main(config_file_path: str):
260
263
  listening_port_list=config_static.TCPServer.listening_port_list,
261
264
  ca_certificate_name=config_static.MainConfig.ca_certificate_name,
262
265
  ca_certificate_filepath=config_static.MainConfig.ca_certificate_filepath,
266
+ ca_certificate_crt_filepath=config_static.MainConfig.ca_certificate_crt_filepath,
267
+ install_ca_certificate_to_root_store=config_static.Certificates.install_ca_certificate_to_root_store,
268
+ uninstall_unused_ca_certificates_with_ca_certificate_name=(
269
+ config_static.Certificates.uninstall_unused_ca_certificates_with_mitm_ca_name),
263
270
  default_server_certificate_usage=config_static.Certificates.default_server_certificate_usage,
264
271
  default_server_certificate_name=config_static.MainConfig.default_server_certificate_name,
265
272
  default_certificate_domain_list=config_static.Certificates.domains_all_times,
@@ -313,11 +320,10 @@ def mitm_server_main(config_file_path: str):
313
320
  dns_gateway_server_list = [base.DEFAULT_IPV4]
314
321
  set_dns_gateway = True
315
322
 
323
+ # Get current network interface state.
324
+ global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
325
+ NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
316
326
  if set_dns_gateway:
317
- # Get current network interface state.
318
- global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
319
- NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
320
-
321
327
  # Set the DNS gateway to the specified one only if the DNS gateway is dynamic or it is static but different
322
328
  # from the one specified in the configuration file.
323
329
  if (NETWORK_INTERFACE_IS_DYNAMIC or (not NETWORK_INTERFACE_IS_DYNAMIC and
@@ -36,7 +36,9 @@ def convert_object_to_x509(certificate):
36
36
  """
37
37
 
38
38
  # Check if 'certificate' is a string and a path.
39
- if isinstance(certificate, str) and os.path.isfile(certificate):
39
+ if isinstance(certificate, str):
40
+ if not os.path.isfile(certificate):
41
+ raise FileNotFoundError(f'File not found: {certificate}')
40
42
  # Import the certificate from the path.
41
43
  certificate = file_io.read_file(certificate, file_mode='rb')
42
44
 
@@ -124,6 +124,7 @@ def _wrap_do_rollover(handler, header):
124
124
  handler.doRollover = new_do_rollover
125
125
 
126
126
 
127
+ # noinspection PyPep8Naming
127
128
  def add_timedfilehandler_with_queuehandler(
128
129
  logger: logging.Logger,
129
130
  file_path: str,
@@ -144,6 +145,7 @@ def add_timedfilehandler_with_queuehandler(
144
145
  when: str = 'midnight',
145
146
  interval: int = 1,
146
147
  delay: bool = True,
148
+ backupCount: int = 0,
147
149
  encoding=None,
148
150
  header: str = None
149
151
  ):
@@ -164,15 +166,16 @@ def add_timedfilehandler_with_queuehandler(
164
166
  filesystem.create_directory(os.path.dirname(file_path))
165
167
 
166
168
  file_handler = get_timed_rotating_file_handler(
167
- file_path, when=when, interval=interval, delay=delay, encoding=encoding)
169
+ file_path, when=when, interval=interval, delay=delay, backupCount=backupCount, encoding=encoding)
168
170
 
169
171
  loggers.set_logging_level(file_handler, logging_level)
170
172
 
171
173
  formatter = _process_formatter_attribute(formatter, file_type=file_type)
172
174
 
173
175
  # If formatter was passed to the function we'll add it to handler.
176
+ # noinspection GrazieInspection
174
177
  if formatter:
175
- # Convert string to Formatter object. Moved to newer styling of python 3: style='{'
178
+ # Convert string to Formatter object. Moved to newer styling of python 3: style='{'.
176
179
  logging_formatter = formatters.get_logging_formatter_from_string(
177
180
  formatter=formatter, use_nanoseconds=formatter_use_nanoseconds)
178
181
  # Setting the formatter in file handler.
@@ -240,8 +243,14 @@ def get_stream_handler() -> logging.StreamHandler:
240
243
  return logging.StreamHandler()
241
244
 
242
245
 
246
+ # noinspection PyPep8Naming
243
247
  def get_timed_rotating_file_handler(
244
- log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None
248
+ log_file_path: str,
249
+ when: str = "midnight",
250
+ interval: int = 1,
251
+ backupCount: int = 0,
252
+ delay: bool = False,
253
+ encoding=None
245
254
  ) -> TimedRotatingFileHandler:
246
255
  """
247
256
  Function to get a TimedRotatingFileHandler.
@@ -255,13 +264,16 @@ def get_timed_rotating_file_handler(
255
264
  "D" - Days
256
265
  "midnight" - Roll over at midnight
257
266
  :param interval: Interval to rotate the log file.
267
+ :param backupCount: int, Number of backup files to keep. Default is 0.
268
+ If backupCount is > 0, when rollover is done, no more than backupCount files are kept, the oldest are deleted.
269
+ If backupCount is == 0, all the backup files will be kept.
258
270
  :param delay: bool, If set to True, the log file will be created only if there's something to write.
259
271
  :param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
260
272
  :return: TimedRotatingFileHandler.
261
273
  """
262
274
 
263
275
  return TimedRotatingFileHandler(
264
- filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding)
276
+ filename=log_file_path, when=when, interval=interval, backupCount=backupCount, delay=delay, encoding=encoding)
265
277
 
266
278
 
267
279
  def start_queue_listener_for_file_handler(
@@ -32,6 +32,7 @@ def create_logger(
32
32
  filehandler_rotation_use_default_namer_function: bool = True,
33
33
  when: str = "midnight",
34
34
  interval: int = 1,
35
+ backupCount: int = 0,
35
36
  delay: bool = False,
36
37
  encoding=None,
37
38
  header: str = None
@@ -95,6 +96,9 @@ def create_logger(
95
96
  :param interval: int, Interval to rotate the log file. Default is 1.
96
97
  If 'when="midnight"' and 'interval=1', then the log file will be rotated every midnight.
97
98
  If 'when="midnight"' and 'interval=2', then the log file will be rotated every 2nd midnights.
99
+ :param backupCount: int, Number of backup files to keep. Default is 0.
100
+ If backupCount is > 0, when rollover is done, no more than backupCount files are kept, the oldest are deleted.
101
+ If backupCount is == 0, all the backup files will be kept.
98
102
  :param delay: bool, If set to True, the log file will be created only if there's something to write.
99
103
  :param encoding: string, Encoding to use for the log file. Default is None.
100
104
  :param header: string, Header to write to the log file.
@@ -181,7 +185,7 @@ def create_logger(
181
185
  rotation_date_format=filehandler_rotation_date_format,
182
186
  rotation_callback_namer_function=filehandler_rotation_callback_namer_function,
183
187
  rotation_use_default_callback_namer_function=filehandler_rotation_use_default_namer_function,
184
- when=when, interval=interval, delay=delay, encoding=encoding, header=header)
188
+ when=when, interval=interval, delay=delay, backupCount=backupCount, encoding=encoding, header=header)
185
189
 
186
190
  return logger
187
191
 
@@ -0,0 +1,89 @@
1
+ from typing import Literal
2
+ import win32crypt as wcrypt
3
+
4
+ from ...print_api import print_api
5
+ from ... import certificates
6
+
7
+
8
+ # lpszStoreProvider
9
+ CERT_STORE_PROV_SYSTEM = 0x0000000A
10
+ # dwFlags
11
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
12
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
13
+ CERT_CLOSE_STORE_FORCE_FLAG = 0x00000001
14
+ CRYPT_STRING_BASE64HEADER = 0x00000000
15
+ X509_ASN_ENCODING = 0x00000001
16
+ CERT_STORE_ADD_REPLACE_EXISTING = 3
17
+
18
+
19
+ STORE_LOCATION_TO_CERT_SYSTEM_STORE: dict = {
20
+ "ROOT": CERT_SYSTEM_STORE_LOCAL_MACHINE,
21
+ "CA": CERT_SYSTEM_STORE_LOCAL_MACHINE,
22
+ "MY": CERT_SYSTEM_STORE_CURRENT_USER
23
+ }
24
+
25
+
26
+ def delete_certificate_by_issuer_name(
27
+ issuer_name: str,
28
+ store_location: Literal[
29
+ "ROOT",
30
+ "CA",
31
+ "MY"] = "ROOT",
32
+ print_kwargs: dict = None
33
+ ):
34
+ """
35
+ NEED ADMIN RIGHTS.
36
+ The function will remove all certificates with the specified issuer name.
37
+ There can be several certificates with this name.
38
+
39
+ :param issuer_name: string, issuer name to search for.
40
+ :param store_location: string, store location to search in. Default is "ROOT".
41
+ :param print_kwargs: dict, print_api kwargs.
42
+ """
43
+
44
+ store = wcrypt.CertOpenStore(
45
+ CERT_STORE_PROV_SYSTEM, 0, None, STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location], store_location)
46
+
47
+ for cert in store.CertEnumCertificatesInStore():
48
+ # Certificate properties.
49
+ # cert.CertEnumCertificateContextProperties()
50
+ subject_string: str = wcrypt.CertNameToStr(cert.Subject)
51
+ if subject_string == issuer_name:
52
+ # Remove the certificate.
53
+ cert.CertDeleteCertificateFromStore()
54
+ print_api(f"Removed the Certificate with issuer: {issuer_name}", **(print_kwargs or {}))
55
+
56
+ # There is an exception about store close.
57
+ # store.CertCloseStore()
58
+
59
+
60
+ def install_certificate_file(
61
+ file_path: str,
62
+ store_location: Literal[
63
+ "ROOT", "CA", "MY"] = "ROOT",
64
+ print_kwargs: dict = None
65
+ ):
66
+ """
67
+ NEED ADMIN RIGHTS.
68
+ The function will install the certificate from the file to the specified store location.
69
+
70
+ :param file_path: string, full file path to the certificate file.
71
+ :param store_location: string, store location to install the certificate. Default is "ROOT".
72
+ :param print_kwargs: dict, print_api kwargs.
73
+ """
74
+
75
+ with open(file_path, 'r') as f:
76
+ certificate_string = f.read()
77
+
78
+ certificate_pem = certificates.get_pem_certificate_from_string(certificate_string)
79
+
80
+ certificate_bytes = wcrypt.CryptStringToBinary(certificate_pem, CRYPT_STRING_BASE64HEADER)[0]
81
+
82
+ store = wcrypt.CertOpenStore(
83
+ CERT_STORE_PROV_SYSTEM, 0, None, STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location], store_location)
84
+
85
+ store.CertAddEncodedCertificateToStore(X509_ASN_ENCODING, certificate_bytes, CERT_STORE_ADD_REPLACE_EXISTING)
86
+ store.CertCloseStore(CERT_CLOSE_STORE_FORCE_FLAG)
87
+
88
+ message = f"Certificate installed to the store: [{store_location}] from file: [{file_path}]"
89
+ print_api(message, **(print_kwargs or {}))
@@ -35,6 +35,7 @@ class DnsServer:
35
35
  listening_interface: str,
36
36
  listening_port: int,
37
37
  log_directory_path: str,
38
+ backupCount_log_files_x_days: int = 0,
38
39
  forwarding_dns_service_ipv4: str = '8.8.8.8',
39
40
  forwarding_dns_service_port: int = 53,
40
41
  resolve_to_tcp_server_only_tcp_resolve_domains: bool = False,
@@ -56,6 +57,9 @@ class DnsServer:
56
57
  Example: '0.0.0.0'. For all interfaces.
57
58
  :param listening_port: int: Port number that the DNS Server will listen on.
58
59
  :param log_directory_path: str: Path to the directory where the logs will be saved.
60
+ :param backupCount_log_files_x_days: int: How many days the log files will be kept.
61
+ Default is 0, which means that the log files will be kept indefinitely.
62
+ More than 0 means that the log files will be deleted after the specified days.
59
63
  :param forwarding_dns_service_ipv4: str: IPv4 address of the DNS Service that will be used for resolving.
60
64
  Example: '8.8.8.8'. For Google DNS Service.
61
65
  :param forwarding_dns_service_port: int: Port number of the DNS Service that will be used for resolving.
@@ -126,7 +130,8 @@ class DnsServer:
126
130
  logger_name="dns",
127
131
  directory_path=self.log_directory_path,
128
132
  add_timedfile=True,
129
- formatter_filehandler='DEFAULT'
133
+ formatter_filehandler='DEFAULT',
134
+ backupCount=backupCount_log_files_x_days
130
135
  )
131
136
 
132
137
  self.test_config()
@@ -1,10 +1,13 @@
1
1
  import threading
2
2
  import select
3
3
  from typing import Literal, Union
4
+ from pathlib import Path
4
5
 
5
6
  from ..psutilw import networks
7
+ from ..certauthw import certauthw
6
8
  from ...script_as_string_processor import ScriptAsStringProcessor
7
- from ... import queues, filesystem
9
+ from ...permissions import permissions
10
+ from ... import queues, filesystem, certificates
8
11
  from ...basics import booleans
9
12
  from ...print_api import print_api
10
13
 
@@ -30,6 +33,9 @@ class SocketWrapper:
30
33
  forwarding_dns_service_ipv4_list___only_for_localhost: list = None,
31
34
  ca_certificate_name: str = None,
32
35
  ca_certificate_filepath: str = None,
36
+ ca_certificate_crt_filepath: str = None,
37
+ install_ca_certificate_to_root_store: bool = False,
38
+ uninstall_unused_ca_certificates_with_ca_certificate_name: bool = False,
33
39
  default_server_certificate_usage: bool = False,
34
40
  default_server_certificate_name: str = None,
35
41
  default_certificate_domain_list: list = None,
@@ -68,7 +74,13 @@ class SocketWrapper:
68
74
  Example: '0.0.0.0'. For all interfaces.
69
75
  :param listening_port_list: list, of ports that will be listened on.
70
76
  :param ca_certificate_name: CA certificate name.
71
- :param ca_certificate_filepath: CA certificate file path.
77
+ :param ca_certificate_filepath: CA certificate file path with '.pem' extension.
78
+ :param ca_certificate_crt_filepath: CA certificate file path with '.crt' extension.
79
+ This file will be created from the PEM file 'ca_certificate_filepath' for manual installation.
80
+ :param install_ca_certificate_to_root_store: boolean, if True, CA certificate will be installed
81
+ to the root store.
82
+ :param uninstall_unused_ca_certificates_with_ca_certificate_name: boolean, if True, unused CA certificates
83
+ with provided 'ca_certificate_name' will be uninstalled.
72
84
  :param default_server_certificate_usage: boolean, if True, default server certificate will be used
73
85
  for each incoming socket.
74
86
  :param sni_custom_callback_function: callable, custom callback function that will be executed when
@@ -150,6 +162,10 @@ class SocketWrapper:
150
162
  self.listening_port_list: list[int] = listening_port_list
151
163
  self.ca_certificate_name: str = ca_certificate_name
152
164
  self.ca_certificate_filepath: str = ca_certificate_filepath
165
+ self.ca_certificate_crt_filepath: str = ca_certificate_crt_filepath
166
+ self.install_ca_certificate_to_root_store: bool = install_ca_certificate_to_root_store
167
+ self.uninstall_unused_ca_certificates_with_ca_certificate_name: bool = \
168
+ uninstall_unused_ca_certificates_with_ca_certificate_name
153
169
  self.default_server_certificate_usage: bool = default_server_certificate_usage
154
170
  self.default_server_certificate_name: str = default_server_certificate_name
155
171
  self.default_certificate_domain_list: list = default_certificate_domain_list
@@ -270,6 +286,59 @@ class SocketWrapper:
270
286
  error_messages.append(f"Port [{port}] is already in use by process: {process_info}")
271
287
  raise SocketWrapperPortInUseError("\n".join(error_messages))
272
288
 
289
+ if not filesystem.is_file_exists(file_path=self.ca_certificate_filepath):
290
+ # Initialize CertAuthWrapper.
291
+ ca_certificate_directory: str = str(Path(self.ca_certificate_filepath).parent)
292
+ certauth_wrapper = certauthw.CertAuthWrapper(
293
+ ca_certificate_name=self.ca_certificate_name,
294
+ ca_certificate_filepath=self.ca_certificate_filepath,
295
+ server_certificate_directory=ca_certificate_directory
296
+ )
297
+
298
+ # Create CA certificate if it doesn't exist.
299
+ certauth_wrapper.create_use_ca_certificate()
300
+
301
+ certificates.write_crt_certificate_file_in_pem_format_from_pem_file(
302
+ pem_file_path=self.ca_certificate_filepath,
303
+ crt_file_path=self.ca_certificate_crt_filepath)
304
+
305
+ if self.install_ca_certificate_to_root_store:
306
+ if not self.ca_certificate_filepath:
307
+ message = "You set [install_ca_certificate_to_root_store = True],\n" \
308
+ "But you didn't set [ca_certificate_filepath]."
309
+ raise SocketWrapperConfigurationValuesError(message)
310
+
311
+ # Before installation check if there are any unused certificates with the same name.
312
+ if self.uninstall_unused_ca_certificates_with_ca_certificate_name:
313
+ # Check how many certificates with our ca certificate name are installed.
314
+ is_installed_by_name, certificate_list_by_name = certificates.is_certificate_in_store(
315
+ issuer_name=self.ca_certificate_name)
316
+ # If there is more than one certificate with the same name, delete them all.
317
+ if is_installed_by_name and len(certificate_list_by_name) > 1:
318
+ certificates.delete_certificate_by_issuer_name(self.ca_certificate_name)
319
+ # If there is only one certificate with the same name, check if it is the same certificate.
320
+ elif is_installed_by_name and len(certificate_list_by_name) == 1:
321
+ is_installed_by_file, certificate_list_by_file = certificates.is_certificate_in_store(
322
+ certificate=self.ca_certificate_filepath, by_cert_thumbprint=True, by_cert_issuer=True)
323
+ # If the certificate is not the same, delete it.
324
+ if not is_installed_by_file:
325
+ if not permissions.is_admin():
326
+ raise SocketWrapperConfigurationValuesError(
327
+ "You need to run the script with admin rights to uninstall the unused certificates.")
328
+ certificates.delete_certificate_by_issuer_name(
329
+ self.ca_certificate_name, store_location="ROOT", print_kwargs={'logger': self.logger})
330
+
331
+ if self.install_ca_certificate_to_root_store:
332
+ # Install CA certificate to the root store if it is not installed.
333
+ is_installed_by_file, certificate_list_by_file = certificates.is_certificate_in_store(
334
+ certificate=self.ca_certificate_filepath, by_cert_thumbprint=True, by_cert_issuer=True)
335
+ if not is_installed_by_file:
336
+ if not permissions.is_admin():
337
+ raise SocketWrapperConfigurationValuesError(
338
+ "You need to run the script with admin rights to install the CA certificate.")
339
+ certificates.install_certificate_file(
340
+ self.ca_certificate_filepath, store_location="ROOT", print_kwargs={'logger': self.logger})
341
+
273
342
  # Creating listening sockets.
274
343
  def create_socket_ipv4_tcp(self, ip_address: str, port: int):
275
344
  self.sni_execute_extended = True
@@ -60,7 +60,7 @@ def is_tls(client_socket) -> Optional[Tuple[str, str]]:
60
60
  }
61
61
 
62
62
  # Get the tuple of the type and version as strings.
63
- tls_content_and_version_tuple: tuple =\
63
+ tls_content_and_version_tuple: tuple = \
64
64
  content_type_map.get(content_type), version_map.get((version_major, version_minor))
65
65
 
66
66
  # If both parts of the tuple are not None, return the protocol type.
@@ -1,6 +1,5 @@
1
1
  import datetime
2
2
 
3
- from ...print_api import print_api
4
3
  from ...file_io import csvs
5
4
  from ..loggingw import loggingw
6
5
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.13
3
+ Version: 2.16.15
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,9 +1,9 @@
1
- atomicshop/__init__.py,sha256=nKRmwq7NbTn58KeTMLn39MOGpSUYRy9TXJGc5QuPXOg,124
1
+ atomicshop/__init__.py,sha256=UzNeZyRkmLnYywv5edPjB0_p7zEgYfoGzcusp1FpodI,124
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
5
5
  atomicshop/appointment_management.py,sha256=BsYH_PClTGLVazcuNjt30--hpXKYjSmHp1R1iQbM4Hc,7330
6
- atomicshop/certificates.py,sha256=J-cmd6Rpq3zZyzsOH-GcdqIXdg2UwM8_E9mg7XtUph8,3787
6
+ atomicshop/certificates.py,sha256=ssOTD1uN4OaB9TU7mI0Dg_sSGFb6Xxw28A7TtyKmMMY,7637
7
7
  atomicshop/command_line_processing.py,sha256=u5yT9Ger_cu7ni5ID0VFlRbVD46ARHeNC9tRM-_YXrQ,1038
8
8
  atomicshop/config_init.py,sha256=BSxc2FhytQPv06g5z9wbAXuA6oYCAsAJLxu_mTExhwI,2491
9
9
  atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,4667
@@ -122,13 +122,13 @@ atomicshop/file_io/tomls.py,sha256=ol8EvQPf9sryTmZUf1v55BYSUQ6ml7HVVBHpNKbsIlA,9
122
122
  atomicshop/file_io/xlsxs.py,sha256=v_dyg9GD4LqgWi6wA1QuWRZ8zG4ZwB6Dz52ytdcmmmI,2184
123
123
  atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,2104
124
124
  atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
- atomicshop/mitm/config_static.py,sha256=e7-tqQbt3sZ3Inqwyy4c-iE62Ldoa_QkAg-TUOwuzpE,7105
125
+ atomicshop/mitm/config_static.py,sha256=UNvRTb3ZuFVGZpF33fvaIf0xw_VGyml2ExIwqW_uYcg,7777
126
126
  atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
127
127
  atomicshop/mitm/connection_thread_worker.py,sha256=1MBpRoLpzWJMvxqQKizo6IVQ4XYsbKGsjxianNQLUlE,20051
128
- atomicshop/mitm/import_config.py,sha256=5peDr6cT0ZWK3J53yG-VEew77CKrvB88CphM10SQd3I,7868
129
- atomicshop/mitm/initialize_engines.py,sha256=F_FESrvTghEln-P9wesvXnr3_sxczYJBN6QtmQ3d2WE,8249
128
+ atomicshop/mitm/import_config.py,sha256=_nu8mgA-M4s6dZ8_QWx3x0aVb75upvsCuX_PIUg4X2w,8345
129
+ atomicshop/mitm/initialize_engines.py,sha256=kBG8TBnyFuwlJ1uKaWDzc5AiZNpwdvouq2pr-PYrdEA,8349
130
130
  atomicshop/mitm/message.py,sha256=d_sm3O_aoZf87dDQP44xOMNEG-uZBN1ZecQgMCacbZs,1814
131
- atomicshop/mitm/mitm_main.py,sha256=UQ8vCHE83H5Q5Uffv3dIGCH8Buigq6FxSWgLm6_vRGc,20683
131
+ atomicshop/mitm/mitm_main.py,sha256=CdCv4nYt_jwd23AI14v6lC2H8SZeIZqsXjFhwq61UtM,21285
132
132
  atomicshop/mitm/recs_files.py,sha256=btOuYQca4DuBOAKp9OY21HGjeEVOx9r_k-AnZOqs3Dk,3007
133
133
  atomicshop/mitm/shared_functions.py,sha256=hplm98tz8pgJ4WHUVI9sf_oVqUM2KJ1Y2pD6EFSb8P0,1879
134
134
  atomicshop/mitm/statistic_analyzer.py,sha256=AzL9rQhg0tLJj33gZfxdwWghmbXGLh_HyMBDpzuBmsQ,24709
@@ -178,7 +178,7 @@ atomicshop/wrappers/_process_wrapper_curl.py,sha256=XkZZXYl7D0Q6UfdWqy-18AvpU0yV
178
178
  atomicshop/wrappers/_process_wrapper_tar.py,sha256=WUMZFKNrlG4nJP9tWZ51W7BR1j_pIjsjgyAStmWjRGs,655
179
179
  atomicshop/wrappers/astw.py,sha256=VkYfkfyc_PJLIOxByT6L7B8uUmKY6-I8XGZl4t_z828,4239
180
180
  atomicshop/wrappers/configparserw.py,sha256=JwDTPjZoSrv44YKwIRcjyUnpN-FjgXVfMqMK_tJuSgU,22800
181
- atomicshop/wrappers/cryptographyw.py,sha256=_5jgzQjgCDQrFN1f6_0y-IdIR6bGaYIjIy92bjJRuHI,13168
181
+ atomicshop/wrappers/cryptographyw.py,sha256=LfzTnwvJE03G6WZryOOf43VKhhnyMakzHpn8DPPCoy4,13252
182
182
  atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
183
183
  atomicshop/wrappers/githubw.py,sha256=AQcFuT5mvDUNT_cI31MwkJ7srdhMtttF8FyXS8vs5cU,12270
184
184
  atomicshop/wrappers/msiw.py,sha256=GQLqud72nfex3kvO1bJSruNriCYTYX1_G1gSf1MPkIA,6118
@@ -247,9 +247,9 @@ atomicshop/wrappers/fibratusw/install.py,sha256=PLVymDe0HuOvU0r2lje8BkQAgtiOWEeR
247
247
  atomicshop/wrappers/loggingw/consts.py,sha256=JWiUJEydjhwatBxtIJsGTmDUSTLbmIRidtR6qRLMaIY,1608
248
248
  atomicshop/wrappers/loggingw/filters.py,sha256=CMs5PAMb68zxJgBcQobaOFDG5kLJBOVYnoBHjDgksO8,2859
249
249
  atomicshop/wrappers/loggingw/formatters.py,sha256=7XUJvlB0CK4DCkEp8NTL0S0dkyrZD0UTADgEwkStKOY,5483
250
- atomicshop/wrappers/loggingw/handlers.py,sha256=yFYBeTkxnpmtlauoH3ZEFEHUYQYu9YL-ycd9sYTvOl4,16928
250
+ atomicshop/wrappers/loggingw/handlers.py,sha256=_69ZtmZRUsdgL8Q1rh2X-a0ComDU9gg9v101ngbJcac,17436
251
251
  atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
252
- atomicshop/wrappers/loggingw/loggingw.py,sha256=lo4OZPXCbYZi3GqpaaJSs9SOGFfqD2EgHzzTK7f5IR4,11275
252
+ atomicshop/wrappers/loggingw/loggingw.py,sha256=BBQe_2i8otcbe0cZcAKd3tZ2D5XL3SPb_IWTXCepSxw,11595
253
253
  atomicshop/wrappers/loggingw/reading.py,sha256=tplnJlQ7RMxWv2s782tWGOo1C7WNemk2SRTZCCgD9J0,16474
254
254
  atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
255
255
  atomicshop/wrappers/mongodbw/infrastructure.py,sha256=tHqtt__yKGtj24CT5AIk0V0k9t1p_PjezFExXRmmmcA,1517
@@ -278,6 +278,7 @@ atomicshop/wrappers/pycharmw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
278
278
  atomicshop/wrappers/pycharmw/ubuntu.py,sha256=l2_9SXrw7ApSvFOROGZdPZa5ylZBKofk4sYqf4IugQs,1223
279
279
  atomicshop/wrappers/pycharmw/win.py,sha256=jdnTkUqZX_BrMW8AmW-xGtxdV-wmmNr_NMA2jB6JHsQ,2725
280
280
  atomicshop/wrappers/pywin32w/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
281
+ atomicshop/wrappers/pywin32w/cert_store.py,sha256=bpFm5nH9j6I9eJdLjPnSvo-g4OyJO7Sb5VddzVE9-UM,3156
281
282
  atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bCJquxaTquns,1175
282
283
  atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
283
284
  atomicshop/wrappers/pywin32w/win_event_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -295,7 +296,7 @@ atomicshop/wrappers/socketw/accepter.py,sha256=hZZKVYlF3LOHQJsSIEKXZUf6QXXWm-Atq
295
296
  atomicshop/wrappers/socketw/base.py,sha256=evoOIxg5Xff3THJnrVX00D5HobaOpDp6_e_gso7TJmA,2191
296
297
  atomicshop/wrappers/socketw/certificator.py,sha256=3CpQKtcW68FSbH6LVSEZTqWBS6Yg_-3K0x4nFkId4UY,12236
297
298
  atomicshop/wrappers/socketw/creator.py,sha256=3_OraDkw2DAWZfoSdY3svCGMOIxpjLEEY7NxWd7M5P4,9873
298
- atomicshop/wrappers/socketw/dns_server.py,sha256=VIpz6cluQ53nZ14QGhnXU9zJqlcOa5fEZsHeKJWr9ec,45127
299
+ atomicshop/wrappers/socketw/dns_server.py,sha256=53S52lkA9RWetNlee-mjt000OJW4Mi7Ktcru0WXrMZE,45504
299
300
  atomicshop/wrappers/socketw/exception_wrapper.py,sha256=B-X5SHLSUIWToihH2MKnOB1F4A81_X0DpLLfnYKYbEc,7067
300
301
  atomicshop/wrappers/socketw/get_process.py,sha256=_YMVxYhVlzjJpeOR36tphZ5QWeKYwk03Ilw0muCxDbg,5950
301
302
  atomicshop/wrappers/socketw/receiver.py,sha256=G3hDTacm7nwwUNHEbKZpxO0c8rHcl0NeKpZy7Xc6zpA,9008
@@ -303,13 +304,13 @@ atomicshop/wrappers/socketw/sender.py,sha256=d7YQFlCBMFTYtkGxbS-8cm5rh5WWFeBVvrE
303
304
  atomicshop/wrappers/socketw/sni.py,sha256=fVwyh3h9IqfLMnf4__bMIzcF4c-Kk9mlbDWMRXKN-ow,17155
304
305
  atomicshop/wrappers/socketw/socket_client.py,sha256=FNmTt94YvjZP0X4RPb7icO3xD_nBHQ_XynnObdWFiAU,19682
305
306
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=wAwyst8YdVyVfZfERav1A9_OnMJAiVBy-4uY0RpNqkU,6339
306
- atomicshop/wrappers/socketw/socket_wrapper.py,sha256=g7f_8RkW80EZeQWNTqGYnfrQkgAI56T3SwWybq7ZsXg,28521
307
- atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
308
- atomicshop/wrappers/socketw/statistics_csv.py,sha256=Jc0D12crkKRaqoCRQ-2Mz1zm6n4UUx9dXakf-N2TYWA,3065
307
+ atomicshop/wrappers/socketw/socket_wrapper.py,sha256=DiDzg9OlgahxpqAsnOkCKHNcE-H-YAY3ndVMeU-oCjw,33363
308
+ atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
309
+ atomicshop/wrappers/socketw/statistics_csv.py,sha256=V_m1D0KpizQox3IEWp2AUcncwWy5kG25hbFrc-mBSJE,3029
309
310
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
310
311
  atomicshop/wrappers/winregw/winreg_network.py,sha256=bQ8Jql8bVGBJ0dt3VQ56lga_1LBOMLI3Km_otvvbU6c,7138
311
- atomicshop-2.16.13.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
312
- atomicshop-2.16.13.dist-info/METADATA,sha256=2s0TVb_dZrMajZtHu9zT9kJVA7e0_U9-uBCFyPfVfmY,10503
313
- atomicshop-2.16.13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
314
- atomicshop-2.16.13.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
315
- atomicshop-2.16.13.dist-info/RECORD,,
312
+ atomicshop-2.16.15.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
313
+ atomicshop-2.16.15.dist-info/METADATA,sha256=i5Xgtqpn2TfPbeSQ_L3nAryipoFyy2Z6whmJsDFQyD4,10503
314
+ atomicshop-2.16.15.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
315
+ atomicshop-2.16.15.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
316
+ atomicshop-2.16.15.dist-info/RECORD,,