mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-02-02 04:33:10 +08:00
143 lines
3.7 KiB
Python
143 lines
3.7 KiB
Python
|
|
"""域名、IP、端口和目标验证工具函数"""
|
|||
|
|
import ipaddress
|
|||
|
|
import logging
|
|||
|
|
import validators
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def validate_domain(domain: str) -> None:
|
|||
|
|
"""
|
|||
|
|
验证域名格式(使用 validators 库)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
domain: 域名字符串(应该已经规范化)
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
ValueError: 域名格式无效
|
|||
|
|
"""
|
|||
|
|
if not domain:
|
|||
|
|
raise ValueError("域名不能为空")
|
|||
|
|
|
|||
|
|
# 使用 validators 库验证域名格式
|
|||
|
|
# 支持国际化域名(IDN)和各种边界情况
|
|||
|
|
if not validators.domain(domain):
|
|||
|
|
raise ValueError(f"域名格式无效: {domain}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def validate_ip(ip: str) -> None:
|
|||
|
|
"""
|
|||
|
|
验证 IP 地址格式(支持 IPv4 和 IPv6)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: IP 地址字符串(应该已经规范化)
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
ValueError: IP 地址格式无效
|
|||
|
|
"""
|
|||
|
|
if not ip:
|
|||
|
|
raise ValueError("IP 地址不能为空")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
ipaddress.ip_address(ip)
|
|||
|
|
except ValueError:
|
|||
|
|
raise ValueError(f"IP 地址格式无效: {ip}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def validate_cidr(cidr: str) -> None:
|
|||
|
|
"""
|
|||
|
|
验证 CIDR 格式(支持 IPv4 和 IPv6)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
cidr: CIDR 字符串(应该已经规范化)
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
ValueError: CIDR 格式无效
|
|||
|
|
"""
|
|||
|
|
if not cidr:
|
|||
|
|
raise ValueError("CIDR 不能为空")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
ipaddress.ip_network(cidr, strict=False)
|
|||
|
|
except ValueError:
|
|||
|
|
raise ValueError(f"CIDR 格式无效: {cidr}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def detect_target_type(name: str) -> str:
|
|||
|
|
"""
|
|||
|
|
检测目标类型(不做规范化,只验证)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 目标名称(应该已经规范化)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
str: 目标类型 ('domain', 'ip', 'cidr') - 使用 Target.TargetType 枚举值
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
ValueError: 如果无法识别目标类型
|
|||
|
|
"""
|
|||
|
|
# 在函数内部导入模型,避免 AppRegistryNotReady 错误
|
|||
|
|
from apps.targets.models import Target
|
|||
|
|
|
|||
|
|
if not name:
|
|||
|
|
raise ValueError("目标名称不能为空")
|
|||
|
|
|
|||
|
|
# 检查是否是 CIDR 格式(包含 /)
|
|||
|
|
if '/' in name:
|
|||
|
|
validate_cidr(name)
|
|||
|
|
return Target.TargetType.CIDR
|
|||
|
|
|
|||
|
|
# 检查是否是 IP 地址
|
|||
|
|
try:
|
|||
|
|
validate_ip(name)
|
|||
|
|
return Target.TargetType.IP
|
|||
|
|
except ValueError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 检查是否是合法域名
|
|||
|
|
try:
|
|||
|
|
validate_domain(name)
|
|||
|
|
return Target.TargetType.DOMAIN
|
|||
|
|
except ValueError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 无法识别的格式
|
|||
|
|
raise ValueError(f"无法识别的目标格式: {name},必须是域名、IP地址或CIDR范围")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def validate_port(port: any) -> tuple[bool, int | None]:
|
|||
|
|
"""
|
|||
|
|
验证并转换端口号
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
port: 待验证的端口号(可能是字符串、整数或其他类型)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
tuple: (is_valid, port_number)
|
|||
|
|
- is_valid: 端口是否有效
|
|||
|
|
- port_number: 有效时为整数端口号,无效时为 None
|
|||
|
|
|
|||
|
|
验证规则:
|
|||
|
|
1. 必须能转换为整数
|
|||
|
|
2. 必须在 1-65535 范围内
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
>>> is_valid, port_num = validate_port(8080)
|
|||
|
|
>>> is_valid, port_num
|
|||
|
|
(True, 8080)
|
|||
|
|
|
|||
|
|
>>> is_valid, port_num = validate_port("invalid")
|
|||
|
|
>>> is_valid, port_num
|
|||
|
|
(False, None)
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
port_num = int(port)
|
|||
|
|
if 1 <= port_num <= 65535:
|
|||
|
|
return True, port_num
|
|||
|
|
else:
|
|||
|
|
logger.warning("端口号超出有效范围 (1-65535): %d", port_num)
|
|||
|
|
return False, None
|
|||
|
|
except (ValueError, TypeError):
|
|||
|
|
logger.warning("端口号格式错误,无法转换为整数: %s", port)
|
|||
|
|
return False, None
|