atomicshop 3.1.8__py3-none-any.whl → 3.1.9__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/dns.py +12 -2
- atomicshop/wrappers/netshw.py +150 -0
- atomicshop/wrappers/winregw/winreg_network.py +6 -1
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.9.dist-info}/METADATA +1 -1
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.9.dist-info}/RECORD +9 -8
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.9.dist-info}/LICENSE.txt +0 -0
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.9.dist-info}/WHEEL +0 -0
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.9.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/dns.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import socket
|
|
1
2
|
import argparse
|
|
2
3
|
|
|
3
4
|
# noinspection PyPackageRequirements
|
|
@@ -6,7 +7,7 @@ import dns.resolver
|
|
|
6
7
|
from . import print_api
|
|
7
8
|
from .permissions import permissions
|
|
8
9
|
from .wrappers.pywin32w.wmis import win32networkadapter
|
|
9
|
-
from .wrappers
|
|
10
|
+
from .wrappers import netshw
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# Defining Dictionary of Numeric to String DNS Query Types.
|
|
@@ -73,7 +74,16 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
73
74
|
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
74
75
|
"""
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
interfaces_with_dns_settings: list[dict] = netshw.get_netsh_ipv4()
|
|
78
|
+
|
|
79
|
+
default_interface_ipv4 = socket.gethostbyname(socket.gethostname())
|
|
80
|
+
is_dynamic, dns_servers = None, None
|
|
81
|
+
for interface in interfaces_with_dns_settings:
|
|
82
|
+
if default_interface_ipv4 in interface['ip_addresses']:
|
|
83
|
+
is_dynamic = interface['dns_mode']
|
|
84
|
+
dns_servers = interface['dns_servers']
|
|
85
|
+
break
|
|
86
|
+
|
|
77
87
|
return is_dynamic, dns_servers
|
|
78
88
|
|
|
79
89
|
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import re
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
|
|
5
|
+
# ── regex helpers ─────────────────────────────────────────────────────────
|
|
6
|
+
IP_PATTERN = r'(?:\d{1,3}\.){3}\d{1,3}'
|
|
7
|
+
RE_ADAPTER_HEADER = re.compile(r'Configuration for interface +"([^"]+)"', re.I)
|
|
8
|
+
RE_NUMERIC = re.compile(r'\d+')
|
|
9
|
+
RE_SUBNET = re.compile(rf'(?P<prefix>{IP_PATTERN}/\d+)\s+\(mask\s+(?P<mask>{IP_PATTERN})', re.I)
|
|
10
|
+
RE_IP = re.compile(IP_PATTERN)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_netsh_show_config() -> str:
|
|
14
|
+
"""Run `netsh interface ipv4 show config` and return the raw text."""
|
|
15
|
+
return subprocess.check_output(
|
|
16
|
+
["netsh", "interface", "ipv4", "show", "config"],
|
|
17
|
+
text=True, encoding="utf-8", errors="ignore"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_netsh_ipv4() -> List[Dict[str, Any]]:
|
|
22
|
+
"""
|
|
23
|
+
Parse *all* data from `netsh interface ipv4 show config`.
|
|
24
|
+
|
|
25
|
+
Returns a list of dicts – one per adapter – with keys:
|
|
26
|
+
interface, dhcp_enabled, ip_addresses, subnet_prefixes, subnet_masks,
|
|
27
|
+
default_gateways, gateway_metric, interface_metric,
|
|
28
|
+
dns_mode, dns_servers, wins_mode, wins_servers
|
|
29
|
+
"""
|
|
30
|
+
config_text = _get_netsh_show_config()
|
|
31
|
+
|
|
32
|
+
adapters: List[Dict[str, Any]] = []
|
|
33
|
+
adapter: Dict[str, Any] | None = None
|
|
34
|
+
|
|
35
|
+
# Track whether we’re in continuation lines of DNS / WINS lists
|
|
36
|
+
dns_list_type: str | None = None # 'static' | 'dynamic' | None
|
|
37
|
+
wins_list_type: str | None = None
|
|
38
|
+
|
|
39
|
+
for raw_line in config_text.splitlines():
|
|
40
|
+
line = raw_line.strip()
|
|
41
|
+
|
|
42
|
+
# 1) New adapter block ------------------------------------------------
|
|
43
|
+
header_match = RE_ADAPTER_HEADER.search(line)
|
|
44
|
+
if header_match:
|
|
45
|
+
# Flush the previous adapter, if any
|
|
46
|
+
if adapter:
|
|
47
|
+
adapters.append(adapter)
|
|
48
|
+
|
|
49
|
+
iface_name = header_match.group(1)
|
|
50
|
+
adapter = {
|
|
51
|
+
'interface_name' : iface_name,
|
|
52
|
+
'dhcp_enabled' : None,
|
|
53
|
+
'gateway_metric' : None,
|
|
54
|
+
'interface_metric' : None,
|
|
55
|
+
'dns_mode' : 'unknown',
|
|
56
|
+
'wins_mode' : 'unknown',
|
|
57
|
+
'dns_servers' : [],
|
|
58
|
+
'wins_servers' : [],
|
|
59
|
+
'ip_addresses' : [],
|
|
60
|
+
'subnet_prefixes' : [],
|
|
61
|
+
'subnet_masks' : [],
|
|
62
|
+
'default_gateways' : [],
|
|
63
|
+
}
|
|
64
|
+
dns_list_type = wins_list_type = None
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
if adapter is None: # skip prologue lines
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
# 2) DHCP flag -------------------------------------------------------
|
|
71
|
+
if line.startswith("DHCP enabled"):
|
|
72
|
+
adapter['dhcp_enabled'] = "yes" in line.lower()
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
# 3) IP addresses ----------------------------------------------------
|
|
76
|
+
if line.startswith("IP Address"):
|
|
77
|
+
adapter['ip_addresses'].extend(RE_IP.findall(line))
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
# 4) Subnet prefix & mask -------------------------------------------
|
|
81
|
+
if line.startswith("Subnet Prefix"):
|
|
82
|
+
subnet_match = RE_SUBNET.search(line)
|
|
83
|
+
if subnet_match:
|
|
84
|
+
adapter['subnet_prefixes'].append(subnet_match.group('prefix'))
|
|
85
|
+
adapter['subnet_masks'].append(subnet_match.group('mask'))
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
# 5) Gateway & metrics ----------------------------------------------
|
|
89
|
+
if line.startswith("Default Gateway"):
|
|
90
|
+
adapter['default_gateways'].extend(RE_IP.findall(line))
|
|
91
|
+
continue
|
|
92
|
+
if line.startswith("Gateway Metric"):
|
|
93
|
+
metric = RE_NUMERIC.search(line)
|
|
94
|
+
if metric:
|
|
95
|
+
adapter['gateway_metric'] = int(metric.group())
|
|
96
|
+
continue
|
|
97
|
+
if line.startswith("InterfaceMetric"):
|
|
98
|
+
metric = RE_NUMERIC.search(line)
|
|
99
|
+
if metric:
|
|
100
|
+
adapter['interface_metric'] = int(metric.group())
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
# 6) DNS header lines -----------------------------------------------
|
|
104
|
+
if "DNS servers configured through DHCP" in line:
|
|
105
|
+
adapter['dns_mode'] = 'dynamic'
|
|
106
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
107
|
+
dns_list_type = 'dynamic'
|
|
108
|
+
continue
|
|
109
|
+
if "Statically Configured DNS Servers" in line:
|
|
110
|
+
adapter['dns_mode'] = 'static'
|
|
111
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
112
|
+
dns_list_type = 'static'
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
# 7) WINS header lines ----------------------------------------------
|
|
116
|
+
if "WINS servers configured through DHCP" in line:
|
|
117
|
+
adapter['wins_mode'] = 'dynamic'
|
|
118
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
119
|
+
wins_list_type = 'dynamic'
|
|
120
|
+
continue
|
|
121
|
+
if line.startswith(("Primary WINS Server", "Secondary WINS Server")):
|
|
122
|
+
adapter['wins_mode'] = 'static'
|
|
123
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
124
|
+
wins_list_type = 'static'
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
# 8) Continuation lines for DNS / WINS -------------------------------
|
|
128
|
+
if dns_list_type and RE_IP.search(line):
|
|
129
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
130
|
+
continue
|
|
131
|
+
if wins_list_type and RE_IP.search(line):
|
|
132
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Flush the final adapter block
|
|
136
|
+
if adapter:
|
|
137
|
+
adapters.append(adapter)
|
|
138
|
+
|
|
139
|
+
# # ── post-process: detect “mixed” modes ----------------------------------
|
|
140
|
+
# NOT SURE THIS PART WORKS AS INTENDED!!!
|
|
141
|
+
# for ad in adapters:
|
|
142
|
+
# if ad['dns_mode'] == 'dynamic' and ad['dns_servers']:
|
|
143
|
+
# # If both headers appeared the last one wins; treat that as mixed
|
|
144
|
+
# if any(k in ad['dns_servers'] for k in ad['default_gateways']):
|
|
145
|
+
# ad['dns_mode'] = 'mixed'
|
|
146
|
+
# if ad['wins_mode'] == 'dynamic' and ad['wins_servers']:
|
|
147
|
+
# if any(ip not in ad['wins_servers'] for ip in ad['wins_servers']):
|
|
148
|
+
# ad['wins_mode'] = 'mixed'
|
|
149
|
+
|
|
150
|
+
return adapters
|
|
@@ -137,8 +137,13 @@ def get_network_connections_details(get_enum_info: bool = True) -> dict:
|
|
|
137
137
|
return adapter_details
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
def
|
|
140
|
+
def _get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
141
141
|
"""
|
|
142
|
+
NOTICE: This stopped working from the last Windows update on 11.06.2025.
|
|
143
|
+
They moved it to 'ProfileNameServer', anyway Since Windows 8 the recommended API has been the WMI
|
|
144
|
+
NetTCPIP CIM provider (MSFT_DNSClientServerAddress) - didn't test it though.
|
|
145
|
+
Just moved to netsh wrapping for now.
|
|
146
|
+
|
|
142
147
|
Get the default DNS gateway from the Windows registry.
|
|
143
148
|
|
|
144
149
|
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=EQJIgq1QRIHAmPvbH_Qo_f07M3JEn4xL2sUgoWRi8Q4,122
|
|
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=OHcjuzWAys6WmfRnMIU_nkJA634kKmJh6T8w1VtUTJM,2714
|
|
11
11
|
atomicshop/datetimes.py,sha256=IQZ66lmta-ZqxYbyHzm_9eugbJFSilXK1e0kfMgoXGg,18371
|
|
12
12
|
atomicshop/diff_check.py,sha256=vxTDccVbGZHEge6Ja9_ArLWwslOUgIoJAdYPylh4cZg,27176
|
|
13
|
-
atomicshop/dns.py,sha256=
|
|
13
|
+
atomicshop/dns.py,sha256=XB0tijVi1bxWLWKV9hPzpt75jK4SrGbZCV5VJbiiQ74,7185
|
|
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
|
|
@@ -199,6 +199,7 @@ atomicshop/wrappers/cryptographyw.py,sha256=LfzTnwvJE03G6WZryOOf43VKhhnyMakzHpn8
|
|
|
199
199
|
atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
|
|
200
200
|
atomicshop/wrappers/githubw.py,sha256=vaVQg0pkHY63_TfIbCoWmHDPtI2rDPAqMYwsOl3GN78,22559
|
|
201
201
|
atomicshop/wrappers/msiw.py,sha256=GQLqud72nfex3kvO1bJSruNriCYTYX1_G1gSf1MPkIA,6118
|
|
202
|
+
atomicshop/wrappers/netshw.py,sha256=8WE_576XiiHykwFuE-VkCx5CydMpFlztX4frlEteCtI,6350
|
|
202
203
|
atomicshop/wrappers/numpyw.py,sha256=sBV4gSKyr23kXTalqAb1oqttzE_2XxBooCui66jbAqc,1025
|
|
203
204
|
atomicshop/wrappers/olefilew.py,sha256=biD5m58rogifCYmYhJBrAFb9O_Bn_spLek_9HofLeYE,2051
|
|
204
205
|
atomicshop/wrappers/pipw.py,sha256=mu4jnHkSaYNfpBiLZKMZxEX_E2LqW5BVthMZkblPB_c,1317
|
|
@@ -335,9 +336,9 @@ atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0L
|
|
|
335
336
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=WcNyaqEZ82S5-f3kzqi1nllNT2Nd2P_zg8HqCc7vW4s,4120
|
|
336
337
|
atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
337
338
|
atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
|
|
338
|
-
atomicshop/wrappers/winregw/winreg_network.py,sha256=
|
|
339
|
-
atomicshop-3.1.
|
|
340
|
-
atomicshop-3.1.
|
|
341
|
-
atomicshop-3.1.
|
|
342
|
-
atomicshop-3.1.
|
|
343
|
-
atomicshop-3.1.
|
|
339
|
+
atomicshop/wrappers/winregw/winreg_network.py,sha256=ih0BVNwByLvf9F_Lac4EdmDYYJA3PzMvmG0PieDZrsE,9905
|
|
340
|
+
atomicshop-3.1.9.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
341
|
+
atomicshop-3.1.9.dist-info/METADATA,sha256=-CO4-bPxF4SKM5Gi0aC0Sbqkl0s974zx0o5bR5gnBWE,10662
|
|
342
|
+
atomicshop-3.1.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
343
|
+
atomicshop-3.1.9.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
344
|
+
atomicshop-3.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|