mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
- Add usedforsecurity=False to MD5 (AMS fingerprint) and SHA1 (git blob hash) calls to silence Bandit B303 / CodeQL weak-crypto findings - Convert ~996 f-string logging calls to parameterized %s-style across 55 files to prevent log injection (Bandit G201 / CodeQL log-injection) - Narrow ~199 broad except Exception blocks to specific types: OperationalError for DB migrations, OSError for network/file cleanup, (OSError, ftplib.error_reply) for FTP, and targeted tuples for ZIP/XML/JSON parsing — 36 intentionally left broad (mixed async, re-raise patterns)
120 lines
3.5 KiB
Python
120 lines
3.5 KiB
Python
"""Network utility functions for interface detection."""
|
|
|
|
import ipaddress
|
|
import logging
|
|
import socket
|
|
import struct
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Interfaces to exclude from selection
|
|
EXCLUDED_INTERFACE_PREFIXES = ("lo", "docker", "br-", "veth", "virbr")
|
|
|
|
|
|
def get_network_interfaces() -> list[dict]:
|
|
"""Get all network interfaces with their IPs and subnets.
|
|
|
|
Returns:
|
|
List of dicts with name, ip, netmask, subnet, broadcast
|
|
"""
|
|
interfaces = []
|
|
|
|
try:
|
|
import fcntl
|
|
|
|
for iface in socket.if_nameindex():
|
|
name = iface[1]
|
|
|
|
# Skip excluded interfaces
|
|
if any(name.startswith(prefix) for prefix in EXCLUDED_INTERFACE_PREFIXES):
|
|
continue
|
|
|
|
try:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
|
# Get IP address
|
|
ip_bytes = fcntl.ioctl(
|
|
s.fileno(),
|
|
0x8915, # SIOCGIFADDR
|
|
struct.pack("256s", name[:15].encode()),
|
|
)[20:24]
|
|
ip = socket.inet_ntoa(ip_bytes)
|
|
|
|
# Get netmask
|
|
netmask_bytes = fcntl.ioctl(
|
|
s.fileno(),
|
|
0x891B, # SIOCGIFNETMASK
|
|
struct.pack("256s", name[:15].encode()),
|
|
)[20:24]
|
|
netmask = socket.inet_ntoa(netmask_bytes)
|
|
|
|
# Calculate subnet
|
|
network = ipaddress.IPv4Network(f"{ip}/{netmask}", strict=False)
|
|
|
|
interfaces.append(
|
|
{
|
|
"name": name,
|
|
"ip": ip,
|
|
"netmask": netmask,
|
|
"subnet": str(network),
|
|
}
|
|
)
|
|
|
|
s.close()
|
|
except OSError:
|
|
# Interface doesn't have an IP or other error
|
|
pass
|
|
except Exception as e:
|
|
logger.debug("Error getting info for interface %s: %s", name, e)
|
|
|
|
except ImportError:
|
|
# fcntl not available (Windows)
|
|
logger.warning("fcntl not available, interface detection limited")
|
|
except Exception as e:
|
|
logger.error("Error enumerating interfaces: %s", e)
|
|
|
|
return interfaces
|
|
|
|
|
|
def find_interface_for_ip(target_ip: str) -> dict | None:
|
|
"""Find which interface is on the same subnet as the target IP.
|
|
|
|
Args:
|
|
target_ip: IP address to find the matching interface for
|
|
|
|
Returns:
|
|
Interface dict or None if not found
|
|
"""
|
|
try:
|
|
target = ipaddress.IPv4Address(target_ip)
|
|
except ValueError:
|
|
logger.error("Invalid target IP: %s", target_ip)
|
|
return None
|
|
|
|
interfaces = get_network_interfaces()
|
|
|
|
for iface in interfaces:
|
|
try:
|
|
network = ipaddress.IPv4Network(iface["subnet"], strict=False)
|
|
if target in network:
|
|
logger.debug("Found interface %s (%s) for target %s", iface["name"], iface["ip"], target_ip)
|
|
return iface
|
|
except ValueError:
|
|
continue
|
|
|
|
logger.warning("No interface found for target IP %s", target_ip)
|
|
return None
|
|
|
|
|
|
def get_other_interfaces(exclude_ip: str) -> list[dict]:
|
|
"""Get all interfaces except the one with the given IP.
|
|
|
|
Args:
|
|
exclude_ip: IP address of interface to exclude
|
|
|
|
Returns:
|
|
List of interface dicts
|
|
"""
|
|
interfaces = get_network_interfaces()
|
|
return [iface for iface in interfaces if iface["ip"] != exclude_ip]
|