Files
chunyu_project/utils/email_utils.py
T
2026-08-05 23:59:15 +08:00

64 lines
2.2 KiB
Python

import logging
import dns.resolver
from django.core.cache import caches
logger = logging.getLogger(__name__)
default_cache = caches['default']
MX_CACHE_TTL = 3600 # MX 验证结果缓存1小时,避免每次请求都做慢速DNS查询
# 常用邮箱域名白名单:大概率可正常收发,直接放行,无需DNS查询
COMMON_EMAIL_DOMAINS = {
'qq.com', 'foxmail.com', '163.com', '126.com', 'yeah.net',
'139.com', '189.cn', 'sina.com', 'sohu.com', '21cn.com',
'gmail.com', 'googlemail.com', 'outlook.com', 'hotmail.com',
'live.com', 'msn.com', 'icloud.com', 'me.com', 'yahoo.com',
'proton.me', 'protonmail.com', 'gmx.com', 'gmx.net', 'aol.com', 'zoho.com',
}
def validate_email_mx(email):
"""验证邮箱域名是否有MX记录(通过DNS MX查询),排除 null MX (MX 0 .) 的情况"""
try:
domain = email.split('@')[1].lower()
except IndexError:
return False
# 常用邮箱域名直接放行,免DNS查询
if domain in COMMON_EMAIL_DOMAINS:
return True
cache_key = f'mx_valid_{domain}'
try:
cached = default_cache.get(cache_key)
if cached is not None:
return cached
except Exception:
pass
valid = _query_mx(domain)
try:
default_cache.set(cache_key, valid, timeout=MX_CACHE_TTL)
except Exception:
pass
return valid
def _query_mx(domain):
try:
answers = dns.resolver.resolve(domain, 'MX')
# 检查是否是 null MX (MX 0 .) — 域名明确拒绝收件
for answer in answers:
exchange = answer.exchange.to_text().rstrip('.')
if exchange: # 有真实的邮件服务器
return True
# 所有 MX 记录都指向空(null MX),表示不接受邮件
logger.warning(f'[Email] Domain has null MX (rejects mail): {domain}')
return False
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.resolver.NoNameservers):
logger.warning(f'[Email] No MX record found for domain: {domain}')
return False
except Exception as e:
logger.warning(f'[Email] MX record check failed: domain={domain}, error={str(e)}')
return False