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

Files changed (66) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/a_installs/ubuntu/pycharm.py +7 -0
  3. atomicshop/a_installs/win/pycharm.py +2 -2
  4. atomicshop/{addons/mains/install_wsl_ubuntu_lts_admin.py → a_installs/win/wsl_ubuntu_lts.py} +1 -0
  5. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  6. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  7. atomicshop/basics/booleans.py +14 -5
  8. atomicshop/dns.py +104 -0
  9. atomicshop/file_io/docxs.py +8 -0
  10. atomicshop/file_io/tomls.py +133 -0
  11. atomicshop/filesystem.py +5 -4
  12. atomicshop/get_process_list.py +3 -3
  13. atomicshop/mitm/config_static.py +195 -0
  14. atomicshop/mitm/config_toml_editor.py +55 -0
  15. atomicshop/mitm/connection_thread_worker.py +54 -90
  16. atomicshop/mitm/import_config.py +148 -139
  17. atomicshop/mitm/initialize_engines.py +7 -2
  18. atomicshop/mitm/initialize_mitm_server.py +162 -107
  19. atomicshop/mitm/shared_functions.py +0 -1
  20. atomicshop/mitm/statistic_analyzer.py +13 -1
  21. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +54 -14
  22. atomicshop/permissions/__init__.py +0 -0
  23. atomicshop/permissions/permissions.py +22 -0
  24. atomicshop/{permissions.py → permissions/ubuntu_permissions.py} +4 -54
  25. atomicshop/permissions/win_permissions.py +33 -0
  26. atomicshop/script_as_string_processor.py +5 -1
  27. atomicshop/wrappers/cryptographyw.py +3 -3
  28. atomicshop/wrappers/dockerw/install_docker.py +6 -5
  29. atomicshop/wrappers/elasticsearchw/install_elastic.py +2 -1
  30. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -4
  31. atomicshop/wrappers/mongodbw/install_mongodb.py +2 -1
  32. atomicshop/wrappers/msiw.py +2 -3
  33. atomicshop/wrappers/psutilw/networks.py +25 -1
  34. atomicshop/wrappers/pycharmw/__init__.py +0 -0
  35. atomicshop/wrappers/pycharmw/ubuntu.py +38 -0
  36. atomicshop/wrappers/{pycharmw.py → pycharmw/win.py} +2 -2
  37. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  38. atomicshop/wrappers/pywin32w/wmis/helpers.py +127 -0
  39. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +167 -0
  40. atomicshop/wrappers/socketw/accepter.py +8 -8
  41. atomicshop/wrappers/socketw/base.py +13 -0
  42. atomicshop/wrappers/socketw/certificator.py +202 -149
  43. atomicshop/wrappers/socketw/creator.py +15 -35
  44. atomicshop/wrappers/socketw/dns_server.py +155 -102
  45. atomicshop/wrappers/socketw/exception_wrapper.py +8 -27
  46. atomicshop/wrappers/socketw/get_process.py +115 -95
  47. atomicshop/wrappers/socketw/sni.py +298 -164
  48. atomicshop/wrappers/socketw/socket_client.py +5 -12
  49. atomicshop/wrappers/socketw/socket_server_tester.py +1 -1
  50. atomicshop/wrappers/socketw/socket_wrapper.py +328 -72
  51. atomicshop/wrappers/socketw/statistics_csv.py +94 -16
  52. atomicshop/wrappers/ubuntu_terminal.py +6 -6
  53. atomicshop/wrappers/wslw.py +1 -0
  54. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/METADATA +1 -1
  55. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/RECORD +63 -54
  56. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  57. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  58. atomicshop/mitm/config_editor.py +0 -37
  59. /atomicshop/{addons/mains/install_docker_rootless_ubuntu.py → a_installs/ubuntu/docker_rootless.py} +0 -0
  60. /atomicshop/{addons/mains/install_docker_ubuntu_main_sudo.py → a_installs/ubuntu/docker_sudo.py} +0 -0
  61. /atomicshop/{addons/mains/install_elastic_search_and_kibana_ubuntu.py → a_installs/ubuntu/elastic_search_and_kibana.py} +0 -0
  62. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  63. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  64. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/LICENSE.txt +0 -0
  65. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/WHEEL +0 -0
  66. {atomicshop-2.15.13.dist-info → atomicshop-2.16.1.dist-info}/top_level.txt +0 -0
@@ -1,53 +1,57 @@
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 .import_config import ImportConfig
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
8
+ from ..permissions import permissions
13
9
  from ..python_functions import get_current_python_version_string, check_python_version_compliance
14
- from ..wrappers.socketw.socket_wrapper import SocketWrapper
15
- from ..wrappers.socketw import dns_server
16
- from ..wrappers.psutilw import networks
17
- from ..basics import dicts_nested
10
+ from ..wrappers.socketw import socket_wrapper, dns_server, base
18
11
  from ..wrappers.loggingw import loggingw
19
12
  from ..print_api import print_api
20
13
 
14
+ from .initialize_engines import ModuleCategory
15
+ from .connection_thread_worker import thread_worker_main
16
+ from . import config_static
17
+
18
+
19
+ def exit_cleanup():
20
+ if config_static.DNSServer.set_default_dns_gateway:
21
+ is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
22
+ print_api(f'Current DNS Gateway: {current_dns_gateway}')
21
23
 
22
- STATISTICS_HEADER: str = \
23
- 'request_time_sent,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,process_cmd,error'
24
+ if current_dns_gateway == config_static.DNSServer.set_default_dns_gateway and not is_dns_dynamic:
25
+ if permissions.is_admin():
26
+ dns.set_connection_dns_gateway_dynamic(use_default_connection=True)
27
+ print_api("Returned default DNS gateway...", color='blue')
24
28
 
25
29
 
26
- def initialize_mitm_server(config_static):
30
+ def initialize_mitm_server(config_file_path: str):
31
+ on_exit.register_exit_handler(exit_cleanup)
32
+
27
33
  # Main function should return integer with error code, 0 is successful.
28
34
  # Since listening server is infinite, this will not be reached.
29
35
  # After modules import - we check for python version.
30
36
  check_python_version_compliance(minimum_version='3.11')
31
37
 
32
- # Preparing everything for the logging module.
33
- # Log folder path is in the "config.ini" file, so we need to read it before setting loggers.
34
- config_importer = ImportConfig(
35
- file_name=config_static.CONFIG_INI_SERVER_FILE_NAME, directory_path=config_static.WORKING_DIRECTORY)
36
- config_importer.open()
37
- config = config_importer.config
38
+ # Import the configuration file.
39
+ result = config_static.load_config(config_file_path)
40
+ if result != 0:
41
+ return result
38
42
 
39
43
  # Create folders.
40
- filesystem.create_directory(config['log']['logs_path'])
41
- filesystem.create_directory(config['recorder']['recordings_path'])
42
- if config['certificates']['sni_get_server_certificate_from_server_socket']:
44
+ filesystem.create_directory(config_static.Log.logs_path)
45
+ filesystem.create_directory(config_static.Recorder.recordings_path)
46
+ if config_static.Certificates.sni_get_server_certificate_from_server_socket:
43
47
  filesystem.create_directory(
44
- config['certificates']['sni_server_certificate_from_server_socket_download_directory'])
48
+ config_static.Certificates.sni_server_certificate_from_server_socket_download_directory)
45
49
 
46
50
  # Create a logger that will log messages to file, Initiate System logger.
47
51
  logger_name = "system"
48
52
  system_logger = loggingw.create_logger(
49
53
  logger_name=logger_name,
50
- file_path=f"{config['log']['logs_path']}{os.sep}{logger_name}.txt",
54
+ file_path=f"{config_static.Log.logs_path}{os.sep}{logger_name}.txt",
51
55
  add_stream=True,
52
56
  add_timedfile=True,
53
57
  formatter_streamhandler='DEFAULT',
@@ -56,44 +60,37 @@ def initialize_mitm_server(config_static):
56
60
 
57
61
  # Writing first log.
58
62
  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
63
  system_logger.info("Server Started.")
65
64
  system_logger.info(f"Python Version: {get_current_python_version_string()}")
66
65
  system_logger.info(f"Script Version: {config_static.SCRIPT_VERSION}")
67
66
  system_logger.info(f"Atomic Workshop Version: {atomicshop.__version__}")
68
- system_logger.info(f"Loaded config.ini: {config_importer.config_parser.file_path}")
69
- system_logger.info(f"Log folder: {config['log']['logs_path']}")
70
- system_logger.info(f"Recordings folder for Requests/Responses: {config['recorder']['recordings_path']}")
67
+ system_logger.info(f"Log folder: {config_static.Log.logs_path}")
68
+ system_logger.info(f"Recordings folder for Requests/Responses: {config_static.Recorder.recordings_path}")
71
69
  system_logger.info(f"Loaded system logger: {system_logger}")
72
70
 
73
- system_logger.info(f"TCP Server Target IP: {config['dns']['target_tcp_server_ipv4']}")
71
+ system_logger.info(f"TCP Server Target IP: {config_static.DNSServer.target_tcp_server_ipv4}")
74
72
 
75
73
  # Some 'config.ini' settings logging ===========================================================================
76
- if config['certificates']['default_server_certificate_usage']:
74
+ if config_static.Certificates.default_server_certificate_usage:
77
75
  system_logger.info(
78
76
  f"Default server certificate usage enabled, if no SNI available: "
79
- f"{config_static.CONFIG_EXTENDED['certificates']['default_server_certificate_directory']}"
80
- f"{os.sep}{config_static.CONFIG_EXTENDED['certificates']['default_server_certificate_name']}.pem")
77
+ f"{config_static.MainConfig.default_server_certificate_filepath}")
81
78
 
82
- if config['certificates']['sni_server_certificates_cache_directory']:
79
+ if config_static.Certificates.sni_server_certificates_cache_directory:
83
80
  system_logger.info(
84
81
  f"SNI function certificates creation enabled. Certificates cache: "
85
- f"{config['certificates']['sni_server_certificates_cache_directory']}")
82
+ f"{config_static.Certificates.sni_server_certificates_cache_directory}")
86
83
  else:
87
84
  system_logger.info(f"SNI function certificates creation disabled.")
88
85
 
89
- if config['certificates']['custom_server_certificate_usage']:
86
+ if config_static.Certificates.custom_server_certificate_usage:
90
87
  system_logger.info(f"Custom server certificate usage is enabled.")
91
- system_logger.info(f"Custom Certificate Path: {config['certificates']['custom_server_certificate_path']}")
88
+ system_logger.info(f"Custom Certificate Path: {config_static.Certificates.custom_server_certificate_path}")
92
89
 
93
90
  # If 'custom_private_key_path' field was populated.
94
- if config['certificates']['custom_private_key_path']:
91
+ if config_static.Certificates.custom_private_key_path:
95
92
  system_logger.info(
96
- f"Custom Certificate Private Key Path: {config['certificates']['custom_private_key_path']}")
93
+ f"Custom Certificate Private Key Path: {config_static.Certificates.custom_private_key_path}")
97
94
  else:
98
95
  system_logger.info(f"Custom Certificate Private Key Path wasn't provided in [advanced] section. "
99
96
  f"Assuming the private key is inside the certificate file.")
@@ -103,17 +100,17 @@ def initialize_mitm_server(config_static):
103
100
 
104
101
  # Get full paths of all the 'engine_config.ini' files.
105
102
  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)
103
+ directory_path=config_static.MainConfig.ENGINES_DIRECTORY_PATH,
104
+ file_name_check_pattern=config_static.MainConfig.ENGINE_CONFIG_FILE_NAME)
108
105
 
109
106
  # Iterate through all the 'engine_config.ini' file paths.
110
107
  domains_engine_list_full: list = list()
111
108
  engines_list: list = list()
112
109
  for engine_config_path in engine_config_path_list:
113
110
  # Initialize engine.
114
- current_module = ModuleCategory(config_static.WORKING_DIRECTORY)
111
+ current_module = ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
115
112
  current_module.fill_engine_fields_from_config(engine_config_path)
116
- current_module.initialize_engine(logs_path=config['log']['logs_path'],
113
+ current_module.initialize_engine(logs_path=config_static.Log.logs_path,
117
114
  logger=system_logger)
118
115
 
119
116
  # Extending the full engine domain list with this list.
@@ -122,9 +119,9 @@ def initialize_mitm_server(config_static):
122
119
  engines_list.append(current_module)
123
120
  # === EOF Importing engine modules =============================================================================
124
121
  # ==== Initialize Reference Module =============================================================================
125
- reference_module = ModuleCategory(config_static.WORKING_DIRECTORY)
126
- reference_module.fill_engine_fields_from_general_reference(config_static.ENGINES_DIRECTORY_PATH)
127
- reference_module.initialize_engine(logs_path=config['log']['logs_path'],
122
+ reference_module = ModuleCategory(config_static.MainConfig.SCRIPT_DIRECTORY)
123
+ reference_module.fill_engine_fields_from_general_reference(config_static.MainConfig.ENGINES_DIRECTORY_PATH)
124
+ reference_module.initialize_engine(logs_path=config_static.Log.logs_path,
128
125
  logger=system_logger, stdout=False, reference_general=True)
129
126
  # === EOF Initialize Reference Module ==========================================================================
130
127
  # === Engine logging ===========================================================================================
@@ -139,41 +136,41 @@ def initialize_mitm_server(config_static):
139
136
  f"{engine.recorder_class_object.__name__}")
140
137
  print_api(message, logger=system_logger)
141
138
 
142
- if config['dns']['enable_dns_server']:
139
+ if config_static.DNSServer.enable:
143
140
  print_api("DNS Server is enabled.", logger=system_logger)
144
141
 
145
142
  # If engines were found and dns is set to route by the engine domains.
146
- if engines_list and config['dns']['route_to_tcp_server_only_engine_domains']:
143
+ if engines_list and config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
147
144
  print_api("Engine domains will be routed by the DNS server to Built-in TCP Server.", logger=system_logger)
148
145
  # If engines were found, but the dns isn't set to route to engines.
149
- elif engines_list and not config['dns']['route_to_tcp_server_only_engine_domains']:
146
+ elif engines_list and not config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
150
147
  message = f"[*] Engine domains found, but the DNS routing is set not to use them for routing."
151
148
  print_api(message, color="yellow", logger=system_logger)
152
- elif not engines_list and config['dns']['route_to_tcp_server_only_engine_domains']:
149
+ elif not engines_list and config_static.DNSServer.resolve_to_tcp_server_only_engine_domains:
153
150
  raise ValueError("No engines were found, but the DNS routing is set to use them for routing.\n"
154
151
  "Please check your DNS configuration in the 'config.ini' file.")
155
152
 
156
- if config['dns']['route_to_tcp_server_all_domains']:
153
+ if config_static.DNSServer.resolve_to_tcp_server_all_domains:
157
154
  print_api("All domains will be routed by the DNS server to Built-in TCP Server.", logger=system_logger)
158
155
 
159
- if config['dns']['regular_resolving']:
156
+ if config_static.DNSServer.resolve_regular:
160
157
  print_api(
161
158
  "Regular DNS resolving is enabled. Built-in TCP server will not be routed to",
162
159
  logger=system_logger, color="yellow")
163
160
  else:
164
161
  print_api("DNS Server is disabled.", logger=system_logger, color="yellow")
165
162
 
166
- if config['tcp']['enable_tcp_server']:
163
+ if config_static.TCPServer.enable:
167
164
  print_api("TCP Server is enabled.", logger=system_logger)
168
165
 
169
- if engines_list and not config['tcp']['engines_usage']:
166
+ if engines_list and not config_static.TCPServer.engines_usage:
170
167
  message = \
171
168
  f"Engines found, but the TCP server is set not to use them for processing. General responses only."
172
169
  print_api(message, color="yellow", logger=system_logger)
173
- elif engines_list and config['tcp']['engines_usage']:
170
+ elif engines_list and config_static.TCPServer.engines_usage:
174
171
  message = f"Engines found, and the TCP server is set to use them for processing."
175
172
  print_api(message, logger=system_logger)
176
- elif not engines_list and config['tcp']['engines_usage']:
173
+ elif not engines_list and config_static.TCPServer.engines_usage:
177
174
  raise ValueError("No engines were found, but the TCP server is set to use them for processing.\n"
178
175
  "Please check your TCP configuration in the 'config.ini' file.")
179
176
  else:
@@ -182,22 +179,12 @@ def initialize_mitm_server(config_static):
182
179
  # === EOF Engine Logging =======================================================================================
183
180
 
184
181
  # Assigning all the engines domains to all time domains, that will be responsible for adding new domains.
185
- config_static.CONFIG_EXTENDED['certificates']['domains_all_times'] = list(domains_engine_list_full)
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
- )
182
+ config_static.Certificates.domains_all_times = list(domains_engine_list_full)
196
183
 
197
184
  network_logger_name = "network"
198
185
  network_logger = loggingw.create_logger(
199
186
  logger_name=network_logger_name,
200
- directory_path=config['log']['logs_path'],
187
+ directory_path=config_static.Log.logs_path,
201
188
  add_stream=True,
202
189
  add_timedfile=True,
203
190
  formatter_streamhandler='DEFAULT',
@@ -209,60 +196,128 @@ def initialize_mitm_server(config_static):
209
196
  listener_logger = loggingw.get_logger_with_level(f'{network_logger_name}.listener')
210
197
  system_logger.info(f"Loaded listener logger: {listener_logger}")
211
198
 
199
+ print_api("Press [Ctrl]+[C] to stop.", color='blue')
200
+
212
201
  # Create request domain queue.
213
202
  domain_queue = queues.NonBlockQueue()
214
203
 
215
204
  # === Initialize DNS module ====================================================================================
216
- if config['dns']['enable_dns_server']:
217
- # Check if the DNS server port is in use.
218
- port_in_use = networks.get_processes_using_port_list([config['dns']['listening_port']])
219
- if port_in_use:
220
- for port, process_info in port_in_use.items():
221
- message = f"Port [{port}] is already in use by process: {process_info}"
222
- print_api(message, error_type=True, logger_method='critical', logger=system_logger)
223
-
205
+ if config_static.DNSServer.enable:
206
+ try:
207
+ dns_server_instance = dns_server.DnsServer(
208
+ listening_interface=config_static.DNSServer.listening_interface,
209
+ listening_port=config_static.DNSServer.listening_port,
210
+ forwarding_dns_service_ipv4=config_static.DNSServer.forwarding_dns_service_ipv4,
211
+ tcp_target_server_ipv4=config_static.DNSServer.target_tcp_server_ipv4,
212
+ # Passing the engine domain list to DNS server to work with.
213
+ # 'list' function re-initializes the current list, or else it will be the same instance object.
214
+ tcp_resolve_domain_list=list(config_static.Certificates.domains_all_times),
215
+ log_directory_path=config_static.Log.logs_path,
216
+ offline_mode=config_static.DNSServer.offline_mode,
217
+ resolve_to_tcp_server_only_tcp_resolve_domains=(
218
+ config_static.DNSServer.resolve_to_tcp_server_only_engine_domains),
219
+ resolve_to_tcp_server_all_domains=config_static.DNSServer.resolve_to_tcp_server_all_domains,
220
+ resolve_regular=config_static.DNSServer.resolve_regular,
221
+ cache_timeout_minutes=config_static.DNSServer.cache_timeout_minutes,
222
+ request_domain_queue=domain_queue
223
+ )
224
+ except (dns_server.DnsPortInUseError, dns_server.DnsConfigurationValuesError) as e:
225
+ print_api(e, error_type=True, color="red", logger=system_logger)
224
226
  # Wait for the message to be printed and saved to file.
225
227
  time.sleep(1)
226
- sys.exit(1)
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)
228
+ return 1
233
229
 
234
- dns_server_instance.request_domain_queue = domain_queue
235
- # Initiate the thread.
236
230
  dns_thread = threading.Thread(target=dns_server_instance.start)
237
231
  dns_thread.daemon = True
238
232
  dns_thread.start()
239
233
 
240
234
  # === EOF Initialize DNS module ================================================================================
241
235
  # === Initialize TCP Server ====================================================================================
242
- if config['tcp']['enable_tcp_server']:
243
- port_in_use = networks.get_processes_using_port_list(config['tcp']['listening_port_list'])
244
- if port_in_use:
245
- for port, process_info in port_in_use.items():
246
- print_api(f"Port [{port}] is already in use by process: {process_info}", logger=system_logger,
247
- error_type=True, logger_method='critical')
236
+ if config_static.TCPServer.enable:
237
+ try:
238
+ socket_wrapper_instance = socket_wrapper.SocketWrapper(
239
+ listening_interface=config_static.TCPServer.listening_interface,
240
+ listening_port_list=config_static.TCPServer.listening_port_list,
241
+ ca_certificate_name=config_static.MainConfig.ca_certificate_name,
242
+ ca_certificate_filepath=config_static.MainConfig.ca_certificate_filepath,
243
+ default_server_certificate_usage=config_static.Certificates.default_server_certificate_usage,
244
+ default_server_certificate_name=config_static.MainConfig.default_server_certificate_name,
245
+ default_certificate_domain_list=config_static.Certificates.domains_all_times,
246
+ default_server_certificate_directory=config_static.MainConfig.SCRIPT_DIRECTORY,
247
+ sni_use_default_callback_function=True,
248
+ sni_use_default_callback_function_extended=True,
249
+ sni_add_new_domains_to_default_server_certificate=(
250
+ config_static.Certificates.sni_add_new_domains_to_default_server_certificate),
251
+ sni_create_server_certificate_for_each_domain=(
252
+ config_static.Certificates.sni_create_server_certificate_for_each_domain),
253
+ sni_server_certificates_cache_directory=(
254
+ config_static.Certificates.sni_server_certificates_cache_directory),
255
+ sni_get_server_certificate_from_server_socket=(
256
+ config_static.Certificates.sni_get_server_certificate_from_server_socket),
257
+ sni_server_certificate_from_server_socket_download_directory=(
258
+ config_static.Certificates.sni_server_certificate_from_server_socket_download_directory),
259
+ custom_server_certificate_usage=config_static.Certificates.custom_server_certificate_usage,
260
+ custom_server_certificate_path=config_static.Certificates.custom_server_certificate_path,
261
+ custom_private_key_path=config_static.Certificates.custom_private_key_path,
262
+ get_process_name=config_static.ProcessName.get_process_name,
263
+ ssh_user=config_static.ProcessName.ssh_user,
264
+ ssh_pass=config_static.ProcessName.ssh_pass,
265
+ ssh_script_to_execute=config_static.ProcessName.ssh_script_to_execute,
266
+ logger=listener_logger,
267
+ statistics_logs_directory=config_static.Log.logs_path,
268
+ forwarding_dns_service_ipv4_list___only_for_localhost=(
269
+ config_static.TCPServer.forwarding_dns_service_ipv4_list___only_for_localhost),
270
+ skip_extension_id_list=config_static.SkipExtensions.SKIP_EXTENSION_ID_LIST
271
+ )
272
+ except socket_wrapper.SocketWrapperPortInUseError as e:
273
+ print_api(e, error_type=True, color="red", logger=system_logger)
248
274
  # Wait for the message to be printed and saved to file.
249
275
  time.sleep(1)
250
- sys.exit(1)
251
-
252
- socket_wrapper = SocketWrapper(
253
- config=dicts_nested.merge(config, config_static.CONFIG_EXTENDED), logger=listener_logger,
254
- statistics_logger=statistics_logger)
255
-
256
- socket_wrapper.create_tcp_listening_socket_list()
257
-
258
- socket_wrapper.requested_domain_from_dns_server = domain_queue
276
+ return 1
277
+
278
+ statistics_writer = socket_wrapper_instance.statistics_writer
279
+
280
+ socket_wrapper_instance.create_tcp_listening_socket_list()
281
+
282
+ socket_wrapper_instance.requested_domain_from_dns_server = domain_queue
283
+
284
+ # Before we start the loop. we can set the default gateway if specified.
285
+ set_dns_gateway = False
286
+ dns_gateway_server_list = list()
287
+ if config_static.DNSServer.set_default_dns_gateway:
288
+ dns_gateway_server_list = config_static.DNSServer.set_default_dns_gateway
289
+ set_dns_gateway = True
290
+ elif config_static.DNSServer.set_default_dns_gateway_to_localhost:
291
+ dns_gateway_server_list = [base.LOCALHOST_IPV4]
292
+ set_dns_gateway = True
293
+ elif config_static.DNSServer.set_default_dns_gateway_to_default_interface_ipv4:
294
+ dns_gateway_server_list = [base.DEFAULT_IPV4]
295
+ set_dns_gateway = True
296
+
297
+ if set_dns_gateway:
298
+ # noinspection PyTypeChecker
299
+ dns.set_connection_dns_gateway_static(
300
+ dns_servers=dns_gateway_server_list,
301
+ use_default_connection=True
302
+ )
259
303
 
260
304
  # General exception handler will catch all the exceptions that occurred in the threads and write it to the log.
305
+ # noinspection PyBroadException
261
306
  try:
262
- socket_wrapper.loop_for_incoming_sockets(function_reference=thread_worker_main, reference_args=(
263
- network_logger, statistics_logger, engines_list, reference_module, config,))
307
+ socket_thread = threading.Thread(
308
+ target=socket_wrapper_instance.loop_for_incoming_sockets,
309
+ kwargs={
310
+ 'reference_function_name': thread_worker_main,
311
+ 'reference_function_args': (network_logger, statistics_writer, engines_list, reference_module,)
312
+ }
313
+ )
314
+
315
+ socket_thread.daemon = True
316
+ socket_thread.start()
264
317
  except Exception:
265
318
  message = f"Unhandled Exception occurred in 'loop_for_incoming_sockets' function"
266
319
  print_api(message, error_type=True, color="red", logger=network_logger, traceback_string=True, oneline=True)
267
320
 
268
- # === EOF Initialize TCP Server ================================================================================
321
+ # This is needed for Keyboard Exception.
322
+ while True:
323
+ time.sleep(1)
@@ -1,5 +1,4 @@
1
1
  import json
2
- # Needed to get the function caller module.
3
2
  import inspect
4
3
 
5
4
  from ..wrappers.loggingw import loggingw
@@ -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(content: list) -> dict:
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 line['host'] not in hosts_requests_responses:
121
- hosts_requests_responses[line['host']] = {
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[line['host']]['request_sizes'].append(int(request_size_bytes))
136
- hosts_requests_responses[line['host']]['response_sizes'].append(int(response_size_bytes))
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(content: list):
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
- hosts_requests_responses: dict = get_data_dict_from_statistics_content(content)
175
- compute_statistics_from_data_dict(hosts_requests_responses)
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 hosts_requests_responses
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(last_x_window_days_content_list)
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(previous_days_content_list: list) -> dict:
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
  })
File without changes