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,6 +1,6 @@
1
1
  from typing import Union, Literal
2
2
 
3
- from .wrappers.pywin32w import wmi_win32process
3
+ from .wrappers.pywin32w.wmis import win32process
4
4
  from .wrappers.psutilw import psutilw
5
5
  from .basics import dicts
6
6
  from . import get_process_name_cmd_dll
@@ -36,7 +36,7 @@ class GetProcessList:
36
36
  self.process_polling_instance = psutilw.PsutilProcesses()
37
37
  self.connected = True
38
38
  elif self.get_method == 'pywin32':
39
- self.process_polling_instance = wmi_win32process.Pywin32Processes()
39
+ self.process_polling_instance = win32process.Pywin32Processes()
40
40
  elif self.get_method == 'process_dll':
41
41
  self.process_polling_instance = get_process_name_cmd_dll.ProcessNameCmdline()
42
42
 
@@ -130,4 +130,4 @@ test = get_process_list.get_processes()
130
130
 
131
131
  # globals need to be specified, otherwise the setup_code won't work with passed variables.
132
132
  times = timeit.timeit(setup=setup_code, stmt=test_code, number=times_to_test, globals=locals())
133
- print(f'Execution time: {times}')
133
+ print(f'Execution time: {times}')
atomicshop/http_parse.py CHANGED
@@ -1,10 +1,43 @@
1
- # v1.0.1 - 26.03.2023 23:40
2
1
  from http.server import BaseHTTPRequestHandler
3
2
  from http.client import HTTPResponse
4
3
  import http
5
4
  from io import BytesIO
6
5
 
7
6
 
7
+ def get_request_methods() -> list[str]:
8
+ """
9
+ Function to return all available HTTP request methods.
10
+ """
11
+ # noinspection PyUnresolvedReferences
12
+ return [method.value for method in http.HTTPMethod]
13
+
14
+
15
+ def is_first_bytes_http_request(request_bytes: bytes) -> bool:
16
+ """
17
+ Function to check if the first bytes are HTTP request or not.
18
+ """
19
+ http_request_methods_list: list = get_request_methods()
20
+ http_request_methods_list = [method.encode() for method in http_request_methods_list]
21
+
22
+ # If the first bytes are HTTP request, then the first word should be one of the HTTP request methods.
23
+ for method in http_request_methods_list:
24
+ if request_bytes.startswith(method):
25
+ return True
26
+ return False
27
+
28
+
29
+ def is_first_bytes_http_response(response_bytes: bytes) -> bool:
30
+ """
31
+ Function to check if the first bytes are HTTP response or not.
32
+ """
33
+ http_response_beginning: bytes = b'HTTP/'
34
+
35
+ # If the first bytes are HTTP response, then the first word should be 'HTTP/'.
36
+ if response_bytes.startswith(http_response_beginning):
37
+ return True
38
+ return False
39
+
40
+
8
41
  class HTTPRequestParse(BaseHTTPRequestHandler):
9
42
  """
10
43
  The class will parse HTTP requests.
@@ -49,41 +82,27 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
49
82
  client_message.request_raw_decoded = request_decoded
50
83
  """
51
84
 
52
- def __init__(self, request_text):
53
- self.rfile = BytesIO(request_text)
54
- self.raw_requestline = self.rfile.readline()
55
- self.error_code = self.error_message = None
56
- self.parse_request()
57
-
58
- # Check if ".path" attribute exists after HTTP request parsing
59
- if not hasattr(self, 'path'):
60
- self.path = None
85
+ # noinspection PyMissingConstructor
86
+ def __init__(self, request_bytes: bytes):
87
+ self.request_bytes: bytes = request_bytes
61
88
 
62
- # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values
63
- try:
64
- # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
65
- if 'Content-Length' in self.headers.keys():
66
- # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
67
- self.content_length = int(self.headers.get('Content-Length'))
68
- self.body = self.rfile.read(self.content_length)
69
- else:
70
- self.content_length = None
71
- self.body = None
72
- except Exception:
73
- self.content_length = None
74
- self.body = None
75
- pass
89
+ # noinspection PyTypeChecker
90
+ self.rfile = None
91
+ self.raw_requestline = None
92
+ self.error_code = None
93
+ self.error_message = None
76
94
 
77
- # Examples:
78
- # Getting path: self.path
79
- # Getting Request Version: self.request_version
80
- # Getting specific header: self.headers['host']
95
+ self.content_length = None
96
+ self.body = None
97
+ # noinspection PyTypeChecker
98
+ self.path = None
81
99
 
100
+ # noinspection PyMethodOverriding
82
101
  def send_error(self, code, message):
83
102
  self.error_code = code
84
103
  self.error_message = message
85
104
 
86
- def check_if_http(self):
105
+ def parse(self):
87
106
  """
88
107
  Function to check if parsed object is HTTP request or not.
89
108
  'reason' will be populated with parsing status and errors.
@@ -117,88 +136,125 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
117
136
 
118
137
  client_message.request_raw_decoded = request_decoded
119
138
  """
120
- # Defining variables
121
- reason = None
122
- function_result: bool = True
123
- error: bool = False
124
-
125
- # If there's any error in HTTP parsing
126
- if self.error_message:
127
- # If error status is "BAD_REQUEST"
128
- if self.error_message.startswith("Bad request"):
129
- # If it's 'Bad request' this is not HTTP request, so we can
130
- # continue the execution and parse the code as NON-HTTP Request.
131
- # Currently, seen 'Bad request syntax' and 'Bad request version'.
132
- reason = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
133
- function_result = False
134
- error: False
135
- else:
136
- reason = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
137
- function_result = True
138
- error = True
139
- # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
139
+
140
+ if self.request_bytes is None or not is_first_bytes_http_request(self.request_bytes):
141
+ error = "HTTP Request Parsing: Not HTTP request by first bytes."
142
+ is_http = False
140
143
  else:
141
- reason = "HTTP Request Parsing: Valid HTTP request"
142
- function_result = True
143
- error = False
144
+ error: str = str()
145
+
146
+ self.rfile = BytesIO(self.request_bytes)
147
+ self.raw_requestline = self.rfile.readline()
148
+ self.error_code = self.error_message = None
149
+ self.parse_request()
144
150
 
145
- return function_result, reason, error
151
+ # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values.
152
+ if hasattr(self, 'headers'):
153
+ # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
154
+ if 'Content-Length' in self.headers.keys():
155
+ # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
156
+ self.content_length = int(self.headers.get('Content-Length'))
157
+ self.body = self.rfile.read(self.content_length)
158
+
159
+ # Examples:
160
+ # Getting path: self.path
161
+ # Getting Request Version: self.request_version
162
+ # Getting specific header: self.headers['host']
163
+
164
+ # If there's any error in HTTP parsing
165
+ if self.error_message:
166
+ # If error status is "BAD_REQUEST"
167
+ if self.error_message.startswith("Bad request"):
168
+ # If it's 'Bad request' this is not HTTP request, so we can
169
+ # continue the execution and parse the code as NON-HTTP Request.
170
+ # Currently, seen 'Bad request syntax' and 'Bad request version'.
171
+ error = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
172
+ is_http = False
173
+ else:
174
+ error = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
175
+ is_http = False
176
+ # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
177
+ else:
178
+ is_http = True
179
+
180
+ return self, is_http, error
146
181
 
147
182
 
148
183
  class FakeSocket:
149
184
  """
150
- FakeSocket is needed to parse HTTP Response. Socket object is needed for HTTPResponse class input.
185
+ FakeSocket mimics a socket object for parsing HTTP responses.
151
186
  """
152
187
  def __init__(self, response_bytes):
153
188
  self._file = BytesIO(response_bytes)
154
189
 
155
- def makefile(self, *args, **kwargs):
190
+ # noinspection PyUnusedLocal
191
+ def makefile(self, mode='rb', buffering=-1) -> BytesIO:
192
+ """
193
+ Mimics the socket's makefile method, returning the BytesIO object.
194
+ """
156
195
  return self._file
157
196
 
197
+ def fileno(self) -> int:
198
+ """
199
+ Provide a dummy file descriptor, as some code might call this.
200
+ """
201
+ raise OSError("File descriptor not available in FakeSocket")
202
+
158
203
 
159
204
  class HTTPResponseParse:
160
205
  def __init__(self, response_raw_bytes: bytes):
161
- self.error = None
162
206
  self.response_raw_bytes: bytes = response_raw_bytes
163
- # Assigning FakeSocket with response_raw_bytes.
164
- self.source = FakeSocket(self.response_raw_bytes)
165
207
 
166
- # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
167
- self.response_raw_decoded = HTTPResponse(self.source)
208
+ self.error = None
209
+ self.source = None
210
+ self.response_raw_parsed = None
211
+ self.is_http: bool = False
168
212
 
169
- # Try to parse HTTP Response.
170
- try:
171
- self.response_raw_decoded.begin()
172
- # If there were problems with the status line.
173
- except http.client.BadStatusLine:
174
- self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
175
- pass
213
+ def parse(self):
214
+ if self.response_raw_bytes is None or not is_first_bytes_http_response(self.response_raw_bytes):
215
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response by first bytes."
216
+ self.is_http = False
217
+ self.response_raw_parsed = None
218
+ else:
219
+ # Assigning FakeSocket with response_raw_bytes.
220
+ self.source = FakeSocket(self.response_raw_bytes)
176
221
 
177
- try:
178
- # If no exception was thrown, but there are some problems with headers.
179
- if self.response_raw_decoded.headers.defects:
222
+ # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
223
+ # noinspection PyTypeChecker
224
+ self.response_raw_parsed = HTTPResponse(self.source)
225
+
226
+ # Try to parse HTTP Response.
227
+ try:
228
+ self.response_raw_parsed.begin()
229
+ self.is_http = True
230
+ # If there were problems with the status line.
231
+ except http.client.BadStatusLine:
232
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
233
+ self.is_http = False
234
+
235
+ header_exists: bool = False
236
+ if (self.response_raw_parsed is not None and hasattr(self.response_raw_parsed, 'headers') and
237
+ self.response_raw_parsed.headers is not None):
238
+ header_exists = True
239
+
240
+ if header_exists and self.response_raw_parsed.headers.defects:
180
241
  self.error = f"HTTP Response Parsing: Not a valid HTTP Response: Some defects in headers: " \
181
- f"{self.response_raw_decoded.headers.defects}"
182
- # If the attribute of defects doesn't exist, probably the response wasn't parsed at all by the library,
183
- # Meaning, that the exception was already handled.
184
- except AttributeError:
185
- pass
242
+ f"{self.response_raw_parsed.headers.defects}"
243
+ self.is_http = False
186
244
 
187
- # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values
188
- try:
189
- # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
190
- if 'Content-Length' in self.response_raw_decoded.headers.keys():
191
- # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
192
- # "int" converts it to integer.
193
- self.response_raw_decoded.content_length = int(self.response_raw_decoded.headers.get('Content-Length'))
194
- # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
195
- # of the response that we received.
196
- # self.response_raw_bytes[-23:]
197
- self.response_raw_decoded.body = self.response_raw_bytes[-self.response_raw_decoded.content_length:]
198
- else:
199
- self.response_raw_decoded.content_length = None
200
- self.response_raw_decoded.body = None
201
- except Exception:
202
- self.response_raw_decoded.content_length = None
203
- self.response_raw_decoded.body = None
204
- pass
245
+ if self.is_http:
246
+ # Before checking for body, we need to make sure that ".headers" property exists,
247
+ # if not, return empty values.
248
+ self.response_raw_parsed.content_length = None
249
+ self.response_raw_parsed.body = None
250
+ if header_exists and 'Content-Length' in self.response_raw_parsed.headers.keys():
251
+ # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
252
+ # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
253
+ # "int" converts it to integer.
254
+ self.response_raw_parsed.content_length = int(self.response_raw_parsed.headers.get('Content-Length'))
255
+ # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
256
+ # of the response that we received.
257
+ # self.response_raw_bytes[-23:]
258
+ self.response_raw_parsed.body = self.response_raw_bytes[-self.response_raw_parsed.content_length:]
259
+
260
+ return self.response_raw_parsed, self.is_http, self.error
@@ -2,7 +2,12 @@ import ipaddress
2
2
  from typing import Union, Literal
3
3
 
4
4
 
5
- def is_ip_address(string_value: str, ip_type: Union[Literal['ipv4', 'ipv6'], None] = None) -> bool:
5
+ def is_ip_address(
6
+ string_value: str,
7
+ ip_type: Union[
8
+ Literal['ipv4', 'ipv6'],
9
+ None] = None
10
+ ) -> bool:
6
11
  """
7
12
  The function checks if the string is an IPv4 or IPv6 address.
8
13
 
@@ -0,0 +1,132 @@
1
+ import os
2
+ import argparse
3
+ import base64
4
+
5
+ from ..print_api import print_api
6
+ from .. import networks, ssh_remote, package_mains_processor
7
+ from . import config_static, mitm_main
8
+
9
+
10
+ def _make_parser() -> argparse.ArgumentParser:
11
+ parser = argparse.ArgumentParser(
12
+ description="Apply centered network settings to the target hosts based on the configuration file and arguments."
13
+ )
14
+ parser.add_argument(
15
+ "-t", "--target-hosts-file",
16
+ type=str,
17
+ required=True,
18
+ help="Path to the text file that will include the list of hosts (name/ipv4)."
19
+ )
20
+
21
+ parser.add_argument(
22
+ "-dns", "--set-default-dns-gateway",
23
+ action="store_true",
24
+ help="Set the default gateway as this server LAN IPv4 on the target hosts."
25
+ )
26
+ parser.add_argument(
27
+ "-ca", "--install-ca-cert",
28
+ action="store_true",
29
+ help="Install the CA certificate on the target hosts."
30
+ )
31
+
32
+
33
+ return parser
34
+
35
+
36
+ def centered_settings_main(config_file_path: str, script_version: str):
37
+ print(f"Centered Settings Application Script Version: {script_version}")
38
+ # Import the configuration file.
39
+ rc: int = config_static.load_config(config_file_path, print_kwargs=dict(stdout=False))
40
+ if rc != 0:
41
+ return rc
42
+
43
+ if config_static.MainConfig.is_localhost:
44
+ print_api("The server is set to localhost mode. No changes will be applied.", color="yellow")
45
+ return 0
46
+
47
+ interface_name: str = mitm_main._get_interface_name()
48
+ if interface_name is None:
49
+ return 1
50
+
51
+ # File path to the CA certificate file.
52
+ crt_file_path: str = config_static.MainConfig.ca_certificate_crt_filepath
53
+ with open(crt_file_path, 'r') as crt_file:
54
+ ca_certificate_string: str = crt_file.read()
55
+
56
+ # Get the main non-virtual IPv4 address.
57
+ main_ipv4_list: list[str] = networks.get_interface_ips_powershell(interface_name, "dynamic")
58
+
59
+ if not main_ipv4_list:
60
+ print_api(f"Could not determine the main IPv4 address for interface: {interface_name}", color="red")
61
+ return 1
62
+ else:
63
+ main_ipv4: str = main_ipv4_list[0]
64
+
65
+ parser = _make_parser()
66
+ args = parser.parse_args()
67
+
68
+ target_hosts_file_path: str = args.target_hosts_file
69
+ set_default_dns_gateway: bool = args.set_default_dns_gateway
70
+ install_ca_cert: bool = args.install_ca_cert
71
+
72
+ if not set_default_dns_gateway and not install_ca_cert:
73
+ print_api("No actions specified. Use -dns and/or -ca arguments to apply settings.", color="yellow")
74
+ return 0
75
+
76
+ if not os.path.exists(target_hosts_file_path):
77
+ print_api(f"Target host list file does not exist: {target_hosts_file_path}", color="red")
78
+ return 1
79
+
80
+ # Read the target hosts from the file.
81
+ with open(target_hosts_file_path, 'r') as f:
82
+ target_hosts: list[str] = [line.strip() for line in f if line.strip()]
83
+ if not target_hosts:
84
+ print_api(f"No target hosts found in the file: {target_hosts_file_path}", color="red")
85
+ return 1
86
+
87
+ dns_script_string: str | None = None
88
+ if set_default_dns_gateway:
89
+ package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
90
+ script_file_stem="set_default_dns_gateway")
91
+ dns_script_string = package_processor.read_script_file_to_string()
92
+ ca_script_string: str | None = None
93
+ if install_ca_cert:
94
+ package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
95
+ script_file_stem="install_ca_certificate")
96
+ ca_script_string = package_processor.read_script_file_to_string()
97
+
98
+ for host in target_hosts:
99
+ ssh_client = ssh_remote.SSHRemote(
100
+ ip_address=host,
101
+ username=config_static.ProcessName.ssh_user,
102
+ password=config_static.ProcessName.ssh_pass
103
+ )
104
+ stderr = ssh_client.connect()
105
+ if stderr:
106
+ print_api(f"SSH connection to {host} failed:\n"
107
+ f"{stderr}", color="red")
108
+ continue
109
+
110
+ if set_default_dns_gateway:
111
+ stdout, stderr = ssh_client.remote_execution_python(
112
+ script_string=dns_script_string, script_arg_values=(main_ipv4,))
113
+
114
+ if stderr:
115
+ print_api(f"Failed to apply settings on {host}:\n{stderr}", color="red")
116
+ else:
117
+ print_api(f"Successfully applied settings on {host}:\n{stdout}", color="green")
118
+ if install_ca_cert:
119
+ cert_b64 = base64.b64encode(ca_certificate_string.encode("utf-8")).decode("ascii")
120
+ stdout, stderr = ssh_client.remote_execution_python(
121
+ script_string=ca_script_string, script_arg_values=(config_static.MainConfig.ca_certificate_name, cert_b64,))
122
+
123
+ if stderr:
124
+ print_api(f"Failed to install CA certificate on {host}:\n{stderr}", color="red")
125
+ else:
126
+ print_api(f"Successfully installed CA certificate on {host}:\n{stdout}", color="green")
127
+
128
+ # Closing SSH connection to the target host.
129
+ ssh_client.close()
130
+
131
+
132
+ return 0
@@ -0,0 +1,207 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+ from typing import Literal
4
+
5
+ from . import import_config
6
+ from .message import ClientMessage
7
+
8
+
9
+ # noinspection PyTypeChecker
10
+ ENGINES_LIST: list = None # list[initialize_engines.ModuleCategory]
11
+ REFERENCE_MODULE = None # initialize_engines.ModuleCategory
12
+
13
+
14
+ class MainConfig:
15
+ # '' (empty) - system's default internet interface.
16
+ # Any other network interface name available on the system.
17
+ is_offline: bool
18
+ network_interface: str
19
+ is_localhost: bool
20
+
21
+ set_default_dns_gateway: list[str]
22
+ set_default_dns_gateway_to_localhost: bool = False
23
+ set_default_dns_gateway_to_network_interface_ipv4: bool = False
24
+ default_localhost_dns_gateway_ipv4: str = '127.0.0.1'
25
+
26
+ LOGGER_NAME: str = 'network'
27
+
28
+ SCRIPT_DIRECTORY: str = None
29
+
30
+ ENGINES_DIRECTORY_PATH: str = None
31
+ ENGINES_DIRECTORY_NAME: str = "engines"
32
+ ENGINE_CONFIG_FILE_NAME: str = "engine_config.toml"
33
+
34
+ # Certificates.
35
+ default_server_certificate_name: str = 'default'
36
+ ca_certificate_name: str = 'ElaborateCA'
37
+ ca_certificate_pem_filename: str = f'{ca_certificate_name}.pem'
38
+ ca_certificate_crt_filename: str = f'{ca_certificate_name}_for_manual_installation_not_used_by_script.crt'
39
+ # CA Certificate name and file name without extension.
40
+ ca_certificate_filepath: str = None
41
+ ca_certificate_crt_filepath: str = None
42
+ # Default server certificate file name and path.
43
+ default_server_certificate_filename = f'{default_server_certificate_name}.pem'
44
+ default_server_certificate_filepath: str = None
45
+
46
+ @classmethod
47
+ def update(cls):
48
+ # This runs after the dataclass is initialized
49
+ cls.ENGINES_DIRECTORY_PATH = cls.SCRIPT_DIRECTORY + os.sep + cls.ENGINES_DIRECTORY_NAME
50
+ cls.ca_certificate_filepath = f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.ca_certificate_pem_filename}'
51
+ cls.ca_certificate_crt_filepath = f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.ca_certificate_crt_filename}'
52
+ cls.default_server_certificate_filepath = \
53
+ f'{cls.SCRIPT_DIRECTORY}{os.sep}{cls.default_server_certificate_filename}'
54
+
55
+
56
+ @dataclass
57
+ class DNSServer:
58
+ is_enabled: bool
59
+ offline_mode: bool
60
+
61
+ listening_ipv4: str
62
+ listening_port: int
63
+
64
+ listening_address: str
65
+ forwarding_dns_service_ipv4: str
66
+ cache_timeout_minutes: int
67
+
68
+ resolve_by_engine: bool
69
+ resolve_regular_pass_thru: bool
70
+ resolve_all_domains_to_ipv4_enable: bool
71
+ target_ipv4: str
72
+
73
+ # Convertable variables.
74
+ resolve_all_domains_to_ipv4: dict
75
+
76
+ # Static variables.
77
+ forwarding_dns_service_port: int = 53
78
+
79
+
80
+
81
+ @dataclass
82
+ class TCPServer:
83
+ is_enabled: bool
84
+ no_engines_usage_to_listen_addresses_enable: bool
85
+ no_engines_listening_address_list: list[str]
86
+
87
+ # Convertable variables.
88
+ no_engines_usage_to_listen_addresses: dict
89
+
90
+ @dataclass
91
+ class LogRec:
92
+ logs_path: str
93
+ recordings_path: str
94
+ enable_request_response_recordings_in_logs: bool
95
+ store_logs_for_x_days: int
96
+
97
+ recordings_directory_name: str = 'recs'
98
+
99
+
100
+ @dataclass
101
+ class Certificates:
102
+ install_ca_certificate_to_root_store: bool
103
+ uninstall_unused_ca_certificates_with_mitm_ca_name: bool
104
+
105
+ default_server_certificate_usage: bool
106
+ sni_add_new_domains_to_default_server_certificate: bool
107
+
108
+ custom_server_certificate_usage: bool
109
+ custom_server_certificate_path: str
110
+ custom_private_key_path: str
111
+
112
+ sni_create_server_certificate_for_each_domain: bool
113
+ sni_server_certificates_cache_directory: str
114
+ sni_get_server_certificate_from_server_socket: bool
115
+ sni_server_certificate_from_server_socket_download_directory: str
116
+
117
+ domains_all_times: list[str]
118
+ sslkeylog_file_path: str
119
+
120
+ sslkeylog_file_name: str = "sslkeylog.txt"
121
+ enable_sslkeylogfile_env_to_client_ssl_context: bool = True
122
+
123
+
124
+ @dataclass
125
+ class SkipExtensions:
126
+ tls_web_client_authentication: bool
127
+ crl_distribution_points: bool
128
+ authority_information_access: bool
129
+
130
+ SKIP_EXTENSION_ID_LIST: list
131
+
132
+
133
+ @dataclass
134
+ class ProcessName:
135
+ get_process_name: bool
136
+ ssh_user: str
137
+ ssh_pass: str
138
+
139
+ ssh_script_to_execute: Literal['process_from_port', 'process_from_ipv4'] = 'process_from_port'
140
+
141
+
142
+ def load_config(
143
+ config_toml_file_path: str,
144
+ print_kwargs: dict = None
145
+ ):
146
+ # global CONFIG
147
+
148
+ script_path = os.path.dirname(config_toml_file_path)
149
+ MainConfig.SCRIPT_DIRECTORY = script_path
150
+ MainConfig.update()
151
+
152
+ # Load the configuration file.
153
+ result = import_config.import_config_files(config_toml_file_path, print_kwargs=print_kwargs or {})
154
+ return result
155
+
156
+
157
+ def get_listening_addresses(client_message: ClientMessage) -> dict | None:
158
+ """
159
+ Get the list of listening addresses from the TCPServer configuration.
160
+ If no_engines_usage_to_listen_addresses_enable is True, return the no_engines_listening_address_list.
161
+ Otherwise, return an empty list.
162
+ """
163
+
164
+ for engine in ENGINES_LIST:
165
+ if engine.engine_name == client_message.engine_name:
166
+ return {
167
+ 'domain_target_dict': engine.domain_target_dict,
168
+ 'port_target_dict': engine.port_target_dict
169
+ }
170
+
171
+
172
+ # ============ Server Tester Specific ===============
173
+ CONFIG_INI_TESTER_FILE_NAME: str = 'config_tester.ini'
174
+
175
+ """
176
+ config.toml:
177
+ target_domain_or_ip: the domain or ip that the requests will be sent to. Better use domains, for better testing
178
+ simulation.
179
+ target_port: the port that requests will be sent to.
180
+ request_type: type of each request: json / string / binary.
181
+ json format that contain a key with hex string of the request that will be converted to bytes.
182
+ string that will contain a request and will be converted to bytes.
183
+ binary file that will contain all request data - will be converted to bytes.
184
+ request_json_hex_key_list: this key stores raw request in hex format, since there can be characters that can't be
185
+ decoded to string / unicode.
186
+ 'request_raw_hex' key is the default key in recorded files from mitm server, you may add keys for your custom
187
+ JSON files.
188
+ requests_directory: the directory that requests are will be taken from. Can be relative folder that will be in
189
+ current working directory.
190
+
191
+ parallel_connections_bool: boolean, sets if sockets should be initialized in threads (in parallel) or one after another.
192
+ Use all the connections / cycles in parallel when 'True'. New sockets will be created for each request.
193
+ Reuse the same socket / connection for all the requests when 'False'.
194
+ interval_between_requests_defaults_seconds: default interval in seconds between request sends.
195
+ interval_between_request_custom_list_seconds: list of intervals in seconds. If this configuration will not be empty,
196
+ this should be a list. Each interval in the list will follow the interval between requests and
197
+ 'interval_between_requests_defaults_seconds' will not be used. It will be used only if number of requests
198
+ is less than then number of intervals in 'interval_between_request_custom_list_seconds'. The missing intervals
199
+ will be filled by default values from 'interval_between_requests_defaults_seconds'.
200
+ Example: you have 10 requests.
201
+ interval_between_requests_defaults_seconds = 5
202
+ interval_between_request_custom_list_seconds = 7, 10, 8, 4, 15
203
+ The rest will be filled from defaults: 7, 10, 8, 4, 15, 5, 5, 5, 5
204
+ send_request_batch_cycles: how many batch cycles to send of the same 10 requests (or any other number of requests that
205
+ you might have in the requests folder.
206
+ interval_between_batch_cycles_seconds: interval in seconds between each batch.
207
+ """