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

Files changed (42) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  3. atomicshop/basics/booleans.py +14 -5
  4. atomicshop/dns.py +104 -0
  5. atomicshop/file_io/docxs.py +8 -0
  6. atomicshop/file_io/tomls.py +133 -0
  7. atomicshop/filesystem.py +5 -4
  8. atomicshop/get_process_list.py +3 -3
  9. atomicshop/mitm/config_static.py +195 -0
  10. atomicshop/mitm/config_toml_editor.py +55 -0
  11. atomicshop/mitm/connection_thread_worker.py +54 -90
  12. atomicshop/mitm/import_config.py +147 -139
  13. atomicshop/mitm/initialize_engines.py +7 -2
  14. atomicshop/mitm/initialize_mitm_server.py +161 -107
  15. atomicshop/mitm/shared_functions.py +0 -1
  16. atomicshop/mitm/statistic_analyzer.py +13 -1
  17. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +54 -14
  18. atomicshop/script_as_string_processor.py +5 -1
  19. atomicshop/wrappers/cryptographyw.py +3 -3
  20. atomicshop/wrappers/psutilw/networks.py +25 -1
  21. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  22. atomicshop/wrappers/pywin32w/wmis/helpers.py +127 -0
  23. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +167 -0
  24. atomicshop/wrappers/socketw/accepter.py +8 -8
  25. atomicshop/wrappers/socketw/base.py +13 -0
  26. atomicshop/wrappers/socketw/certificator.py +202 -149
  27. atomicshop/wrappers/socketw/creator.py +15 -35
  28. atomicshop/wrappers/socketw/dns_server.py +155 -102
  29. atomicshop/wrappers/socketw/exception_wrapper.py +8 -27
  30. atomicshop/wrappers/socketw/get_process.py +115 -95
  31. atomicshop/wrappers/socketw/sni.py +298 -164
  32. atomicshop/wrappers/socketw/socket_client.py +5 -12
  33. atomicshop/wrappers/socketw/socket_server_tester.py +1 -1
  34. atomicshop/wrappers/socketw/socket_wrapper.py +328 -72
  35. atomicshop/wrappers/socketw/statistics_csv.py +94 -16
  36. {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/METADATA +1 -1
  37. {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/RECORD +41 -36
  38. atomicshop/mitm/config_editor.py +0 -37
  39. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  40. {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/LICENSE.txt +0 -0
  41. {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/WHEEL +0 -0
  42. {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/top_level.txt +0 -0
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.15.13'
4
+ __version__ = '2.16.0'
@@ -0,0 +1,11 @@
1
+ import sys
2
+
3
+ from atomicshop import dns
4
+
5
+
6
+ def main():
7
+ dns.default_dns_gateway_main()
8
+
9
+
10
+ if __name__ == '__main__':
11
+ sys.exit(main())
@@ -1,14 +1,23 @@
1
- def check_3_booleans_when_only_1_can_be_true(boolean1: tuple, boolean2: tuple, boolean3: tuple) -> None:
1
+ def check_3_booleans_when_only_1_can_be_true(
2
+ boolean1: tuple,
3
+ boolean2: tuple,
4
+ boolean3: tuple
5
+ ) -> None:
2
6
  """
7
+ Check if only one boolean can be 'True' from 3 booleans
8
+
9
+ :param boolean1: tuple, (value, string name of the setting you want to print to the user to be aware of).
10
+ :param boolean2: tuple, (value, string name of the setting you want to print to the user to be aware of).
11
+ :param boolean3: tuple, (value, string name of the setting you want to print to the user to be aware of).
12
+ :return:
13
+
14
+ ------------------------------------------------
15
+
3
16
  Example:
4
17
  check_3_booleans_when_only_1_can_be_true(
5
18
  (self.config['section']['default_usage'], 'default_usage'),
6
19
  (self.config['section']['create_usage'], 'create_usage'),
7
20
  (self.config['section']['custom_usage'], 'custom_usage'))
8
- :param boolean1: tuple, (value, string name of the setting you want to print to the user to be aware of).
9
- :param boolean2: tuple, (value, string name of the setting you want to print to the user to be aware of).
10
- :param boolean3: tuple, (value, string name of the setting you want to print to the user to be aware of).
11
- :return:
12
21
  """
13
22
 
14
23
  check_if_3_booleans_are_false(boolean1, boolean2, boolean3)
atomicshop/dns.py CHANGED
@@ -1,6 +1,10 @@
1
+ import argparse
2
+
1
3
  import dns.resolver
2
4
 
3
5
  from .print_api import print_api
6
+ from . import permissions
7
+ from .wrappers.pywin32w.wmis import win32networkadapter
4
8
 
5
9
 
6
10
  # Defining Dictionary of Numeric to String DNS Query Types.
@@ -59,3 +63,103 @@ def resolve_dns_localhost(domain_name: str, dns_servers_list: list = None, print
59
63
  pass
60
64
 
61
65
  return connection_ip
66
+
67
+
68
+ def get_default_dns_gateway() -> tuple[bool, list[str]]:
69
+ """
70
+ Get the default DNS gateway from the system.
71
+ :return: tuple(is dynamic boolean, list of DNS server IPv4s).
72
+ """
73
+
74
+ resolver = dns.resolver.Resolver()
75
+ dns_servers = list(resolver.nameservers)
76
+
77
+ is_dynamic = win32networkadapter.is_adapter_dns_gateway_from_dhcp(use_default_interface=True)
78
+ return is_dynamic, dns_servers
79
+
80
+
81
+ def set_connection_dns_gateway_static(
82
+ dns_servers: list[str],
83
+ connection_name: str = None,
84
+ use_default_connection: bool = False
85
+ ) -> None:
86
+ """
87
+ Set the DNS servers for a network adapter.
88
+ :param connection_name: string, adapter name as shown in the network settings.
89
+ :param dns_servers: list of strings, DNS server IPv4 addresses.
90
+ :param use_default_connection: bool, if True, the default network interface will be used. This is the connection
91
+ that you internet is being used from.
92
+ :return: None
93
+ """
94
+
95
+ win32networkadapter.set_dns_server(
96
+ connection_name=connection_name, dns_servers=dns_servers, use_default_interface=use_default_connection)
97
+
98
+
99
+ def set_connection_dns_gateway_dynamic(
100
+ connection_name: str = None,
101
+ use_default_connection: bool = False
102
+ ) -> None:
103
+ """
104
+ Set the DNS servers for a network adapter to obtain them automatically from DHCP.
105
+ :param connection_name: string, adapter name as shown in the network settings.
106
+ :param use_default_connection: bool, if True, the default network interface will be used. This is the connection
107
+ that you internet is being used from.
108
+ :return: None
109
+ """
110
+
111
+ win32networkadapter.set_dns_server(
112
+ connection_name=connection_name, dns_servers=None, use_default_interface=use_default_connection)
113
+
114
+
115
+ def default_dns_gateway_main() -> int:
116
+ """
117
+ Main function for the default DNS gateway manipulations.
118
+ :return: None
119
+ """
120
+
121
+ argparse_obj = argparse.ArgumentParser(description="Get/Set the DNS gateway for the network adapter.")
122
+ arg_action_group = argparse_obj.add_mutually_exclusive_group(required=True)
123
+ arg_action_group.add_argument(
124
+ '-g', '--get', action='store_true', help='Get the default DNS gateway for the system.')
125
+ arg_action_group.add_argument(
126
+ '-s', '--set', type=str, nargs='+',
127
+ help='Set static DNS gateway for the system, provide values with spaces between each value.\n'
128
+ ' Example: -s 8.8.8.8 1.1.1.1.')
129
+ arg_action_group.add_argument(
130
+ '-d', '--dynamic', action='store_true',
131
+ help='Set the DNS gateway to obtain automatically from DHCP.')
132
+
133
+ arg_connection_group = argparse_obj.add_mutually_exclusive_group()
134
+ arg_connection_group.add_argument(
135
+ '-cn', '--connection_name', type=str, help='Connection name as shown in the network settings.')
136
+ arg_connection_group.add_argument(
137
+ '-cd', '--connection_default', action='store_true', help='Use the default connection.')
138
+
139
+ args = argparse_obj.parse_args()
140
+
141
+ if (args.set or args.dynamic) and not (args.connection_name or args.connection_default):
142
+ print_api("Please provide the connection name [-cn] or use the default connection [-cd].", color='red')
143
+ return 1
144
+
145
+ if args.set or args.dynamic:
146
+ if not permissions.is_admin():
147
+ print_api("You need to run this script as an administrator", color='red')
148
+ return 1
149
+
150
+ if args.get:
151
+ is_dynamic, dns_servers = get_default_dns_gateway()
152
+
153
+ if is_dynamic:
154
+ is_dynamic_string = 'Dynamic'
155
+ else:
156
+ is_dynamic_string = 'Static'
157
+ print_api(f'DNS Gateway: {is_dynamic_string} - {dns_servers}', color='blue')
158
+ elif args.set:
159
+ # dns_servers_list: list = args.dns_servers.split(',')
160
+ set_connection_dns_gateway_static(
161
+ dns_servers=args.set, connection_name=args.connection_name,
162
+ use_default_connection=args.connection_default)
163
+ elif args.dynamic:
164
+ set_connection_dns_gateway_dynamic(
165
+ connection_name=args.connection_name, use_default_connection=args.connection_default)
@@ -26,6 +26,14 @@ def get_hyperlinks(docx_path):
26
26
  if not paragraph.hyperlinks:
27
27
  continue
28
28
  for hyperlink in paragraph.hyperlinks:
29
+ # Hyperlinks are stored in docx (document.xml) without the fragment part.
30
+ # Fragment is the anchor of the link, for example: 'https://www.example.com#anchor'.
31
+ # So the hyperlink.address is stored as 'https://www.example.com'.
32
+ # And the fragment is stored in the hyperlink.fragment as 'anchor'.
33
+ # For the full hyperlink, we need to concatenate the address and the fragment.
34
+ # If there is no anchor in the link the fragment will be empty string ('').
35
+ if hyperlink.fragment:
36
+ hyperlinks.append(hyperlink.address + "#" + hyperlink.fragment)
29
37
  hyperlinks.append(hyperlink.address)
30
38
 
31
39
  return hyperlinks
@@ -10,6 +10,10 @@ except ModuleNotFoundError:
10
10
  from . import file_io
11
11
 
12
12
 
13
+ class TomlValueNotImplementedError(Exception):
14
+ pass
15
+
16
+
13
17
  @file_io.read_file_decorator
14
18
  def read_toml_file(file_path: str,
15
19
  file_mode: str = 'rb',
@@ -84,3 +88,132 @@ def dumps(toml_dict: dict):
84
88
  toml_string += process_item(key, value)
85
89
 
86
90
  return toml_string
91
+
92
+
93
+ def update_toml_file_with_new_config(
94
+ main_config_file_path: str,
95
+ changes_config_file_path: str = None,
96
+ changes_dict: dict = None,
97
+ new_config_file_path: str = None
98
+ ) -> None:
99
+ """
100
+ Update the old toml config file with the new values from the new toml config file.
101
+ This will update only the changed values.
102
+ If the values from the changes file aren't present in the main config file, they will not be added.
103
+
104
+ :param main_config_file_path: string, path to the main config file that you want to use as the main reference.
105
+ If you provide the 'new_config_file_path', then changes to the 'main_config_file_path' will be written there.
106
+ :param changes_config_file_path: string, the config file path that have the changes.
107
+ Only changed values will be updated to the 'main_config_file_path'.
108
+ :param changes_dict: dict, the dictionary with the changes.
109
+ Instead of providing the 'changes_config_file_path', you can provide only the dictionary with the changes.
110
+ :param new_config_file_path: string, path to the new config file.
111
+ If provided, the changes will be written to this file.
112
+ If not, the changes will be written to the 'main_config_file_path'.
113
+ """
114
+
115
+ # Sync the config files.
116
+ with open(main_config_file_path, 'r') as file:
117
+ main_config_file_text_lines: list = file.readlines()
118
+
119
+ main_config_file_text_lines_backup: list = list(main_config_file_text_lines)
120
+
121
+ # Read the new config file.
122
+ main_config_file_dict: dict = read_toml_file(main_config_file_path)
123
+
124
+ if changes_dict:
125
+ changes_config_file_dict = changes_dict
126
+ else:
127
+ changes_config_file_dict: dict = read_toml_file(changes_config_file_path)
128
+
129
+ # Update the config text lines.
130
+ for category, settings in main_config_file_dict.items():
131
+ if category not in changes_config_file_dict:
132
+ continue
133
+
134
+ for key, value in settings.items():
135
+ # If the key is in the old config file, use the old value.
136
+ if key not in changes_config_file_dict[category]:
137
+ continue
138
+
139
+ if main_config_file_dict[category][key] != changes_config_file_dict[category][key]:
140
+ # Get the line of the current category line.
141
+ current_category_line_index_in_text = None
142
+ for current_category_line_index_in_text, line in enumerate(main_config_file_text_lines):
143
+ if f"[{category}]" in line:
144
+ break
145
+
146
+ # current_category_line_index_in_text = main_config_file_text_lines.index(f"[{category}]\n")
147
+ current_category_index_in_main_config_dict = list(main_config_file_dict.keys()).index(category)
148
+
149
+ try:
150
+ next_category_name = list(
151
+ main_config_file_dict.keys())[current_category_index_in_main_config_dict + 1]
152
+ except IndexError:
153
+ next_category_name = list(main_config_file_dict.keys())[-1]
154
+
155
+ next_category_line_index_in_text = None
156
+ for next_category_line_index_in_text, line in enumerate(main_config_file_text_lines):
157
+ if f"[{next_category_name}]" in line:
158
+ break
159
+
160
+ # In case the current and the next categories are the same and the last in the file.
161
+ if next_category_line_index_in_text == current_category_line_index_in_text:
162
+ next_category_line_index_in_text = len(main_config_file_text_lines)
163
+
164
+ string_to_check_list: list = list()
165
+ if isinstance(value, bool):
166
+ string_to_check_list.append(f"{key} = {str(value).lower()}")
167
+ elif isinstance(value, int):
168
+ string_to_check_list.append(f"{key} = {value}")
169
+ elif isinstance(value, str):
170
+ string_to_check_list.append(f"{key} = '{value}'")
171
+ string_to_check_list.append(f'{key} = "{value}"')
172
+ else:
173
+ raise TomlValueNotImplementedError(f"Value type '{type(value)}' not implemented.")
174
+
175
+ # next_category_line_index_in_text = main_config_file_text_lines.index(f"[{next_category_name}]\n")
176
+ # Find the index of this line in the text file between current category line and
177
+ # the next category line.
178
+ line_index = None
179
+ found_line = False
180
+ for line_index in range(current_category_line_index_in_text, next_category_line_index_in_text):
181
+ if found_line:
182
+ line_index = line_index - 1
183
+ break
184
+ for string_to_check in string_to_check_list:
185
+ if string_to_check in main_config_file_text_lines[line_index]:
186
+ found_line = True
187
+ break
188
+
189
+ if found_line:
190
+ # If there are comments, get only them from the line. Comment will also get the '\n' character.
191
+ # noinspection PyUnboundLocalVariable
192
+ comment = main_config_file_text_lines[line_index].replace(string_to_check, '')
193
+
194
+ object_type = type(changes_config_file_dict[category][key])
195
+ if object_type == bool:
196
+ value_string_to_set = str(changes_config_file_dict[category][key]).lower()
197
+ elif object_type == str:
198
+ value_string_to_set = f"'{changes_config_file_dict[category][key]}'"
199
+ elif object_type == int:
200
+ value_string_to_set = str(changes_config_file_dict[category][key])
201
+
202
+ # noinspection PyUnboundLocalVariable
203
+ line_to_set = f"{key} = {value_string_to_set}{comment}"
204
+ # Replace the line with the old value.
205
+ main_config_file_text_lines[line_index] = line_to_set
206
+
207
+ main_config_file_dict[category][key] = changes_config_file_dict[category][key]
208
+
209
+ if new_config_file_path:
210
+ file_path_to_write = new_config_file_path
211
+ else:
212
+ file_path_to_write = main_config_file_path
213
+
214
+ if not main_config_file_text_lines == main_config_file_text_lines_backup:
215
+ # Write the final config file.
216
+ with open(file_path_to_write, 'w') as file:
217
+ file.writelines(main_config_file_text_lines)
218
+ else:
219
+ print("No changes to the config file.")
atomicshop/filesystem.py CHANGED
@@ -240,7 +240,7 @@ def check_absolute_path___add_full(filesystem_path: str, full_path_to_add: str)
240
240
  """
241
241
 
242
242
  if not check_absolute_path(filesystem_path):
243
- return f'{full_path_to_add}{os.sep}{remove_first_separator(filesystem_path)}'
243
+ return f'{full_path_to_add}{os.sep}{remove_last_separator(filesystem_path)}'
244
244
  else:
245
245
  return filesystem_path
246
246
 
@@ -815,10 +815,11 @@ def remove_last_separator(directory_path: str) -> str:
815
815
  return directory_path.removesuffix(os.sep)
816
816
 
817
817
 
818
- def remove_first_separator(filesystem_path: str) -> str:
818
+ def remove_last_separator(filesystem_path: str) -> str:
819
819
  """
820
- The function removes the first character in 'filesystem_path' if it is a separator returning the processed string.
821
- If the first character is not a separator, nothing is happening.
820
+ The function removes the last character in 'filesystem_path' if it is a system separator ('/' or '\')
821
+ returning the processed string.
822
+ If the character is not a separator, nothing happens.
822
823
 
823
824
  :param filesystem_path:
824
825
  :return:
@@ -1,6 +1,6 @@
1
1
  from typing import Union, Literal
2
2
 
3
- from .wrappers.pywin32w import wmi_win32process
3
+ from .wrappers.pywin32w.wmis import win32process
4
4
  from .wrappers.psutilw import psutilw
5
5
  from .basics import dicts
6
6
  from . import get_process_name_cmd_dll
@@ -36,7 +36,7 @@ class GetProcessList:
36
36
  self.process_polling_instance = psutilw.PsutilProcesses()
37
37
  self.connected = True
38
38
  elif self.get_method == 'pywin32':
39
- self.process_polling_instance = wmi_win32process.Pywin32Processes()
39
+ self.process_polling_instance = win32process.Pywin32Processes()
40
40
  elif self.get_method == 'process_dll':
41
41
  self.process_polling_instance = get_process_name_cmd_dll.ProcessNameCmdline()
42
42
 
@@ -130,4 +130,4 @@ test = get_process_list.get_processes()
130
130
 
131
131
  # globals need to be specified, otherwise the setup_code won't work with passed variables.
132
132
  times = timeit.timeit(setup=setup_code, stmt=test_code, number=times_to_test, globals=locals())
133
- print(f'Execution time: {times}')
133
+ print(f'Execution time: {times}')
@@ -0,0 +1,195 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+
4
+ from . import import_config
5
+
6
+
7
+ SCRIPT_VERSION: str = '1.7.0'
8
+
9
+
10
+ # CONFIG = None
11
+ LIST_OF_BOOLEANS: list = [
12
+ ('dns', 'enable'),
13
+ ('dns', 'offline_mode'),
14
+ ('dns', 'resolve_to_tcp_server_only_engine_domains'),
15
+ ('dns', 'resolve_to_tcp_server_all_domains'),
16
+ ('dns', 'resolve_regular'),
17
+ ('dns', 'set_default_dns_gateway_to_localhost'),
18
+ ('dns', 'set_default_dns_gateway_to_default_interface_ipv4'),
19
+ ('tcp', 'enable'),
20
+ ('tcp', 'engines_usage'),
21
+ ('tcp', 'server_response_mode'),
22
+ ('certificates', 'default_server_certificate_usage'),
23
+ ('certificates', 'sni_add_new_domains_to_default_server_certificate'),
24
+ ('certificates', 'custom_server_certificate_usage'),
25
+ ('certificates', 'sni_create_server_certificate_for_each_domain'),
26
+ ('certificates', 'sni_get_server_certificate_from_server_socket'),
27
+ ('skip_extensions', 'tls_web_client_authentication'),
28
+ ('skip_extensions', 'crl_distribution_points'),
29
+ ('skip_extensions', 'authority_information_access'),
30
+ ('process_name', 'get_process_name')
31
+ ]
32
+
33
+
34
+ TOML_TO_STATIC_CATEGORIES: dict = {
35
+ 'dns': 'DNSServer',
36
+ 'tcp': 'TCPServer',
37
+ 'log': 'Log',
38
+ 'recorder': 'Recorder',
39
+ 'certificates': 'Certificates',
40
+ 'skip_extensions': 'SkipExtensions',
41
+ 'process_name': 'ProcessName'
42
+ }
43
+
44
+
45
+ class MainConfig:
46
+ SCRIPT_DIRECTORY: str = None
47
+
48
+ ENGINES_DIRECTORY_PATH: str = None
49
+ ENGINES_DIRECTORY_NAME: str = "engines"
50
+ ENGINE_CONFIG_FILE_NAME: str = "engine_config.toml"
51
+
52
+ # Certificates.
53
+ default_server_certificate_name: str = 'default'
54
+ ca_certificate_name: str = 'ElaborateCA'
55
+ # CA Certificate name and file name without extension.
56
+ ca_certificate_filepath: str = None
57
+ # Default server certificate file name and path.
58
+ default_server_certificate_filename = f'{default_server_certificate_name}.pem'
59
+ default_server_certificate_filepath: str = None
60
+
61
+ @classmethod
62
+ def update(cls):
63
+ # This runs after the dataclass is initialized
64
+ 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'
66
+ cls.default_server_certificate_filepath = \
67
+ f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.default_server_certificate_filename}'
68
+
69
+
70
+ @dataclass
71
+ class DNSServer:
72
+ enable: bool
73
+ offline_mode: bool
74
+
75
+ listening_interface: str
76
+ listening_port: int
77
+ forwarding_dns_service_ipv4: str
78
+ cache_timeout_minutes: int
79
+
80
+ resolve_to_tcp_server_only_engine_domains: bool
81
+ resolve_to_tcp_server_all_domains: bool
82
+ resolve_regular: bool
83
+ target_tcp_server_ipv4: str
84
+
85
+ set_default_dns_gateway: str
86
+ set_default_dns_gateway_to_localhost: bool
87
+ set_default_dns_gateway_to_default_interface_ipv4: bool
88
+
89
+
90
+ @dataclass
91
+ class TCPServer:
92
+ enable: bool
93
+
94
+ engines_usage: bool
95
+ server_response_mode: bool
96
+
97
+ listening_interface: str
98
+ listening_port_list: list[int]
99
+
100
+ forwarding_dns_service_ipv4_list___only_for_localhost: list[str]
101
+
102
+
103
+ @dataclass
104
+ class Log:
105
+ logs_path: str
106
+
107
+
108
+ @dataclass
109
+ class Recorder:
110
+ recordings_path: str
111
+
112
+
113
+ @dataclass
114
+ class Certificates:
115
+ default_server_certificate_usage: bool
116
+ sni_add_new_domains_to_default_server_certificate: bool
117
+
118
+ custom_server_certificate_usage: bool
119
+ custom_server_certificate_path: str
120
+ custom_private_key_path: str
121
+
122
+ sni_create_server_certificate_for_each_domain: bool
123
+ sni_server_certificates_cache_directory: str
124
+ sni_get_server_certificate_from_server_socket: bool
125
+ sni_server_certificate_from_server_socket_download_directory: str
126
+
127
+ domains_all_times: list[str]
128
+
129
+
130
+ @dataclass
131
+ class SkipExtensions:
132
+ tls_web_client_authentication: bool
133
+ crl_distribution_points: bool
134
+ authority_information_access: bool
135
+
136
+ SKIP_EXTENSION_ID_LIST: list
137
+
138
+
139
+ @dataclass
140
+ class ProcessName:
141
+ get_process_name: bool
142
+ ssh_user: str
143
+ ssh_pass: str
144
+
145
+ ssh_script_to_execute = 'process_from_port'
146
+
147
+
148
+ def load_config(config_toml_file_path: str):
149
+ # global CONFIG
150
+
151
+ script_path = os.path.dirname(config_toml_file_path)
152
+ MainConfig.SCRIPT_DIRECTORY = script_path
153
+ MainConfig.update()
154
+
155
+ # Load the configuration file.
156
+ result = import_config.import_config_file(config_toml_file_path)
157
+ return result
158
+
159
+
160
+ # ============ Server Tester Specific ===============
161
+ CONFIG_INI_TESTER_FILE_NAME: str = 'config_tester.ini'
162
+
163
+ """
164
+ config.ini:
165
+ target_domain_or_ip: the domain or ip that the requests will be sent to. Better use domains, for better testing
166
+ simulation.
167
+ target_port: the port that requests will be sent to.
168
+ request_type: type of each request: json / string / binary.
169
+ json format that contain a key with hex string of the request that will be converted to bytes.
170
+ string that will contain a request and will be converted to bytes.
171
+ binary file that will contain all request data - will be converted to bytes.
172
+ request_json_hex_key_list: this key stores raw request in hex format, since there can be characters that can't be
173
+ decoded to string / unicode.
174
+ 'request_raw_hex' key is the default key in recorded files from mitm server, you may add keys for your custom
175
+ JSON files.
176
+ requests_directory: the directory that requests are will be taken from. Can be relative folder that will be in
177
+ current working directory.
178
+
179
+ parallel_connections_bool: boolean, sets if sockets should be initialized in threads (in parallel) or one after another.
180
+ Use all the connections / cycles in parallel when 'True'. New sockets will be created for each request.
181
+ Reuse the same socket / connection for all the requests when 'False'.
182
+ interval_between_requests_defaults_seconds: default interval in seconds between request sends.
183
+ interval_between_request_custom_list_seconds: list of intervals in seconds. If this configuration will not be empty,
184
+ this should be a list. Each interval in the list will follow the interval between requests and
185
+ 'interval_between_requests_defaults_seconds' will not be used. It will be used only if number of requests
186
+ is less than then number of intervals in 'interval_between_request_custom_list_seconds'. The missing intervals
187
+ will be filled by default values from 'interval_between_requests_defaults_seconds'.
188
+ Example: you have 10 requests.
189
+ interval_between_requests_defaults_seconds = 5
190
+ interval_between_request_custom_list_seconds = 7, 10, 8, 4, 15
191
+ The rest will be filled from defaults: 7, 10, 8, 4, 15, 5, 5, 5, 5
192
+ send_request_batch_cycles: how many batch cycles to send of the same 10 requests (or any other number of requests that
193
+ you might have in the requests folder.
194
+ interval_between_batch_cycles_seconds: interval in seconds between each batch.
195
+ """
@@ -0,0 +1,55 @@
1
+ from dataclasses import dataclass
2
+
3
+ from ..file_io import tomls
4
+
5
+
6
+ class CategoryNotFoundInConfigError(Exception):
7
+ pass
8
+
9
+
10
+ @dataclass
11
+ class PropertyUpdate:
12
+ """
13
+ :param category: str, Category in the config file.
14
+ :param category_property: str, Property in the category.
15
+ :param value: str, Value to set to the property.
16
+ """
17
+ category: str
18
+ category_property: str
19
+ value: any
20
+
21
+
22
+ def update_properties(
23
+ property_update_list: list[PropertyUpdate],
24
+ config_file_path: str
25
+ ) -> None:
26
+ """
27
+ Edit a property in the config file.
28
+
29
+ :param property_update_list: list of PropertyUpdate objects.
30
+ :param config_file_path: str, Path to the config file.
31
+
32
+ -----------
33
+
34
+ Config Example:
35
+ [category]
36
+ category_property = value
37
+ """
38
+
39
+ toml_config = tomls.read_toml_file(config_file_path)
40
+
41
+ for property_update in property_update_list:
42
+ if property_update.category not in toml_config:
43
+ raise CategoryNotFoundInConfigError(f"Category '{property_update.category}' not found in the config file.")
44
+
45
+ changes_dict: dict = dict()
46
+ for property_update in property_update_list:
47
+ if property_update.category in changes_dict:
48
+ changes_dict[property_update.category].update({property_update.category_property: property_update.value})
49
+ else:
50
+ changes_dict[property_update.category] = {property_update.category_property: property_update.value}
51
+
52
+ tomls.update_toml_file_with_new_config(
53
+ main_config_file_path=config_file_path,
54
+ changes_dict=changes_dict
55
+ )