update 2023-04-07 16:21:24

This commit is contained in:
github-actions[bot] 2023-04-07 16:21:24 +08:00
parent 30f387f142
commit 573e989a66
20 changed files with 349 additions and 200 deletions

View File

@ -126,7 +126,7 @@ update_rules() {
yhosts_rules_local=`cat /usr/share/koolproxy/data/rules/yhosts.txt | sed -n '1p' | cut -d ":" -f2`
antiad_rules_local=`cat /usr/share/koolproxy/data/rules/antiad.txt | sed -n '2p' | cut -d "=" -f2`
koolproxy_rules_local=`cat /usr/share/koolproxy/data/rules/koolproxy.txt | sed -n '3p'|awk '{print $3,$4}'`
adgk_rules_local=`cat /usr/share/koolproxy/data/rules/adgk.txt | sed -n '1p'|awk '{print $3}'`
adgk_rules_local=`cat /usr/share/koolproxy/data/rules/adgk.txt | sed -n '2p'|awk '{print $3}'`
echo $(date "+%F %T"): -------------------AdGuard规则 Version $adg_rules_local >>$LOGFILE
echo $(date "+%F %T"): -------------------Steven规则 Version $steven_rules_local >>$LOGFILE
echo $(date "+%F %T"): -------------------Yhosts规则 Version $yhosts_rules_local >>$LOGFILE

View File

@ -126,7 +126,7 @@ update_rules() {
yhosts_rules_local=`cat /usr/share/koolproxy/data/rules/yhosts.txt | sed -n '1p' | cut -d ":" -f2`
antiad_rules_local=`cat /usr/share/koolproxy/data/rules/antiad.txt | sed -n '2p' | cut -d "=" -f2`
koolproxy_rules_local=`cat /usr/share/koolproxy/data/rules/koolproxy.txt | sed -n '3p'|awk '{print $3,$4}'`
adgk_rules_local=`cat /usr/share/koolproxy/data/rules/adgk.txt | sed -n '1p'|awk '{print $3}'`
adgk_rules_local=`cat /usr/share/koolproxy/data/rules/adgk.txt | sed -n '2p'|awk '{print $3}'`
echo $(date "+%F %T"): -------------------AdGuard规则 Version $adg_rules_local >>$LOGFILE
echo $(date "+%F %T"): -------------------Steven规则 Version $steven_rules_local >>$LOGFILE
echo $(date "+%F %T"): -------------------Yhosts规则 Version $yhosts_rules_local >>$LOGFILE

View File

@ -5,11 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=1.10
PKG_RELEASE:=6
PKG_VERSION:=1.11
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_Transparent_Proxy \
CONFIG_PACKAGE_$(PKG_NAME)_Transparent_Proxy_Iptables-nft \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Brook \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat \
@ -52,6 +53,7 @@ menu "Configuration"
config PACKAGE_$(PKG_NAME)_Transparent_Proxy
bool "Transparent Proxy"
select PACKAGE_dnsmasq-full
select PACKAGE_dnsmasq_full_ipset
select PACKAGE_ipset
select PACKAGE_iptables
select PACKAGE_iptables-zz-legacy
@ -61,7 +63,21 @@ config PACKAGE_$(PKG_NAME)_Transparent_Proxy
select PACKAGE_iptables-mod-conntrack-extra
select PACKAGE_kmod-ipt-nat
depends on PACKAGE_$(PKG_NAME)
default y
default y if ! PACKAGE_firewall4
config PACKAGE_$(PKG_NAME)_Transparent_Proxy_Iptables-nft
bool "Transparent Proxy Use Iptables-nft"
select PACKAGE_dnsmasq-full
select PACKAGE_dnsmasq_full_ipset
select PACKAGE_ipset
select PACKAGE_iptables-nft
select PACKAGE_iptables-mod-iprange
select PACKAGE_iptables-mod-socket
select PACKAGE_iptables-mod-tproxy
select PACKAGE_iptables-mod-conntrack-extra
select PACKAGE_kmod-ipt-nat
depends on PACKAGE_$(PKG_NAME)
default y if PACKAGE_firewall4
config PACKAGE_$(PKG_NAME)_INCLUDE_Brook
bool "Include Brook"

View File

@ -129,10 +129,21 @@ for k, e in ipairs(api.get_valid_nodes()) do
end
-- 负载均衡列表
balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"))
local balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"))
for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end
balancing_node:depends("protocol", "_balancing")
local balancingStrategy = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy"))
balancingStrategy:depends("protocol", "_balancing")
balancingStrategy:value("random")
balancingStrategy:value("leastPing")
balancingStrategy.default = "random"
local probeInterval = s:option(Value, "probeInterval", translate("Probe Interval"))
probeInterval:depends("balancingStrategy", "leastPing")
probeInterval.default = "1m"
probeInterval.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
-- 分流
uci:foreach(appname, "shunt_rules", function(e)
if e[".name"] and e.remarks then
@ -192,13 +203,11 @@ domainStrategy.description = "<br /><ul><li>" .. translate("'AsIs': Only use dom
.. "</li><li>" .. translate("'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again.")
.. "</li><li>" .. translate("'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately.")
.. "</li></ul>"
domainStrategy:depends("protocol", "_balancing")
domainStrategy:depends("protocol", "_shunt")
domainMatcher = s:option(ListValue, "domainMatcher", translate("Domain matcher"))
domainMatcher:value("hybrid")
domainMatcher:value("linear")
domainMatcher:depends("protocol", "_balancing")
domainMatcher:depends("protocol", "_shunt")
@ -544,9 +553,9 @@ xray_fingerprint:value("chrome")
xray_fingerprint:value("firefox")
xray_fingerprint:value("safari")
xray_fingerprint:value("ios")
xray_fingerprint:value("android")
--xray_fingerprint:value("android")
xray_fingerprint:value("edge")
xray_fingerprint:value("360")
--xray_fingerprint:value("360")
xray_fingerprint:value("qq")
xray_fingerprint:value("random")
xray_fingerprint:value("randomized")
@ -579,9 +588,9 @@ reality_fingerprint:value("chrome")
reality_fingerprint:value("firefox")
reality_fingerprint:value("safari")
reality_fingerprint:value("ios")
reality_fingerprint:value("android")
--reality_fingerprint:value("android")
reality_fingerprint:value("edge")
reality_fingerprint:value("360")
--reality_fingerprint:value("360")
reality_fingerprint:value("qq")
reality_fingerprint:value("random")
reality_fingerprint:value("randomized")
@ -638,6 +647,11 @@ wireguard_mtu = s:option(Value, "wireguard_mtu", translate("MTU"))
wireguard_mtu.default = "1420"
wireguard_mtu:depends({ type = "Xray", protocol = "wireguard" })
if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then
wireguard_reserved = s:option(Value, "wireguard_reserved", translate("Reserved"))
wireguard_reserved:depends({ type = "Xray", protocol = "wireguard" })
end
wireguard_keepAlive = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
wireguard_keepAlive.default = "0"
wireguard_keepAlive:depends({ type = "Xray", protocol = "wireguard" })
@ -833,6 +847,9 @@ hysteria_hop_interval:depends("type", "Hysteria")
hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
hysteria_disable_mtu_discovery:depends("type", "Hysteria")
hysteria_lazy_start = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start"))
hysteria_lazy_start:depends("type", "Hysteria")
protocol.validate = function(self, value)
if value == "_shunt" or value == "_balancing" then
address.rmempty = true

View File

@ -13,6 +13,17 @@ command_timeout = 300
LEDE_BOARD = nil
DISTRIB_TARGET = nil
LOG_FILE = "/tmp/log/passwall2.log"
function log(...)
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
local f, err = io.open(LOG_FILE, "a")
if f and err == nil then
f:write(result .. "\n")
f:close()
end
end
function exec_call(cmd)
local process = io.popen(cmd .. '; echo -e "\n$?"')
local lines = {}
@ -355,7 +366,7 @@ function get_customed_path(e)
end
function is_finded(e)
return luci.sys.exec('type -t -p "/bin/%s" -p "%s" "%s"' % {e, get_customed_path(e), e}) ~= "" and true or false
return luci.sys.exec('type -t -p "/bin/%s" -p "/usr/bin/%s" -p "%s" "%s"' % {e, e, get_customed_path(e), e}) ~= "" and true or false
end

View File

@ -74,6 +74,7 @@ function gen_config(var)
hop_interval = (node.hysteria_hop_interval) and tonumber(node.hysteria_hop_interval) or nil,
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery) and true or false,
fast_open = (node.fast_open == "1") and true or false,
lazy_start = (node.hysteria_lazy_start) and true or false,
socks5 = (local_socks_address and local_socks_port) and {
listen = local_socks_address .. ":" .. local_socks_port,
timeout = 300,

View File

@ -106,6 +106,14 @@ function gen_outbound(flag, node, tag, proxy_table)
end
end
if node.protocol == "wireguard" and node.wireguard_reserved then
local bytes = {}
node.wireguard_reserved:gsub("[^,]+", function(b)
bytes[#bytes+1] = tonumber(b)
end)
node.wireguard_reserved = #bytes > 0 and bytes or nil
end
result = {
_flag_tag = node_id,
_flag_proxy = proxy,
@ -230,7 +238,8 @@ function gen_outbound(flag, node, tag, proxy_table)
keepAlive = node.wireguard_keepAlive and tonumber(node.wireguard_keepAlive) or nil
}
} or nil,
mtu = (node.protocol == "wireguard" and node.wireguard_mtu) and tonumber(node.wireguard_mtu) or nil
mtu = (node.protocol == "wireguard" and node.wireguard_mtu) and tonumber(node.wireguard_mtu) or nil,
reserved = (node.protocol == "wireguard" and node.wireguard_reserved) and node.wireguard_reserved or nil
}
}
local alpn = {}
@ -522,6 +531,7 @@ function gen_config(var)
local inbounds = {}
local outbounds = {}
local routing = nil
local observatory = nil
if local_socks_port then
local inbound = {
@ -817,10 +827,18 @@ function gen_config(var)
local outbound = gen_outbound(flag, node)
if outbound then table.insert(outbounds, outbound) end
end
if node.balancingStrategy == "leastPing" then
observatory = {
subjectSelector = nodes,
probeInterval = node.probeInterval or "1m"
}
end
routing = {
domainStrategy = node.domainStrategy or "AsIs",
domainMatcher = node.domainMatcher or "hybrid",
balancers = {{tag = "balancer", selector = nodes}},
balancers = {{
tag = "balancer",
selector = nodes,
strategy = {type = node.balancingStrategy or "random"}
}},
rules = {
{type = "field", network = "tcp,udp", balancerTag = "balancer"}
}
@ -1093,6 +1111,8 @@ function gen_config(var)
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 连接观测
observatory = observatory,
-- 路由
routing = routing,
-- 本地策略
@ -1277,7 +1297,7 @@ function gen_dns_config(var)
dns = {
tag = "dns-in1",
hosts = {},
disableCache = (dns_cache and dns_cache == "0") and true or false,
disableCache = (dns_cache == "1") and false or true,
disableFallback = true,
disableFallbackIfMatch = true,
servers = {},

View File

@ -56,26 +56,18 @@ local has_xray = api.is_finded("xray")
return b64decsafe(v);
}
function parseNodeUrl(url) {
var m = url.match(/^(([^:\/?#]+:)?(?:\/\/((?:([^\/?#:]*)([^\/?#:]*)@)?([^\/?#:]*)(?::([^\/?#:]*))?)))?([^?#]*)(\?[^#]*)?(#.*)?$/),
r = {
hash: m[10] || "", // #asd
host: m[3] || "", // localhost:257
hostname: m[6] || "", // localhost
href: m[0] || "", // http://username:password@localhost:257/deploy/?asd=asd#asd
origin: m[1] || "", // http://username:password@localhost:257
pathname: m[8] || (m[1] ? "/" : ""), // /deploy/
port: m[7] || "", // 257
protocol: m[2] || "", // http:
search: m[9] || "", // ?asd=asd
passwd: m[4] || "", // username
removed: m[5] || "" // password
};
if (r.protocol.length === 2) {
r.protocol = "file:///" + r.protocol.toUpperCase();
r.origin = r.protocol + "//" + r.host;
}
r.href = r.origin + r.pathname + r.search + r.hash;
return m && r;
let protocol = url.substring(0, url.indexOf("://")) + ":"
let str = "http" + url.substring(url.indexOf("://"))
const parsedUrl = new URL(str);
var r = {
hash: parsedUrl.hash, // #asd
host: parsedUrl.host, // localhost:257
hostname: parsedUrl.hostname, // localhost
port: parsedUrl.port, // 257
search: parsedUrl.search, // ?asd=asd
passwd: parsedUrl.username || parsedUrl.password // username
};
return r;
}
function buildUrl(btn, urlname, sid) {
@ -130,15 +122,25 @@ local has_xray = api.is_finded("xray")
opt.client = urlname.indexOf("server") === -1;
var v_type = opt.get("type").value;
var v_alias = opt.get("remarks");
var _address = ""
try {
var v_server = opt.get("address");
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
if (ipv6Regex.test(v_server.value)) {
_address = "[" + v_server.value + "]"
} else {
_address = v_server.value
}
} catch(e) {
}
var url = null;
if (v_type === "SS") {
var v_server = opt.get("address");
var v_port = opt.get("port");
var v_method = opt.get("ss_encrypt_method");
var v_password = opt.get("password");
url = b64encsafe(v_method.value + ":" + v_password.value) + "@" +
v_server.value + ":" +
_address + ":" +
v_port.value + "/?";
var params = "";
@ -160,7 +162,6 @@ local has_xray = api.is_finded("xray")
}
url += params;
} else if (v_type === "SSR") {
var v_server = opt.get("address");
var v_port = opt.get("port");
var v_protocol = opt.get("ssr_protocol");
var v_method = opt.get("ssr_encrypt_method");
@ -168,7 +169,7 @@ local has_xray = api.is_finded("xray")
var v_password = opt.get("password");
var v_obfs_param = opt.get("obfs_param");
var v_protocol_param = opt.get("protocol_param");
var ssr_str = v_server.value + ":" +
var ssr_str = _address + ":" +
v_port.value + ":" +
v_protocol.value + ":" +
v_method.value + ":" +
@ -184,6 +185,7 @@ local has_xray = api.is_finded("xray")
info.v = "2";
info.ps = v_alias.value;
info.add = opt.get("address").value;
//info.add = _address;
info.port = opt.get("port").value;
info.id = opt.get("uuid").value;
@ -196,8 +198,10 @@ local has_xray = api.is_finded("xray")
info.path = opt.get("h2_path").value;
} else if (v_transport === "tcp") {
info.type = opt.get("tcp_guise").value;
info.host = opt.get("tcp_guise_http_host").value;
info.path = opt.get("tcp_guise_http_path").value;
if (info.type === "http") {
info.host = opt.get("tcp_guise_http_host").value;
info.path = opt.get("tcp_guise_http_path").value;
}
} else if (v_transport === "mkcp") {
v_transport = "kcp";
info.type = opt.get("mkcp_guise").value;
@ -223,10 +227,9 @@ local has_xray = api.is_finded("xray")
} else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vless") {
v_type = "vless";
var v_password = opt.get("uuid");
var v_server = opt.get("address");
var v_port = opt.get("port");
url = encodeURIComponent(v_password.value) +
"@" + v_server.value +
"@" + _address +
":" + v_port.value + "?";
var params = "";
@ -235,6 +238,7 @@ local has_xray = api.is_finded("xray")
params += opt.query("host", "ws_host");
params += opt.query("path", "ws_path");
} else if (v_transport === "h2") {
v_transport = "http";
params += opt.query("host", "h2_host");
params += opt.query("path", "h2_path");
} else if (v_transport === "tcp") {
@ -252,21 +256,32 @@ local has_xray = api.is_finded("xray")
//不知道是用path还是serviceName这里先这样吧
params += opt.query("path", "grpc_serviceName");
params += opt.query("serviceName", "grpc_serviceName");
params += opt.query("mode", "grpc_mode");
}
params += "&type=" + v_transport;
params += opt.query("encryption", "encryption");
if (opt.get("tls").checked) {
var v_security = "tls";
params += "&security=" + v_security;
if (opt.get("tlsflow").value) {
if (opt.get("xray_fingerprint") && opt.get("xray_fingerprint").value != "") {
let v_fp = opt.get("xray_fingerprint").value;
params += "&fp=" + v_fp;
}
if(opt.get("reality") && opt.get("reality").checked) {
v_security = "reality";
if (opt.get("reality_fingerprint") && opt.get("reality_fingerprint").value != "") {
let v_fp = opt.get("reality_fingerprint").value;
params += "&fp=" + v_fp;
}
params += opt.query("pbk", "reality_publicKey");
params += opt.query("sid", "reality_shortId");
params += opt.query("spx", "reality_spiderX");
}
if (opt.get("tlsflow") && opt.get("tlsflow").value) {
let v_flow = opt.get("tlsflow").value;
params += "&flow=" + v_flow;
}
if (opt.get("xray_fingerprint").value && opt.get("xray_fingerprint").value != "") {
let v_fp = opt.get("xray_fingerprint").value
params += "&fp=" + v_fp;
}
params += "&security=" + v_security;
params += opt.query("sni", "tls_serverName");
}
@ -280,10 +295,9 @@ local has_xray = api.is_finded("xray")
v_type = "trojan";
}
var v_password = opt.get("password");
var v_server = opt.get("address");
var v_port = opt.get("port");
url = encodeURIComponent(v_password.value) +
"@" + v_server.value +
"@" + _address +
":" + v_port.value + "/?";
var params = "";
if (opt.get("tls").checked) {
@ -300,7 +314,6 @@ local has_xray = api.is_finded("xray")
var url = "";
var params = "?";
var v_protocol = opt.get("brook_protocol");
var v_server = opt.get("address");
var v_port = opt.get("port");
var v_password = opt.get("password");
var b_protocol_value = v_protocol.value.split('client').join('server');
@ -319,14 +332,13 @@ local has_xray = api.is_finded("xray")
if (v_path_value.length > 1 && v_path_value.indexOf('/') < 0) {
v_path_value = '/' + v_path_value;
}
params += "&" + url_protocol + "=" + encodeURIComponent(prefix + v_server.value + ":" + v_port.value + v_path_value);
params += "&" + url_protocol + "=" + encodeURIComponent(prefix + _address + ":" + v_port.value + v_path_value);
} else {
params += "&" + url_protocol + "=" + encodeURIComponent(v_server.value + ":" + v_port.value);
params += "&" + url_protocol + "=" + encodeURIComponent(_address + ":" + v_port.value);
}
url += url_protocol;
url += params;
} else if (v_type === "Hysteria") {
var v_server = opt.get("address");
var v_port = opt.get("port");
var params = "";
params += opt.query("protocol", "hysteria_protocol");
@ -338,7 +350,7 @@ local has_xray = api.is_finded("xray")
params += opt.query("alpn", "hysteria_alpn");
params += opt.query("obfsParam", "hysteria_obfs");
var url =
v_server.value + ":" +
_address + ":" +
v_port.value + "?" +
params +
"#" + encodeURI(v_alias.value);
@ -677,6 +689,7 @@ local has_xray = api.is_finded("xray")
if (queryParam.security) {
if (queryParam.security == "tls") {
opt.set('tls', true);
opt.set('reality', false)
opt.set('tlsflow', queryParam.flow || '');
opt.set('tls_serverName', queryParam.sni || '');
opt.set('tls_allowInsecure', true);
@ -688,10 +701,25 @@ local has_xray = api.is_finded("xray")
}
}
}
if (queryParam.security == "reality") {
opt.set('tls', true);
opt.set('reality', true)
opt.set('tlsflow', queryParam.flow || '');
opt.set('tls_serverName', queryParam.sni || '');
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set('reality_fingerprint', queryParam.fp);
}
opt.set('reality_publicKey', queryParam.pbk || '');
opt.set('reality_shortId', queryParam.sid || '');
opt.set('reality_spiderX', queryParam.spx || '');
}
queryParam.type = queryParam.type.toLowerCase();
if (queryParam.type === "kcp" || queryParam.type === "mkcp")
queryParam.type = "mkcp"
if (queryParam.type === "h2" || queryParam.type === "http")
queryParam.type = "h2"
opt.set('transport', queryParam.type);
if (queryParam.type === "tcp") {
opt.set('tcp_guise', queryParam.headerType || "none");
@ -702,7 +730,7 @@ local has_xray = api.is_finded("xray")
} else if (queryParam.type === "ws") {
opt.set('ws_host', queryParam.host || "");
opt.set('ws_path', queryParam.path || "");
} else if (queryParam.type === "h2") {
} else if (queryParam.type === "h2" || queryParam.type === "http") {
opt.set('h2_host', queryParam.host || "");
opt.set('h2_path', queryParam.path || "");
} else if (queryParam.type === "quic") {
@ -713,6 +741,7 @@ local has_xray = api.is_finded("xray")
opt.set('mkcp_guise', queryParam.headerType || "none");
} else if (queryParam.type === "grpc") {
opt.set('grpc_serviceName', (queryParam.serviceName || queryParam.path) || "");
opt.set('grpc_mode', queryParam.mode);
}
if (m.hash) {

View File

@ -274,6 +274,15 @@ msgstr "Xray 负载均衡"
msgid "V2ray_balancing"
msgstr "V2ray 负载均衡"
msgid "Balancing Strategy"
msgstr "负载均衡策略"
msgid "Probe Interval"
msgstr "探测间隔"
msgid "The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively."
msgstr "发起探测的间隔。每经过这个时间,就会对一个服务器进行服务器状态检测。时间格式为数字+单位,比如<code>&quot;10s&quot;</code>, <code>&quot;2h45m&quot;</code>,支持的时间单位有 <code>ns</code><code>us</code><code>ms</code><code>s</code><code>m</code><code>h</code>,分别对应纳秒、微秒、毫秒、秒、分、时。"
msgid "Shunt"
msgstr "分流"
@ -406,6 +415,9 @@ msgstr "QUIC 连接接收窗口"
msgid "Disable MTU detection"
msgstr "禁用 MTU 检测"
msgid "Lazy Start"
msgstr "延迟启动"
msgid "Encrypt Method"
msgstr "加密"

View File

@ -70,6 +70,7 @@ config nodes 'myshunt'
option AD 'nil'
option BT '_direct'
option Netflix 'nil'
option OpenAI 'nil'
option TVB 'nil'
option Proxy '_default'
option China '_direct'
@ -173,6 +174,10 @@ config shunt_rules 'Netflix'
option remarks 'Netflix'
option network 'tcp,udp'
option domain_list 'geosite:netflix'
config shunt_rules 'OpenAI'
option remarks 'OpenAI'
option domain_list 'geosite:openai'
config shunt_rules 'TVB'
option remarks 'TVB'

View File

@ -299,12 +299,12 @@ run_v2ray() {
}
local buffer_size=$(config_t_get global_forwarding buffer_size)
[ -n "${buffer_size}" ] && _extra_param="${_extra_param} -buffer_size ${buffer_size}"
local protocol=$(config_n_get $node protocol)
[ "$protocol" == "_iface" ] && {
IFACES="$IFACES $(config_n_get $node iface)"
}
[ -n "$dns_listen_port" ] && {
V2RAY_DNS_DIRECT_CONFIG="${TMP_PATH}/${flag}_dns_direct.json"
V2RAY_DNS_DIRECT_LOG="${TMP_PATH}/${flag}_dns_direct.log"
@ -345,10 +345,10 @@ run_v2ray() {
esac
[ -n "$direct_dns_query_strategy" ] && V2RAY_DNS_DIRECT_ARGS="${V2RAY_DNS_DIRECT_ARGS} -dns_query_strategy ${direct_dns_query_strategy}"
[ -n "$direct_dns_client_ip" ] && V2RAY_DNS_DIRECT_ARGS="${V2RAY_DNS_DIRECT_ARGS} -dns_client_ip ${direct_dns_client_ip}"
lua $UTIL_XRAY gen_dns_config ${V2RAY_DNS_DIRECT_ARGS} > $V2RAY_DNS_DIRECT_CONFIG
ln_run "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $V2RAY_DNS_DIRECT_LOG run -c "$V2RAY_DNS_DIRECT_CONFIG"
[ "$remote_dns_protocol" != "fakedns" ] && {
V2RAY_DNS_REMOTE_CONFIG="${TMP_PATH}/${flag}_dns_remote.json"
V2RAY_DNS_REMOTE_LOG="${TMP_PATH}/${flag}_dns_remote.log"
@ -386,15 +386,15 @@ run_v2ray() {
V2RAY_DNS_REMOTE_ARGS="${V2RAY_DNS_REMOTE_ARGS} -remote_dns_fake 1"
;;
esac
[ -n "$remote_dns_query_strategy" ] && V2RAY_DNS_REMOTE_ARGS="${V2RAY_DNS_REMOTE_ARGS} -dns_query_strategy ${remote_dns_query_strategy}"
[ -n "$remote_dns_client_ip" ] && V2RAY_DNS_REMOTE_ARGS="${V2RAY_DNS_REMOTE_ARGS} -dns_client_ip ${remote_dns_client_ip}"
V2RAY_DNS_REMOTE_ARGS="${V2RAY_DNS_REMOTE_ARGS} -remote_dns_outbound_socks_address 127.0.0.1 -remote_dns_outbound_socks_port ${socks_port}"
lua $UTIL_XRAY gen_dns_config ${V2RAY_DNS_REMOTE_ARGS} > $V2RAY_DNS_REMOTE_CONFIG
ln_run "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $V2RAY_DNS_REMOTE_LOG run -c "$V2RAY_DNS_REMOTE_CONFIG"
}
[ -n "$dns_listen_port" ] && _extra_param="${_extra_param} -dns_listen_port ${dns_listen_port}"
[ -n "$dns_cache" ] && _extra_param="${_extra_param} -dns_cache ${dns_cache}"
_extra_param="${_extra_param} -dns_query_strategy UseIP"
@ -405,7 +405,7 @@ run_v2ray() {
_extra_param="${_extra_param} -remote_dns_port ${dns_remote_listen_port} -remote_dns_udp_server 127.0.0.1"
fi
}
lua $UTIL_XRAY gen_config -node $node -redir_port $redir_port -tcp_proxy_way $tcp_proxy_way -loglevel $loglevel ${_extra_param} > $config_file
ln_run "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file run -c "$config_file"
}
@ -441,7 +441,7 @@ run_socks() {
else
error_msg="某种原因,此 Socks 服务的相关配置已失联,启动中止!"
fi
if [ "$type" == "v2ray" ] || [ "$type" == "xray" ]; then
local protocol=$(config_n_get $node protocol)
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ]; then
@ -558,7 +558,7 @@ node_switch() {
new_script_func=$(echo $script_func | sed "s#${now_node_arg}#node=${new_node}#g")
${new_script_func}
echo $new_node > $TMP_ID_PATH/${flag}
[ "$shunt_logic" != "0" ] && [ "$(config_n_get $new_node protocol nil)" = "_shunt" ] && {
echo $(config_n_get $new_node default_node nil) > $TMP_ID_PATH/${flag}_default
echo $(config_n_get $new_node main_node nil) > $TMP_ID_PATH/${flag}_main
@ -642,7 +642,7 @@ run_global() {
}
msg="${msg}"
echolog ${msg}
source $APP_PATH/helper_dnsmasq.sh stretch
source $APP_PATH/helper_dnsmasq.sh add TMP_DNSMASQ_PATH=$TMP_DNSMASQ_PATH DNSMASQ_CONF_FILE=/tmp/dnsmasq.d/dnsmasq-passwall2.conf DEFAULT_DNS=$AUTO_DNS LOCAL_DNS=$LOCAL_DNS TUN_DNS=$TUN_DNS
@ -801,7 +801,7 @@ acl_app() {
sid=$(uci -q show "${CONFIG}.${item}" | grep "=acl_rule" | awk -F '=' '{print $1}' | awk -F '.' '{print $2}')
eval $(uci -q show "${CONFIG}.${item}" | cut -d'.' -sf 3-)
[ "$enabled" = "1" ] || continue
[ -z "${sources}" ] && continue
for s in $sources; do
is_iprange=$(lua_api "iprange(\"${s}\")")
@ -821,7 +821,7 @@ acl_app() {
[ -z "${rule_list}" ] && continue
mkdir -p $TMP_ACL_PATH/$sid
echo -e "${rule_list}" | sed '/^$/d' > $TMP_ACL_PATH/$sid/rule_list
tcp_proxy_mode="global"
udp_proxy_mode="global"
node=${node:-default}
@ -833,7 +833,7 @@ acl_app() {
remote_dns=${remote_dns:-1.1.1.1}
[ "$remote_dns_protocol" = "doh" ] && remote_dns=${remote_dns_doh:-https://1.1.1.1/dns-query}
remote_dns_query_strategy=${remote_dns_query_strategy:-UseIPv4}
[ "$node" != "nil" ] && {
if [ "$node" = "default" ]; then
node=$NODE
@ -893,18 +893,18 @@ acl_app() {
start() {
pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
echolog "程序已启动,无需重复启动!"
return 0
echolog "程序已启动,先停止再重新启动!"
stop
}
ulimit -n 65535
start_socks
local USE_TABLES="iptables"
if [ -z "$(command -v iptables-legacy || command -v iptables)" ] || [ -z "$(command -v ipset)" ]; then
echolog "系统未安装iptables或ipset无法透明代理"
fi
[ "$ENABLED_DEFAULT_ACL" == 1 ] && run_global
source $APP_PATH/${USE_TABLES}.sh start
[ "$ENABLED_DEFAULT_ACL" == 1 ] && source $APP_PATH/helper_dnsmasq.sh logic_restart

View File

@ -21,14 +21,10 @@ local geosite_api = ucic:get_first(name, 'global_rules', "geosite_url", "https:/
local log = function(...)
if arg1 then
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
if arg1 == "log" then
local f, err = io.open("/tmp/log/passwall2.log", "a")
if f and err == nil then
f:write(result .. "\n")
f:close()
end
api.log(...)
elseif arg1 == "print" then
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
print(result)
end
end

View File

@ -80,15 +80,11 @@ local nodeResult = {} -- update result
local debug = false
local log = function(...)
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
if debug == true then
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
print(result)
else
local f, err = io.open("/tmp/log/" .. appname .. ".log", "a")
if f and err == nil then
f:write(result .. "\n")
f:close()
end
api.log(...)
end
end
@ -328,15 +324,17 @@ local function processData(szType, content, add_mode, add_from)
add_mode = add_mode, --0为手动配置,1为导入,2为订阅
add_from = add_from
}
--ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
if szType == 'ssr' then
result.type = "SSR"
local dat = split(content, "/%?")
local hostInfo = split(dat[1], ':')
result.type = "SSR"
result.address = ""
for i=1,#hostInfo-5,1 do
result.address = result.address .. hostInfo[i] .. ":"
if dat[1]:match('%[(.*)%]') then
result.address = dat[1]:match('%[(.*)%]')
else
result.address = hostInfo[#hostInfo-5]
end
result.address = string.sub(result.address, 0, #result.address-1)
result.port = hostInfo[#hostInfo-4]
result.protocol = hostInfo[#hostInfo-3]
result.method = hostInfo[#hostInfo-2]
@ -410,6 +408,15 @@ local function processData(szType, content, add_mode, add_from)
result.tls = "0"
end
elseif szType == "ss" then
result.type = "SS"
--SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ]
--userinfo = websafe-base64-encode-utf8(method ":" password)
--ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1
--ss://cmM0LW1kNTpwYXNzd2Q@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp#Example2
--ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3
--ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3
local idx_sp = 0
local alias = ""
if content:find("#") then
@ -418,28 +425,9 @@ local function processData(szType, content, add_mode, add_from)
end
result.remarks = UrlDecode(alias)
local info = content:sub(1, idx_sp - 1)
local hostInfo = split(base64Decode(info), "@")
local hostInfoLen = #hostInfo
local host = nil
local userinfo = nil
if hostInfoLen > 2 then
host = split(hostInfo[hostInfoLen], ":")
userinfo = {}
for i = 1, hostInfoLen - 1 do
tinsert(userinfo, hostInfo[i])
end
userinfo = table.concat(userinfo, '@')
else
host = split(hostInfo[2], ":")
userinfo = base64Decode(hostInfo[1])
end
local method = userinfo:sub(1, userinfo:find(":") - 1)
local password = userinfo:sub(userinfo:find(":") + 1, #userinfo)
result.type = "SS"
result.address = host[1]
if host[2] and host[2]:find("/%?") then
local query = split(host[2], "/%?")
result.port = query[1]
if info:find("/%?") then
local find_index = info:find("/%?")
local query = split(info, "/%?")
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
@ -459,39 +447,70 @@ local function processData(szType, content, add_mode, add_from)
if result.plugin and result.plugin == "simple-obfs" then
result.plugin = "obfs-local"
end
else
result.port = host[2]
info = info:sub(1, find_index - 1)
end
result.method = method
result.password = password
local aead = false
for k, v in ipairs({"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305"}) do
if method:lower() == v:lower() then
aead = true
local hostInfo = split(base64Decode(info), "@")
if hostInfo and #hostInfo > 0 then
local host_port = hostInfo[#hostInfo]
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
if host_port:find(":") then
local sp = split(host_port, ":")
result.port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.address = sp[1]
end
else
result.address = host_port
end
end
if aead then
if ss_aead_type_default == "shadowsocks-libev" and has_ss then
result.type = "SS"
elseif ss_aead_type_default == "shadowsocks-rust" and has_ss_rust then
result.type = 'SS-Rust'
if method:lower() == "chacha20-poly1305" then
result.method = "chacha20-ietf-poly1305"
local userinfo = nil
if #hostInfo > 2 then
userinfo = {}
for i = 1, #hostInfo - 1 do
tinsert(userinfo, hostInfo[i])
end
elseif ss_aead_type_default == "v2ray" and has_v2ray and not result.plugin then
result.type = 'V2ray'
result.protocol = 'shadowsocks'
result.transport = 'tcp'
if method:lower() == "chacha20-ietf-poly1305" then
result.method = "chacha20-poly1305"
userinfo = table.concat(userinfo, '@')
else
userinfo = base64Decode(hostInfo[1])
end
local method = userinfo:sub(1, userinfo:find(":") - 1)
local password = userinfo:sub(userinfo:find(":") + 1, #userinfo)
result.method = method
result.password = password
local aead = false
for k, v in ipairs({"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305"}) do
if method:lower() == v:lower() then
aead = true
end
elseif ss_aead_type_default == "xray" and has_xray and not result.plugin then
result.type = 'Xray'
result.protocol = 'shadowsocks'
result.transport = 'tcp'
if method:lower() == "chacha20-ietf-poly1305" then
result.method = "chacha20-poly1305"
end
if aead then
if ss_aead_type_default == "shadowsocks-libev" and has_ss then
result.type = "SS"
elseif ss_aead_type_default == "shadowsocks-rust" and has_ss_rust then
result.type = 'SS-Rust'
if method:lower() == "chacha20-poly1305" then
result.method = "chacha20-ietf-poly1305"
end
elseif ss_aead_type_default == "v2ray" and has_v2ray and not result.plugin then
result.type = 'V2ray'
result.protocol = 'shadowsocks'
result.transport = 'tcp'
if method:lower() == "chacha20-ietf-poly1305" then
result.method = "chacha20-poly1305"
end
elseif ss_aead_type_default == "xray" and has_xray and not result.plugin then
result.type = 'Xray'
result.protocol = 'shadowsocks'
result.transport = 'tcp'
if method:lower() == "chacha20-ietf-poly1305" then
result.method = "chacha20-poly1305"
end
end
end
end
@ -513,33 +532,28 @@ local function processData(szType, content, add_mode, add_from)
result.password = UrlDecode(Info[1])
local port = "443"
Info[2] = (Info[2] or ""):gsub("/%?", "?")
local hostInfo = nil
if Info[2]:find(":") then
hostInfo = split(Info[2], ":")
result.address = hostInfo[1]
local idx_port = 2
if hostInfo[2]:find("?") then
hostInfo = split(hostInfo[2], "?")
idx_port = 1
end
if hostInfo[idx_port] ~= "" then port = hostInfo[idx_port] end
else
if Info[2]:find("?") then
hostInfo = split(Info[2], "?")
end
result.address = hostInfo and hostInfo[1] or Info[2]
end
local peer, sni = nil, ""
local allowInsecure = allowInsecure_default
local query = split(Info[2], "?")
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
params[string.lower(t[1])] = UrlDecode(t[2])
end
if params.allowinsecure then
allowInsecure = params.allowinsecure
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
if host_port:find(":") then
local sp = split(host_port, ":")
port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.address = sp[1]
end
else
result.address = host_port
end
local peer, sni = nil, ""
if params.peer then peer = params.peer end
sni = params.sni and params.sni or ""
if params.ws and params.ws == "1" then
@ -551,7 +565,16 @@ local function processData(szType, content, add_mode, add_from)
result.port = port
result.tls = '1'
result.tls_serverName = peer and peer or sni
result.tls_allowInsecure = allowInsecure and "1" or "0"
if params.allowinsecure then
if params.allowinsecure == "1" or params.allowinsecure == "0" then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
end
--log(result.remarks .. ' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
end
elseif szType == "ssd" then
result.type = "SS"
@ -581,36 +604,34 @@ local function processData(szType, content, add_mode, add_from)
result.uuid = UrlDecode(Info[1])
local port = "443"
Info[2] = (Info[2] or ""):gsub("/%?", "?")
local hostInfo = nil
if Info[2]:find(":") then
hostInfo = split(Info[2], ":")
result.address = hostInfo[1]
local idx_port = 2
if hostInfo[2]:find("?") then
hostInfo = split(hostInfo[2], "?")
idx_port = 1
end
if hostInfo[idx_port] ~= "" then port = hostInfo[idx_port] end
else
if Info[2]:find("?") then
hostInfo = split(Info[2], "?")
end
result.address = hostInfo and hostInfo[1] or Info[2]
end
local query = split(Info[2], "?")
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
params[t[1]] = UrlDecode(t[2])
end
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
if host_port:find(":") then
local sp = split(host_port, ":")
port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.address = sp[1]
end
else
result.address = host_port
end
params.type = string.lower(params.type)
if params.type == 'ws' then
result.ws_host = params.host
result.ws_path = params.path
end
if params.type == 'h2' then
if params.type == 'h2' or params.type == 'http' then
params.type = "h2"
result.h2_host = params.host
result.h2_path = params.path
end
@ -637,17 +658,24 @@ local function processData(szType, content, add_mode, add_from)
if params.type == 'grpc' then
if params.path then result.grpc_serviceName = params.path end
if params.serviceName then result.grpc_serviceName = params.serviceName end
result.grpc_mode = params.mode
end
result.transport = params.type
result.encryption = params.encryption or "none"
result.tls = "0"
if params.security == "tls" then
if params.security == "tls" or params.security == "reality" then
result.tls = "1"
result.tlsflow = params.flow or nil
result.tls_serverName = (params.sni and params.sni ~= "") and params.sni or params.host
result.fingerprint = (params.fp and params.fp ~= "") and params.fp or "chrome"
if params.security == "reality" then
result.reality = "1"
result.reality_publicKey = params.pbk or nil
result.reality_shortId = params.sid or nil
result.reality_spiderX = params.spx or nil
end
end
result.port = port
@ -664,9 +692,7 @@ local function processData(szType, content, add_mode, add_from)
result.type = "Hysteria"
local dat = split(content, '%?')
local hostInfo = split(dat[1], ':')
result.address = hostInfo[1]
result.port = hostInfo[2]
local host_port = dat[1]
local params = {}
for _, v in pairs(split(dat[2], '&')) do
local t = split(v, '=')
@ -674,6 +700,19 @@ local function processData(szType, content, add_mode, add_from)
params[t[1]] = t[2]
end
end
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
if host_port:find(":") then
local sp = split(host_port, ":")
result.port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.address = sp[1]
end
else
result.address = host_port
end
result.protocol = params.protocol
result.hysteria_obfs = params.obfsParam
result.hysteria_auth_type = "string"
@ -990,8 +1029,8 @@ local function parse_link(raw, add_mode, add_from)
if result then
if not result.type then
log('丢弃节点:' .. result.remarks .. ",找不到可使用二进制.")
elseif (add_mode == "2" and is_filter_keyword(result.remarks)) or not result.address or result.remarks == "NULL" or result.address=="127.0.0.1" or
(not datatypes.hostname(result.address) and not (datatypes.ipmask4(result.address) or datatypes.ipmask6(result.address))) then
elseif (add_mode == "2" and is_filter_keyword(result.remarks)) or not result.address or result.remarks == "NULL" or result.address == "127.0.0.1" or
(not datatypes.hostname(result.address) and not (api.is_ip(result.address))) then
log('丢弃过滤节点: ' .. result.type .. ' 节点, ' .. result.remarks)
else
tinsert(node_list, result)

View File

@ -9,10 +9,13 @@ LUCI_TITLE:=LuCI support for quickstart
LUCI_DEPENDS:=+quickstart +luci-app-store
LUCI_PKGARCH:=all
PKG_VERSION:=0.6.9-4
PKG_VERSION:=0.7.2-1
# PKG_RELEASE MUST be empty for luci.mk
PKG_RELEASE:=
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_quickstart:=$(ARCH)
PKG_NAME:=quickstart
PKG_VERSION:=0.6.11
PKG_RELEASE:=2
PKG_VERSION:=0.7.3
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://fw.koolcenter.com/binary/quickstart/
PKG_HASH:=f6cbc4a8dab3c5e659b45ffda617df94ea81cf5e9e07dbe88183036167b9bfb5
PKG_HASH:=65c14deccf7afb8179a347e31a6f1e5d20333e7b01f9003837abae15c3955ada
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)