diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
index 1cc68769..89f42efd 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
@@ -325,4 +325,10 @@ for k, v in pairs(nodes_table) do
end
end
+o = s:option(Flag, "hwid", translate("Send HWID"))
+o.default = 0
+o.description = translate("Some subscription providers may require a hardware identifier (HWID) for authentication.") .. "
" ..
+ translate("Enabling this option will send a hashed HWID when updating subscriptions.") .. "
" ..
+ translate("This may reveal limited hardware information. Enable only if you trust your provider.")
+
return m
diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po
index c0617a07..d30d9d94 100644
--- a/luci-app-passwall2/po/fa/passwall2.po
+++ b/luci-app-passwall2/po/fa/passwall2.po
@@ -1758,6 +1758,18 @@ msgstr "گره فرود (Landing Node)"
msgid "Only support a layer of proxy."
msgstr "فقط از یک لایه پروکسی پشتیبانی میکند."
+msgid "Send HWID"
+msgstr "ارسال HWID"
+
+msgid "Some subscription providers may require a hardware identifier (HWID) for authentication."
+msgstr "برخی ارائهدهندگان اشتراک ممکن است برای احراز هویت به شناسه سختافزاری (HWID) نیاز داشته باشند."
+
+msgid "Enabling this option will send a hashed HWID when updating subscriptions."
+msgstr "با فعال کردن این گزینه، هنگام بهروزرسانی اشتراکها یک HWID هششده ارسال خواهد شد."
+
+msgid "This may reveal limited hardware information. Enable only if you trust your provider."
+msgstr "این کار ممکن است بخشی از اطلاعات سختافزاری دستگاه را آشکار کند. فقط در صورتی این گزینه را فعال کنید که به ارائهدهنده خود اعتماد دارید."
+
msgid ""
"Chained proxy works only with Xray or Sing-box nodes.
"
"You can only use manual or imported nodes as chained nodes."
diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po
index f47553ad..7b8e83d1 100644
--- a/luci-app-passwall2/po/ru/passwall2.po
+++ b/luci-app-passwall2/po/ru/passwall2.po
@@ -1759,6 +1759,18 @@ msgstr "Сервер выхода"
msgid "Only support a layer of proxy."
msgstr "Поддерживается только один уровень цепочки."
+msgid "Send HWID"
+msgstr "Отправлять HWID"
+
+msgid "Some subscription providers may require a hardware identifier (HWID) for authentication."
+msgstr "Некоторые поставщики подписок могут требовать аппаратный идентификатор (HWID) для аутентификации."
+
+msgid "Enabling this option will send a hashed HWID when updating subscriptions."
+msgstr "При включении этой опции во время обновления подписок будет отправляться хэшированный HWID."
+
+msgid "This may reveal limited hardware information. Enable only if you trust your provider."
+msgstr "Это может раскрыть ограниченную информацию об оборудовании. Включайте эту опцию только в том случае, если доверяете своему поставщику."
+
msgid ""
"Chained proxy works only with Xray or Sing-box nodes.
"
"You can only use manual or imported nodes as chained nodes."
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index 6b566f38..e2aab053 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -1744,6 +1744,18 @@ msgstr "落地节点"
msgid "Only support a layer of proxy."
msgstr "仅支持一层代理。"
+msgid "Send HWID"
+msgstr "发送 HWID"
+
+msgid "Some subscription providers may require a hardware identifier (HWID) for authentication."
+msgstr "某些订阅服务提供商可能需要硬件标识符(HWID)进行身份验证。"
+
+msgid "Enabling this option will send a hashed HWID when updating subscriptions."
+msgstr "启用此选项后,在更新订阅时将发送经过哈希处理的 HWID。"
+
+msgid "This may reveal limited hardware information. Enable only if you trust your provider."
+msgstr "这可能会泄露有限的硬件信息。仅在信任您的服务提供商时启用。"
+
msgid ""
"Chained proxy works only with Xray or Sing-box nodes.
"
"You can only use manual or imported nodes as chained nodes."
diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po
index 26cecd9c..2c5f7f3e 100644
--- a/luci-app-passwall2/po/zh-tw/passwall2.po
+++ b/luci-app-passwall2/po/zh-tw/passwall2.po
@@ -1750,6 +1750,18 @@ msgstr "落地節點"
msgid "Only support a layer of proxy."
msgstr "仅支持一層代理。"
+msgid "Send HWID"
+msgstr "傳送 HWID"
+
+msgid "Some subscription providers may require a hardware identifier (HWID) for authentication."
+msgstr "部分訂閱服務提供者可能需要硬體識別碼(HWID)進行驗證。"
+
+msgid "Enabling this option will send a hashed HWID when updating subscriptions."
+msgstr "啟用此選項後,更新訂閱時將傳送經過雜湊處理的 HWID。"
+
+msgid "This may reveal limited hardware information. Enable only if you trust your provider."
+msgstr "這可能會洩露有限的硬體資訊。請僅在信任您的服務提供者時啟用。"
+
msgid ""
"Chained proxy works only with Xray or Sing-box nodes.
"
"You can only use manual or imported nodes as chained nodes."
diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
index eb40231f..8352028f 100755
--- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
+++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
@@ -1928,7 +1928,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
return result
end
-local function curl(url, file, ua, mode)
+local function curl(url, file, ua, mode, hwid)
if not url or url == "" then return 22, 404 end
local curl_args = {
"-fskL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "-H 'Accept-Encoding: identity'"
@@ -1937,6 +1937,9 @@ local function curl(url, file, ua, mode)
ua = (ua == "passwall2") and ("passwall2/" .. api.get_version()) or ua
curl_args[#curl_args + 1] = '--user-agent "' .. ua .. '"'
end
+ if hwid == "1" then
+ curl_args[#curl_args + 1] = get_headers()
+ end
local return_code, result
if mode == "direct" then
return_code, result = api.curl_direct(url, file, curl_args)
@@ -1948,6 +1951,57 @@ local function curl(url, file, ua, mode)
return return_code, tonumber(result)
end
+function get_headers()
+ local cache_file = "/tmp/etc/" .. appname .. "_tmp/sub_curl_headers"
+ if fs.access(cache_file) then
+ return luci.sys.exec("cat " .. cache_file)
+ end
+ local headers = {}
+
+ local function readfile(path)
+ local f = io.open(path, "r")
+ if not f then return nil end
+ local c = f:read("*a")
+ f:close()
+ return api.trim(c)
+ end
+
+ headers[#headers + 1] = "x-device-os: OpenWrt"
+
+ local rel = readfile("/etc/openwrt_release")
+ local os_ver = rel and rel:match("DISTRIB_RELEASE='([^']+)'")
+ if os_ver then
+ headers[#headers + 1] = "x-ver-os: " .. os_ver
+ end
+
+ local model = readfile("/tmp/sysinfo/model")
+ if model then
+ headers[#headers + 1] = "x-device-model: " .. model
+ end
+
+ local mac = readfile("/sys/class/net/eth0/address")
+ if mac and model then
+ local raw = mac .. "-" .. model
+ local p = io.popen("printf '%s' '" .. raw:gsub("'", "'\\''") .. "' | sha256sum")
+ if p then
+ local hash = p:read("*l")
+ p:close()
+ hash = hash and hash:match("^%w+")
+ if hash then
+ headers[#headers + 1] = "x-hwid: " .. hash
+ end
+ end
+ end
+
+ local out = {}
+ for i = 1, #headers do
+ out[i] = "-H '" .. headers[i]:gsub("'", "'\\''") .. "'"
+ end
+ local headers_str = table.concat(out, " ")
+ local f = io.open(cache_file, "w"); if f then f:write(headers_str); f:close() end
+ return headers_str
+end
+
local function truncate_nodes(group)
for _, config in pairs(CONFIG) do
if config.currentNodes and #config.currentNodes > 0 then
@@ -2409,7 +2463,7 @@ local execute = function()
log(1, i18n.translatef("Start subscribing: %s", '【' .. remark .. '】' .. url .. ' [' .. result .. ']'))
tmp_file = "/tmp/" .. cfgid
local return_code
- return_code, value.http_code = curl(url, tmp_file, ua, access_mode)
+ return_code, value.http_code = curl(url, tmp_file, ua, access_mode, value.hwid)
if return_code ~= 0 then
fail_list[#fail_list + 1] = value
luci.sys.call("rm -f " .. tmp_file)