atomicshop 3.3.8__py3-none-any.whl → 3.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/basics/strings.py +1 -1
- atomicshop/certificates.py +2 -2
- atomicshop/dns.py +26 -28
- atomicshop/etws/traces/trace_tcp.py +1 -2
- atomicshop/mitm/centered_settings.py +133 -0
- atomicshop/mitm/config_static.py +22 -44
- atomicshop/mitm/connection_thread_worker.py +383 -165
- atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
- atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
- atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
- atomicshop/mitm/engines/create_module_template.py +1 -2
- atomicshop/mitm/import_config.py +91 -89
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +238 -122
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +33 -12
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +104 -31
- atomicshop/networks.py +160 -92
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/process.py +11 -4
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +140 -164
- atomicshop/web.py +63 -22
- atomicshop/web_apis/google_llm.py +22 -14
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
- atomicshop/wrappers/dockerw/dockerw.py +2 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +0 -190
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +180 -68
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +20 -4
- atomicshop/wrappers/loggingw/reading.py +18 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +0 -38
- atomicshop/wrappers/netshw.py +124 -3
- atomicshop/wrappers/playwrightw/scenarios.py +1 -1
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
- atomicshop/wrappers/socketw/certificator.py +19 -9
- atomicshop/wrappers/socketw/creator.py +101 -14
- atomicshop/wrappers/socketw/dns_server.py +17 -5
- atomicshop/wrappers/socketw/exception_wrapper.py +21 -16
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +29 -9
- atomicshop/wrappers/socketw/sender.py +10 -9
- atomicshop/wrappers/socketw/sni.py +31 -10
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +11 -10
- atomicshop/wrappers/socketw/socket_wrapper.py +125 -32
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/ubuntu_terminal.py +21 -18
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/RECORD +83 -109
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
- atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
- atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py +0 -10
- atomicshop/a_installs/ubuntu/mongodb.py +0 -12
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/wsl_ubuntu_lts.py +0 -10
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/__init__.py +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/search_in_archive.py +0 -284
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -73
- atomicshop/archiver/shutils.py +0 -34
- atomicshop/archiver/zips.py +0 -353
- atomicshop/file_types.py +0 -24
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/script_as_string_processor.py +0 -42
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -449
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -233
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/__init__.py +0 -0
- atomicshop/wrappers/fibratusw/install.py +0 -80
- atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py +0 -100
- atomicshop/wrappers/mongodbw/install_mongodb_win.py +0 -244
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- atomicshop/wrappers/wslw.py +0 -192
- atomicshop-3.3.8.dist-info/entry_points.txt +0 -2
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
atomicshop/networks.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
import time
|
|
3
|
-
from typing import Union
|
|
3
|
+
from typing import Union, Literal
|
|
4
4
|
import os
|
|
5
5
|
import psutil
|
|
6
6
|
import ctypes
|
|
7
|
+
from logging import Logger
|
|
8
|
+
import subprocess
|
|
7
9
|
|
|
8
10
|
from icmplib import ping
|
|
9
11
|
from icmplib.models import Host
|
|
10
12
|
from win32com.client import CDispatch
|
|
11
13
|
|
|
12
|
-
from .
|
|
14
|
+
from .print_api import print_api
|
|
15
|
+
from .wrappers.pywin32w.wmis import win32networkadapter, win32_networkadapterconfiguration, wmi_helpers
|
|
13
16
|
from .wrappers.ctyping import setup_device
|
|
14
17
|
from .wrappers.winregw import winreg_network
|
|
15
18
|
from .wrappers.psutilw import psutil_networks
|
|
19
|
+
from .wrappers import powershell_networking, netshw
|
|
20
|
+
from .wrappers.socketw import socket_base
|
|
16
21
|
|
|
17
22
|
|
|
18
23
|
MICROSOFT_LOOPBACK_DEVICE_NAME: str = 'Microsoft KM-TEST Loopback Adapter'
|
|
@@ -61,6 +66,7 @@ def is_ip_in_use_arp(
|
|
|
61
66
|
def _send_arp(ip: str) -> str | None:
|
|
62
67
|
"""Return MAC string like 'aa:bb:cc:dd:ee:ff' if IP is claimed on the LAN, else None."""
|
|
63
68
|
# inet_addr returns DWORD in network byte order
|
|
69
|
+
# noinspection PyUnresolvedReferences
|
|
64
70
|
dest_ip = ws2_32.inet_addr(ip.encode('ascii'))
|
|
65
71
|
if dest_ip == 0xFFFFFFFF: # INVALID
|
|
66
72
|
raise ValueError(f"Bad IPv4 address: {ip}")
|
|
@@ -68,6 +74,7 @@ def is_ip_in_use_arp(
|
|
|
68
74
|
mac_buf = ctypes.c_uint64(0) # storage for up to 8 bytes
|
|
69
75
|
mac_len = ctypes.c_ulong(ctypes.sizeof(mac_buf)) # in/out len
|
|
70
76
|
# SrcIP=0 lets Windows pick the right interface
|
|
77
|
+
# noinspection PyUnresolvedReferences
|
|
71
78
|
rc = iphlpapi.SendARP(dest_ip, 0, ctypes.byref(mac_buf), ctypes.byref(mac_len))
|
|
72
79
|
if rc != 0: # Non-zero means no ARP reply / not on-link / other error
|
|
73
80
|
return None
|
|
@@ -105,32 +112,34 @@ def get_default_internet_ipv4(target: str = "8.8.8.8") -> str:
|
|
|
105
112
|
return s.getsockname()[0] # local address of that route
|
|
106
113
|
|
|
107
114
|
|
|
108
|
-
def
|
|
115
|
+
def get_default_interface_name() -> str:
|
|
116
|
+
default_connection_name_dict: dict = psutil_networks.get_default_connection_name()
|
|
117
|
+
if not default_connection_name_dict:
|
|
118
|
+
return ""
|
|
119
|
+
# Get the first key from the dictionary.
|
|
120
|
+
connection_name: str = list(default_connection_name_dict.keys())[0]
|
|
121
|
+
return connection_name
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def list_network_interfaces() -> list[str]:
|
|
109
125
|
"""
|
|
110
|
-
|
|
111
|
-
:return:
|
|
126
|
+
List all network interfaces on the system.
|
|
127
|
+
:return: list of strings, network interface names.
|
|
112
128
|
"""
|
|
113
129
|
|
|
114
|
-
return
|
|
130
|
+
return psutil_networks.list_network_interfaces()
|
|
115
131
|
|
|
116
132
|
|
|
117
|
-
def
|
|
133
|
+
def get_hostname() -> str:
|
|
118
134
|
"""
|
|
119
135
|
Get the default network interface name that is being used for internet.
|
|
120
136
|
:return: string, default network interface name.
|
|
121
137
|
"""
|
|
122
138
|
|
|
123
|
-
|
|
124
|
-
if not interface_dict:
|
|
125
|
-
result = None
|
|
126
|
-
else:
|
|
127
|
-
# Get the first interface name from the dictionary.
|
|
128
|
-
result = next(iter(interface_dict.keys()), None)
|
|
129
|
-
|
|
130
|
-
return result
|
|
139
|
+
return socket.gethostname()
|
|
131
140
|
|
|
132
141
|
|
|
133
|
-
def
|
|
142
|
+
def get_interface_ips_psutil(
|
|
134
143
|
interface_name: str = None,
|
|
135
144
|
ipv4: bool = True,
|
|
136
145
|
ipv6: bool = True,
|
|
@@ -144,7 +153,7 @@ def get_interface_ips(
|
|
|
144
153
|
|
|
145
154
|
if default_interface:
|
|
146
155
|
# Get the default interface name.
|
|
147
|
-
interface_name =
|
|
156
|
+
interface_name = get_default_interface_name()
|
|
148
157
|
|
|
149
158
|
physical_ip_types: list[str] = []
|
|
150
159
|
if ipv4:
|
|
@@ -169,6 +178,75 @@ def get_interface_ips(
|
|
|
169
178
|
return ips
|
|
170
179
|
|
|
171
180
|
|
|
181
|
+
def get_host_ips_psutil(
|
|
182
|
+
localhost: bool = True,
|
|
183
|
+
ipv4: bool = True,
|
|
184
|
+
ipv6: bool = True
|
|
185
|
+
) -> list[str]:
|
|
186
|
+
"""
|
|
187
|
+
Yield (ifname, family, ip) for all UP interfaces that have bindable addresses.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
localhost: include 127.0.0.0/8 and ::1 if True.
|
|
191
|
+
ipv4: include IPv4 addresses if True.
|
|
192
|
+
ipv6: include IPv6 addresses if True.
|
|
193
|
+
"""
|
|
194
|
+
stats = psutil.net_if_stats()
|
|
195
|
+
|
|
196
|
+
ip_list: list[str] = []
|
|
197
|
+
for ifname, addrs in psutil.net_if_addrs().items():
|
|
198
|
+
st = stats.get(ifname)
|
|
199
|
+
if not st or not st.isup:
|
|
200
|
+
continue # interface is down or unknown
|
|
201
|
+
|
|
202
|
+
for a in addrs:
|
|
203
|
+
fam = a.family
|
|
204
|
+
if fam not in (socket.AF_INET, socket.AF_INET6):
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
# Family filters
|
|
208
|
+
if fam == socket.AF_INET and not ipv4:
|
|
209
|
+
continue
|
|
210
|
+
if fam == socket.AF_INET6 and not ipv6:
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
ip = a.address
|
|
214
|
+
|
|
215
|
+
# Skip placeholders/wildcards
|
|
216
|
+
if fam == socket.AF_INET and ip == "0.0.0.0":
|
|
217
|
+
continue
|
|
218
|
+
if fam == socket.AF_INET6 and ip in ("::",):
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
# Optionally skip loopback
|
|
222
|
+
if not localhost:
|
|
223
|
+
if fam == socket.AF_INET and ip.startswith("127."):
|
|
224
|
+
continue
|
|
225
|
+
if fam == socket.AF_INET6 and (ip == "::1" or ip.startswith("::1%")):
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
# yield ifname, fam, ip
|
|
229
|
+
ip_list.append(ip)
|
|
230
|
+
|
|
231
|
+
return ip_list
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def get_interface_ips_powershell(
|
|
235
|
+
interface_name: str = None,
|
|
236
|
+
ip_type: Literal["virtual", "dynamic", "all"] = "virtual"
|
|
237
|
+
) -> list[str]:
|
|
238
|
+
"""
|
|
239
|
+
Get the IP addresses of a network interface using PowerShell.
|
|
240
|
+
|
|
241
|
+
:param interface_name: string, name of the network interface.
|
|
242
|
+
If None, all interfaces will be queried.
|
|
243
|
+
:param ip_type: string, type of IP addresses to retrieve.
|
|
244
|
+
:return: list of strings, IP addresses of the network interface.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
return powershell_networking.get_interface_ips(interface_name=interface_name, ip_type=ip_type)
|
|
248
|
+
|
|
249
|
+
|
|
172
250
|
def get_microsoft_loopback_device_network_configuration(
|
|
173
251
|
wmi_instance: CDispatch = None,
|
|
174
252
|
timeout: int = 1,
|
|
@@ -303,17 +381,14 @@ def change_interface_metric_restart_device(
|
|
|
303
381
|
|
|
304
382
|
|
|
305
383
|
def get_wmi_network_adapter_configuration(
|
|
306
|
-
|
|
307
|
-
connection_name: str = None,
|
|
384
|
+
interface_name: str = None,
|
|
308
385
|
mac_address: str = None,
|
|
309
386
|
wmi_instance: CDispatch = None,
|
|
310
387
|
get_info_from_network_config: bool = True
|
|
311
388
|
) -> tuple:
|
|
312
389
|
"""
|
|
313
390
|
Get the WMI network configuration for a network adapter.
|
|
314
|
-
:param
|
|
315
|
-
This is the adapter that your internet is being used from.
|
|
316
|
-
:param connection_name: string, adapter name as shown in the network settings.
|
|
391
|
+
:param interface_name: string, adapter name as shown in the network settings.
|
|
317
392
|
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
318
393
|
:param wmi_instance: WMI instance. You can get it from:
|
|
319
394
|
wrappers.pywin32s.wmis.wmi_helpers.get_wmi_instance()
|
|
@@ -324,14 +399,14 @@ def get_wmi_network_adapter_configuration(
|
|
|
324
399
|
"""
|
|
325
400
|
|
|
326
401
|
wmi_network_config, wmi_network_adapter = win32_networkadapterconfiguration.get_adapter_network_configuration(
|
|
327
|
-
|
|
328
|
-
connection_name=connection_name,
|
|
402
|
+
interface_name=interface_name,
|
|
329
403
|
mac_address=mac_address,
|
|
330
404
|
wmi_instance=wmi_instance
|
|
331
405
|
)
|
|
332
406
|
|
|
333
407
|
if get_info_from_network_config:
|
|
334
408
|
adapter_info: dict = win32_networkadapterconfiguration.get_info_from_network_config(wmi_network_config)
|
|
409
|
+
adapter_info['name'] = wmi_network_adapter.NetConnectionID
|
|
335
410
|
else:
|
|
336
411
|
adapter_info: dict = {}
|
|
337
412
|
|
|
@@ -415,7 +490,7 @@ def generate_unused_ipv4_addresses_from_ip(
|
|
|
415
490
|
return generated_ips, masks_for_ips
|
|
416
491
|
|
|
417
492
|
|
|
418
|
-
def
|
|
493
|
+
def set_dynamic_ip_for_adapter_wmi(
|
|
419
494
|
network_config: CDispatch,
|
|
420
495
|
reset_dns: bool = True,
|
|
421
496
|
reset_wins: bool = True
|
|
@@ -431,7 +506,7 @@ def set_dynamic_ip_for_adapter(
|
|
|
431
506
|
nic_cfg=network_config, reset_dns=reset_dns, reset_wins=reset_wins)
|
|
432
507
|
|
|
433
508
|
|
|
434
|
-
def
|
|
509
|
+
def set_static_ip_for_adapter_wmi(
|
|
435
510
|
network_config: CDispatch,
|
|
436
511
|
ips: list[str],
|
|
437
512
|
masks: list[str],
|
|
@@ -459,19 +534,19 @@ def set_static_ip_for_adapter(
|
|
|
459
534
|
)
|
|
460
535
|
|
|
461
536
|
|
|
462
|
-
def
|
|
537
|
+
def add_virtual_ips_to_network_interface(
|
|
538
|
+
interface_name: str,
|
|
463
539
|
number_of_ips: int = 0,
|
|
464
540
|
virtual_ipv4s_to_add: list[str] = None,
|
|
465
541
|
virtual_ipv4_masks_to_add: list[str] = None,
|
|
466
542
|
set_virtual_ips_skip_as_source: bool = True,
|
|
467
|
-
gateways: list[str] | None = None,
|
|
468
|
-
dns_gateways: list[str] | None = None,
|
|
469
|
-
availability_wait_seconds: int = 15,
|
|
470
543
|
simulate_only: bool = False,
|
|
471
544
|
locator: CDispatch = None,
|
|
472
545
|
wait_until_applied: bool = True,
|
|
473
|
-
wait_until_applied_seconds: int = 15
|
|
474
|
-
|
|
546
|
+
wait_until_applied_seconds: int = 15,
|
|
547
|
+
verbose: bool = False,
|
|
548
|
+
logger: Logger = None,
|
|
549
|
+
) -> tuple[list[str], list[str]] | None:
|
|
475
550
|
"""
|
|
476
551
|
Add virtual IP addresses to the default network adapter.
|
|
477
552
|
The adapter will set to static IP and DNS gateway, instead of dynamic DHCP.
|
|
@@ -482,6 +557,8 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
482
557
|
While generating the IPs, the function will skip the already existing IPs in the adapter, like default gateway
|
|
483
558
|
and DNS servers.
|
|
484
559
|
|
|
560
|
+
:param interface_name: string, adapter name as shown in the network settings.
|
|
561
|
+
|
|
485
562
|
:param number_of_ips: int, number of IPs to generate in addition to the IPv4s that already exist in the adapter.
|
|
486
563
|
Or you add the IPs and masks to the adapter with the parameters virtual_ipv4s_to_add and virtual_ipv4_masks_to_add.
|
|
487
564
|
|
|
@@ -491,15 +568,11 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
491
568
|
|
|
492
569
|
:param set_virtual_ips_skip_as_source: bool, if True, the SkipAsSource flag will be set for the virtual IPs.
|
|
493
570
|
This is needed to avoid the endless accept() loop.
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
:param
|
|
498
|
-
|
|
499
|
-
[]: No DNS servers will be assigned.
|
|
500
|
-
:param availability_wait_seconds: int, seconds to wait for the adapter to be available after setting the IP address.
|
|
501
|
-
:param simulate_only: bool, if True, the function will only prepare the ip addresses and return them without changing anything.
|
|
502
|
-
:param locator: CDispatch, WMI locator object. If not specified, it will be created.
|
|
571
|
+
|
|
572
|
+
:param simulate_only: bool, if True, the function will only simulate the addition of the IP addresses.
|
|
573
|
+
No changes will be made to the system.
|
|
574
|
+
:param locator: CDispatch, WMI locator object. You can get it from:
|
|
575
|
+
wrappers.pywin32s.wmis.wmi_helpers.get_wmi_instance()
|
|
503
576
|
|
|
504
577
|
:param wait_until_applied: bool, if True, the function will wait until the IP addresses are applied.
|
|
505
578
|
By default, while WMI command is executed, there is no indication if the addresses were finished applying or not.
|
|
@@ -509,7 +582,10 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
509
582
|
after setting the IP addresses. This is the time to wait for the IP addresses to be
|
|
510
583
|
applied after setting them. If the IP addresses are not applied in this time, a TimeoutError will be raised.
|
|
511
584
|
|
|
512
|
-
:
|
|
585
|
+
:param verbose: bool, if True, the function will print verbose output.
|
|
586
|
+
:param logger: Logger, if provided, the function will log messages to this logger.
|
|
587
|
+
|
|
588
|
+
:return: tuple of lists, (ips_to_assign, masks_to_assign)
|
|
513
589
|
"""
|
|
514
590
|
|
|
515
591
|
if virtual_ipv4s_to_add and not virtual_ipv4_masks_to_add:
|
|
@@ -526,24 +602,19 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
526
602
|
# Connect to WMi.
|
|
527
603
|
wmi_civ2_instance, locator = wmi_helpers.get_wmi_instance(locator=locator)
|
|
528
604
|
|
|
529
|
-
#
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
default_network_adapter_config, default_network_adapter, default_adapter_info = get_wmi_network_adapter_configuration(
|
|
533
|
-
use_default_interface=True, wmi_instance=wmi_civ2_instance, get_info_from_network_config=True)
|
|
534
|
-
|
|
535
|
-
current_ipv4s: list[str] = default_adapter_info['ipv4s']
|
|
536
|
-
current_ipv4_masks: list[str] = default_adapter_info['ipv4_subnet_masks']
|
|
605
|
+
# Get the network adapter configuration.
|
|
606
|
+
network_adapter_config, network_adapter, adapter_info = get_wmi_network_adapter_configuration(
|
|
607
|
+
interface_name=interface_name, wmi_instance=wmi_civ2_instance, get_info_from_network_config=True)
|
|
537
608
|
|
|
538
|
-
|
|
539
|
-
|
|
609
|
+
current_ipv4s: list[str] = adapter_info['ipv4s']
|
|
610
|
+
current_ipv4_masks: list[str] = adapter_info['ipv4_subnet_masks']
|
|
540
611
|
|
|
541
612
|
if number_of_ips > 0:
|
|
542
613
|
ips_to_assign, masks_to_assign = generate_unused_ipv4_addresses_from_ip(
|
|
543
614
|
ip_address=current_ipv4s[0],
|
|
544
615
|
mask=current_ipv4_masks[0],
|
|
545
616
|
number_of_ips=number_of_ips,
|
|
546
|
-
skip_ips=current_ipv4s +
|
|
617
|
+
skip_ips=current_ipv4s + adapter_info['default_gateways'] + adapter_info['dns_gateways']
|
|
547
618
|
)
|
|
548
619
|
elif virtual_ipv4s_to_add and virtual_ipv4_masks_to_add:
|
|
549
620
|
ips_to_assign = virtual_ipv4s_to_add
|
|
@@ -553,51 +624,48 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
553
624
|
masks_to_assign = []
|
|
554
625
|
|
|
555
626
|
if not simulate_only:
|
|
556
|
-
#
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
ips_for_skip_as_source = ips.copy()
|
|
575
|
-
# Add it back to the beginning of the list.
|
|
576
|
-
ips.insert(0, default_ip_address)
|
|
577
|
-
|
|
578
|
-
win32_networkadapterconfiguration.set_static_ips(
|
|
579
|
-
default_network_adapter_config, ips=ips, masks=masks,
|
|
580
|
-
gateways=gateways, dns_gateways=dns_gateways,
|
|
581
|
-
availability_wait_seconds=availability_wait_seconds)
|
|
582
|
-
|
|
583
|
-
# If there were already virtual IPs assigned to the adapter and already were set SkipAsSource,
|
|
584
|
-
# we need to set SkipAsSource for them once again as well as for the new IPs.
|
|
585
|
-
if set_virtual_ips_skip_as_source:
|
|
586
|
-
wmi_standard_cimv2_instance, _ = wmi_helpers.get_wmi_instance(
|
|
587
|
-
namespace='root\\StandardCimv2', wmi_instance=wmi_civ2_instance, locator=locator)
|
|
588
|
-
msft_netipaddress.set_skip_as_source(ips_for_skip_as_source, enable=True, wmi_instance=wmi_standard_cimv2_instance)
|
|
589
|
-
else:
|
|
590
|
-
# print("[!] No new IPs to assign.")
|
|
591
|
-
pass
|
|
627
|
+
# Enable DHCP + static IP coexistence on the interface.
|
|
628
|
+
process_complete: subprocess.CompletedProcess = netshw.enable_dhcp_static_coexistence(interface_name=interface_name)
|
|
629
|
+
if process_complete.returncode != 0:
|
|
630
|
+
print_api(f"[!] Failed to enable DHCP + static IP coexistence on interface {interface_name}.\n"
|
|
631
|
+
f" stdout: {process_complete.stdout}\n"
|
|
632
|
+
f" stderr: {process_complete.stderr}", color="red", logger=logger)
|
|
633
|
+
return None
|
|
634
|
+
|
|
635
|
+
for ip, mask in zip(ips_to_assign, masks_to_assign):
|
|
636
|
+
if verbose:
|
|
637
|
+
print_api(f"[+] Adding virtual IP {ip} with mask {mask} to interface {interface_name}.", logger=logger)
|
|
638
|
+
|
|
639
|
+
netshw.add_virtual_ip(
|
|
640
|
+
interface_name=interface_name,
|
|
641
|
+
ip=ip,
|
|
642
|
+
mask=mask,
|
|
643
|
+
skip_as_source=set_virtual_ips_skip_as_source
|
|
644
|
+
)
|
|
592
645
|
|
|
593
646
|
if wait_until_applied:
|
|
594
647
|
# Wait until the IP addresses are applied.
|
|
595
648
|
for _ in range(wait_until_applied_seconds):
|
|
596
|
-
|
|
597
|
-
if set(
|
|
649
|
+
current_virtual_ips = get_interface_ips_powershell(interface_name=interface_name, ip_type="virtual")
|
|
650
|
+
if set(current_virtual_ips) == set(ips_to_assign):
|
|
598
651
|
break
|
|
599
652
|
time.sleep(1)
|
|
600
653
|
else:
|
|
601
654
|
raise TimeoutError("Timeout while waiting for the IP addresses to be applied.")
|
|
602
655
|
|
|
603
|
-
return
|
|
656
|
+
return ips_to_assign, masks_to_assign
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def wait_for_ip_bindable_socket(
|
|
660
|
+
ip: str,
|
|
661
|
+
port: int = 0,
|
|
662
|
+
timeout: float = 15.0,
|
|
663
|
+
interval: float = 0.5,
|
|
664
|
+
) -> None:
|
|
665
|
+
"""
|
|
666
|
+
Wait until a single IP is bindable (or timeout).
|
|
667
|
+
|
|
668
|
+
Raises TimeoutError if the IP cannot be bound within 'timeout' seconds.
|
|
669
|
+
"""
|
|
670
|
+
|
|
671
|
+
socket_base.wait_for_ip_bindable(ip=ip, port=port, timeout=timeout, interval=interval)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Loading resources using stdlib importlib.resources APIs (Python 3.7+)
|
|
2
|
+
https://docs.python.org/3/library/importlib.html#module-importlib.resources"""
|
|
3
|
+
import importlib.resources
|
|
4
|
+
from contextlib import redirect_stdout
|
|
5
|
+
import io
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PackageMainsProcessor:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
script_file_stem: str = None
|
|
15
|
+
):
|
|
16
|
+
self.script_file_stem: str = script_file_stem
|
|
17
|
+
self.resources_directory_name: str = 'a_mains'
|
|
18
|
+
|
|
19
|
+
def get_resource_path(self) -> str:
|
|
20
|
+
return f'{__package__}.{self.resources_directory_name}'
|
|
21
|
+
|
|
22
|
+
def read_script_file_to_string(self) -> str:
|
|
23
|
+
script_string = importlib.resources.read_text(self.get_resource_path(), f'{self.script_file_stem}.py')
|
|
24
|
+
|
|
25
|
+
return script_string
|
|
26
|
+
|
|
27
|
+
def execute_script_file(
|
|
28
|
+
self,
|
|
29
|
+
function_name: str = 'main',
|
|
30
|
+
args: tuple = None,
|
|
31
|
+
kwargs: dict = None,
|
|
32
|
+
get_printed_output: bool = False
|
|
33
|
+
) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Execute a script file from the package resources and get result as string.
|
|
36
|
+
|
|
37
|
+
:param function_name: Name of the function to call within the script.
|
|
38
|
+
:param args: Tuple of positional arguments to pass to the function.
|
|
39
|
+
:param kwargs: Dictionary of keyword arguments to pass to the function.
|
|
40
|
+
:param get_printed_output: If True, captures and returns printed output instead of return value.
|
|
41
|
+
|
|
42
|
+
:return: Output of the script execution as a string.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
if not args:
|
|
46
|
+
args = ()
|
|
47
|
+
if not kwargs:
|
|
48
|
+
kwargs = {}
|
|
49
|
+
|
|
50
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}" # script_file_name WITHOUT ".py"
|
|
51
|
+
|
|
52
|
+
module = importlib.import_module(module_name)
|
|
53
|
+
callable_function: Callable = getattr(module, function_name)
|
|
54
|
+
|
|
55
|
+
if get_printed_output:
|
|
56
|
+
with io.StringIO() as buffer, redirect_stdout(buffer):
|
|
57
|
+
callable_function(*args, **kwargs)
|
|
58
|
+
result = buffer.getvalue()
|
|
59
|
+
else:
|
|
60
|
+
result = callable_function(*args, **kwargs)
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def execute_script_with_subprocess(
|
|
65
|
+
self,
|
|
66
|
+
arguments: list = None
|
|
67
|
+
) -> tuple[str, str, int]:
|
|
68
|
+
"""
|
|
69
|
+
Execute a script file from the package resources using subprocess and get result as string.
|
|
70
|
+
:param arguments: Dictionary of arguments to pass to the script.
|
|
71
|
+
Example: ['--port', '8080', '-v']
|
|
72
|
+
:return: Tuple containing (stdout, stderr, returncode).
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# script_file_name WITHOUT ".py"
|
|
76
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}"
|
|
77
|
+
|
|
78
|
+
command = [sys.executable, "-m", module_name]
|
|
79
|
+
if arguments:
|
|
80
|
+
command.extend(arguments)
|
|
81
|
+
|
|
82
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
83
|
+
|
|
84
|
+
return result.stdout, result.stderr, result.returncode
|
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import stat
|
|
3
3
|
import contextlib
|
|
4
4
|
import subprocess
|
|
5
|
+
import getpass
|
|
5
6
|
|
|
6
7
|
# Import pwd only on linux.
|
|
7
8
|
if os.name == 'posix':
|
|
@@ -20,6 +21,52 @@ def get_sudo_executer_username() -> str:
|
|
|
20
21
|
return ''
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def detect_current_user(
|
|
25
|
+
optional_env_user_var: str = 'CUSTOM_SCRIPTED_USER'
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Try to robustly determine the 'real' installing user.
|
|
29
|
+
|
|
30
|
+
Priority:
|
|
31
|
+
1. FDB_INSTALL_USER env var (explicit override).
|
|
32
|
+
2. If running as root with sudo: use SUDO_USER.
|
|
33
|
+
3. Otherwise: use effective uid.
|
|
34
|
+
4. Fallbacks: getpass.getuser() / $USER.
|
|
35
|
+
|
|
36
|
+
:param optional_env_user_var: str, name of the environment variable that can override the user detection.
|
|
37
|
+
:return: str, username.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# 1. Explicit override for weird environments (CI, containers, etc.)
|
|
41
|
+
env_user = os.getenv(optional_env_user_var)
|
|
42
|
+
if env_user:
|
|
43
|
+
return env_user
|
|
44
|
+
|
|
45
|
+
# 2. If we are root, prefer the sudo caller if any
|
|
46
|
+
try:
|
|
47
|
+
euid = os.geteuid()
|
|
48
|
+
except AttributeError: # non-POSIX, very unlikely here
|
|
49
|
+
euid = None
|
|
50
|
+
|
|
51
|
+
if euid == 0:
|
|
52
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
53
|
+
if sudo_user:
|
|
54
|
+
return sudo_user
|
|
55
|
+
|
|
56
|
+
# 3. Normal case: effective uid -> username
|
|
57
|
+
if euid is not None:
|
|
58
|
+
try:
|
|
59
|
+
return pwd.getpwuid(euid).pw_name
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# 4. Fallbacks that don’t depend on utmp/tty
|
|
64
|
+
try:
|
|
65
|
+
return getpass.getuser()
|
|
66
|
+
except Exception:
|
|
67
|
+
return os.environ.get("USER", "unknown")
|
|
68
|
+
|
|
69
|
+
|
|
23
70
|
def set_executable(file_path: str):
|
|
24
71
|
"""
|
|
25
72
|
Function sets the executable permission on a file.
|
atomicshop/print_api.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import logging
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
from .basics import ansi_escape_codes
|
|
5
6
|
from .basics import tracebacks
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def print_api(
|
|
9
|
-
message:
|
|
10
|
-
color:
|
|
10
|
+
message: Any,
|
|
11
|
+
color: Any = None,
|
|
11
12
|
print_end: str = '\n',
|
|
12
13
|
rtl: bool = False,
|
|
13
14
|
error_type: bool = False,
|
|
@@ -73,7 +74,6 @@ def print_api(
|
|
|
73
74
|
|
|
74
75
|
# Inner functions already get all the local variables of the main function.
|
|
75
76
|
def print_or_logger():
|
|
76
|
-
from .wrappers.loggingw import loggingw
|
|
77
77
|
nonlocal message
|
|
78
78
|
nonlocal color
|
|
79
79
|
nonlocal traceback_string
|
|
@@ -83,8 +83,6 @@ def print_api(
|
|
|
83
83
|
|
|
84
84
|
# If 'rtl' is set to 'True', we'll add Right-To-Left text conversion to 'message'.
|
|
85
85
|
if rtl:
|
|
86
|
-
# Lazy importing of 'bidi' library. It's not a problem since python caches the library after first import.
|
|
87
|
-
# Off-course, it will be imported from the cache each time this section is triggered.
|
|
88
86
|
# pip install python-bidi
|
|
89
87
|
from bidi.algorithm import get_display
|
|
90
88
|
message = get_display(message)
|
atomicshop/process.py
CHANGED
|
@@ -316,14 +316,21 @@ def kill_process_by_filename_pattern(pattern: str):
|
|
|
316
316
|
processes.kill_process_by_pid(running_processes[0]['pid'])
|
|
317
317
|
|
|
318
318
|
|
|
319
|
-
def run_powershell_command(
|
|
319
|
+
def run_powershell_command(
|
|
320
|
+
command: str
|
|
321
|
+
):
|
|
320
322
|
try:
|
|
321
323
|
result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
|
|
322
|
-
|
|
324
|
+
if result.stdout:
|
|
325
|
+
print_api(result.stdout)
|
|
326
|
+
if result.stderr: # PS can write warnings to stderr even on success
|
|
327
|
+
print_api(result.stderr, color='yellow')
|
|
323
328
|
return result.stdout
|
|
324
329
|
except subprocess.CalledProcessError as e:
|
|
325
|
-
|
|
326
|
-
|
|
330
|
+
# e.stderr and e.stdout are populated because capture_output=True
|
|
331
|
+
msg = (e.stderr or e.stdout or f"PowerShell exited with code {e.returncode}")
|
|
332
|
+
print_api(msg, color='red', error_type=True)
|
|
333
|
+
return msg
|
|
327
334
|
|
|
328
335
|
|
|
329
336
|
"""
|