steam-backlog-enforcer/steam_backlog_enforcer/_store_iptables.py
Krzysztof kuhy Rudnicki 30fcaacc5d Deduplicate snapshot loading, pick application and library hiding into shared helpers
require_snapshot_games / snapshot_game_name (_snapshot), apply_configured_pick (_actions) and hide_others_and_report (library_hider) replace copies spread across the CLI, MCP and stats paths. Tests retargeted to the new owners; test_main_pick_manual split to stay under 250 lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01D55DvFEHiwcPN59kwPqwiU
2026-09-25 18:44:07 +02:00

133 lines
3.6 KiB
Python

"""Steam-store blocking at the iptables/DNS layer.
These call nothing else in the blocker, so they extract cleanly: the
one-way dependency is what keeps store_blocker importable.
Split to keep both files under the 250-line cap.
"""
from __future__ import annotations
import contextlib
import logging
import socket
import subprocess
from steam_backlog_enforcer._store_tools import (
IPTABLES,
IPTABLES_CHAIN,
SUDO,
ensure_chain_in_output,
run_quiet,
)
from steam_backlog_enforcer.config import (
BLOCKED_DOMAINS,
)
logger = logging.getLogger(__name__)
def _is_iptables_blocked() -> bool:
"""Check if our iptables chain exists and has rules."""
try:
result = subprocess.run(
[SUDO, IPTABLES, "-L", IPTABLES_CHAIN, "-n"],
capture_output=True,
text=True,
timeout=5,
check=False,
)
except OSError, subprocess.SubprocessError:
return False
return result.returncode == 0 and "DROP" in result.stdout
def _block_store_iptables() -> bool:
"""Block Steam Store domains using iptables (IP-based)."""
try:
# Create chain if it doesn't exist.
subprocess.run(
[SUDO, IPTABLES, "-N", IPTABLES_CHAIN],
capture_output=True,
timeout=5,
check=False,
)
# Flush existing rules in our chain.
subprocess.run(
[SUDO, IPTABLES, "-F", IPTABLES_CHAIN],
capture_output=True,
timeout=5,
check=True,
)
# Resolve domains and block their IPs.
blocked_ips: set[str] = set()
for domain in BLOCKED_DOMAINS:
with contextlib.suppress(socket.gaierror):
ips = socket.getaddrinfo(domain, 443, socket.AF_INET)
for _, _, _, _, addr in ips:
blocked_ips.add(addr[0])
for ip in blocked_ips:
subprocess.run(
[
SUDO,
IPTABLES,
"-A",
IPTABLES_CHAIN,
"-d",
ip,
"-j",
"DROP",
],
capture_output=True,
timeout=5,
check=True,
)
ensure_chain_in_output(IPTABLES_CHAIN)
except OSError, subprocess.SubprocessError:
logger.exception("Failed to block store via iptables")
return False
logger.info("Steam Store blocked via iptables (%d IPs).", len(blocked_ips))
return True
def _unblock_store_iptables() -> bool:
"""Remove iptables-based block."""
try:
subprocess.run(
[SUDO, IPTABLES, "-D", "OUTPUT", "-j", IPTABLES_CHAIN],
capture_output=True,
timeout=5,
check=False,
)
subprocess.run(
[SUDO, IPTABLES, "-F", IPTABLES_CHAIN],
capture_output=True,
timeout=5,
check=False,
)
subprocess.run(
[SUDO, IPTABLES, "-X", IPTABLES_CHAIN],
capture_output=True,
timeout=5,
check=False,
)
except OSError, subprocess.SubprocessError:
logger.exception("Failed to unblock iptables")
return False
logger.info("Steam Store unblocked from iptables.")
return True
def flush_dns_cache() -> None:
"""Flush the system DNS cache."""
commands = [
["systemd-resolve", "--flush-caches"],
["resolvectl", "flush-caches"],
["nscd", "--invalidate=hosts"],
]
for cmd in commands:
with contextlib.suppress(FileNotFoundError, OSError):
run_quiet(cmd)