mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-01 21:07:01 +08:00
108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
"""
|
||
基于系统套接字和标准库解析 IP,并提供兼容的内网地址判定。
|
||
"""
|
||
|
||
import ipaddress
|
||
import socket
|
||
from urllib.parse import urlparse
|
||
|
||
|
||
class IpUtils:
|
||
"""
|
||
提供 IP 地址、域名和内外网判断能力。
|
||
"""
|
||
|
||
@staticmethod
|
||
def is_ipv4(ip: str) -> bool:
|
||
"""
|
||
检查 IPv4 地址文本,系统不提供 inet_pton 时回退至 inet_aton 并检查分段。
|
||
|
||
:param ip: 要检查的地址文本
|
||
:return: 可解析为 IPv4 地址时返回 True
|
||
"""
|
||
try:
|
||
socket.inet_pton(socket.AF_INET, ip)
|
||
# 部分平台没有 inet_pton,使用旧接口保留 IPv4 检查能力。
|
||
except AttributeError:
|
||
try:
|
||
socket.inet_aton(ip)
|
||
except socket.error:
|
||
return False
|
||
return ip.count('.') == 3
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
@staticmethod
|
||
def is_ipv6(ip: str) -> bool:
|
||
"""
|
||
检查 IPv6 地址文本。
|
||
|
||
:param ip: 要检查的地址文本
|
||
:return: 系统可以将其解析为 IPv6 地址时返回 True
|
||
"""
|
||
try:
|
||
socket.inet_pton(socket.AF_INET6, ip)
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
@staticmethod
|
||
def is_internal(hostname: str) -> bool:
|
||
"""
|
||
从 URL 提取主机名,并检查地址或域名解析结果是否为内网地址。
|
||
|
||
:param hostname: 包含主机部分的 URL;无法提取主机名时按非内网处理
|
||
:return: 主机被识别为私有地址时返回 True,否则返回 False
|
||
"""
|
||
parsed_hostname = urlparse(hostname).hostname
|
||
if not parsed_hostname:
|
||
return False
|
||
if IpUtils.is_ip(parsed_hostname):
|
||
return IpUtils.is_private_ip(parsed_hostname)
|
||
else:
|
||
return IpUtils.is_internal_domain(parsed_hostname)
|
||
|
||
@staticmethod
|
||
def is_ip(addr: str) -> bool:
|
||
"""
|
||
使用系统 inet_aton 检查 IPv4 地址,沿用其对缩写格式的接受规则。
|
||
|
||
:param addr: 待解析的 IPv4 文本;本方法不检查 IPv6
|
||
:return: 系统可以解析时返回 True,否则返回 False
|
||
"""
|
||
try:
|
||
socket.inet_aton(addr)
|
||
return True
|
||
except socket.error:
|
||
return False
|
||
|
||
@staticmethod
|
||
def is_internal_domain(domain: str) -> bool:
|
||
"""
|
||
同步查询域名的单个 IPv4 地址并判断是否为私有地址。
|
||
|
||
:param domain: 要提交给系统 DNS 的域名
|
||
:return: 解析地址为私有地址时返回 True;解析失败时返回 False
|
||
"""
|
||
try:
|
||
ip = socket.gethostbyname(domain)
|
||
except socket.error:
|
||
return False
|
||
|
||
return IpUtils.is_private_ip(ip)
|
||
|
||
@staticmethod
|
||
def is_private_ip(ip_str: str) -> bool:
|
||
"""
|
||
按 Python 标准库的 is_private 规则检查 IPv4 或 IPv6 地址。
|
||
|
||
:param ip_str: 地址文本,首尾空白会被去除
|
||
:return: 标准库判定为私有地址时返回 True;无效地址返回 False
|
||
"""
|
||
try:
|
||
return ipaddress.ip_address(ip_str.strip()).is_private
|
||
except Exception as e:
|
||
print(str(e))
|
||
return False
|