Files
lisglosips/infra/server-a/s28/lisglosips_hotpath.lua
T

257 lines
9.4 KiB
Lua

local active = redis.call('GET', KEYS[1])
if not active or active == '' then
return {
'reject',
'CONFIG_MISSING',
'none',
'none',
'none',
'none',
'no_active_version',
'none',
'none',
'none',
'none',
'UNKNOWN',
'UNKNOWN',
'UNKNOWN',
'UNKNOWN',
'UNKNOWN',
'UNKNOWN',
''
}
end
local source_ip = ARGV[1]
local caller = ARGV[2] or ''
local callee = ARGV[3] or ''
local prefix = 'cfg:v:' .. active
local function field(json, name)
return string.match(json, '"' .. name .. '":"([^"]*)"')
end
local function normalize_callee(value)
local digits = string.gsub(value or '', '[^0-9]', '')
if string.sub(digits, 1, 4) == '0086' then
digits = string.sub(digits, 5)
elseif string.sub(digits, 1, 2) == '86' and string.len(digits) == 13 then
digits = string.sub(digits, 3)
end
return digits
end
local function unknown_number(callee_digits)
return {
city_code = 'UNKNOWN',
city_name = 'UNKNOWN',
province_code = 'UNKNOWN',
province_name = 'UNKNOWN',
carrier = 'UNKNOWN',
number_type = 'UNKNOWN',
normalized = callee_digits or ''
}
end
local function with_geo_city(info)
if info.city_code == 'UNKNOWN' or info.province_code ~= 'UNKNOWN' then
return info
end
local city_json = redis.call('GET', prefix .. ':geo_city:' .. info.city_code)
if city_json then
info.province_code = field(city_json, 'provinceCode') or info.province_code
info.province_name = field(city_json, 'provinceName') or info.province_name
info.city_name = field(city_json, 'cityName') or info.city_name
end
return info
end
local function resolve_carrier(digits, fallback)
if string.len(digits) >= 4 then
local prefix4_json = redis.call('GET', prefix .. ':carrier_prefix:' .. string.sub(digits, 1, 4))
if prefix4_json then
return field(prefix4_json, 'carrier') or fallback
end
end
if string.len(digits) >= 3 then
local prefix3_json = redis.call('GET', prefix .. ':carrier_prefix:' .. string.sub(digits, 1, 3))
if prefix3_json then
return field(prefix3_json, 'carrier') or fallback
end
end
return fallback
end
local function resolve_number(value)
local digits = normalize_callee(value)
local info = unknown_number(digits)
if string.match(digits, '^1%d%d%d%d%d%d%d%d%d%d$') then
info.number_type = 'MOBILE'
local segment_json = redis.call('GET', prefix .. ':phone_segment:' .. string.sub(digits, 1, 7))
if segment_json then
info.city_code = field(segment_json, 'cityCode') or info.city_code
info.city_name = field(segment_json, 'cityName') or info.city_name
info.province_code = field(segment_json, 'provinceCode') or info.province_code
info.province_name = field(segment_json, 'provinceName') or info.province_name
info.carrier = field(segment_json, 'carrier') or info.carrier
end
info.carrier = resolve_carrier(digits, info.carrier)
return with_geo_city(info)
end
if string.sub(digits, 1, 1) == '0' and string.len(digits) >= 3 then
info.number_type = 'LANDLINE'
local area_json = nil
if string.len(digits) >= 4 then
area_json = redis.call('GET', prefix .. ':area_code:' .. string.sub(digits, 1, 4))
end
if not area_json and string.len(digits) >= 3 then
area_json = redis.call('GET', prefix .. ':area_code:' .. string.sub(digits, 1, 3))
end
if area_json then
info.city_code = field(area_json, 'cityCode') or info.city_code
info.city_name = field(area_json, 'cityName') or info.city_name
info.province_code = field(area_json, 'provinceCode') or info.province_code
info.province_name = field(area_json, 'provinceName') or info.province_name
end
return with_geo_city(info)
end
info.carrier = resolve_carrier(digits, info.carrier)
return info
end
local number_info = resolve_number(callee)
local function result(decision, reason, gateway_id, version, line_group_id, policy_id, customer_id, vendor_id, vendor_gateway_id, host, port)
return {
decision,
reason,
gateway_id,
version,
line_group_id,
policy_id,
customer_id,
vendor_id,
vendor_gateway_id,
host,
port,
number_info.city_code,
number_info.city_name,
number_info.province_name,
number_info.carrier,
number_info.number_type,
number_info.province_code,
number_info.normalized
}
end
local gateway_id = redis.call('GET', prefix .. ':auth:ip:' .. source_ip)
if not gateway_id then
return result('reject', 'AUTH_MISSING', 'none', active, 'none', 'none', 'no_auth_ip', 'none', 'none', 'none', 'none')
end
local gateway_json = redis.call('GET', prefix .. ':customer_gateway:' .. gateway_id)
if not gateway_json then
return result('reject', 'GATEWAY_MISSING', gateway_id, active, 'none', 'none', 'gateway_missing', 'none', 'none', 'none', 'none')
end
if not string.find(gateway_json, '"status":"ENABLED"', 1, true) then
return result('reject', 'GATEWAY_DISABLED', gateway_id, active, 'none', 'none', 'gateway_disabled', 'none', 'none', 'none', 'none')
end
local customer_id = string.match(gateway_json, '"customerId":"([^"]+)"')
if not customer_id then
return result('reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_id_missing', 'none', 'none', 'none', 'none')
end
local customer_json = redis.call('GET', prefix .. ':customer:' .. customer_id)
if not customer_json then
return result('reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_missing', 'none', 'none', 'none', 'none')
end
if not string.find(customer_json, '"status":"ENABLED"', 1, true) then
return result('reject', 'CUSTOMER_DISABLED', gateway_id, active, 'none', 'none', 'customer_disabled', 'none', 'none', 'none', 'none')
end
local function region_blocked(vendor_gateway_id)
if number_info.city_code ~= 'UNKNOWN' then
local city_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_city_codes', number_info.city_code)
if city_blocked == 1 then
return true
end
end
if number_info.province_code ~= 'UNKNOWN' then
local province_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_province_codes', number_info.province_code)
if province_blocked == 1 then
return true
end
end
return false
end
local function first_vendor_route(line_group_id)
local line_group_json = redis.call('GET', prefix .. ':line_group:' .. line_group_id)
if not line_group_json then
return nil, 'LINE_GROUP_MISSING'
end
if not string.find(line_group_json, '"status":"ENABLED"', 1, true) then
return nil, 'LINE_GROUP_DISABLED'
end
local items = redis.call('LRANGE', prefix .. ':line_group:' .. line_group_id .. ':items', 0, -1)
local skipped_region = false
for _, item_json in ipairs(items) do
if string.find(item_json, '"status":"ENABLED"', 1, true) then
local vendor_gateway_id = string.match(item_json, '"vendorGatewayId":"([^"]+)"')
if vendor_gateway_id then
local vendor_gateway_json = redis.call('GET', prefix .. ':vendor_gateway:' .. vendor_gateway_id)
if vendor_gateway_json and string.find(vendor_gateway_json, '"status":"ENABLED"', 1, true) then
if region_blocked(vendor_gateway_id) then
skipped_region = true
else
local vendor_id = string.match(vendor_gateway_json, '"vendorId":"([^"]+)"') or 'none'
local host = string.match(vendor_gateway_json, '"host":"([^"]+)"') or 'none'
local port = string.match(vendor_gateway_json, '"port":([0-9]+)') or '5060'
return {vendor_id, vendor_gateway_id, host, port}, 'OK'
end
end
end
end
end
if skipped_region then
return nil, 'NO_VENDOR_ROUTE_REGION_BLOCKED'
end
return nil, 'NO_VENDOR_ROUTE'
end
local policies = redis.call('LRANGE', prefix .. ':customer_gateway:' .. gateway_id .. ':policies', 0, -1)
for _, policy_json in ipairs(policies) do
if string.find(policy_json, '"status":"ENABLED"', 1, true) then
local caller_mode = string.match(policy_json, '"callerMode":"([^"]+)"') or 'ANY'
local caller_value = string.match(policy_json, '"callerValue":"([^"]*)"') or ''
local callee_mode = string.match(policy_json, '"calleeMode":"([^"]+)"') or 'ANY'
local callee_value = string.match(policy_json, '"calleeValue":"([^"]*)"') or ''
local caller_ok = caller_mode == 'ANY'
or (caller_mode == 'EQUALS' and caller == caller_value)
or (caller_mode == 'PREFIX' and string.sub(caller, 1, string.len(caller_value)) == caller_value)
local callee_ok = callee_mode == 'ANY'
or (callee_mode == 'EQUALS' and callee == callee_value)
or (callee_mode == 'PREFIX' and string.sub(callee, 1, string.len(callee_value)) == callee_value)
if caller_ok and callee_ok then
local policy_id = string.match(policy_json, '"id":"([^"]+)"') or 'none'
local line_group_id = string.match(policy_json, '"lineGroupId":"([^"]+)"') or 'none'
local vendor_route, route_reason = first_vendor_route(line_group_id)
if vendor_route then
return result('allow', 'OK', gateway_id, active, line_group_id, policy_id, customer_id, vendor_route[1], vendor_route[2], vendor_route[3], vendor_route[4])
end
return result('reject', route_reason, gateway_id, active, line_group_id, policy_id, customer_id, 'none', 'none', 'none', 'none')
end
end
end
return result('reject', 'NO_POLICY', gateway_id, active, 'none', 'none', 'no_policy_match', 'none', 'none', 'none', 'none')