#!/usr/bin/env python3 """IP helper functions for the intrusion-detection system.""" from __future__ import annotations import ipaddress from typing import Iterable from whitelists import WHITELIST_NETS as _WHITELIST_NETS def parse_ip(value: str): """Return an ip_address object, or None if `value` is not an IP.""" try: return ipaddress.ip_address(value) except (ValueError, TypeError): return None def ip_version(value: str) -> int | None: addr = parse_ip(value) return addr.version if addr else None def is_ipv6(value: str) -> bool: return ip_version(value) == 6 def is_ipv4(value: str) -> bool: return ip_version(value) == 4 def whitelist_of(value: str) -> str | None: """Return the whitelist name an IP belongs to, else None.""" addr = parse_ip(value) if addr is None: return None for name, nets in _WHITELIST_NETS.items(): for net in nets: if addr.version == net.version and addr in net: return name return None def is_whitelisted(value: str) -> bool: return whitelist_of(value) is not None def sort_ips(ips: Iterable[str]) -> list[str]: """Numeric sort, IPv4 before IPv6, unparseable last.""" def key(ip: str): addr = parse_ip(ip) if addr is None: return (9, b"", ip) return (addr.version, addr.packed, "") return sorted(ips, key=key) def split_versions(ips: Iterable[str]) -> tuple[list[str], list[str]]: """Split an IP iterable into (sorted IPv4, sorted IPv6).""" ips = list(ips) return (sort_ips(ip for ip in ips if is_ipv4(ip)), sort_ips(ip for ip in ips if is_ipv6(ip)))