atomicshop 2.15.11__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 (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,15 @@
1
- # v1.0.0 - 26.03.2023 14:00
2
1
  from ...shared_functions import create_custom_logger
3
2
  from ...message import ClientMessage
4
3
 
5
4
 
6
- # Class that parses the message received from client.
7
5
  class ParserParent:
8
- # Initializing the logger in the "class variable" section will leave the instance of the logger initiated
9
- # and the rest of the instances of the class will use the same logger.
10
- # It is not in the "__init__" section, so it's not going to be initiated again.
11
- # The name of the logger using "__name__" variable, which is the full name of the module package.
12
- # Example: classes.parsers.parser_1_reference_general
13
-
14
- # The code outside the functions will be executed during import of the module. When initializing a class
15
- # in the script these lines will not be called again, only the "init" function.
16
- logger = create_custom_logger()
17
-
6
+ """Class that parses the message received from client."""
18
7
  def __init__(self, class_client_message: ClientMessage):
19
8
  self.class_client_message: ClientMessage = class_client_message
9
+ self.logger = create_custom_logger()
20
10
 
21
11
  def parse(self):
22
12
  # This is general parser, so we don't parse anything and 'request_body_parsed' gets empty byte string.
23
- self.class_client_message.request_body_parsed = b''
13
+ self.class_client_message.request_custom_parsed = b''
24
14
 
25
- try:
26
- self.logger.info(f"Parsed: {self.class_client_message.request_body_parsed[0: 100]}...")
27
- except Exception:
28
- pass
15
+ self.logger.info(f"Parsed: {self.class_client_message.request_custom_parsed[0: 100]}...")
@@ -1,35 +1,45 @@
1
1
  import os
2
2
  from datetime import datetime
3
+ import queue
4
+ import threading
5
+ from pathlib import Path
3
6
 
4
- from ...shared_functions import build_module_names, create_custom_logger, get_json
5
- from ...message import ClientMessage
6
- from ....urls import url_parser
7
+ from ...shared_functions import build_module_names, create_custom_logger
8
+ from ... import message, recs_files
7
9
  from .... import filesystem
10
+ from ....file_io import jsons
11
+ from ....print_api import print_api
8
12
 
9
13
 
10
14
  # The class that is responsible for Recording Requests / Responses.
11
15
  class RecorderParent:
12
- # The code outside the functions will be executed during import of the module. When initializing a class
13
- # in the script these lines will not be called again, only the "init" function.
14
- logger = create_custom_logger()
15
16
 
16
- def __init__(self, class_client_message: ClientMessage, record_path: str):
17
- self.class_client_message: ClientMessage = class_client_message
17
+ # noinspection PyTypeChecker
18
+ def __init__(self, record_path: str):
18
19
  self.record_path: str = record_path
20
+
19
21
  self.file_extension: str = ".json"
20
22
  self.engine_name = None
21
23
  self.module_name = None
22
24
  self.engine_record_path: str = str()
23
25
  self.record_file_path: str = str()
26
+ self.class_client_message: message.ClientMessage = None
27
+
28
+ self.logger = create_custom_logger()
24
29
 
25
30
  # Get engine name and module name
26
31
  self.get_engine_module()
27
- # Build full file path.
28
- self.build_record_full_file_path()
32
+
33
+ # Build the record path with file name
34
+ self.build_record_path_to_engine()
29
35
 
30
36
  # Create folder.
31
37
  filesystem.create_directory(self.engine_record_path)
32
38
 
39
+ # Initialize a queue to hold messages
40
+ self.message_queue: queue.Queue = queue.Queue()
41
+ self.recorder_worker_thread = None
42
+
33
43
  # "self.__module__" is fully qualified module name: classes.engines.ENGINE-NAME.MODULE-NAME
34
44
  def get_engine_module(self):
35
45
  _, self.engine_name, self.module_name = build_module_names(self.__module__)
@@ -38,33 +48,10 @@ class RecorderParent:
38
48
  self.engine_record_path = self.record_path + os.sep + self.engine_name
39
49
 
40
50
  def build_record_full_file_path(self):
41
- # current date and time in object
42
- now = datetime.now()
43
- # Formatting the date and time and converting it to string object
44
- day_time_format: str = now.strftime("%Y_%m_%d-%H_%M_%S_%f")
45
-
46
- # Build the record path with file name
47
- self.build_record_path_to_engine()
48
-
49
- # Define empty 'http_path'.
50
- http_path: str = str()
51
- # If 'self.class_client_message.request_raw_decoded.path' will be undefined, exception will raise.
52
- # This will happen if the message is not HTTP.
53
- try:
54
- # Parse the url to components.
55
- http_path_parsed = url_parser(self.class_client_message.request_raw_decoded.path)
56
- # Get only directories.
57
- http_path_directories_string = '-'.join(http_path_parsed['directories'])
58
- # Add '_' character before 'http_path' to look better on the file name.
59
- http_path = f'_{http_path_directories_string}'
60
- # If 'self.class_client_message.request_raw_decoded.path' is not defined, we'll pass the exception.
61
- except Exception:
62
- pass
63
-
64
51
  # If HTTP Path is not defined, 'http_path' will be empty, and it will not interfere with file name.
65
- self.record_file_path: str = \
66
- self.engine_record_path + os.sep + \
67
- day_time_format + "_" + self.class_client_message.server_name + http_path + self.file_extension
52
+ self.record_file_path: str = (
53
+ f"{self.engine_record_path}{os.sep}th{self.class_client_message.thread_id}_"
54
+ f"{self.class_client_message.server_name}{self.file_extension}")
68
55
 
69
56
  def convert_messages(self):
70
57
  """
@@ -74,26 +61,96 @@ class RecorderParent:
74
61
  # We need to check that the values that we want to convert aren't empty or 'None'.
75
62
  if self.class_client_message.request_raw_bytes:
76
63
  self.class_client_message.request_raw_hex = self.class_client_message.request_raw_bytes.hex()
77
- if self.class_client_message.response_list_of_raw_bytes:
78
- # We checked that the list isn't empty, now we check if the first value is not empty, since if we
79
- # check it in the same expression as check for list is empty, we will get an exception since
80
- # the list is empty.
81
- if self.class_client_message.response_list_of_raw_bytes[0]:
82
- for response_raw_bytes in self.class_client_message.response_list_of_raw_bytes:
83
- self.class_client_message.response_list_of_raw_hex.append(response_raw_bytes.hex())
64
+ if self.class_client_message.response_raw_bytes:
65
+ self.class_client_message.response_raw_hex = self.class_client_message.response_raw_bytes.hex()
66
+
67
+ def record(self, class_client_message: message.ClientMessage):
68
+ self.class_client_message = class_client_message
84
69
 
85
- def record(self):
86
- self.logger.info("Recording Message...")
70
+ # Build full file path if it is not already built.
71
+ if not self.record_file_path:
72
+ self.build_record_full_file_path()
73
+
74
+ # Start the worker thread if it is not already running
75
+ if not self.recorder_worker_thread:
76
+ self.recorder_worker_thread = threading.Thread(
77
+ target=save_message_worker,
78
+ args=(self.record_file_path, self.message_queue, self.logger),
79
+ name=f"{self.class_client_message.thread_process} | Thread-{self.class_client_message.thread_id}-Recorder",
80
+ daemon=True
81
+ )
82
+ self.recorder_worker_thread.start()
83
+
84
+ self.logger.info("Putting Message to Recorder Thread Queue...")
87
85
 
88
86
  # Convert the requests and responses to hex.
89
87
  self.convert_messages()
90
88
  # Get the message in dict / JSON format
91
- record_message = get_json(self.class_client_message)
89
+ record_message_dict: dict = dict(self.class_client_message)
92
90
 
93
- # Since we already dumped the object to dictionary string, we'll just save the object to regular file.
94
- with open(self.record_file_path, 'w') as output_file:
95
- output_file.write(record_message)
96
-
97
- self.logger.info(f"Recorded to file: {self.record_file_path}")
91
+ # Put the message in the queue to be processed by the worker thread
92
+ self.message_queue.put(record_message_dict)
98
93
 
99
94
  return self.record_file_path
95
+
96
+
97
+ def save_message_worker(
98
+ record_file_path_no_date: str,
99
+ message_queue: queue.Queue,
100
+ logger
101
+ ):
102
+ """Worker function to process messages from the queue and write them to the file."""
103
+ original_file_path_object: Path = Path(record_file_path_no_date)
104
+ original_file_stem: str = original_file_path_object.stem
105
+ original_file_extension: str = original_file_path_object.suffix
106
+ original_file_directory: str = str(original_file_path_object.parent)
107
+
108
+ original_datetime_string: str = get_datetime_string()
109
+ previous_date_string: str = get_date_string()
110
+
111
+ record_file_path: str = f'{original_file_directory}{os.sep}{original_datetime_string}_{original_file_stem}{original_file_extension}'
112
+
113
+ while True:
114
+ # Get a message from the queue
115
+ record_message_dict = message_queue.get()
116
+
117
+ # Check for the "stop" signal
118
+ if record_message_dict is None:
119
+ break
120
+
121
+ current_date_string: str = get_date_string()
122
+
123
+ # If current datetime string is different from the original datetime string, we will create a new file path.
124
+ if current_date_string != previous_date_string:
125
+ previous_date_string = current_date_string
126
+ current_datetime_string: str = get_datetime_string()
127
+ record_file_path = f'{original_file_directory}{os.sep}{current_datetime_string}_{original_file_stem}_partof_{original_datetime_string}{original_file_extension}'
128
+
129
+ try:
130
+ jsons.append_to_json(
131
+ record_message_dict, record_file_path, indent=2,
132
+ enable_long_file_path=True, print_kwargs={'logger': logger}
133
+ )
134
+ except TypeError as e:
135
+ print_api(str(e), logger_method="critical", logger=logger)
136
+ raise e
137
+
138
+ logger.info(f"Recorded to file: {record_file_path}")
139
+
140
+ # Indicate task completion
141
+ message_queue.task_done()
142
+
143
+
144
+ def get_datetime_string():
145
+ # current date and time in object
146
+ now = datetime.now()
147
+ # Formatting the date and time and converting it to string object
148
+ day_time_format: str = now.strftime(recs_files.REC_FILE_DATE_TIME_FORMAT)
149
+ return day_time_format
150
+
151
+ def get_date_string():
152
+ # current date and time in object
153
+ now = datetime.now()
154
+ # Formatting the date and time and converting it to string object
155
+ date_format: str = now.strftime(recs_files.REC_FILE_DATE_FORMAT)
156
+ return date_format
@@ -0,0 +1,116 @@
1
+ # Using to convert status code to status phrase / string.
2
+ from http import HTTPStatus
3
+ # Parsing PATH template to variables.
4
+ from pathlib import PurePosixPath
5
+ from urllib.parse import unquote
6
+ # Needed to extract parameters after question mark in URL / Path.
7
+ from urllib.parse import urlparse
8
+ from urllib.parse import parse_qs
9
+
10
+ from ...message import ClientMessage
11
+ from .... import http_parse
12
+ from ....print_api import print_api
13
+
14
+ from atomicshop.mitm.shared_functions import create_custom_logger
15
+
16
+
17
+ class RequesterParent:
18
+ """The class that is responsible for generating request to client based on the received message."""
19
+ def __init__(self):
20
+ self.logger = create_custom_logger()
21
+
22
+ def build_byte_request(
23
+ self,
24
+ http_method: str,
25
+ endpoint: str,
26
+ http_version: str,
27
+ headers: dict,
28
+ body: bytes
29
+ ) -> bytes:
30
+ # noinspection GrazieInspection
31
+ """
32
+ Create genuine request from input parameters.
33
+ ---------------
34
+ The request is built from:
35
+ <http_method> <endpoint> <http_version>\r\n
36
+ Headers1: Value\r\n
37
+ Headers2: Value\r\n
38
+ \r\n # This is meant to end the headers' section
39
+ Body # Request doesn't end with '\r\n\r\n'
40
+ ---------------
41
+ Example for POST request:
42
+ POST /api/v1/resource HTTP/1.1\r\n
43
+ Cache-Control: max-age=86400\r\n
44
+ Content-Type: application/json; charset=utf-8\r\n
45
+ \r\n
46
+ {"id":1,"name":"something"}
47
+ ---------------
48
+ You can create response as:
49
+
50
+ ...POST endpoint/api/1 HTTP/1.1
51
+ header1: value
52
+ header2: value
53
+
54
+ {data: value}...
55
+
56
+ Change 3 dots ("...") to 3 double quotes before "POST" and after "value}".
57
+ This way there will be "\n" added automatically after each line.
58
+ While, the HTTP Client does the parsing of the text and not raw data, most probably it will be parsed well,
59
+ but genuine requests from HTTP sources come with "\r\n" at the end of the line, so better use these for
60
+ better compatibility.
61
+ ---------------
62
+
63
+ :param http_method: HTTP Method of Request, e.g. 'GET', 'POST', etc.
64
+ :param endpoint: Endpoint of Request, e.g. '/api/v1/resource'.
65
+ :param http_version: HTTP Version of Response in HTTP Status line.
66
+ :param headers: HTTP Headers of Response.
67
+ :param body: HTTP body data of Response, bytes.
68
+ :return: bytes of the response.
69
+ """
70
+
71
+ try:
72
+ # CHeck if the HTTP method is valid.
73
+ if http_method not in http_parse.get_request_methods():
74
+ raise ValueError(f"Invalid HTTP Method: {http_method}")
75
+
76
+ # Building the full method endpoint string line and the "\r\n" in the end.
77
+ method_full: str = f"{http_method} {endpoint} {http_version}\r\n"
78
+
79
+ # Defining headers string.
80
+ headers_string: str = str()
81
+ # Adding all the headers to the full response
82
+ for keys, values in headers.items():
83
+ headers_string = headers_string + str(keys) + ": " + str(values) + "\r\n"
84
+
85
+ # Building full string request.
86
+ # 1. Adding full method line.
87
+ # 2. Adding headers string.
88
+ # 3. Adding a line that end headers (with "\r\n").
89
+ # 4. Adding body as byte string.
90
+ request_full_no_body: str = method_full + headers_string + "\r\n"
91
+
92
+ # Converting the HTTP Request string to bytes and adding 'body' bytes.
93
+ request_raw_bytes = request_full_no_body.encode() + body
94
+ except ValueError as exception_object:
95
+ message = \
96
+ f'Create Byte request function error, of the of values provided is not standard: {exception_object}'
97
+ print_api(message, error_type=True, logger=self.logger, logger_method='error', color='red')
98
+
99
+ request_raw_bytes = b''
100
+
101
+ # Parsing the request we created.
102
+ request_parse_test = http_parse.HTTPRequestParse(request_raw_bytes)
103
+ # If there were errors during parsing, it means that something is wrong with response created.
104
+ if request_parse_test.error_message:
105
+ self.logger.error(request_parse_test.error_message)
106
+ request_raw_bytes = b''
107
+ else:
108
+ self.logger.info("Created Valid Byte Request.")
109
+
110
+ return request_raw_bytes
111
+
112
+ def create_request(self, class_client_message: ClientMessage, **kwargs) -> bytes:
113
+ """ This function should be overridden in the child class. """
114
+
115
+ request_bytes: bytes = None
116
+ return request_bytes
@@ -1,4 +1,3 @@
1
- # v1.0.0 - 26.03.2023 14:20
2
1
  # Using to convert status code to status phrase / string.
3
2
  from http import HTTPStatus
4
3
  # Parsing PATH template to variables.
@@ -8,20 +7,31 @@ from urllib.parse import unquote
8
7
  from urllib.parse import urlparse
9
8
  from urllib.parse import parse_qs
10
9
 
11
- from ...shared_functions import create_custom_logger
12
10
  from ...message import ClientMessage
13
11
  from ....http_parse import HTTPResponseParse
14
12
  from ....print_api import print_api
15
13
 
14
+ from atomicshop.mitm.shared_functions import create_custom_logger
16
15
 
17
- # The class that is responsible for generating response to client based on the received message.
18
- class ResponderParent:
19
- # The code outside the functions will be executed during import of the module. When initializing a class
20
- # in the script these lines will not be called again, only the "init" function.
21
- logger = create_custom_logger()
22
16
 
17
+ class ResponderParent:
18
+ """The class that is responsible for generating response to client based on the received message."""
23
19
  def __init__(self):
24
- return
20
+ self.logger = create_custom_logger()
21
+ # engine: initialize_engines.ModuleCategory
22
+ self.engine = None
23
+
24
+ def add_args(
25
+ self,
26
+ # engine: initialize_engines.ModuleCategory
27
+ engine = None
28
+ ):
29
+ """
30
+ Add more arguments to the class.
31
+ This is needed to be backwards compatible and not to change the child class apis.
32
+ """
33
+
34
+ self.engine = engine
25
35
 
26
36
  @staticmethod
27
37
  def get_path_parts(path: str):
@@ -107,51 +117,56 @@ class ResponderParent:
107
117
 
108
118
  return parameter_value
109
119
 
110
- def build_byte_response_and_fill_lists(
111
- self, http_version: str, status_code: int, headers: dict, body: bytes, client_message: ClientMessage):
112
- """
113
- Create genuine response from input parameters.
114
- ---------------
115
- The response is built from:
116
- HTTP-Version HTTP-Status HTTP-Status-String\r\n
117
- Headers1: Value\r\n
118
- Headers2: Value\r\n
119
- \r\n # This is meant to end the headers' section
120
- Body\r\n\r\n # In most cases Body is ended with '\r\n\r\n'
121
- ---------------
122
- Example for 200 response:
123
- HTTP/1.1 200 OK\r\n
124
- Cache-Control: max-age=86400\r\n
125
- Content-Type: application/json; charset=utf-8\r\n
126
- \r\n
127
- {"id":1,"name":"something"}
128
- ---------------
129
- The final response will look like oneline string:
130
- HTTP/1.1 200 OK\r\nCache-Control: max-age=86400\r\n
131
- Content-Type: application/json; charset=utf-8\r\n\r\n{"id":1,"name":"something"}
132
- ---------------
133
- You can create response as:
134
-
135
- ...HTTP/1.1 200 OK
136
- header1: value
137
- header2: value
138
-
139
- {data: value}...
140
-
141
- Change 3 dots ("...") to 3 double quotes before "HTTP" and after "value}".
142
- This way there will be "\n" added automatically after each line.
143
- While, the HTTP Client does the parsing of the text and not raw data, most probably it will be parsed well,
144
- but genuine responses from HTTP sources come with "\r\n" at the end of the line, so better use these for
145
- better compatibility.
146
- ---------------
147
-
148
- :param http_version: HTTP Version of Response in HTTP Status line.
149
- :param status_code: HTTP Status Code of Response in HTTP Status line.
150
- :param headers: HTTP Headers of Response.
151
- :param body: HTTP body data of Response, bytes.
152
- :param client_message: client message class.
153
- :return:
120
+ def build_byte_response(
121
+ self,
122
+ http_version: str,
123
+ status_code: int,
124
+ headers: dict,
125
+ body: bytes
126
+ ) -> bytes:
127
+ # noinspection GrazieInspection
154
128
  """
129
+ Create genuine response from input parameters.
130
+ ---------------
131
+ The response is built from:
132
+ HTTP-Version HTTP-Status HTTP-Status-String\r\n
133
+ Headers1: Value\r\n
134
+ Headers2: Value\r\n
135
+ \r\n # This is meant to end the headers' section
136
+ Body\r\n\r\n # In most cases Body is ended with '\r\n\r\n'
137
+ ---------------
138
+ Example for 200 response:
139
+ HTTP/1.1 200 OK\r\n
140
+ Cache-Control: max-age=86400\r\n
141
+ Content-Type: application/json; charset=utf-8\r\n
142
+ \r\n
143
+ {"id":1,"name":"something"}
144
+ ---------------
145
+ The final response will look like oneline string:
146
+ HTTP/1.1 200 OK\r\nCache-Control: max-age=86400\r\n
147
+ Content-Type: application/json; charset=utf-8\r\n\r\n{"id":1,"name":"something"}
148
+ ---------------
149
+ You can create response as:
150
+
151
+ ...HTTP/1.1 200 OK
152
+ header1: value
153
+ header2: value
154
+
155
+ {data: value}...
156
+
157
+ Change 3 dots ("...") to 3 double quotes before "HTTP" and after "value}".
158
+ This way there will be "\n" added automatically after each line.
159
+ While, the HTTP Client does the parsing of the text and not raw data, most probably it will be parsed well,
160
+ but genuine responses from HTTP sources come with "\r\n" at the end of the line, so better use these for
161
+ better compatibility.
162
+ ---------------
163
+
164
+ :param http_version: HTTP Version of Response in HTTP Status line.
165
+ :param status_code: HTTP Status Code of Response in HTTP Status line.
166
+ :param headers: HTTP Headers of Response.
167
+ :param body: HTTP body data of Response, bytes.
168
+ :return: bytes of the response.
169
+ """
155
170
 
156
171
  try:
157
172
  # Building full status string line and the "\r\n" to the end of the status line
@@ -178,7 +193,6 @@ class ResponderParent:
178
193
  print_api(message, error_type=True, logger=self.logger, logger_method='error', color='red')
179
194
 
180
195
  response_raw_bytes = b''
181
- pass
182
196
 
183
197
  # Parsing the response we created.
184
198
  response_parse_test = HTTPResponseParse(response_raw_bytes)
@@ -186,73 +200,20 @@ class ResponderParent:
186
200
  if response_parse_test.error:
187
201
  self.logger.error(response_parse_test.error)
188
202
  response_raw_bytes = b''
189
- response_decoded = None
190
203
  else:
191
204
  self.logger.info("Created Valid Byte Response.")
192
- response_decoded = response_parse_test.response_raw_decoded
193
205
 
194
- # Add 'response_raw_bytes' and 'response_decoded' to appropriate response lists in 'class_message'.
195
- self.add_response_elements_to_lists(client_message, response_raw_bytes, response_decoded)
206
+ return response_raw_bytes
196
207
 
197
208
  @staticmethod
198
- def add_response_elements_to_lists(class_client_message: ClientMessage, byte_response, decoded_response):
199
- """
200
- Function just adds the byte response to the 'response_list_of_raw_bytes'.
201
- :param class_client_message:
202
- :param byte_response:
203
- :param decoded_response:
204
- :return:
205
- """
209
+ def create_connect_response(class_client_message: ClientMessage):
210
+ """ This function should be overridden in the child class. """
206
211
 
207
- class_client_message.response_list_of_raw_bytes.append(byte_response)
208
- class_client_message.response_list_of_raw_decoded.append(decoded_response)
212
+ _ = class_client_message
213
+ response_bytes_list: list[bytes] = list()
214
+ return response_bytes_list
209
215
 
210
216
  def create_response(self, class_client_message: ClientMessage):
211
- # noinspection GrazieInspection
212
- """
213
- Function to create Response based on ClientMessage and its Request.
214
-
215
- :param class_client_message: contains request and other parameters to help creating response.
216
- :return: "class_client_message.response_list_of_raw_bytes" is populated with list of responses in bytes.
217
- -----------------------------------
218
- # Remember that 'response_list_of_raw_bytes' is a list object that will contain byte response/s.
219
-
220
- # Example of creating 'response_list_of_raw_bytes' using 'build_byte_response' function:
221
- class_client_message.response_list_of_raw_bytes.append(
222
- self.build_byte_response(
223
- http_version=class_client_message.request_raw_decoded.request_version,
224
- status_code=200,
225
- headers=response_headers,
226
- body=b''
227
- )
228
- )
229
-
230
- # Or you can use the 'add_byte_response_to_response_list' function that will do it for you:
231
- byte_response = self.build_byte_response(
232
- http_version=class_client_message.request_raw_decoded.request_version,
233
- status_code=200,
234
- headers=response_headers,
235
- body=b''
236
- )
237
- self.add_byte_response_to_response_list(class_client_message, byte_response)
238
- -----------------------------------
239
- # Example of extracting variables from URL PATH based on custom PATH TEMPLATE:
240
- # (more examples in 'self.extract_variables_from_path_template' function description)
241
- template_path: str = "/hithere/<variable1>/else/<variable2>/tested/"
242
- path_variables: dict = extract_variables_from_path_template(
243
- path=class_client_message.request_raw_decoded.path,
244
- template_path=template_path
245
- )
246
- -----------------------------------
247
- # Example of extracting value from URL PATH parameters after question mark:
248
- parameter_value = extract_value_from_path_parameter(
249
- path=class_client_message.request_raw_decoded.path,
250
- parameter='test_id'
251
- )
252
- """
217
+ """ This function should be overridden in the child class. """
253
218
 
254
- byte_response = None
255
- decoded_response = None
256
- # class_client_message.response_list_of_raw_bytes.append(byte_response)
257
- self.add_response_elements_to_lists(class_client_message, byte_response, decoded_response)
258
- self.logger.info(f"Response: {class_client_message.response_list_of_raw_bytes}")
219
+ return None
@@ -3,21 +3,24 @@ from atomicshop.mitm.engines.__parent.parser___parent import ParserParent
3
3
  from atomicshop.mitm.shared_functions import create_custom_logger
4
4
  from atomicshop.mitm.message import ClientMessage
5
5
 
6
- # # This is 'example' '.proto' file that contains message 'ExampleRequest'.
7
- # from .example_pb2 import ExampleRequest
8
- # # Import from 'protobuf' library of function 'MessageToDict' that
9
- # converts protobuf message object type to python dict.
10
- # from google.protobuf.json_format import MessageToDict
6
+
7
+ """
8
+ # This is 'example' '.proto' file that contains message 'ExampleRequest'.
9
+ from .example_pb2 import ExampleRequest
10
+ # Import from 'protobuf' library of function 'MessageToDict' that
11
+ converts protobuf message object type to python dict.
12
+ from google.protobuf.json_format import MessageToDict
13
+ """
11
14
 
12
15
 
13
16
  # Class that parses the message received from client.
14
17
  class ParserGeneral(ParserParent):
15
- logger = create_custom_logger()
16
-
17
18
  # When initializing main classes through "super" you need to pass parameters to init
18
19
  def __init__(self, class_client_message: ClientMessage):
19
20
  super().__init__(class_client_message)
20
21
 
22
+ self.logger = create_custom_logger()
23
+
21
24
  # ==================================================================================================================
22
25
  # Uncomment this section in order to begin building custom responder.
23
26
  # def parse_example_request(self):
@@ -4,10 +4,10 @@ from atomicshop.mitm.shared_functions import create_custom_logger
4
4
  from atomicshop.mitm.message import ClientMessage
5
5
 
6
6
 
7
- # The class that is responsible for Recording Requests / Responses
8
7
  class RecorderGeneral(RecorderParent):
9
- logger = create_custom_logger()
10
-
8
+ """The class that is responsible for Recording Requests / Responses"""
11
9
  # When initializing main classes through "super" you need to pass parameters to init
12
- def __init__(self, class_client_message: ClientMessage, record_path):
13
- super().__init__(class_client_message, record_path)
10
+ def __init__(self, record_path):
11
+ super().__init__(record_path)
12
+
13
+ self.logger = create_custom_logger()