FreeSwitch 的 mod_blacklist
模块详解
mod_blacklist
是 FreeSwitch 中用于实现黑名单/白名单功能的模块,可基于来电号码(Caller ID)、IP 地址或其他条件拦截或放行呼叫。结合 LuaJIT 脚本,能实现动态、高效的呼叫控制逻辑。
1. 核心功能
- 黑白名单控制 :
- 黑名单(Blacklist):直接拒绝特定号码或 IP 的呼叫。
- 白名单(Whitelist):仅允许特定号码或 IP 的呼叫(优先级高于黑名单)。
- 拦截条件 :
- 支持
Caller ID
(主叫号码)、Destination Number
(被叫号码)、IP 地址
、SIP 头
等字段。 - 可配置自动拉黑机制(如:多次验证失败后自动加入黑名单)。
- 支持
- 动态管理:支持通过数据库、API 或 LuaJIT 脚本实时更新名单。
2. 配置方法
2.1 启用模块
在 FreeSwitch 配置文件(autoload_configs/modules.conf.xml
)中启用模块:
xml
<load module="mod_blacklist"/>
2.2 配置黑白名单
-
静态配置 :在
vars.xml
或dialplan
中定义名单:xml<!-- 黑名单 --> <X-PRE-PROCESS cmd="set" data="blacklist=^(恶意号码正则|192\.168\.1\.100)$"/> <!-- 白名单 --> <X-PRE-PROCESS cmd="set" data="whitelist=^(\+86139\d+|信任IP)$"/>
-
自动拉黑:配置失败次数阈值和时间窗口(如 5 分钟内失败 3 次自动拉黑):
xml<param name="auto_ban_seconds" value="3600"/> <!-- 拉黑时长(秒) --> <param name="max_failed_attempts" value="3"/> <!-- 最大失败次数 -->
3. 结合 LuaJIT 实现动态控制
3.1 基本流程
通过 LuaJIT 脚本在呼叫建立阶段(SESSION_INIT
)检查黑名单:
- 从数据库/缓存(如 Redis)中动态查询号码或 IP。
- 调用
mod_blacklist
的 API 进行拦截或放行。
3.2 LuaJIT 脚本示例
lua
-- blacklist_check.lua
local blacklist = require "mod_blacklist"
local caller_id = session:getVariable("caller_id_number")
local client_ip = session:getVariable("sip_remote_ip")
-- 检查黑名单
if blacklist.check(caller_id) or blacklist.check(client_ip) then
session:answer()
session:streamFile("ivr/access_denied.wav")
session:hangup()
return
end
-- 放行并继续路由
session:continue()
3.3 集成到拨号计划
在 dialplan
中调用 Lua 脚本:
xml
<extension name="blacklist_check">
<condition>
<action application="lua" data="blacklist_check.lua"/>
</condition>
</extension>
4. 高级用法
4.1 自动拉黑恶意IP
lua
-- 失败次数统计(示例伪代码)
local max_attempts = 3
local ban_time = 3600
local key = "blacklist:" .. client_ip
-- 使用 Redis 统计失败次数
local redis = require "redis"
local conn = redis.connect("127.0.0.1", 6379)
local attempts = conn:incr(key)
if attempts >= max_attempts then
conn:expire(key, ban_time) -- 设置过期时间
blacklist.add(client_ip) -- 动态加入黑名单
end
4.2 白名单例外
lua
local whitelist = {"+8613812345678", "192.168.1.1"}
if not (blacklist.in_whitelist(caller_id) or blacklist.in_whitelist(client_ip)) then
-- 执行黑名单检查
end
5. 应用场景
- 防骚扰/欺诈:拦截高频呼叫、虚假号码。
- 安全防护 :阻止 SIP 扫描工具或 DoS 攻击(如
freeswitch-py
发起的恶意注册)。 - 企业内控:限制非授权号码访问特定IVR菜单。
6. 注意事项
- 性能:LuaJIT 的高效性适合高并发场景,但频繁访问外部数据库可能成为瓶颈。
- 误拦截:建议结合人工审核或日志监控。
- 版本兼容性 :FreeSwitch 1.10+ 对
mod_blacklist
支持较好,旧版本需测试。
通过 mod_blacklist
和 LuaJIT 的灵活组合,可构建强大的呼叫控制逻辑,显著提升 FreeSwitch 的安全性和可管理性。
被叫号码的黑名单管理
是的,mod_blacklist
可以结合 LuaJIT 脚本实现被叫号码(Destination Number)的黑名单管理 。虽然 mod_blacklist
默认功能更偏向主叫号码(Caller ID)或 IP 拦截,但通过灵活的脚本逻辑和拨号计划(Dialplan)的配合,完全能实现对被叫号码的拦截。以下是具体实现方法:
1. 核心思路
通过 LuaJIT 脚本 在呼叫路由阶段(dialplan
)检查被叫号码(destination_number
)是否在黑名单中:
- 若被叫号码在黑名单中,直接拦截呼叫。
- 若不在黑名单,则放行并继续路由。
2. 实现步骤
2.1 定义被叫号码黑名单
在 Lua 脚本中定义黑名单逻辑(支持动态加载或静态列表):
lua
-- blacklist_destination.lua
local destination = session:getVariable("destination_number") -- 获取被叫号码
-- 定义被叫号码黑名单(支持正则表达式或静态列表)
local blocked_numbers = {
"^95013$", -- 拦截 95013
"^\\+?86(400|800)", -- 拦截 400/800 开头的号码(国际格式兼容)
"^192\.168\.1\.", -- 拦截特定 IP 发起的被叫请求(如 SIP 扫描工具)
}
-- 检查是否命中黑名单
for _, pattern in ipairs(blocked_numbers) do
if string.match(destination, pattern) then
session:answer()
session:streamFile("ivr/blocked.wav") -- 播放拦截提示音
session:hangup("CALL_REJECTED") -- 挂断并标记原因
return
end
end
-- 放行呼叫
session:continue()
2.2 集成到拨号计划(Dialplan)
在 FreeSwitch 的拨号计划(如 default/context
)中添加拦截规则:
xml
<extension name="destination_blacklist">
<condition field="destination_number" expression="^.*$"> <!-- 匹配所有被叫号码 -->
<action application="lua" data="blacklist_destination.lua"/>
</condition>
</extension>
2.3 动态管理黑名单(可选)
若需动态更新黑名单(例如从数据库或 API 加载):
lua
-- 从 Redis 获取实时黑名单
local redis = require "redis"
local conn = redis.connect("127.0.0.1", 6379)
local blocked_numbers = conn:smembers("blocked_destinations") -- 假设使用集合存储
-- 检查被叫号码
if blocked_numbers then
for _, number in ipairs(blocked_numbers) do
if destination == number then
session:hangup("CALL_REJECTED")
return
end
end
end
3. 高级场景
3.1 按主叫+被叫组合拦截
若需拦截特定主叫拨打特定被叫号码(如防止内部号码拨打国际长途):
lua
local caller = session:getVariable("caller_id_number")
local destination = session:getVariable("destination_number")
-- 定义规则:主叫 1001 不允许拨打 00 开头的号码
if caller == "1001" and string.match(destination, "^00") then
session:hangup("CALL_REJECTED")
return
end
3.2 基于计费系统的拦截
结合外部计费系统,检查账户余额是否允许拨打高价值号码:
lua
local account = session:getVariable("user_account")
local destination = session:getVariable("destination_number")
-- 调用外部 API 检查权限
local http = require "socket.http"
local response = http.request("http://billing/api/check?account="..account.."&dest="..destination)
if response == "DENY" then
session:streamFile("ivr/insufficient_balance.wav")
session:hangup()
return
end
4. 性能优化
- 缓存黑名单:将黑名单缓存在 Lua 脚本的全局变量中,定期更新(如每 5 分钟从数据库加载一次),避免每次呼叫都查询外部存储。
- 正则优化 :使用高效的正则表达式(如避免
.*
贪婪匹配),减少 CPU 开销。 - 分层次拦截 :
- IP 层拦截 :优先通过
mod_blacklist
的 IP 黑名单拦截恶意流量。 - Lua 脚本拦截:再通过 Lua 脚本精细化处理被叫号码逻辑。
- IP 层拦截 :优先通过
5. 注意事项
-
拨号计划顺序 :确保拦截逻辑的
extension
在拨号计划中优先级最高(先执行拦截检查,再处理正常路由)。 -
日志记录 :在黑名单拦截时记录日志,便于审计:
luafreeswitch.consoleLog("WARNING", "拦截被叫号码: " .. destination .. "\n")
-
误拦截恢复:提供管理接口(如 HTTP API)允许动态移除误加的黑名单条目。
6. 替代方案
如果 mod_blacklist
的默认功能无法满足需求,还可考虑:
mod_lua
+ 数据库:完全自定义逻辑。mod_acl
:基于 IP/CIDR 的访问控制(仅限 IP 层)。mod_fail2ban
:自动封禁恶意 IP(需配合系统日志)。
通过上述方法,可以灵活利用 mod_blacklist
结合 LuaJIT 实现被叫号码的黑名单管理,既能满足静态规则拦截,也能支持动态策略,适用于防欺诈、合规控制、资源保护等多种场景。
结合mysq,redis,http实现动态黑名单
分步解决方案
1. 数据库表优化(MySQL)
为 prefix
字段创建唯一索引,确保数据唯一性:
sql
-- 检查现有表结构
DESC blacklist;
-- 添加唯一索引
ALTER TABLE blacklist ADD UNIQUE INDEX idx_prefix (prefix);
2. Redis 缓存配置(10号库)
-
使用
SET
类型存储黑名单前缀,例如:bash# 示例:添加前缀 110、120、119 到 Redis 10号库 redis-cli -n 10 SADD blocked_prefixes "110" "120" "119"
3. LuaJIT 脚本实现
创建脚本 /usr/share/freeswitch/scripts/blacklist_check.lua
,逻辑如下:
lua
-- blacklist_check.lua
local log = freeswitch.consoleLog
local session = require "freeswitch.session"
local http = require "socket.http"
local ltn12 = require "ltn12"
local redis = require "redis"
local luasql = require "luasql.mysql"
-- 配置参数
local REDIS_HOST = "127.0.0.1"
local REDIS_PORT = 6379
local REDIS_DB = 10
local MYSQL_CONN_STR = "mysql://user:password@localhost/dbname"
local HTTP_API_URL = "http://blacklist-api/check?number="
-- 初始化 Redis 连接
local redis_client = redis.connect(REDIS_HOST, REDIS_PORT)
redis_client:select(REDIS_DB)
-- 初始化 MySQL 连接
local mysql_env = luasql.mysql()
local mysql_conn = mysql_env:connect("dbname", "user", "password", "localhost")
-- 检查紧急号码前缀(如 110、120、119)
local function is_emergency_number(destination)
local emergency_prefixes = {"110", "120", "119"}
for _, prefix in ipairs(emergency_prefixes) do
if string.sub(destination, 1, #prefix) == prefix then
return true
end
end
return false
end
-- 查询 Redis 黑名单
local function check_redis(prefix)
local exists = redis_client:sismember("blocked_prefixes", prefix)
return exists == 1
end
-- 查询 MySQL 黑名单
local function check_mysql(prefix)
local cursor = mysql_conn:execute(string.format("SELECT 1 FROM blacklist WHERE prefix = '%s' LIMIT 1", prefix))
local row = cursor:fetch({}, "a")
cursor:close()
return row ~= nil
end
-- HTTP 查询并缓存结果
local function check_http_and_cache(prefix)
local response_body = {}
local res, code = http.request{
url = HTTP_API_URL .. prefix,
sink = ltn12.sink.table(response_body)
}
if code == 200 then
local result = table.concat(response_body)
if result == "BLOCK" then
-- 写入 MySQL
mysql_conn:execute(string.format("INSERT IGNORE INTO blacklist (prefix) VALUES ('%s')", prefix))
-- 写入 Redis
redis_client:sadd("blocked_prefixes", prefix)
return true
end
end
return false
end
-- 主逻辑
local function handle_call(session)
local destination = session:getVariable("destination_number")
if not destination then return session:continue() end
-- 拦截紧急号码
if is_emergency_number(destination) then
session:hangup("CALL_REJECTED")
return
end
-- 检查 Redis
local blocked = check_redis(destination)
if not blocked then
-- 检查 MySQL
blocked = check_mysql(destination)
if not blocked then
-- 查询 HTTP API
blocked = check_http_and_cache(destination)
else
-- 缓存到 Redis
redis_client:sadd("blocked_prefixes", destination)
end
end
if blocked then
session:answer()
session:streamFile("ivr/blocked.wav")
session:hangup("CALL_REJECTED")
else
session:continue()
end
end
-- 执行
local sess = session.new()
handle_call(sess)
4. FreeSwitch 集成
在拨号计划中调用脚本:
xml
<!-- /etc/freeswitch/dialplan/default/01_blacklist.xml -->
<extension name="blacklist_check">
<condition field="destination_number" expression="^.*$">
<action application="lua" data="blacklist_check.lua"/>
</condition>
</extension>
5. 依赖安装(Ubuntu 22.04)
安装 LuaJIT、Redis 和 MySQL 客户端库:
bash
sudo apt-get install -y luajit lua-socket lua-sec luarocks libmysqlclient-dev
sudo luarocks install luasql-mysql MYSQL_INCDIR=/usr/include/mysql
sudo luarocks install lua-resty-redis
6. 性能优化与验证
-
连接池 :使用
lua-resty-redis
和luasql
的连接池功能。 -
日志监控 :
lualog("NOTICE", string.format("拦截号码: %s\n", destination))
-
压力测试 :使用
sipp
工具模拟高并发呼叫验证性能。
关键逻辑流程图
plaintext
1. 获取被叫号码(destination_number)
│
├─ 是紧急号码(110/120/119前缀)? → 拦截
│
└─ 否 → 查询 Redis
│
├─ 存在 → 拦截
│
└─ 不存在 → 查询 MySQL
│
├─ 存在 → 缓存到 Redis → 拦截
│
└─ 不存在 → 查询 HTTP API
│
├─ 需拦截 → 写入 MySQL + Redis → 拦截
│
└─ 放行 → 继续路由
通过上述方案,可实现高效、动态的黑名单拦截,满足复杂业务场景需求。
针对紧急号码如110等的处理方案
分步解决方案:灵活控制紧急号码拦截
1. 数据库设计优化
在 MySQL 中创建两个新表,用于管理紧急号码拦截策略 和主叫白名单:
sql
-- 紧急号码拦截配置表
CREATE TABLE emergency_config (
prefix VARCHAR(20) PRIMARY KEY COMMENT '紧急号码前缀(如110、120)',
is_blocked TINYINT DEFAULT 1 COMMENT '是否拦截(1=拦截,0=放行)',
description VARCHAR(255) COMMENT '规则描述'
);
-- 主叫白名单表(允许绕过紧急号码拦截)
CREATE TABLE emergency_whitelist (
caller_id VARCHAR(20) PRIMARY KEY COMMENT '主叫号码',
note VARCHAR(255) COMMENT '备注'
);
-- 插入示例数据
INSERT INTO emergency_config (prefix, is_blocked, description) VALUES
('110', 1, '默认拦截110'),
('120', 0, '放行120'),
('119', 1, '工作日拦截119');
INSERT INTO emergency_whitelist (caller_id, note) VALUES
('+8613912345678', '安保部门专用号码'),
('1001', '内部测试号码');
2. Redis 缓存策略更新
将紧急号码配置和白名单缓存到 Redis 10号库,使用 Hash 和 Set 结构:
bash
# 紧急号码配置(Hash 存储)
redis-cli -n 10 HSET emergency:config 110 1 120 0 119 1
# 主叫白名单(Set 存储)
redis-cli -n 10 SADD emergency:whitelist +8613912345678 1001
3. LuaJIT 脚本增强逻辑
修改 /usr/share/freeswitch/scripts/blacklist_check.lua
,添加灵活控制逻辑:
lua
-- 新增函数:检查主叫是否在白名单
local function is_whitelisted_caller(caller_id)
local exists = redis_client:sismember("emergency:whitelist", caller_id)
return exists == 1
end
-- 新增函数:检查紧急号码是否需要拦截
local function check_emergency_block(destination, caller_id)
-- 如果主叫在白名单,直接放行
if is_whitelisted_caller(caller_id) then
return false
end
-- 检查是否为紧急号码
local emergency_prefixes = redis_client:hkeys("emergency:config")
for _, prefix in ipairs(emergency_prefixes) do
if string.sub(destination, 1, #prefix) == prefix then
-- 从 Redis 获取拦截配置
local block_flag = redis_client:hget("emergency:config", prefix)
if block_flag == "1" then
-- 附加条件:例如拦截119仅在工作日
if prefix == "119" then
local weekday = os.date("%w") -- 0=周日,1=周一...
if weekday >= 1 and weekday <= 5 then
return true
else
return false
end
end
return true
end
end
end
return false
end
-- 修改主逻辑
local function handle_call(session)
local destination = session:getVariable("destination_number")
local caller_id = session:getVariable("caller_id_number")
-- 检查紧急号码拦截(带条件)
if check_emergency_block(destination, caller_id) then
session:hangup("CALL_REJECTED")
return
end
-- ...原有黑名单逻辑...
end
4. 动态配置更新机制
实现配置热更新(避免重启 FreeSwitch):
4.1 定时同步数据库到 Redis
创建 Python 脚本 /opt/scripts/sync_emergency_config.py
:
python
import redis
import MySQLdb
import schedule
import time
def sync_config():
# 连接 MySQL
db = MySQLdb.connect("localhost","user","pass","db")
cursor = db.cursor()
# 同步紧急号码配置
cursor.execute("SELECT prefix, is_blocked FROM emergency_config")
r = redis.Redis(host='localhost', port=6379, db=10)
r.delete("emergency:config")
for row in cursor.fetchall():
r.hset("emergency:config", row[0], row[1])
# 同步主叫白名单
cursor.execute("SELECT caller_id FROM emergency_whitelist")
r.delete("emergency:whitelist")
for row in cursor.fetchall():
r.sadd("emergency:whitelist", row[0])
db.close()
# 每5分钟同步一次
schedule.every(5).minutes.do(sync_config)
while True:
schedule.run_pending()
time.sleep(1)
4.2 使用 Systemd 管理同步服务
创建 /etc/systemd/system/emergency_sync.service
:
ini
[Unit]
Description=Emergency Config Sync Service
[Service]
ExecStart=/usr/bin/python3 /opt/scripts/sync_emergency_config.py
Restart=always
[Install]
WantedBy=multi-user.target
启动服务:
bash
sudo systemctl daemon-reload
sudo systemctl start emergency_sync
sudo systemctl enable emergency_sync
5. 多维度拦截控制示例
场景1:时间条件拦截
在 check_emergency_block
函数中添加时间判断:
lua
-- 拦截110仅在 00:00-06:00
if prefix == "110" and block_flag == "1" then
local hour = tonumber(os.date("%H"))
if hour >= 0 and hour < 6 then
return true
else
return false
end
end
场景2:基于地理位置的放行
调用 IP 地理接口(需额外实现):
lua
local function get_location(ip)
local http = require "socket.http"
local res, code = http.request("http://ip-api.com/json/"..ip)
if code == 200 then
return json.decode(res).countryCode
end
return nil
end
-- 在检查逻辑中添加:
local caller_ip = session:getVariable("sip_remote_ip")
if prefix == "119" and get_location(caller_ip) == "CN" then
return false -- 国内IP放行119
end
6. 管理接口示例
通过 HTTP API 动态修改配置:
6.1 使用 Flask 创建管理 API
创建 /opt/scripts/emergency_api.py
:
python
from flask import Flask, request
import MySQLdb
import redis
app = Flask(__name__)
@app.route('/update_emergency', methods=['POST'])
def update_config():
data = request.json
prefix = data['prefix']
action = data['action'] # block/unblock
# 更新数据库
db = MySQLdb.connect("localhost","user","pass","db")
cursor = db.cursor()
cursor.execute(f"INSERT INTO emergency_config (prefix, is_blocked) VALUES ('{prefix}', {1 if action=='block' else 0}) ON DUPLICATE KEY UPDATE is_blocked=VALUES(is_blocked)")
db.commit()
# 立即同步到Redis
r = redis.Redis(db=10)
r.hset("emergency:config", prefix, 1 if action=='block' else 0)
return {"status": "success"}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
6.2 调用示例
bash
curl -X POST http://localhost:8080/update_emergency \
-H "Content-Type: application/json" \
-d '{"prefix": "110", "action": "unblock"}'
架构拓扑图
plaintext
+----------------+ +----------------+
| FreeSwitch | | MySQL |
| | | |
| LuaJIT Script |<----->| (黑名单配置) |
+--------+-------+ +--------+-------+
| |
| |
+--------v-------+ +--------v-------+
| Redis (DB10) | | 同步脚本 |
| (缓存配置) |<------+ (Python) |
+--------+-------+ +----------------+
|
|
+--------v-------+
| 管理API |
| (Flask) |
+----------------+
通过此方案,可实现:
- 紧急号码动态管控:通过数据库配置实时调整拦截策略
- 主叫白名单机制:允许特定号码绕过拦截
- 多维度条件判断:时间、地理位置、主叫身份等
- 高效缓存同步:确保配置变更秒级生效
- 运维友好性:提供 API 和脚本化运维接口
mod_translate【号码格式化】和mod_blacklist【黑明那单】两个模块的执行顺序建议
分步解决方案:mod_translate
与 mod_blacklist
执行顺序详解
1. 核心原则
FreeSwitch 模块的执行顺序由 模块加载顺序 和 事件订阅优先级 共同决定。mod_translate
(号码转换)与 mod_blacklist
(黑名单拦截)的协作需遵循以下原则:
- 先拦截后转换 :若需基于原始号码进行拦截(如防止攻击者通过篡改号码绕过黑名单),应先执行
mod_blacklist
。 - 先转换后拦截 :若需基于标准化后的号码进行拦截(如黑名单中存储的是转换后的完整号码),应先执行
mod_translate
。
2. 典型场景与配置
场景1:先拦截后转换(推荐安全模式)
需求:
- 直接拦截原始恶意号码(如
95013
),无需转换。 - 合法号码需转换为标准格式(如
95013 → +8610095013
)。
配置方法:
-
模块加载顺序 :
确保
mod_blacklist
早于mod_translate
加载(修改modules.conf.xml
):xml<!-- 先加载黑名单模块 --> <load module="mod_blacklist"/> <!-- 再加载转换模块 --> <load module="mod_translate"/>
-
事件优先级调整 (可选):
通过
mod_blacklist
的session:check_blacklist
事件在CHANNEL_CREATE
阶段拦截:xml<!-- 在 vars.xml 中设置拦截事件优先级 --> <X-PRE-PROCESS cmd="set" data="blacklist_early_check=true"/>
-
拨号计划顺序 :
在
dialplan
中明确逻辑顺序:xml<extension name="blacklist_first"> <!-- 1. 先执行黑名单检查 --> <condition> <action application="lua" data="blacklist_check.lua"/> </condition> </extension> <extension name="translate_after"> <!-- 2. 号码转换 --> <condition field="destination_number" expression="^(\d+)$"> <action application="translate" data="$1 /path/to/rules.xml"/> </condition> </extension>
场景2:先转换后拦截(业务适配模式)
需求:
- 拦截转换后的号码(如黑名单中存储国际格式
+8610095013
)。 - 需对原始号码(如
95013
)先转换再检查。
配置方法:
-
模块加载顺序 :
确保
mod_translate
早于mod_blacklist
加载:xml<load module="mod_translate"/> <load module="mod_blacklist"/>
-
转换规则配置 (
rules.xml
):xml<rule name="Local to E164"> <match>^(\d{5})$</match> <!-- 匹配短号 95013 --> <action>+86100$1</action> <!-- 转换为 +8610095013 --> </rule>
-
黑名单配置 :
在
mod_blacklist
中设置拦截转换后的号码:xml<param name="blacklist" value="^\+8610095013$"/>
-
Lua 脚本适配 :
在
blacklist_check.lua
中检查转换后的号码:lualocal destination = session:getVariable("destination_number") -- 直接使用转换后的号码 if blacklist.check(destination) then session:hangup() end
3. 混合模式(动态决策)
需求:
- 对某些号码(如 VIP)直接放行,不触发转换。
- 对普通号码先转换再拦截。
实现方法:
xml
<!-- 拨号计划分层次处理 -->
<extension name="vip_bypass">
<condition field="${vip_caller}" expression="^true$">
<!-- VIP 主叫直接跳过转换和拦截 -->
<action application="bridge" data="user/${destination_number}"/>
</condition>
</extension>
<extension name="normal_process">
<!-- 1. 普通号码先转换 -->
<condition>
<action application="translate" data="${destination_number} /path/to/rules.xml"/>
</condition>
<!-- 2. 再拦截 -->
<condition>
<action application="lua" data="blacklist_check.lua"/>
</condition>
</extension>
4. 性能与安全注意事项
-
模块加载顺序验证 :
通过
fs_cli
命令module_exists mod_blacklist
检查模块是否按需加载。 -
日志调试 :
在关键节点添加日志,观察执行顺序:
lua-- Lua 脚本中 freeswitch.consoleLog("NOTICE", "转换前号码: " .. orig_number .. "\n") freeswitch.consoleLog("NOTICE", "转换后号码: " .. new_number .. "\n")
-
正则表达式优化:
mod_blacklist
使用高效正则(如^\+?\d+$
避免回溯)。mod_translate
的规则文件采用最长匹配优先
策略。
-
防御性设计:
xml<!-- 防止转换模块被绕过 --> <param name="translate_suppress_leg" value="true"/>
5. 执行顺序流程图
plaintext
+-----------------+
| SIP INVITE |
+--------+--------+
|
+--------------+--------------+
| 模块拦截顺序 |
| 1. mod_blacklist (if loaded first)
| 2. mod_translate (if loaded next)
+--------------+--------------+
|
+--------------+--------------+
| 拨号计划逻辑顺序 |
| 1. 黑名单检查(Lua/XML) |
| 2. 号码转换(translate) |
+--------------+--------------+
|
+--------v--------+
| 路由决策 |
+-----------------+
总结
- 安全优先场景 :
mod_blacklist
→mod_translate
(原始号码拦截)。 - 业务适配场景 :
mod_translate
→mod_blacklist
(标准化后拦截)。 - 混合模式 :通过拨号计划分层次控制,兼顾灵活性与安全性。
实际配置需结合具体业务需求,通过模块加载顺序、拨号计划优先级、日志监控三者协同验证。