first commit
This commit is contained in:
574
proxy_chain_manager/sysproxy.py
Normal file
574
proxy_chain_manager/sysproxy.py
Normal file
@@ -0,0 +1,574 @@
|
||||
"""Set / clear the Windows system-wide HTTP proxy.
|
||||
|
||||
Layers configured so the proxy actually applies system-wide:
|
||||
|
||||
1. ``HKCU\\…\\Internet Settings`` — simple values WinINet reads first
|
||||
2. ``Internet Settings\\Connections\\DefaultConnectionSettings`` binary blob —
|
||||
overrides #1 for any process that opens the per-connection structure.
|
||||
3. ``HKLM\\…\\Internet Settings`` mirror (Admin only) — required on Server when
|
||||
``ProxySettingsPerUser=0`` is set by Group Policy / baseline image.
|
||||
4. ``netsh winhttp set proxy`` — .NET, Windows Update, services and
|
||||
headless browser components that bypass WinINet.
|
||||
|
||||
We also actively detect and warn about:
|
||||
- ``ProxySettingsPerUser=0`` → HKCU is ignored; we fix it when Admin.
|
||||
- Group Policy proxy keys → those override us; we surface the path so the
|
||||
user can verify or remove them.
|
||||
- ``AutoConfigURL`` / ``AutoDetect`` (WPAD) → wiped on engage.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import logging
|
||||
import sys
|
||||
import struct
|
||||
import subprocess
|
||||
import winreg
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_KEY_PATH = r"Software\Microsoft\Windows\CurrentVersion\Internet Settings"
|
||||
_CONN_KEY_PATH = (
|
||||
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
|
||||
)
|
||||
_HKLM_INET_PATH = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
|
||||
_HKLM_CONN_PATH = (
|
||||
r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
|
||||
)
|
||||
_POLICY_PATHS = (
|
||||
(winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings"),
|
||||
(winreg.HKEY_CURRENT_USER,
|
||||
r"SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings"),
|
||||
(winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Policies\Microsoft\Internet Explorer\Control Panel"),
|
||||
)
|
||||
_SETTINGS_CHANGED = 39
|
||||
_REFRESH = 37
|
||||
|
||||
_FLAG_DIRECT = 0x01
|
||||
_FLAG_MANUAL_PROXY = 0x02
|
||||
_FLAG_AUTO_CONFIG = 0x04
|
||||
_FLAG_AUTO_DETECT = 0x08
|
||||
|
||||
|
||||
def _mac_network_services() -> list[str]:
|
||||
if sys.platform != "darwin":
|
||||
return []
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["networksetup", "-listallnetworkservices"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=12,
|
||||
)
|
||||
except Exception:
|
||||
return []
|
||||
services: list[str] = []
|
||||
for line in (r.stdout or "").splitlines():
|
||||
name = line.strip()
|
||||
if not name or name.startswith("An asterisk"):
|
||||
continue
|
||||
services.append(name.lstrip("*").strip())
|
||||
return services
|
||||
|
||||
|
||||
def _mac_run_networksetup(args: list[str]) -> None:
|
||||
try:
|
||||
subprocess.run(["networksetup", *args], capture_output=True, text=True, timeout=20)
|
||||
except Exception as exc:
|
||||
log.debug("networksetup failed (%s): %s", args, exc)
|
||||
|
||||
|
||||
def _mac_set_system_proxy(host: str, port: int, bypass: str) -> None:
|
||||
bypass_hosts = [
|
||||
h.strip().replace("*", "")
|
||||
for h in bypass.replace(";", ",").split(",")
|
||||
if h.strip() and h.strip() != "<local>"
|
||||
]
|
||||
for service in _mac_network_services():
|
||||
_mac_run_networksetup(["-setwebproxy", service, host, str(port)])
|
||||
_mac_run_networksetup(["-setsecurewebproxy", service, host, str(port)])
|
||||
_mac_run_networksetup(["-setwebproxystate", service, "on"])
|
||||
_mac_run_networksetup(["-setsecurewebproxystate", service, "on"])
|
||||
if bypass_hosts:
|
||||
_mac_run_networksetup(["-setproxybypassdomains", service, *bypass_hosts])
|
||||
log.info("macOS system proxy set to %s:%s for %d service(s)", host, port, len(_mac_network_services()))
|
||||
|
||||
|
||||
def _mac_clear_system_proxy() -> None:
|
||||
for service in _mac_network_services():
|
||||
_mac_run_networksetup(["-setwebproxystate", service, "off"])
|
||||
_mac_run_networksetup(["-setsecurewebproxystate", service, "off"])
|
||||
log.info("macOS system proxy cleared for %d service(s)", len(_mac_network_services()))
|
||||
|
||||
|
||||
def _mac_system_proxy_set() -> bool:
|
||||
for service in _mac_network_services():
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["networksetup", "-getwebproxy", service],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=8,
|
||||
)
|
||||
if "Enabled: Yes" in (r.stdout or ""):
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def _is_admin() -> bool:
|
||||
try:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() != 0 # type: ignore[attr-defined]
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _broadcast() -> None:
|
||||
"""Tell running apps (browsers, etc.) that proxy settings changed."""
|
||||
try:
|
||||
inet = ctypes.windll.wininet # type: ignore[attr-defined]
|
||||
inet.InternetSetOptionW(0, _SETTINGS_CHANGED, 0, 0)
|
||||
inet.InternetSetOptionW(0, _REFRESH, 0, 0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _bump_counter(prev: bytes | None) -> int:
|
||||
if not prev or len(prev) < 8:
|
||||
return 1
|
||||
try:
|
||||
return int.from_bytes(prev[4:8], "little") + 1
|
||||
except Exception:
|
||||
return 1
|
||||
|
||||
|
||||
def _build_blob(
|
||||
proxy: str,
|
||||
bypass: str,
|
||||
auto_config_url: str = "",
|
||||
counter: int = 1,
|
||||
) -> bytes:
|
||||
"""REG_BINARY layout for DefaultConnectionSettings (little-endian)."""
|
||||
flags = _FLAG_DIRECT
|
||||
if proxy:
|
||||
flags |= _FLAG_MANUAL_PROXY
|
||||
if auto_config_url:
|
||||
flags |= _FLAG_AUTO_CONFIG
|
||||
|
||||
p = proxy.encode("utf-8")
|
||||
b = bypass.encode("utf-8")
|
||||
a = auto_config_url.encode("utf-8")
|
||||
|
||||
return (
|
||||
struct.pack("<III", 0x46, counter, flags)
|
||||
+ struct.pack("<I", len(p)) + p
|
||||
+ struct.pack("<I", len(b)) + b
|
||||
+ struct.pack("<I", len(a)) + a
|
||||
+ b"\x00" * 32
|
||||
)
|
||||
|
||||
|
||||
def _read_blob_counter(hive: int, path: str) -> int:
|
||||
try:
|
||||
with winreg.OpenKey(hive, path, 0, winreg.KEY_QUERY_VALUE) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "DefaultConnectionSettings")
|
||||
return _bump_counter(val)
|
||||
except OSError:
|
||||
return 1
|
||||
|
||||
|
||||
def _write_conn_blob(hive: int, path: str, proxy: str, bypass: str) -> bool:
|
||||
counter = _read_blob_counter(hive, path)
|
||||
blob = _build_blob(proxy, bypass, "", counter)
|
||||
try:
|
||||
key = winreg.CreateKeyEx(hive, path, 0, winreg.KEY_SET_VALUE)
|
||||
with key:
|
||||
winreg.SetValueEx(key, "DefaultConnectionSettings", 0, winreg.REG_BINARY, blob)
|
||||
winreg.SetValueEx(key, "SavedLegacySettings", 0, winreg.REG_BINARY, blob)
|
||||
return True
|
||||
except OSError as e:
|
||||
log.debug("Connections blob write failed (%s): %s", path, e)
|
||||
return False
|
||||
|
||||
|
||||
def _clear_conn_blob(hive: int, path: str) -> None:
|
||||
counter = _read_blob_counter(hive, path)
|
||||
blob = _build_blob("", "", "", counter)
|
||||
try:
|
||||
with winreg.OpenKey(hive, path, 0, winreg.KEY_SET_VALUE) as key:
|
||||
winreg.SetValueEx(key, "DefaultConnectionSettings", 0, winreg.REG_BINARY, blob)
|
||||
winreg.SetValueEx(key, "SavedLegacySettings", 0, winreg.REG_BINARY, blob)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def _wipe_autoconfig(key: winreg.HKEYType) -> None:
|
||||
for name in ("AutoConfigURL", "AutoDetect"):
|
||||
try:
|
||||
winreg.DeleteValue(key, name)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def _set_winhttp_proxy(host: str, port: int, bypass: str) -> None:
|
||||
try:
|
||||
bypass_list = (
|
||||
bypass.replace(";", " ").replace("<local>", "<local>").strip()
|
||||
) or "<local>"
|
||||
subprocess.run(
|
||||
["netsh", "winhttp", "set", "proxy",
|
||||
f"{host}:{port}", f"bypass-list={bypass_list}"],
|
||||
capture_output=True, text=True, timeout=15,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
except Exception as e:
|
||||
log.debug("netsh winhttp set proxy failed: %s", e)
|
||||
|
||||
|
||||
def _reset_winhttp_proxy() -> None:
|
||||
try:
|
||||
subprocess.run(
|
||||
["netsh", "winhttp", "reset", "proxy"],
|
||||
capture_output=True, text=True, timeout=15,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ── browser-policy proxy (Chrome / Edge) ─────────────────────────────────────
|
||||
#
|
||||
# Why this exists: when you set WebRTC policy on Chrome/Edge, the browser sees
|
||||
# itself as "managed". Some managed-Chrome builds then *ignore* the system
|
||||
# proxy unless an explicit ``ProxyServer`` policy is also set. They also keep
|
||||
# trying HTTP/3 over QUIC (UDP/443), which bypasses HTTP proxies entirely —
|
||||
# under the kill-switch those UDP packets get dropped and pages hang forever.
|
||||
#
|
||||
# Setting these policy values pins the browser to our chain (HTTP proxy) and
|
||||
# forces it off QUIC. Cleared cleanly on disengage.
|
||||
_BROWSER_POLICY_PATHS = (
|
||||
r"SOFTWARE\Policies\Google\Chrome",
|
||||
r"SOFTWARE\Policies\Microsoft\Edge",
|
||||
r"SOFTWARE\Policies\Chromium",
|
||||
)
|
||||
|
||||
def _set_browser_proxy_policy(host: str, port: int, bypass: str) -> int:
|
||||
"""Pin Chromium-based browsers (Chrome, Edge, Chromium) to our local proxy
|
||||
via Group Policy registry, and disable QUIC. Returns count of keys updated.
|
||||
|
||||
Requires Admin to write under HKLM\\SOFTWARE\\Policies. Silently skips when
|
||||
not elevated — the user gets a warning in the service log instead.
|
||||
"""
|
||||
if not _is_admin():
|
||||
return 0
|
||||
proxy_value = f"http={host}:{port};https={host}:{port}"
|
||||
bypass_value = bypass.replace(";", ",")
|
||||
written = 0
|
||||
for path in _BROWSER_POLICY_PATHS:
|
||||
try:
|
||||
with winreg.CreateKeyEx(
|
||||
winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxyMode", 0, winreg.REG_SZ, "fixed_servers")
|
||||
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, proxy_value)
|
||||
winreg.SetValueEx(key, "ProxyBypassList", 0, winreg.REG_SZ, bypass_value)
|
||||
winreg.SetValueEx(key, "QuicAllowed", 0, winreg.REG_DWORD, 0)
|
||||
written += 1
|
||||
except OSError:
|
||||
continue
|
||||
return written
|
||||
|
||||
|
||||
def _clear_browser_proxy_policy() -> None:
|
||||
if not _is_admin():
|
||||
return
|
||||
for path in _BROWSER_POLICY_PATHS:
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
for name in ("ProxyMode", "ProxyServer", "ProxyBypassList", "QuicAllowed"):
|
||||
try:
|
||||
winreg.DeleteValue(key, name)
|
||||
except OSError:
|
||||
pass
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
|
||||
def _read_proxy_settings_per_user() -> int | None:
|
||||
"""``HKLM\\…\\Internet Settings\\ProxySettingsPerUser``.
|
||||
|
||||
When 0, Windows ignores HKCU proxy values and reads only HKLM. Set on many
|
||||
Server images and AD policy baselines — the #1 cause of "set the proxy but
|
||||
the browser still goes direct" on Server.
|
||||
"""
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, 0, winreg.KEY_QUERY_VALUE
|
||||
) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "ProxySettingsPerUser")
|
||||
return int(val)
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def _fix_proxy_settings_per_user() -> bool:
|
||||
"""Force ``ProxySettingsPerUser=1`` (per-user proxy honored). Admin only."""
|
||||
if not _is_admin():
|
||||
return False
|
||||
try:
|
||||
with winreg.CreateKeyEx(
|
||||
winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxySettingsPerUser", 0, winreg.REG_DWORD, 1)
|
||||
log.info("HKLM\\…\\ProxySettingsPerUser → 1 (was %s)", _read_proxy_settings_per_user())
|
||||
return True
|
||||
except OSError as e:
|
||||
log.warning("Could not fix ProxySettingsPerUser: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
def _set_hklm_root(proxy: str, bypass: str) -> bool:
|
||||
"""Mirror the per-user keys at HKLM so machine-scope WinINet reads honor us."""
|
||||
if not _is_admin():
|
||||
return False
|
||||
try:
|
||||
with winreg.CreateKeyEx(
|
||||
winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 1)
|
||||
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, proxy)
|
||||
winreg.SetValueEx(key, "ProxyOverride", 0, winreg.REG_SZ, bypass)
|
||||
_wipe_autoconfig(key)
|
||||
return True
|
||||
except OSError as e:
|
||||
log.warning("HKLM root proxy write failed: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
def _clear_hklm_root() -> None:
|
||||
if not _is_admin():
|
||||
return
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 0)
|
||||
_wipe_autoconfig(key)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def detect_policy_overrides() -> list[str]:
|
||||
if sys.platform != "win32":
|
||||
return []
|
||||
"""Return human-readable paths of Group Policy proxy locks that beat us.
|
||||
|
||||
Anything we find here will silently override our manual proxy. We do *not*
|
||||
delete policy keys (admins set those deliberately) — we just surface them.
|
||||
"""
|
||||
hits: list[str] = []
|
||||
proxy_value_names = ("ProxyServer", "ProxyEnable", "AutoConfigURL", "ProxyOverride")
|
||||
for hive, path in _POLICY_PATHS:
|
||||
try:
|
||||
with winreg.OpenKey(hive, path, 0, winreg.KEY_QUERY_VALUE) as key:
|
||||
for n in proxy_value_names:
|
||||
try:
|
||||
winreg.QueryValueEx(key, n)
|
||||
hive_name = "HKLM" if hive == winreg.HKEY_LOCAL_MACHINE else "HKCU"
|
||||
hits.append(f"{hive_name}\\{path}\\{n}")
|
||||
except OSError:
|
||||
continue
|
||||
except OSError:
|
||||
continue
|
||||
return hits
|
||||
|
||||
|
||||
def _read_str(hive: int, path: str, name: str) -> str:
|
||||
try:
|
||||
with winreg.OpenKey(hive, path, 0, winreg.KEY_QUERY_VALUE) as key:
|
||||
val, _ = winreg.QueryValueEx(key, name)
|
||||
return str(val)
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
def _read_dword(hive: int, path: str, name: str) -> int | None:
|
||||
try:
|
||||
with winreg.OpenKey(hive, path, 0, winreg.KEY_QUERY_VALUE) as key:
|
||||
val, _ = winreg.QueryValueEx(key, name)
|
||||
return int(val)
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def _read_winhttp_proxy() -> str:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["netsh", "winhttp", "show", "proxy"],
|
||||
capture_output=True, text=True, timeout=10,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
for ln in (r.stdout or "").splitlines():
|
||||
if "proxy" in ln.lower() and ":" in ln:
|
||||
return ln.strip()
|
||||
return (r.stdout or "").strip().splitlines()[-1] if r.stdout else ""
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def diagnose_system_proxy() -> list[str]:
|
||||
if sys.platform == "darwin":
|
||||
out = []
|
||||
for service in _mac_network_services()[:8]:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["networksetup", "-getwebproxy", service],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=8,
|
||||
)
|
||||
first = " ".join((r.stdout or "").splitlines()[:3])
|
||||
except Exception as exc:
|
||||
first = str(exc)
|
||||
out.append(f"macOS {service}: {first}")
|
||||
return out or ["macOS network services not detected"]
|
||||
"""Return a list of human-readable lines covering every place proxy can
|
||||
be configured. Logged on each engage so "chain green but browser leaks"
|
||||
is debuggable without RegEdit gymnastics.
|
||||
"""
|
||||
lines: list[str] = []
|
||||
per_user = _read_proxy_settings_per_user()
|
||||
per_user_msg = (
|
||||
"1 (HKCU honored)" if per_user == 1
|
||||
else "0 (HKCU IGNORED — Server lock!)" if per_user == 0
|
||||
else "(unset → defaults to HKCU)"
|
||||
)
|
||||
lines.append(f"ProxySettingsPerUser = {per_user_msg}")
|
||||
|
||||
en_u = _read_dword(winreg.HKEY_CURRENT_USER, _KEY_PATH, "ProxyEnable")
|
||||
ps_u = _read_str(winreg.HKEY_CURRENT_USER, _KEY_PATH, "ProxyServer")
|
||||
lines.append(f"HKCU Enable={en_u} Server='{ps_u}'")
|
||||
|
||||
en_m = _read_dword(winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, "ProxyEnable")
|
||||
ps_m = _read_str(winreg.HKEY_LOCAL_MACHINE, _HKLM_INET_PATH, "ProxyServer")
|
||||
lines.append(f"HKLM Enable={en_m} Server='{ps_m}'")
|
||||
|
||||
ac_u = _read_str(winreg.HKEY_CURRENT_USER, _KEY_PATH, "AutoConfigURL")
|
||||
ad_u = _read_dword(winreg.HKEY_CURRENT_USER, _KEY_PATH, "AutoDetect")
|
||||
if ac_u or ad_u:
|
||||
lines.append(f"WPAD/PAC: AutoConfigURL='{ac_u}' AutoDetect={ad_u}")
|
||||
|
||||
winhttp = _read_winhttp_proxy()
|
||||
if winhttp:
|
||||
lines.append(f"WinHTTP: {winhttp}")
|
||||
|
||||
pol = detect_policy_overrides()
|
||||
if pol:
|
||||
lines.append(f"Policy overrides (these BEAT us): {len(pol)} entries")
|
||||
for p in pol[:5]:
|
||||
lines.append(f" ! {p}")
|
||||
if len(pol) > 5:
|
||||
lines.append(f" ! …and {len(pol) - 5} more")
|
||||
else:
|
||||
lines.append("Policy overrides: none")
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
def set_system_proxy(
|
||||
host: str,
|
||||
port: int,
|
||||
bypass: str = "localhost;127.*;10.*;192.168.*;<local>",
|
||||
) -> None:
|
||||
if sys.platform == "darwin":
|
||||
_mac_set_system_proxy(host, port, bypass)
|
||||
return
|
||||
proxy = f"{host}:{port}"
|
||||
|
||||
# HKCU root
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER, _KEY_PATH, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 1)
|
||||
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, proxy)
|
||||
winreg.SetValueEx(key, "ProxyOverride", 0, winreg.REG_SZ, bypass)
|
||||
_wipe_autoconfig(key)
|
||||
except OSError:
|
||||
log.exception("Failed to set system proxy (HKCU root)")
|
||||
return
|
||||
|
||||
_write_conn_blob(winreg.HKEY_CURRENT_USER, _CONN_KEY_PATH, proxy, bypass)
|
||||
|
||||
# If ProxySettingsPerUser=0 (Server / GPO baseline), HKCU is ignored.
|
||||
# When Admin, fix the flag AND mirror to HKLM so the proxy actually applies.
|
||||
per_user = _read_proxy_settings_per_user()
|
||||
if per_user == 0:
|
||||
log.warning(
|
||||
"ProxySettingsPerUser=0 detected — Windows is ignoring per-user "
|
||||
"proxy. Attempting Admin fix (HKLM mirror)."
|
||||
)
|
||||
if _is_admin():
|
||||
_fix_proxy_settings_per_user()
|
||||
_set_hklm_root(proxy, bypass)
|
||||
_write_conn_blob(winreg.HKEY_LOCAL_MACHINE, _HKLM_CONN_PATH, proxy, bypass)
|
||||
else:
|
||||
log.warning(
|
||||
"Not running as Admin — cannot fix ProxySettingsPerUser. "
|
||||
"Re-launch elevated for proxy to apply on this Server image."
|
||||
)
|
||||
|
||||
# On Admin runs always mirror HKLM root too. Harmless when PerUser=1 and
|
||||
# correct when PerUser=0 or absent.
|
||||
if _is_admin():
|
||||
_set_hklm_root(proxy, bypass)
|
||||
_write_conn_blob(winreg.HKEY_LOCAL_MACHINE, _HKLM_CONN_PATH, proxy, bypass)
|
||||
|
||||
_set_winhttp_proxy(host, port, bypass)
|
||||
n_browser = _set_browser_proxy_policy(host, port, bypass)
|
||||
_broadcast()
|
||||
log.info(
|
||||
"System proxy set %s (bypass=%s) — HKCU + HKLM(adm) + Connections + WinHTTP + %d browser policy",
|
||||
proxy, bypass, n_browser,
|
||||
)
|
||||
|
||||
|
||||
def clear_system_proxy() -> None:
|
||||
if sys.platform == "darwin":
|
||||
_mac_clear_system_proxy()
|
||||
return
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER, _KEY_PATH, 0, winreg.KEY_SET_VALUE
|
||||
) as key:
|
||||
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 0)
|
||||
_wipe_autoconfig(key)
|
||||
except OSError:
|
||||
log.exception("Failed to clear system proxy")
|
||||
|
||||
_clear_conn_blob(winreg.HKEY_CURRENT_USER, _CONN_KEY_PATH)
|
||||
if _is_admin():
|
||||
_clear_hklm_root()
|
||||
_clear_conn_blob(winreg.HKEY_LOCAL_MACHINE, _HKLM_CONN_PATH)
|
||||
_reset_winhttp_proxy()
|
||||
_clear_browser_proxy_policy()
|
||||
_broadcast()
|
||||
log.info("System proxy cleared — HKCU + HKLM(adm) + Connections + WinHTTP + browser policy")
|
||||
|
||||
|
||||
def is_system_proxy_set() -> bool:
|
||||
if sys.platform == "darwin":
|
||||
return _mac_system_proxy_set()
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER, _KEY_PATH, 0, winreg.KEY_QUERY_VALUE
|
||||
) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "ProxyEnable")
|
||||
return val == 1
|
||||
except OSError:
|
||||
return False
|
||||
Reference in New Issue
Block a user