atomicshop 2.20.8__py3-none-any.whl → 2.21.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/mitm/config_static.py +27 -20
- atomicshop/mitm/connection_thread_worker.py +136 -164
- atomicshop/mitm/engines/__parent/responder___parent.py +43 -43
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +43 -43
- atomicshop/mitm/engines/create_module_template.py +11 -5
- atomicshop/mitm/engines/create_module_template_main_example.py +13 -0
- atomicshop/mitm/import_config.py +174 -22
- atomicshop/mitm/initialize_engines.py +49 -68
- atomicshop/mitm/mitm_main.py +118 -152
- atomicshop/wrappers/socketw/dns_server.py +92 -68
- atomicshop/wrappers/socketw/socket_client.py +1 -1
- atomicshop/wrappers/socketw/socket_wrapper.py +136 -46
- {atomicshop-2.20.8.dist-info → atomicshop-2.21.1.dist-info}/METADATA +1 -1
- {atomicshop-2.20.8.dist-info → atomicshop-2.21.1.dist-info}/RECORD +18 -18
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- {atomicshop-2.20.8.dist-info → atomicshop-2.21.1.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.20.8.dist-info → atomicshop-2.21.1.dist-info}/WHEEL +0 -0
- {atomicshop-2.20.8.dist-info → atomicshop-2.21.1.dist-info}/top_level.txt +0 -0
|
@@ -23,49 +23,49 @@ class ResponderGeneral(ResponderParent):
|
|
|
23
23
|
|
|
24
24
|
self.logger = create_custom_logger()
|
|
25
25
|
|
|
26
|
-
def create_response(self, class_client_message: ClientMessage):
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
26
|
+
# def create_response(self, class_client_message: ClientMessage):
|
|
27
|
+
# # noinspection GrazieInspection
|
|
28
|
+
# """
|
|
29
|
+
# Function to create Response based on ClientMessage and its Request.
|
|
30
|
+
#
|
|
31
|
+
# :param class_client_message: contains request and other parameters to help creating response.
|
|
32
|
+
# :return: list of responses in bytes.
|
|
33
|
+
# -----------------------------------
|
|
34
|
+
#
|
|
35
|
+
# # Example of creating list of bytes using 'build_byte_response' function:
|
|
36
|
+
# result_list: list[bytes] = list()
|
|
37
|
+
# result_list.append(
|
|
38
|
+
# self.build_byte_response(
|
|
39
|
+
# http_version=class_client_message.request_raw_decoded.request_version,
|
|
40
|
+
# status_code=200,
|
|
41
|
+
# headers=response_headers,
|
|
42
|
+
# body=b''
|
|
43
|
+
# )
|
|
44
|
+
# )
|
|
45
|
+
#
|
|
46
|
+
# return result_list
|
|
47
|
+
# -----------------------------------
|
|
48
|
+
# # Example of extracting variables from URL PATH based on custom PATH TEMPLATE:
|
|
49
|
+
# # (more examples in 'self.extract_variables_from_path_template' function description)
|
|
50
|
+
# template_path: str = "/hithere/<variable1>/else/<variable2>/tested/"
|
|
51
|
+
# path_variables: dict = extract_variables_from_path_template(
|
|
52
|
+
# path=class_client_message.request_raw_decoded.path,
|
|
53
|
+
# template_path=template_path
|
|
54
|
+
# )
|
|
55
|
+
# -----------------------------------
|
|
56
|
+
# # Example of extracting value from URL PATH parameters after question mark:
|
|
57
|
+
# parameter_value = extract_value_from_path_parameter(
|
|
58
|
+
# path=class_client_message.request_raw_decoded.path,
|
|
59
|
+
# parameter='test_id'
|
|
60
|
+
# )
|
|
61
|
+
# """
|
|
62
|
+
#
|
|
63
|
+
# # byte_response: bytes = b''
|
|
64
|
+
# # self.logger.info(f"Response: {byte_response}")
|
|
65
|
+
#
|
|
66
|
+
# response_bytes_list: list[bytes] = list()
|
|
67
|
+
# # response_bytes_list.append(byte_response)
|
|
68
|
+
# return response_bytes_list
|
|
69
69
|
|
|
70
70
|
# def create_connect_response(self, class_client_message: ClientMessage):
|
|
71
71
|
# """
|
|
@@ -73,12 +73,18 @@ class CreateModuleTemplate:
|
|
|
73
73
|
|
|
74
74
|
# Add "" to each domain.
|
|
75
75
|
domains_with_quotes: list = [f'"{domain}"' for domain in self.domains]
|
|
76
|
-
|
|
76
|
+
|
|
77
|
+
config_lines_list.append('[engine]')
|
|
78
|
+
config_lines_list.append(f'domains = [{", ".join(domains_with_quotes)}]')
|
|
79
|
+
config_lines_list.append('dns_target = "127.0.0.1"')
|
|
80
|
+
config_lines_list.append('tcp_listening_address_list = ["0.0.0.0:443"]\n')
|
|
81
|
+
# config_lines_list.append(f'\n')
|
|
82
|
+
config_lines_list.append('[mtls]')
|
|
83
|
+
config_lines_list.append('# "subdomain.domain.com" = "file_name_in_current_dir.pem"\n')
|
|
77
84
|
# config_lines_list.append(f'\n')
|
|
78
|
-
config_lines_list.append(
|
|
79
|
-
config_lines_list.append(
|
|
80
|
-
config_lines_list.append(
|
|
81
|
-
config_lines_list.append(f'# "domain" = "example.com"\n')
|
|
85
|
+
config_lines_list.append('[no_sni]')
|
|
86
|
+
config_lines_list.append('get_from_dns = 1 # Blocking, the accept function will wait until the domain is received from DNS.')
|
|
87
|
+
config_lines_list.append('serve_domain_on_address = {0 = [{"example.com" = "127.0.0.2:443"}]}')
|
|
82
88
|
|
|
83
89
|
config_file_path = self.new_engine_directory + os.sep + CONFIG_FILE_NAME
|
|
84
90
|
|
atomicshop/mitm/import_config.py
CHANGED
|
@@ -8,7 +8,7 @@ from ..permissions import permissions
|
|
|
8
8
|
from ..wrappers.socketw import base
|
|
9
9
|
from ..basics import booleans
|
|
10
10
|
|
|
11
|
-
from . import config_static
|
|
11
|
+
from . import config_static, initialize_engines
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def assign_bool(dict_instance: dict, section: str, key: str):
|
|
@@ -19,16 +19,27 @@ def assign_bool(dict_instance: dict, section: str, key: str):
|
|
|
19
19
|
dict_instance[section][key] = True
|
|
20
20
|
elif dict_instance[section][key] == 0:
|
|
21
21
|
dict_instance[section][key] = False
|
|
22
|
+
elif isinstance(dict_instance[section][key], dict):
|
|
23
|
+
for subkey, subvalue in dict_instance[section][key].items():
|
|
24
|
+
if subkey == '1':
|
|
25
|
+
dict_instance[section][key] = {True: subvalue}
|
|
26
|
+
elif subkey == '0':
|
|
27
|
+
dict_instance[section][key] = {False: subvalue}
|
|
28
|
+
else:
|
|
29
|
+
print_api(f"Error: {section}.{key}.{subkey} must be 0 or 1.", color='red')
|
|
30
|
+
return 1
|
|
31
|
+
break
|
|
22
32
|
else:
|
|
23
33
|
print_api(f"Error: {section}.{key} must be 0 or 1.", color='red')
|
|
24
34
|
return 1
|
|
25
35
|
|
|
26
36
|
|
|
27
|
-
def
|
|
37
|
+
def import_config_files(
|
|
28
38
|
config_file_path: str
|
|
29
39
|
):
|
|
30
40
|
"""
|
|
31
41
|
Import the configuration file 'config.toml' and write all the values to 'config_static' dataclasses module.
|
|
42
|
+
|
|
32
43
|
:param config_file_path:
|
|
33
44
|
:return:
|
|
34
45
|
"""
|
|
@@ -50,10 +61,55 @@ def import_config_file(
|
|
|
50
61
|
|
|
51
62
|
manipulations_after_import()
|
|
52
63
|
|
|
64
|
+
result = import_engines_configs()
|
|
65
|
+
if result != 0:
|
|
66
|
+
return result
|
|
67
|
+
|
|
53
68
|
result = check_configurations()
|
|
54
69
|
return result
|
|
55
70
|
|
|
56
71
|
|
|
72
|
+
def import_engines_configs() -> int:
|
|
73
|
+
"""
|
|
74
|
+
Import the engines configuration files and write all the values to 'config_static' dataclasses module.
|
|
75
|
+
|
|
76
|
+
:return: int, status code.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# Get full paths of all the 'engine_config.ini' files.
|
|
80
|
+
engine_config_path_list = filesystem.get_paths_from_directory(
|
|
81
|
+
directory_path=config_static.MainConfig.ENGINES_DIRECTORY_PATH,
|
|
82
|
+
get_file=True,
|
|
83
|
+
file_name_check_pattern=config_static.MainConfig.ENGINE_CONFIG_FILE_NAME)
|
|
84
|
+
|
|
85
|
+
# Iterate through all the 'engine_config.ini' file paths.
|
|
86
|
+
domains_engine_list_full: list = list()
|
|
87
|
+
engines_list: list = list()
|
|
88
|
+
for engine_config_path in engine_config_path_list:
|
|
89
|
+
# Initialize engine.
|
|
90
|
+
current_module: initialize_engines.ModuleCategory = initialize_engines.ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
|
|
91
|
+
current_module.fill_engine_fields_from_config(engine_config_path.path)
|
|
92
|
+
current_module.initialize_engine()
|
|
93
|
+
|
|
94
|
+
# Extending the full engine domain list with this list.
|
|
95
|
+
domains_engine_list_full.extend(current_module.domain_list)
|
|
96
|
+
# Append the object to the engines list
|
|
97
|
+
engines_list.append(current_module)
|
|
98
|
+
# === EOF Importing engine modules =============================================================================
|
|
99
|
+
# ==== Initialize Reference Module =============================================================================
|
|
100
|
+
reference_module: initialize_engines.ModuleCategory = initialize_engines.ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
|
|
101
|
+
reference_module.fill_engine_fields_from_general_reference(config_static.MainConfig.ENGINES_DIRECTORY_PATH)
|
|
102
|
+
reference_module.initialize_engine(reference_general=True)
|
|
103
|
+
|
|
104
|
+
# Assigning all the engines domains to all time domains, that will be responsible for adding new domains.
|
|
105
|
+
config_static.Certificates.domains_all_times = list(domains_engine_list_full)
|
|
106
|
+
|
|
107
|
+
config_static.ENGINES_LIST = engines_list
|
|
108
|
+
config_static.REFERENCE_MODULE = reference_module
|
|
109
|
+
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
|
|
57
113
|
def check_configurations() -> int:
|
|
58
114
|
"""
|
|
59
115
|
Check the configurations from the 'config.toml' file.
|
|
@@ -68,31 +124,103 @@ def check_configurations() -> int:
|
|
|
68
124
|
print_api("Both DNS and TCP servers in config ini file, nothing to run. Exiting...", color='red')
|
|
69
125
|
return 1
|
|
70
126
|
|
|
71
|
-
#
|
|
72
|
-
if not config_static.TCPServer.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
# Checking if listening interfaces were set.
|
|
128
|
+
if not config_static.TCPServer.no_engines_usage_to_listen_addresses_enable:
|
|
129
|
+
# If no engines were found, check if listening interfaces were set in the main config.
|
|
130
|
+
if not config_static.ENGINES_LIST:
|
|
131
|
+
message = (
|
|
132
|
+
"\n"
|
|
133
|
+
"No engines found. Create with [create_template.py].\n"
|
|
134
|
+
"Exiting...")
|
|
135
|
+
print_api(message, color="red")
|
|
136
|
+
return 1
|
|
137
|
+
else:
|
|
138
|
+
if not config_static.TCPServer.no_engines_listening_address_list:
|
|
139
|
+
message = (
|
|
140
|
+
"\n"
|
|
141
|
+
"No listening interfaces. Set [no_engines_usage_to_listen_addresses] in the main [config.toml].\n"
|
|
142
|
+
"Exiting...")
|
|
143
|
+
print_api(message, color="red")
|
|
144
|
+
return 1
|
|
145
|
+
|
|
146
|
+
if not config_static.ENGINES_LIST and config_static.DNSServer.resolve_by_engine:
|
|
147
|
+
error_message = (
|
|
148
|
+
f"No engines were found in: [{config_static.MainConfig.ENGINES_DIRECTORY_PATH}]\n"
|
|
149
|
+
f"But the DNS routing is set to use them for routing.\n"
|
|
150
|
+
f"Please check your DNS routing configuration in the [config.toml] file or create an engine with [create_template.py].")
|
|
151
|
+
print_api(error_message, color="red")
|
|
78
152
|
return 1
|
|
79
153
|
|
|
154
|
+
for engine in config_static.ENGINES_LIST:
|
|
155
|
+
if engine.no_sni.get_from_dns and engine.no_sni.serve_domain_on_address_enable:
|
|
156
|
+
message = (
|
|
157
|
+
f"Both [get_from_dns] and [serve_domain_on_address] are enabled in [no_sni] section of the engine.\n"
|
|
158
|
+
f"Only one Can be True.")
|
|
159
|
+
print_api(message, color="red")
|
|
160
|
+
return 1
|
|
161
|
+
if not engine.no_sni.get_from_dns and not engine.no_sni.serve_domain_on_address_enable:
|
|
162
|
+
message = (
|
|
163
|
+
f"Both [get_from_dns] and [serve_domain_on_address] are disabled in [no_sni] section of the engine.\n"
|
|
164
|
+
f"Only one Can be True.")
|
|
165
|
+
print_api(message, color="red")
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
if engine.no_sni.serve_domain_on_address_enable:
|
|
169
|
+
# Check if the domains in no_sni are the same as in the engine. They should not be.
|
|
170
|
+
# Same goes for the address.
|
|
171
|
+
for domain, address_ip_port in engine.no_sni.serve_domain_on_address_dict.items():
|
|
172
|
+
if domain in engine.domain_list:
|
|
173
|
+
message = (
|
|
174
|
+
f"[*] No SNI setting: The domain [{domain}] is in the engine domains list [{engine.domain_list}].\n"
|
|
175
|
+
f"The point of the no_sni section is to serve specific domains on separate addresses.\n")
|
|
176
|
+
print_api(message, color="red")
|
|
177
|
+
return 1
|
|
178
|
+
|
|
179
|
+
if address_ip_port in engine.tcp_listening_address_list:
|
|
180
|
+
message = (
|
|
181
|
+
f"[*] No SNI setting: The address [{address_ip_port}] is in the engine listening interfaces list [{engine.tcp_listening_address_list}].\n"
|
|
182
|
+
f"The point of the no_sni section is to serve specific domains on separate addresses.\n")
|
|
183
|
+
print_api(message, color="red")
|
|
184
|
+
return 1
|
|
185
|
+
|
|
80
186
|
# Check admin right if on localhost ============================================================================
|
|
81
|
-
# If the
|
|
187
|
+
# If any of the DNS IP target addresses is localhost loopback, then we need to check if the script
|
|
82
188
|
# is executed with admin rights. There are some processes that 'psutil' can't get their command line if not
|
|
83
189
|
# executed with administrative privileges.
|
|
84
190
|
# Also, check Admin privileges only if 'config.tcp['get_process_name']' was set to 'True' in 'config.ini' of
|
|
85
191
|
# the script.
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
192
|
+
if config_static.ProcessName.get_process_name:
|
|
193
|
+
# If the DNS server was set to resolve by engines, we need to check all relevant engine settings.
|
|
194
|
+
if config_static.DNSServer.resolve_by_engine:
|
|
195
|
+
for engine in config_static.ENGINES_LIST:
|
|
196
|
+
# Check if the DNS target is localhost loopback.
|
|
197
|
+
if engine.dns_target in base.THIS_DEVICE_IP_LIST or engine.dns_target.startswith('127.'):
|
|
198
|
+
if not is_admin:
|
|
199
|
+
message: str = \
|
|
200
|
+
("Need to run the script with administrative rights to get the process name while TCP "
|
|
201
|
+
"running on the same computer.\nExiting...")
|
|
202
|
+
print_api(message, color='red')
|
|
203
|
+
return 1
|
|
204
|
+
if engine.no_sni.serve_domain_on_address_enable:
|
|
205
|
+
no_sni_target_address_list: list = engine.no_sni.serve_domain_on_address_dict.values()
|
|
206
|
+
for no_sni_target_address in no_sni_target_address_list:
|
|
207
|
+
if no_sni_target_address in base.THIS_DEVICE_IP_LIST or \
|
|
208
|
+
no_sni_target_address.startswith('127.'):
|
|
209
|
+
if not is_admin:
|
|
210
|
+
message: str = \
|
|
211
|
+
("Need to run the script with administrative rights to get the process name while TCP "
|
|
212
|
+
"running on the same computer.\nExiting...")
|
|
213
|
+
print_api(message, color='red')
|
|
214
|
+
return 1
|
|
215
|
+
if config_static.DNSServer.resolve_all_domains_to_ipv4:
|
|
216
|
+
if config_static.DNSServer.target_ipv4 in base.THIS_DEVICE_IP_LIST or \
|
|
217
|
+
config_static.DNSServer.target_ipv4.startswith('127.'):
|
|
218
|
+
if not is_admin:
|
|
219
|
+
message: str = \
|
|
220
|
+
("Need to run the script with administrative rights to get the process name while TCP "
|
|
221
|
+
"running on the same computer.\nExiting...")
|
|
222
|
+
print_api(message, color='red')
|
|
223
|
+
return 1
|
|
96
224
|
|
|
97
225
|
try:
|
|
98
226
|
booleans.is_only_1_true_in_list(
|
|
@@ -136,6 +264,14 @@ def check_configurations() -> int:
|
|
|
136
264
|
print_api(message, color='red')
|
|
137
265
|
return 1
|
|
138
266
|
|
|
267
|
+
if not config_static.DNSServer.resolve_by_engine and not config_static.DNSServer.resolve_regular_pass_thru and not \
|
|
268
|
+
config_static.DNSServer.resolve_all_domains_to_ipv4_enable:
|
|
269
|
+
message: str = (
|
|
270
|
+
"No DNS server resolving settings were set.\n"
|
|
271
|
+
"Please check your DNS server settings in the [config.toml] file.")
|
|
272
|
+
print_api(message, color='red')
|
|
273
|
+
return 1
|
|
274
|
+
|
|
139
275
|
# This is checked directly in the SocketWrapper.
|
|
140
276
|
# if (config_static.Certificates.install_ca_certificate_to_root_store and not is_admin) or \
|
|
141
277
|
# (config_static.Certificates.uninstall_unused_ca_certificates_with_mitm_ca_name and not is_admin):
|
|
@@ -144,11 +280,27 @@ def check_configurations() -> int:
|
|
|
144
280
|
# print_api(message, color='red')
|
|
145
281
|
# return 1
|
|
146
282
|
|
|
147
|
-
|
|
148
283
|
return 0
|
|
149
284
|
|
|
150
285
|
|
|
151
286
|
def manipulations_after_import():
|
|
287
|
+
for key, value in config_static.DNSServer.resolve_all_domains_to_ipv4.items():
|
|
288
|
+
config_static.DNSServer.resolve_all_domains_to_ipv4_enable = key
|
|
289
|
+
config_static.DNSServer.target_ipv4 = value
|
|
290
|
+
break
|
|
291
|
+
|
|
292
|
+
for key, value in config_static.TCPServer.no_engines_usage_to_listen_addresses.items():
|
|
293
|
+
# If the key is False, it means that the user doesn't want to use the no_engines_listening_address_list.
|
|
294
|
+
# So, we'll assign an empty list to it.
|
|
295
|
+
if not key:
|
|
296
|
+
config_static.TCPServer.no_engines_usage_to_listen_addresses_enable = False
|
|
297
|
+
config_static.TCPServer.no_engines_listening_address_list = list()
|
|
298
|
+
# If the key is True, it means that the user wants to use the no_engines_listening_address_list.
|
|
299
|
+
else:
|
|
300
|
+
config_static.TCPServer.no_engines_usage_to_listen_addresses_enable = key
|
|
301
|
+
config_static.TCPServer.no_engines_listening_address_list = value
|
|
302
|
+
break
|
|
303
|
+
|
|
152
304
|
# Convert extensions to skip to a list of extension IDs.
|
|
153
305
|
skip_extensions: list = list()
|
|
154
306
|
if config_static.SkipExtensions.tls_web_client_authentication:
|
|
@@ -166,7 +318,7 @@ def manipulations_after_import():
|
|
|
166
318
|
config_static.Certificates.custom_server_certificate_path, config_static.MainConfig.SCRIPT_DIRECTORY)
|
|
167
319
|
|
|
168
320
|
config_static.LogRec.recordings_path = (
|
|
169
|
-
|
|
321
|
+
config_static.LogRec.logs_path + os.sep + config_static.LogRec.recordings_directory_name)
|
|
170
322
|
|
|
171
323
|
# At this point the user that sets the config can set it to null or empty string ''. We will make sure
|
|
172
324
|
# that the path is None if it's empty.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import sys
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
|
|
5
4
|
from ..file_io import tomls
|
|
@@ -8,12 +7,24 @@ from .engines.__reference_general import parser___reference_general, responder__
|
|
|
8
7
|
recorder___reference_general
|
|
9
8
|
|
|
10
9
|
|
|
10
|
+
class NoSNI:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.get_from_dns: bool = False
|
|
13
|
+
self.serve_domain_on_address_enable: bool = False
|
|
14
|
+
self.serve_domain_on_address_dict: dict = dict()
|
|
15
|
+
|
|
16
|
+
|
|
11
17
|
class ModuleCategory:
|
|
12
18
|
def __init__(self, script_directory: str):
|
|
13
|
-
self.domain_list: list = list()
|
|
14
19
|
self.engine_name: str = str()
|
|
15
20
|
self.script_directory: str = script_directory
|
|
16
21
|
|
|
22
|
+
self.domain_list: list = list()
|
|
23
|
+
self.dns_target: str = str()
|
|
24
|
+
self.tcp_listening_address_list: list = list()
|
|
25
|
+
self.mtls: dict = dict()
|
|
26
|
+
self.no_sni: NoSNI = NoSNI()
|
|
27
|
+
|
|
17
28
|
self.parser_file_path: str = str()
|
|
18
29
|
self.responder_file_path: str = str()
|
|
19
30
|
self.recorder_file_path: str = str()
|
|
@@ -22,12 +33,6 @@ class ModuleCategory:
|
|
|
22
33
|
self.responder_class_object: str = str()
|
|
23
34
|
self.recorder_class_object: str = str()
|
|
24
35
|
|
|
25
|
-
# The instance of the recorder class that will be initiated once in the script start
|
|
26
|
-
self.responder_instance = None
|
|
27
|
-
|
|
28
|
-
self.mtls: dict = dict()
|
|
29
|
-
self.no_sni: dict = dict()
|
|
30
|
-
|
|
31
36
|
def fill_engine_fields_from_general_reference(self, engines_fullpath: str):
|
|
32
37
|
# Reference module variables.
|
|
33
38
|
self.engine_name = '__reference_general'
|
|
@@ -45,9 +50,24 @@ class ModuleCategory:
|
|
|
45
50
|
self.engine_name = Path(engine_directory_path).name
|
|
46
51
|
|
|
47
52
|
# Getting the parameters from engine config file
|
|
48
|
-
self.domain_list = configuration_data['domains']
|
|
49
|
-
self.
|
|
50
|
-
self.
|
|
53
|
+
self.domain_list = configuration_data['engine']['domains']
|
|
54
|
+
self.dns_target = configuration_data['engine']['dns_target']
|
|
55
|
+
self.tcp_listening_address_list = configuration_data['engine']['tcp_listening_address_list']
|
|
56
|
+
|
|
57
|
+
if 'mtls' in configuration_data:
|
|
58
|
+
self.mtls = configuration_data['mtls']
|
|
59
|
+
|
|
60
|
+
self.no_sni.get_from_dns = bool(configuration_data['no_sni']['get_from_dns'])
|
|
61
|
+
|
|
62
|
+
for enable_bool, address_list in configuration_data['no_sni']['serve_domain_on_address'].items():
|
|
63
|
+
if enable_bool in ['0', '1']:
|
|
64
|
+
self.no_sni.serve_domain_on_address_enable = bool(int(enable_bool))
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError(f"Error: no_sni -> serve_domain_on_address -> key must be 0 or 1.")
|
|
67
|
+
|
|
68
|
+
for address in address_list:
|
|
69
|
+
for domain, address_ip_port in address.items():
|
|
70
|
+
self.no_sni.serve_domain_on_address_dict = {domain: address_ip_port}
|
|
51
71
|
|
|
52
72
|
# If there's module configuration file, but no domains in it, there's no point to continue.
|
|
53
73
|
# Since, each engine is based on domains.
|
|
@@ -69,60 +89,36 @@ class ModuleCategory:
|
|
|
69
89
|
for subdomain, file_name in self.mtls.items():
|
|
70
90
|
self.mtls[subdomain] = f'{engine_directory_path}{os.sep}{file_name}'
|
|
71
91
|
|
|
72
|
-
def initialize_engine(self,
|
|
73
|
-
# Initiating logger for each engine by its name
|
|
74
|
-
# loggingw.create_logger(
|
|
75
|
-
# logger_name=self.engine_name,
|
|
76
|
-
# directory_path=logs_path,
|
|
77
|
-
# add_stream=True,
|
|
78
|
-
# add_timedfile_with_internal_queue=True,
|
|
79
|
-
# formatter_streamhandler='DEFAULT',
|
|
80
|
-
# formatter_filehandler='DEFAULT',
|
|
81
|
-
# backupCount=config_static.LogRec.store_logs_for_x_days
|
|
82
|
-
# )
|
|
83
|
-
|
|
92
|
+
def initialize_engine(self, reference_general: bool = False):
|
|
84
93
|
if not reference_general:
|
|
85
94
|
self.parser_class_object = import_first_class_name_from_file_path(
|
|
86
|
-
self.script_directory, self.parser_file_path
|
|
95
|
+
self.script_directory, self.parser_file_path)
|
|
87
96
|
self.responder_class_object = import_first_class_name_from_file_path(
|
|
88
|
-
self.script_directory, self.responder_file_path
|
|
97
|
+
self.script_directory, self.responder_file_path)
|
|
89
98
|
self.recorder_class_object = import_first_class_name_from_file_path(
|
|
90
|
-
self.script_directory, self.recorder_file_path
|
|
99
|
+
self.script_directory, self.recorder_file_path)
|
|
91
100
|
else:
|
|
92
101
|
self.parser_class_object = parser___reference_general.ParserGeneral
|
|
93
102
|
self.responder_class_object = responder___reference_general.ResponderGeneral
|
|
94
103
|
self.recorder_class_object = recorder___reference_general.RecorderGeneral
|
|
95
104
|
|
|
96
|
-
try:
|
|
97
|
-
# Since we're using responder to aggregate requests to build responses based on several
|
|
98
|
-
# requests, we need to initiate responder's class only once in the beginning and assign
|
|
99
|
-
# this instance to a variable that will be called later per domain.
|
|
100
|
-
self.responder_instance = self.responder_class_object()
|
|
101
|
-
except Exception as exception_object:
|
|
102
|
-
logger.error_exception(f"Exception while initializing responder: {exception_object}")
|
|
103
|
-
sys.exit()
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
# Assigning external class object by message domain received from client. If the domain is not in the list,
|
|
107
|
-
# the reference general module will be assigned.
|
|
108
106
|
def assign_class_by_domain(
|
|
109
|
-
engines_usage: bool,
|
|
110
107
|
engines_list: list,
|
|
111
108
|
message_domain_name: str,
|
|
112
|
-
reference_module
|
|
113
|
-
logger=None
|
|
109
|
+
reference_module
|
|
114
110
|
):
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
mtls_data: dict = dict()
|
|
111
|
+
"""
|
|
112
|
+
Assigning external class object by message domain received from client. If the domain is not in the list,
|
|
113
|
+
the reference general module will be assigned.
|
|
114
|
+
"""
|
|
120
115
|
|
|
121
116
|
# In case SNI came empty in the request from client, then there's no point in iterating through engine domains.
|
|
117
|
+
module = None
|
|
122
118
|
if message_domain_name:
|
|
123
|
-
# If
|
|
119
|
+
# If engine/s exit, the engines_list will not be empty, then we'll iterate through the list of engines
|
|
124
120
|
# to find the domain in the list of domains of the engine.
|
|
125
|
-
if
|
|
121
|
+
if engines_list:
|
|
126
122
|
# Checking if current domain is in engines' domain list to activate domain specific engine
|
|
127
123
|
for function_module in engines_list:
|
|
128
124
|
# The list: matches_list = ["domain1.com", "domain2.com", "domain3.com"]
|
|
@@ -134,18 +130,8 @@ def assign_class_by_domain(
|
|
|
134
130
|
# in the list of strings: if any(a_string in x for x in matches_list):
|
|
135
131
|
# In this case list is the same and string: a_string = domain
|
|
136
132
|
if any(x in message_domain_name for x in function_module.domain_list):
|
|
137
|
-
# Assigning
|
|
138
|
-
|
|
139
|
-
function_recorder = function_module.recorder_class_object
|
|
140
|
-
# Since the responder is being initiated only once, we're assigning only the instance
|
|
141
|
-
function_responder = function_module.responder_instance
|
|
142
|
-
mtls_data = function_module.mtls
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
logger.info(f"Assigned Modules for [{message_domain_name}]: "
|
|
146
|
-
f"{function_module.parser_class_object.__name__}, "
|
|
147
|
-
f"{function_module.responder_class_object.__name__}, "
|
|
148
|
-
f"{function_module.recorder_class_object.__name__}")
|
|
133
|
+
# Assigning module by current engine of the domain
|
|
134
|
+
module = function_module
|
|
149
135
|
|
|
150
136
|
# If the domain was found in the current list of class domains, we can stop the loop
|
|
151
137
|
break
|
|
@@ -154,12 +140,7 @@ def assign_class_by_domain(
|
|
|
154
140
|
# It's enough to check only parser, since responder and recorder also will be empty.
|
|
155
141
|
# This section is also relevant if SNI came empty in the request from the client and no domain was passed by the
|
|
156
142
|
# DNS Server.
|
|
157
|
-
if not
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
# Since the responder is being initiated only once, we're assigning only the instance
|
|
162
|
-
function_responder = reference_module.responder_instance
|
|
163
|
-
|
|
164
|
-
# Return all the initiated modules
|
|
165
|
-
return function_parser, function_responder, function_recorder, mtls_data
|
|
143
|
+
if not module:
|
|
144
|
+
module = reference_module
|
|
145
|
+
|
|
146
|
+
return module
|