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.
- atomicshop/__init__.py +1 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/basics/booleans.py +14 -5
- atomicshop/dns.py +104 -0
- atomicshop/file_io/docxs.py +8 -0
- atomicshop/file_io/tomls.py +133 -0
- atomicshop/filesystem.py +5 -4
- atomicshop/get_process_list.py +3 -3
- atomicshop/mitm/config_static.py +195 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +54 -90
- atomicshop/mitm/import_config.py +147 -139
- atomicshop/mitm/initialize_engines.py +7 -2
- atomicshop/mitm/initialize_mitm_server.py +161 -107
- atomicshop/mitm/shared_functions.py +0 -1
- atomicshop/mitm/statistic_analyzer.py +13 -1
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +54 -14
- atomicshop/script_as_string_processor.py +5 -1
- atomicshop/wrappers/cryptographyw.py +3 -3
- atomicshop/wrappers/psutilw/networks.py +25 -1
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/helpers.py +127 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +167 -0
- atomicshop/wrappers/socketw/accepter.py +8 -8
- atomicshop/wrappers/socketw/base.py +13 -0
- atomicshop/wrappers/socketw/certificator.py +202 -149
- atomicshop/wrappers/socketw/creator.py +15 -35
- atomicshop/wrappers/socketw/dns_server.py +155 -102
- atomicshop/wrappers/socketw/exception_wrapper.py +8 -27
- atomicshop/wrappers/socketw/get_process.py +115 -95
- atomicshop/wrappers/socketw/sni.py +298 -164
- atomicshop/wrappers/socketw/socket_client.py +5 -12
- atomicshop/wrappers/socketw/socket_server_tester.py +1 -1
- atomicshop/wrappers/socketw/socket_wrapper.py +328 -72
- atomicshop/wrappers/socketw/statistics_csv.py +94 -16
- {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/METADATA +1 -1
- {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/RECORD +41 -36
- atomicshop/mitm/config_editor.py +0 -37
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/WHEEL +0 -0
- {atomicshop-2.15.13.dist-info → atomicshop-2.16.0.dist-info}/top_level.txt +0 -0
|
@@ -1,53 +1,56 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import sys
|
|
3
2
|
import threading
|
|
4
3
|
import time
|
|
5
4
|
|
|
6
|
-
# Importing atomicshop package to get the version of the package.
|
|
7
|
-
import atomicshop
|
|
5
|
+
import atomicshop # Importing atomicshop package to get the version of the package.
|
|
8
6
|
|
|
9
|
-
from
|
|
10
|
-
from .initialize_engines import ModuleCategory
|
|
11
|
-
from .connection_thread_worker import thread_worker_main
|
|
12
|
-
from .. import filesystem, queues
|
|
7
|
+
from .. import filesystem, queues, dns, on_exit, permissions
|
|
13
8
|
from ..python_functions import get_current_python_version_string, check_python_version_compliance
|
|
14
|
-
from ..wrappers.socketw
|
|
15
|
-
from ..wrappers.socketw import dns_server
|
|
16
|
-
from ..wrappers.psutilw import networks
|
|
17
|
-
from ..basics import dicts_nested
|
|
9
|
+
from ..wrappers.socketw import socket_wrapper, dns_server, base
|
|
18
10
|
from ..wrappers.loggingw import loggingw
|
|
19
11
|
from ..print_api import print_api
|
|
20
12
|
|
|
13
|
+
from .initialize_engines import ModuleCategory
|
|
14
|
+
from .connection_thread_worker import thread_worker_main
|
|
15
|
+
from . import config_static
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def exit_cleanup():
|
|
19
|
+
if config_static.DNSServer.set_default_dns_gateway:
|
|
20
|
+
is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
|
|
21
|
+
print_api(f'Current DNS Gateway: {current_dns_gateway}')
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
if current_dns_gateway == config_static.DNSServer.set_default_dns_gateway and not is_dns_dynamic:
|
|
24
|
+
if permissions.is_admin():
|
|
25
|
+
dns.set_connection_dns_gateway_dynamic(use_default_connection=True)
|
|
26
|
+
print_api("Returned default DNS gateway...", color='blue')
|
|
24
27
|
|
|
25
28
|
|
|
26
|
-
def initialize_mitm_server(
|
|
29
|
+
def initialize_mitm_server(config_file_path: str):
|
|
30
|
+
on_exit.register_exit_handler(exit_cleanup)
|
|
31
|
+
|
|
27
32
|
# Main function should return integer with error code, 0 is successful.
|
|
28
33
|
# Since listening server is infinite, this will not be reached.
|
|
29
34
|
# After modules import - we check for python version.
|
|
30
35
|
check_python_version_compliance(minimum_version='3.11')
|
|
31
36
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
config_importer.open()
|
|
37
|
-
config = config_importer.config
|
|
37
|
+
# Import the configuration file.
|
|
38
|
+
result = config_static.load_config(config_file_path)
|
|
39
|
+
if result != 0:
|
|
40
|
+
return result
|
|
38
41
|
|
|
39
42
|
# Create folders.
|
|
40
|
-
filesystem.create_directory(
|
|
41
|
-
filesystem.create_directory(
|
|
42
|
-
if
|
|
43
|
+
filesystem.create_directory(config_static.Log.logs_path)
|
|
44
|
+
filesystem.create_directory(config_static.Recorder.recordings_path)
|
|
45
|
+
if config_static.Certificates.sni_get_server_certificate_from_server_socket:
|
|
43
46
|
filesystem.create_directory(
|
|
44
|
-
|
|
47
|
+
config_static.Certificates.sni_server_certificate_from_server_socket_download_directory)
|
|
45
48
|
|
|
46
49
|
# Create a logger that will log messages to file, Initiate System logger.
|
|
47
50
|
logger_name = "system"
|
|
48
51
|
system_logger = loggingw.create_logger(
|
|
49
52
|
logger_name=logger_name,
|
|
50
|
-
file_path=f"{
|
|
53
|
+
file_path=f"{config_static.Log.logs_path}{os.sep}{logger_name}.txt",
|
|
51
54
|
add_stream=True,
|
|
52
55
|
add_timedfile=True,
|
|
53
56
|
formatter_streamhandler='DEFAULT',
|
|
@@ -56,44 +59,37 @@ def initialize_mitm_server(config_static):
|
|
|
56
59
|
|
|
57
60
|
# Writing first log.
|
|
58
61
|
system_logger.info("======================================")
|
|
59
|
-
|
|
60
|
-
if config_importer.admin_rights is not None:
|
|
61
|
-
if not config_importer.admin_rights:
|
|
62
|
-
system_logger.error("User continued with errors on Command Line harvesting for system processes.")
|
|
63
|
-
|
|
64
62
|
system_logger.info("Server Started.")
|
|
65
63
|
system_logger.info(f"Python Version: {get_current_python_version_string()}")
|
|
66
64
|
system_logger.info(f"Script Version: {config_static.SCRIPT_VERSION}")
|
|
67
65
|
system_logger.info(f"Atomic Workshop Version: {atomicshop.__version__}")
|
|
68
|
-
system_logger.info(f"
|
|
69
|
-
system_logger.info(f"
|
|
70
|
-
system_logger.info(f"Recordings folder for Requests/Responses: {config['recorder']['recordings_path']}")
|
|
66
|
+
system_logger.info(f"Log folder: {config_static.Log.logs_path}")
|
|
67
|
+
system_logger.info(f"Recordings folder for Requests/Responses: {config_static.Recorder.recordings_path}")
|
|
71
68
|
system_logger.info(f"Loaded system logger: {system_logger}")
|
|
72
69
|
|
|
73
|
-
system_logger.info(f"TCP Server Target IP: {
|
|
70
|
+
system_logger.info(f"TCP Server Target IP: {config_static.DNSServer.target_tcp_server_ipv4}")
|
|
74
71
|
|
|
75
72
|
# Some 'config.ini' settings logging ===========================================================================
|
|
76
|
-
if
|
|
73
|
+
if config_static.Certificates.default_server_certificate_usage:
|
|
77
74
|
system_logger.info(
|
|
78
75
|
f"Default server certificate usage enabled, if no SNI available: "
|
|
79
|
-
f"{config_static.
|
|
80
|
-
f"{os.sep}{config_static.CONFIG_EXTENDED['certificates']['default_server_certificate_name']}.pem")
|
|
76
|
+
f"{config_static.MainConfig.default_server_certificate_filepath}")
|
|
81
77
|
|
|
82
|
-
if
|
|
78
|
+
if config_static.Certificates.sni_server_certificates_cache_directory:
|
|
83
79
|
system_logger.info(
|
|
84
80
|
f"SNI function certificates creation enabled. Certificates cache: "
|
|
85
|
-
f"{
|
|
81
|
+
f"{config_static.Certificates.sni_server_certificates_cache_directory}")
|
|
86
82
|
else:
|
|
87
83
|
system_logger.info(f"SNI function certificates creation disabled.")
|
|
88
84
|
|
|
89
|
-
if
|
|
85
|
+
if config_static.Certificates.custom_server_certificate_usage:
|
|
90
86
|
system_logger.info(f"Custom server certificate usage is enabled.")
|
|
91
|
-
system_logger.info(f"Custom Certificate Path: {
|
|
87
|
+
system_logger.info(f"Custom Certificate Path: {config_static.Certificates.custom_server_certificate_path}")
|
|
92
88
|
|
|
93
89
|
# If 'custom_private_key_path' field was populated.
|
|
94
|
-
if
|
|
90
|
+
if config_static.Certificates.custom_private_key_path:
|
|
95
91
|
system_logger.info(
|
|
96
|
-
f"Custom Certificate Private Key Path: {
|
|
92
|
+
f"Custom Certificate Private Key Path: {config_static.Certificates.custom_private_key_path}")
|
|
97
93
|
else:
|
|
98
94
|
system_logger.info(f"Custom Certificate Private Key Path wasn't provided in [advanced] section. "
|
|
99
95
|
f"Assuming the private key is inside the certificate file.")
|
|
@@ -103,17 +99,17 @@ def initialize_mitm_server(config_static):
|
|
|
103
99
|
|
|
104
100
|
# Get full paths of all the 'engine_config.ini' files.
|
|
105
101
|
engine_config_path_list = filesystem.get_file_paths_from_directory(
|
|
106
|
-
directory_path=config_static.ENGINES_DIRECTORY_PATH,
|
|
107
|
-
file_name_check_pattern=config_static.ENGINE_CONFIG_FILE_NAME)
|
|
102
|
+
directory_path=config_static.MainConfig.ENGINES_DIRECTORY_PATH,
|
|
103
|
+
file_name_check_pattern=config_static.MainConfig.ENGINE_CONFIG_FILE_NAME)
|
|
108
104
|
|
|
109
105
|
# Iterate through all the 'engine_config.ini' file paths.
|
|
110
106
|
domains_engine_list_full: list = list()
|
|
111
107
|
engines_list: list = list()
|
|
112
108
|
for engine_config_path in engine_config_path_list:
|
|
113
109
|
# Initialize engine.
|
|
114
|
-
current_module = ModuleCategory(config_static.
|
|
110
|
+
current_module = ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
|
|
115
111
|
current_module.fill_engine_fields_from_config(engine_config_path)
|
|
116
|
-
current_module.initialize_engine(logs_path=
|
|
112
|
+
current_module.initialize_engine(logs_path=config_static.Log.logs_path,
|
|
117
113
|
logger=system_logger)
|
|
118
114
|
|
|
119
115
|
# Extending the full engine domain list with this list.
|
|
@@ -122,9 +118,9 @@ def initialize_mitm_server(config_static):
|
|
|
122
118
|
engines_list.append(current_module)
|
|
123
119
|
# === EOF Importing engine modules =============================================================================
|
|
124
120
|
# ==== Initialize Reference Module =============================================================================
|
|
125
|
-
reference_module = ModuleCategory(config_static.
|
|
126
|
-
reference_module.fill_engine_fields_from_general_reference(config_static.ENGINES_DIRECTORY_PATH)
|
|
127
|
-
reference_module.initialize_engine(logs_path=
|
|
121
|
+
reference_module = ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
|
|
122
|
+
reference_module.fill_engine_fields_from_general_reference(config_static.MainConfig.ENGINES_DIRECTORY_PATH)
|
|
123
|
+
reference_module.initialize_engine(logs_path=config_static.Log.logs_path,
|
|
128
124
|
logger=system_logger, stdout=False, reference_general=True)
|
|
129
125
|
# === EOF Initialize Reference Module ==========================================================================
|
|
130
126
|
# === Engine logging ===========================================================================================
|
|
@@ -139,41 +135,41 @@ def initialize_mitm_server(config_static):
|
|
|
139
135
|
f"{engine.recorder_class_object.__name__}")
|
|
140
136
|
print_api(message, logger=system_logger)
|
|
141
137
|
|
|
142
|
-
if
|
|
138
|
+
if config_static.DNSServer.enable:
|
|
143
139
|
print_api("DNS Server is enabled.", logger=system_logger)
|
|
144
140
|
|
|
145
141
|
# If engines were found and dns is set to route by the engine domains.
|
|
146
|
-
if engines_list and
|
|
142
|
+
if engines_list and config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
|
|
147
143
|
print_api("Engine domains will be routed by the DNS server to Built-in TCP Server.", logger=system_logger)
|
|
148
144
|
# If engines were found, but the dns isn't set to route to engines.
|
|
149
|
-
elif engines_list and not
|
|
145
|
+
elif engines_list and not config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
|
|
150
146
|
message = f"[*] Engine domains found, but the DNS routing is set not to use them for routing."
|
|
151
147
|
print_api(message, color="yellow", logger=system_logger)
|
|
152
|
-
elif not engines_list and
|
|
148
|
+
elif not engines_list and config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
|
|
153
149
|
raise ValueError("No engines were found, but the DNS routing is set to use them for routing.\n"
|
|
154
150
|
"Please check your DNS configuration in the 'config.ini' file.")
|
|
155
151
|
|
|
156
|
-
if
|
|
152
|
+
if config_static.DNSServer.resolve_to_tcp_server_all_domains:
|
|
157
153
|
print_api("All domains will be routed by the DNS server to Built-in TCP Server.", logger=system_logger)
|
|
158
154
|
|
|
159
|
-
if
|
|
155
|
+
if config_static.DNSServer.resolve_regular:
|
|
160
156
|
print_api(
|
|
161
157
|
"Regular DNS resolving is enabled. Built-in TCP server will not be routed to",
|
|
162
158
|
logger=system_logger, color="yellow")
|
|
163
159
|
else:
|
|
164
160
|
print_api("DNS Server is disabled.", logger=system_logger, color="yellow")
|
|
165
161
|
|
|
166
|
-
if
|
|
162
|
+
if config_static.TCPServer.enable:
|
|
167
163
|
print_api("TCP Server is enabled.", logger=system_logger)
|
|
168
164
|
|
|
169
|
-
if engines_list and not
|
|
165
|
+
if engines_list and not config_static.TCPServer.engines_usage:
|
|
170
166
|
message = \
|
|
171
167
|
f"Engines found, but the TCP server is set not to use them for processing. General responses only."
|
|
172
168
|
print_api(message, color="yellow", logger=system_logger)
|
|
173
|
-
elif engines_list and
|
|
169
|
+
elif engines_list and config_static.TCPServer.engines_usage:
|
|
174
170
|
message = f"Engines found, and the TCP server is set to use them for processing."
|
|
175
171
|
print_api(message, logger=system_logger)
|
|
176
|
-
elif not engines_list and
|
|
172
|
+
elif not engines_list and config_static.TCPServer.engines_usage:
|
|
177
173
|
raise ValueError("No engines were found, but the TCP server is set to use them for processing.\n"
|
|
178
174
|
"Please check your TCP configuration in the 'config.ini' file.")
|
|
179
175
|
else:
|
|
@@ -182,22 +178,12 @@ def initialize_mitm_server(config_static):
|
|
|
182
178
|
# === EOF Engine Logging =======================================================================================
|
|
183
179
|
|
|
184
180
|
# Assigning all the engines domains to all time domains, that will be responsible for adding new domains.
|
|
185
|
-
config_static.
|
|
186
|
-
|
|
187
|
-
# Creating Statistics logger.
|
|
188
|
-
statistics_logger = loggingw.create_logger(
|
|
189
|
-
logger_name="statistics",
|
|
190
|
-
directory_path=config['log']['logs_path'],
|
|
191
|
-
add_timedfile=True,
|
|
192
|
-
formatter_filehandler='MESSAGE',
|
|
193
|
-
file_type='csv',
|
|
194
|
-
header=STATISTICS_HEADER
|
|
195
|
-
)
|
|
181
|
+
config_static.Certificates.domains_all_times = list(domains_engine_list_full)
|
|
196
182
|
|
|
197
183
|
network_logger_name = "network"
|
|
198
184
|
network_logger = loggingw.create_logger(
|
|
199
185
|
logger_name=network_logger_name,
|
|
200
|
-
directory_path=
|
|
186
|
+
directory_path=config_static.Log.logs_path,
|
|
201
187
|
add_stream=True,
|
|
202
188
|
add_timedfile=True,
|
|
203
189
|
formatter_streamhandler='DEFAULT',
|
|
@@ -209,60 +195,128 @@ def initialize_mitm_server(config_static):
|
|
|
209
195
|
listener_logger = loggingw.get_logger_with_level(f'{network_logger_name}.listener')
|
|
210
196
|
system_logger.info(f"Loaded listener logger: {listener_logger}")
|
|
211
197
|
|
|
198
|
+
print_api("Press [Ctrl]+[C] to stop.", color='blue')
|
|
199
|
+
|
|
212
200
|
# Create request domain queue.
|
|
213
201
|
domain_queue = queues.NonBlockQueue()
|
|
214
202
|
|
|
215
203
|
# === Initialize DNS module ====================================================================================
|
|
216
|
-
if
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
204
|
+
if config_static.DNSServer.enable:
|
|
205
|
+
try:
|
|
206
|
+
dns_server_instance = dns_server.DnsServer(
|
|
207
|
+
listening_interface=config_static.DNSServer.listening_interface,
|
|
208
|
+
listening_port=config_static.DNSServer.listening_port,
|
|
209
|
+
forwarding_dns_service_ipv4=config_static.DNSServer.forwarding_dns_service_ipv4,
|
|
210
|
+
tcp_target_server_ipv4=config_static.DNSServer.target_tcp_server_ipv4,
|
|
211
|
+
# Passing the engine domain list to DNS server to work with.
|
|
212
|
+
# 'list' function re-initializes the current list, or else it will be the same instance object.
|
|
213
|
+
tcp_resolve_domain_list=list(config_static.Certificates.domains_all_times),
|
|
214
|
+
log_directory_path=config_static.Log.logs_path,
|
|
215
|
+
offline_mode=config_static.DNSServer.offline_mode,
|
|
216
|
+
resolve_to_tcp_server_only_tcp_resolve_domains=(
|
|
217
|
+
config_static.DNSServer.resolve_to_tcp_server_only_engine_domains),
|
|
218
|
+
resolve_to_tcp_server_all_domains=config_static.DNSServer.resolve_to_tcp_server_all_domains,
|
|
219
|
+
resolve_regular=config_static.DNSServer.resolve_regular,
|
|
220
|
+
cache_timeout_minutes=config_static.DNSServer.cache_timeout_minutes,
|
|
221
|
+
request_domain_queue=domain_queue
|
|
222
|
+
)
|
|
223
|
+
except (dns_server.DnsPortInUseError, dns_server.DnsConfigurationValuesError) as e:
|
|
224
|
+
print_api(e, error_type=True, color="red", logger=system_logger)
|
|
224
225
|
# Wait for the message to be printed and saved to file.
|
|
225
226
|
time.sleep(1)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
# before executing TCP sockets and after executing 'network' logger.
|
|
229
|
-
dns_server_instance = dns_server.DnsServer(config)
|
|
230
|
-
# Passing the engine domain list to DNS server to work with.
|
|
231
|
-
# 'list' function re-initializes the current list, or else it will be the same instance object.
|
|
232
|
-
dns_server_instance.domain_list = list(domains_engine_list_full)
|
|
227
|
+
return 1
|
|
233
228
|
|
|
234
|
-
dns_server_instance.request_domain_queue = domain_queue
|
|
235
|
-
# Initiate the thread.
|
|
236
229
|
dns_thread = threading.Thread(target=dns_server_instance.start)
|
|
237
230
|
dns_thread.daemon = True
|
|
238
231
|
dns_thread.start()
|
|
239
232
|
|
|
240
233
|
# === EOF Initialize DNS module ================================================================================
|
|
241
234
|
# === Initialize TCP Server ====================================================================================
|
|
242
|
-
if
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
235
|
+
if config_static.TCPServer.enable:
|
|
236
|
+
try:
|
|
237
|
+
socket_wrapper_instance = socket_wrapper.SocketWrapper(
|
|
238
|
+
listening_interface=config_static.TCPServer.listening_interface,
|
|
239
|
+
listening_port_list=config_static.TCPServer.listening_port_list,
|
|
240
|
+
ca_certificate_name=config_static.MainConfig.ca_certificate_name,
|
|
241
|
+
ca_certificate_filepath=config_static.MainConfig.ca_certificate_filepath,
|
|
242
|
+
default_server_certificate_usage=config_static.Certificates.default_server_certificate_usage,
|
|
243
|
+
default_server_certificate_name=config_static.MainConfig.default_server_certificate_name,
|
|
244
|
+
default_certificate_domain_list=config_static.Certificates.domains_all_times,
|
|
245
|
+
default_server_certificate_directory=config_static.MainConfig.SCRIPT_DIRECTORY,
|
|
246
|
+
sni_use_default_callback_function=True,
|
|
247
|
+
sni_use_default_callback_function_extended=True,
|
|
248
|
+
sni_add_new_domains_to_default_server_certificate=(
|
|
249
|
+
config_static.Certificates.sni_add_new_domains_to_default_server_certificate),
|
|
250
|
+
sni_create_server_certificate_for_each_domain=(
|
|
251
|
+
config_static.Certificates.sni_create_server_certificate_for_each_domain),
|
|
252
|
+
sni_server_certificates_cache_directory=(
|
|
253
|
+
config_static.Certificates.sni_server_certificates_cache_directory),
|
|
254
|
+
sni_get_server_certificate_from_server_socket=(
|
|
255
|
+
config_static.Certificates.sni_get_server_certificate_from_server_socket),
|
|
256
|
+
sni_server_certificate_from_server_socket_download_directory=(
|
|
257
|
+
config_static.Certificates.sni_server_certificate_from_server_socket_download_directory),
|
|
258
|
+
custom_server_certificate_usage=config_static.Certificates.custom_server_certificate_usage,
|
|
259
|
+
custom_server_certificate_path=config_static.Certificates.custom_server_certificate_path,
|
|
260
|
+
custom_private_key_path=config_static.Certificates.custom_private_key_path,
|
|
261
|
+
get_process_name=config_static.ProcessName.get_process_name,
|
|
262
|
+
ssh_user=config_static.ProcessName.ssh_user,
|
|
263
|
+
ssh_pass=config_static.ProcessName.ssh_pass,
|
|
264
|
+
ssh_script_to_execute=config_static.ProcessName.ssh_script_to_execute,
|
|
265
|
+
logger=listener_logger,
|
|
266
|
+
statistics_logs_directory=config_static.Log.logs_path,
|
|
267
|
+
forwarding_dns_service_ipv4_list___only_for_localhost=(
|
|
268
|
+
config_static.TCPServer.forwarding_dns_service_ipv4_list___only_for_localhost),
|
|
269
|
+
skip_extension_id_list=config_static.SkipExtensions.SKIP_EXTENSION_ID_LIST
|
|
270
|
+
)
|
|
271
|
+
except socket_wrapper.SocketWrapperPortInUseError as e:
|
|
272
|
+
print_api(e, error_type=True, color="red", logger=system_logger)
|
|
248
273
|
# Wait for the message to be printed and saved to file.
|
|
249
274
|
time.sleep(1)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
275
|
+
return 1
|
|
276
|
+
|
|
277
|
+
statistics_writer = socket_wrapper_instance.statistics_writer
|
|
278
|
+
|
|
279
|
+
socket_wrapper_instance.create_tcp_listening_socket_list()
|
|
280
|
+
|
|
281
|
+
socket_wrapper_instance.requested_domain_from_dns_server = domain_queue
|
|
282
|
+
|
|
283
|
+
# Before we start the loop. we can set the default gateway if specified.
|
|
284
|
+
set_dns_gateway = False
|
|
285
|
+
dns_gateway_server_list = list()
|
|
286
|
+
if config_static.DNSServer.set_default_dns_gateway:
|
|
287
|
+
dns_gateway_server_list = config_static.DNSServer.set_default_dns_gateway
|
|
288
|
+
set_dns_gateway = True
|
|
289
|
+
elif config_static.DNSServer.set_default_dns_gateway_to_localhost:
|
|
290
|
+
dns_gateway_server_list = [base.LOCALHOST_IPV4]
|
|
291
|
+
set_dns_gateway = True
|
|
292
|
+
elif config_static.DNSServer.set_default_dns_gateway_to_default_interface_ipv4:
|
|
293
|
+
dns_gateway_server_list = [base.DEFAULT_IPV4]
|
|
294
|
+
set_dns_gateway = True
|
|
295
|
+
|
|
296
|
+
if set_dns_gateway:
|
|
297
|
+
# noinspection PyTypeChecker
|
|
298
|
+
dns.set_connection_dns_gateway_static(
|
|
299
|
+
dns_servers=dns_gateway_server_list,
|
|
300
|
+
use_default_connection=True
|
|
301
|
+
)
|
|
259
302
|
|
|
260
303
|
# General exception handler will catch all the exceptions that occurred in the threads and write it to the log.
|
|
304
|
+
# noinspection PyBroadException
|
|
261
305
|
try:
|
|
262
|
-
|
|
263
|
-
|
|
306
|
+
socket_thread = threading.Thread(
|
|
307
|
+
target=socket_wrapper_instance.loop_for_incoming_sockets,
|
|
308
|
+
kwargs={
|
|
309
|
+
'reference_function_name': thread_worker_main,
|
|
310
|
+
'reference_function_args': (network_logger, statistics_writer, engines_list, reference_module,)
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
socket_thread.daemon = True
|
|
315
|
+
socket_thread.start()
|
|
264
316
|
except Exception:
|
|
265
317
|
message = f"Unhandled Exception occurred in 'loop_for_incoming_sockets' function"
|
|
266
318
|
print_api(message, error_type=True, color="red", logger=network_logger, traceback_string=True, oneline=True)
|
|
267
319
|
|
|
268
|
-
|
|
320
|
+
# This is needed for Keyboard Exception.
|
|
321
|
+
while True:
|
|
322
|
+
time.sleep(1)
|
|
@@ -346,6 +346,7 @@ def analyze(main_file_path: str):
|
|
|
346
346
|
|
|
347
347
|
def deviation_calculator_by_moving_average_main(
|
|
348
348
|
statistics_file_directory: str,
|
|
349
|
+
by_type: Literal['host', 'url'],
|
|
349
350
|
moving_average_window_days: int,
|
|
350
351
|
top_bottom_deviation_percentage: float,
|
|
351
352
|
get_deviation_for_last_day_only: bool = False,
|
|
@@ -359,6 +360,9 @@ def deviation_calculator_by_moving_average_main(
|
|
|
359
360
|
:param statistics_file_directory: string, the directory where 'statistics.csv' file resides.
|
|
360
361
|
Also, all the rotated files like: statistics_2021-01-01.csv, statistics_2021-01-02.csv, etc.
|
|
361
362
|
These will be analyzed in the order of the date in the file name.
|
|
363
|
+
:param by_type: string, 'host' or 'url'. The type of the deviation calculation.
|
|
364
|
+
'host' will calculate the deviation by the host name. Example: maps.google.com, yahoo.com, etc.
|
|
365
|
+
'url' will calculate the deviation by the URL. Example: maps.google.com/maps, yahoo.com/news, etc.
|
|
362
366
|
:param moving_average_window_days: integer, the moving average window days.
|
|
363
367
|
:param top_bottom_deviation_percentage: float, the top bottom deviation percentage. Example: 0.1 for 10%.
|
|
364
368
|
:param get_deviation_for_last_day_only: bool, if True, only the last day will be analyzed.
|
|
@@ -402,6 +406,9 @@ def deviation_calculator_by_moving_average_main(
|
|
|
402
406
|
if output_file_type not in ['json', 'csv']:
|
|
403
407
|
raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
|
|
404
408
|
|
|
409
|
+
if by_type not in ['host', 'url']:
|
|
410
|
+
raise ValueError(f'by_type must be "host" or "url", not [{by_type}]')
|
|
411
|
+
|
|
405
412
|
statistics_file_path: str = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
|
|
406
413
|
|
|
407
414
|
def convert_data_value_to_string(value_key: str, list_index: int) -> None:
|
|
@@ -413,6 +420,7 @@ def deviation_calculator_by_moving_average_main(
|
|
|
413
420
|
|
|
414
421
|
deviation_list = moving_average_helper.calculate_moving_average(
|
|
415
422
|
statistics_file_path,
|
|
423
|
+
by_type,
|
|
416
424
|
moving_average_window_days,
|
|
417
425
|
top_bottom_deviation_percentage,
|
|
418
426
|
get_deviation_for_last_day_only
|
|
@@ -436,7 +444,11 @@ def deviation_calculator_by_moving_average_main(
|
|
|
436
444
|
'value': deviation.get('value', None),
|
|
437
445
|
'ma_value': deviation.get('ma_value', None),
|
|
438
446
|
'deviation_percentage': deviation.get('deviation_percentage', None),
|
|
439
|
-
'total_entries_averaged': total_entries_averaged
|
|
447
|
+
'total_entries_averaged': total_entries_averaged,
|
|
448
|
+
'median_request_size': deviation.get('median_request_size', None),
|
|
449
|
+
'median_response_size': deviation.get('median_response_size', None),
|
|
450
|
+
'mm_request_size': deviation.get('mm_request_size', None),
|
|
451
|
+
'mm_response_size': deviation.get('mm_response_size', None),
|
|
440
452
|
})
|
|
441
453
|
|
|
442
454
|
deviation_list = summary_deviation_list
|
|
@@ -4,10 +4,12 @@ from typing import Literal
|
|
|
4
4
|
from ...print_api import print_api
|
|
5
5
|
from ...wrappers.loggingw import reading, consts
|
|
6
6
|
from ...file_io import csvs
|
|
7
|
+
from ... import urls
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def calculate_moving_average(
|
|
10
11
|
file_path: str,
|
|
12
|
+
by_type: Literal['host', 'url'],
|
|
11
13
|
moving_average_window_days,
|
|
12
14
|
top_bottom_deviation_percentage: float,
|
|
13
15
|
get_deviation_for_last_day_only: bool = False,
|
|
@@ -17,6 +19,7 @@ def calculate_moving_average(
|
|
|
17
19
|
This function calculates the moving average of the daily statistics.
|
|
18
20
|
|
|
19
21
|
:param file_path: string, the path to the 'statistics.csv' file.
|
|
22
|
+
:param by_type: string, the type to calculate the moving average by. Can be 'host' or 'url'.
|
|
20
23
|
:param moving_average_window_days: integer, the window size for the moving average.
|
|
21
24
|
:param top_bottom_deviation_percentage: float, the percentage of deviation from the moving average to the top or
|
|
22
25
|
bottom.
|
|
@@ -65,8 +68,7 @@ def calculate_moving_average(
|
|
|
65
68
|
|
|
66
69
|
# Get the data dictionary from the statistics content.
|
|
67
70
|
statistics_content[date_string]['statistics_daily'] = compute_statistics_from_content(
|
|
68
|
-
statistics_content[date_string]['content_no_errors']
|
|
69
|
-
)
|
|
71
|
+
statistics_content[date_string]['content_no_errors'], by_type)
|
|
70
72
|
|
|
71
73
|
moving_average_dict: dict = compute_moving_averages_from_average_statistics(
|
|
72
74
|
statistics_content,
|
|
@@ -106,19 +108,36 @@ def get_content_without_errors(content: list) -> list:
|
|
|
106
108
|
return traffic_statistics_without_errors
|
|
107
109
|
|
|
108
110
|
|
|
109
|
-
def get_data_dict_from_statistics_content(
|
|
111
|
+
def get_data_dict_from_statistics_content(
|
|
112
|
+
content: list,
|
|
113
|
+
by_type: Literal['host', 'url']
|
|
114
|
+
) -> dict:
|
|
110
115
|
"""
|
|
111
116
|
This function gets the data dictionary from the 'statistics.csv' file content.
|
|
112
117
|
|
|
113
118
|
:param content: list, the content list.
|
|
119
|
+
:param by_type: string, the type to calculate the moving average by. Can be 'host' or 'url'.
|
|
114
120
|
:return: dict, the data dictionary.
|
|
115
121
|
"""
|
|
116
122
|
|
|
117
123
|
hosts_requests_responses: dict = {}
|
|
118
124
|
for line in content:
|
|
125
|
+
if by_type == 'host':
|
|
126
|
+
type_to_check: str = line['host']
|
|
127
|
+
elif by_type == 'url':
|
|
128
|
+
# Combine host and path to URL.
|
|
129
|
+
type_to_check: str = line['host'] + line['path']
|
|
130
|
+
# Remove the parameters from the URL.
|
|
131
|
+
url_parsed = urls.url_parser(type_to_check)
|
|
132
|
+
type_to_check = url_parsed['path']
|
|
133
|
+
# Remove the last slash from the URL.
|
|
134
|
+
type_to_check = type_to_check.removesuffix('/')
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(f'Invalid by_type: {by_type}')
|
|
137
|
+
|
|
119
138
|
# If subdomain is not in the dictionary, add it.
|
|
120
|
-
if
|
|
121
|
-
hosts_requests_responses[
|
|
139
|
+
if type_to_check not in hosts_requests_responses:
|
|
140
|
+
hosts_requests_responses[type_to_check] = {
|
|
122
141
|
'request_sizes': [],
|
|
123
142
|
'response_sizes': []
|
|
124
143
|
}
|
|
@@ -132,8 +151,8 @@ def get_data_dict_from_statistics_content(content: list) -> dict:
|
|
|
132
151
|
if response_size_bytes == '':
|
|
133
152
|
response_size_bytes = '0'
|
|
134
153
|
|
|
135
|
-
hosts_requests_responses[
|
|
136
|
-
hosts_requests_responses[
|
|
154
|
+
hosts_requests_responses[type_to_check]['request_sizes'].append(int(request_size_bytes))
|
|
155
|
+
hosts_requests_responses[type_to_check]['response_sizes'].append(int(response_size_bytes))
|
|
137
156
|
except ValueError:
|
|
138
157
|
print_api(line, color='yellow')
|
|
139
158
|
raise
|
|
@@ -163,18 +182,22 @@ def compute_statistics_from_data_dict(data_dict: dict):
|
|
|
163
182
|
data_dict[host]['median_response_size'] = median_response_size
|
|
164
183
|
|
|
165
184
|
|
|
166
|
-
def compute_statistics_from_content(
|
|
185
|
+
def compute_statistics_from_content(
|
|
186
|
+
content: list,
|
|
187
|
+
by_type: Literal['host', 'url']
|
|
188
|
+
):
|
|
167
189
|
"""
|
|
168
190
|
This function computes the statistics from the 'statistics.csv' file content.
|
|
169
191
|
|
|
170
192
|
:param content: list, the content list.
|
|
193
|
+
:param by_type: string, the type to calculate the moving average by. Can be 'host' or 'url'.
|
|
171
194
|
:return: dict, the statistics dictionary.
|
|
172
195
|
"""
|
|
173
196
|
|
|
174
|
-
|
|
175
|
-
compute_statistics_from_data_dict(
|
|
197
|
+
requests_responses: dict = get_data_dict_from_statistics_content(content, by_type)
|
|
198
|
+
compute_statistics_from_data_dict(requests_responses)
|
|
176
199
|
|
|
177
|
-
return
|
|
200
|
+
return requests_responses
|
|
178
201
|
|
|
179
202
|
|
|
180
203
|
def compute_moving_averages_from_average_statistics(
|
|
@@ -200,12 +223,15 @@ def compute_moving_averages_from_average_statistics(
|
|
|
200
223
|
list(average_statistics_dict.values()))[current_day-moving_average_window_days:current_day]
|
|
201
224
|
|
|
202
225
|
# Compute the moving averages.
|
|
203
|
-
moving_average[day] = compute_average_for_current_day_from_past_x_days(
|
|
226
|
+
moving_average[day] = compute_average_for_current_day_from_past_x_days(
|
|
227
|
+
last_x_window_days_content_list)
|
|
204
228
|
|
|
205
229
|
return moving_average
|
|
206
230
|
|
|
207
231
|
|
|
208
|
-
def compute_average_for_current_day_from_past_x_days(
|
|
232
|
+
def compute_average_for_current_day_from_past_x_days(
|
|
233
|
+
previous_days_content_list: list
|
|
234
|
+
) -> dict:
|
|
209
235
|
"""
|
|
210
236
|
This function computes the average for the current day from the past x days.
|
|
211
237
|
|
|
@@ -222,11 +248,15 @@ def compute_average_for_current_day_from_past_x_days(previous_days_content_list:
|
|
|
222
248
|
'counts': [],
|
|
223
249
|
'avg_request_sizes': [],
|
|
224
250
|
'avg_response_sizes': [],
|
|
251
|
+
'median_request_sizes': [],
|
|
252
|
+
'median_response_sizes': []
|
|
225
253
|
}
|
|
226
254
|
|
|
227
255
|
moving_average[host]['counts'].append(int(host_dict['count']))
|
|
228
256
|
moving_average[host]['avg_request_sizes'].append(float(host_dict['avg_request_size']))
|
|
229
257
|
moving_average[host]['avg_response_sizes'].append(float(host_dict['avg_response_size']))
|
|
258
|
+
moving_average[host]['median_request_sizes'].append(float(host_dict['median_request_size']))
|
|
259
|
+
moving_average[host]['median_response_sizes'].append(float(host_dict['median_response_size']))
|
|
230
260
|
|
|
231
261
|
# Compute the moving average.
|
|
232
262
|
moving_average_results: dict = {}
|
|
@@ -234,14 +264,20 @@ def compute_average_for_current_day_from_past_x_days(previous_days_content_list:
|
|
|
234
264
|
ma_count = statistics.mean(host_dict['counts'])
|
|
235
265
|
ma_request_size = statistics.mean(host_dict['avg_request_sizes'])
|
|
236
266
|
ma_response_size = statistics.mean(host_dict['avg_response_sizes'])
|
|
267
|
+
mm_request_size = statistics.median(host_dict['median_request_sizes'])
|
|
268
|
+
mm_response_size = statistics.median(host_dict['median_response_sizes'])
|
|
237
269
|
|
|
238
270
|
moving_average_results[host] = {
|
|
239
271
|
'ma_count': ma_count,
|
|
240
272
|
'ma_request_size': ma_request_size,
|
|
241
273
|
'ma_response_size': ma_response_size,
|
|
274
|
+
'mm_request_size': mm_request_size,
|
|
275
|
+
'mm_response_size': mm_response_size,
|
|
242
276
|
'counts': host_dict['counts'],
|
|
243
277
|
'avg_request_sizes': host_dict['avg_request_sizes'],
|
|
244
|
-
'avg_response_sizes': host_dict['avg_response_sizes']
|
|
278
|
+
'avg_response_sizes': host_dict['avg_response_sizes'],
|
|
279
|
+
'median_request_sizes': host_dict['median_request_sizes'],
|
|
280
|
+
'median_response_sizes': host_dict['median_response_sizes']
|
|
245
281
|
}
|
|
246
282
|
|
|
247
283
|
return moving_average_results
|
|
@@ -303,6 +339,10 @@ def find_deviation_from_moving_average(
|
|
|
303
339
|
'ma_value_checked': check_type_moving_above,
|
|
304
340
|
'deviation_percentage': deviation_percentage,
|
|
305
341
|
'deviation_type': deviation_type,
|
|
342
|
+
'median_request_size': day_statistics_content_dict['median_request_size'],
|
|
343
|
+
'median_response_size': day_statistics_content_dict['median_response_size'],
|
|
344
|
+
'mm_request_size': moving_averages_dict[host]['mm_request_size'],
|
|
345
|
+
'mm_response_size': moving_averages_dict[host]['mm_response_size'],
|
|
306
346
|
'data': day_statistics_content_dict,
|
|
307
347
|
'ma_data': moving_averages_dict[host]
|
|
308
348
|
})
|