atomicshop 3.3.28__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 +18 -43
- atomicshop/mitm/connection_thread_worker.py +376 -162
- 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 +79 -88
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +222 -121
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/networks.py +108 -93
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +115 -51
- atomicshop/web.py +20 -7
- 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/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +175 -63
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +17 -1
- 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 +30 -7
- atomicshop/wrappers/socketw/dns_server.py +6 -6
- atomicshop/wrappers/socketw/exception_wrapper.py +3 -3
- 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 +23 -6
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +6 -8
- atomicshop/wrappers/socketw/socket_wrapper.py +82 -21
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/RECORD +74 -88
- 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/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/ffmpegw.py +0 -125
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- /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.28.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
|
|
3
|
+
import paramiko
|
|
4
|
+
|
|
5
|
+
from .. import package_mains_processor, ssh_remote, config_init
|
|
6
|
+
from ..wrappers.socketw import process_getter
|
|
7
|
+
from ..print_api import print_api
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PORT_TO_CMD_FILE: str = 'process_from_port'
|
|
11
|
+
TCP_PORTS_FILE: str = 'get_local_tcp_ports'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_ssh_main(config: dict) -> int:
|
|
15
|
+
hosts: list = config['main']['hosts_or_ips']
|
|
16
|
+
|
|
17
|
+
for host in hosts:
|
|
18
|
+
print("-----------------------------------")
|
|
19
|
+
print_api(f"Testing cmd for host: {host}", color='blue')
|
|
20
|
+
|
|
21
|
+
if host in config['main']:
|
|
22
|
+
print("Using host-specific credentials")
|
|
23
|
+
username = config[host]['user']
|
|
24
|
+
password = config[host]['pass']
|
|
25
|
+
else:
|
|
26
|
+
print("Didn't find host-specific credential, using defaults")
|
|
27
|
+
username = config['all_hosts']['user']
|
|
28
|
+
password = config['all_hosts']['pass']
|
|
29
|
+
|
|
30
|
+
ssh_client = ssh_remote.SSHRemote(ip_address=host, username=username, password=password)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
ssh_client.connect()
|
|
34
|
+
except socket.gaierror as e:
|
|
35
|
+
if e.errno == 11001:
|
|
36
|
+
print_api(f"Couldn't resolve IP to {host}: {str(e)}\n"
|
|
37
|
+
f"Try providing IP address instead of hostname", color='red')
|
|
38
|
+
continue
|
|
39
|
+
else:
|
|
40
|
+
raise e
|
|
41
|
+
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
|
42
|
+
print_api(f"Couldn't connect to {host}: {str(e)}", color='red')
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
# Read the TCP ports file to string.
|
|
46
|
+
tcp_ports_package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
|
|
47
|
+
script_file_stem=TCP_PORTS_FILE)
|
|
48
|
+
tcp_ports_script_string: str = tcp_ports_package_processor.read_script_file_to_string()
|
|
49
|
+
|
|
50
|
+
# Execute the TCP ports script remotely via SSH to get the list of open TCP ports.
|
|
51
|
+
tcp_ports_output, tcp_ports_error = ssh_client.remote_execution_python(script_string=tcp_ports_script_string)
|
|
52
|
+
if tcp_ports_error:
|
|
53
|
+
print_api(f"Error getting TCP ports from host {host}: {tcp_ports_error}", color='red')
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
tcp_ports_list: list = tcp_ports_output.strip().splitlines()
|
|
57
|
+
if not tcp_ports_list:
|
|
58
|
+
print_api(f"No TCP ports found on host {host}", color='red')
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
last_port: int = int(tcp_ports_list[-1])
|
|
62
|
+
|
|
63
|
+
port_to_cmd_package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
|
|
64
|
+
script_file_stem=PORT_TO_CMD_FILE)
|
|
65
|
+
get_command_instance = process_getter.GetCommandLine(
|
|
66
|
+
client_ip=host,
|
|
67
|
+
client_port=last_port,
|
|
68
|
+
package_processor=port_to_cmd_package_processor,
|
|
69
|
+
ssh_client=ssh_client)
|
|
70
|
+
process_name = get_command_instance.get_process_name()
|
|
71
|
+
print(f"Process for port {last_port} on host {host}: {process_name}")
|
|
72
|
+
|
|
73
|
+
print("Closing SSH connection")
|
|
74
|
+
ssh_client.close()
|
|
75
|
+
|
|
76
|
+
if not process_name:
|
|
77
|
+
print_api(f"Failed to get process name for port {last_port} on host {host}", color='red')
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
print_api(f"SSH test success!", color='green')
|
|
81
|
+
|
|
82
|
+
return 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,7 +178,7 @@ def get_interface_ips(
|
|
|
169
178
|
return ips
|
|
170
179
|
|
|
171
180
|
|
|
172
|
-
def
|
|
181
|
+
def get_host_ips_psutil(
|
|
173
182
|
localhost: bool = True,
|
|
174
183
|
ipv4: bool = True,
|
|
175
184
|
ipv6: bool = True
|
|
@@ -222,6 +231,22 @@ def get_host_ips(
|
|
|
222
231
|
return ip_list
|
|
223
232
|
|
|
224
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
|
+
|
|
225
250
|
def get_microsoft_loopback_device_network_configuration(
|
|
226
251
|
wmi_instance: CDispatch = None,
|
|
227
252
|
timeout: int = 1,
|
|
@@ -356,17 +381,14 @@ def change_interface_metric_restart_device(
|
|
|
356
381
|
|
|
357
382
|
|
|
358
383
|
def get_wmi_network_adapter_configuration(
|
|
359
|
-
|
|
360
|
-
connection_name: str = None,
|
|
384
|
+
interface_name: str = None,
|
|
361
385
|
mac_address: str = None,
|
|
362
386
|
wmi_instance: CDispatch = None,
|
|
363
387
|
get_info_from_network_config: bool = True
|
|
364
388
|
) -> tuple:
|
|
365
389
|
"""
|
|
366
390
|
Get the WMI network configuration for a network adapter.
|
|
367
|
-
:param
|
|
368
|
-
This is the adapter that your internet is being used from.
|
|
369
|
-
: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.
|
|
370
392
|
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
371
393
|
:param wmi_instance: WMI instance. You can get it from:
|
|
372
394
|
wrappers.pywin32s.wmis.wmi_helpers.get_wmi_instance()
|
|
@@ -377,14 +399,14 @@ def get_wmi_network_adapter_configuration(
|
|
|
377
399
|
"""
|
|
378
400
|
|
|
379
401
|
wmi_network_config, wmi_network_adapter = win32_networkadapterconfiguration.get_adapter_network_configuration(
|
|
380
|
-
|
|
381
|
-
connection_name=connection_name,
|
|
402
|
+
interface_name=interface_name,
|
|
382
403
|
mac_address=mac_address,
|
|
383
404
|
wmi_instance=wmi_instance
|
|
384
405
|
)
|
|
385
406
|
|
|
386
407
|
if get_info_from_network_config:
|
|
387
408
|
adapter_info: dict = win32_networkadapterconfiguration.get_info_from_network_config(wmi_network_config)
|
|
409
|
+
adapter_info['name'] = wmi_network_adapter.NetConnectionID
|
|
388
410
|
else:
|
|
389
411
|
adapter_info: dict = {}
|
|
390
412
|
|
|
@@ -468,7 +490,7 @@ def generate_unused_ipv4_addresses_from_ip(
|
|
|
468
490
|
return generated_ips, masks_for_ips
|
|
469
491
|
|
|
470
492
|
|
|
471
|
-
def
|
|
493
|
+
def set_dynamic_ip_for_adapter_wmi(
|
|
472
494
|
network_config: CDispatch,
|
|
473
495
|
reset_dns: bool = True,
|
|
474
496
|
reset_wins: bool = True
|
|
@@ -484,7 +506,7 @@ def set_dynamic_ip_for_adapter(
|
|
|
484
506
|
nic_cfg=network_config, reset_dns=reset_dns, reset_wins=reset_wins)
|
|
485
507
|
|
|
486
508
|
|
|
487
|
-
def
|
|
509
|
+
def set_static_ip_for_adapter_wmi(
|
|
488
510
|
network_config: CDispatch,
|
|
489
511
|
ips: list[str],
|
|
490
512
|
masks: list[str],
|
|
@@ -512,19 +534,19 @@ def set_static_ip_for_adapter(
|
|
|
512
534
|
)
|
|
513
535
|
|
|
514
536
|
|
|
515
|
-
def
|
|
537
|
+
def add_virtual_ips_to_network_interface(
|
|
538
|
+
interface_name: str,
|
|
516
539
|
number_of_ips: int = 0,
|
|
517
540
|
virtual_ipv4s_to_add: list[str] = None,
|
|
518
541
|
virtual_ipv4_masks_to_add: list[str] = None,
|
|
519
542
|
set_virtual_ips_skip_as_source: bool = True,
|
|
520
|
-
gateways: list[str] | None = None,
|
|
521
|
-
dns_gateways: list[str] | None = None,
|
|
522
|
-
availability_wait_seconds: int = 15,
|
|
523
543
|
simulate_only: bool = False,
|
|
524
544
|
locator: CDispatch = None,
|
|
525
545
|
wait_until_applied: bool = True,
|
|
526
|
-
wait_until_applied_seconds: int = 15
|
|
527
|
-
|
|
546
|
+
wait_until_applied_seconds: int = 15,
|
|
547
|
+
verbose: bool = False,
|
|
548
|
+
logger: Logger = None,
|
|
549
|
+
) -> tuple[list[str], list[str]] | None:
|
|
528
550
|
"""
|
|
529
551
|
Add virtual IP addresses to the default network adapter.
|
|
530
552
|
The adapter will set to static IP and DNS gateway, instead of dynamic DHCP.
|
|
@@ -535,6 +557,8 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
535
557
|
While generating the IPs, the function will skip the already existing IPs in the adapter, like default gateway
|
|
536
558
|
and DNS servers.
|
|
537
559
|
|
|
560
|
+
:param interface_name: string, adapter name as shown in the network settings.
|
|
561
|
+
|
|
538
562
|
:param number_of_ips: int, number of IPs to generate in addition to the IPv4s that already exist in the adapter.
|
|
539
563
|
Or you add the IPs and masks to the adapter with the parameters virtual_ipv4s_to_add and virtual_ipv4_masks_to_add.
|
|
540
564
|
|
|
@@ -544,15 +568,11 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
544
568
|
|
|
545
569
|
:param set_virtual_ips_skip_as_source: bool, if True, the SkipAsSource flag will be set for the virtual IPs.
|
|
546
570
|
This is needed to avoid the endless accept() loop.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
:param
|
|
551
|
-
|
|
552
|
-
[]: No DNS servers will be assigned.
|
|
553
|
-
:param availability_wait_seconds: int, seconds to wait for the adapter to be available after setting the IP address.
|
|
554
|
-
:param simulate_only: bool, if True, the function will only prepare the ip addresses and return them without changing anything.
|
|
555
|
-
: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()
|
|
556
576
|
|
|
557
577
|
:param wait_until_applied: bool, if True, the function will wait until the IP addresses are applied.
|
|
558
578
|
By default, while WMI command is executed, there is no indication if the addresses were finished applying or not.
|
|
@@ -562,7 +582,10 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
562
582
|
after setting the IP addresses. This is the time to wait for the IP addresses to be
|
|
563
583
|
applied after setting them. If the IP addresses are not applied in this time, a TimeoutError will be raised.
|
|
564
584
|
|
|
565
|
-
:
|
|
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)
|
|
566
589
|
"""
|
|
567
590
|
|
|
568
591
|
if virtual_ipv4s_to_add and not virtual_ipv4_masks_to_add:
|
|
@@ -579,24 +602,19 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
579
602
|
# Connect to WMi.
|
|
580
603
|
wmi_civ2_instance, locator = wmi_helpers.get_wmi_instance(locator=locator)
|
|
581
604
|
|
|
582
|
-
#
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
default_network_adapter_config, default_network_adapter, default_adapter_info = get_wmi_network_adapter_configuration(
|
|
586
|
-
use_default_interface=True, wmi_instance=wmi_civ2_instance, get_info_from_network_config=True)
|
|
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)
|
|
587
608
|
|
|
588
|
-
current_ipv4s: list[str] =
|
|
589
|
-
current_ipv4_masks: list[str] =
|
|
590
|
-
|
|
591
|
-
# print(f"Current IPs: {current_ipv4s}")
|
|
592
|
-
# current_ips_count: int = len(current_ipv4s)
|
|
609
|
+
current_ipv4s: list[str] = adapter_info['ipv4s']
|
|
610
|
+
current_ipv4_masks: list[str] = adapter_info['ipv4_subnet_masks']
|
|
593
611
|
|
|
594
612
|
if number_of_ips > 0:
|
|
595
613
|
ips_to_assign, masks_to_assign = generate_unused_ipv4_addresses_from_ip(
|
|
596
614
|
ip_address=current_ipv4s[0],
|
|
597
615
|
mask=current_ipv4_masks[0],
|
|
598
616
|
number_of_ips=number_of_ips,
|
|
599
|
-
skip_ips=current_ipv4s +
|
|
617
|
+
skip_ips=current_ipv4s + adapter_info['default_gateways'] + adapter_info['dns_gateways']
|
|
600
618
|
)
|
|
601
619
|
elif virtual_ipv4s_to_add and virtual_ipv4_masks_to_add:
|
|
602
620
|
ips_to_assign = virtual_ipv4s_to_add
|
|
@@ -606,51 +624,48 @@ def add_virtual_ips_to_default_adapter_by_current_setting(
|
|
|
606
624
|
masks_to_assign = []
|
|
607
625
|
|
|
608
626
|
if not simulate_only:
|
|
609
|
-
#
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
ips_for_skip_as_source = ips.copy()
|
|
628
|
-
# Add it back to the beginning of the list.
|
|
629
|
-
ips.insert(0, default_ip_address)
|
|
630
|
-
|
|
631
|
-
win32_networkadapterconfiguration.set_static_ips(
|
|
632
|
-
default_network_adapter_config, ips=ips, masks=masks,
|
|
633
|
-
gateways=gateways, dns_gateways=dns_gateways,
|
|
634
|
-
availability_wait_seconds=availability_wait_seconds)
|
|
635
|
-
|
|
636
|
-
# If there were already virtual IPs assigned to the adapter and already were set SkipAsSource,
|
|
637
|
-
# we need to set SkipAsSource for them once again as well as for the new IPs.
|
|
638
|
-
if set_virtual_ips_skip_as_source:
|
|
639
|
-
wmi_standard_cimv2_instance, _ = wmi_helpers.get_wmi_instance(
|
|
640
|
-
namespace='root\\StandardCimv2', wmi_instance=wmi_civ2_instance, locator=locator)
|
|
641
|
-
msft_netipaddress.set_skip_as_source(ips_for_skip_as_source, enable=True, wmi_instance=wmi_standard_cimv2_instance)
|
|
642
|
-
else:
|
|
643
|
-
# print("[!] No new IPs to assign.")
|
|
644
|
-
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
|
+
)
|
|
645
645
|
|
|
646
646
|
if wait_until_applied:
|
|
647
647
|
# Wait until the IP addresses are applied.
|
|
648
648
|
for _ in range(wait_until_applied_seconds):
|
|
649
|
-
|
|
650
|
-
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):
|
|
651
651
|
break
|
|
652
652
|
time.sleep(1)
|
|
653
653
|
else:
|
|
654
654
|
raise TimeoutError("Timeout while waiting for the IP addresses to be applied.")
|
|
655
655
|
|
|
656
|
-
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)
|