feat: adds module to obtain registered domain name using PSL
This commit is contained in:
parent
3f0d12c266
commit
1577679053
1 changed files with 105 additions and 0 deletions
105
src/lua/psl.lua
Normal file
105
src/lua/psl.lua
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
local http = require "resty.http"
|
||||||
|
|
||||||
|
local _M = {}
|
||||||
|
|
||||||
|
local function fetch_psl()
|
||||||
|
local httpc = http.new()
|
||||||
|
local res, err = httpc:request_uri("https://publicsuffix.org/list/public_suffix_list.dat", {
|
||||||
|
method = "GET",
|
||||||
|
ssl_verify = true
|
||||||
|
})
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
return nil, "Failed to fetch PSL: " .. err
|
||||||
|
end
|
||||||
|
|
||||||
|
local rules = {}
|
||||||
|
for line in res.body:gmatch("[^\r\n]+") do
|
||||||
|
line = line:match("^%s*(.-)%s*$")
|
||||||
|
if line ~= "" and not line:match("^//") then
|
||||||
|
table.insert(rules, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return rules
|
||||||
|
end
|
||||||
|
|
||||||
|
local function load_psl()
|
||||||
|
local cache = ngx.shared.jasima_cache
|
||||||
|
local cached = cache:get("psl")
|
||||||
|
if cached then return cached end
|
||||||
|
local rules, err = fetch_psl()
|
||||||
|
if rules then
|
||||||
|
cache:set("psl", rules, 86400 * 7) -- 1 week
|
||||||
|
return rules
|
||||||
|
else
|
||||||
|
ngx.log(ngx.ERR, err or "Unknown error loading PSL")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split(domain)
|
||||||
|
local parts = {}
|
||||||
|
for part in domain:gmatch("[^%.]+") do table.insert(parts, part) end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function domain_matches_rule(domain_parts, rule_parts)
|
||||||
|
for i = 1, #rule_parts do
|
||||||
|
local rule_part = rule_parts[#rule_parts - i + 1]
|
||||||
|
local domain_part = domain_parts[#domain_parts - i + 1]
|
||||||
|
if rule_part == "*" then
|
||||||
|
-- wildcard match
|
||||||
|
elseif not domain_part or rule_part ~= domain_part then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_matching_rule(domain, rules)
|
||||||
|
local domain_parts = split(domain)
|
||||||
|
local match = nil
|
||||||
|
local max_len = 0
|
||||||
|
|
||||||
|
for _, rule in ipairs(rules) do
|
||||||
|
local is_exception = rule:sub(1, 1) == "!"
|
||||||
|
local clean_rule = is_exception and rule:sub(2) or rule
|
||||||
|
local rule_parts = split(clean_rule)
|
||||||
|
|
||||||
|
if domain_matches_rule(domain_parts, rule_parts) then
|
||||||
|
if #rule_parts > max_len then
|
||||||
|
match = { rule = rule, is_exception = is_exception }
|
||||||
|
max_len = #rule_parts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return match
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.get_registered_domain(domain)
|
||||||
|
if not domain or domain == "" then return nil end
|
||||||
|
local rules = load_psl()
|
||||||
|
if not rules then return nil end
|
||||||
|
|
||||||
|
local domain_parts = split(domain:lower())
|
||||||
|
local match = find_matching_rule(domain, rules)
|
||||||
|
if not match then return domain end
|
||||||
|
|
||||||
|
local rule = match.rule
|
||||||
|
local is_exception = match.is_exception
|
||||||
|
local rule_parts = split(rule:gsub("^!", ""))
|
||||||
|
local offset = is_exception and (#rule_parts + 1) or #rule_parts
|
||||||
|
|
||||||
|
local start_idx = #domain_parts - offset
|
||||||
|
if start_idx < 1 then return domain end
|
||||||
|
|
||||||
|
local reg_parts = {}
|
||||||
|
for i = start_idx, #domain_parts do
|
||||||
|
table.insert(reg_parts, domain_parts[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(reg_parts, ".")
|
||||||
|
end
|
||||||
|
|
||||||
|
return _M
|
Loading…
Add table
Add a link
Reference in a new issue