atomicshop 3.3.8__py3-none-any.whl → 3.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

Files changed (120) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  3. atomicshop/a_mains/install_ca_certificate.py +172 -0
  4. atomicshop/a_mains/process_from_port.py +119 -0
  5. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  6. atomicshop/basics/strings.py +1 -1
  7. atomicshop/certificates.py +2 -2
  8. atomicshop/dns.py +26 -28
  9. atomicshop/etws/traces/trace_tcp.py +1 -2
  10. atomicshop/mitm/centered_settings.py +133 -0
  11. atomicshop/mitm/config_static.py +22 -44
  12. atomicshop/mitm/connection_thread_worker.py +383 -165
  13. atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
  14. atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
  15. atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
  16. atomicshop/mitm/engines/create_module_template.py +1 -2
  17. atomicshop/mitm/import_config.py +91 -89
  18. atomicshop/mitm/initialize_engines.py +1 -2
  19. atomicshop/mitm/message.py +5 -4
  20. atomicshop/mitm/mitm_main.py +238 -122
  21. atomicshop/mitm/recs_files.py +61 -5
  22. atomicshop/mitm/ssh_tester.py +82 -0
  23. atomicshop/mitm/statistic_analyzer.py +33 -12
  24. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +104 -31
  25. atomicshop/networks.py +160 -92
  26. atomicshop/package_mains_processor.py +84 -0
  27. atomicshop/permissions/ubuntu_permissions.py +47 -0
  28. atomicshop/print_api.py +3 -5
  29. atomicshop/process.py +11 -4
  30. atomicshop/python_functions.py +23 -108
  31. atomicshop/speech_recognize.py +8 -0
  32. atomicshop/ssh_remote.py +140 -164
  33. atomicshop/web.py +63 -22
  34. atomicshop/web_apis/google_llm.py +22 -14
  35. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  36. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
  37. atomicshop/wrappers/dockerw/dockerw.py +2 -2
  38. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  39. atomicshop/wrappers/elasticsearchw/elastic_infra.py +0 -190
  40. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
  41. atomicshop/wrappers/githubw.py +180 -68
  42. atomicshop/wrappers/loggingw/consts.py +1 -1
  43. atomicshop/wrappers/loggingw/handlers.py +1 -1
  44. atomicshop/wrappers/loggingw/loggingw.py +20 -4
  45. atomicshop/wrappers/loggingw/reading.py +18 -0
  46. atomicshop/wrappers/mongodbw/mongo_infra.py +0 -38
  47. atomicshop/wrappers/netshw.py +124 -3
  48. atomicshop/wrappers/playwrightw/scenarios.py +1 -1
  49. atomicshop/wrappers/powershell_networking.py +80 -0
  50. atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
  51. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  52. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  53. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  54. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
  55. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
  56. atomicshop/wrappers/socketw/certificator.py +19 -9
  57. atomicshop/wrappers/socketw/creator.py +101 -14
  58. atomicshop/wrappers/socketw/dns_server.py +17 -5
  59. atomicshop/wrappers/socketw/exception_wrapper.py +21 -16
  60. atomicshop/wrappers/socketw/process_getter.py +86 -0
  61. atomicshop/wrappers/socketw/receiver.py +29 -9
  62. atomicshop/wrappers/socketw/sender.py +10 -9
  63. atomicshop/wrappers/socketw/sni.py +31 -10
  64. atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
  65. atomicshop/wrappers/socketw/socket_client.py +11 -10
  66. atomicshop/wrappers/socketw/socket_wrapper.py +125 -32
  67. atomicshop/wrappers/socketw/ssl_base.py +6 -2
  68. atomicshop/wrappers/ubuntu_terminal.py +21 -18
  69. atomicshop/wrappers/win_auditw.py +189 -0
  70. {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
  71. {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/RECORD +83 -109
  72. atomicshop/_basics_temp.py +0 -101
  73. atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
  74. atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
  75. atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py +0 -10
  76. atomicshop/a_installs/ubuntu/mongodb.py +0 -12
  77. atomicshop/a_installs/win/fibratus.py +0 -9
  78. atomicshop/a_installs/win/mongodb.py +0 -9
  79. atomicshop/a_installs/win/wsl_ubuntu_lts.py +0 -10
  80. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  81. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  82. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  83. atomicshop/addons/package_setup/Setup.cmd +0 -7
  84. atomicshop/archiver/__init__.py +0 -0
  85. atomicshop/archiver/_search_in_zip.py +0 -189
  86. atomicshop/archiver/search_in_archive.py +0 -284
  87. atomicshop/archiver/sevenz_app_w.py +0 -86
  88. atomicshop/archiver/sevenzs.py +0 -73
  89. atomicshop/archiver/shutils.py +0 -34
  90. atomicshop/archiver/zips.py +0 -353
  91. atomicshop/file_types.py +0 -24
  92. atomicshop/pbtkmultifile_argparse.py +0 -88
  93. atomicshop/script_as_string_processor.py +0 -42
  94. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  95. atomicshop/ssh_scripts/process_from_port.py +0 -27
  96. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  97. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  98. atomicshop/wrappers/dockerw/install_docker.py +0 -449
  99. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -233
  100. atomicshop/wrappers/ffmpegw.py +0 -125
  101. atomicshop/wrappers/fibratusw/__init__.py +0 -0
  102. atomicshop/wrappers/fibratusw/install.py +0 -80
  103. atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py +0 -100
  104. atomicshop/wrappers/mongodbw/install_mongodb_win.py +0 -244
  105. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  106. atomicshop/wrappers/socketw/get_process.py +0 -123
  107. atomicshop/wrappers/wslw.py +0 -192
  108. atomicshop-3.3.8.dist-info/entry_points.txt +0 -2
  109. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  110. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  111. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  112. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  113. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  114. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  115. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  116. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  117. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  118. {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
  119. {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
  120. {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
@@ -5,21 +5,31 @@ import datetime
5
5
  import os
6
6
  import sys
7
7
  import logging
8
+ import signal
8
9
 
9
10
  import atomicshop # Importing atomicshop package to get the version of the package.
10
11
 
11
12
  from .. import filesystem, on_exit, print_api, networks, dns
12
13
  from ..permissions import permissions
13
- from ..python_functions import get_current_python_version_string, check_python_version_compliance
14
- from ..wrappers.socketw import socket_wrapper, dns_server, base, statistics_csv
14
+ from .. import python_functions
15
+ from ..wrappers.socketw import socket_wrapper, dns_server, statistics_csv
15
16
  from ..wrappers.loggingw import loggingw
16
17
  from ..wrappers.ctyping import win_console
18
+ from ..wrappers import netshw
17
19
  from ..basics import multiprocesses
18
20
 
19
21
  from .connection_thread_worker import thread_worker_main
20
22
  from . import config_static, recs_files
21
23
 
22
24
 
25
+ # If you have 'pip-system-certs' package installed, this section overrides this behavior, since it injects
26
+ # the ssl default behavior, which we don't need when using ssl and sockets.
27
+ import ssl, importlib
28
+ if getattr(ssl.SSLContext.wrap_socket, "__module__", "").startswith("pip._vendor.truststore"):
29
+ # Truststore injection is active; restore stdlib ssl
30
+ importlib.reload(ssl)
31
+
32
+
23
33
  class NetworkSettings:
24
34
  """
25
35
  Class to store network settings.
@@ -27,6 +37,7 @@ class NetworkSettings:
27
37
 
28
38
  def __init__(
29
39
  self,
40
+ name: str | None = None,
30
41
  description: str | None = None,
31
42
  interface_index: int | None = None,
32
43
  is_dynamic: bool = False,
@@ -37,7 +48,7 @@ class NetworkSettings:
37
48
  default_gateways: list[str] = None,
38
49
  dns_gateways: list[str] = None
39
50
  ):
40
-
51
+ self.name: str | None = name
41
52
  self.description: str | None = description
42
53
  self.interface_index: int | None = interface_index
43
54
  self.is_dynamic: bool = is_dynamic
@@ -51,8 +62,6 @@ class NetworkSettings:
51
62
 
52
63
  # Global variables for setting the network interface to external IPs (eg: 192.168.0.1)
53
64
  NETWORK_INTERFACE_SETTINGS: NetworkSettings = NetworkSettings()
54
- CURRENT_IPV4S: list[str] = list()
55
- CURRENT_IPV4_MASKS: list[str] = list()
56
65
  IPS_TO_ASSIGN: list[str] = list()
57
66
  MASKS_TO_ASSIGN: list[str] = list()
58
67
 
@@ -87,41 +96,37 @@ except win_console.NotWindowsConsoleError:
87
96
  pass
88
97
 
89
98
 
99
+ # noinspection PyUnusedLocal
100
+ def _graceful_shutdown(signum, frame):
101
+ exit_cleanup()
102
+
103
+
90
104
  def exit_cleanup():
91
- if config_static.ENGINES_LIST[0].is_localhost:
92
- if permissions.is_admin() and IS_SET_DNS_GATEWAY:
93
- is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
94
- status_string = 'Dynamic' if is_dns_dynamic else 'Static'
95
- print_api.print_api(f'Current DNS Gateway: {status_string}, {current_dns_gateway}')
96
-
97
- if is_dns_dynamic != NETWORK_INTERFACE_IS_DYNAMIC or \
98
- (not is_dns_dynamic and current_dns_gateway != NETWORK_INTERFACE_IPV4_ADDRESS_LIST):
99
- if NETWORK_INTERFACE_IS_DYNAMIC:
100
- dns.set_connection_dns_gateway_dynamic(use_default_connection=True)
101
- else:
102
- dns.set_connection_dns_gateway_static(
103
- dns_servers=NETWORK_INTERFACE_IPV4_ADDRESS_LIST, use_default_connection=True)
104
-
105
- print_api.print_api("Returned default DNS gateway...", color='blue')
106
- else:
107
- # Get current network interface state.
108
- default_network_adapter_config, default_network_adapter, default_adapter_info = networks.get_wmi_network_adapter_configuration(
109
- use_default_interface=True, get_info_from_network_config=True)
105
+ if not config_static.MainConfig.is_localhost:
106
+ # Remove all the virtual IPs from the interface.
107
+ current_virtual_ips: list[str] = networks.get_interface_ips_powershell(NETWORK_INTERFACE_SETTINGS.name, "virtual")
108
+ for ip in current_virtual_ips:
109
+ netshw.remove_virtual_ip(NETWORK_INTERFACE_SETTINGS.name, ip)
110
110
 
111
- if NETWORK_INTERFACE_SETTINGS.is_dynamic:
112
- # If the network interface was dynamic before the script started, we will return it to dynamic.
113
- networks.set_dynamic_ip_for_adapter(default_network_adapter_config)
114
- else:
115
- networks.set_static_ip_for_adapter(
116
- default_network_adapter,
117
- ips=NETWORK_INTERFACE_SETTINGS.ipv4s,
118
- masks=NETWORK_INTERFACE_SETTINGS.ipv4_subnet_masks,
119
- gateways=NETWORK_INTERFACE_SETTINGS.default_gateways,
120
- dns_gateways=NETWORK_INTERFACE_SETTINGS.dns_gateways
121
- )
111
+ netshw.disable_dhcp_static_coexistence(interface_name=NETWORK_INTERFACE_SETTINGS.name)
122
112
 
123
113
  print_api.print_api("Returned network adapter settings...", color='blue')
124
114
 
115
+ if permissions.is_admin() and IS_SET_DNS_GATEWAY:
116
+ is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
117
+ status_string = 'Dynamic' if is_dns_dynamic else 'Static'
118
+ print_api.print_api(f'Current DNS Gateway: {status_string}, {current_dns_gateway}')
119
+
120
+ if is_dns_dynamic != NETWORK_INTERFACE_IS_DYNAMIC or \
121
+ (not is_dns_dynamic and current_dns_gateway != NETWORK_INTERFACE_IPV4_ADDRESS_LIST):
122
+ if NETWORK_INTERFACE_IS_DYNAMIC:
123
+ dns.set_interface_dns_gateway_dynamic(interface_name=NETWORK_INTERFACE_SETTINGS.name)
124
+ else:
125
+ dns.set_interface_dns_gateway_static(
126
+ dns_servers=NETWORK_INTERFACE_IPV4_ADDRESS_LIST, interface_name=NETWORK_INTERFACE_SETTINGS.name)
127
+
128
+ print_api.print_api("Returned default DNS gateway...", color='blue')
129
+
125
130
  # The process will not be executed if there was an exception in the beginning.
126
131
  if RECS_PROCESS_INSTANCE is not None:
127
132
  print_api.print_api(f'Recs archive process alive: {RECS_PROCESS_INSTANCE.is_alive()}')
@@ -148,7 +153,7 @@ def startup_output(system_logger, script_version: str):
148
153
  # Writing first log.
149
154
  system_logger.info("======================================")
150
155
  system_logger.info("Server Started.")
151
- system_logger.info(f"Python Version: {get_current_python_version_string()}")
156
+ system_logger.info(f"Python Version: {python_functions.get_python_version_string()}")
152
157
  system_logger.info(f"Script Version: {script_version}")
153
158
  system_logger.info(f"Atomic Workshop Version: {atomicshop.__version__}")
154
159
  system_logger.info(f"Log folder: {config_static.LogRec.logs_path}")
@@ -162,7 +167,7 @@ def startup_output(system_logger, script_version: str):
162
167
  f"Default server certificate usage enabled, if no SNI available: "
163
168
  f"{config_static.MainConfig.default_server_certificate_filepath}")
164
169
 
165
- if config_static.Certificates.sni_server_certificates_cache_directory:
170
+ if config_static.Certificates.sni_create_server_certificate_for_each_domain:
166
171
  system_logger.info(
167
172
  f"SNI function certificates creation enabled. Certificates cache: "
168
173
  f"{config_static.Certificates.sni_server_certificates_cache_directory}")
@@ -185,6 +190,7 @@ def startup_output(system_logger, script_version: str):
185
190
  # Printing the parsers using "start=1" for index to start counting from "1" and not "0"
186
191
  system_logger.info("Imported engine info.")
187
192
  print_api.print_api(f"[*] Found Engines:", logger=system_logger)
193
+ print_api.print_api( f"-------------------------", logger=system_logger)
188
194
 
189
195
  if not config_static.ENGINES_LIST:
190
196
  message = \
@@ -199,7 +205,6 @@ def startup_output(system_logger, script_version: str):
199
205
  f"{engine.responder_class_object.__name__}, "
200
206
  f"{engine.recorder_class_object.__name__}")
201
207
  print_api.print_api(message, logger=system_logger)
202
- print_api.print_api(f"[*] Name: {engine.engine_name}", logger=system_logger)
203
208
  print_api.print_api(f"[*] Domains: {list(engine.domain_target_dict.keys())}", logger=system_logger)
204
209
  dns_targets: list = list()
205
210
  for domain, ip_port in engine.domain_target_dict.items():
@@ -210,9 +215,11 @@ def startup_output(system_logger, script_version: str):
210
215
  print_api.print_api(f"[*] Connect Ports to IPs: {list(engine.on_port_connect.values())}", logger=system_logger)
211
216
  print_api.print_api(f"[*] Connect Ports to IPs Targets: {list(engine.port_target_dict.values())}", logger=system_logger)
212
217
 
218
+ print_api.print_api("-------------------------", logger=system_logger)
219
+
213
220
  # print_api.print_api(f"[*] TCP Listening Interfaces: {engine.tcp_listening_address_list}", logger=system_logger)
214
221
 
215
- if config_static.DNSServer.enable:
222
+ if config_static.DNSServer.is_enabled:
216
223
  print_api.print_api("DNS Server is enabled.", logger=system_logger)
217
224
 
218
225
  # If engines were found and dns is set to route by the engine domains.
@@ -236,13 +243,42 @@ def startup_output(system_logger, script_version: str):
236
243
  else:
237
244
  print_api.print_api("DNS Server is disabled.", logger=system_logger, color="yellow")
238
245
 
239
- if config_static.TCPServer.enable:
246
+ if config_static.TCPServer.is_enabled:
240
247
  print_api.print_api("TCP Server is enabled.", logger=system_logger)
241
248
  else:
242
249
  print_api.print_api("TCP Server is disabled.", logger=system_logger, color="yellow")
243
250
 
251
+ if config_static.MainConfig.is_localhost:
252
+ selected_net_interface: str = config_static.MainConfig.network_interface
253
+ else:
254
+ selected_net_interface: str = NETWORK_INTERFACE_SETTINGS.name
255
+ print_api.print_api(f"Selected Network Interface: {selected_net_interface}", logger=system_logger)
256
+
257
+ print_api.print_api(f"Listening DNS address: {config_static.DNSServer.listening_address}", logger=system_logger)
244
258
 
245
- def get_ipv4s_for_tcp_server():
259
+
260
+ def _get_interface_name() -> str | None:
261
+ if config_static.MainConfig.network_interface == '':
262
+ interface_name: str = networks.get_default_interface_name()
263
+ if interface_name == '':
264
+ print_api.print_api(
265
+ "Default network interface not found.",
266
+ error_type=True, color="red")
267
+ return None
268
+ else:
269
+ current_network_interface_names: list[str] = networks.list_network_interfaces()
270
+ if config_static.MainConfig.network_interface not in current_network_interface_names:
271
+ print_api.print_api(
272
+ f"Not found Network interface with the name: {config_static.MainConfig.network_interface}",
273
+ error_type=True, color="red")
274
+ return None
275
+ else:
276
+ interface_name = config_static.MainConfig.network_interface
277
+
278
+ return interface_name
279
+
280
+
281
+ def get_ipv4s_for_tcp_server() -> int:
246
282
  """
247
283
  Function to get the IPv4 addresses for the default network adapter to set them to the adapter.
248
284
  """
@@ -255,48 +291,82 @@ def get_ipv4s_for_tcp_server():
255
291
  ports_to_create_ips_for += list(engine.on_port_connect.keys())
256
292
 
257
293
  engine_ips: list[str] = list()
258
- # Check if we need the localhost ips (12.0.0.1) or external local ips (192.168.0.100).
259
- if config_static.ENGINES_LIST[0].is_localhost:
260
- create_ips: int = len(domains_to_create_ips_for) + len(ports_to_create_ips_for)
294
+ create_ips: int = len(domains_to_create_ips_for) + len(ports_to_create_ips_for)
261
295
 
262
- # Generate the list of localhost ips.
263
- for i in range(create_ips):
264
- engine_ips.append(f"127.0.0.{i+1}")
265
- else:
266
- # Get current network interface state.
267
- default_network_adapter_config, default_network_adapter, default_adapter_info = networks.get_wmi_network_adapter_configuration(
268
- use_default_interface=True, get_info_from_network_config=True)
269
-
270
- global NETWORK_INTERFACE_SETTINGS
271
- NETWORK_INTERFACE_SETTINGS = NetworkSettings(
272
- description=default_adapter_info['description'],
273
- interface_index=default_adapter_info['interface_index'],
274
- is_dynamic=default_adapter_info['is_dynamic'],
275
- ipv4s=default_adapter_info['ipv4s'],
276
- ipv6s=default_adapter_info['ipv6s'],
277
- ipv4_subnet_masks=default_adapter_info['ipv4_subnet_masks'],
278
- ipv6_prefixes=default_adapter_info['ipv6_prefixes'],
279
- default_gateways=default_adapter_info['default_gateways'],
280
- dns_gateways=default_adapter_info['dns_gateways']
281
- )
296
+ # Get network interface name.
297
+ interface_name: str = _get_interface_name()
298
+ if interface_name is None:
299
+ return 1
282
300
 
283
- # Adding IP addresses to the default network adapter.
284
- current_ipv4s: list[str] = default_adapter_info['ipv4s']
285
- current_ips_count: int = len(current_ipv4s)
301
+ # Get selected network interface virtual IPs from previous runs.
302
+ # We still need network interface settings for DNS gateway assignment for the network interface doesn't matter in localhost mode or not.
303
+ current_virtual_ips: list[str] = networks.get_interface_ips_powershell(interface_name, "virtual")
286
304
 
287
- # If the number of currently assigned IPs is smaller than the number of IPs to create,
288
- # subtract the current IPs count from the number of IPs to create, to create only what is missing.
289
- create_ips: int = len(domains_to_create_ips_for)
290
- if current_ips_count <= create_ips:
291
- create_ips -= current_ips_count
305
+ if current_virtual_ips:
306
+ print_api.print_api(
307
+ f"Removing previous virtual IPs from interface [{interface_name}]: {current_virtual_ips}",
308
+ color="blue")
292
309
 
310
+ if not permissions.is_admin():
311
+ print_api.print_api(
312
+ f"Administrator permissions are required to remove virtual IPs from interface.",
313
+ error_type=True, color="red")
314
+ return 1
315
+ # Remove all the virtual IPs from the interface.
316
+ for ip in current_virtual_ips:
317
+ netshw.remove_virtual_ip(interface_name, ip)
318
+
319
+ network_adapter_config, network_adapter, adapter_info = networks.get_wmi_network_adapter_configuration(
320
+ interface_name=interface_name,
321
+ get_info_from_network_config=True)
322
+
323
+ global NETWORK_INTERFACE_SETTINGS
324
+ NETWORK_INTERFACE_SETTINGS = NetworkSettings(
325
+ name=adapter_info['name'],
326
+ description=adapter_info['description'],
327
+ interface_index=adapter_info['interface_index'],
328
+ is_dynamic=adapter_info['is_dynamic'],
329
+ ipv4s=adapter_info['ipv4s'],
330
+ ipv6s=adapter_info['ipv6s'],
331
+ ipv4_subnet_masks=adapter_info['ipv4_subnet_masks'],
332
+ ipv6_prefixes=adapter_info['ipv6_prefixes'],
333
+ default_gateways=adapter_info['default_gateways'],
334
+ dns_gateways=adapter_info['dns_gateways']
335
+ )
336
+
337
+ # Check if we need the localhost ips (12.0.0.1) or external local ips (192.168.0.100).
338
+ if config_static.MainConfig.is_localhost:
339
+ # Generate the list of localhost ips. We will start from 127.0.0.2 and end with 127.0.0.2(create_ips + 1)
340
+ for i in range(2, create_ips + 2):
341
+ engine_ips.append(f"127.0.0.{i}")
342
+
343
+ # If the current default DNS gateway ipv4 is inside the engine_ips, then we will remove it and add the next in line.
344
+ if config_static.MainConfig.default_localhost_dns_gateway_ipv4 in engine_ips:
345
+ engine_ips.remove(config_static.MainConfig.default_localhost_dns_gateway_ipv4)
346
+ engine_ips.append(f"127.0.0.{create_ips + 2}")
347
+
348
+ dns_listening_ipv4: str = config_static.MainConfig.default_localhost_dns_gateway_ipv4
349
+ else:
293
350
  # Generate the IPs for the domains.
294
- global CURRENT_IPV4S, CURRENT_IPV4_MASKS, IPS_TO_ASSIGN, MASKS_TO_ASSIGN
295
- CURRENT_IPV4S, CURRENT_IPV4_MASKS, IPS_TO_ASSIGN, MASKS_TO_ASSIGN = networks.add_virtual_ips_to_default_adapter_by_current_setting(
351
+ global IPS_TO_ASSIGN, MASKS_TO_ASSIGN
352
+ assignment_result: tuple | None = networks.add_virtual_ips_to_network_interface(
353
+ interface_name=interface_name,
296
354
  number_of_ips=create_ips,
297
355
  simulate_only=True)
298
356
 
299
- engine_ips += CURRENT_IPV4S + IPS_TO_ASSIGN
357
+ if assignment_result is None:
358
+ return 1
359
+
360
+ IPS_TO_ASSIGN, MASKS_TO_ASSIGN = assignment_result
361
+
362
+ engine_ips += IPS_TO_ASSIGN
363
+ dns_listening_ipv4: str = NETWORK_INTERFACE_SETTINGS.ipv4s[0]
364
+
365
+ # Assign DNS listening address.
366
+ if config_static.DNSServer.listening_ipv4 != '':
367
+ config_static.DNSServer.listening_address = f"{config_static.DNSServer.listening_ipv4}:{str(config_static.DNSServer.listening_port)}"
368
+ else:
369
+ config_static.DNSServer.listening_address = f"{dns_listening_ipv4}:{str(config_static.DNSServer.listening_port)}"
300
370
 
301
371
  # Add the ips to engines.
302
372
  for engine in config_static.ENGINES_LIST:
@@ -309,23 +379,31 @@ def get_ipv4s_for_tcp_server():
309
379
  if port in ports_to_create_ips_for:
310
380
  engine.port_target_dict[port]['ip'] = engine_ips.pop(0)
311
381
 
382
+ return 0
312
383
 
313
- def mitm_server(config_file_path: str, script_version: str):
384
+
385
+ def mitm_server(config_file_path: str, script_version: str) -> int:
314
386
  on_exit.register_exit_handler(exit_cleanup, at_exit=False, kill_signal=False)
315
387
 
316
- # Main function should return integer with error code, 0 is successful.
317
- # Since listening server is infinite, this will not be reached.
318
- # After modules import - we check for python version.
319
- if not check_python_version_compliance(minor_version='3.12'):
388
+ python_version: str = python_functions.get_python_version_string()
389
+ print_api.print_api(f"[*] Python Version: {python_version}")
390
+
391
+ compliance_message: str | None = python_functions.check_python_version_compliance(min_ver=(3,13), max_ver=(3,13,99))
392
+ if compliance_message is not None:
393
+ print_api.print_api(f"[!] {compliance_message}", error_type=True, color="red")
320
394
  return 1
321
395
 
396
+ print_api.print_api("[*] Version Check PASSED.", color="green")
397
+
322
398
  # Import the configuration file.
323
- result = config_static.load_config(config_file_path, print_kwargs=dict(stdout=False))
324
- if result != 0:
325
- return result
399
+ rc: int = config_static.load_config(config_file_path, print_kwargs=dict(stdout=False))
400
+ if rc != 0:
401
+ return rc
326
402
 
327
403
  # Get the IPs that will be set for the adapter and fill the engine configuration with the IPs.
328
- get_ipv4s_for_tcp_server()
404
+ rc: int = get_ipv4s_for_tcp_server()
405
+ if rc != 0:
406
+ return rc
329
407
 
330
408
  global MITM_ERROR_LOGGER
331
409
  MITM_ERROR_LOGGER = loggingw.ExceptionCsvLogger(
@@ -397,7 +475,7 @@ def mitm_server(config_file_path: str, script_version: str):
397
475
  is_ready_multiprocessing_event_list: list[multiprocessing.Event] = list()
398
476
 
399
477
  # === Initialize TCP Server ====================================================================================
400
- if config_static.TCPServer.enable:
478
+ if config_static.TCPServer.is_enabled:
401
479
  # Get the default network adapter configuration and set the one from config.
402
480
  # We set the virtual IPs in the network adapter here, so the server multiprocessing processes can listen on them.
403
481
  setting_result: int = _add_virtual_ips_set_default_dns_gateway(system_logger)
@@ -489,6 +567,8 @@ def mitm_server(config_file_path: str, script_version: str):
489
567
  exceptions_logger_queue=EXCEPTIONS_CSV_LOGGER_QUEUE,
490
568
  forwarding_dns_service_ipv4_list___only_for_localhost=[config_static.DNSServer.forwarding_dns_service_ipv4],
491
569
  skip_extension_id_list=config_static.SkipExtensions.SKIP_EXTENSION_ID_LIST,
570
+ enable_sslkeylogfile_env_to_client_ssl_context=config_static.Certificates.enable_sslkeylogfile_env_to_client_ssl_context,
571
+ sslkeylog_file_path=config_static.Certificates.sslkeylog_file_path,
492
572
  print_kwargs=dict(stdout=False)
493
573
  )
494
574
 
@@ -521,7 +601,7 @@ def mitm_server(config_file_path: str, script_version: str):
521
601
  # === EOF Initialize TCP Server ====================================================================================
522
602
 
523
603
  # === Initialize DNS module ========================================================================================
524
- if config_static.DNSServer.enable:
604
+ if config_static.DNSServer.is_enabled:
525
605
  # noinspection PyTypeHints
526
606
  is_dns_process_ready: multiprocessing.Event = multiprocessing.Event()
527
607
 
@@ -536,7 +616,7 @@ def mitm_server(config_file_path: str, script_version: str):
536
616
  resolve_regular_pass_thru=config_static.DNSServer.resolve_regular_pass_thru,
537
617
  resolve_all_domains_to_ipv4=(
538
618
  config_static.DNSServer.resolve_all_domains_to_ipv4_enable, config_static.DNSServer.target_ipv4),
539
- offline_mode=config_static.MainConfig.offline,
619
+ offline_mode=config_static.MainConfig.is_offline,
540
620
  cache_timeout_minutes=config_static.DNSServer.cache_timeout_minutes,
541
621
  logging_queue=NETWORK_LOGGER_QUEUE,
542
622
  logger_name=network_logger_name,
@@ -558,7 +638,7 @@ def mitm_server(config_file_path: str, script_version: str):
558
638
  return 1
559
639
  # === EOF Initialize DNS module ====================================================================================
560
640
 
561
- if config_static.DNSServer.enable or config_static.TCPServer.enable:
641
+ if config_static.DNSServer.is_enabled or config_static.TCPServer.is_enabled:
562
642
  print_api.print_api("The Server is Ready for Operation!", color="green", logger=system_logger)
563
643
  print_api.print_api("Press [Ctrl]+[C] to stop.", color='blue', logger=system_logger)
564
644
 
@@ -578,6 +658,8 @@ def mitm_server(config_file_path: str, script_version: str):
578
658
 
579
659
  time.sleep(1)
580
660
 
661
+ return 0
662
+
581
663
 
582
664
  # noinspection PyTypeHints
583
665
  def _create_tcp_server_process(
@@ -681,48 +763,59 @@ def _add_virtual_ips_set_default_dns_gateway(system_logger: logging.Logger) -> i
681
763
  # This setting is needed only for the dns gateways configurations from the main config on localhost.
682
764
  set_local_dns_gateway: bool = False
683
765
  # Set the default gateway if specified.
684
- if config_static.DNSServer.set_default_dns_gateway:
685
- dns_gateway_server_list = config_static.DNSServer.set_default_dns_gateway
766
+ if config_static.MainConfig.set_default_dns_gateway:
767
+ dns_gateway_server_list = config_static.MainConfig.set_default_dns_gateway
686
768
  set_local_dns_gateway = True
687
- elif config_static.DNSServer.set_default_dns_gateway_to_localhost:
688
- dns_gateway_server_list = [base.LOCALHOST_IPV4]
769
+ elif config_static.MainConfig.set_default_dns_gateway_to_localhost:
770
+ dns_gateway_server_list = [config_static.MainConfig.default_localhost_dns_gateway_ipv4]
689
771
  set_local_dns_gateway = True
690
- elif config_static.DNSServer.set_default_dns_gateway_to_default_interface_ipv4:
691
- dns_gateway_server_list = [base.DEFAULT_IPV4]
772
+ elif config_static.MainConfig.set_default_dns_gateway_to_network_interface_ipv4:
773
+ dns_gateway_server_list = [NETWORK_INTERFACE_SETTINGS.ipv4s[0]]
692
774
  set_local_dns_gateway = True
693
775
  else:
694
776
  dns_gateway_server_list = NETWORK_INTERFACE_SETTINGS.dns_gateways
695
777
 
696
- if config_static.ENGINES_LIST[0].is_localhost:
697
- if set_local_dns_gateway:
698
- global IS_SET_DNS_GATEWAY
699
- IS_SET_DNS_GATEWAY = True
700
-
701
- # Get current network interface state.
702
- global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
703
- NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
704
-
705
- # Set the DNS gateway to the specified one only if the DNS gateway is dynamic, or it is static but different
706
- # from the one specified in the configuration file.
707
- if (NETWORK_INTERFACE_IS_DYNAMIC or (not NETWORK_INTERFACE_IS_DYNAMIC and
708
- NETWORK_INTERFACE_IPV4_ADDRESS_LIST != dns_gateway_server_list)):
709
- try:
710
- dns.set_connection_dns_gateway_static(
711
- dns_servers=dns_gateway_server_list,
712
- use_default_connection=True
713
- )
714
- except PermissionError as e:
715
- print_api.print_api(e, error_type=True, color="red", logger=system_logger)
716
- # Wait for the message to be printed and saved to file.
717
- time.sleep(1)
718
- # network_logger_queue_listener.stop()
719
- return 1
720
- else:
778
+ if set_local_dns_gateway:
779
+ global IS_SET_DNS_GATEWAY
780
+ IS_SET_DNS_GATEWAY = True
781
+
782
+ # Get current network interface state.
783
+ global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
784
+ NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
785
+
786
+ # Set the DNS gateway to the specified one only if the DNS gateway is dynamic, or it is static but different
787
+ # from the one specified in the configuration file.
788
+ if (NETWORK_INTERFACE_IS_DYNAMIC or (not NETWORK_INTERFACE_IS_DYNAMIC and
789
+ NETWORK_INTERFACE_IPV4_ADDRESS_LIST != dns_gateway_server_list)):
790
+ try:
791
+ dns.set_interface_dns_gateway_static(
792
+ interface_name=NETWORK_INTERFACE_SETTINGS.name,
793
+ dns_servers=dns_gateway_server_list
794
+ )
795
+ except PermissionError as e:
796
+ print_api.print_api(e, error_type=True, color="red", logger=system_logger)
797
+ # Wait for the message to be printed and saved to file.
798
+ time.sleep(1)
799
+ # network_logger_queue_listener.stop()
800
+ return 1
801
+
802
+ if not config_static.MainConfig.is_localhost:
721
803
  # Change the adapter settings and add the virtual IPs.
722
804
  try:
723
- networks.add_virtual_ips_to_default_adapter_by_current_setting(
724
- virtual_ipv4s_to_add=IPS_TO_ASSIGN, virtual_ipv4_masks_to_add=MASKS_TO_ASSIGN,
725
- dns_gateways=dns_gateway_server_list)
805
+ networks.add_virtual_ips_to_network_interface(
806
+ interface_name=NETWORK_INTERFACE_SETTINGS.name,
807
+ virtual_ipv4s_to_add=IPS_TO_ASSIGN,
808
+ virtual_ipv4_masks_to_add=MASKS_TO_ASSIGN,
809
+ verbose=True,
810
+ logger=system_logger
811
+ )
812
+
813
+ for engine in config_static.ENGINES_LIST:
814
+ bindable_test_dict: dict = engine.domain_target_dict | engine.port_target_dict
815
+ for port_or_domain_name, ip_port_dict in bindable_test_dict.items():
816
+ print_api.print_api(f"Checking that virtual IP is bindable: {ip_port_dict['ip']}:{ip_port_dict['port']}", logger=system_logger)
817
+ networks.wait_for_ip_bindable_socket(ip_port_dict['ip'], port=int(ip_port_dict['port']), timeout=15)
818
+ print_api.print_api("BIND test successful for all virtual IPs.", logger=system_logger)
726
819
  except (PermissionError, TimeoutError) as e:
727
820
  print_api.print_api(e, error_type=True, color="red", logger=system_logger)
728
821
  # Wait for the message to be printed and saved to file.
@@ -760,6 +853,29 @@ def _loop_at_midnight_recs_archive(network_logger_name):
760
853
 
761
854
 
762
855
  def mitm_server_main(config_file_path: str, script_version: str):
856
+ # This is for Linux and macOS.
857
+ signal.signal(signal.SIGTERM, _graceful_shutdown)
858
+ signal.signal(signal.SIGINT, _graceful_shutdown)
859
+ # This is for Windows.
860
+ """
861
+ Example:
862
+ script = (Path(__file__).resolve().parent / "ServerTCPWithDNS.py").resolve()
863
+ p = subprocess.Popen(
864
+ [sys.executable, "-u", str(script)],
865
+ creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
866
+ # inherit console; do NOT use CREATE_NEW_CONSOLE
867
+ )
868
+ time.sleep(30)
869
+ p.send_signal(signal.CTRL_BREAK_EVENT)
870
+ try:
871
+ p.wait(timeout=5)
872
+ except subprocess.TimeoutExpired:
873
+ print("Graceful interrupt timed out; terminating")
874
+ p.terminate()
875
+ p.wait()
876
+ """
877
+ signal.signal(signal.SIGBREAK, _graceful_shutdown)
878
+
763
879
  try:
764
880
  # Main function should return integer with error code, 0 is successful.
765
881
  return mitm_server(config_file_path, script_version)
@@ -2,8 +2,9 @@ import datetime
2
2
  import os
3
3
  import multiprocessing
4
4
  import logging
5
+ import zipfile
6
+ import shutil
5
7
 
6
- from ..archiver import zips
7
8
  from .. import filesystem, print_api
8
9
  from .. wrappers.loggingw import consts, loggingw
9
10
 
@@ -13,6 +14,40 @@ REC_FILE_DATE_TIME_FORMAT: str = f'{consts.DEFAULT_ROTATING_SUFFIXES_FROM_WHEN["
13
14
  REC_FILE_DATE_FORMAT: str = REC_FILE_DATE_TIME_FORMAT.split('_')[0]
14
15
 
15
16
 
17
+ def archive(
18
+ directory_path: str,
19
+ include_root_directory: bool = True,
20
+ ) -> str:
21
+ """
22
+ Function archives the directory.
23
+ :param directory_path: string, full path to the directory.
24
+ :param include_root_directory: boolean, default is 'True'.
25
+ 'True': The root directory will be included in the archive.
26
+ 'False': The root directory will not be included in the archive.
27
+ True is usually the case in most archiving utilities.
28
+ :return: string, full path to the archived file.
29
+ """
30
+
31
+ # This is commonly used and supported by most ZIP utilities.
32
+ compression_method = zipfile.ZIP_DEFLATED
33
+
34
+ archive_path: str = directory_path + '.zip'
35
+ with zipfile.ZipFile(archive_path, 'w', compression_method) as zip_object:
36
+ for root, _, files in os.walk(directory_path):
37
+ for file in files:
38
+ file_path = os.path.join(root, file)
39
+
40
+ # If including the root directory, use the relative path from the parent directory of the root
41
+ if include_root_directory:
42
+ arcname = os.path.relpath(file_path, os.path.dirname(directory_path))
43
+ else:
44
+ arcname = os.path.relpath(file_path, directory_path)
45
+
46
+ zip_object.write(file_path, arcname)
47
+
48
+ return archive_path
49
+
50
+
16
51
  def recs_archiver(
17
52
  recs_directory: str,
18
53
  logging_queue: multiprocessing.Queue,
@@ -69,6 +104,8 @@ def recs_archiver(
69
104
  try:
70
105
  archived_files: list = list()
71
106
  for directory_path, all_recs_files in file_list_per_directory:
107
+ print_api.print_api(f"Archiving recs files in directory: {directory_path.path}",
108
+ logger=rec_packer_logger_with_queue_handler, color='blue')
72
109
  for recs_atomic_path in all_recs_files:
73
110
  # We don't need to archive today's files.
74
111
  if today_date_string == recs_atomic_path.datetime_string:
@@ -82,10 +119,29 @@ def recs_archiver(
82
119
  # Archive directories.
83
120
  archive_directories: list = filesystem.get_paths_from_directory(
84
121
  directory_path.path, get_directory=True, recursive=False)
85
- for archive_directory in archive_directories:
86
- archived_file: str = zips.archive_directory(
87
- archive_directory.path, remove_original=True, include_root_directory=True)
88
- archived_files.append(archived_file)
122
+
123
+ if not archive_directories:
124
+ print_api.print_api(
125
+ f"No directories to archive in: {directory_path.path}",
126
+ color='blue',
127
+ logger=rec_packer_logger_with_queue_handler
128
+ )
129
+ else:
130
+ total_archived_files: int = 0
131
+ for archive_directory in archive_directories:
132
+ files_to_archive: list = filesystem.get_paths_from_directory(
133
+ directory_path=archive_directory.path, get_file=True, recursive=False)
134
+ total_archived_files += len(files_to_archive)
135
+ archived_file: str = archive(archive_directory.path, include_root_directory=True)
136
+ # Remove the original directory after archiving.
137
+ shutil.rmtree(archive_directory.path, ignore_errors=True)
138
+ archived_files.append(archived_file)
139
+
140
+ print_api.print_api(
141
+ f'Archived: 'f'Directories: {len(archive_directories)} | '
142
+ f'Total Files: {total_archived_files} | In: {directory_path.path}',
143
+ logger=rec_packer_logger_with_queue_handler, color='blue')
144
+ print_api.print_api(f'Archived files: {archived_files}', logger=rec_packer_logger_with_queue_handler)
89
145
 
90
146
  finalize_output_queue.put(None)
91
147