Files
xingrin/backend/apps/common/validators.py
2025-12-12 18:04:57 +08:00

143 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""域名、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