mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-01-31 11:46:16 +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
|