luci-app-vssr: sync with upstream source

This commit is contained in:
CN_SZTL 2020-02-19 01:19:01 +08:00
parent 8dc511f8b4
commit 0fa220f6fb
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
14 changed files with 913 additions and 855 deletions

View File

@ -90,7 +90,7 @@ function get_subscribe()
end
luci.sys.call('uci commit vssr')
luci.sys.call(
"nohup /usr/share/vssr/subscribe.sh >/www/check_update.htm 2>/dev/null &")
"nohup /usr/bin/lua /usr/share/vssr/subscribe.lua >/www/check_update.htm 2>/dev/null &")
e.error = 0
else
@ -293,88 +293,83 @@ end
-- 刷新检测文件
function refresh_data()
local set = luci.http.formvalue("set")
local icount = 0
local set =luci.http.formvalue("set")
local icount =0
if set == "gfw_data" then
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd =
"wget-ssl --no-check-certificate https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt -O /tmp/gfw.b64"
else
refresh_cmd = "wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64"
end
sret = luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret == 0 then
luci.sys.call("/usr/bin/vssr-gfw")
icount = luci.sys.exec("cat /tmp/gfwnew.txt | wc -l")
if tonumber(icount) > 1000 then
oldcount = luci.sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf")
retstring = tostring(math.ceil(tonumber(icount) / 2))
else
retstring = "0"
end
else
retstring = "-1"
end
luci.sys.exec("rm -f /tmp/gfwnew.txt ")
else
retstring = "-1"
end
elseif set == "ip_data" then
refresh_cmd =
'wget -O- \'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest\' 2>/dev/null| awk -F\\| \'/CN\\|ipv4/ { printf("%s/%d\\n", $4, 32-log($5)/log(2)) }\' > /tmp/china_ssr.txt'
sret = luci.sys.call(refresh_cmd)
icount = luci.sys.exec("cat /tmp/china_ssr.txt | wc -l")
if sret == 0 and tonumber(icount) > 1000 then
oldcount = luci.sys.exec("cat /etc/china_ssr.txt | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/china_ssr.txt /etc/china_ssr.txt")
retstring = tostring(tonumber(icount))
else
retstring = "0"
end
else
retstring = "-1"
end
luci.sys.exec("rm -f /tmp/china_ssr.txt ")
else
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd =
"wget --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt | grep ^\\|\\|[^\\*]*\\^$ | sed -e 's:||:address\\=\\/:' -e 's:\\^:/127\\.0\\.0\\.1:' > /tmp/ad.conf"
else
refresh_cmd = "wget -O /tmp/ad.conf http://iytc.net/tools/ad.conf"
end
sret = luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret == 0 then
icount = luci.sys.exec("cat /tmp/ad.conf | wc -l")
if tonumber(icount) > 1000 then
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
oldcount = luci.sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l")
else
oldcount = 0
end
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/ad.conf /etc/dnsmasq.ssr/ad.conf")
retstring = tostring(math.ceil(tonumber(icount)))
if oldcount == 0 then
luci.sys.call("/etc/init.d/dnsmasq restart")
end
else
retstring = "0"
end
else
retstring = "-1"
end
luci.sys.exec("rm -f /tmp/ad.conf ")
else
retstring = "-1"
end
end
luci.http.prepare_content("application/json")
luci.http.write_json({ret = retstring, retcount = icount})
if set == "gfw_data" then
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd="wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64"
else
refresh_cmd="wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64"
end
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret== 0 then
luci.sys.call("/usr/bin/vssr-gfw")
icount = luci.sys.exec("cat /tmp/gfwnew.txt | wc -l")
if tonumber(icount)>1000 then
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf")
retstring=tostring(math.ceil(tonumber(icount)/2))
else
retstring ="0"
end
else
retstring ="-1"
end
luci.sys.exec("rm -f /tmp/gfwnew.txt ")
else
retstring ="-1"
end
elseif set == "ip_data" then
refresh_cmd="wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' 2>/dev/null| awk -F\\| '/CN\\|ipv4/ { printf(\"%s/%d\\n\", $4, 32-log($5)/log(2)) }' > /tmp/china_ssr.txt"
sret=luci.sys.call(refresh_cmd)
icount = luci.sys.exec("cat /tmp/china_ssr.txt | wc -l")
if sret== 0 and tonumber(icount)>1000 then
oldcount=luci.sys.exec("cat /etc/china_ssr.txt | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/china_ssr.txt /etc/china_ssr.txt")
retstring=tostring(tonumber(icount))
else
retstring ="0"
end
else
retstring ="-1"
end
luci.sys.exec("rm -f /tmp/china_ssr.txt ")
else
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd="wget-ssl --no-check-certificate -O - ".. luci.model.uci.cursor():get_first('vssr', 'global', 'adblock_url','https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt') .." > /tmp/adnew.conf"
end
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret== 0 then
luci.sys.call("/usr/bin/vssr-ad")
icount = luci.sys.exec("cat /tmp/ad.conf | wc -l")
if tonumber(icount)>1000 then
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l")
else
oldcount=0
end
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/ad.conf /etc/dnsmasq.ssr/ad.conf")
retstring=tostring(math.ceil(tonumber(icount)))
if oldcount==0 then
luci.sys.call("/etc/init.d/dnsmasq restart")
end
else
retstring ="0"
end
else
retstring ="-1"
end
luci.sys.exec("rm -f /tmp/ad.conf")
else
retstring ="-1"
end
end
luci.http.prepare_content("application/json")
luci.http.write_json({ ret=retstring ,retcount=icount})
end

View File

@ -74,6 +74,9 @@ s.anonymous = true
o = s:option(Flag, "adblock", translate("Enable adblock"))
o.rmempty = false
o = s:option(Value, "adblock_url", translate("adblock_url"))
o.default = "https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt"
-- [[ haProxy ]]--
s = m:section(TypedSection, "global_haproxy", translate("haProxy settings"))

View File

@ -1,270 +1,282 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
}
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64encutf8safe(str) {
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
function b64decutf8safe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
}
function b64encsafe(str) {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
}
function b64decsafe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return atob(str);
}
function dictvalue(d, key) {
var v = d[key];
if (typeof (v) == 'undefined' || v == '')
return '';
return b64decsafe(v);
}
function export_ssr_url(btn, urlname, sid) {
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var v_server = document.getElementById('cbid.vssr.' + sid + '.server');
var v_port = document.getElementById('cbid.vssr.' + sid + '.server_port');
var v_protocol = document.getElementById('cbid.vssr.' + sid + '.protocol');
var v_method = document.getElementById('cbid.vssr.' + sid + '.encrypt_method');
var v_obfs = document.getElementById('cbid.vssr.' + sid + '.obfs');
var v_password = document.getElementById('cbid.vssr.' + sid + '.password');
var v_obfs_param = document.getElementById('cbid.vssr.' + sid + '.obfs_param');
var v_protocol_param = document.getElementById('cbid.vssr.' + sid + '.protocol_param');
var v_alias = document.getElementById('cbid.vssr.' + sid + '.alias');
var ssr_str = v_server.value + ":" +
v_port.value + ":" +
v_protocol.value + ":" +
v_method.value + ":" +
v_obfs.value + ":" +
b64encsafe(v_password.value) +
"/?obfsparam=" + b64encsafe(v_obfs_param.value) +
"&protoparam=" + b64encsafe(v_protocol_param.value) +
"&remarks=" + b64encutf8safe(v_alias.value);
var textarea = document.createElement("textarea");
textarea.textContent = "ssr://" + b64encsafe(ssr_str);
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy"); // Security exception may be thrown by some browsers.
s.innerHTML = "<font color='green'><%:Copy SSR to clipboard successfully.%></font>";
} catch (ex) {
s.innerHTML = "<font color='red'><%:Unable to copy SSR to clipboard.%></font>";
} finally {
document.body.removeChild(textarea);
}
return false;
}
function getFlag(hosts, sid) {
$.get('<%=luci.dispatcher.build_url("admin", "vpn", "vssr","flag")%>',
{
host: hosts
},
function (data, status) {
document.getElementById('cbid.vssr.' + sid + '.flag').value = data.flag.replace(/[\r\n]/g, "");;
});
}
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
}
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64encutf8safe(str) {
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
function b64decutf8safe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
}
function b64encsafe(str) {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
}
function b64decsafe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return atob(str);
}
function dictvalue(d, key) {
var v = d[key];
if (typeof (v) == 'undefined' || v == '')
return '';
return b64decsafe(v);
}
function export_ssr_url(btn, urlname, sid) {
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var v_server = document.getElementById('cbid.vssr.' + sid + '.server');
var v_port = document.getElementById('cbid.vssr.' + sid + '.server_port');
var v_protocol = document.getElementById('cbid.vssr.' + sid + '.protocol');
var v_method = document.getElementById('cbid.vssr.' + sid + '.encrypt_method');
var v_obfs = document.getElementById('cbid.vssr.' + sid + '.obfs');
var v_password = document.getElementById('cbid.vssr.' + sid + '.password');
var v_obfs_param = document.getElementById('cbid.vssr.' + sid + '.obfs_param');
var v_protocol_param = document.getElementById('cbid.vssr.' + sid + '.protocol_param');
var v_alias = document.getElementById('cbid.vssr.' + sid + '.alias');
function import_ssr_url(btn, urlname, sid) {
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var ssrurl = prompt("在这里黏贴配置链接 ssr:// | ss:// | vmess:// | trojan://", "");
if (ssrurl == null || ssrurl == "") {
s.innerHTML = "<font color='red'>用户取消</font>";
return false;
}
s.innerHTML = "";
//var ssu = ssrurl.match(/ssr:\/\/([A-Za-z0-9_-]+)/i);
var ssu = ssrurl.split('://');
console.log(ssu.length);
if ((ssu[0] != "ssr" && ssu[0] != "ss" && ssu[0] != "vmess" && ssu[0] != "trojan") || ssu[1] == "") {
s.innerHTML = "<font color='red'>无效格式</font>";
return false;
}
var ssr_str = v_server.value + ":" +
v_port.value + ":" +
v_protocol.value + ":" +
v_method.value + ":" +
v_obfs.value + ":" +
b64encsafe(v_password.value) +
"/?obfsparam=" + b64encsafe(v_obfs_param.value) +
"&protoparam=" + b64encsafe(v_protocol_param.value) +
"&remarks=" + b64encutf8safe(v_alias.value);
var textarea = document.createElement("textarea");
textarea.textContent = "ssr://" + b64encsafe(ssr_str);
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy"); // Security exception may be thrown by some browsers.
s.innerHTML = "<font color='green'><%:Copy SSR to clipboard successfully.%></font>";
} catch (ex) {
s.innerHTML = "<font color='red'><%:Unable to copy SSR to clipboard.%></font>";
} finally {
document.body.removeChild(textarea);
}
return false;
}
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (ssu[0] == "ssr") {
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
document.getElementById('cbid.vssr.' + sid + '.type').value = "ssr";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
if (!ssm || ssm.length < 7)
return false;
var pdict = {};
if (param.length > 2)
{
var a = param.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
}
document.getElementById('cbid.vssr.' + sid + '.server').value = ssm[1];
document.getElementById('cbid.vssr.' + sid + '.server_port').value = ssm[2];
document.getElementById('cbid.vssr.' + sid + '.protocol').value = ssm[3];
document.getElementById('cbid.vssr.' + sid + '.encrypt_method').value = ssm[4];
document.getElementById('cbid.vssr.' + sid + '.obfs').value = ssm[5];
document.getElementById('cbid.vssr.' + sid + '.password').value = b64decsafe(ssm[6]);
document.getElementById('cbid.vssr.' + sid + '.obfs_param').value = dictvalue(pdict, 'obfsparam');
document.getElementById('cbid.vssr.' + sid + '.protocol_param').value = dictvalue(pdict, 'protoparam');
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0)
document.getElementById('cbid.vssr.' + sid + '.alias').value = b64decutf8safe(rem);
s.innerHTML = "<font color='green'>导入ShadowsocksR配置信息成功</font>";
return false;
} else if (ssu[0] == "ss") {
var url0, param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1];
}
function import_ssr_url(btn, urlname, sid) {
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var ssrurl = prompt("在这里黏贴配置链接 ssr:// | ss:// | vmess:// | trojan://", "");
if (ssrurl == null || ssrurl == "") {
s.innerHTML = "<font color='red'><%:User Cancel%></font>";
return false;
}
s.innerHTML = "";
//var ssu = ssrurl.match(/ssr:\/\/([A-Za-z0-9_-]+)/i);
var ssu = ssrurl.split('://');
console.log(ssu.length);
if ((ssu[0] != "ssr" && ssu[0] != "ss" && ssu[0] != "vmess") || ssu[1] == "") {
s.innerHTML = "<font color='red'><%:Invalid Format%></font>";
return false;
}
if (sipIndex != -1) {
// SIP002
var userInfo = b64decsafe(url0.substr(0, sipIndex));
var temp = url0.substr(sipIndex + 1).split("/?");
var serverInfo = temp[0].split(":");
var server = serverInfo[0];
var port = serverInfo[1];
var method, password, plugin, pluginOpts;
if (temp[1]) {
var pluginInfo = decodeURIComponent(temp[1]);
var pluginIndex = pluginInfo.indexOf(";");
var pluginNameInfo = pluginInfo.substr(0, pluginIndex);
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
pluginOpts = pluginInfo.substr(pluginIndex + 1);
}
var userInfoSplitIndex = userInfo.indexOf(":");
if (userInfoSplitIndex != -1) {
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
}
document.getElementById('cbid.vssr.' + sid + '.type').value = "ss";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
document.getElementById('cbid.vssr.' + sid + '.server').value = server;
document.getElementById('cbid.vssr.' + sid + '.server_port').value = port;
document.getElementById('cbid.vssr.' + sid + '.password').value = password || "";
document.getElementById('cbid.vssr.' + sid + '.encrypt_method_ss').value = method || "";
document.getElementById('cbid.vssr.' + sid + '.plugin').value = plugin || "";
document.getElementById('cbid.vssr.' + sid + '.plugin_opts').value = pluginOpts || "";
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (ssu[0] == "ssr") {
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
document.getElementById('cbid.vssr.' + sid + '.type').value = "ssr";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
if (!ssm || ssm.length < 7)
return false;
var pdict = {};
if (param.length > 2) {
var a = param.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
}
document.getElementById('cbid.vssr.' + sid + '.server').value = ssm[1];
document.getElementById('cbid.vssr.' + sid + '.server_port').value = ssm[2];
document.getElementById('cbid.vssr.' + sid + '.protocol').value = ssm[3];
document.getElementById('cbid.vssr.' + sid + '.encrypt_method').value = ssm[4];
document.getElementById('cbid.vssr.' + sid + '.obfs').value = ssm[5];
document.getElementById('cbid.vssr.' + sid + '.password').value = b64decsafe(ssm[6]);
document.getElementById('cbid.vssr.' + sid + '.obfs_param').value = dictvalue(pdict, 'obfsparam');
document.getElementById('cbid.vssr.' + sid + '.protocol_param').value = dictvalue(pdict, 'protoparam');
getFlag(ssm[1], sid);
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0)
document.getElementById('cbid.vssr.' + sid + '.alias').value = b64decutf8safe(rem);
s.innerHTML = "<font color='green'><%:Import%>ShadowsocksR<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "ss") {
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1]
}
var sstr = b64decsafe(url0);
document.getElementById('cbid.vssr.' + sid + '.type').value = "ss";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
document.getElementById('cbid.vssr.' + sid + '.server').value = part2[0];
document.getElementById('cbid.vssr.' + sid + '.server_port').value = part2[1];
document.getElementById('cbid.vssr.' + sid + '.password').value = part1[1];
document.getElementById('cbid.vssr.' + sid + '.encrypt_method_ss').value = part1[0];
getFlag(part2[0], sid);
if (param != undefined) {
document.getElementById('cbid.vssr.' + sid + '.alias').value = decodeURI(param);
}
s.innerHTML = "<font color='green'><%:Import%>Shadowsocks<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "trojan") {
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1]
}
var sstr = b64decsafe(url0);
document.getElementById('cbid.shadowsocksr.' + sid + '.type').value = "trojan";
document.getElementById('cbid.shadowsocksr.' + sid + '.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
document.getElementById('cbid.shadowsocksr.' + sid + '.server').value = part2[0];
document.getElementById('cbid.shadowsocksr.' + sid + '.server_port').value = part2[1];
document.getElementById('cbid.shadowsocksr.' + sid + '.password').value = part1[1];
if (param != undefined) {
document.getElementById('cbid.shadowsocksr.' + sid + '.alias').value = decodeURI(param);
}
s.innerHTML = "<font color='green'>导入Trojan配置信息成功</font>";
return false;
} else if (ssu[0] == "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
document.getElementById('cbid.vssr.' + sid + '.type').value = "v2ray";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
document.getElementById('cbid.vssr.' + sid + '.alias').value = ssm.ps;
document.getElementById('cbid.vssr.' + sid + '.server').value = ssm.add;
document.getElementById('cbid.vssr.' + sid + '.server_port').value = ssm.port;
document.getElementById('cbid.vssr.' + sid + '.alter_id').value = ssm.aid;
document.getElementById('cbid.vssr.' + sid + '.vmess_id').value = ssm.id;
document.getElementById('cbid.vssr.' + sid + '.transport').value = ssm.net;
if (ssm.tls == "tls") {
document.getElementById('cbid.vssr.' + sid + '.tls').checked = true;
document.getElementById('cbid.vssr.' + sid + '.insecure').checked = true;
}
document.getElementById('cbid.vssr.' + sid + '.transport').dispatchEvent(event);
switch (ssm.net) {
case "tcp":
break;
case "kcp":
break;
case "ws":
document.getElementById('cbid.vssr.' + sid + '.ws_host').value = ssm.host;
document.getElementById('cbid.vssr.' + sid + '.ws_path').value = ssm.path;
break;
case "h2":
break;
case "quic":
break;
}
getFlag(ssm.add, sid);
s.innerHTML = "<font color='green'><%:Import%>V2ray<%:Configuration Succeeded%></font>";
return false;
}
}
if (param != undefined) {
document.getElementById('cbid.vssr.' + sid + '.alias').value = decodeURI(param);
}
s.innerHTML = "<font color='green'>导入Shadowsocks配置信息成功</font>";
} else {
var sstr = b64decsafe(url0);
document.getElementById('cbid.vssr.' + sid + '.type').value = "ss";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
document.getElementById('cbid.vssr.' + sid + '.server').value = part2[0];
document.getElementById('cbid.vssr.' + sid + '.server_port').value = part2[1];
document.getElementById('cbid.vssr.' + sid + '.password').value = part1[1];
document.getElementById('cbid.vssr.' + sid + '.encrypt_method_ss').value = part1[0];
document.getElementById('cbid.vssr.' + sid + '.plugin').value = "";
document.getElementById('cbid.vssr.' + sid + '.plugin_opts').value = "";
if (param != undefined) {
document.getElementById('cbid.vssr.' + sid + '.alias').value = decodeURI(param);
}
s.innerHTML = "<font color='green'>导入Shadowsocks配置信息成功</font>";
}
return false;
} else if (ssu[0] == "trojan") {
var url0, param = "";
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1]
}
var sstr = b64decsafe(url0);
document.getElementById('cbid.vssr.' + sid + '.type').value = "trojan";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
document.getElementById('cbid.vssr.' + sid + '.server').value = part2[0];
document.getElementById('cbid.vssr.' + sid + '.server_port').value = part2[1];
document.getElementById('cbid.vssr.' + sid + '.password').value = part1[1];
if (param != undefined) {
document.getElementById('cbid.vssr.' + sid + '.alias').value = decodeURI(param);
}
s.innerHTML = "<font color='green'>导入Trojan配置信息成功</font>";
return false;
} else if (ssu[0] == "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
document.getElementById('cbid.vssr.' + sid + '.type').value = "v2ray";
document.getElementById('cbid.vssr.' + sid + '.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
document.getElementById('cbid.vssr.' + sid + '.alias').value = ssm.ps;
document.getElementById('cbid.vssr.' + sid + '.server').value = ssm.add;
document.getElementById('cbid.vssr.' + sid + '.server_port').value = ssm.port;
document.getElementById('cbid.vssr.' + sid + '.alter_id').value = ssm.aid;
document.getElementById('cbid.vssr.' + sid + '.vmess_id').value = ssm.id;
document.getElementById('cbid.vssr.' + sid + '.transport').value = ssm.net;
document.getElementById('cbid.vssr.' + sid + '.transport').dispatchEvent(event);
if (ssm.net == "tcp") {
document.getElementById('cbid.vssr.' + sid + '.http_host').value = ssm.host;
document.getElementById('cbid.vssr.' + sid + '.http_path').value = ssm.path;
}
if (ssm.net == "ws") {
document.getElementById('cbid.vssr.' + sid + '.ws_host').value = ssm.host;
document.getElementById('cbid.vssr.' + sid + '.ws_path').value = ssm.path;
}
if (ssm.net == "h2") {
document.getElementById('cbid.vssr.' + sid + '.h2_host').value = ssm.host;
document.getElementById('cbid.vssr.' + sid + '.h2_path').value = ssm.path;
}
if (ssm.net == "quic") {
document.getElementById('cbid.vssr.' + sid + '.quic_security').value = ssm.securty;
document.getElementById('cbid.vssr.' + sid + '.quic_key').value = ssm.key;
}
if (ssm.net == "kcp") {
document.getElementById('cbid.vssr.' + sid + '.kcp_guise').value = ssm.type;
}
if (ssm.tls == "tls") {
document.getElementById('cbid.vssr.' + sid + '.tls').checked = true;
document.getElementById('cbid.vssr.' + sid + '.tls').dispatchEvent(event);
document.getElementById('cbid.vssr.' + sid + '.tls_host').value = ssm.host;
}
document.getElementById('cbid.vssr.' + sid + '.mux').checked = true;
document.getElementById('cbid.vssr.' + sid + '.mux').dispatchEvent(event);
s.innerHTML = "<font color='green'>导入V2ray配置信息成功</font>";
return false;
}
}
//]]></script>
<input type="button" class="cbi-button cbi-button-apply" value="<%:Import Configuration%>"
onclick="return import_ssr_url(this, '<%=self.option%>', '<%=self.value%>')" />
<input type="button" class="cbi-button cbi-button-apply" value="导入配置信息" onclick="return import_ssr_url(this, '<%=self.option%>', '<%=self.value%>')" />
<input type="button" class="cbi-button cbi-button-apply" value="<%:Export SSR%>" onclick="return export_ssr_url(this,'<%=self.option%>','<%=self.value%>')" /><span id="<%=self.option%>-status"></span>
<span id="<%=self.option%>-status"></span>
<%+cbi/valuefooter%>

View File

@ -1,124 +1,108 @@
<%+cbi/valueheader%>
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=72883">
<label class="cbi-value-title" ><%= translate("Update") %></label>
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=72883">
<label class="cbi-value-title"><%= translate("Update") %></label>
<div class="cbi-value-field">
<button class="cbi-button cbi-button-reload" id="update_subscribe" size="0" ><%= translate("Save And Start Subscribe") %></button>
<input class="cbi-button cbi-button-reload" id="update_subscribe" type="button"
size="0" value="<%= translate("Save And Start Subscribe") %>">
</div>
<script type="text/javascript">
//<![CDATA[
var _responseLen;
var noChange = 0;
var x = 5;
var modal = '<div class="modals-bg">' +
'<div class="modals">' +
'<h2><%:Subscription%></h2>'+
'<h3 style="margin-left:0;"><%:Subscribing,Please do not refresh!%></h3>'+
'<textarea cols="63" rows="28" wrap="on" readonly="readonly" id="log_content3" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>'+
'</div>'+
'</div>';
function update_subscribe() {
$("body").append(modal);
$(".modals-bg").show();
setTimeout("get_realtime_log();", 500);
}
function submit_url(){
const SAVE_SUBSCRIBE_URL = '<%=luci.dispatcher.build_url("admin", "vpn", "vssr","subscribe")%>';
const SERVERS_URL = '<%=luci.dispatcher.build_url("admin", "vpn", "vssr","servers")%>';
var _responseLen;
var noChange = 0;
var modal = '<div class="modals-bg">' +
'<div class="modals">' +
'<h2><%:Subscription%></h2>' +
'<h3 style="margin-left:0;"><%:Subscribing,Please do not refresh!%></h3>' +
'<textarea cols="63" rows="28" wrap="on" readonly="readonly" id="log_content3" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>' +
'</div>' +
'</div>';
//显示并开始刷新订阅
function update_subscribe() {
$("body").append(modal);
$(".modals-bg").show();
setTimeout("get_realtime_log();", 500);
}
//保存订阅按钮
$("#update_subscribe").click(function () {
prefix_array = $("#cbi-vssr-server_subscribe .cbi-section-node").attr("id").split("-");
prefix_array[0] = "cbid";
prefix = prefix_array.join(".");
if($("[name='"+prefix+".auto_update']").is(":checked")){
if ($("[name='" + prefix + ".auto_update']").is(":checked")) {
var auto_update = "1";
}else{
} else {
var auto_update = "0";
}
var auto_update_time = $("[name='"+prefix+".auto_update_time']").val();
var auto_update_time = $("[name='" + prefix + ".auto_update_time']").val();
var subscribe_url = [];
$("[name='"+prefix+".subscribe_url']").each(function(){
if($(this).val() != ""){
$("[name='" + prefix + ".subscribe_url']").each(function () {
if ($(this).val() != "") {
subscribe_url.push($(this).val());
}
});
if($("[name='"+prefix+".proxy']").is(":checked")){
if ($("[name='" + prefix + ".proxy']").is(":checked")) {
var proxy = "1";
}else{
} else {
var proxy = "0";
}
var data = {
auto_update:auto_update,
auto_update_time : auto_update_time,
auto_update: auto_update,
auto_update_time: auto_update_time,
subscribe_url: JSON.stringify(subscribe_url),
proxy: proxy
}
//console.log(data);
$.ajax({
type: "post",
url: "<%=luci.dispatcher.build_url('admin', 'vpn', 'vssr','subscribe')%>",
dataType : "json",
data: data,
success: function (d) {
if(d.error == 0){
//console.log("开始订阅");
update_subscribe();
}else{
alert("请至少填写一个订阅链接");
}
type: "post",
url: SAVE_SUBSCRIBE_URL,
dataType: "json",
data: data,
success: function (d) {
if (d.error == 0) {
update_subscribe();
} else {
alert("请至少填写一个订阅链接");
}
});
}
$("#update_subscribe").click(function(){
//console.log("提交数据");
submit_url();
}
});
return false;
});
function refresh_page(){
location.reload();
return false;
function get_realtime_log() {
$.ajax({
url: '/check_update.htm?v=' + parseInt(Math.random() * 100000000),
dataType: 'html',
error: function (xhr) {
setTimeout("get_realtime_log();", 1000);
},
success: function (response) {
var retArea = document.getElementById("log_content3");
if (response.search("END SUBSCRIBE") != -1) {
noChange++;
}
console.log(noChange);
if (noChange > 10) {
window.location.href = SERVERS_URL;
return false;
} else {
setTimeout("get_realtime_log();", 250);
}
retArea.value = response;
retArea.scrollTop = retArea.scrollHeight;
_responseLen = response.length;
},
error: function () {
setTimeout("get_realtime_log();", 500);
}
});
}
function get_realtime_log() {
$.ajax({
url: '/check_update.htm?v='+parseInt(Math.random() * 100000000),
dataType: 'html',
error: function (xhr) {
setTimeout("get_realtime_log();", 1000);
},
success: function (response) {
var retArea = document.getElementById("log_content3");
if (response.search("END SUBSCRIBE") != -1) {
noChange++;
}
console.log(noChange);
if (noChange > 10) {
window.location.href="<%=luci.dispatcher.build_url('admin', 'vpn', 'vssr','servers')%>"
return false;
} else {
setTimeout("get_realtime_log();", 250);
}
retArea.value = response;
retArea.scrollTop = retArea.scrollHeight;
_responseLen = response.length;
},
error: function () {
setTimeout("get_realtime_log();", 500);
}
});
}
//]]>
</script>
<%+cbi/valuefooter%>

View File

@ -39,6 +39,9 @@ msgstr "广告屏蔽设置"
msgid "Enable adblock"
msgstr "开启广告屏蔽"
msgid "adblock_url"
msgstr "广告屏蔽订阅"
msgid "Socks user"
msgstr "用户名"

View File

@ -12,6 +12,7 @@ config global
option switch_time '667'
option switch_try_count '3'
option adblock '0'
option adblock_url 'https://gitee.com/privacy-protection-tools/anti-ad/raw/master/easylist.txt'
config socks5_proxy
option server 'nil'

View File

@ -54,9 +54,8 @@ uci_get_by_type() {
add_cron() {
sed -i '/vssr.log/d' $CRON_FILE
echo '0 1 * * 0 echo "" > /tmp/vssr.log' >>$CRON_FILE
[ -n "$(grep -w "/usr/share/vssr/subscribe.sh" $CRON_FILE)" ] && sed -i '/\/usr\/share\/vssr\/subscribe.sh/d' $CRON_FILE
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/share/vssr/subscribe.sh" >>$CRON_FILE
[ -z "$(grep -w "/usr/share/vssr/update.sh" $CRON_FILE)" ] && echo "0 5 * * 0 /usr/share/vssr/update.sh" >>$CRON_FILE
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/bin/lua /usr/share/vssr/subscribe.lua" >> $CRON_FILE
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/bin/lua /usr/share/vssr/update.lua" >> $CRON_FILE
crontab $CRON_FILE
}

View File

@ -1,61 +0,0 @@
local ucursor = require"luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3]
local host = arg[4]
local server = ucursor:get_all("vssr", server_section)
local v2ray = {
log = {
-- error = "/var/ssrplus.log",
loglevel = "info"
},
-- 传入连接
inbound = {
port = local_port,
protocol = "dokodemo-door",
settings = {network = proto, followRedirect = true},
sniffing = {enabled = true, destOverride = {"http", "tls"}}
},
-- 传出连接
outbounds = {
{
tag = "protocol_layer",
protocol = "shadowsocks",
settings = {
servers = {
{
address = host,
port = tonumber(server.server_port),
method = server.encrypt_method_ss,
password = server.password
}
}
},
proxySettings = {tag = "transport_layer"}
}, {
tag = "transport_layer",
protocol = "freedom",
settings = (server.obfs_host ~= nil) and{
redirect = server.obfs_host .. ":" ..
tonumber(server.server_port)
} or nil,
streamSettings = (server.obfs_transport ~= nil) and{
network = server.obfs_transport,
security = (server.obfs_opts == '1') and "tls" or "none",
wsSettings = {
path = server.obfs_path,
headers = (server.obfs_host ~= nil) and {host = server.obfs_host} or nil
}
} or nil,
mux = {enabled = (server.mux == "1") and true or false}
}
}
}
print(json.stringify(v2ray, 1))

View File

@ -1,25 +0,0 @@
local ucursor = require "luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3]
local host = arg[4]
local server = ucursor:get_all("vssr", server_section)
local ssr = {
server = host,
server_port = server.server_port,
local_address = "0.0.0.0",
local_port = local_port,
password = server.password,
timeout = (server.timeout ~= nil) and server.timeout or 60,
method = server.encrypt_method,
protocol = server.protocol,
protocol_param = server.protocol_param,
obfs = server.obfs,
obfs_param = server.obfs_param,
reuse_port = true,
fast_open = (server.fast_open == "1") and true or false,
}
print(json.stringify(ssr, 1))

View File

@ -1,40 +0,0 @@
local ucursor = require "luci.model.uci".cursor()
local json = require "luci.jsonc"
local proto = arg[1]
local local_port = arg[2]
local Socks_user = arg[3]
local Socks_pass = arg[4]
local v2ray = {
log = {
--error = "/var/log/v2ray.log",
loglevel = "warning"
},
-- 传入连接
inbound = {
port = local_port,
protocol = proto,
settings = {
auth = "password",
accounts = {
{
user = Socks_user,
pass = Socks_pass
}
}
}
},
-- 传出连接
outbound = {
protocol = "freedom"
},
-- 额外传出连接
outboundDetour = {
{
protocol = "blackhole",
tag = "blocked"
}
}
}
print(json.stringify(v2ray,1))

View File

@ -0,0 +1,383 @@
#!/usr/bin/lua
------------------------------------------------
-- This file is part of the luci-app-vssr subscribe.lua
-- @author William Chan <root@williamchan.me>
------------------------------------------------
require 'nixio'
require 'luci.util'
require 'luci.jsonc'
require 'luci.sys'
-- these global functions are accessed all the time by the event handler
-- so caching them is worth the effort
local luci = luci
local tinsert = table.insert
local ssub, slen, schar, sbyte, sformat, sgsub = string.sub, string.len, string.char, string.byte, string.format, string.gsub
local jsonParse, jsonStringify = luci.jsonc.parse, luci.jsonc.stringify
local b64decode = nixio.bin.b64decode
local cache = {}
local nodeResult = setmetatable({}, { __index = cache }) -- update result
local name = 'vssr'
local uciType = 'servers'
local ucic = luci.model.uci.cursor()
local proxy = ucic:get_first(name, 'server_subscribe', 'proxy', '0')
local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
local log = function(...)
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({ ... }, " "))
end
-- 分割字符串
local function split(full, sep)
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
local off, result = 1, {}
while true do
local nStart, nEnd = full:find(sep, off)
if not nEnd then
local res = ssub(full, off, slen(full))
if #res > 0 then -- 过滤掉 \0
tinsert(result, res)
end
break
else
tinsert(result, ssub(full, off, nStart - 1))
off = nEnd + 1
end
end
return result
end
-- urlencode
local function get_urlencode(c)
return sformat("%%%02X", sbyte(c))
end
local function urlEncode(szText)
local str = szText:gsub("([^0-9a-zA-Z ])", get_urlencode)
str = str:gsub(" ", "+")
return str
end
local function get_urldecode(h)
return schar(tonumber(h, 16))
end
local function UrlDecode(szText)
return szText:gsub("+", " "):gsub("%%(%x%x)", get_urldecode)
end
-- trim
local function trim(text)
if not text or text == "" then
return ""
end
return (sgsub(text, "^%s*(.-)%s*$", "%1"))
end
-- md5
local function md5(content)
local stdout = luci.sys.exec('echo \"' .. urlEncode(content) .. '\" | md5sum | cut -d \" \" -f1')
-- assert(nixio.errno() == 0)
return trim(stdout)
end
-- base64
local function base64Decode(text)
local raw = text
if not text then return '' end
text = text:gsub("%z", "")
text = text:gsub("_", "/")
text = text:gsub("-", "+")
local mod4 = #text % 4
text = text .. string.sub('====', mod4 + 1)
local result = b64decode(text)
if result then
return result:gsub("%z", "")
else
return raw
end
end
-- 处理数据
local function processData(szType, content)
local result = {
-- auth_enable = '0',
switch_enable = '1',
type = szType,
local_port = 1234,
-- timeout = 60, -- 不太确定 好像是死的
-- fast_open = 0,
-- kcp_enable = 0,
-- kcp_port = 0,
kcp_param = '--nocomp'
}
if szType == 'ssr' then
local dat = split(content, "/%?")
local hostInfo = split(dat[1], ':')
result.server = hostInfo[1]
result.server_port = hostInfo[2]
result.protocol = hostInfo[3]
result.encrypt_method = hostInfo[4]
result.obfs = hostInfo[5]
result.password = base64Decode(hostInfo[6])
local params = {}
for _, v in pairs(split(dat[2], '&')) do
local t = split(v, '=')
params[t[1]] = t[2]
end
result.obfs_param = base64Decode(params.obfsparam)
result.protocol_param = base64Decode(params.protoparam)
local group = base64Decode(params.group)
if group then
result.alias = "[" .. group .. "] "
end
result.alias = result.alias .. base64Decode(params.remarks)
elseif szType == 'vmess' then
local info = jsonParse(content)
result.type = 'v2ray'
result.server = info.add
result.server_port = info.port
result.transport = info.net
result.alter_id = info.aid
result.vmess_id = info.id
result.alias = info.ps
-- result.mux = 1
-- result.concurrency = 8
if info.net == 'ws' then
result.ws_host = info.host
result.ws_path = info.path
end
if info.net == 'h2' then
result.h2_host = info.host
result.h2_path = info.path
end
if info.net == 'tcp' then
result.tcp_guise = info.type
result.http_host = info.host
result.http_path = info.path
end
if info.net == 'kcp' then
result.kcp_guise = info.type
result.mtu = 1350
result.tti = 50
result.uplink_capacity = 5
result.downlink_capacity = 20
result.read_buffer_size = 2
result.write_buffer_size = 2
end
if info.net == 'quic' then
result.quic_guise = info.type
result.quic_key = info.key
result.quic_security = info.securty
end
if info.security then
result.security = info.security
end
if info.tls == "tls" or info.tls == "1" then
result.tls = "1"
result.tls_host = info.host
else
result.tls = "0"
end
elseif szType == "ss" then
local idx_sp = 0
local alias = ""
if content:find("#") then
idx_sp = content:find("#")
alias = content:sub(idx_sp + 1, -1)
end
local info = content:sub(1, idx_sp - 1)
local hostInfo = split(base64Decode(info), "@")
local host = split(hostInfo[2], ":")
local userinfo = base64Decode(hostInfo[1])
local method = userinfo:sub(1, userinfo:find(":") - 1)
local password = userinfo:sub(userinfo:find(":") + 1, #userinfo)
result.alias = UrlDecode(alias)
result.type = "ss"
result.server = host[1]
if host[2]:find("/%?") then
local query = split(host[2], "/%?")
result.server_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
params[t[1]] = t[2]
end
if params.plugin then
local plugin_info = UrlDecode(params.plugin)
local idx_pn = plugin_info:find(";")
if idx_pn then
result.plugin = plugin_info:sub(1, idx_pn - 1)
result.plugin_opts = plugin_info:sub(idx_pn + 1, #plugin_info)
else
result.plugin = plugin_info
end
end
else
result.server_port = host[2]
end
result.encrypt_method_ss = method
result.password = password
elseif szType == "ssd" then
result.type = "ss"
result.server = content.server
result.server_port = content.port
result.password = content.password
result.encrypt_method_ss = content.encryption
result.plugin = content.plugin
result.plugin_opts = content.plugin_options
result.alias = "[" .. content.airport .. "] " .. content.remarks
end
if not result.alias then
result.alias = result.server .. ':' .. result.server_port
end
-- alias 不参与 hashkey 计算
local alias = result.alias
result.alias = nil
result.hashkey = md5(jsonStringify(result))
result.alias = alias
return result
end
-- wget
local function wget(url)
local stdout = luci.sys.exec('wget-ssl --user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" --no-check-certificate -t 3 -T 10 -O- "' .. url .. '"')
return trim(stdout)
end
local execute = function()
-- exec
do
if proxy == '0' then -- 不使用代理更新的话先暂停
log('服务正在暂停')
luci.sys.init.stop(name)
end
for k, url in ipairs(subscribe_url) do
local raw = wget(url)
if #raw > 0 then
local nodes, szType
local groupHash = md5(url)
cache[groupHash] = {}
tinsert(nodeResult, {})
local index = #nodeResult
-- SSD 似乎是这种格式 ssd:// 开头的
if raw:find('ssd://') then
szType = 'ssd'
local nEnd = select(2, raw:find('ssd://'))
nodes = base64Decode(raw:sub(nEnd + 1, #raw))
nodes = jsonParse(nodes)
local extra = {
airport = nodes.airport,
port = nodes.port,
encryption = nodes.encryption,
password = nodes.password
}
local servers = {}
-- SS里面包着 干脆直接这样
for _, server in ipairs(nodes.servers) do
tinsert(servers, setmetatable(server, { __index = extra }))
end
nodes = servers
else
-- ssd 外的格式
nodes = split(base64Decode(raw):gsub(" ", "\n"), "\n")
end
for _, v in ipairs(nodes) do
if v then
local result
if szType == 'ssd' then
result = processData(szType, v)
elseif not szType then
local node = trim(v)
local dat = split(node, "://")
if dat and dat[1] and dat[2] then
if dat[1] == 'ss' then
result = processData(dat[1], dat[2])
else
result = processData(dat[1], base64Decode(dat[2]))
end
end
else
log('跳过未知类型: ' .. szType)
end
-- log(result)
if result then
if result.alias:find("过期时间") or
result.alias:find("剩余流量") or
result.alias:find("QQ群") or
result.alias:find("官网") or
not result.server
then
log('丢弃无效节点: ' .. result.type ..' 节点, ' .. result.alias)
else
log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
result.grouphashkey = groupHash
tinsert(nodeResult[index], result)
cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
end
end
end
end
log('成功解析节点数量: ' ..#nodes)
end
end
end
-- diff
do
assert(next(nodeResult), "node result is empty")
local add, del = 0, 0
ucic:foreach(name, uciType, function(old)
if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除
if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then
ucic:delete(name, old['.name'])
del = del + 1
else
local dat = nodeResult[old.grouphashkey][old.hashkey]
ucic:tset(name, old['.name'], dat)
-- 标记一下
setmetatable(nodeResult[old.grouphashkey][old.hashkey], { __index = { _ignore = true } })
end
else
log('忽略手动添加的节点: ' .. old.alias)
end
end)
for k, v in ipairs(nodeResult) do
for kk, vv in ipairs(v) do
if not vv._ignore then
local section = ucic:add(name, uciType)
ucic:tset(name, section, vv)
add = add + 1
end
end
end
ucic:commit(name)
-- 如果服务器已经不见了把帮换一个
local globalServer = ucic:get_first(name, 'global', 'global_server', '')
local firstServer = ucic:get_first(name, uciType)
if not ucic:get(name, globalServer) then
if firstServer then
ucic:set(name, ucic:get_first(name, 'global'), 'global_server', firstServer)
ucic:commit(name)
log('当前主服务器已更新,正在自动更换。')
end
end
if firstServer then
luci.sys.call("/etc/init.d/" .. name .. " restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
else
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
end
log('新增节点数量: ' ..add, '删除节点数量: ' .. del)
log("END SUBSCRIBE")
log('更新成功服务正在启动')
end
end
if subscribe_url and #subscribe_url > 0 then
xpcall(execute, function(e)
log(e)
log(debug.traceback())
log("END SUBSCRIBE")
log('发生错误, 正在恢复服务')
local firstServer = ucic:get_first(name, uciType)
if firstServer then
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
else
luci.sys.call("/etc/init.d/" .. name .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
end
end)
end

View File

@ -1,269 +0,0 @@
#!/bin/bash
# Copyright (C) 2017 XiaoShan https://www.mivm.cn
. /usr/share/libubox/jshn.sh
urlsafe_b64decode() {
local d="====" data=$(echo $1 | sed 's/_/\//g; s/-/+/g')
local mod4=$((${#data} % 4))
[ $mod4 -gt 0 ] && data=${data}${d:mod4}
echo $data | base64 -d
}
urldecode() {
: "${*//+/ }"
echo -e "${_//%/\\x}"
}
echo_date() {
echo $(TZ=UTC-8 date -R +%Y-%m-%d\ %X):$1
}
Server_Update() {
local uci_set="uci -q set $name.$1."
local flag=$(/usr/share/$name/getflag.sh "$ssr_remarks" $ssr_host)
${uci_set}grouphashkey="$ssr_grouphashkey"
${uci_set}hashkey="$ssr_hashkey"
${uci_set}alias="[$ssr_group] $ssr_remarks"
${uci_set}auth_enable="0"
${uci_set}switch_enable="1"
${uci_set}type="$ssr_type"
${uci_set}flag="$flag"
${uci_set}server="$ssr_host"
${uci_set}server_port="$ssr_port"
${uci_set}local_port="1234"
uci -q get $name.@servers[$1].timeout >/dev/null || ${uci_set}timeout="60"
${uci_set}password="$ssr_passwd"
${uci_set}encrypt_method="$ssr_method"
${uci_set}protocol="$ssr_protocol"
${uci_set}protocol_param="$ssr_protoparam"
${uci_set}obfs="$ssr_obfs"
${uci_set}obfs_param="$ssr_obfsparam"
${uci_set}fast_open="0"
${uci_set}kcp_enable="0"
${uci_set}kcp_port="0"
${uci_set}kcp_param="--nocomp"
if [ "$ssr_type" = "v2ray" ]; then
#v2ray
${uci_set}alter_id="$ssr_alter_id"
${uci_set}vmess_id="$ssr_vmess_id"
${uci_set}transport="$ssr_transport"
if [ "$ssr_transport" = "tcp" ]; then
${uci_set}tcp_guise="$ssr_tcp_guise"
fi
if [ "$ssr_transport" = "ws" ]; then
${uci_set}ws_host="$ssr_ws_host"
${uci_set}ws_path="$ssr_ws_path"
fi
if [ "$ssr_transport" = "h2" ]; then
${uci_set}h2_host="$ssr_ws_host"
${uci_set}h2_path="$ssr_ws_path"
fi
${uci_set}tls="$ssr_tls"
${uci_set}insecure="$ssr_insecure"
${uci_set}security="auto"
${uci_set}alias="$ssr_remarks"
fi
if [ "$ssr_type" = "ss" ]; then
${uci_set}encrypt_method_ss="$ss_method"
${uci_set}alias="$ssr_remarks"
fi
}
name=vssr
subscribe_url=($(uci get $name.@server_subscribe[0].subscribe_url)) #订阅服务器地址
[ ${#subscribe_url[@]} -eq 0 ] && exit 1
[ $(uci -q get $name.@server_subscribe[0].proxy || echo 0) -eq 0 ] && /etc/init.d/$name stop >/dev/null 2>&1
log_name=${name}_subscribe
for ((o = 0; o < ${#subscribe_url[@]}; o++)); do
echo_date "${subscribe_url[o]} 获取订阅"
echo_date "开始更新在线订阅列表..."
echo_date "开始下载订阅链接到本地临时文件,请稍等..."
subscribe_data=$(wget-ssl --user-agent="User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" --no-check-certificate -t 10 -T 10 -O- ${subscribe_url[o]})
curl_code=$?
# 计算group的hashkey
ssr_grouphashkey=$(echo "${subscribe_url[o]}" | md5sum | cut -d ' ' -f1)
if [ ! $curl_code -eq 0 ]; then
subscribe_data=$(wget-ssl --no-check-certificate -t 10 -T 10 -O- ${subscribe_url[o]})
curl_code=$?
fi
if [ $curl_code -eq 0 ]; then
echo_date "下载订阅成功..."
echo_date "开始解析节点信息..."
ssr_url=($(echo $subscribe_data | base64 -d | sed 's/\r//g')) # 解码数据并删除 \r 换行符
subscribe_max=$(echo ${ssr_url[0]} | grep -i MAX= | awk -F = '{print $2}')
subscribe_max_x=()
if [ -n "$subscribe_max" ]; then
while [ ${#subscribe_max_x[@]} -ne $subscribe_max ]; do
if [ ${#ssr_url[@]} -ge 10 ]; then
if [ $((${RANDOM:0:2} % 2)) -eq 0 ]; then
temp_x=${RANDOM:0:1}
else
temp_x=${RANDOM:0:2}
fi
else
temp_x=${RANDOM:0:1}
fi
[ $temp_x -lt ${#ssr_url[@]} -a -z "$(echo "${subscribe_max_x[*]}" | grep -w $temp_x)" ] && subscribe_max_x[${#subscribe_max_x[@]}]="$temp_x"
done
else
subscribe_max=${#ssr_url[@]}
fi
echo_date "共计$subscribe_max个节点"
ssr_group=$(urlsafe_b64decode $(urlsafe_b64decode ${ssr_url[$((${#ssr_url[@]} - 1))]//ssr:\/\//} | sed 's/&/\n/g' | grep group= | awk -F = '{print $2}'))
if [ -z "$ssr_group" ]; then
ssr_group="default"
fi
if [ -n "$ssr_group" ]; then
subscribe_i=0
subscribe_n=0
subscribe_o=0
subscribe_x=""
temp_host_o=()
curr_ssr=$(uci show $name | grep @servers | grep -c server=)
for ((x = 0; x < $curr_ssr; x++)); do # 循环已有服务器信息,匹配当前订阅群组
temp_alias=$(uci -q get $name.@servers[$x].grouphashkey | grep "$ssr_grouphashkey")
[ -n "$temp_alias" ] && temp_host_o[${#temp_host_o[@]}]=$(uci get $name.@servers[$x].hashkey)
done
for ((x = 0; x < $subscribe_max; x++)); do # 循环链接
[ ${#subscribe_max_x[@]} -eq 0 ] && temp_x=$x || temp_x=${subscribe_max_x[x]}
result=$(echo ${ssr_url[temp_x]} | grep "ss")
subscribe_url_type=$(echo "$ssr_url" | awk -F ':' '{print $1}')
if [ "$subscribe_url_type" = "ss" ]; then
temp_info=${ssr_url[temp_x]//ss:\/\//} # 解码 SS 链接
# 计算hashkey
ssr_hashkey=$(echo "$temp_info" | md5sum | cut -d ' ' -f1)
info=$(urlsafe_b64decode $(echo "$temp_info" | awk -F '@' '{print $1}'))
temp_info_array=(${info//:/ })
ssr_type="ss"
ss_method=${temp_info_array[0]}
ssr_passwd=${temp_info_array[1]}
info=$(echo "$temp_info" | awk -F '@' '{print $2}' | awk -F '#' '{print $1}')
temp_info_array=(${info//:/ })
ssr_host=${temp_info_array[0]}
ssr_port=${temp_info_array[1]}
ssr_remarks=$(urldecode $(echo "$temp_info" | awk -F '#' '{print $2}'))
fi
if [ "$subscribe_url_type" = "ssr" ]; then
temp_info=$(urlsafe_b64decode ${ssr_url[temp_x]//ssr:\/\//}) # 解码 SSR 链接
# 计算hashkey
ssr_hashkey=$(echo "$temp_info" | md5sum | cut -d ' ' -f1)
info=${temp_info///?*/}
temp_info_array=(${info//:/ })
ssr_type="ssr"
ssr_host=${temp_info_array[0]}
ssr_port=${temp_info_array[1]}
ssr_protocol=${temp_info_array[2]}
ssr_method=${temp_info_array[3]}
ssr_obfs=${temp_info_array[4]}
ssr_passwd=$(urlsafe_b64decode ${temp_info_array[5]})
info=${temp_info:$((${#info} + 2))}
info=(${info//&/ })
ssr_protoparam=""
ssr_obfsparam=""
ssr_remarks="$temp_x"
for ((i = 0; i < ${#info[@]}; i++)); do # 循环扩展信息
temp_info=($(echo ${info[i]} | sed 's/=/ /g'))
case "${temp_info[0]}" in
protoparam)
ssr_protoparam=$(urlsafe_b64decode ${temp_info[1]})
;;
obfsparam)
ssr_obfsparam=$(urlsafe_b64decode ${temp_info[1]})
;;
remarks)
ssr_remarks=$(urlsafe_b64decode ${temp_info[1]})
;;
esac
done
fi
if [ "$subscribe_url_type" = "vmess" ]; then
temp_info=$(urlsafe_b64decode ${ssr_url[temp_x]//vmess:\/\//}) # 解码 Vmess 链接
# 计算hashkey
ssr_hashkey=$(echo "$temp_info" | md5sum | cut -d ' ' -f1)
ssr_type="v2ray"
json_load "$temp_info"
json_get_var ssr_host add
json_get_var ssr_port port
json_get_var ssr_alter_id aid
json_get_var ssr_vmess_id id
json_get_var ssr_transport net
json_get_var ssr_remarks ps
ssr_tcp_guise="none"
json_get_var ssr_ws_host host
json_get_var ssr_ws_path path
json_get_var ssr_tls tls
if [ "$ssr_tls" == "tls" -o "$ssr_tls" == "1" ]; then
ssr_tls="1"
ssr_insecure="1"
else
ssr_tls="0"
fi
fi
if [ -z "ssr_remarks" ]; then # 没有备注的话则生成一个
ssr_remarks="$ssr_host:$ssr_port"
fi
uci_name_tmp=$(uci show $name | grep -w "$ssr_hashkey" | awk -F . '{print $2}')
if [ -z "$uci_name_tmp" ]; then # 判断当前服务器信息是否存在
uci_name_tmp=$(uci add $name servers)
subscribe_n=$(($subscribe_n + 1))
fi
Server_Update $uci_name_tmp
subscribe_x=$subscribe_x$ssr_hashkey" "
ssrtype=$(echo $ssr_type | tr '[a-z]' '[A-Z]')
echo_date "$ssrtype节点:【$ssr_remarks"
# SSR
# echo "服务器地址: $ssr_host"
# echo "服务器端口 $ssr_port"
# echo "密码: $ssr_passwd"
# echo "SS加密: $ss_method"
# echo "加密: $ssr_method"
# echo "协议: $ssr_protocol"
# echo "协议参数: $ssr_protoparam"
# echo "混淆: $ssr_obfs"
# echo "混淆参数: $ssr_obfsparam"
# echo "备注: $ssr_remarks"
done
for ((x = 0; x < ${#temp_host_o[@]}; x++)); do # 新旧服务器信息匹配,如果旧服务器信息不存在于新服务器信息则删除
if [ -z "$(echo "$subscribe_x" | grep -w ${temp_host_o[x]})" ]; then
uci_name_tmp=$(uci show $name | grep ${temp_host_o[x]} | awk -F . '{print $2}')
uci delete $name.$uci_name_tmp
subscribe_o=$(($subscribe_o + 1))
fi
done
echo_date "本次更新订阅来源 【$ssr_group】 服务器数量: ${#ssr_url[@]} 新增服务器: $subscribe_n 删除服务器: $subscribe_o"
echo_date "在线订阅列表更新完成!请等待网页自动刷新!"
subscribe_log="$ssr_group 服务器订阅更新成功 服务器数量: ${#ssr_url[@]} 新增服务器: $subscribe_n 删除服务器: $subscribe_o"
logger -st $log_name[$$] -p6 "$subscribe_log"
uci commit $name
else
echo_date "${subscribe_url[$o]} 订阅数据解析失败 无法获取 Group"
logger -st $log_name[$$] -p3 "${subscribe_url[$o]} 订阅数据解析失败 无法获取 Group"
fi
else
echo_date "${subscribe_url[$o]} 订阅数据获取失败 错误代码: $curl_code"
logger -st $log_name[$$] -p3 "${subscribe_url[$o]} 订阅数据获取失败 错误代码: $curl_code"
fi
done
echo "END SUBSCRIBE"
/etc/init.d/$name restart >/dev/null 2>&1

View File

@ -0,0 +1,99 @@
#!/usr/bin/lua
------------------------------------------------
-- This file is part of the luci-app-ssr-plus update.lua
-- By Mattraks
------------------------------------------------
require 'nixio'
require 'luci.util'
require 'luci.jsonc'
require 'luci.sys'
local icount =0
local ucic = luci.model.uci.cursor()
local log = function(...)
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({ ... }, " "))
end
log('正在更新【GFW列表】数据库')
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd="wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64"
else
refresh_cmd="wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64"
end
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret== 0 then
luci.sys.call("/usr/bin/vssr-gfw")
icount = luci.sys.exec("cat /tmp/gfwnew.txt | wc -l")
if tonumber(icount)>1000 then
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf")
-- retstring=tostring(math.ceil(tonumber(icount)/2))
log('更新成功! 新的总纪录数:'.. icount)
else
log('你已经是最新数据,无需更新!')
end
else
log('更新失败!')
end
luci.sys.exec("rm -f /tmp/gfwnew.txt")
else
log('更新失败!')
end
log('正在更新【国内IP段】数据库')
refresh_cmd="wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' 2>/dev/null| awk -F\\| '/CN\\|ipv4/ { printf(\"%s/%d\\n\", $4, 32-log($5)/log(2)) }' > /tmp/china_ssr.txt"
sret=luci.sys.call(refresh_cmd)
icount = luci.sys.exec("cat /tmp/china_ssr.txt | wc -l")
if sret== 0 then
icount = luci.sys.exec("cat /tmp/china_ssr.txt | wc -l")
if tonumber(icount)>1000 then
oldcount=luci.sys.exec("cat /etc/china_ssr.txt | wc -l")
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/china_ssr.txt /etc/china_ssr.txt")
-- retstring=tostring(math.ceil(tonumber(icount)/2))
log('更新成功! 新的总纪录数:'.. icount)
else
log('你已经是最新数据,无需更新!')
end
else
log('更新失败!')
end
luci.sys.exec("rm -f /tmp/china_ssr.txt")
else
log('更新失败!')
end
if ucic:get_first('vssr', 'global', 'adblock','0') == "1" then
log('正在更新【广告屏蔽】数据库')
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd="wget-ssl --no-check-certificate -O - ".. ucic:get_first('shadowsocksr', 'global', 'adblock_url','https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt') .." > /tmp/adnew.conf"
end
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret== 0 then
luci.sys.call("/usr/bin/vssr-ad")
icount = luci.sys.exec("cat /tmp/ad.conf | wc -l")
if tonumber(icount)>1000 then
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l")
else
oldcount=0
end
if tonumber(icount) ~= tonumber(oldcount) then
luci.sys.exec("cp -f /tmp/ad.conf /etc/dnsmasq.ssr/ad.conf")
-- retstring=tostring(math.ceil(tonumber(icount)))
if oldcount==0 then
luci.sys.call("/etc/init.d/dnsmasq restart")
end
log('更新成功! 新的总纪录数:'.. icount)
else
log('你已经是最新数据,无需更新!')
end
else
log('更新失败!')
end
luci.sys.exec("rm -f /tmp/ad.conf")
else
log('更新失败!')
end
end

View File

@ -1,26 +0,0 @@
#!/bin/sh
chnroute_data=$(wget -O- -t 3 -T 3 http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest)
[ $? -eq 0 ] && {
echo "$chnroute_data" | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > /tmp/china_ssr.txt
}
if [ -s "/tmp/china_ssr.txt" ];then
if ( ! cmp -s /tmp/china_ssr.txt /etc/china_ssr.txt );then
mv /tmp/china_ssr.txt /etc/china_ssr.txt
fi
fi
/usr/share/vssr/chinaipset.sh
wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64
/usr/bin/vssr-gfw
if [ -s "/tmp/gfwnew.txt" ];then
if ( ! cmp -s /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf );then
mv /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf
echo "copy"
fi
fi
/etc/init.d/vssr restart