atomicshop 2.11.47__py3-none-any.whl → 3.10.5__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.
Files changed (268) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
  4. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  5. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  6. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  7. atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
  8. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  9. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  10. atomicshop/a_mains/github_wrapper.py +11 -0
  11. atomicshop/a_mains/install_ca_certificate.py +172 -0
  12. atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
  13. atomicshop/a_mains/process_from_port.py +119 -0
  14. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  15. atomicshop/a_mains/update_config_toml.py +38 -0
  16. atomicshop/appointment_management.py +5 -3
  17. atomicshop/basics/ansi_escape_codes.py +3 -1
  18. atomicshop/basics/argparse_template.py +2 -0
  19. atomicshop/basics/booleans.py +27 -30
  20. atomicshop/basics/bytes_arrays.py +43 -0
  21. atomicshop/basics/classes.py +149 -1
  22. atomicshop/basics/dicts.py +12 -0
  23. atomicshop/basics/enums.py +2 -2
  24. atomicshop/basics/exceptions.py +5 -1
  25. atomicshop/basics/list_of_classes.py +29 -0
  26. atomicshop/basics/list_of_dicts.py +69 -5
  27. atomicshop/basics/lists.py +14 -0
  28. atomicshop/basics/multiprocesses.py +374 -50
  29. atomicshop/basics/package_module.py +10 -0
  30. atomicshop/basics/strings.py +160 -7
  31. atomicshop/basics/threads.py +14 -0
  32. atomicshop/basics/tracebacks.py +13 -4
  33. atomicshop/certificates.py +153 -52
  34. atomicshop/config_init.py +12 -7
  35. atomicshop/console_user_response.py +7 -14
  36. atomicshop/consoles.py +9 -0
  37. atomicshop/datetimes.py +98 -0
  38. atomicshop/diff_check.py +340 -40
  39. atomicshop/dns.py +128 -12
  40. atomicshop/etws/_pywintrace_fix.py +17 -0
  41. atomicshop/etws/const.py +38 -0
  42. atomicshop/etws/providers.py +21 -0
  43. atomicshop/etws/sessions.py +43 -0
  44. atomicshop/etws/trace.py +168 -0
  45. atomicshop/etws/traces/trace_dns.py +162 -0
  46. atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
  47. atomicshop/etws/traces/trace_tcp.py +130 -0
  48. atomicshop/file_io/csvs.py +222 -24
  49. atomicshop/file_io/docxs.py +35 -18
  50. atomicshop/file_io/file_io.py +35 -19
  51. atomicshop/file_io/jsons.py +49 -0
  52. atomicshop/file_io/tomls.py +139 -0
  53. atomicshop/filesystem.py +864 -293
  54. atomicshop/get_process_list.py +133 -0
  55. atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
  56. atomicshop/http_parse.py +149 -93
  57. atomicshop/ip_addresses.py +6 -1
  58. atomicshop/mitm/centered_settings.py +132 -0
  59. atomicshop/mitm/config_static.py +207 -0
  60. atomicshop/mitm/config_toml_editor.py +55 -0
  61. atomicshop/mitm/connection_thread_worker.py +875 -357
  62. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  63. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  64. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  65. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  66. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  67. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  68. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  69. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  70. atomicshop/mitm/engines/create_module_template.py +58 -14
  71. atomicshop/mitm/import_config.py +359 -139
  72. atomicshop/mitm/initialize_engines.py +160 -74
  73. atomicshop/mitm/message.py +64 -23
  74. atomicshop/mitm/mitm_main.py +892 -0
  75. atomicshop/mitm/recs_files.py +183 -0
  76. atomicshop/mitm/shared_functions.py +4 -10
  77. atomicshop/mitm/ssh_tester.py +82 -0
  78. atomicshop/mitm/statistic_analyzer.py +257 -166
  79. atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
  80. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
  81. atomicshop/monitor/change_monitor.py +96 -120
  82. atomicshop/monitor/checks/dns.py +139 -70
  83. atomicshop/monitor/checks/file.py +77 -0
  84. atomicshop/monitor/checks/network.py +81 -77
  85. atomicshop/monitor/checks/process_running.py +33 -34
  86. atomicshop/monitor/checks/url.py +94 -0
  87. atomicshop/networks.py +671 -0
  88. atomicshop/on_exit.py +205 -0
  89. atomicshop/package_mains_processor.py +84 -0
  90. atomicshop/permissions/permissions.py +22 -0
  91. atomicshop/permissions/ubuntu_permissions.py +239 -0
  92. atomicshop/permissions/win_permissions.py +33 -0
  93. atomicshop/print_api.py +24 -41
  94. atomicshop/process.py +63 -17
  95. atomicshop/process_poller/__init__.py +0 -0
  96. atomicshop/process_poller/pollers/__init__.py +0 -0
  97. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  98. atomicshop/process_poller/process_pool.py +207 -0
  99. atomicshop/process_poller/simple_process_pool.py +311 -0
  100. atomicshop/process_poller/tracer_base.py +45 -0
  101. atomicshop/process_poller/tracers/__init__.py +0 -0
  102. atomicshop/process_poller/tracers/event_log.py +46 -0
  103. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  104. atomicshop/python_file_patcher.py +1 -1
  105. atomicshop/python_functions.py +27 -75
  106. atomicshop/question_answer_engine.py +2 -2
  107. atomicshop/scheduling.py +24 -5
  108. atomicshop/sound.py +4 -2
  109. atomicshop/speech_recognize.py +8 -0
  110. atomicshop/ssh_remote.py +158 -172
  111. atomicshop/startup/__init__.py +0 -0
  112. atomicshop/startup/win/__init__.py +0 -0
  113. atomicshop/startup/win/startup_folder.py +53 -0
  114. atomicshop/startup/win/task_scheduler.py +119 -0
  115. atomicshop/system_resource_monitor.py +61 -46
  116. atomicshop/system_resources.py +8 -8
  117. atomicshop/tempfiles.py +1 -2
  118. atomicshop/timer.py +30 -11
  119. atomicshop/urls.py +41 -0
  120. atomicshop/venvs.py +28 -0
  121. atomicshop/versioning.py +27 -0
  122. atomicshop/web.py +110 -25
  123. atomicshop/web_apis/__init__.py +0 -0
  124. atomicshop/web_apis/google_custom_search.py +44 -0
  125. atomicshop/web_apis/google_llm.py +188 -0
  126. atomicshop/websocket_parse.py +450 -0
  127. atomicshop/wrappers/certauthw/certauth.py +1 -0
  128. atomicshop/wrappers/cryptographyw.py +29 -8
  129. atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
  130. atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
  131. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
  132. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  133. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  134. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
  135. atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
  136. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  137. atomicshop/wrappers/ctyping/win_console.py +39 -0
  138. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  139. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  140. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  141. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  142. atomicshop/wrappers/factw/get_file_data.py +12 -5
  143. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  144. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  145. atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
  146. atomicshop/wrappers/githubw.py +583 -51
  147. atomicshop/wrappers/loggingw/consts.py +49 -0
  148. atomicshop/wrappers/loggingw/filters.py +102 -0
  149. atomicshop/wrappers/loggingw/formatters.py +58 -71
  150. atomicshop/wrappers/loggingw/handlers.py +459 -40
  151. atomicshop/wrappers/loggingw/loggers.py +19 -0
  152. atomicshop/wrappers/loggingw/loggingw.py +1010 -178
  153. atomicshop/wrappers/loggingw/reading.py +344 -19
  154. atomicshop/wrappers/mongodbw/__init__.py +0 -0
  155. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  156. atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
  157. atomicshop/wrappers/netshw.py +271 -0
  158. atomicshop/wrappers/playwrightw/engine.py +34 -19
  159. atomicshop/wrappers/playwrightw/infra.py +5 -0
  160. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  161. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  162. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  163. atomicshop/wrappers/playwrightw/waits.py +9 -7
  164. atomicshop/wrappers/powershell_networking.py +80 -0
  165. atomicshop/wrappers/psutilw/processes.py +81 -0
  166. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  167. atomicshop/wrappers/psutilw/psutilw.py +9 -0
  168. atomicshop/wrappers/pyopensslw.py +9 -2
  169. atomicshop/wrappers/pywin32w/__init__.py +0 -0
  170. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  171. atomicshop/wrappers/pywin32w/console.py +34 -0
  172. atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
  173. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  174. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
  175. atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
  176. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
  177. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
  178. atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
  179. atomicshop/wrappers/pywin32w/winshell.py +19 -0
  180. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  181. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  182. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  183. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  184. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  185. atomicshop/wrappers/socketw/accepter.py +21 -7
  186. atomicshop/wrappers/socketw/certificator.py +216 -150
  187. atomicshop/wrappers/socketw/creator.py +190 -50
  188. atomicshop/wrappers/socketw/dns_server.py +500 -173
  189. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  190. atomicshop/wrappers/socketw/process_getter.py +86 -0
  191. atomicshop/wrappers/socketw/receiver.py +144 -102
  192. atomicshop/wrappers/socketw/sender.py +65 -35
  193. atomicshop/wrappers/socketw/sni.py +334 -165
  194. atomicshop/wrappers/socketw/socket_base.py +134 -0
  195. atomicshop/wrappers/socketw/socket_client.py +137 -95
  196. atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
  197. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  198. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  199. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  200. atomicshop/wrappers/sysmonw.py +157 -0
  201. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  202. atomicshop/wrappers/win_auditw.py +189 -0
  203. atomicshop/wrappers/winregw/__init__.py +0 -0
  204. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  205. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  206. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
  207. atomicshop-3.10.5.dist-info/RECORD +306 -0
  208. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  209. atomicshop/_basics_temp.py +0 -101
  210. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  211. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  212. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  213. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  214. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  215. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  216. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  217. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  218. atomicshop/addons/package_setup/Setup.cmd +0 -7
  219. atomicshop/addons/process_list/compile.cmd +0 -2
  220. atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  221. atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  222. atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  223. atomicshop/archiver/_search_in_zip.py +0 -189
  224. atomicshop/archiver/archiver.py +0 -34
  225. atomicshop/archiver/search_in_archive.py +0 -250
  226. atomicshop/archiver/sevenz_app_w.py +0 -86
  227. atomicshop/archiver/sevenzs.py +0 -44
  228. atomicshop/archiver/zips.py +0 -293
  229. atomicshop/etw/dns_trace.py +0 -118
  230. atomicshop/etw/etw.py +0 -61
  231. atomicshop/file_types.py +0 -24
  232. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  233. atomicshop/mitm/initialize_mitm_server.py +0 -240
  234. atomicshop/monitor/checks/hash.py +0 -44
  235. atomicshop/monitor/checks/hash_checks/file.py +0 -55
  236. atomicshop/monitor/checks/hash_checks/url.py +0 -62
  237. atomicshop/pbtkmultifile_argparse.py +0 -88
  238. atomicshop/permissions.py +0 -110
  239. atomicshop/process_poller.py +0 -237
  240. atomicshop/script_as_string_processor.py +0 -38
  241. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  242. atomicshop/ssh_scripts/process_from_port.py +0 -27
  243. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  244. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  245. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  246. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  247. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  248. atomicshop/wrappers/ffmpegw.py +0 -125
  249. atomicshop/wrappers/loggingw/checks.py +0 -20
  250. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  251. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  252. atomicshop/wrappers/socketw/base.py +0 -59
  253. atomicshop/wrappers/socketw/get_process.py +0 -107
  254. atomicshop/wrappers/wslw.py +0 -191
  255. atomicshop-2.11.47.dist-info/RECORD +0 -251
  256. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  257. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  258. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  259. /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  260. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  261. /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
  262. /atomicshop/{archiver → etws}/__init__.py +0 -0
  263. /atomicshop/{etw → etws/traces}/__init__.py +0 -0
  264. /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
  265. /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
  266. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  267. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  268. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,892 @@
1
+ import threading
2
+ import multiprocessing
3
+ import time
4
+ import datetime
5
+ import os
6
+ import sys
7
+ import logging
8
+ import signal
9
+
10
+ import atomicshop # Importing atomicshop package to get the version of the package.
11
+
12
+ from .. import filesystem, on_exit, print_api, networks, dns
13
+ from ..permissions import permissions
14
+ from .. import python_functions
15
+ from ..wrappers.socketw import socket_wrapper, dns_server, statistics_csv
16
+ from ..wrappers.loggingw import loggingw
17
+ from ..wrappers.ctyping import win_console
18
+ from ..wrappers import netshw
19
+ from ..basics import multiprocesses
20
+
21
+ from .connection_thread_worker import thread_worker_main
22
+ from . import config_static, recs_files
23
+
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
+
33
+ class NetworkSettings:
34
+ """
35
+ Class to store network settings.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ name: str | None = None,
41
+ description: str | None = None,
42
+ interface_index: int | None = None,
43
+ is_dynamic: bool = False,
44
+ ipv4s: list[str] = None,
45
+ ipv6s: list[str] = None,
46
+ ipv4_subnet_masks: list[str] = None,
47
+ ipv6_prefixes: list[str] = None,
48
+ default_gateways: list[str] = None,
49
+ dns_gateways: list[str] = None
50
+ ):
51
+ self.name: str | None = name
52
+ self.description: str | None = description
53
+ self.interface_index: int | None = interface_index
54
+ self.is_dynamic: bool = is_dynamic
55
+ self.ipv4s: list[str] = ipv4s if ipv4s is not None else list()
56
+ self.ipv6s: list[str] = ipv6s if ipv6s is not None else list()
57
+ self.ipv4_subnet_masks: list[str] = ipv4_subnet_masks if ipv4_subnet_masks is not None else list()
58
+ self.ipv6_prefixes: list[str] = ipv6_prefixes if ipv6_prefixes is not None else list()
59
+ self.default_gateways: list[str] = default_gateways if default_gateways is not None else list()
60
+ self.dns_gateways: list[str] = dns_gateways if dns_gateways is not None else list()
61
+
62
+
63
+ # Global variables for setting the network interface to external IPs (eg: 192.168.0.1)
64
+ NETWORK_INTERFACE_SETTINGS: NetworkSettings = NetworkSettings()
65
+ IPS_TO_ASSIGN: list[str] = list()
66
+ MASKS_TO_ASSIGN: list[str] = list()
67
+
68
+ # Global variables for setting the network interface to localhost IPs (eg: 127.0.0.1), Only DNS gateway is set.
69
+ NETWORK_INTERFACE_IS_DYNAMIC: bool = bool()
70
+ NETWORK_INTERFACE_IPV4_ADDRESS_LIST: list[str] = list()
71
+ IS_SET_DNS_GATEWAY: bool = False
72
+
73
+
74
+ # noinspection PyTypeChecker
75
+ RECS_PROCESS_INSTANCE: multiprocessing.Process = None
76
+
77
+
78
+ STATISTICS_LOGGER_NAME: str = 'statistics'
79
+ EXCEPTIONS_CSV_LOGGER_NAME: str = 'exceptions'
80
+ EXCEPTIONS_CSV_LOGGER_HEADER: str = 'time,exception'
81
+ # noinspection PyTypeChecker
82
+ MITM_ERROR_LOGGER: loggingw.ExceptionCsvLogger = None
83
+
84
+ # Create logger queues.
85
+ NETWORK_LOGGER_QUEUE: multiprocessing.Queue = multiprocessing.Queue()
86
+ STATISTICS_CSV_LOGGER_QUEUE: multiprocessing.Queue = multiprocessing.Queue()
87
+ EXCEPTIONS_CSV_LOGGER_QUEUE: multiprocessing.Queue = multiprocessing.Queue()
88
+
89
+ # Create finalization queue for the rec archiving process.
90
+ FINALIZE_RECS_ARCHIVE_QUEUE: multiprocessing.Queue = multiprocessing.Queue()
91
+
92
+
93
+ try:
94
+ win_console.disable_quick_edit()
95
+ except win_console.NotWindowsConsoleError:
96
+ pass
97
+
98
+
99
+ # noinspection PyUnusedLocal
100
+ def _graceful_shutdown(signum, frame):
101
+ exit_cleanup()
102
+
103
+
104
+ def exit_cleanup():
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
+
111
+ netshw.disable_dhcp_static_coexistence(interface_name=NETWORK_INTERFACE_SETTINGS.name)
112
+
113
+ print_api.print_api("Returned network adapter settings...", color='blue')
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
+
130
+ # The process will not be executed if there was an exception in the beginning.
131
+ if RECS_PROCESS_INSTANCE is not None:
132
+ print_api.print_api(f'Recs archive process alive: {RECS_PROCESS_INSTANCE.is_alive()}')
133
+ RECS_PROCESS_INSTANCE.terminate()
134
+ RECS_PROCESS_INSTANCE.join()
135
+
136
+ # Before terminating multiprocessing child processes, we need to put None to all the QueueListeners' queues,
137
+ # so they will stop waiting for new logs and will be able to terminate.
138
+ # Or else we will get a BrokenPipeError exception. This happens for because the QueueListener is waiting for
139
+ # new logs to come through the ".get()" method, but the main process is already terminated.
140
+ NETWORK_LOGGER_QUEUE.put(None)
141
+ # Get all the child processes and terminate them.
142
+ for process in multiprocessing.active_children():
143
+ process.terminate()
144
+ # We need for processes to finish, since there is a logger there that needs to write the last log.
145
+ process.join()
146
+
147
+
148
+ def startup_output(system_logger, script_version: str):
149
+ """
150
+ The function outputs the startup information to the console.
151
+ """
152
+
153
+ # Writing first log.
154
+ system_logger.info("======================================")
155
+ system_logger.info("Server Started.")
156
+ system_logger.info(f"Python Version: {python_functions.get_python_version_string()}")
157
+ system_logger.info(f"Script Version: {script_version}")
158
+ system_logger.info(f"Atomic Workshop Version: {atomicshop.__version__}")
159
+ system_logger.info(f"Log folder: {config_static.LogRec.logs_path}")
160
+ if config_static.LogRec.enable_request_response_recordings_in_logs:
161
+ system_logger.info(f"Recordings folder for Requests/Responses: {config_static.LogRec.recordings_path}")
162
+ system_logger.info(f"Loaded system logger: {system_logger}")
163
+
164
+ # Some 'config.ini' settings logging ===========================================================================
165
+ if config_static.Certificates.default_server_certificate_usage:
166
+ system_logger.info(
167
+ f"Default server certificate usage enabled, if no SNI available: "
168
+ f"{config_static.MainConfig.default_server_certificate_filepath}")
169
+
170
+ if config_static.Certificates.sni_create_server_certificate_for_each_domain:
171
+ system_logger.info(
172
+ f"SNI function certificates creation enabled. Certificates cache: "
173
+ f"{config_static.Certificates.sni_server_certificates_cache_directory}")
174
+ else:
175
+ system_logger.info(f"SNI function certificates creation disabled.")
176
+
177
+ if config_static.Certificates.custom_server_certificate_usage:
178
+ system_logger.info(f"Custom server certificate usage is enabled.")
179
+ system_logger.info(f"Custom Certificate Path: {config_static.Certificates.custom_server_certificate_path}")
180
+
181
+ # If 'custom_private_key_path' field was populated.
182
+ if config_static.Certificates.custom_private_key_path:
183
+ system_logger.info(
184
+ f"Custom Certificate Private Key Path: {config_static.Certificates.custom_private_key_path}")
185
+ else:
186
+ system_logger.info(f"Custom Certificate Private Key Path wasn't provided in [advanced] section. "
187
+ f"Assuming the private key is inside the certificate file.")
188
+
189
+ # === Engine logging ===========================================================================================
190
+ # Printing the parsers using "start=1" for index to start counting from "1" and not "0"
191
+ system_logger.info("Imported engine info.")
192
+ print_api.print_api(f"[*] Found Engines:", logger=system_logger)
193
+ print_api.print_api( f"-------------------------", logger=system_logger)
194
+
195
+ if not config_static.ENGINES_LIST:
196
+ message = \
197
+ f"No engines found, the TCP server will use general response engine for all the input domains."
198
+ print_api.print_api(message, color="blue", logger=system_logger)
199
+
200
+ for index, engine in enumerate(config_static.ENGINES_LIST, start=1):
201
+ message = f"[*] {index}: {engine.engine_name} | {engine.domain_list}"
202
+ print_api.print_api(message, logger=system_logger)
203
+
204
+ message = (f"[*] Modules: {engine.parser_class_object.__name__}, "
205
+ f"{engine.responder_class_object.__name__}, "
206
+ f"{engine.recorder_class_object.__name__}")
207
+ print_api.print_api(message, logger=system_logger)
208
+ print_api.print_api(f"[*] Domains: {list(engine.domain_target_dict.keys())}", logger=system_logger)
209
+ dns_targets: list = list()
210
+ for domain, ip_port in engine.domain_target_dict.items():
211
+ dns_targets.append(ip_port['ip'])
212
+ print_api.print_api(f"[*] DNS Targets: {dns_targets}", logger=system_logger)
213
+
214
+ if engine.on_port_connect:
215
+ print_api.print_api(f"[*] Connect Ports to IPs: {list(engine.on_port_connect.values())}", logger=system_logger)
216
+ print_api.print_api(f"[*] Connect Ports to IPs Targets: {list(engine.port_target_dict.values())}", logger=system_logger)
217
+
218
+ print_api.print_api("-------------------------", logger=system_logger)
219
+
220
+ # print_api.print_api(f"[*] TCP Listening Interfaces: {engine.tcp_listening_address_list}", logger=system_logger)
221
+
222
+ if config_static.DNSServer.is_enabled:
223
+ print_api.print_api("DNS Server is enabled.", logger=system_logger)
224
+
225
+ # If engines were found and dns is set to route by the engine domains.
226
+ if config_static.ENGINES_LIST and config_static.DNSServer.resolve_by_engine:
227
+ print_api.print_api(
228
+ "Engine domains will be routed by the DNS server to Built-in TCP Server.", logger=system_logger)
229
+ # If engines were found, but the dns isn't set to route to engines.
230
+ elif config_static.ENGINES_LIST and not config_static.DNSServer.resolve_by_engine:
231
+ message = f"[*] Engines found, but the DNS routing is set not to use them for routing."
232
+ print_api.print_api(message, color="yellow", logger=system_logger)
233
+
234
+ if config_static.DNSServer.resolve_all_domains_to_ipv4_enable:
235
+ print_api.print_api(
236
+ f"All domains will be routed by the DNS server to Built-in TCP Server: [{config_static.DNSServer.target_ipv4}]",
237
+ color="blue", logger=system_logger)
238
+
239
+ if config_static.DNSServer.resolve_regular_pass_thru:
240
+ print_api.print_api(
241
+ "Regular DNS resolving is enabled. Built-in TCP server will not be routed to",
242
+ logger=system_logger, color="yellow")
243
+ else:
244
+ print_api.print_api("DNS Server is disabled.", logger=system_logger, color="yellow")
245
+
246
+ if config_static.TCPServer.is_enabled:
247
+ print_api.print_api("TCP Server is enabled.", logger=system_logger)
248
+ else:
249
+ print_api.print_api("TCP Server is disabled.", logger=system_logger, color="yellow")
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)
258
+
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:
282
+ """
283
+ Function to get the IPv4 addresses for the default network adapter to set them to the adapter.
284
+ """
285
+
286
+ # Create a list of all the domains in all the engines.
287
+ domains_to_create_ips_for: list[str] = list()
288
+ ports_to_create_ips_for: list[str] = list()
289
+ for engine in config_static.ENGINES_LIST:
290
+ domains_to_create_ips_for += list(engine.domain_target_dict.keys())
291
+ ports_to_create_ips_for += list(engine.on_port_connect.keys())
292
+
293
+ engine_ips: list[str] = list()
294
+ create_ips: int = len(domains_to_create_ips_for) + len(ports_to_create_ips_for)
295
+
296
+ # Get network interface name.
297
+ interface_name: str = _get_interface_name()
298
+ if interface_name is None:
299
+ return 1
300
+
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")
304
+
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")
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:
350
+ # Generate the IPs for the domains.
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,
354
+ number_of_ips=create_ips,
355
+ simulate_only=True)
356
+
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)}"
370
+
371
+ # Add the ips to engines.
372
+ for engine in config_static.ENGINES_LIST:
373
+ for domain in engine.domain_target_dict.keys():
374
+ # If the domain is in the list of domains to create IPs for, add the IP to the engine.
375
+ if domain in domains_to_create_ips_for:
376
+ engine.domain_target_dict[domain]['ip'] = engine_ips.pop(0)
377
+ for port in engine.on_port_connect.keys():
378
+ # If the port is in the list of ports to create IPs for, add the IP to the engine.
379
+ if port in ports_to_create_ips_for:
380
+ engine.port_target_dict[port]['ip'] = engine_ips.pop(0)
381
+
382
+ return 0
383
+
384
+
385
+ def mitm_server(config_file_path: str, script_version: str) -> int:
386
+ on_exit.register_exit_handler(exit_cleanup, at_exit=False, kill_signal=False)
387
+
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")
394
+ return 1
395
+
396
+ print_api.print_api("[*] Version Check PASSED.", color="green")
397
+
398
+ # Import the configuration file.
399
+ rc: int = config_static.load_config(config_file_path, print_kwargs=dict(stdout=False))
400
+ if rc != 0:
401
+ return rc
402
+
403
+ # Get the IPs that will be set for the adapter and fill the engine configuration with the IPs.
404
+ rc: int = get_ipv4s_for_tcp_server()
405
+ if rc != 0:
406
+ return rc
407
+
408
+ global MITM_ERROR_LOGGER
409
+ MITM_ERROR_LOGGER = loggingw.ExceptionCsvLogger(
410
+ logger_name=EXCEPTIONS_CSV_LOGGER_NAME,
411
+ directory_path=config_static.LogRec.logs_path,
412
+ log_queue=EXCEPTIONS_CSV_LOGGER_QUEUE,
413
+ add_queue_handler_start_listener_multiprocessing=True,
414
+ )
415
+
416
+ # Create folders.
417
+ filesystem.create_directory(config_static.LogRec.logs_path)
418
+
419
+ if config_static.Certificates.sni_get_server_certificate_from_server_socket:
420
+ filesystem.create_directory(
421
+ config_static.Certificates.sni_server_certificate_from_server_socket_download_directory)
422
+
423
+ network_logger_name = config_static.MainConfig.LOGGER_NAME
424
+
425
+ # Start the network logger and its queue listener.
426
+ _ = loggingw.create_logger(
427
+ logger_name=network_logger_name,
428
+ log_queue=NETWORK_LOGGER_QUEUE,
429
+ start_queue_listener_multiprocess_add_queue_handler=True,
430
+ file_path=f'{config_static.LogRec.logs_path}{os.sep}{network_logger_name}.txt',
431
+ add_stream=True,
432
+ add_timedfile=True,
433
+ formatter_streamhandler='DEFAULT',
434
+ formatter_filehandler='DEFAULT',
435
+ backupCount=config_static.LogRec.store_logs_for_x_days)
436
+
437
+ """
438
+ # Create this in other multiprocesses (You need to pass only the logger_name and log_queue to the other process):
439
+ logger = loggingw.create_logger(
440
+ logger_name=logger_name,
441
+ add_queue_handler=True,
442
+ log_queue=NETWORK_LOGGER_QUEUE
443
+ )
444
+ """
445
+
446
+ # Initiate Listener logger, which is a child of network logger, so he uses the same settings and handlers
447
+ system_logger: logging.Logger = loggingw.get_logger_with_level(f'{network_logger_name}.system')
448
+
449
+ if config_static.LogRec.enable_request_response_recordings_in_logs:
450
+ filesystem.create_directory(config_static.LogRec.recordings_path)
451
+ # Compress recordings of the previous days if there are any.
452
+ global RECS_PROCESS_INSTANCE
453
+ RECS_PROCESS_INSTANCE = recs_files.recs_archiver_in_process(
454
+ config_static.LogRec.recordings_path,
455
+ logging_queue=NETWORK_LOGGER_QUEUE,
456
+ logger_name=network_logger_name,
457
+ finalize_output_queue=FINALIZE_RECS_ARCHIVE_QUEUE
458
+ )
459
+
460
+ archiver_result = FINALIZE_RECS_ARCHIVE_QUEUE.get()
461
+ if isinstance(archiver_result, Exception):
462
+ print_api.print_api(
463
+ f"Error while archiving recordings: {archiver_result}",
464
+ error_type=True, color="red", logger=system_logger, logger_method='critical')
465
+ # Wait for the message to be printed and saved to file.
466
+ time.sleep(1)
467
+ # network_logger_queue_listener.stop()
468
+ return 1
469
+
470
+ # Logging Startup information.
471
+ startup_output(system_logger, script_version)
472
+
473
+ multiprocess_list: list[multiprocessing.Process] = list()
474
+ # noinspection PyTypeHints
475
+ is_ready_multiprocessing_event_list: list[multiprocessing.Event] = list()
476
+
477
+ # === Initialize TCP Server ====================================================================================
478
+ if config_static.TCPServer.is_enabled:
479
+ # Get the default network adapter configuration and set the one from config.
480
+ # We set the virtual IPs in the network adapter here, so the server multiprocessing processes can listen on them.
481
+ setting_result: int = _add_virtual_ips_set_default_dns_gateway(system_logger)
482
+ if setting_result != 0:
483
+ print_api.print_api("Failed to set the default DNS gateway OR Virtual IPs.", error_type=True, color="red",
484
+ logger=system_logger)
485
+ # Wait for the message to be printed and saved to file.
486
+ time.sleep(1)
487
+ return setting_result
488
+
489
+ # Start statistics CSV Queue listener and the logger.
490
+ _ = statistics_csv.StatisticsCSVWriter(
491
+ directory_path=config_static.LogRec.logs_path,
492
+ log_queue=STATISTICS_CSV_LOGGER_QUEUE,
493
+ add_queue_handler_start_listener_multiprocessing=True)
494
+
495
+ no_engine_usage_enable: bool = config_static.TCPServer.no_engines_usage_to_listen_addresses_enable
496
+ no_engines_listening_address_list: list[str] = config_static.TCPServer.no_engines_listening_address_list
497
+
498
+ # If engines were passed, we will use the listening addresses from the engines.
499
+ listening_interfaces: list[dict] = list()
500
+ if not no_engine_usage_enable:
501
+ for engine in config_static.ENGINES_LIST:
502
+ # Combine the domain and port dicts.
503
+ connection_dict: dict = {**engine.domain_target_dict, **engine.port_target_dict}
504
+
505
+ # Start all the regular listening interfaces.
506
+ for domain_or_port, ip_port_dict in connection_dict.items():
507
+ current_interface_dict: dict = {
508
+ 'engine': engine,
509
+ 'process_name': f'tcp_server-{engine.engine_name}-{domain_or_port}',
510
+ 'ip': ip_port_dict['ip'],
511
+ 'port': int(ip_port_dict['port'])
512
+ }
513
+ listening_interfaces.append(current_interface_dict)
514
+ else:
515
+ # If no engines were passed, we will use the listening addresses from the configuration.
516
+ for address in no_engines_listening_address_list:
517
+ listening_ip_address, port_str = address.split(':')
518
+ current_interface_dict: dict = {
519
+ 'engine': None, # No engine for this address.
520
+ 'process_name': f'tcp_server-{listening_ip_address}_{port_str}',
521
+ 'ip': listening_ip_address,
522
+ 'port': int(port_str)
523
+ }
524
+ listening_interfaces.append(current_interface_dict)
525
+
526
+ # Starting the TCP server multiprocessing processes.
527
+ for interface_dict in listening_interfaces:
528
+ socket_wrapper_kwargs: dict = dict(
529
+ ip_address=interface_dict['ip'],
530
+ port=interface_dict['port'],
531
+ engine=interface_dict['engine'],
532
+ ca_certificate_name=config_static.MainConfig.ca_certificate_name,
533
+ ca_certificate_filepath=config_static.MainConfig.ca_certificate_filepath,
534
+ ca_certificate_crt_filepath=config_static.MainConfig.ca_certificate_crt_filepath,
535
+ install_ca_certificate_to_root_store=config_static.Certificates.install_ca_certificate_to_root_store,
536
+ uninstall_unused_ca_certificates_with_ca_certificate_name=(
537
+ config_static.Certificates.uninstall_unused_ca_certificates_with_mitm_ca_name),
538
+ default_server_certificate_usage=config_static.Certificates.default_server_certificate_usage,
539
+ default_server_certificate_name=config_static.MainConfig.default_server_certificate_name,
540
+ default_certificate_domain_list=config_static.Certificates.domains_all_times,
541
+ default_server_certificate_directory=config_static.MainConfig.SCRIPT_DIRECTORY,
542
+ sni_use_default_callback_function=True,
543
+ sni_use_default_callback_function_extended=True,
544
+ sni_add_new_domains_to_default_server_certificate=(
545
+ config_static.Certificates.sni_add_new_domains_to_default_server_certificate),
546
+ sni_create_server_certificate_for_each_domain=(
547
+ config_static.Certificates.sni_create_server_certificate_for_each_domain),
548
+ sni_server_certificates_cache_directory=(
549
+ config_static.Certificates.sni_server_certificates_cache_directory),
550
+ sni_get_server_certificate_from_server_socket=(
551
+ config_static.Certificates.sni_get_server_certificate_from_server_socket),
552
+ sni_server_certificate_from_server_socket_download_directory=(
553
+ config_static.Certificates.sni_server_certificate_from_server_socket_download_directory),
554
+ custom_server_certificate_usage=config_static.Certificates.custom_server_certificate_usage,
555
+ custom_server_certificate_path=config_static.Certificates.custom_server_certificate_path,
556
+ custom_private_key_path=config_static.Certificates.custom_private_key_path,
557
+ get_process_name=config_static.ProcessName.get_process_name,
558
+ ssh_user=config_static.ProcessName.ssh_user,
559
+ ssh_pass=config_static.ProcessName.ssh_pass,
560
+ ssh_script_to_execute=config_static.ProcessName.ssh_script_to_execute,
561
+ logs_directory=config_static.LogRec.logs_path,
562
+ logger_name=network_logger_name,
563
+ logger_queue=NETWORK_LOGGER_QUEUE,
564
+ statistics_logger_name=STATISTICS_LOGGER_NAME,
565
+ statistics_logger_queue=STATISTICS_CSV_LOGGER_QUEUE,
566
+ exceptions_logger_name=EXCEPTIONS_CSV_LOGGER_NAME,
567
+ exceptions_logger_queue=EXCEPTIONS_CSV_LOGGER_QUEUE,
568
+ forwarding_dns_service_ipv4_list___only_for_localhost=[config_static.DNSServer.forwarding_dns_service_ipv4],
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,
572
+ print_kwargs=dict(stdout=False)
573
+ )
574
+
575
+ # noinspection PyTypeHints
576
+ is_tcp_process_ready: multiprocessing.Event = multiprocessing.Event()
577
+ is_ready_multiprocessing_event_list.append(is_tcp_process_ready)
578
+
579
+ tcp_process: multiprocessing.Process = multiprocessing.Process(
580
+ target=_create_tcp_server_process,
581
+ name=interface_dict['process_name'],
582
+ args=(
583
+ socket_wrapper_kwargs,
584
+ config_file_path,
585
+ network_logger_name,
586
+ NETWORK_LOGGER_QUEUE,
587
+ is_tcp_process_ready
588
+ ),
589
+ daemon=True
590
+ )
591
+ tcp_process.start()
592
+ multiprocess_list.append(tcp_process)
593
+
594
+ # Compress recordings each day in a separate process.
595
+ recs_archiver_thread = threading.Thread(target=_loop_at_midnight_recs_archive, args=(network_logger_name,), daemon=True)
596
+ recs_archiver_thread.start()
597
+
598
+ # Check that all the multiprocesses are ready.
599
+ if not _wait_for_events(is_ready_multiprocessing_event_list, timeout=30, system_logger=system_logger):
600
+ return 1
601
+ # === EOF Initialize TCP Server ====================================================================================
602
+
603
+ # === Initialize DNS module ========================================================================================
604
+ if config_static.DNSServer.is_enabled:
605
+ # noinspection PyTypeHints
606
+ is_dns_process_ready: multiprocessing.Event = multiprocessing.Event()
607
+
608
+ dns_server_kwargs: dict = dict(
609
+ listening_address=config_static.DNSServer.listening_address,
610
+ log_directory_path=config_static.LogRec.logs_path,
611
+ backupCount_log_files_x_days=config_static.LogRec.store_logs_for_x_days,
612
+ forwarding_dns_service_ipv4=config_static.DNSServer.forwarding_dns_service_ipv4,
613
+ forwarding_dns_service_port=config_static.DNSServer.forwarding_dns_service_port,
614
+ resolve_by_engine=(
615
+ config_static.DNSServer.resolve_by_engine, config_static.ENGINES_LIST),
616
+ resolve_regular_pass_thru=config_static.DNSServer.resolve_regular_pass_thru,
617
+ resolve_all_domains_to_ipv4=(
618
+ config_static.DNSServer.resolve_all_domains_to_ipv4_enable, config_static.DNSServer.target_ipv4),
619
+ offline_mode=config_static.MainConfig.is_offline,
620
+ cache_timeout_minutes=config_static.DNSServer.cache_timeout_minutes,
621
+ logging_queue=NETWORK_LOGGER_QUEUE,
622
+ logger_name=network_logger_name,
623
+ is_ready_multiprocessing=is_dns_process_ready
624
+ )
625
+
626
+ dns_process = multiprocessing.Process(
627
+ target=dns_server.start_dns_server_multiprocessing_worker,
628
+ kwargs=dns_server_kwargs,
629
+ name="dns_server",
630
+ daemon=True
631
+ )
632
+ dns_process.start()
633
+
634
+ multiprocess_list.append(dns_process)
635
+
636
+ # Check that the multiprocess is ready.
637
+ if not _wait_for_events([is_dns_process_ready], timeout=30, system_logger=system_logger):
638
+ return 1
639
+ # === EOF Initialize DNS module ====================================================================================
640
+
641
+ if config_static.DNSServer.is_enabled or config_static.TCPServer.is_enabled:
642
+ print_api.print_api("The Server is Ready for Operation!", color="green", logger=system_logger)
643
+ print_api.print_api("Press [Ctrl]+[C] to stop.", color='blue', logger=system_logger)
644
+
645
+ # Get al the queue listener processes (basically this is not necessary, since they're 'daemons', but this is a good practice).
646
+ multiprocess_list.extend(loggingw.get_listener_processes())
647
+
648
+ # This is needed for Keyboard Exception.
649
+ while True:
650
+ # If it is the first cycle and some process had an exception, we will exist before printing that the
651
+ # server is ready for operation.
652
+ result, process_name = multiprocesses.is_process_crashed(multiprocess_list)
653
+ # If result is None, all processes are still alive.
654
+ if result is not None:
655
+ # If result is 0 or 1, we can exit the loop.
656
+ print(f"Process [{process_name}] finished with exit code {result}.")
657
+ break
658
+
659
+ time.sleep(1)
660
+
661
+ return 0
662
+
663
+
664
+ # noinspection PyTypeHints
665
+ def _create_tcp_server_process(
666
+ socket_wrapper_kwargs: dict,
667
+ config_file_path: str,
668
+ network_logger_name: str,
669
+ network_logger_queue: multiprocessing.Queue,
670
+ is_tcp_process_ready: multiprocessing.Event
671
+ ):
672
+ # Load config_static per process, since it is not shared between processes.
673
+ config_static.load_config(config_file_path, print_kwargs=dict(stdout=False))
674
+
675
+ # First create a network logger with a queue handler.
676
+ _ = loggingw.create_logger(
677
+ logger_name=network_logger_name,
678
+ add_queue_handler=True,
679
+ log_queue=network_logger_queue,
680
+ )
681
+
682
+ # Now get the system logger and listener loggers.
683
+ system_logger: logging.Logger = loggingw.get_logger_with_level(f'{network_logger_name}.system')
684
+ # If the listener logger is available in current process, the SocketWrapper will use it.
685
+ _ = loggingw.get_logger_with_level(f'{network_logger_name}.listener')
686
+
687
+ try:
688
+ # noinspection PyTypeChecker
689
+ socket_wrapper_instance = socket_wrapper.SocketWrapper(**socket_wrapper_kwargs)
690
+ except socket_wrapper.SocketWrapperPortInUseError as e:
691
+ print_api.print_api(e, error_type=True, color="red", logger=system_logger)
692
+ # Wait for the message to be printed and saved to file.
693
+ time.sleep(1)
694
+ # network_logger_queue_listener.stop()
695
+ sys.exit(1)
696
+ except socket_wrapper.SocketWrapperConfigurationValuesError as e:
697
+ print_api.print_api(e, error_type=True, color="red", logger=system_logger, logger_method='critical')
698
+ # Wait for the message to be printed and saved to file.
699
+ time.sleep(1)
700
+ # network_logger_queue_listener.stop()
701
+ sys.exit(1)
702
+
703
+ try:
704
+ socket_wrapper_instance.start_listening_socket(
705
+ callable_function=thread_worker_main, callable_args=(config_static,))
706
+ except OSError as e:
707
+ if e.winerror == 10022: # Invalid argument error on Windows.
708
+ message = (
709
+ str(f"{e}\n"
710
+ f"Check that the IP address and port are correct: {socket_wrapper_kwargs['ip_address']}:{socket_wrapper_kwargs['port']}\n"))
711
+ print_api.print_api(message, error_type=True, color="red", logger=system_logger, logger_method='critical')
712
+ # Wait for the message to be printed and saved to file.
713
+ time.sleep(1)
714
+ # network_logger_queue_listener.stop()
715
+ sys.exit(1)
716
+ else:
717
+ raise e
718
+
719
+ # Notify that the TCP server is ready.
720
+ is_tcp_process_ready.set()
721
+
722
+ try:
723
+ while True:
724
+ time.sleep(1) # Keep the process alive, since the listening socket is in an infinite loop.
725
+ except KeyboardInterrupt:
726
+ sys.exit(0)
727
+
728
+
729
+ # noinspection PyTypeHints
730
+ def _wait_for_events(
731
+ events: list[multiprocessing.Event],
732
+ timeout: int = 30,
733
+ system_logger: logging.Logger = None
734
+ ) -> bool:
735
+ """
736
+ Wait for all events in the list to be set.
737
+
738
+ :param events: List of multiprocessing.Event objects.
739
+ :param timeout: Maximum time to wait for all events to be set.
740
+ :return: True if all events are set, False if timeout occurs.
741
+ """
742
+
743
+ # Check that all the multiprocesses are ready.
744
+ for event in events:
745
+ if not event.wait(timeout=timeout):
746
+ print_api.print_api("One of the processes didn't start in time.", error_type=True, color="red",
747
+ logger=system_logger)
748
+ # Wait for the message to be printed and saved to file.
749
+ time.sleep(1)
750
+ return False
751
+
752
+ return True
753
+
754
+
755
+ def _add_virtual_ips_set_default_dns_gateway(system_logger: logging.Logger) -> int:
756
+ """
757
+ The function reads the current DNS gateway setting and sets the new one.
758
+
759
+ :param system_logger: The logger to use for logging messages.
760
+ :return: 0 if successful, 1 if there was an error.
761
+ """
762
+
763
+ # This setting is needed only for the dns gateways configurations from the main config on localhost.
764
+ set_local_dns_gateway: bool = False
765
+ # Set the default gateway if specified.
766
+ if config_static.MainConfig.set_default_dns_gateway:
767
+ dns_gateway_server_list = config_static.MainConfig.set_default_dns_gateway
768
+ set_local_dns_gateway = True
769
+ elif config_static.MainConfig.set_default_dns_gateway_to_localhost:
770
+ dns_gateway_server_list = [config_static.MainConfig.default_localhost_dns_gateway_ipv4]
771
+ set_local_dns_gateway = True
772
+ elif config_static.MainConfig.set_default_dns_gateway_to_network_interface_ipv4:
773
+ dns_gateway_server_list = [NETWORK_INTERFACE_SETTINGS.ipv4s[0]]
774
+ set_local_dns_gateway = True
775
+ else:
776
+ dns_gateway_server_list = NETWORK_INTERFACE_SETTINGS.dns_gateways
777
+
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:
803
+ # Change the adapter settings and add the virtual IPs.
804
+ try:
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)
819
+ except (PermissionError, TimeoutError) as e:
820
+ print_api.print_api(e, error_type=True, color="red", logger=system_logger)
821
+ # Wait for the message to be printed and saved to file.
822
+ time.sleep(1)
823
+ # network_logger_queue_listener.stop()
824
+ return 1
825
+
826
+ return 0
827
+
828
+
829
+ def _loop_at_midnight_recs_archive(network_logger_name):
830
+ previous_date = datetime.datetime.now().strftime('%d')
831
+ while True:
832
+ # Get current time.
833
+ current_date = datetime.datetime.now().strftime('%d')
834
+ # If it's midnight, start the archiving process.
835
+ if current_date != previous_date:
836
+ if config_static.LogRec.enable_request_response_recordings_in_logs:
837
+ global RECS_PROCESS_INSTANCE
838
+ RECS_PROCESS_INSTANCE = recs_files.recs_archiver_in_process(
839
+ config_static.LogRec.recordings_path,
840
+ logging_queue=NETWORK_LOGGER_QUEUE,
841
+ logger_name=network_logger_name,
842
+ finalize_output_queue=FINALIZE_RECS_ARCHIVE_QUEUE
843
+ )
844
+
845
+ archiver_result = FINALIZE_RECS_ARCHIVE_QUEUE.get()
846
+ if isinstance(archiver_result, Exception):
847
+ MITM_ERROR_LOGGER.write(archiver_result)
848
+
849
+ # Update the previous date.
850
+ previous_date = current_date
851
+ # Sleep for 1 minute.
852
+ time.sleep(60)
853
+
854
+
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
+
879
+ try:
880
+ # Main function should return integer with error code, 0 is successful.
881
+ return mitm_server(config_file_path, script_version)
882
+ except KeyboardInterrupt:
883
+ print_api.print_api("Server Stopped by [KeyboardInterrupt].", color='blue')
884
+ exit_cleanup()
885
+ return 0
886
+ except Exception as e:
887
+ print_api.print_api('', error_type=True, color='red', traceback_string=True)
888
+ # The error logger will not be initiated if there will be a problem with configuration file or checks.
889
+ if MITM_ERROR_LOGGER is not None:
890
+ MITM_ERROR_LOGGER.write(e)
891
+ exit_cleanup()
892
+ return 1