atomicshop 2.16.42__py3-none-any.whl → 2.16.44__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/basics/bytes_arrays.py +24 -0
- atomicshop/dns.py +11 -0
- atomicshop/file_io/docxs.py +1 -1
- atomicshop/http_parse.py +4 -4
- atomicshop/mitm/connection_thread_worker.py +2 -7
- atomicshop/mitm/mitm_main.py +9 -4
- atomicshop/web.py +5 -2
- atomicshop/websocket_parse.py +415 -0
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/mongodbw/mongodbw.py +5 -2
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/scenarios.py +14 -5
- atomicshop/wrappers/winregw/winreg_network.py +45 -16
- {atomicshop-2.16.42.dist-info → atomicshop-2.16.44.dist-info}/METADATA +2 -1
- {atomicshop-2.16.42.dist-info → atomicshop-2.16.44.dist-info}/RECORD +19 -18
- {atomicshop-2.16.42.dist-info → atomicshop-2.16.44.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.16.42.dist-info → atomicshop-2.16.44.dist-info}/WHEEL +0 -0
- {atomicshop-2.16.42.dist-info → atomicshop-2.16.44.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -53,6 +53,30 @@ def convert_sequence_of_bytes_to_sequence_of_strings(byte_sequence: bytes) -> li
|
|
|
53
53
|
return result
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
def convert_sequence_of_bytes_to_exact_string(
|
|
57
|
+
byte_sequence: bytes,
|
|
58
|
+
add_space_between_bytes: bool = False,
|
|
59
|
+
) -> str:
|
|
60
|
+
"""
|
|
61
|
+
Convert sequence of bytes to exact string.
|
|
62
|
+
Example: b'\xc0\x00' -> 'c000'
|
|
63
|
+
|
|
64
|
+
:param byte_sequence: bytes, sequence of bytes.
|
|
65
|
+
:param add_space_between_bytes: bool, add space between bytes.
|
|
66
|
+
Example: b'\xc0\x00' -> 'c0 00'
|
|
67
|
+
:return: string.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
# Convert to hex string and format
|
|
71
|
+
byte_list: list = []
|
|
72
|
+
for byte in byte_sequence:
|
|
73
|
+
byte_list.append(f'{byte:02x}')
|
|
74
|
+
|
|
75
|
+
result = ''.join(byte_list)
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
56
80
|
def find_position(target: bytes, file_path: str = None, file_bytes: bytes = None, chunk_size: int = None, starting_position: int = 0) -> int:
|
|
57
81
|
"""
|
|
58
82
|
Find position of the target bytes string in the file.
|
atomicshop/dns.py
CHANGED
|
@@ -77,6 +77,17 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
77
77
|
return is_dynamic, dns_servers
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
def get_default_dns_gateway_with_dns_resolver() -> list[str]:
|
|
81
|
+
"""
|
|
82
|
+
Get the default DNS gateway from the system using dns.resolver.
|
|
83
|
+
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
resolver = dns.resolver.Resolver()
|
|
87
|
+
dns_servers = list(resolver.nameservers)
|
|
88
|
+
return dns_servers
|
|
89
|
+
|
|
90
|
+
|
|
80
91
|
def set_connection_dns_gateway_static(
|
|
81
92
|
dns_servers: list[str],
|
|
82
93
|
connection_name: str = None,
|
atomicshop/file_io/docxs.py
CHANGED
|
@@ -75,7 +75,7 @@ def search_for_hyperlink_in_files(directory_path: str, hyperlink: str, relative_
|
|
|
75
75
|
|
|
76
76
|
# Get all the docx files in the specified directory.
|
|
77
77
|
files = filesystem.get_paths_from_directory(
|
|
78
|
-
directory_path, get_file=True, file_name_check_pattern="
|
|
78
|
+
directory_path, get_file=True, file_name_check_pattern="*.docx",
|
|
79
79
|
add_relative_directory=True, relative_file_name_as_directory=True)
|
|
80
80
|
|
|
81
81
|
found_in_files: list = list()
|
atomicshop/http_parse.py
CHANGED
|
@@ -2,7 +2,6 @@ from http.server import BaseHTTPRequestHandler
|
|
|
2
2
|
from http.client import HTTPResponse
|
|
3
3
|
import http
|
|
4
4
|
from io import BytesIO
|
|
5
|
-
import socket
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
@@ -50,8 +49,8 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
|
50
49
|
"""
|
|
51
50
|
|
|
52
51
|
# noinspection PyMissingConstructor
|
|
53
|
-
def __init__(self,
|
|
54
|
-
self.
|
|
52
|
+
def __init__(self, request_bytes: bytes):
|
|
53
|
+
self.request_bytes: bytes = request_bytes
|
|
55
54
|
|
|
56
55
|
# noinspection PyTypeChecker
|
|
57
56
|
self.rfile = None
|
|
@@ -107,7 +106,7 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
|
107
106
|
error: str = str()
|
|
108
107
|
info: str = str()
|
|
109
108
|
|
|
110
|
-
self.rfile = BytesIO(self.
|
|
109
|
+
self.rfile = BytesIO(self.request_bytes)
|
|
111
110
|
self.raw_requestline = self.rfile.readline()
|
|
112
111
|
self.error_code = self.error_message = None
|
|
113
112
|
self.parse_request()
|
|
@@ -152,6 +151,7 @@ class FakeSocket:
|
|
|
152
151
|
def __init__(self, response_bytes):
|
|
153
152
|
self._file = BytesIO(response_bytes)
|
|
154
153
|
|
|
154
|
+
# noinspection PyUnusedLocal
|
|
155
155
|
def makefile(self, mode='rb', buffering=-1) -> BytesIO:
|
|
156
156
|
"""
|
|
157
157
|
Mimics the socket's makefile method, returning the BytesIO object.
|
|
@@ -100,15 +100,12 @@ def thread_worker_main(
|
|
|
100
100
|
client_message.protocol = 'HTTP'
|
|
101
101
|
client_message.request_raw_decoded = request_decoded
|
|
102
102
|
print_api(request_parsing_info, logger=network_logger, logger_method='info')
|
|
103
|
-
network_logger.info(f"Method: {request_decoded.command}")
|
|
104
|
-
network_logger.info(f"Path: {request_decoded.path}")
|
|
105
|
-
# If there was error - the request is really HTTP, but there's a problem with its structure.
|
|
103
|
+
network_logger.info(f"Method: {request_decoded.command} | Path: {request_decoded.path}")
|
|
106
104
|
else:
|
|
107
|
-
# client_message.error = request_parsing_error
|
|
108
|
-
print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
|
|
109
105
|
# It doesn't matter if we have HTTP Parsing error, since the request may not be really HTTP, so it is OK
|
|
110
106
|
# not to log it into statistics.
|
|
111
107
|
# statistics_error_list.append(error_message)
|
|
108
|
+
print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
|
|
112
109
|
|
|
113
110
|
def finish_thread():
|
|
114
111
|
# At this stage there could be several times that the same socket was used to the service server - we need to
|
|
@@ -204,8 +201,6 @@ def thread_worker_main(
|
|
|
204
201
|
client_message.request_raw_bytes = client_received_raw_data
|
|
205
202
|
|
|
206
203
|
parse_http()
|
|
207
|
-
if client_message.protocol != 'HTTP':
|
|
208
|
-
pass
|
|
209
204
|
|
|
210
205
|
# Custom parser, should parse HTTP body or the whole message if not HTTP.
|
|
211
206
|
parser_instance = parser(client_message)
|
atomicshop/mitm/mitm_main.py
CHANGED
|
@@ -19,6 +19,7 @@ from . import config_static, recs_files
|
|
|
19
19
|
|
|
20
20
|
NETWORK_INTERFACE_IS_DYNAMIC: bool = bool()
|
|
21
21
|
NETWORK_INTERFACE_IPV4_ADDRESS_LIST: list[str] = list()
|
|
22
|
+
IS_SET_DNS_GATEWAY: bool = False
|
|
22
23
|
# noinspection PyTypeChecker
|
|
23
24
|
RECS_PROCESS_INSTANCE: multiprocessing.Process = None
|
|
24
25
|
|
|
@@ -36,7 +37,7 @@ except win_console.NotWindowsConsoleError:
|
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def exit_cleanup():
|
|
39
|
-
if permissions.is_admin():
|
|
40
|
+
if permissions.is_admin() and IS_SET_DNS_GATEWAY:
|
|
40
41
|
is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
|
|
41
42
|
status_string = 'Dynamic' if is_dns_dynamic else 'Static'
|
|
42
43
|
print_api.print_api(f'Current DNS Gateway: {status_string}, {current_dns_gateway}')
|
|
@@ -347,10 +348,14 @@ def mitm_server(config_file_path: str):
|
|
|
347
348
|
dns_gateway_server_list = [base.DEFAULT_IPV4]
|
|
348
349
|
set_dns_gateway = True
|
|
349
350
|
|
|
350
|
-
# Get current network interface state.
|
|
351
|
-
global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
|
|
352
|
-
NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
|
|
353
351
|
if set_dns_gateway:
|
|
352
|
+
global IS_SET_DNS_GATEWAY
|
|
353
|
+
IS_SET_DNS_GATEWAY = True
|
|
354
|
+
|
|
355
|
+
# Get current network interface state.
|
|
356
|
+
global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
|
|
357
|
+
NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
|
|
358
|
+
|
|
354
359
|
# Set the DNS gateway to the specified one only if the DNS gateway is dynamic, or it is static but different
|
|
355
360
|
# from the one specified in the configuration file.
|
|
356
361
|
if (NETWORK_INTERFACE_IS_DYNAMIC or (not NETWORK_INTERFACE_IS_DYNAMIC and
|
atomicshop/web.py
CHANGED
|
@@ -90,10 +90,13 @@ def get_page_bytes(
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def get_page_content(
|
|
93
|
-
url: str,
|
|
93
|
+
url: str,
|
|
94
|
+
get_method: str = 'urllib',
|
|
95
|
+
path: str = None,
|
|
94
96
|
playwright_pdf_format: str = 'A4',
|
|
95
97
|
playwright_html_txt_convert_to_bytes: bool = True,
|
|
96
|
-
print_kwargs: dict = None
|
|
98
|
+
print_kwargs: dict = None
|
|
99
|
+
) -> any:
|
|
97
100
|
"""
|
|
98
101
|
Function returns the page content from the given URL.
|
|
99
102
|
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
from typing import Union, Generator
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from websockets.server import ServerProtocol
|
|
5
|
+
from websockets.client import ClientProtocol
|
|
6
|
+
from websockets.extensions.permessage_deflate import PerMessageDeflate, ServerPerMessageDeflateFactory, ClientPerMessageDeflateFactory
|
|
7
|
+
from websockets.http11 import Request, Response
|
|
8
|
+
from websockets.frames import Frame, Opcode
|
|
9
|
+
from websockets.uri import parse_uri
|
|
10
|
+
from websockets.exceptions import InvalidHeaderValue
|
|
11
|
+
from websockets.protocol import OPEN
|
|
12
|
+
from websockets.streams import StreamReader
|
|
13
|
+
from websockets.exceptions import ProtocolError, PayloadTooBig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WebsocketParseWrongOpcode(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_byte_http_response(
|
|
21
|
+
byte_http_request: Union[bytes, bytearray],
|
|
22
|
+
enable_logging: bool = False
|
|
23
|
+
) -> bytes:
|
|
24
|
+
"""
|
|
25
|
+
Create a byte HTTP response from a byte HTTP request.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
- byte_http_request (bytes, bytearray): The byte HTTP request.
|
|
29
|
+
- enable_logging (bool): Whether to enable logging.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
- bytes: The byte HTTP response.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Set up extensions
|
|
36
|
+
permessage_deflate_factory = ServerPerMessageDeflateFactory()
|
|
37
|
+
|
|
38
|
+
# Create the protocol instance
|
|
39
|
+
protocol = ServerProtocol(
|
|
40
|
+
extensions=[permessage_deflate_factory],
|
|
41
|
+
)
|
|
42
|
+
# At this state the protocol.state is State.CONNECTING
|
|
43
|
+
|
|
44
|
+
if enable_logging:
|
|
45
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
46
|
+
protocol.logger.setLevel(logging.DEBUG)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
protocol.receive_data(byte_http_request)
|
|
50
|
+
events = protocol.events_received()
|
|
51
|
+
event = events[0]
|
|
52
|
+
if isinstance(event, Request):
|
|
53
|
+
# Accept the handshake.
|
|
54
|
+
# After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
|
|
55
|
+
# Only after this state we can parse frames.
|
|
56
|
+
response = protocol.accept(event)
|
|
57
|
+
return response.serialize()
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError("The event is not a Request object.")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse_frame_bytes(data_bytes: bytes):
|
|
63
|
+
# Define the read_exact function
|
|
64
|
+
def read_exact(n: int) -> Generator[None, None, bytes]:
|
|
65
|
+
return reader.read_exact(n)
|
|
66
|
+
|
|
67
|
+
# Helper function to run generator-based coroutines
|
|
68
|
+
def run_coroutine(coroutine):
|
|
69
|
+
try:
|
|
70
|
+
while True:
|
|
71
|
+
next(coroutine)
|
|
72
|
+
except StopIteration as e:
|
|
73
|
+
return e.value
|
|
74
|
+
except Exception as e:
|
|
75
|
+
raise e # Re-raise exceptions to be handled by the caller
|
|
76
|
+
|
|
77
|
+
# Function to parse frames
|
|
78
|
+
def parse_frame(mask: bool):
|
|
79
|
+
try:
|
|
80
|
+
# Use Frame.parse to parse the frame
|
|
81
|
+
frame_parser = Frame.parse(
|
|
82
|
+
read_exact,
|
|
83
|
+
mask=mask, # Client frames are masked
|
|
84
|
+
max_size=None,
|
|
85
|
+
extensions=[permessage_deflate], # Include the extension unconditionally
|
|
86
|
+
)
|
|
87
|
+
current_frame = run_coroutine(frame_parser)
|
|
88
|
+
except EOFError as e:
|
|
89
|
+
# Not enough data to parse a complete frame
|
|
90
|
+
raise e
|
|
91
|
+
except (ProtocolError, PayloadTooBig) as e:
|
|
92
|
+
print("Error parsing frame:", e)
|
|
93
|
+
raise e
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print("Error parsing frame:", e)
|
|
96
|
+
raise e
|
|
97
|
+
return current_frame
|
|
98
|
+
|
|
99
|
+
def process_frame(current_frame):
|
|
100
|
+
if current_frame.opcode == Opcode.TEXT:
|
|
101
|
+
message = current_frame.data.decode('utf-8', errors='replace')
|
|
102
|
+
return message
|
|
103
|
+
elif current_frame.opcode == Opcode.BINARY:
|
|
104
|
+
return current_frame.data
|
|
105
|
+
elif current_frame.opcode == Opcode.CLOSE:
|
|
106
|
+
print("Received close frame")
|
|
107
|
+
elif current_frame.opcode == Opcode.PING:
|
|
108
|
+
print("Received ping")
|
|
109
|
+
elif current_frame.opcode == Opcode.PONG:
|
|
110
|
+
print("Received pong")
|
|
111
|
+
else:
|
|
112
|
+
raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
|
|
113
|
+
|
|
114
|
+
# Create the StreamReader instance
|
|
115
|
+
reader = StreamReader()
|
|
116
|
+
|
|
117
|
+
# Instantiate the permessage-deflate extension
|
|
118
|
+
permessage_deflate = PerMessageDeflate(
|
|
119
|
+
remote_no_context_takeover=False,
|
|
120
|
+
local_no_context_takeover=False,
|
|
121
|
+
remote_max_window_bits=15,
|
|
122
|
+
local_max_window_bits=15,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
masked = is_frame_masked(data_bytes)
|
|
126
|
+
|
|
127
|
+
# Feed the data into the reader
|
|
128
|
+
reader.feed_data(data_bytes)
|
|
129
|
+
|
|
130
|
+
# Parse and process frames
|
|
131
|
+
frame = parse_frame(masked)
|
|
132
|
+
result = process_frame(frame)
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def create_websocket_frame(
|
|
137
|
+
data: Union[str, bytes, bytearray],
|
|
138
|
+
deflate: bool = False,
|
|
139
|
+
mask: bool = False,
|
|
140
|
+
opcode: int = None
|
|
141
|
+
) -> bytes:
|
|
142
|
+
"""
|
|
143
|
+
Create a WebSocket frame with the given data, optionally applying
|
|
144
|
+
permessage-deflate compression and masking.
|
|
145
|
+
|
|
146
|
+
Parameters:
|
|
147
|
+
- data (str, bytes, bytearray): The payload data.
|
|
148
|
+
If str, it will be encoded to bytes using UTF-8.
|
|
149
|
+
- deflate (bool): Whether to apply permessage-deflate compression.
|
|
150
|
+
- mask (bool): Whether to apply masking to the frame.
|
|
151
|
+
- opcode (int): The opcode of the frame. If not provided, it will be
|
|
152
|
+
determined based on the type of data.
|
|
153
|
+
Example:
|
|
154
|
+
from websockets.frames import Opcode
|
|
155
|
+
Opcode.TEXT, Opcode.BINARY, Opcode.CLOSE, Opcode.PING, Opcode.PONG.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
- bytes: The serialized WebSocket frame ready to be sent.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
# Determine the opcode if not provided
|
|
162
|
+
if opcode is None:
|
|
163
|
+
if isinstance(data, str):
|
|
164
|
+
opcode = Opcode.TEXT
|
|
165
|
+
elif isinstance(data, (bytes, bytearray)):
|
|
166
|
+
opcode = Opcode.BINARY
|
|
167
|
+
else:
|
|
168
|
+
raise TypeError("Data must be of type str, bytes, or bytearray.")
|
|
169
|
+
else:
|
|
170
|
+
if not isinstance(opcode, int):
|
|
171
|
+
raise TypeError("Opcode must be an integer.")
|
|
172
|
+
if not isinstance(data, (str, bytes, bytearray)):
|
|
173
|
+
raise TypeError("Data must be of type str, bytes, or bytearray.")
|
|
174
|
+
|
|
175
|
+
# Encode string data if necessary
|
|
176
|
+
if isinstance(data, str):
|
|
177
|
+
payload = data.encode('utf-8')
|
|
178
|
+
else:
|
|
179
|
+
payload = bytes(data)
|
|
180
|
+
|
|
181
|
+
# Create the Frame instance
|
|
182
|
+
frame = Frame(opcode=opcode, data=payload)
|
|
183
|
+
|
|
184
|
+
# Set up extensions if deflate is True
|
|
185
|
+
extensions = []
|
|
186
|
+
if deflate:
|
|
187
|
+
permessage_deflate = PerMessageDeflate(
|
|
188
|
+
remote_no_context_takeover=False,
|
|
189
|
+
local_no_context_takeover=False,
|
|
190
|
+
remote_max_window_bits=15,
|
|
191
|
+
local_max_window_bits=15,
|
|
192
|
+
)
|
|
193
|
+
extensions.append(permessage_deflate)
|
|
194
|
+
|
|
195
|
+
# Serialize the frame with the specified options
|
|
196
|
+
try:
|
|
197
|
+
frame_bytes = frame.serialize(
|
|
198
|
+
mask=mask,
|
|
199
|
+
extensions=extensions,
|
|
200
|
+
)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
raise RuntimeError(f"Error serializing frame: {e}")
|
|
203
|
+
|
|
204
|
+
return frame_bytes
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def is_frame_masked(frame_bytes):
|
|
208
|
+
"""
|
|
209
|
+
Determine whether a WebSocket frame is masked.
|
|
210
|
+
|
|
211
|
+
Parameters:
|
|
212
|
+
- frame_bytes (bytes): The raw bytes of the WebSocket frame.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
- bool: True if the frame is masked, False otherwise.
|
|
216
|
+
"""
|
|
217
|
+
if len(frame_bytes) < 2:
|
|
218
|
+
raise ValueError("Frame is too short to determine masking.")
|
|
219
|
+
|
|
220
|
+
# The second byte of the frame header contains the MASK bit
|
|
221
|
+
second_byte = frame_bytes[1]
|
|
222
|
+
|
|
223
|
+
# The MASK bit is the most significant bit (MSB) of the second byte
|
|
224
|
+
mask_bit = (second_byte & 0x80) != 0 # 0x80 is 1000 0000 in binary
|
|
225
|
+
|
|
226
|
+
return mask_bit
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def is_frame_deflated(frame_bytes):
|
|
230
|
+
"""
|
|
231
|
+
Determine whether a WebSocket frame is deflated (compressed).
|
|
232
|
+
|
|
233
|
+
Parameters:
|
|
234
|
+
- frame_bytes (bytes): The raw bytes of the WebSocket frame.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
- bool: True if the frame is deflated (compressed), False otherwise.
|
|
238
|
+
"""
|
|
239
|
+
if len(frame_bytes) < 1:
|
|
240
|
+
raise ValueError("Frame is too short to determine deflation status.")
|
|
241
|
+
|
|
242
|
+
# The first byte of the frame header contains the RSV1 bit
|
|
243
|
+
first_byte = frame_bytes[0]
|
|
244
|
+
|
|
245
|
+
# The RSV1 bit is the second most significant bit (bit 6)
|
|
246
|
+
rsv1 = (first_byte & 0x40) != 0 # 0x40 is 0100 0000 in binary
|
|
247
|
+
|
|
248
|
+
return rsv1
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class _WebsocketRequestParse:
|
|
252
|
+
"""
|
|
253
|
+
THIS IS ONLY FOR THE REFERENCE IT IS NOT CURRENTLY USED OR SHOULD BE USED.
|
|
254
|
+
Parse the websocket request and return the data
|
|
255
|
+
"""
|
|
256
|
+
def __init__(
|
|
257
|
+
self,
|
|
258
|
+
enable_logging: bool = False,
|
|
259
|
+
):
|
|
260
|
+
"""
|
|
261
|
+
Initialize the websocket parser.
|
|
262
|
+
|
|
263
|
+
:param enable_logging: bool: Enable logging for the websocket protocol.
|
|
264
|
+
"""
|
|
265
|
+
# noinspection PyTypeChecker
|
|
266
|
+
self.request_bytes: bytes = None
|
|
267
|
+
|
|
268
|
+
# Set up extensions
|
|
269
|
+
permessage_deflate_factory = ServerPerMessageDeflateFactory()
|
|
270
|
+
|
|
271
|
+
# Create the protocol instance
|
|
272
|
+
self.protocol = ServerProtocol(
|
|
273
|
+
extensions=[permessage_deflate_factory],
|
|
274
|
+
)
|
|
275
|
+
# At this state the protocol.state is State.CONNECTING
|
|
276
|
+
|
|
277
|
+
if enable_logging:
|
|
278
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
279
|
+
self.protocol.logger.setLevel(logging.DEBUG)
|
|
280
|
+
|
|
281
|
+
def parse(
|
|
282
|
+
self,
|
|
283
|
+
request_bytes: bytes
|
|
284
|
+
) -> Union[str, bytes, Request]:
|
|
285
|
+
"""
|
|
286
|
+
Parse the websocket request and return the data
|
|
287
|
+
|
|
288
|
+
:param request_bytes: bytes: The raw bytes of the websocket request.
|
|
289
|
+
:return: Request: The parsed request object.
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
self.protocol.receive_data(request_bytes)
|
|
293
|
+
events = self.protocol.events_received()
|
|
294
|
+
for event in events:
|
|
295
|
+
if isinstance(event, Request):
|
|
296
|
+
# Accept the handshake.
|
|
297
|
+
# After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
|
|
298
|
+
# Only after this state we can parse frames.
|
|
299
|
+
response = self.protocol.accept(event)
|
|
300
|
+
self.protocol.send_response(response)
|
|
301
|
+
return event
|
|
302
|
+
elif isinstance(event, Frame):
|
|
303
|
+
frame = event
|
|
304
|
+
if frame.opcode == Opcode.TEXT:
|
|
305
|
+
message = frame.data.decode('utf-8')
|
|
306
|
+
return message
|
|
307
|
+
elif frame.opcode == Opcode.BINARY:
|
|
308
|
+
return frame.data
|
|
309
|
+
|
|
310
|
+
"""
|
|
311
|
+
# Handle control frames, these are here for the future references.
|
|
312
|
+
elif frame.opcode == Opcode.CLOSE:
|
|
313
|
+
close_info = Close.parse(frame.data)
|
|
314
|
+
print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
|
|
315
|
+
# Send a close frame in response if not already sent
|
|
316
|
+
if self.protocol.state == self.protocol.OPEN:
|
|
317
|
+
self.protocol.send_close()
|
|
318
|
+
elif frame.opcode == Opcode.PING:
|
|
319
|
+
# Respond to ping with pong
|
|
320
|
+
self.protocol.send_pong(frame.data)
|
|
321
|
+
elif frame.opcode == Opcode.PONG:
|
|
322
|
+
print("Received pong")
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class _WebsocketResponseParse:
|
|
327
|
+
"""
|
|
328
|
+
THIS IS ONLY FOR THE REFERENCE IT IS NOT CURRENTLY USED OR SHOULD BE USED.
|
|
329
|
+
Parse the websocket response and return the data
|
|
330
|
+
"""
|
|
331
|
+
def __init__(
|
|
332
|
+
self,
|
|
333
|
+
enable_logging: bool = False,
|
|
334
|
+
):
|
|
335
|
+
"""
|
|
336
|
+
Initialize the websocket parser.
|
|
337
|
+
|
|
338
|
+
:param enable_logging: bool: Enable logging for the websocket protocol.
|
|
339
|
+
"""
|
|
340
|
+
# noinspection PyTypeChecker
|
|
341
|
+
self.response_bytes: bytes = None
|
|
342
|
+
|
|
343
|
+
# Set up extensions
|
|
344
|
+
permessage_deflate_factory = ClientPerMessageDeflateFactory()
|
|
345
|
+
|
|
346
|
+
# Parse the WebSocket URI.
|
|
347
|
+
# Since we're parsing the response, we don't need the URI, but the protocol object requires it.
|
|
348
|
+
# So we will just use a dummy URI.
|
|
349
|
+
wsuri = parse_uri('ws://example.com/websocket')
|
|
350
|
+
|
|
351
|
+
# Create the protocol instance
|
|
352
|
+
self.protocol = ClientProtocol(
|
|
353
|
+
wsuri,
|
|
354
|
+
extensions=[permessage_deflate_factory],
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if enable_logging:
|
|
358
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
359
|
+
# self.protocol.logger.setLevel(logging.DEBUG)
|
|
360
|
+
self.protocol.debug = True
|
|
361
|
+
|
|
362
|
+
# Perform the handshake and emulate the connection and request sending.
|
|
363
|
+
request = self.protocol.connect()
|
|
364
|
+
self.protocol.send_request(request)
|
|
365
|
+
_ = self.protocol.data_to_send()
|
|
366
|
+
# At this state the protocol.state is State.CONNECTING
|
|
367
|
+
|
|
368
|
+
def parse(
|
|
369
|
+
self,
|
|
370
|
+
response_bytes: bytes
|
|
371
|
+
) -> Union[str, bytes, Response]:
|
|
372
|
+
"""
|
|
373
|
+
Parse the websocket response and return the data
|
|
374
|
+
|
|
375
|
+
:param response_bytes: bytes: The raw bytes of the websocket response.
|
|
376
|
+
:return: The parsed response.
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
self.protocol.receive_data(response_bytes)
|
|
380
|
+
events = self.protocol.events_received()
|
|
381
|
+
for event in events:
|
|
382
|
+
if isinstance(event, Response):
|
|
383
|
+
# Accept the handshake.
|
|
384
|
+
# After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
|
|
385
|
+
# Only after this state we can parse frames.
|
|
386
|
+
try:
|
|
387
|
+
self.protocol.process_response(event)
|
|
388
|
+
except InvalidHeaderValue as e:
|
|
389
|
+
headers = event.headers
|
|
390
|
+
self.protocol.extensions = self.protocol.process_extensions(headers)
|
|
391
|
+
self.protocol.subprotocol = self.protocol.process_subprotocol(headers)
|
|
392
|
+
self.protocol.state = OPEN
|
|
393
|
+
return event
|
|
394
|
+
elif isinstance(event, Frame):
|
|
395
|
+
frame = event
|
|
396
|
+
if frame.opcode == Opcode.TEXT:
|
|
397
|
+
message = frame.data.decode('utf-8')
|
|
398
|
+
return message
|
|
399
|
+
elif frame.opcode == Opcode.BINARY:
|
|
400
|
+
return frame.data
|
|
401
|
+
|
|
402
|
+
"""
|
|
403
|
+
# Handle control frames, these are here for the future references.
|
|
404
|
+
elif frame.opcode == Opcode.CLOSE:
|
|
405
|
+
close_info = Close.parse(frame.data)
|
|
406
|
+
print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
|
|
407
|
+
# Send a close frame in response if not already sent
|
|
408
|
+
if self.protocol.state == self.protocol.OPEN:
|
|
409
|
+
self.protocol.send_close()
|
|
410
|
+
elif frame.opcode == Opcode.PING:
|
|
411
|
+
# Respond to ping with pong
|
|
412
|
+
self.protocol.send_pong(frame.data)
|
|
413
|
+
elif frame.opcode == Opcode.PONG:
|
|
414
|
+
print("Received pong")
|
|
415
|
+
"""
|
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
from typing import Union, Literal
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
from .... import process,
|
|
4
|
+
from .... import process, print_api
|
|
5
5
|
from .. import config_install
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
PLUGIN_LIST: list = [
|
|
9
|
+
'qemu_exec',
|
|
10
|
+
'binwalk',
|
|
11
|
+
'users_and_passwords',
|
|
12
|
+
'kernel_config',
|
|
13
|
+
'cve_lookup',
|
|
14
|
+
'crypto_hints',
|
|
15
|
+
'input_vectors',
|
|
16
|
+
'cwe_checker',
|
|
17
|
+
'linter',
|
|
18
|
+
'ip_and_uri_finder',
|
|
19
|
+
'device_tree',
|
|
20
|
+
'file_system_metadata',
|
|
21
|
+
'ipc',
|
|
22
|
+
'software_components',
|
|
23
|
+
'architecture_detection',
|
|
24
|
+
'known_vulnerabilities'
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
INSTALLING_STRINGS: list = ['Installing', 'plugin']
|
|
29
|
+
FINISHED_INSTALLING_STRINGS: list = ['Finished installing', 'plugin']
|
|
30
|
+
LOG_FINISHED_STRING: str = 'installation complete'
|
|
31
|
+
|
|
32
|
+
|
|
8
33
|
def install_after_restart(
|
|
9
34
|
installation_directory: str,
|
|
10
|
-
install_type: Union[
|
|
11
|
-
|
|
35
|
+
install_type: Union[
|
|
36
|
+
None,
|
|
37
|
+
Literal['backend', 'frontend', 'db']] = None,
|
|
38
|
+
log_level: Union[
|
|
39
|
+
None,
|
|
40
|
+
Literal['DEBUG', 'INFO', 'WARNING', 'ERROR']] = None,
|
|
41
|
+
log_file: Union[
|
|
42
|
+
None,
|
|
43
|
+
str] = None,
|
|
44
|
+
analyze_log: bool = False
|
|
45
|
+
) -> int:
|
|
12
46
|
"""
|
|
13
47
|
This function will continue the installation the FACT_core after the restart of the computer.
|
|
14
48
|
|
|
@@ -20,7 +54,13 @@ def install_after_restart(
|
|
|
20
54
|
--backend: Distributed setup, Install the FACT_core backend.
|
|
21
55
|
--frontend: Distributed setup, Install the FACT_core frontend.
|
|
22
56
|
--db: Distributed setup, Install the FACT_core database.
|
|
23
|
-
:
|
|
57
|
+
:param log_level: string, the log level to use for the installation.
|
|
58
|
+
The same as using the '--log-level' parameter in the 'install.py' script.
|
|
59
|
+
The default is 'INFO' in the 'install.py' script.
|
|
60
|
+
:param log_file: string, the log file to use for the installation.
|
|
61
|
+
The same as using the '--log-file' parameter in the 'install.py' script.
|
|
62
|
+
:param analyze_log: bool, if True, the log file will be analyzed for plugin installation errors.
|
|
63
|
+
:return: int, 0 if the installation was successful, otherwise 1.
|
|
24
64
|
"""
|
|
25
65
|
|
|
26
66
|
install_command: str = 'python3 "' + str(Path(installation_directory, config_install.INSTALL_FILE_PATH)) + '"'
|
|
@@ -28,8 +68,52 @@ def install_after_restart(
|
|
|
28
68
|
if install_type:
|
|
29
69
|
install_command = install_command + ' --' + install_type
|
|
30
70
|
|
|
71
|
+
if log_level:
|
|
72
|
+
install_command = install_command + ' --log_level ' + log_level
|
|
73
|
+
|
|
74
|
+
if log_file:
|
|
75
|
+
install_command = install_command + ' --log_file "' + log_file + '"'
|
|
76
|
+
|
|
31
77
|
# Install the FACT_core repo.
|
|
32
78
|
process.execute_with_live_output(cmd=install_command, verbose=True)
|
|
79
|
+
|
|
80
|
+
# Analyze the log file for errors.
|
|
81
|
+
if analyze_log and (install_type == 'backend' or install_type is None):
|
|
82
|
+
if not log_file:
|
|
83
|
+
log_file = str(Path.cwd() / config_install.INSTALL_LOG_FILE_NAME)
|
|
84
|
+
|
|
85
|
+
return analyze_log_file(log_file=log_file)
|
|
86
|
+
|
|
33
87
|
# Remove the FACT_core installation log.
|
|
34
|
-
working_directory_path: str = filesystem.get_working_directory()
|
|
88
|
+
# working_directory_path: str = filesystem.get_working_directory()
|
|
35
89
|
# filesystem.remove_file(str(Path(working_directory_path, config_install.INSTALL_LOG_FILE_NAME)))
|
|
90
|
+
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def analyze_log_file(log_file: str):
|
|
95
|
+
"""
|
|
96
|
+
This function will analyze the log file for plugin installation errors.
|
|
97
|
+
:param log_file:
|
|
98
|
+
:return:
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
with open(log_file, 'r') as file:
|
|
102
|
+
log_content: str = file.read()
|
|
103
|
+
|
|
104
|
+
for plugin in PLUGIN_LIST:
|
|
105
|
+
if f'{FINISHED_INSTALLING_STRINGS[0]} {plugin} {FINISHED_INSTALLING_STRINGS[1]}' not in log_content:
|
|
106
|
+
message = (f'Error: [{plugin}] installation failed.\n'
|
|
107
|
+
f'Check the log file: {log_file}\n'
|
|
108
|
+
f'Exiting...')
|
|
109
|
+
print_api.print_api(message, color='red')
|
|
110
|
+
return 1
|
|
111
|
+
|
|
112
|
+
if LOG_FINISHED_STRING not in log_content:
|
|
113
|
+
message = (f'Error: Installation failed.\n'
|
|
114
|
+
f'Check the log file: {log_file}\n'
|
|
115
|
+
f'Exiting...')
|
|
116
|
+
print_api.print_api(message, color='red')
|
|
117
|
+
return 1
|
|
118
|
+
|
|
119
|
+
return 0
|
|
@@ -174,6 +174,7 @@ class MongoDBWrapper:
|
|
|
174
174
|
self,
|
|
175
175
|
collection_name: str,
|
|
176
176
|
filter_query: dict = None,
|
|
177
|
+
projection: dict = None,
|
|
177
178
|
page: int = None,
|
|
178
179
|
items: int = None,
|
|
179
180
|
sort: dict[str, Literal[
|
|
@@ -192,6 +193,7 @@ class MongoDBWrapper:
|
|
|
192
193
|
filter_query = None
|
|
193
194
|
|
|
194
195
|
CHECK MORE EXAMPLES IN THE DOCSTRING OF THE FUNCTION 'find' BELOW which is not in this class.
|
|
196
|
+
:param projection: dict, the only fields to return or exclude.
|
|
195
197
|
:param page: int, the page number (Optional).
|
|
196
198
|
The results are filtered after results are fetched from db.
|
|
197
199
|
:param items: int, the number of results per page (Optional).
|
|
@@ -229,7 +231,8 @@ class MongoDBWrapper:
|
|
|
229
231
|
|
|
230
232
|
entries: list[dict] = find(
|
|
231
233
|
database=self.db, collection_name=collection_name,
|
|
232
|
-
filter_query=filter_query,
|
|
234
|
+
filter_query=filter_query, projection=projection,
|
|
235
|
+
page=page, items=items, sort=sort,
|
|
233
236
|
convert_object_id_to_str=convert_object_id_to_str, key_convert_to_dict=keys_convert_to_dict,
|
|
234
237
|
mongo_client=self.client, close_client=False)
|
|
235
238
|
|
|
@@ -827,7 +830,7 @@ def find(
|
|
|
827
830
|
|
|
828
831
|
entries: list[dict] = list(collection_items)
|
|
829
832
|
|
|
830
|
-
if convert_object_id_to_str:
|
|
833
|
+
if entries and convert_object_id_to_str and '_id' in entries[0]:
|
|
831
834
|
for entry_index, entry in enumerate(entries):
|
|
832
835
|
entries[entry_index]['_id'] = str(entry['_id'])
|
|
833
836
|
|
|
@@ -44,8 +44,12 @@ def get_page_text_content(page) -> str:
|
|
|
44
44
|
:return: string, text content of the page.
|
|
45
45
|
"""
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
# Full javascript.
|
|
48
|
+
# text_content: str = page.evaluate('''() => {
|
|
49
|
+
# return document.body.innerText;
|
|
50
|
+
# }''')
|
|
51
|
+
|
|
52
|
+
# Short javascript.
|
|
53
|
+
text_content: str = page.evaluate("document.body.innerText")
|
|
50
54
|
|
|
51
55
|
return text_content
|
|
@@ -43,8 +43,12 @@ def get_text_from_html_tag(url: str, tag_name: str, attribute: str, value: str)
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def get_page_content(
|
|
46
|
-
url: str,
|
|
47
|
-
|
|
46
|
+
url: str,
|
|
47
|
+
page_format: str = 'html',
|
|
48
|
+
path: str = None,
|
|
49
|
+
pdf_format: str = 'A4',
|
|
50
|
+
html_txt_convert_to_bytes: bool = True,
|
|
51
|
+
print_kwargs: dict = None
|
|
48
52
|
) -> any:
|
|
49
53
|
"""
|
|
50
54
|
The function receives playwright engine and page object, navigates to URL, gets page content in specified format,
|
|
@@ -57,10 +61,10 @@ def get_page_content(
|
|
|
57
61
|
'png' - returns png binary.
|
|
58
62
|
'jpeg' - returns jpeg binary.
|
|
59
63
|
:param path: string of path to save the file to. Default is None.
|
|
60
|
-
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
61
64
|
:param pdf_format: string of pdf format, applicable only if 'page_format=pdf'. Default is 'A4'.
|
|
62
65
|
:param html_txt_convert_to_bytes: boolean, applicable only if 'page_format=html' or 'page_format=txt'.
|
|
63
66
|
Default is True.
|
|
67
|
+
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
64
68
|
|
|
65
69
|
:return: any page content in specified format.
|
|
66
70
|
"""
|
|
@@ -95,8 +99,13 @@ def get_page_content(
|
|
|
95
99
|
|
|
96
100
|
|
|
97
101
|
def get_page_content_in_thread(
|
|
98
|
-
url: str,
|
|
99
|
-
|
|
102
|
+
url: str,
|
|
103
|
+
page_format: str = 'html',
|
|
104
|
+
path: str = None,
|
|
105
|
+
pdf_format: str = 'A4',
|
|
106
|
+
html_txt_convert_to_bytes: bool = True,
|
|
107
|
+
print_kwargs: dict = None
|
|
108
|
+
):
|
|
100
109
|
"""
|
|
101
110
|
The function uses 'threads.thread_wrap_var' function in order to wrap the function 'get_page_content' and
|
|
102
111
|
execute it in a thread with arguments and return the result.
|
|
@@ -144,8 +144,23 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
144
144
|
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
145
145
|
"""
|
|
146
146
|
|
|
147
|
+
def get_current_interface_status(current_interface_settings: dict) -> tuple[bool, list[str]]:
|
|
148
|
+
if current_interface_settings['NameServer']:
|
|
149
|
+
result = (False, current_interface_settings['NameServer'].split(','))
|
|
150
|
+
else:
|
|
151
|
+
result = (True, current_interface_settings['DhcpNameServer'].split(','))
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
|
|
147
156
|
# Get current default IPv4 address of the interface that is being used for internet.
|
|
148
157
|
default_ipv4_address: str = socket.gethostbyname(socket.gethostname())
|
|
158
|
+
# If the default IPv4 address is localhost, then it could mean that the system is not connected to the internet.
|
|
159
|
+
# Or there is no network adapter at all.
|
|
160
|
+
default_dns_gateway_list: list[str] = []
|
|
161
|
+
if default_ipv4_address == '127.0.0.1':
|
|
162
|
+
from ... import dns
|
|
163
|
+
default_dns_gateway_list = dns.get_default_dns_gateway_with_dns_resolver()
|
|
149
164
|
|
|
150
165
|
# Get all network interface settings from the registry.
|
|
151
166
|
all_interfaces_configurations = get_network_interfaces_settings()
|
|
@@ -153,22 +168,36 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
153
168
|
# Find the interface that has this IPv4 assigned.
|
|
154
169
|
function_result: tuple = tuple()
|
|
155
170
|
for interface_guid, interface_settings in all_interfaces_configurations.items():
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
if not interface_settings:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
if ' ' in interface_settings['NameServer']:
|
|
175
|
+
interface_settings['NameServer'] = interface_settings['NameServer'].replace(' ', ',')
|
|
176
|
+
if ' ' in interface_settings['DhcpNameServer']:
|
|
177
|
+
interface_settings['DhcpNameServer'] = interface_settings['DhcpNameServer'].replace(' ', ',')
|
|
178
|
+
|
|
179
|
+
if not default_dns_gateway_list:
|
|
180
|
+
current_interface_static_ipv4_address: list = interface_settings.get('IPAddress', None)
|
|
181
|
+
current_interface_dynamic_ipv4_address: str = interface_settings.get('DhcpIPAddress', None)
|
|
182
|
+
|
|
183
|
+
static_and_ip_match: bool = (
|
|
184
|
+
current_interface_static_ipv4_address and
|
|
185
|
+
current_interface_static_ipv4_address[0] == default_ipv4_address)
|
|
186
|
+
dynamic_and_ip_match: bool = (
|
|
187
|
+
current_interface_dynamic_ipv4_address and
|
|
188
|
+
current_interface_dynamic_ipv4_address == default_ipv4_address)
|
|
189
|
+
if static_and_ip_match or dynamic_and_ip_match:
|
|
190
|
+
function_result = get_current_interface_status(interface_settings)
|
|
191
|
+
|
|
192
|
+
break
|
|
193
|
+
else:
|
|
194
|
+
current_interface_name_server_list: list[str] = interface_settings['NameServer'].split(',')
|
|
195
|
+
current_interface_dhcp_name_server_list: list[str] = interface_settings['DhcpNameServer'].split(',')
|
|
196
|
+
if (current_interface_name_server_list == default_dns_gateway_list or
|
|
197
|
+
current_interface_dhcp_name_server_list == default_dns_gateway_list):
|
|
198
|
+
function_result = get_current_interface_status(interface_settings)
|
|
199
|
+
|
|
200
|
+
break
|
|
172
201
|
|
|
173
202
|
# noinspection PyTypeChecker
|
|
174
203
|
return function_result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: atomicshop
|
|
3
|
-
Version: 2.16.
|
|
3
|
+
Version: 2.16.44
|
|
4
4
|
Summary: Atomic functions and classes to make developer life easier
|
|
5
5
|
Author: Denis Kras
|
|
6
6
|
License: MIT License
|
|
@@ -60,6 +60,7 @@ Requires-Dist: SoundCard
|
|
|
60
60
|
Requires-Dist: soundfile
|
|
61
61
|
Requires-Dist: SpeechRecognition
|
|
62
62
|
Requires-Dist: tldextract
|
|
63
|
+
Requires-Dist: websockets
|
|
63
64
|
Requires-Dist: pywin32 ; platform_system == "Windows"
|
|
64
65
|
|
|
65
66
|
<h1 align="center">Atomic Workshop</h1>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=cTkolJcyCusHq9bBDXgphXYHAv5YCLiWysUXbZegGZc,124
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
|
|
4
4
|
atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
|
|
@@ -10,7 +10,7 @@ atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,
|
|
|
10
10
|
atomicshop/console_user_response.py,sha256=31HIy9QGXa7f-GVR8MzJauQ79E_ZqAeagF3Ks4GGdDU,3234
|
|
11
11
|
atomicshop/datetimes.py,sha256=IQZ66lmta-ZqxYbyHzm_9eugbJFSilXK1e0kfMgoXGg,18371
|
|
12
12
|
atomicshop/diff_check.py,sha256=Q9RCqRa-jEgo7Fujx08_JTuZ6qcgttMI6aNYB6zN9Ik,27173
|
|
13
|
-
atomicshop/dns.py,sha256=
|
|
13
|
+
atomicshop/dns.py,sha256=5Gimq_WY2arqg7BeGmR7P--fGfnH0Dsh8lrOt_H0jRY,6817
|
|
14
14
|
atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
|
|
15
15
|
atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
|
|
16
16
|
atomicshop/file_types.py,sha256=-0jzQMRlmU1AP9DARjk-HJm1tVE22E6ngP2mRblyEjY,763
|
|
@@ -19,7 +19,7 @@ atomicshop/functions.py,sha256=pK8hoCE9z61PtWCxQJsda7YAphrLH1wxU5x-1QJP-sY,499
|
|
|
19
19
|
atomicshop/get_process_list.py,sha256=8cxb7gKe9sl4R6H2yMi8J6oe-RkonTvCdKjRFqi-Fs4,6075
|
|
20
20
|
atomicshop/get_process_name_cmd_dll.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
|
|
21
21
|
atomicshop/hashing.py,sha256=Le8qGFyt3_wX-zGTeQShz7L2HL_b6nVv9PnawjglyHo,3474
|
|
22
|
-
atomicshop/http_parse.py,sha256=
|
|
22
|
+
atomicshop/http_parse.py,sha256=WoUoz5HeiIjPzFQYn7d2aeuAKa0kVWvrswyKMZT6VUg,10039
|
|
23
23
|
atomicshop/inspect_wrapper.py,sha256=sGRVQhrJovNygHTydqJj0hxES-aB2Eg9KbIk3G31apw,11429
|
|
24
24
|
atomicshop/ip_addresses.py,sha256=Hvi4TumEFoTEpKWaq5WNF-YzcRzt24IxmNgv-Mgax1s,1190
|
|
25
25
|
atomicshop/keyboard_press.py,sha256=1W5kRtOB75fulVx-uF2yarBhW0_IzdI1k73AnvXstk0,452
|
|
@@ -44,7 +44,8 @@ atomicshop/timer.py,sha256=7Zw1KRV0acHCRATMnanyX2MLBb63Hc-6us3rCZ9dNlY,2345
|
|
|
44
44
|
atomicshop/urls.py,sha256=aJ0NGS9qqaKeqjkkWBs80jaBBg6MYBiPuLIyPGxscVc,1557
|
|
45
45
|
atomicshop/uuids.py,sha256=JSQdm3ZTJiwPQ1gYe6kU0TKS_7suwVrHc8JZDGYlydM,2214
|
|
46
46
|
atomicshop/virtualization.py,sha256=LPP4vjE0Vr10R6DA4lqhfX_WaNdDGRAZUW0Am6VeGco,494
|
|
47
|
-
atomicshop/web.py,sha256=
|
|
47
|
+
atomicshop/web.py,sha256=GLdTXgMxg1_0UQaXC4bOvARVyuFg7SPIeJdsCHV8rNE,11662
|
|
48
|
+
atomicshop/websocket_parse.py,sha256=PygRqEFnqEcPx3PNRtJoZcKN-mXvT_zighzTJR_B-Tc,14934
|
|
48
49
|
atomicshop/a_installs/ubuntu/docker_rootless.py,sha256=9IPNtGZYjfy1_n6ZRt7gWz9KZgR6XCgevjqq02xk-o0,281
|
|
49
50
|
atomicshop/a_installs/ubuntu/docker_sudo.py,sha256=JzayxeyKDtiuT4Icp2L2LyFRbx4wvpyN_bHLfZ-yX5E,281
|
|
50
51
|
atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py,sha256=yRB-l1zBxdiN6av-FwNkhcBlaeu4zrDPjQ0uPGgpK2I,244
|
|
@@ -84,7 +85,7 @@ atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
84
85
|
atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX98jcBMpJE,3140
|
|
85
86
|
atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
|
|
86
87
|
atomicshop/basics/booleans.py,sha256=V36NaMf8AffhCom_ovQeOZlYcdtGyIcQwWKki6h7O0M,1745
|
|
87
|
-
atomicshop/basics/bytes_arrays.py,sha256=
|
|
88
|
+
atomicshop/basics/bytes_arrays.py,sha256=tU7KF0UAFSErGNcan4Uz1GJxeIYVRuUu9sHVZHgyNnw,6542
|
|
88
89
|
atomicshop/basics/classes.py,sha256=T0Bm13hKvkXG3med68ptL7XuoWiCi3TE-K5TMINDlrY,10655
|
|
89
90
|
atomicshop/basics/dicts.py,sha256=DeYHIh940pMMBrFhpXt4dsigFVYzTrlqWymNo4Pq_Js,14049
|
|
90
91
|
atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
|
|
@@ -116,7 +117,7 @@ atomicshop/etws/traces/trace_dns.py,sha256=WvOZm7KNdP4r6ofkZhUGi9WjtYlkV3mUp_yxi
|
|
|
116
117
|
atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=OM-bkK38uYMwWLZKNOTDa0Xdk3sO6sqsxoMUIiPvm5g,4656
|
|
117
118
|
atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
118
119
|
atomicshop/file_io/csvs.py,sha256=jBdm3_z5cMyvxLxJnGcybUAptHAbyL0r0tlLqY0sdTQ,9327
|
|
119
|
-
atomicshop/file_io/docxs.py,sha256=
|
|
120
|
+
atomicshop/file_io/docxs.py,sha256=3ctQ9JiGx8K8EYeKWiuraLtqhilW1qk1cZX9lHv0usk,5753
|
|
120
121
|
atomicshop/file_io/file_io.py,sha256=5Kl0P6vF4GQVdwew1lzHLb-db9qiMvDjTgccbi5P-zk,7167
|
|
121
122
|
atomicshop/file_io/jsons.py,sha256=q9ZU8slBKnHLrtn3TnbK1qxrRpj5ZvCm6AlsFzoANjo,5303
|
|
122
123
|
atomicshop/file_io/tomls.py,sha256=ol8EvQPf9sryTmZUf1v55BYSUQ6ml7HVVBHpNKbsIlA,9768
|
|
@@ -125,11 +126,11 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
|
|
|
125
126
|
atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
126
127
|
atomicshop/mitm/config_static.py,sha256=ROAtbibSWSsF3BraUbhu-QO3MPIFqYY5KUKgsQbiSkk,7813
|
|
127
128
|
atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
|
|
128
|
-
atomicshop/mitm/connection_thread_worker.py,sha256=
|
|
129
|
+
atomicshop/mitm/connection_thread_worker.py,sha256=DxHCgDtCbSQCIX80WK9hJhp3v37pMjmm-mr3y6tyJMY,17581
|
|
129
130
|
atomicshop/mitm/import_config.py,sha256=0Ij14aISTllTOiWYJpIUMOWobQqGofD6uafui5uWllE,9272
|
|
130
131
|
atomicshop/mitm/initialize_engines.py,sha256=VyJE8QnzlgD3QbX5inz5o6rC3zQ3is9CeTL7-B10g1w,8292
|
|
131
132
|
atomicshop/mitm/message.py,sha256=URR5JKSuAT8XmGIkyprEjlPW2GW4ef_gfUz_GgcFseE,2184
|
|
132
|
-
atomicshop/mitm/mitm_main.py,sha256=
|
|
133
|
+
atomicshop/mitm/mitm_main.py,sha256=5c-9oxBiLueTbZr4Dyd4EEOorEUix5vSWxX9p5O1fBs,23375
|
|
133
134
|
atomicshop/mitm/recs_files.py,sha256=mMyO1kPB-VkS_pbWCDhZHKdbWzlPbYSout61QuzHOao,3077
|
|
134
135
|
atomicshop/mitm/shared_functions.py,sha256=l6oEyv4ug5D_03V3QLADYoocbcL2Ml_dYVW2WKM21l4,1818
|
|
135
136
|
atomicshop/mitm/statistic_analyzer.py,sha256=5_sAYGX2Xunzo_pS2W5WijNCwr_BlGJbbOO462y_wN4,27533
|
|
@@ -228,7 +229,7 @@ atomicshop/wrappers/factw/fact_extractor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
|
228
229
|
atomicshop/wrappers/factw/fact_extractor/docker_image.py,sha256=2FyYjnw8gxFNwISQ83OwH-iGivkFi6EAluyCZ0loHEQ,2501
|
|
229
230
|
atomicshop/wrappers/factw/fact_extractor/get_extractor.py,sha256=2mfOAftHIlCcGt1s7MWdq7DsDCuI6wX3MtvcEZ4SK-0,756
|
|
230
231
|
atomicshop/wrappers/factw/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
231
|
-
atomicshop/wrappers/factw/install/install_after_restart.py,sha256
|
|
232
|
+
atomicshop/wrappers/factw/install/install_after_restart.py,sha256=cr8E9BAEdpJXJ1VdyXr85byU0PvE3tPV0uPALQOmiKg,4318
|
|
232
233
|
atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py,sha256=GFsO9MTH0czKoxkiPJtjalilUwsmFLBCcx9Znv37S4M,5945
|
|
233
234
|
atomicshop/wrappers/factw/postgresql/__init__.py,sha256=xMBn2d3Exo23IPP2F_9-SXmOlhFbwWDgS9KwozSTjA0,162
|
|
234
235
|
atomicshop/wrappers/factw/postgresql/analysis.py,sha256=2Rxzy2jyq3zEKIo53z8VkjuslKE_i5mq2ZpmJAvyd6U,716
|
|
@@ -257,7 +258,7 @@ atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
257
258
|
atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py,sha256=pmI9AwWJ2cv5h8GionSpSJkllg6kfp0M381pk6y4Y5U,4015
|
|
258
259
|
atomicshop/wrappers/mongodbw/install_mongodb_win.py,sha256=3ZPqrXxj3lC-PnAKGXclylLuOqsbyXYeUpb5iGjdeUU,6626
|
|
259
260
|
atomicshop/wrappers/mongodbw/mongo_infra.py,sha256=IjEF0jPzQz866MpTm7rnksnyyWQeUT_B2h2DA9ryAio,2034
|
|
260
|
-
atomicshop/wrappers/mongodbw/mongodbw.py,sha256=
|
|
261
|
+
atomicshop/wrappers/mongodbw/mongodbw.py,sha256=IkEw86QFyVRU-5p5s6_6yupvSxmaQxr59GKNgSEkAm4,52617
|
|
261
262
|
atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
262
263
|
atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=TKGa3jSlSqZTL2NA0nMkWDFtlkz7rxGGn44ywCg7MN8,5228
|
|
263
264
|
atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -266,11 +267,11 @@ atomicshop/wrappers/playwrightw/base.py,sha256=WeRpx8otdXuKSr-BjY-uCJTze21kbPpfi
|
|
|
266
267
|
atomicshop/wrappers/playwrightw/combos.py,sha256=215y7wugyyBrFK9_0WutnMXsF1jqJ2PLm9eHEa2PMz0,7145
|
|
267
268
|
atomicshop/wrappers/playwrightw/engine.py,sha256=fapOeaqSMNoxJucTvhzp30cKfKNSVe45drhZJcJ-fOk,15191
|
|
268
269
|
atomicshop/wrappers/playwrightw/infra.py,sha256=ncp62l6CgAgLz4lqBtKEWZO_6ES8cqnbzJBov-tlpBo,97
|
|
269
|
-
atomicshop/wrappers/playwrightw/javascript.py,sha256=
|
|
270
|
+
atomicshop/wrappers/playwrightw/javascript.py,sha256=_bW7CAtm0Y8IHYrAalg5HpPFnk6juiqicmkvZ9dITaA,1235
|
|
270
271
|
atomicshop/wrappers/playwrightw/keyboard.py,sha256=zN3YddGO-qUkn6C0CRVFejP4cTuaUwXLDNFhFREjERY,422
|
|
271
272
|
atomicshop/wrappers/playwrightw/locators.py,sha256=6wsLywZxDuii7mwv-zQsRbqQC8r7j96Bma5b5_7ZoVo,2411
|
|
272
273
|
atomicshop/wrappers/playwrightw/mouse.py,sha256=-2FZbQtjgH7tdXWld6ZPGqlKFUdf5in0ujN0hewxa50,656
|
|
273
|
-
atomicshop/wrappers/playwrightw/scenarios.py,sha256=
|
|
274
|
+
atomicshop/wrappers/playwrightw/scenarios.py,sha256=OzI3SV0QgazRwMZ0hMEopDHUYG-aygBSxZ50w78lIP8,5310
|
|
274
275
|
atomicshop/wrappers/playwrightw/waits.py,sha256=PBFdz_PoM7Fo7O8hLqMrxNPzBEYgPoXwZceFFCGGeu8,7182
|
|
275
276
|
atomicshop/wrappers/psutilw/cpus.py,sha256=w6LPBMINqS-T_X8vzdYkLS2Wzuve28Ydp_GafTCngrc,236
|
|
276
277
|
atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mBCX8G4jE,9221
|
|
@@ -313,9 +314,9 @@ atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9E
|
|
|
313
314
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
|
|
314
315
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=w1AH-zf4mBuT4euf28UKij9ihM-b1BRU9Qfby0QDdqI,2957
|
|
315
316
|
atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
316
|
-
atomicshop/wrappers/winregw/winreg_network.py,sha256=
|
|
317
|
-
atomicshop-2.16.
|
|
318
|
-
atomicshop-2.16.
|
|
319
|
-
atomicshop-2.16.
|
|
320
|
-
atomicshop-2.16.
|
|
321
|
-
atomicshop-2.16.
|
|
317
|
+
atomicshop/wrappers/winregw/winreg_network.py,sha256=ChnVG8ZecKJ-DMF8nUHRiifWJq2M4slEwKat6FBfPfE,8685
|
|
318
|
+
atomicshop-2.16.44.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
319
|
+
atomicshop-2.16.44.dist-info/METADATA,sha256=yeA-HD5CtX5FhTCrJG9ylhYHECkHvJL_ai4gaOnilY4,10500
|
|
320
|
+
atomicshop-2.16.44.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
321
|
+
atomicshop-2.16.44.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
322
|
+
atomicshop-2.16.44.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|