luci-app-vssr: add package

This commit is contained in:
CN_SZTL 2020-07-31 02:39:19 +08:00
parent dbb01cb419
commit 1ec392b30e
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
843 changed files with 40854 additions and 1 deletions

View File

@ -40,7 +40,8 @@ MT-Wifi 5.0.4.0: [MeIsReallyBa/mt7615-dbdc-linux5.4](https://github.com/MeIsReal
node-request source: [jerrykuku/node-request](https://github.com/jerrykuku/node-request).<br/>
luci-app-jd-dailybonus source: [jerrykuku/luci-app-jd-dailybonus](https://github.com/jerrykuku/luci-app-jd-dailybonus).<br/>
luci-app-oled source: [NateLol/luci-app-oled](https://github.com/NateLol/luci-app-oled).<br/>
luci-app-beardropper source: [NateLol/natelol](https://github.com/NateLol/natelol).
luci-app-beardropper source: [NateLol/natelol](https://github.com/NateLol/natelol).<br/>
luci-app-vssr source: [jerrykuku/luci-app-vssr](https://github.com/jerrykuku/luci-app-vssr).
## License
### Depend on their own License.

View File

@ -0,0 +1,91 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-vssr
PKG_VERSION:=1.11
PKG_RELEASE:=20200719
PKG_CONFIG_DEPENDS:= CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Socks
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)/config
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray
bool "Include V2ray"
default y
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan
bool "Include Trojan"
default y
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server
bool "Include ShadowsocksR Server"
default n
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Socks
bool "Include ShadowsocksR Socks and Tunnel"
default y
endef
define Package/luci-app-vssr
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=A New SS/SSR/V2Ray/Trojan LuCI interface
PKGARCH:=all
DEPENDS:=+shadowsocksr-libev-alt +ipset +ip-full +iptables-mod-tproxy +dnsmasq-full +coreutils +coreutils-base64 +bash +pdnsd-alt +wget +luasocket +jshn +lua-cjson +coreutils-nohup +lua-maxminddb \
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray \
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan:trojan \
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan:ipt2socks \
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server:shadowsocksr-libev-server \
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Socks:shadowsocksr-libev-ssr-local
endef
define Build/Prepare
endef
define Build/Compile
endef
define Package/luci-app-vssr/conffiles
/etc/ssr_ip
/etc/dnsmasq.ssr/gfw_list.conf
/etc/china_ssr.txt
/etc/dnsmasq.ssr/gfw_list.conf
/etc/dnsmasq.ssr/gfw_base.conf
/etc/dnsmasq.oversea/oversea_list.conf
endef
define Package/luci-app-vssr/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_DIR) $(1)/
cp -pR ./root/* $(1)/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/vssr.po $(1)/usr/lib/lua/luci/i18n/vssr.zh-cn.lmo
endef
define Package/luci-app-vssr/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
( . /etc/uci-defaults/luci-vssr ) && rm -f /etc/uci-defaults/luci-vssr
rm -rf /tmp/luci-indexcache
rm -rf /tmp/luci-modulecache/*
chmod 755 /etc/init.d/vssr >/dev/null 2>&1
/etc/init.d/vssr enable >/dev/null 2>&1
fi
exit 0
endef
define Package/luci-app-vssr/prerm
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
/etc/init.d/vssr disable
/etc/init.d/vssr stop
fi
exit 0
endef
$(eval $(call BuildPackage,luci-app-vssr))

View File

@ -0,0 +1,383 @@
-- Copyright (C) 2018 jerrykuku <jerrykuku@qq.com>
-- Licensed to the public under the GNU General Public License v3.
module("luci.controller.vssr", package.seeall)
function index()
if not nixio.fs.access("/etc/config/vssr") then return end
if nixio.fs.access("/usr/bin/ssr-redir") then
entry({"admin", "services", "vssr"},
alias("admin", "services", "vssr", "client"), _("Hello World"), 10).dependent =
true -- 首页
entry({"admin", "services", "vssr", "client"}, cbi("vssr/client"),
_("SSR Client"), 10).leaf = true -- 基本设置
entry({"admin", "services", "vssr", "servers"}, cbi("vssr/servers"),
_("Severs Nodes"), 11).leaf = true -- 服务器节点
entry({"admin", "services", "vssr", "servers"},
arcombine(cbi("vssr/servers"), cbi("vssr/client-config")),
_("Severs Nodes"), 11).leaf = true -- 编辑节点
entry({"admin", "services", "vssr", "control"}, cbi("vssr/control"),
_("Access Control"), 12).leaf = true -- 访问控制
if nixio.fs.access("/usr/bin/v2ray/v2ray") then
entry({"admin", "services", "vssr", "socks5"}, cbi("vssr/socks5"),
_("Socks5"), 13).leaf = true -- Socks5代理
end
entry({"admin", "services", "vssr", "advanced"}, cbi("vssr/advanced"),
_("Advanced Settings"), 14).leaf = true -- 高级设置
elseif nixio.fs.access("/usr/bin/ssr-server") then
entry({"admin", "services", "vssr"},
alias("admin", "services", "vssr", "server"), _("vssr"), 10).dependent =
true
else
return
end
if nixio.fs.access("/usr/bin/ssr-server") then
entry({"admin", "services", "vssr", "server"},
arcombine(cbi("vssr/server"), cbi("vssr/server-config")),
_("SSR Server"), 20).leaf = true
end
entry({"admin", "services", "vssr", "log"}, cbi("vssr/log"), _("Log"), 30).leaf =
true
entry({"admin", "services", "vssr", "licence"}, template("vssr/licence"),
_("Licence"), 40).leaf = true
entry({"admin", "services", "vssr", "refresh"}, call("refresh_data")) -- 更新白名单和GFWLIST
entry({"admin", "services", "vssr", "checkport"}, call("check_port")) -- 检测单个端口并返回Ping
entry({"admin", "services", "vssr", "run"}, call("act_status")) -- 检测全局服务器状态
entry({"admin", "services", "vssr", "change"}, call("change_node")) -- 切换节点
entry({"admin", "services", "vssr", "allserver"}, call("get_servers")) -- 获取所有节点Json
entry({"admin", "services", "vssr", "subscribe"}, call("get_subscribe")) -- 执行订阅
entry({"admin", "services", "vssr", "flag"}, call("get_flag")) -- 获取节点国旗 iso code
entry({"admin", "services", "vssr", "ip"}, call("check_ip")) -- 获取ip情况
entry({"admin", "services", "vssr", "switch"}, call("switch")) -- 设置节点为自动切换
end
-- 执行订阅
function get_subscribe()
local cjson = require "cjson"
local e = {}
local uci = luci.model.uci.cursor()
local auto_update = luci.http.formvalue("auto_update")
local auto_update_time = luci.http.formvalue("auto_update_time")
local proxy = luci.http.formvalue("proxy")
local subscribe_url = luci.http.formvalue("subscribe_url")
if subscribe_url ~= "[]" then
local cmd1 = 'uci set vssr.@server_subscribe[0].auto_update="' ..
auto_update .. '"'
local cmd2 = 'uci set vssr.@server_subscribe[0].auto_update_time="' ..
auto_update_time .. '"'
local cmd3 = 'uci set vssr.@server_subscribe[0].proxy="' .. proxy .. '"'
luci.sys.call('uci delete vssr.@server_subscribe[0].subscribe_url ')
luci.sys.call(cmd1)
luci.sys.call(cmd2)
luci.sys.call(cmd3)
for k, v in ipairs(cjson.decode(subscribe_url)) do
luci.sys.call(
'uci add_list vssr.@server_subscribe[0].subscribe_url="' .. v ..
'"')
end
luci.sys.call('uci commit vssr')
luci.sys.call(
"nohup /usr/bin/lua /usr/share/vssr/subscribe.lua >/www/check_update.htm 2>/dev/null &")
e.error = 0
else
e.error = 1
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
-- 获取所有节点
function get_servers()
local uci = luci.model.uci.cursor()
local server_table = {}
uci:foreach("vssr", "servers", function(s)
local e = {}
e["name"] = s[".name"]
local t1 = luci.sys.exec(
"ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'" %
s["server"])
e["t1"] = t1
table.insert(server_table, e)
end)
luci.http.prepare_content("application/json")
luci.http.write_json(server_table)
end
-- 切换节点
function change_node()
local e = {}
local uci = luci.model.uci.cursor()
local sid = luci.http.formvalue("set")
local name = ""
uci:foreach("vssr", "global", function(s) name = s[".name"] end)
e.status = false
e.sid = sid
if sid ~= "" then
uci:set("vssr", name, "global_server", sid)
uci:commit("vssr")
luci.sys.call("/etc/init.d/vssr restart")
e.status = true
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
--设置节点为自动切换
function switch()
local e = {}
local uci = luci.model.uci.cursor()
local sid = luci.http.formvalue("node")
local isSwitch = uci:get("vssr", sid, "switch_enable")
if isSwitch == "1" then
uci:set("vssr", sid, "switch_enable","0")
e.switch = false
else
uci:set("vssr", sid, "switch_enable","1")
e.switch = true
end
uci:commit("vssr")
e.status = true
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
-- 检测全局服务器状态
function act_status()
math.randomseed(os.time())
local e = {}
-- 全局服务器
e.global = luci.sys.call(
"busybox ps -w | grep vssr_t | grep -v grep >/dev/null") == 0
-- 检测PDNSD状态
e.pdnsd = luci.sys.call("pidof pdnsd >/dev/null") == 0
-- 检测游戏模式状态
e.game = luci.sys.call(
"busybox ps -w | grep vssr_u | grep -v grep >/dev/null") == 0
-- 检测Socks5
e.socks5 = luci.sys.call(
"busybox ps -w | grep vssr_s | grep -v grep >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
-- 刷新检测文件
function refresh_data()
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://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
local need_process = 0
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd =
"wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf"
need_process = 1
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
if need_process == 1 then
luci.sys.call("/usr/bin/vssr-ad")
end
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
-- 检测单个节点状态并返回连接速度
function check_port()
local sockets = require "socket"
local set = luci.http.formvalue("host")
local port = luci.http.formvalue("port")
local retstring = ""
local iret = 1
iret = luci.sys.call(" ipset add ss_spec_wan_ac " .. set .. " 2>/dev/null")
socket = nixio.socket("inet", "stream")
socket:setopt("socket", "rcvtimeo", 2)
socket:setopt("socket", "sndtimeo", 2)
local t0 = sockets.gettime()
ret = socket:connect(set, port)
socket:close()
local t1 = sockets.gettime()
if tostring(ret) == "true" then
retstring = "1"
else
retstring = "0"
end
if iret == 0 then
luci.sys.call(" ipset del ss_spec_wan_ac " .. set)
end
local tt = t1 - t0
luci.http.prepare_content("application/json")
luci.http.write_json({ret = retstring , used = math.floor(tt*1000 + 0.5)})
end
function JudgeIPString(ipStr)
if type(ipStr) ~= "string" then return false end
-- 判断长度
local len = string.len(ipStr)
if len < 7 or len > 15 then -- 长度不对
return false
end
-- 判断出现的非数字字符
local point = string.find(ipStr, "%p", 1) -- 字符"."出现的位置
local pointNum = 0 -- 字符"."出现的次数 正常ip有3个"."
while point ~= nil do
if string.sub(ipStr, point, point) ~= "." then -- 得到非数字符号不是字符"."
return false
end
pointNum = pointNum + 1
point = string.find(ipStr, "%p", point + 1)
if pointNum > 3 then return false end
end
if pointNum ~= 3 then -- 不是正确的ip格式
return false
end
-- 判断数字对不对
local num = {}
for w in string.gmatch(ipStr, "%d+") do
num[#num + 1] = w
local kk = tonumber(w)
if kk == nil or kk > 255 then -- 不是数字或超过ip正常取值范围了
return false
end
end
if #num ~= 4 then -- 不是4段数字
return false
end
return ipStr
end
-- 检测 当前节点ip 和 网站访问情况
function check_ip()
-- 获取当前的ip和国家
local e = {}
local d = {}
local mm = require 'maxminddb'
local db = mm.open('/usr/share/vssr/GeoLite2-Country.mmdb')
local ip = string.gsub(luci.sys.exec("content=`wget --no-check-certificate -q -O - https://api.ip.sb/ip`;echo $content"), "\n", "")
local res = db:lookup(ip)
d.flag = string.lower(res:get("country", "iso_code"))
d.country = res:get("country", "names", "zh-CN")
e.outboard = ip
e.outboardip = d
-- 检测国内通道
e.baidu = false
sret1 = luci.sys.call("/usr/bin/ssr-check www.baidu.com 80 3 1")
if sret1 == 0 then e.baidu = true end
e.taobao = false
sret2 = luci.sys.call("/usr/bin/ssr-check www.taobao.com 80 3 1")
if sret2 == 0 then e.taobao = true end
-- 检测国外通道
e.google = false
sret3 = luci.sys.call("/usr/bin/ssr-check www.google.com 80 3 1")
if sret3 == 0 then e.google = true end
e.youtube = false
sret4 = luci.sys.call("/usr/bin/ssr-check www.youtube.com 80 3 1")
if sret4 == 0 then e.youtube = true end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
-- 获取节点国旗 iso code
function get_flag()
local e = {}
local host = luci.http.formvalue("host")
local remark = luci.http.formvalue("remark")
local cmd = '/usr/share/vssr/getflag.sh "' .. remark .. '" ' .. host
e.host = host
e.flag = luci.sys.exec(cmd)
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -0,0 +1,140 @@
local vssr = "vssr"
local uci = luci.model.uci.cursor()
local server_table = {}
local gfwmode = 0
local gfw_count = 0
local ip_count = 0
local ad_count = 0
if nixio.fs.access("/etc/dnsmasq.ssr/gfw_list.conf") then gfwmode = 1 end
local sys = require "luci.sys"
if gfwmode == 1 then
gfw_count =
tonumber(sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")) / 2
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
ad_count = tonumber(sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l"))
end
end
if nixio.fs.access("/etc/china_ssr.txt") then
ip_count = sys.exec("cat /etc/china_ssr.txt | wc -l")
end
uci:foreach(vssr, "servers", function(s)
if s["type"] == "v2ray" then
if s.alias then
server_table[s[".name"]] = "[%s]:%s" %
{string.upper(s.type), s.alias}
elseif s.server and s.server_port then
server_table[s[".name"]] = "[%s]:%s:%s" %
{
string.upper(s.type), s.server, s.server_port
}
end
end
end)
local key_table = {}
for key, _ in pairs(server_table) do table.insert(key_table, key) end
table.sort(key_table)
m = Map(vssr)
-- [[ 服务器节点故障自动切换设置 ]]--
s = m:section(TypedSection, "global",
translate("Server failsafe auto swith settings"))
s.anonymous = true
o = s:option(Flag, "monitor_enable", translate("Enable Process Deamon"))
o.rmempty = false
o = s:option(Flag, "enable_switch", translate("Enable Auto Switch"))
o.rmempty = false
o = s:option(Value, "switch_time", translate("Switch check cycly(second)"))
o.datatype = "uinteger"
o:depends("enable_switch", "1")
o.default = 3600
o = s:option(Value, "switch_timeout", translate("Check timout(second)"))
o.datatype = "uinteger"
o:depends("enable_switch", "1")
o.default = 5
o = s:option(Value, "switch_try_count", translate("Check Try Count"))
o.datatype = "uinteger"
o:depends("enable_switch", "1")
o.default = 3
-- [[ 节点订阅 ]]--
s = m:section(TypedSection, "server_subscribe",
translate("Servers subscription and manage"))
s.anonymous = true
o = s:option(Flag, "auto_update", translate("Auto Update"))
o.rmempty = false
o.description = translate(
"Auto Update Server subscription, GFW list and CHN route")
o =
s:option(ListValue, "auto_update_time", translate("Update time (every day)"))
for t = 0, 23 do o:value(t, t .. ":00") end
o.default = 2
o.rmempty = false
o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL"))
o.rmempty = true
o = s:option(Flag, "proxy", translate("Through proxy update"))
o.rmempty = false
o.description = translate("Through proxy update list, Not Recommended ")
o = s:option(DummyValue, "", "")
o.rawhtml = true
o.template = "vssr/update_subscribe"
o = s:option(Button, "delete", translate("Delete all severs"))
o.inputstyle = "reset"
o.write = function()
uci:delete_all("vssr", "servers", function(s) return true end)
uci:commit("vssr")
luci.sys.call("/etc/init.d/vssr stop")
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "vssr",
"advanced"))
end
-- [[ adblock ]]--
s = m:section(TypedSection, "global", translate("adblock settings"))
s.anonymous = true
o = s:option(Flag, "adblock", translate("Enable adblock"))
o.rmempty = false
-- [[ 更新设置 ]]--
s = m:section(TypedSection, "socks5_proxy", translate("Update Setting"))
s.anonymous = true
o = s:option(Button, "gfw_data", translate("GFW List Data"))
o.rawhtml = true
o.template = "vssr/refresh"
o.value = tostring(math.ceil(gfw_count)) .. " " .. translate("Records")
o = s:option(Button, "ip_data", translate("China IP Data"))
o.rawhtml = true
o.template = "vssr/refresh"
o.value = ip_count .. " " .. translate("Records")
if uci:get_first('vssr', 'global', 'adblock', '') == '1' then
o = s:option(Button, "ad_data", translate("Advertising Data"))
o.rawhtml = true
o.template = "vssr/refresh"
o.value = ad_count .. " " .. translate("Records")
end
return m

View File

@ -0,0 +1,387 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
-- Licensed to the public under the GNU General Public License v3.
local m, s, o, kcp_enable
local vssr = "vssr"
local uci = luci.model.uci.cursor()
local fs = require "nixio.fs"
local sys = require "luci.sys"
local sid = arg[1]
local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
local function isKcptun(file)
if not fs.access(file, "rwx", "rx", "rx") then fs.chmod(file, 755) end
local str = sys.exec(file .. " -v | awk '{printf $1}'")
return (str:lower() == "kcptun")
end
local server_table = {}
local encrypt_methods = {
"none", "table", "rc4", "rc4-md5-6", "rc4-md5", "aes-128-cfb",
"aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
"bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
"cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "salsa20",
"chacha20", "chacha20-ietf"
}
local encrypt_methods_ss = {
-- aead
"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305", -- stream
"table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb",
"aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
"camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
"chacha20-ietf"
}
local protocol = {
"origin", "verify_deflate", "auth_sha1_v4", "auth_aes128_sha1",
"auth_aes128_md5", "auth_chain_a", "auth_chain_b", "auth_chain_c",
"auth_chain_d", "auth_chain_e", "auth_chain_f"
}
obfs = {
"plain", "http_simple", "http_post", "random_head", "tls1.2_ticket_auth"
}
local securitys = {"auto", "none", "aes-128-gcm", "chacha20-poly1305"}
m = Map(vssr, translate("Edit vssr Server"))
m.redirect = luci.dispatcher.build_url("admin/services/vssr/servers")
if m.uci:get(vssr, sid) ~= "servers" then
luci.http.redirect(m.redirect)
return
end
-- [[ Servers Setting ]]--
s = m:section(NamedSection, sid, "servers")
s.anonymous = true
s.addremove = false
o = s:option(DummyValue, "ssr_url", translate("Configuration Url"))
o.rawhtml = true
o.template = "vssr/ssrurl"
o.value = sid
o = s:option(ListValue, "type", translate("Server Node Type"))
o:value("ssr", translate("ShadowsocksR"))
if nixio.fs.access("/usr/bin/v2ray/v2ray") or nixio.fs.access("/usr/bin/v2ray") then
o:value("ss", translate("Shadowsocks New Version"))
o:value("v2ray", translate("V2Ray"))
end
if nixio.fs.access("/usr/sbin/trojan") then
o:value("trojan", translate("Trojan"))
end
o.description = translate(
"Using incorrect encryption mothod may causes service fail to start")
o = s:option(Value, "alias", translate("Alias(optional)"))
o = s:option(Value, "flag", translate("Area"))
o.description = translate("请自己指定。格式cn us hk 等")
o.rmempty = true
o = s:option(Value, "server", translate("Server Address"))
o.datatype = "host"
o.rmempty = false
o = s:option(Value, "server_port", translate("Server Port"))
o.datatype = "port"
o.rmempty = false
-- o = s:option(Value, "timeout", translate("Connection Timeout"))
-- o.datatype = "uinteger"
-- o.default = 60
-- o.rmempty = false
o = s:option(Value, "password", translate("Password"))
o.password = true
o.rmempty = true
o:depends("type", "ssr")
o:depends("type", "ss")
o:depends("type", "trojan")
o = s:option(Value, "peer", translate("Peer"))
o.datatype = "host"
o.rmempty = true
o:depends("type", "trojan")
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods) do o:value(v) end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(ListValue, "encrypt_method_ss", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods_ss) do o:value(v) end
o.rmempty = true
o:depends("type", "ss")
o = s:option(ListValue, "protocol", translate("Protocol"))
for _, v in ipairs(protocol) do o:value(v) end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(Value, "protocol_param", translate("Protocol param(optional)"))
o:depends("type", "ssr")
o = s:option(ListValue, "obfs", translate("Obfs"))
for _, v in ipairs(obfs) do o:value(v) end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(Flag, "v2ray_plugin", translate("V2ray-plugin"))
o.rmempty = false
o:depends("type", "ss")
o = s:option(Value, "obfs_transport", translate("V2ray-plugin-transport"))
o.rmempty = true
o.default = "ws"
o:depends("v2ray_plugin", "1")
o = s:option(Value, "obfs_host", translate("V2ray-plugin-host"))
o.rmempty = true
o:depends("v2ray_plugin", "1")
o = s:option(Value, "obfs_path", translate("V2ray-plugin-path"))
o.rmempty = true
o:depends("v2ray_plugin", "1")
o = s:option(Flag, "obfs_opts", translate("TLS"))
o.rmempty = false
o:depends("v2ray_plugin", "1")
o = s:option(Value, "obfs_param", translate("Obfs param(optional)"))
o:depends("type", "ssr")
-- AlterId
o = s:option(Value, "alter_id", translate("AlterId"))
o.datatype = "port"
o.default = 16
o.rmempty = true
o:depends("type", "v2ray")
-- VmessId
o = s:option(Value, "vmess_id", translate("VmessId (UUID)"))
o.rmempty = true
o.default = uuid
o:depends("type", "v2ray")
-- 加密方式
o = s:option(ListValue, "security", translate("Encrypt Method"))
for _, v in ipairs(securitys) do o:value(v, v:upper()) end
o.rmempty = true
o:depends("type", "v2ray")
-- 传输协议
o = s:option(ListValue, "transport", translate("Transport"))
o:value("tcp", "TCP")
o:value("kcp", "mKCP")
o:value("ws", "WebSocket")
o:value("h2", "HTTP/2")
o:value("quic", "QUIC")
o.rmempty = true
o:depends("type", "v2ray")
-- [[ TCP部分 ]]--
-- TCP伪装
o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:depends("transport", "tcp")
o:value("none", translate("None"))
o:value("http", "HTTP")
o.rmempty = true
-- HTTP域名
o = s:option(DynamicList, "http_host", translate("HTTP Host"))
o:depends("tcp_guise", "http")
o.rmempty = true
-- HTTP路径
o = s:option(DynamicList, "http_path", translate("HTTP Path"))
o:depends("tcp_guise", "http")
o.rmempty = true
-- [[ WS部分 ]]--
-- WS域名
o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends("transport", "ws")
o.rmempty = true
-- WS路径
o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends("transport", "ws")
o.rmempty = true
-- [[ H2部分 ]]--
-- H2域名
o = s:option(DynamicList, "h2_host", translate("HTTP/2 Host"))
o:depends("transport", "h2")
o.rmempty = true
-- H2路径
o = s:option(Value, "h2_path", translate("HTTP/2 Path"))
o:depends("transport", "h2")
o.rmempty = true
-- [[ QUIC部分 ]]--
o = s:option(ListValue, "quic_security", translate("QUIC Security"))
o:depends("transport", "quic")
o.rmempty = true
o:value("none", translate("None"))
o:value("aes-128-gcm", translate("aes-128-gcm"))
o:value("chacha20-poly1305", translate("chacha20-poly1305"))
o = s:option(Value, "quic_key", translate("QUIC Key"))
o:depends("transport", "quic")
o.rmempty = true
o = s:option(ListValue, "quic_guise", translate("Header"))
o:depends("transport", "quic")
o.rmempty = true
o:value("none", translate("None"))
o:value("srtp", translate("VideoCall (SRTP)"))
o:value("utp", translate("BitTorrent (uTP)"))
o:value("wechat-video", translate("WechatVideo"))
o:value("dtls", "DTLS 1.2")
o:value("wireguard", "WireGuard")
-- [[ mKCP部分 ]]--
o = s:option(ListValue, "kcp_guise", translate("Camouflage Type"))
o:depends("transport", "kcp")
o:value("none", translate("None"))
o:value("srtp", translate("VideoCall (SRTP)"))
o:value("utp", translate("BitTorrent (uTP)"))
o:value("wechat-video", translate("WechatVideo"))
o:value("dtls", "DTLS 1.2")
o:value("wireguard", "WireGuard")
o.rmempty = true
o = s:option(Value, "mtu", translate("MTU"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 1350
o.rmempty = true
o = s:option(Value, "tti", translate("TTI"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 50
o.rmempty = true
o = s:option(Value, "uplink_capacity", translate("Uplink Capacity"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 5
o.rmempty = true
o = s:option(Value, "downlink_capacity", translate("Downlink Capacity"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 20
o.rmempty = true
o = s:option(Value, "read_buffer_size", translate("Read Buffer Size"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 2
o.rmempty = true
o = s:option(Value, "write_buffer_size", translate("Write Buffer Size"))
o.datatype = "uinteger"
o:depends("transport", "kcp")
o.default = 2
o.rmempty = true
o = s:option(Flag, "congestion", translate("Congestion"))
o:depends("transport", "kcp")
o.rmempty = true
-- [[ allowInsecure ]]--
o = s:option(Flag, "insecure", translate("allowInsecure"))
o.rmempty = true
o:depends("type", "v2ray")
o:depends("type", "trojan")
-- [[ TLS ]]--
o = s:option(Flag, "tls", translate("TLS"))
o.rmempty = true
o.default = "0"
o:depends("type", "v2ray")
o:depends("type", "trojan")
-- [[ Mux ]]--
o = s:option(Flag, "mux", translate("Mux"))
o.rmempty = true
o.default = "0"
o:depends("type", "v2ray")
o:depends("v2ray_plugin", "1")
o = s:option(Value, "concurrency", translate("Concurrency"))
o.datatype = "uinteger"
o.rmempty = true
o.default = "8"
o:depends("mux", "1")
o = s:option(Flag, "fast_open", translate("TCP Fast Open"))
o.rmempty = true
o.default = "0"
o:depends("type", "ssr")
o:depends("type", "ss")
o:depends("type", "trojan")
o = s:option(Flag, "switch_enable", translate("Enable Auto Switch"))
o.rmempty = false
o.default = "1"
o = s:option(Value, "local_port", translate("Local Port"))
o.datatype = "port"
o.default = 1234
o.rmempty = false
if nixio.fs.access("/usr/bin/kcptun-client") then
kcp_enable = s:option(Flag, "kcp_enable", translate("KcpTun Enable"),
translate("bin:/usr/bin/kcptun-client"))
kcp_enable.rmempty = true
kcp_enable.default = "0"
kcp_enable:depends("type", "ssr")
kcp_enable:depends("type", "ss")
o = s:option(Value, "kcp_port", translate("KcpTun Port"))
o.datatype = "port"
o.default = 4000
function o.validate(self, value, section)
local kcp_file = "/usr/bin/kcptun-client"
local enable = kcp_enable:formvalue(section) or kcp_enable.disabled
if enable == kcp_enable.enabled then
if not fs.access(kcp_file) then
return nil, translate("Haven't a Kcptun executable file")
elseif not isKcptun(kcp_file) then
return nil, translate("Not a Kcptun executable file")
end
end
return value
end
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_password", translate("KcpTun Password"))
o.password = true
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_param", translate("KcpTun Param"))
o.default = "--nocomp"
o:depends("type", "ssr")
o:depends("type", "ss")
end
return m

View File

@ -0,0 +1,148 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
-- Copyright (C) 2018 lean <coolsnowwolf@gmail.com> github.com/coolsnowwolf
-- Licensed to the public under the GNU General Public License v3.
local m, s, sec, o, kcp_enable
local vssr = "vssr"
local gfwmode=0
if nixio.fs.access("/etc/dnsmasq.ssr/gfw_list.conf") then
gfwmode=1
end
local uci = luci.model.uci.cursor()
m = Map(vssr)
m:section(SimpleSection).template = "vssr/status_top"
local server_table = {}
local v2ray_table = {}
uci:foreach(vssr, "servers", function(s)
if s.alias then
server_table[s[".name"]] = "[%s]:%s" %{string.upper(s.type), s.alias}
elseif s.server and s.server_port then
server_table[s[".name"]] = "[%s]:%s:%s" %{string.upper(s.type), s.server, s.server_port}
end
if s.type == "v2ray" then
if s.alias then
v2ray_table[s[".name"]] = "[%s]:%s" %{string.upper(s.type), s.alias}
elseif s.server and s.server_port then
v2ray_table[s[".name"]] = "[%s]:%s:%s" %{string.upper(s.type), s.server, s.server_port}
end
end
end)
local key_table = {}
for key,_ in pairs(server_table) do
table.insert(key_table,key)
end
table.sort(key_table)
local key_table_v2 = {}
for key,_ in pairs(v2ray_table) do
table.insert(key_table_v2,key)
end
table.sort(key_table_v2)
-- [[ Global Setting ]]--
s = m:section(TypedSection, "global",translate("Basic Settings [SS(R)|V2ray|Trojan]"))
s.anonymous = true
o = s:option(ListValue, "global_server", translate("Main Server"))
o:value("nil", translate("Disable"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
o.default = "nil"
o.rmempty = false
o = s:option(ListValue, "udp_relay_server", translate("Game Mode UDP Server"))
o:value("", translate("Disable"))
o:value("same", translate("Same as Global Server"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
o = s:option(Flag, "v2ray_flow", translate("Open v2ray split-flow"))
o.rmempty = false
o.description = translate("When open v2ray split-flow,your main server must be a v2ray server")
o = s:option(ListValue, "youtube_server", translate("Youtube Proxy"))
o:value("nil", translate("Same as Global Server"))
for _,key in pairs(key_table_v2) do o:value(key,v2ray_table[key]) end
o:depends("v2ray_flow", "1")
o.default = "nil"
o = s:option(ListValue, "tw_video_server", translate("TaiWan Video Proxy"))
o:value("nil", translate("Same as Global Server"))
for _,key in pairs(key_table_v2) do o:value(key,v2ray_table[key]) end
o:depends("v2ray_flow", "1")
o.default = "nil"
o = s:option(ListValue, "netflix_server", translate("Netflix Proxy"))
o:value("nil", translate("Same as Global Server"))
for _,key in pairs(key_table_v2) do o:value(key,v2ray_table[key]) end
o:depends("v2ray_flow", "1")
o.default = "nil"
o = s:option(ListValue, "disney_server", translate("Diseny+ Proxy"))
o:value("nil", translate("Same as Global Server"))
for _,key in pairs(key_table_v2) do o:value(key,v2ray_table[key]) end
o:depends("v2ray_flow", "1")
o.default = "nil"
o = s:option(ListValue, "prime_server", translate("Prime Video Proxy"))
o:value("nil", translate("Same as Global Server"))
for _,key in pairs(key_table_v2) do o:value(key,v2ray_table[key]) end
o:depends("v2ray_flow", "1")
o.default = "nil"
o = s:option(ListValue, "threads", translate("Multi Threads Option"))
o:value("0", translate("Auto Threads"))
o:value("1", translate("1 Thread"))
o:value("2", translate("2 Threads"))
o:value("4", translate("4 Threads"))
o:value("8", translate("8 Threads"))
o.default = "0"
o.rmempty = false
o = s:option(ListValue, "run_mode", translate("Running Mode"))
o:value("gfw", translate("GFW List Mode"))
o:value("router", translate("IP Route Mode"))
o:value("all", translate("Global Mode"))
o:value("oversea", translate("Oversea Mode"))
o.default = "router"
o = s:option(ListValue, "dports", translate("Proxy Ports"))
o:value("1", translate("All Ports"))
o:value("2", translate("Only Common Ports"))
o.default = 1
o = s:option(ListValue, "pdnsd_enable", translate("Resolve Dns Mode"))
o:value("1", translate("Use Pdnsd tcp query and cache"))
o:value("0", translate("Use Local DNS Service listen port 5335"))
o.default = 1
o = s:option(Value, "tunnel_forward", translate("Anti-pollution DNS Server"))
o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)"))
o:value("8.8.8.8:53", translate("Google Public DNS (8.8.8.8)"))
o:value("208.67.222.222:53", translate("OpenDNS (208.67.222.222)"))
o:value("208.67.220.220:53", translate("OpenDNS (208.67.220.220)"))
o:value("209.244.0.3:53", translate("Level 3 Public DNS (209.244.0.3)"))
o:value("209.244.0.4:53", translate("Level 3 Public DNS (209.244.0.4)"))
o:value("4.2.2.1:53", translate("Level 3 Public DNS (4.2.2.1)"))
o:value("4.2.2.2:53", translate("Level 3 Public DNS (4.2.2.2)"))
o:value("4.2.2.3:53", translate("Level 3 Public DNS (4.2.2.3)"))
o:value("4.2.2.4:53", translate("Level 3 Public DNS (4.2.2.4)"))
o:value("1.1.1.1:53", translate("Cloudflare DNS (1.1.1.1)"))
o:value("114.114.114.114:53", translate("Oversea Mode DNS-1 (114.114.114.114)"))
o:value("114.114.115.115:53", translate("Oversea Mode DNS-2 (114.114.115.115)"))
o:depends("pdnsd_enable", "1")
m:section(SimpleSection).template = "vssr/status_bottom"
return m

View File

@ -0,0 +1,88 @@
local m, s, o
local NXFS = require "nixio.fs"
m = Map("vssr", translate("IP black-and-white list"))
s = m:section(TypedSection, "access_control")
s.anonymous = true
-- Part of WAN
s:tab("wan_ac", translate("WAN IP AC"))
o = s:taboption("wan_ac", DynamicList, "wan_bp_ips", translate("WAN White List IP"))
o.datatype = "ip4addr"
o = s:taboption("wan_ac", DynamicList, "wan_fw_ips", translate("WAN Force Proxy IP"))
o.datatype = "ip4addr"
-- Part of LAN
s:tab("lan_ac", translate("LAN IP AC"))
o = s:taboption("lan_ac", DynamicList, "lan_ac_ips", translate("LAN Bypassed Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
end)
o = s:taboption("lan_ac", DynamicList, "lan_fp_ips", translate("LAN Force Proxy Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
end)
o = s:taboption("lan_ac", DynamicList, "lan_gm_ips", translate("Game Mode Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
end)
-- Part of Self
-- s:tab("self_ac", translate("Router Self AC"))
-- o = s:taboption("self_ac",ListValue, "router_proxy", translate("Router Self Proxy"))
-- o:value("1", translatef("Normal Proxy"))
-- o:value("0", translatef("Bypassed Proxy"))
-- o:value("2", translatef("Forwarded Proxy"))
-- o.rmempty = false
s:tab("esc", translate("Bypass Domain List"))
local escconf = "/etc/config/white.list"
o = s:taboption("esc", TextValue, "escconf")
o.rows = 13
o.wrap = "off"
o.rmempty = true
o.cfgvalue = function(self, section)
return NXFS.readfile(escconf) or ""
end
o.write = function(self, section, value)
NXFS.writefile(escconf, value:gsub("\r\n", "\n"))
end
o.remove = function(self, section, value)
NXFS.writefile(escconf, "")
end
s:tab("block", translate("Black Domain List"))
local blockconf = "/etc/config/black.list"
o = s:taboption("block", TextValue, "blockconf")
o.rows = 13
o.wrap = "off"
o.rmempty = true
o.cfgvalue = function(self, section)
return NXFS.readfile(blockconf) or " "
end
o.write = function(self, section, value)
NXFS.writefile(blockconf, value:gsub("\r\n", "\n"))
end
o.remove = function(self, section, value)
NXFS.writefile(blockconf, "")
end
return m

View File

@ -0,0 +1,15 @@
local fs = require "nixio.fs"
local conffile = "/tmp/ssrpro.log"
f = SimpleForm("logview")
t = f:field(TextValue, "conf")
t.rmempty = true
t.rows = 20
function t.cfgvalue()
luci.sys.exec("[ -f /tmp/vssr.log ] && sed '1!G;h;$!d' /tmp/vssr.log > /tmp/ssrpro.log")
return fs.readfile(conffile) or ""
end
t.readonly="readonly"
return f

View File

@ -0,0 +1,95 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
-- Licensed to the public under the GNU General Public License v3.
local m, s, o
local vssr = "vssr"
local sid = arg[1]
local encrypt_methods = {
"rc4-md5",
"rc4-md5-6",
"rc4",
"table",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
}
local protocol = {
"origin",
}
obfs = {
"plain",
"http_simple",
"http_post",
}
m = Map(vssr, translate("Edit vssr Server"))
m.redirect = luci.dispatcher.build_url("admin/services/vssr/server")
if m.uci:get(vssr, sid) ~= "server_config" then
luci.http.redirect(m.redirect)
return
end
-- [[ Server Setting ]]--
s = m:section(NamedSection, sid, "server_config")
s.anonymous = true
s.addremove = false
o = s:option(Flag, "enable", translate("Enable"))
o.default = 1
o.rmempty = false
o = s:option(Value, "server_port", translate("Server Port"))
o.datatype = "port"
o.default = 8388
o.rmempty = false
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 60
o.rmempty = false
o = s:option(Value, "password", translate("Password"))
o.password = true
o.rmempty = false
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods) do o:value(v) end
o.rmempty = false
o = s:option(ListValue, "protocol", translate("Protocol"))
for _, v in ipairs(protocol) do o:value(v) end
o.rmempty = false
o = s:option(ListValue, "obfs", translate("Obfs"))
for _, v in ipairs(obfs) do o:value(v) end
o.rmempty = false
o = s:option(Value, "obfs_param", translate("Obfs param(optional)"))
o = s:option(Flag, "fast_open", translate("TCP Fast Open"))
o.rmempty = false
return m

View File

@ -0,0 +1,114 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
-- Licensed to the public under the GNU General Public License v3.
local m, sec, o
local vssr = "vssr"
local uci = luci.model.uci.cursor()
local ipkg = require("luci.model.ipkg")
m = Map(vssr, translate("vssr Server"))
local encrypt_methods = {
"table",
"rc4",
"rc4-md5",
"rc4-md5-6",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
}
local protocol = {
"origin",
"verify_deflate",
"auth_sha1_v4",
"auth_aes128_sha1",
"auth_aes128_md5",
"auth_chain_a",
}
obfs = {
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth",
"tls1.2_ticket_fastauth",
}
-- [[ Global Setting ]]--
sec = m:section(TypedSection, "server_global", translate("Global Setting"))
sec.anonymous = true
o = sec:option(Flag, "enable_server", translate("Enable Server"))
o.rmempty = false
-- [[ Server Setting ]]--
sec = m:section(TypedSection, "server_config", translate("Server Setting"))
sec.anonymous = true
sec.addremove = true
sec.template = "cbi/tblsection"
sec.extedit = luci.dispatcher.build_url("admin/services/vssr/server/%s")
function sec.create(...)
local sid = TypedSection.create(...)
if sid then
luci.http.redirect(sec.extedit % sid)
return
end
end
o = sec:option(Flag, "enable", translate("Enable"))
function o.cfgvalue(...)
return Value.cfgvalue(...) or translate("0")
end
o.rmempty = false
o = sec:option(DummyValue, "server_port", translate("Server Port"))
function o.cfgvalue(...)
return Value.cfgvalue(...) or "?"
end
o = sec:option(DummyValue, "encrypt_method", translate("Encrypt Method"))
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
return v and v:upper() or "?"
end
o = sec:option(DummyValue, "protocol", translate("Protocol"))
function o.cfgvalue(...)
return Value.cfgvalue(...) or "?"
end
o = sec:option(DummyValue, "obfs", translate("Obfs"))
function o.cfgvalue(...)
return Value.cfgvalue(...) or "?"
end
return m

View File

@ -0,0 +1,43 @@
-- Licensed to the public under the GNU General Public License v3.
local m, s, o
local vssr = "vssr"
local cjson = require("cjson")
local uci = luci.model.uci.cursor()
local server_count = 0
local server_table = {}
uci:foreach("vssr", "servers", function(s)
server_count = server_count + 1
s["name"] = s[".name"]
table.insert(server_table, s)
end)
local name = ""
uci:foreach("vssr", "global", function(s) name = s[".name"] end)
m = Map(vssr)
m:section(SimpleSection).template = "vssr/status_top"
-- [[ Servers List ]]--
s = m:section(TypedSection, "servers")
s.anonymous = true
s.addremove = true
s.sortable = false
s.des = server_count
s.current = uci:get("vssr", name, "global_server")
s.servers = cjson.encode(server_table)
s.template = "vssr/tblsection"
s.extedit = luci.dispatcher.build_url("admin/services/vssr/servers/%s")
function s.create(...)
local sid = TypedSection.create(...)
if sid then
luci.http.redirect(s.extedit % sid)
return
end
end
m:section(SimpleSection).template = "vssr/status_bottom"
return m

View File

@ -0,0 +1,41 @@
local vssr = "vssr"
local uci = luci.model.uci.cursor()
local server_table = {}
local sys = require "luci.sys"
m = Map(vssr)
-- [[ SOCKS5 Proxy ]]--
if nixio.fs.access("/usr/bin/v2ray/v2ray") then
s = m:section(TypedSection, "socks5_proxy", translate("V2ray SOCKS5 Proxy"))
s.anonymous = true
o = s:option(Flag, "enable_server", translate("Enable Servers"))
o.rmempty = false
o = s:option(Flag, "enable_auth", translate("Enable Auth"))
o.rmempty = false
o = s:option(Value, "Socks_user", translate("Socks user"))
o.default="user"
o.rmempty = true
o:depends("enable_auth", "1")
o = s:option(Value, "Socks_pass", translate("Socks pass"))
o.default="password"
o.password = true
o.rmempty = true
o:depends("enable_auth", "1")
o = s:option(Value, "local_port", translate("Local Port"))
o.datatype = "port"
o.default = 1080
o.rmempty = false
end
return m

View File

@ -0,0 +1 @@
</div>

View File

@ -0,0 +1,2 @@
<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" class="incon<%=subcount%>" data-depends="<%=pcdata(self:deplist2json(section))%>">

View File

@ -0,0 +1,21 @@
<%+header%>
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=<%=math.random(1,100000)%>">
<div class="pure-g status">
<div class="pure-u-1">
<div class="block pure-g">
<div class="pure-u-1">
<h4 id="vssr_status"><span class="green" style="color: #404040;">GeoLite2</span></h4>
<p style="margin: 1rem; line-height: 1.8em;">This product includes GeoLite2 data created by MaxMind,
available from
<a href="https://www.maxmind.com">https://www.maxmind.com</a>.</p>
<h4 id="vssr_status"><span class="green" style="color: #404040;">Flag-icon-css</span></h4>
<p style="margin: 1rem; line-height: 1.8em;">A collection of all country flags in SVG — plus the CSS for easier integration <a href="https://flagicons.lipis.dev">https://flagicons.lipis.dev</a></p>
</div>
</div>
</div>
</div>
<%+footer%>

View File

@ -0,0 +1,35 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function refresh_data(btn, dataname) {
btn.disabled = true;
btn.value = '<%:Refresh...%> ';
murl = dataname;
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "vssr","refresh")%>',
{ set: murl },
function (x, rv) {
var s = document.getElementById(dataname + '-status');
if (s) {
if (rv.ret == "0")
s.innerHTML = "<font color='green'>" + "<%:No new data!%> " + "</font>";
else if (rv.ret == "-1") {
s.innerHTML = "<font color='red'>" + "<%:Refresh Error!%> " + "</font>";
}
else {
s.innerHTML = "<font color='green'>" + "<%:Refresh OK!%> " + "<%:Total Records:%>" + rv.ret + "</font>";
}
}
btn.disabled = false;
btn.value = '<%:Refresh Data %>';
}
);
return false;
}
//]]></script>
<input type="button" class="cbi-button cbi-input-reload" value="<%:Refresh Data%> "
onclick="return refresh_data(this,'<%=self.option%>')" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,275 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
const GET_FLAG_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","flag")%>';
var sid;
function getFlag(remark, hosts, sid) {
XHR.get(GET_FLAG_URL,
{ host: hosts, remark: remark },
function (x, rv) {
el('.flag').value = rv.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, "=");
try {
return atob(str);
}catch(err) {
return str;
}
}
function dictvalue(d, key) {
var v = d[key];
if (typeof (v) == 'undefined' || v == '')
return '';
return b64decsafe(v);
}
function el(id) {
return document.getElementById('cbid.vssr.' + sid + id);
}
function import_ssr_url(btn, urlname, sids) {
sid = sids;
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var ssrurl = prompt("<%:Paste Node Link Here%> 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[0] != "trojan") || ssu[1] == "") {
s.innerHTML = "<font color='red'><%:Invalid Format%></font>";
return false;
}
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
var rema = "";
if (ssu[0] == "ssr") {
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "ssr";
el('.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] || '');
}
}
el('.server').value = ssm[1];
el('.server_port').value = ssm[2];
el('.protocol').value = ssm[3];
el('.encrypt_method').value = ssm[4];
el('.obfs').value = ssm[5];
el('.password').value = b64decsafe(ssm[6]);
el('.obfs_param').value = dictvalue(pdict, 'obfsparam');
el('.protocol_param').value = dictvalue(pdict, 'protoparam');
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0)
rema = b64decutf8safe(rem);
el('.alias').value = b64decutf8safe(rem);
getFlag(rema, ssm[1], sid); //get flag iso code
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);
el('.type').value = "ss";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
el('.server').value = part2[0];
el('.server_port').value = part2[1];
el('.password').value = part1[1];
el('.encrypt_method_ss').value = part1[0];
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, part2[0], sid); //get flag iso code
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);
el('.type').value = "trojan";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(team);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
var others = part2[1].split('?');
var queryParam = {}
if(others.length > 1) {
var queryParams = others[1]
var queryArray = queryParams.split('&')
for (i = 0; i < queryArray.length; i++) {
var params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
el('.server').value = part2[0];
el('.server_port').value = others[0];
el('.password').value = part1[1];
if(queryParam.peer || queryParam.sni){
el('.tls').checked = true;
el('.peer').value = queryParam.peer || queryParam.sni;
}
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, part2[0], sid); //get flag iso code
s.innerHTML = "<font color='green'>导入Trojan<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "v2ray";
el('.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
el('.alias').value = ssm.ps;
el('.server').value = ssm.add;
el('.server_port').value = ssm.port;
el('.alter_id').value = ssm.aid;
el('.vmess_id').value = ssm.id;
el('.transport').value = ssm.net;
el('.transport').dispatchEvent(event);
if (ssm.net == "tcp") {
try {
el('.http_host').value = ssm.host;
el('.http_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "ws") {
try {
el('.ws_host').value = ssm.host;
el('.ws_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "h2") {
try {
el('.h2_host').value = ssm.host;
el('.h2_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "quic") {
try {
el('.quic_security').value = ssm.securty;
el('.quic_key').value = ssm.key;
} catch (err) { }
}
if (ssm.net == "kcp") {
try {
el('.kcp_guise').value = ssm.type;
} catch (err) { }
}
if (ssm.tls == "tls") {
try {
el('.tls').checked = true;
el('.tls').dispatchEvent(event);
el('.tls_host').value = ssm.host;
} catch (err) { }
}
el('.mux').checked = true;
el('.mux').dispatchEvent(event);
getFlag(ssm.ps, ssm.add, sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>V2ray<%:Configuration Succeeded%></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%>')" />
<span id="<%=self.option%>-status"></span>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,67 @@
<div class="status-bar">
<div class="inner">
<div class="pure-g">
<div class="pure-u-1-2">
<span class="flag"><img src="/luci-static/vssr/flags/4x3/un.svg" class="pure-img"></span> <span
class="status-info">获取中...</span>
</div>
<div class="pure-u-1-2">
<div class="icon-con">
<img src="/luci-static/vssr/img/site_icon1_01.png" class="pure-img i1">
<img src="/luci-static/vssr/img/site_icon1_02.png" class="pure-img i2">
<img src="/luci-static/vssr/img/site_icon1_03.png" class="pure-img i3">
<img src="/luci-static/vssr/img/site_icon1_04.png" class="pure-img i4">
</div>
</div>
</div>
</div>
</div>
<script>
const CHECK_IP_URL = '<%=url([[admin]], [[services]], [[vssr]], [[ip]])%>';
var wW = $(window).width();
function resize() {
wW = $(window).width();
lw = $(".main-left").width()
$(".status-bar").width(wW - lw);
$(".status-bar .flag").width($(".status-bar .flag").height() / 3 * 4);
$(".flag-icon").each(function (index, el) {
if ($(el).height < 60) {
$(el).parent.height(60);
$(el).width(60)
} else {
$(el).width($(el).height());
}
});
}
function wirte_status(data) {
if (data.outboard) {
json = data.outboardip;
country = (json.flag == "tw") ? "中国 台湾" : json.country;
$(".flag img").attr("src", VSSR_ASSETS + "flags/4x3/" + json.flag + ".svg");
$(".status-info").html(data.outboard + "<br>" + country);
}
data.baidu ? $(".i1").attr("src", VSSR_ASSETS + "img/site_icon_01.png") : $(".i1").attr("src", VSSR_ASSETS + "img/site_icon1_01.png");
data.taobao ? $(".i2").attr("src", VSSR_ASSETS + "img/site_icon_02.png") : $(".i2").attr("src", VSSR_ASSETS + "img/site_icon1_02.png");
data.google ? $(".i3").attr("src", VSSR_ASSETS + "img/site_icon_03.png") : $(".i3").attr("src", VSSR_ASSETS + "img/site_icon1_03.png");
data.youtube ? $(".i4").attr("src", VSSR_ASSETS + "img/site_icon_04.png") : $(".i4").attr("src", VSSR_ASSETS + "img/site_icon1_04.png");
}
XHR.poll(5, CHECK_IP_URL, null,
function (x, data) {
wirte_status(data);
}
);
$(document).ready(function () {
resize();
$.getJSON(CHECK_IP_URL, wirte_status);
});
$(window).resize(resize);
</script>

View File

@ -0,0 +1,91 @@
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=202000427-1">
<script src="<%=media%>/js/jquery.min.js"></script>
<div class="pure-g status">
<div class="pure-u-1-4">
<div class="block pure-g">
<div class="pure-u-3-5">
<h4 id="vssr_status"><%:Client%><br /><span class="red"><%:Not Running%></span></h4>
</div>
<div class="pure-u-2-5">
<div class="img-con">
<img src="/luci-static/vssr/img/client.svg">
</div>
</div>
</div>
</div>
<div class="pure-u-1-4">
<div class="block pure-g">
<div class="pure-u-3-5">
<h4 id="game_status"><%:Game Mode%><br /><span class="red"><%:Not Running%></span></h4>
</div>
<div class="pure-u-2-5">
<div class="img-con">
<img src="/luci-static/vssr/img/udp.svg">
</div>
</div>
</div>
</div>
<div class="pure-u-1-4">
<div class="block pure-g">
<div class="pure-u-3-5">
<h4 id="pdnsd_status">PDNSD<br /><span class="red"><%:Not Running%></span></h4>
</div>
<div class="pure-u-2-5">
<div class="img-con">
<img src="/luci-static/vssr/img/pdnsd.svg">
</div>
</div>
</div>
</div>
<div class="pure-u-1-4">
<div class="block pure-g">
<div class="pure-u-3-5">
<h4 id="socks5_status">SOCKS5<br /><span class="red"><%:Not Running%></span></h4>
</div>
<div class="pure-u-2-5">
<div class="img-con">
<img src="/luci-static/vssr/img/socks5.svg">
</div>
</div>
</div>
</div>
</div>
<script>
const VSSR_ASSETS = '/luci-static/vssr/';
const STATUS1_URL = '<%=url([[admin]], [[services]], [[vssr]], [[run]])%>';
const CLIENT = '<%:Client%>';
const GAME_MODE = '<%:Game Mode%>';
const RUNNING = '<%:Running%>';
const NOT_RUNNING = '<%:Not Running%>';
XHR.poll(5, STATUS1_URL, null,
function (x, data) {
if (data) {
if (data.global) {
$("#vssr_status").html(CLIENT + '<br><span class="green">' + RUNNING + '</span>');
} else {
$("#vssr_status").html(CLIENT + '<br><span class="red">' + NOT_RUNNING + '</span>');
}
if (data.game) {
$("#game_status").html(GAME_MODE + '<br><span class="green">' + RUNNING + '</span>');
} else {
$("#game_status").html(GAME_MODE + '<br><span class="red">' + NOT_RUNNING + '</span>');
}
if (data.pdnsd) {
$("#pdnsd_status").html('PDNSD<br><span class="green">' + RUNNING + '</span>');
} else {
$("#pdnsd_status").html('PDNSD<br><span class="red">' + NOT_RUNNING + '</span>');
}
if (data.socks5) {
$("#socks5_status").html('SOCKS5<br><span class="green">' + RUNNING + '</span>');
} else {
$("#socks5_status").html('SOCKS5<br><span class="red">' + NOT_RUNNING + '</span>');
}
}
}
);
//]]>
</script>

View File

@ -0,0 +1,216 @@
<script type="text/javascript" src="/luci-static/vssr/emoji.js"></script>
<fieldset class="cbi-section" id="cbi-apply-vssr1" style="display: none;">
<span class="panel-title">正在切换节点</span>
<img src="/luci-static/resources/icons/loading.gif" alt="载入中" style="vertical-align:middle">
</fieldset>
<!-- tblsection -->
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
<button class="cbi-button cbi-button-check "><%:Ping All Servers%></button><span class="panel-title">总计
<%- print(self.des)-%>个节点</span>
<div class="cbi-section-node">
<%- local count = 0 -%>
<div class="cbi-section-table pure-g p-in5">
<%- local isempty = true
for i, k in ipairs(self:cfgsections()) do
section = k
isempty = false
scope = { valueheader = "cbi/cell_valueheader", valuefooter = "cbi/cell_valuefooter" }
-%>
<div class="pure-u-1-5">
<div class="cbi-section-table-row " id="cbi-<%=self.config%>-<%=section%>">
<div class="loadings hide"></div>
<div class="incon type flag-icon <%=self.flag%>" data-setction="<%=section%>">
<img class="pure-imgw hidden" src="/luci-static/vssr/img/switch.png">
<div class="tp"></div>
</div>
<div class="incon alias"></div>
<%- if self.extedit or self.addremove then -%>
<div class="cbi-section-table-cell">
<div class="host_con"></div>
<a class="cbi-button ssr-button" type="button" value="" onclick="apply_node('<%=section%>')"
alt="<%:Apply%>" title="<%:Apply%>"><span class="icon-ok"></span> <%:Apply%></a>
<%- if self.extedit then -%>
<a class="cbi-button ssr-button " type="button" value="" <%- if type(self.extedit) == "string" then
%> onclick="location.href='<%=self.extedit:format(section)%>'" <%- elseif type(self.extedit) == "function" then
%> onclick="location.href='<%=self:extedit(section)%>'" <%- end
%> alt="<%:Edit%>" title="<%:Edit%>"><span class="icon-edit"></span> <%:Edit%></a>
<%- end; if self.addremove then %>
<button class="cbi-button ssr-button" type="submit" value=""
onclick="this.form.cbi_state = 'del-section'; return true"
name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" /><span
class="icon-delete"></span> <%:Delete%></button>
<%- end -%>
</div>
<%- end -%>
</div>
</div>
<%- end -%>
<%- if isempty then -%>
<div class="cbi-section-table-row">
<div colspan="<%=count%>"><em><br /><%:This section contains no values yet%></em></div>
</div>
<%- end -%>
</div>
<% if self.error then %>
<div class="cbi-section-error">
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
<li><%=pcdata(e):gsub("\n","<br />")%></li>
<%- end end %></ul>
</div>
<% end %>
<%- if self.addremove then -%>
<% if self.template_addremove then include(self.template_addremove) else -%>
<div class="cbi-section-create cbi-tblsection-create">
<% if self.anonymous then %>
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
<% else %>
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
<input type="text" class="cbi-section-create-name"
id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname"
data-optional="true" />
<input class="cbi-button cbi-button-add" type="submit"
onclick="this.form.cbi_state = 'add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
<% if self.invalid_cts then -%>
<br /><%:Invalid%></div>
<%- end %>
<% end %>
</div>
<%- end %>
<%- end -%>
</div>
</fieldset>
<!-- /tblsection -->
<script type="text/javascript">
const CONFIG = '<%=self.config%>';
const CURRENT = '<%=self.current%>';
const CHANGE_NODE_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","change")%>';
const CHECK_PING_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","checkport")%>';
const SWITCH_NODE_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","switch")%>';
var servers = JSON.parse('<%= self.servers%>'.replace(/\t/g, ""));
var ajaxArray = new Array();
//渲染节点列表
$.each(servers, function (i, val) {
var id = '#cbi-' + CONFIG + '-' + val.name;
if (val.name == CURRENT) {
$(id).addClass("fast");
}
val.flag = (val.flag == undefined) ? "un" : val.flag;
$(id).find(".type .tp").text(val.type);
$(id).find(".type").addClass("flag-icon-" + val.flag);
$(id).find(".alias").text(val.alias);
$(id).attr("server", val.server);
$(id).attr("server_port", val.server_port);
if (val.switch_enable == "1") {
$(id).find(".pure-imgw").removeClass("hidden");
} else {
$(id).find(".pure-imgw").addClass("hidden");
}
});
//转换节点名称的Emoji显示
$(".alias").emoji();
//适配方框的大小
$(".flag-icon").each(function (index, el) {
if ($(el).height < 60) {
$(el).parent.height(60);
$(el).width(60)
} else {
$(el).width($(el).height());
}
});
//切换节点
function apply_node(node) {
$("#cbi-apply-vssr1").show(); //显示应用中
$.each(ajaxArray, function (n, value) { value.abort(); }) //中断所有的ajax请求
$.get(CHANGE_NODE_URL, { set: node },
function (data, status) {
$("#cbi-apply-vssr1").hide(); //隐藏应用中
if (data.status) {
var id = '#cbi-<%=self.config%>-' + node;
$(".cbi-section-table-row").removeClass("fast");
$(id).addClass("fast");
}
});
}
//设定自动切换台
$(".incon").click(function () {
$.each(ajaxArray, function (n, value) { value.abort(); }) //中断所有的ajax请求
$node_switch_icon = $(this).find(".pure-imgw");
$node_target = $(this).attr("data-setction");
$.post(SWITCH_NODE_URL, { node: $node_target }, function (data) {
if(data.status){
if(data.switch){
$node_switch_icon.removeClass("hidden");
}else{
$node_switch_icon.addClass("hidden");
}
}else{
alert("请求出错!")
}
}, "json");
})
//检测所有节点延迟
function check() {
$(".host_con").html("");
ajaxArray = [];
$(".pure-u-1-5").each(function () {
host = $(this).find(".cbi-section-table-row ").attr("server");
port = $(this).find(".cbi-section-table-row ").attr("server_port");
$(".host_con").text("");
check_port(host, port, this);
});
}
//检测单个节点延迟
function check_port(hosts, ports, target) {
$.ajaxSettings.async = true;
var axhr = $.get(CHECK_PING_URL, { host: hosts, port: ports },
function (data, status) {
var host_con = $(target).find(".host_con");
host_con.removeClass("fast");
host_con.removeClass("middle");
host_con.removeClass("slow");
host_con.removeClass("nopass");
if (data.ret == 1) {
if (data.used == "") {
host_con.addClass("nopass");
host_con.text("Error");
} else if (data.used <= 80) {
host_con.addClass("fast");
host_con.text(data.used + "ms");
} else if (data.used > 80 && data.used <= 200) {
host_con.addClass("middle");
host_con.text(data.used + "ms");
} else if (data.used > 200) {
host_con.addClass("slow");
host_con.text(data.used + "ms");
}
} else {
host_con.addClass("nopass");
host_con.text("Error");
}
});
ajaxArray.push(axhr);
}
$(document).ready(function () {
setTimeout(function () { check(); }, 500); //延迟500MS开始检测
$(".cbi-page-actions").hide(); //隐藏底部保存提交按钮
$(".cbi-button-check").click(function () {
check();
return false;
});
});
</script>

View File

@ -0,0 +1,108 @@
<%+cbi/valueheader%>
<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">
<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">
const SAVE_SUBSCRIBE_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","subscribe")%>';
const SERVERS_URL = '<%=luci.dispatcher.build_url("admin", "services", "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")) {
var auto_update = "1";
} else {
var auto_update = "0";
}
var auto_update_time = $("[name='" + prefix + ".auto_update_time']").val();
var subscribe_url = [];
$("[name='" + prefix + ".subscribe_url']").each(function () {
if ($(this).val() != "") {
subscribe_url.push($(this).val());
}
});
if ($("[name='" + prefix + ".proxy']").is(":checked")) {
var proxy = "1";
} else {
var proxy = "0";
}
var data = {
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: SAVE_SUBSCRIBE_URL,
dataType: "json",
data: data,
success: function (d) {
if (d.error == 0) {
update_subscribe();
} else {
alert("请至少填写一个订阅链接");
}
}
});
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);
}
});
}
</script>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,649 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "vssr"
msgstr "Hello World"
msgid "Client"
msgstr "客户端"
msgid "Enable"
msgstr "启用"
msgid "Disable"
msgstr "停用"
msgid "Log"
msgstr "日志"
msgid "Running"
msgstr "运行中"
msgid "Not Running"
msgstr "未运行"
msgid "Game Mode"
msgstr "游戏模式"
msgid "Global Setting"
msgstr "全局设置"
msgid "Global Server"
msgstr "全局服务器"
msgid "vssr SOCK5 Proxy is running"
msgstr "vssr SOCK5代理运行中"
msgid "UDP Relay Server"
msgstr "UDP中继服务器"
msgid "Same as Global Server"
msgstr "与全局服务器相同"
msgid "Servers Setting"
msgstr "服务器配置"
msgid "Alias(optional)"
msgstr "别名(可选)"
msgid "Onetime Authentication"
msgstr "一次验证"
msgid "Server Address"
msgstr "服务器地址"
msgid "Server Port"
msgstr "服务器端口"
msgid "Local Port"
msgstr "本地端口"
msgid "Connection Timeout"
msgstr "连接超时"
msgid "Password"
msgstr "密码"
msgid "Encrypt Method"
msgstr "加密方式"
msgid "Protocol"
msgstr "传输协议"
msgid "Protocol param(optional)"
msgstr "传输协议参数(可选)"
msgid "Obfs"
msgstr "混淆插件"
msgid "Obfs param(optional)"
msgstr "混淆参数(可选)"
msgid "Enable Tunnel(DNS)"
msgstr "启用隧道DNS转发"
msgid "Tunnel Port"
msgstr "隧道DNS本地端口"
msgid "Forwarding Tunnel"
msgstr "隧道DNS转发地址"
msgid "Access Control"
msgstr "访问控制"
msgid "Interfaces - WAN"
msgstr "接口 - WAN"
msgid "Bypassed IP List"
msgstr "被忽略IP列表"
msgid "NULL - As Global Proxy"
msgstr "留空 - 作为全局代理"
msgid "Bypassed IP"
msgstr "额外被忽略IP"
msgid "Forwarded IP"
msgstr "强制走代理IP"
msgid "Interfaces - LAN"
msgstr "接口 - LAN"
msgid "LAN Access Control"
msgstr "内网访问控制"
msgid "Allow listed only"
msgstr "仅允许列表内"
msgid "Allow all except listed"
msgstr "仅允许列表外"
msgid "LAN Host List"
msgstr "内网主机列表"
msgid "SSR Client"
msgstr "基本设置"
msgid "SSR Server"
msgstr "服务端"
msgid "vssr Server"
msgstr "vssr 服务端"
msgid "vssr Server is running"
msgstr "vssr 服务端运行中"
msgid "vssr Server is not running"
msgstr "vssr 服务端未运行"
msgid "Enable Server"
msgstr "启动服务端"
msgid "Server Setting"
msgstr "服务端配置"
msgid "KcpTun Enable"
msgstr "KcpTun 启用"
msgid "bin:/usr/bin/kcptun-client"
msgstr "二进制文件:/usr/bin/kcptun-client"
msgid "KcpTun Port"
msgstr "KcpTun 端口"
msgid "KcpTun Param"
msgstr "KcpTun 参数"
msgid "KcpTun Password"
msgstr "KcpTun 密码"
msgid "Haven't a Kcptun executable file"
msgstr "不存在Kcptun可执行文件请下载Kcptun可执行文件并改名放入/usr/bin/kcptun-client"
msgid "Not a Kcptun executable file"
msgstr "Kcptun可执行文件格式不正确请确认是否正确下载了路由器对应的可执行文件"
msgid "Enable Process Monitor"
msgstr "启用进程监控"
msgid "Edit vssr Server"
msgstr "编辑服务器配置"
msgid "Alias"
msgstr "别名"
msgid "V2ray SOCKS5 Proxy"
msgstr "V2ray SOCKS5代理"
msgid "Server"
msgstr "服务器"
msgid "TCP Fast Open"
msgstr "TCP快速打开"
msgid "Status"
msgstr "状态"
msgid "Unknown"
msgstr "未知"
msgid "Running Status"
msgstr "运行状态"
msgid "Global Client"
msgstr "TCP透明代理"
msgid "Global SSR Server"
msgstr "SSR服务端"
msgid "DNS Tunnel"
msgstr "DNS 隧道"
msgid "IPK Version"
msgstr "IPK 版本号"
msgid "KcpTun Version"
msgstr "KcpTun 版本号"
msgid "Not exist"
msgstr "未安装可执行文件"
msgid "IPK Installation Time"
msgstr "IPK 安装时间"
msgid "Project"
msgstr "项目地址"
msgid "Not Running"
msgstr "未运行"
msgid "Running"
msgstr "运行中"
msgid "Enable GFW mode"
msgstr "启用 GFW 模式"
msgid "Running Mode"
msgstr "运行模式"
msgid "IP Route Mode"
msgstr "绕过中国大陆IP模式"
msgid "GFW List Mode"
msgstr "GFW列表模式"
msgid "Global Mode"
msgstr "全局模式"
msgid "Oversea Mode"
msgstr "海外用户回国模式"
msgid "Router Proxy"
msgstr "路由器访问控制"
msgid "Normal Proxy"
msgstr "正常代理"
msgid "Bypassed Proxy"
msgstr "不走代理"
msgid "Forwarded Proxy"
msgstr "强制走代理"
msgid "UDP Relay"
msgstr "UDP中继"
msgid "Google Connectivity"
msgstr "【谷歌】连通性检查"
msgid "Baidu Connectivity"
msgstr "【百度】连通性检查"
msgid "No Check"
msgstr "未检查"
msgid "Check"
msgstr "检查"
msgid "Connect OK"
msgstr "连接正常"
msgid "Connect Error"
msgstr "连接错误"
msgid "Check..."
msgstr "正在检查.."
msgid "Proxy Check"
msgstr "代理检查"
msgid "GFW List Data"
msgstr "【GFW列表】数据库"
msgid "China IP Data"
msgstr "【国内IP段】数据库"
msgid "Records"
msgstr "条记录"
msgid "Refresh Data"
msgstr "更新数据库"
msgid "Refresh..."
msgstr "正在更新,请稍候.."
msgid "Refresh OK!"
msgstr "更新成功!"
msgid "Refresh Error!"
msgstr "更新失败!"
msgid "No new data!"
msgstr "你已经是最新数据,无需更新!"
msgid "Total Records:"
msgstr "新的总纪录数:"
msgid "Check Server Port"
msgstr "【服务器端口】检查"
msgid "Check Connect"
msgstr "检查连通性"
msgid "Check Server"
msgstr "检查服务器"
msgid "Auto Switch"
msgstr "自动切换"
msgid "Enable Auto Switch"
msgstr "启用自动切换"
msgid "Switch check cycly(second)"
msgstr "自动切换检查周期(秒)"
msgid "Check timout(second)"
msgstr "切换检查超时时间(秒)"
msgid "Check Try Count"
msgstr "切换检查重试次数"
msgid "Enable Process Deamon"
msgstr "启用进程自动守护"
msgid "Advertising Data"
msgstr "【广告屏蔽】数据库"
msgid "DNS Server IP and Port"
msgstr "DNS服务器地址和端口"
msgid "Resolve Dns Mode"
msgstr "DNS解析方式"
msgid "Use SSR DNS Tunnel"
msgstr "使用SSR-DNS隧道"
msgid "Use Pdnsd"
msgstr "使用Pdnsd"
msgid "Use Other DNS Tunnel(Need to install)"
msgstr "使用其他DNS转发(需要自己安装)"
msgid "Import SSR"
msgstr "导入ssr配置信息"
msgid "Export SSR"
msgstr "导出ssr配置信息"
msgid "Import SSR successfully."
msgstr "成功导入SSR。"
msgid "Invalid SSR format."
msgstr "无效的SSR格式。"
msgid "User cancelled."
msgstr "用户已取消。"
msgid "Paste ssr url here"
msgstr "在此处粘贴ssr://网址"
msgid "Unable to copy SSR to clipboard."
msgstr "无法复制SSR网址到剪贴板。"
msgid "Copy SSR to clipboard successfully."
msgstr "成功复制SSR网址到剪贴板。"
msgid "Servers Manage"
msgstr "服务器管理"
msgid "Auto Update"
msgstr "自动更新"
msgid "Through proxy update"
msgstr "通过代理更新"
msgid "GFW List"
msgstr "GFW列表"
msgid "Basic Settings [SS(R)|V2ray|Trojan]"
msgstr "基本设置 [SS(R)|V2ray|Trojan]"
msgid "Main Server"
msgstr "主服务器"
msgid "Anti-pollution DNS Server"
msgstr "访问国外域名DNS服务器"
msgid "Use Pdnsd tcp query and cache"
msgstr "使用PDNSD TCP查询并缓存"
msgid "DNS Server IP:Port"
msgstr "DNS服务器 IP:Port"
msgid "Update time (every day)"
msgstr "更新时间 (每天)"
msgid "Auto Update Server subscription, GFW list and CHN route"
msgstr "自动更新服务器订阅、GFW列表和 CHN路由表"
msgid "Subscribe URL"
msgstr "SSR/V2RAY订阅URL地址"
msgid "Update"
msgstr "更新"
msgid "Save And Start Subscribe"
msgstr "保存并开始订阅"
msgid "Server Count"
msgstr "节点数量"
msgid "IP black-and-white list"
msgstr "黑白名单"
msgid "WAN IP AC"
msgstr "广域网访问控制"
msgid "WAN White List IP"
msgstr "不走代理的广域网 IP"
msgid "WAN Force Proxy IP"
msgstr "强制走代理的广域网 IP"
msgid "LAN Bypassed Host List"
msgstr "不走代理的局域网LAN IP"
msgid "LAN Force Proxy Host List"
msgstr "全局代理的LAN IP"
msgid "Router Self AC"
msgstr "路由器自身代理设置"
msgid "Router Self Proxy"
msgstr "路由器自身代理方式"
msgid "Normal Proxy"
msgstr "跟随全局设置"
msgid "Bypassed Proxy"
msgstr "不走代理"
msgid "Forwarded Proxy"
msgstr "全局代理"
msgid "GFW Custom List"
msgstr "GFW 用户自定义列表"
msgid "Please refer to the following writing"
msgstr "每行一个域名,无需写前面的 HTTP(S):// ,提交后即时生效"
msgid "Servers subscription and manage"
msgstr "服务器节点订阅管理支持SSR/V2ray 订阅)"
msgid "Subscription"
msgstr "节点订阅"
msgid "Through proxy update list, Not Recommended"
msgstr "通过路由器自身代理更新订阅(不推荐)"
msgid "LAN IP AC"
msgstr "局域网访问控制"
msgid "Game Mode UDP Server"
msgstr "游戏模式UDP中继服务器"
msgid "Game Mode UDP Relay"
msgstr "游戏模式UDP中继"
msgid "Server failsafe auto swith settings"
msgstr "服务器节点故障自动切换设置"
msgid "Delete all severs"
msgstr "删除所有服务器"
msgid "Severs Nodes"
msgstr "节点列表"
msgid "Use Local DNS Service listen port 5335"
msgstr "使用本机端口为5335的DNS服务"
msgid "Server Node Type"
msgstr "服务器节点类型"
msgid "Using incorrect encryption mothod may causes service fail to start"
msgstr "输入不正确的参数组合可能会导致服务无法启动"
msgid "Game Mode Host List"
msgstr "增强游戏模式客户端LAN IP"
msgid "Multi Threads Option"
msgstr "多线程并发转发"
msgid "Auto Threads"
msgstr "自动CPU线程数"
msgid "1 Thread"
msgstr "单线程"
msgid "2 Threads"
msgstr "2 线程"
msgid "4 Threads"
msgstr "4 线程"
msgid "8 Threads"
msgstr "8 线程"
msgid "Proxy Ports"
msgstr "需要代理的端口"
msgid "All Ports"
msgstr "所有端口(默认)"
msgid "Only Common Ports"
msgstr "仅常用端口不走P2P流量到代理"
msgid "Ping Latency"
msgstr "Ping延迟"
msgid "Bypass Domain List"
msgstr "直连域名"
msgid "Black Domain List"
msgstr "代理域名"
msgid "Socks user"
msgstr "用户名"
msgid "Socks pass"
msgstr "密码"
msgid "Enable Servers"
msgstr "开启服务"
msgid "Enable Auth"
msgstr "开启验证"
msgid "Subscribing,Please do not refresh!"
msgstr "请勿刷新本页面,正在订阅中 ..."
msgid "Apply"
msgstr "应用"
msgid "Edit"
msgstr "修改"
msgid "Delete"
msgstr "删除"
msgid "Ping All Servers"
msgstr "Ping 所有节点"
msgid "Check Ip And Access"
msgstr "Ip地址检查"
msgid "Test From Internal"
msgstr "从国内测试"
msgid "Test From Abroad"
msgstr "从国外测试"
msgid "Test From Google"
msgstr "从谷歌测试"
msgid "Baidu"
msgstr "百度搜索"
msgid "Connection OK"
msgstr "访问正常"
msgid "Cannot Access"
msgstr "无法访问"
msgid "Unavailable"
msgstr "无法获取"
msgid "Connection Timeout"
msgstr "连接超时"
msgid "Update Setting"
msgstr "更新设置"
msgid "Paste Node Link Here"
msgstr "在这里粘贴配置链接"
msgid "User Cancel"
msgstr "用户取消"
msgid "Invalid Format"
msgstr "无效的格式"
msgid "Import Configuration"
msgstr "导入配置信息"
msgid "Import"
msgstr "导入"
msgid "Configuration Succeeded"
msgstr "配置信息成功"
msgid "Import Configuration"
msgstr "导入配置信息"
msgid "Configuration Url"
msgstr "配置链接"
msgid "Open v2ray split-flow"
msgstr "开启V2ray分流"
msgid "When open v2ray split-flow,your main server must be a v2ray server"
msgstr "当使用v2ray分流功能时 主服务器必须为V2ray"
msgid "Youtube Proxy"
msgstr "Youtube 代理"
msgid "TaiWan Video Proxy"
msgstr "台湾视频服务代理"
msgid "Netflix Proxy"
msgstr "Netflix 代理"
msgid "Diseny+ Proxy"
msgstr "Diseny+ 代理"
msgid "Prime Video Proxy"
msgstr "Prime Video 代理"
msgid "adblock settings"
msgstr "广告屏蔽设置"
msgid "Enable adblock"
msgstr "开启广告屏蔽"
msgid "Licence"
msgstr "许可"
msgid "Area"
msgstr "区域"

View File

@ -0,0 +1,178 @@
########################################################
#
# RELEASE NOTES
#
########################################################
########################################################
//
// 1.11 2020-07-19
// bug修复
//
########################################################
Updates
- UPD: 弃用curl 改用 wget 来获取远端ip
- UPD: 修复了不能自动更新gfw list的问题
########################################################
//
// 1.10 2020-07-14
// bug修复
//
########################################################
Updates
- UPD: 修复了trojan链接无法导入的问题
- UPD: 修改部分中文翻译
- UPD: 更新了域名和ip名单
########################################################
//
// 1.09 2020-04-27
// bug修复
//
########################################################
Updates
- UPD: 增加了Trojan 订阅支持
- UPD: 底部应用图标替换成透明背景PNG 以适应深色模式
- UPD: 增加试验脚本以便于可以和SSR+同时编译
########################################################
//
// 1.08 2020-03-09
// bug修复
//
########################################################
Updates
- UPD: 修改了主服务图标
- UPD: 去除创建服务配置时的域名转换ip
Bug fixes
- FIX: 修复了Sock5默认开启的问题。
########################################################
//
// 1.07 2020-02-20
// 小幅度优化
//
########################################################
Updates
- UPD: 所有订阅节点默认全部取消自动切换。在节点列表页面点击国旗手动将节点添加为自动切换节点。优点是可以按照自己的喜好设定2-3个自动切换节点这样自动切换不会乱跳。
- UPD: 优化了一些页面跳转的逻辑。
- UPD: 再次优化了部分获取国旗部分的代码,现在可以识别部分节点名称中的国家名。
Bug fixes
- FIX: 修复了V2ray分流中的Netflix不能分流的问题。
########################################################
//
// 1.06 2020-02-17
// 这是还是一个中期代码精简优化的版本无新功能如果当前使用无bug不建议更新。
//
########################################################
Updates
- UPD: 使用lua-maxminddb(18k) 库 取代 pyhton-maxminddb(20k) 同时也不再需要依赖 pyhton-base(1.07Mb) 和python-light(1.61Mb) 大幅度减少固件体积,同时解析速度得到大幅提升。
- UPD: 优化了节点列表的一些显示问题。
- UPD: 优化了部分获取国旗部分的代码。
- UPD: 删除一些不必要的国家的国旗,进一步节省空间。
Bug fixes
- FIX: 修复了使用GFW 模式时无法正确获取代理IP的问题
########################################################
//
// 1.05 2020-02-11
// 这是一个中期代码精简优化的版本无新功能如果当前使用无bug不建议更新。
//
########################################################
Updates
- UPD: 删除了一些不必要的文件。
- UPD: 优化了部分前端模板的命名。
- UPD: 优化了大部分的前端界面中的Javascript代码[未来还需要进一步的优化]。
Bug fixes
- FIX: 修复了Vmess链接导入时前端js出错无法获得国家代码的Bug。
- FIX: 修复了一些文字翻译的错误。
########################################################
//
// 1.05 2020-02-10
//
########################################################
New features
- NEW: 增加了Trojan支持
- NEW: 增加了广告屏蔽的功能
Updates
- UPD: 更新了gfwlist chinaip 和 ad conf的更新代码。
- UPD: 当在节点列表应用新的节点时会先中断所有的ping以保证切换节点优先。
########################################################
//
// 1.04 2020-02-09
//
########################################################
Updates
- UPD: 增加了V2ray简易分流模式现在当你在使用v2ray作为主节点的时候可以选择为几个主流的视频插件指定特定的节点来达到分流的目的。目前可以分流的视频有Youtube 一些台湾的视频平台 Netflix Diseny+ Prime Video
- UPD: 更新了GeoIp2 库
- UPD: 同步更新lean源的订阅代码
- UPD: 增加了一个Licence页面用来显示GEOIP2的授权
Bug fixes
- FIX: 修复一些节点列表的显示问题。
########################################################
//
// 1.02 2019-12-14
//
########################################################
New features
- NEW: 给Hello World 增加了IP状态显示在页面底部 左边显示当前节点国旗 ip 和中文国家 右边 是四个网站的访问状态 可以访问是彩色 不能访问是灰色。
Updates
- UPD: 基于lean ssr+ 全新修改的Vssr更名为Hello World 主要做了很多的修改,同时感谢插件原作者所做出的的努力和贡献!
- UPD: 节点列表支持国旗显示 TW节点为五星红旗 节点列表页面 打开自动ping.
- UPD: 优化了在节点列表页面点击应用后节点切换的速度。同时也优化了自动切换的速度。
- UPD: 将节点订阅转移至 高级设置 请悉知 由于需要获取ip的国家code 新的订阅速度可能会比原来慢一点点 x86无影响。
- UPD: 去掉了ss插件ss节点将通过v2ray进行代理支持ss的v2ray plugin可能会遇到老的加密方式不兼容的情况。
- UPD: 优化了国旗匹配方法在部分带有emoji counrty code的节点名称中 优先使用 emoji code 匹配国旗。
########################################################==
//
// END RELEASE NOTES
//
########################################################==

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
api.ip.sb

View File

@ -0,0 +1,53 @@
91smartyun.pt
adobe.com
amazonaws.com
ampproject.org
apple.news
aws.amazon.com
azureedge.net
backpackers.com.tw
bitfinex.com
buzzfeed.com
clockwise.ee
cloudfront.net
coindesk.com
coinsquare.io
cryptocompare.com
dropboxstatic.com
eurecom.fr
gdax.com
github.com
kknews.cc
nutaq.com
openairinterface.org
skype.com
sublimetext.com
textnow.com
textnow.me
trouter.io
uploaded.net
whatsapp.com
whatsapp.net
wsj.net
google.com
google.com.hk
gstatic.com
googleusercontent.com
googlepages.com
googlevideo.com
googlecode.com
googleapis.com
googlesource.com
googledrive.com
ggpht.com
youtube.com
youtu.be
ytimg.com
twitter.com
facebook.com
fastly.net
akamai.net
akamaiedge.net
akamaihd.net
edgesuite.net
edgekey.net

View File

@ -0,0 +1,42 @@
config global
option tunnel_forward '8.8.4.4:53'
option tunnel_address '0.0.0.0'
option run_mode 'router'
option pdnsd_enable '1'
option monitor_enable '1'
option global_server 'nil'
option enable_switch '0'
option switch_timeout '5'
option switch_time '667'
option switch_try_count '3'
option adblock '0'
config socks5_proxy
option enable_server '0'
option local_port '1080'
option local_address '0.0.0.0'
option enable_auth '1'
option Socks_user 'user'
option Socks_pass 'password'
config access_control
option wan_bp_list '/etc/china_ssr.txt'
option lan_ac_mode 'b'
option router_proxy '1'
list wan_fw_ips '149.154.160.0/20'
list wan_fw_ips '67.198.55.0/24'
list wan_fw_ips '91.108.4.0/22'
list wan_fw_ips '91.108.56.0/22'
list wan_fw_ips '109.239.140.0/24'
config server_global
option enable_server '0'
config server_subscribe
option proxy '0'
option auto_update_time '2'
option auto_update '1'

View File

@ -0,0 +1,192 @@
server=/v.youku.com/127.0.0.1#5335
server=/api.youku.com/127.0.0.1#5335
server=/v2.tudou.com/127.0.0.1#5335
server=/www.tudou.com/127.0.0.1#5335
server=/s.plcloud.music.qq.com/127.0.0.1#5335
server=/i.y.qq.com/127.0.0.1#5335
server=/hot.vrs.sohu.com/127.0.0.1#5335
server=/live.tv.sohu.com/127.0.0.1#5335
server=/pad.tv.sohu.com/127.0.0.1#5335
server=/my.tv.sohu.com/127.0.0.1#5335
server=/hot.vrs.letv.com/127.0.0.1#5335
server=/data.video.qiyi.com/127.0.0.1#5335
server=/cache.video.qiyi.com/127.0.0.1#5335
server=/cache.vip.qiyi.com/127.0.0.1#5335
server=/vv.video.qq.com/127.0.0.1#5335
server=/tt.video.qq.com/127.0.0.1#5335
server=/ice.video.qq.com/127.0.0.1#5335
server=/tjsa.video.qq.com/127.0.0.1#5335
server=/a10.video.qq.com/127.0.0.1#5335
server=/xyy.video.qq.com/127.0.0.1#5335
server=/vcq.video.qq.com/127.0.0.1#5335
server=/vsh.video.qq.com/127.0.0.1#5335
server=/vbj.video.qq.com/127.0.0.1#5335
server=/bobo.video.qq.com/127.0.0.1#5335
server=/flvs.video.qq.com/127.0.0.1#5335
server=/bkvv.video.qq.com/127.0.0.1#5335
server=/info.zb.qq.com/127.0.0.1#5335
server=/geo.js.kankan.xunlei.com/127.0.0.1#5335
server=/web-play.pptv.com/127.0.0.1#5335
server=/web-play.pplive.cn/127.0.0.1#5335
server=/dyn.ugc.pps.tv/127.0.0.1#5335
server=/v.pps.tv/127.0.0.1#5335
server=/inner.kandian.com/127.0.0.1#5335
server=/ipservice.163.com/127.0.0.1#5335
server=/so.open.163.com/127.0.0.1#5335
server=/zb.s.qq.com/127.0.0.1#5335
server=/ip.kankan.xunlei.com/127.0.0.1#5335
server=/vxml.56.com/127.0.0.1#5335
server=/music.sina.com.cn/127.0.0.1#5335
server=/play.baidu.com/127.0.0.1#5335
server=/v.iask.com/127.0.0.1#5335
server=/tv.weibo.com/127.0.0.1#5335
server=/wtv.v.iask.com/127.0.0.1#5335
server=/video.sina.com.cn/127.0.0.1#5335
server=/www.yinyuetai.com/127.0.0.1#5335
server=/api.letv.com/127.0.0.1#5335
server=/live.gslb.letv.com/127.0.0.1#5335
server=/static.itv.letv.com/127.0.0.1#5335
server=/ip.apps.cntv.cn/127.0.0.1#5335
server=/vdn.apps.cntv.cn/127.0.0.1#5335
server=/vdn.live.cntv.cn/127.0.0.1#5335
server=/vip.sports.cntv.cn/127.0.0.1#5335
server=/a.play.api.3g.youku.com/127.0.0.1#5335
server=/i.play.api.3g.youku.com/127.0.0.1#5335
server=/api.3g.youku.com/127.0.0.1#5335
server=/tv.api.3g.youku.com/127.0.0.1#5335
server=/play.api.3g.youku.com/127.0.0.1#5335
server=/play.api.3g.tudou.com/127.0.0.1#5335
server=/tv.api.3g.tudou.com/127.0.0.1#5335
server=/api.3g.tudou.com/127.0.0.1#5335
server=/api.tv.sohu.com/127.0.0.1#5335
server=/access.tv.sohu.com/127.0.0.1#5335
server=/iface.iqiyi.com/127.0.0.1#5335
server=/iface2.iqiyi.com/127.0.0.1#5335
server=/cache.m.iqiyi.com/127.0.0.1#5335
server=/dynamic.app.m.letv.com/127.0.0.1#5335
server=/dynamic.meizi.app.m.letv.com/127.0.0.1#5335
server=/dynamic.search.app.m.letv.com/127.0.0.1#5335
server=/dynamic.live.app.m.letv.com/127.0.0.1#5335
server=/listso.m.areainfo.ppstream.com/127.0.0.1#5335
server=/epg.api.pptv.com/127.0.0.1#5335
server=/play.api.pptv.com/127.0.0.1#5335
server=/m.letv.com/127.0.0.1#5335
server=/interface.bilibili.com/127.0.0.1#5335
server=/3g.music.qq.com/127.0.0.1#5335
server=/mqqplayer.3g.qq.com/127.0.0.1#5335
server=/proxy.music.qq.com/127.0.0.1#5335
server=/proxymc.qq.com/127.0.0.1#5335
server=/ip2.kugou.com/127.0.0.1#5335
server=/ip.kugou.com/127.0.0.1#5335
server=/client.api.ttpod.com/127.0.0.1#5335
server=/mobi.kuwo.cn/127.0.0.1#5335
server=/mobilefeedback.kugou.com/127.0.0.1#5335
server=/tingapi.ting.baidu.com/127.0.0.1#5335
server=/music.baidu.com/127.0.0.1#5335
server=/serviceinfo.sdk.duomi.com/127.0.0.1#5335
server=/music.163.com/127.0.0.1#5335
server=/www.xiami.com/127.0.0.1#5335
server=/spark.api.xiami.com/127.0.0.1#5335
server=/iplocation.geo.qiyi.com/127.0.0.1#5335
server=/sns.video.qq.com/127.0.0.1#5335
server=/v5.pc.duomi.com/127.0.0.1#5335
server=/tms.is.ysten.com/127.0.0.1#5335
server=/internal.check.duokanbox.com/127.0.0.1#5335
server=/openapi.youku.com/127.0.0.1#5335
server=/y.qq.com/127.0.0.1#5335
ipset=/v.youku.com/oversea
ipset=/api.youku.com/oversea
ipset=/v2.tudou.com/oversea
ipset=/www.tudou.com/oversea
ipset=/s.plcloud.music.qq.com/oversea
ipset=/i.y.qq.com/oversea
ipset=/hot.vrs.sohu.com/oversea
ipset=/live.tv.sohu.com/oversea
ipset=/pad.tv.sohu.com/oversea
ipset=/my.tv.sohu.com/oversea
ipset=/hot.vrs.letv.com/oversea
ipset=/data.video.qiyi.com/oversea
ipset=/cache.video.qiyi.com/oversea
ipset=/cache.vip.qiyi.com/oversea
ipset=/vv.video.qq.com/oversea
ipset=/tt.video.qq.com/oversea
ipset=/ice.video.qq.com/oversea
ipset=/tjsa.video.qq.com/oversea
ipset=/a10.video.qq.com/oversea
ipset=/xyy.video.qq.com/oversea
ipset=/vcq.video.qq.com/oversea
ipset=/vsh.video.qq.com/oversea
ipset=/vbj.video.qq.com/oversea
ipset=/bobo.video.qq.com/oversea
ipset=/flvs.video.qq.com/oversea
ipset=/bkvv.video.qq.com/oversea
ipset=/info.zb.qq.com/oversea
ipset=/geo.js.kankan.xunlei.com/oversea
ipset=/web-play.pptv.com/oversea
ipset=/web-play.pplive.cn/oversea
ipset=/dyn.ugc.pps.tv/oversea
ipset=/v.pps.tv/oversea
ipset=/inner.kandian.com/oversea
ipset=/ipservice.163.com/oversea
ipset=/so.open.163.com/oversea
ipset=/zb.s.qq.com/oversea
ipset=/ip.kankan.xunlei.com/oversea
ipset=/vxml.56.com/oversea
ipset=/music.sina.com.cn/oversea
ipset=/play.baidu.com/oversea
ipset=/v.iask.com/oversea
ipset=/tv.weibo.com/oversea
ipset=/wtv.v.iask.com/oversea
ipset=/video.sina.com.cn/oversea
ipset=/www.yinyuetai.com/oversea
ipset=/api.letv.com/oversea
ipset=/live.gslb.letv.com/oversea
ipset=/static.itv.letv.com/oversea
ipset=/ip.apps.cntv.cn/oversea
ipset=/vdn.apps.cntv.cn/oversea
ipset=/vdn.live.cntv.cn/oversea
ipset=/vip.sports.cntv.cn/oversea
ipset=/a.play.api.3g.youku.com/oversea
ipset=/i.play.api.3g.youku.com/oversea
ipset=/api.3g.youku.com/oversea
ipset=/tv.api.3g.youku.com/oversea
ipset=/play.api.3g.youku.com/oversea
ipset=/play.api.3g.tudou.com/oversea
ipset=/tv.api.3g.tudou.com/oversea
ipset=/api.3g.tudou.com/oversea
ipset=/api.tv.sohu.com/oversea
ipset=/access.tv.sohu.com/oversea
ipset=/iface.iqiyi.com/oversea
ipset=/iface2.iqiyi.com/oversea
ipset=/cache.m.iqiyi.com/oversea
ipset=/dynamic.app.m.letv.com/oversea
ipset=/dynamic.meizi.app.m.letv.com/oversea
ipset=/dynamic.search.app.m.letv.com/oversea
ipset=/dynamic.live.app.m.letv.com/oversea
ipset=/listso.m.areainfo.ppstream.com/oversea
ipset=/epg.api.pptv.com/oversea
ipset=/play.api.pptv.com/oversea
ipset=/m.letv.com/oversea
ipset=/interface.bilibili.com/oversea
ipset=/3g.music.qq.com/oversea
ipset=/mqqplayer.3g.qq.com/oversea
ipset=/proxy.music.qq.com/oversea
ipset=/proxymc.qq.com/oversea
ipset=/ip2.kugou.com/oversea
ipset=/ip.kugou.com/oversea
ipset=/client.api.ttpod.com/oversea
ipset=/mobi.kuwo.cn/oversea
ipset=/mobilefeedback.kugou.com/oversea
ipset=/tingapi.ting.baidu.com/oversea
ipset=/music.baidu.com/oversea
ipset=/serviceinfo.sdk.duomi.com/oversea
ipset=/music.163.com/oversea
ipset=/www.xiami.com/oversea
ipset=/spark.api.xiami.com/oversea
ipset=/iplocation.geo.qiyi.com/oversea
ipset=/sns.video.qq.com/oversea
ipset=/v5.pc.duomi.com/oversea
ipset=/tms.is.ysten.com/oversea
ipset=/internal.check.duokanbox.com/oversea
ipset=/openapi.youku.com/oversea
ipset=/y.qq.com/oversea

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,534 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2017 openwrt-ssr
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
# Copyright (C) 2018 lean <coolsnowwolf@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
START=90
STOP=15
SERVICE_DAEMONIZE=1
NAME=vssr
EXTRA_COMMANDS=rules
#定义配置文件名称
CONFIG_FILE=/var/etc/${NAME}_t.json
CONFIG_UDP_FILE=/var/etc/${NAME}_u.json
CONFIG_SOCK5_FILE=/var/etc/${NAME}_s.json
server_count=0
redir_tcp=0
redir_udp=0
tunnel_enable=0
local_enable=0
kcp_enable_flag=0
kcp_flag=0
pdnsd_enable_flag=0
switch_enable=0
switch_server=$1
MAXFD=32768
CRON_FILE=/etc/crontabs/root
threads=1
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
add_cron() {
sed -i '/vssr.log/d' $CRON_FILE && echo '0 1 * * * echo "" > /tmp/vssr.log' >> $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
}
del_cron() {
sed -i '/vssr/d' $CRON_FILE
sed -i '/vssr.log/d' $CRON_FILE
/etc/init.d/cron restart
}
run_mode=$(uci_get_by_type global run_mode)
gen_config_file() {
local hostip=$(uci_get_by_name $1 server)
[ $2 = "0" -a $kcp_flag = "1" ] && hostip="127.0.0.1"
if [ $2 = "0" ]; then
re_type="tcp"
config_file=$CONFIG_FILE
server_obj=$GLOBAL_SERVER
elif [ $2 = "1" ]; then
re_type="udp"
config_file=$CONFIG_UDP_FILE
server_obj=$UDP_RELAY_SERVER
fi
if [ $(uci_get_by_name $1 fast_open 0) = "1" ]; then
fastopen="true"
else
fastopen="false"
fi
local stype=$(uci_get_by_name $1 type)
local port=$(uci_get_by_name $1 local_port)
if [ "$stype" == "trojan" ]; then
if [ "$re_type" == "udp" ]; then
re_type="client"
port="10801"
else
re_type="nat"
fi
fi
lua /usr/share/vssr/genconfig_${stype}.lua ${server_obj} ${re_type} ${port} ${hostip} >${config_file}
sed -i 's/\\//g' $config_file
}
get_arg_out() {
case "$(uci_get_by_type access_control router_proxy 1)" in
1) echo "-o" ;;
2) echo "-O" ;;
esac
}
start_rules() {
local server=$(uci_get_by_name $GLOBAL_SERVER server)
#resolve name
if echo $server | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
server=${server}
elif [ "$server" != "${server#*:[0-9a-fA-F]}" ]; then
server=${server}
else
server=$(ping ${server} -s 1 -c 1 | grep PING | cut -d'(' -f 2 | cut -d')' -f1)
if echo $server | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
echo $server >/etc/ssr_ip
else
server=$(cat /etc/ssr_ip)
fi
fi
kcp_server=$server
local kcp_enable=$(uci_get_by_name $GLOBAL_SERVER kcp_enable 0)
if [ $kcp_enable = "1" ]; then
kcp_flag=1
fi
local local_port=$(uci_get_by_name $GLOBAL_SERVER local_port)
local lan_ac_ips=$(uci_get_by_type access_control lan_ac_ips)
local lan_ac_mode="b"
local router_proxy=$(uci_get_by_type access_control router_proxy)
if [ "$GLOBAL_SERVER" = "$UDP_RELAY_SERVER" -a $kcp_flag = 0 ]; then
ARG_UDP="-u"
elif [ -n "$UDP_RELAY_SERVER" ]; then
ARG_UDP="-U"
local udp_server=$(uci_get_by_name $UDP_RELAY_SERVER server)
local udp_local_port=$(uci_get_by_name $UDP_RELAY_SERVER local_port)
fi
if [ -n "$lan_ac_ips" ]; then
case "$lan_ac_mode" in
w | W | b | B) local ac_ips="$lan_ac_mode$lan_ac_ips" ;;
esac
fi
#deal gfw firewall rule
local gfwmode=""
if [ "$run_mode" = "gfw" ]; then
gfwmode="-g"
elif [ "$run_mode" = "router" ]; then
gfwmode="-r"
elif [ "$run_mode" = "oversea" ]; then
gfwmode="-c"
elif [ "$run_mode" = "all" ]; then
gfwmode="-z"
fi
local dports=$(uci_get_by_type global dports 1)
if [ $dports = "1" ]; then
proxyport=" "
else
proxyport="-m multiport --dports 22,53,587,465,995,993,143,80,443 "
fi
/usr/bin/vssr-rules \
-s "$server" \
-l "$local_port" \
-S "$udp_server" \
-L "$udp_local_port" \
-a "$ac_ips" \
-i "$(uci_get_by_type access_control wan_bp_list)" \
-b "$(uci_get_by_type access_control wan_bp_ips)" \
-w "$(uci_get_by_type access_control wan_fw_ips)" \
-p "$(uci_get_by_type access_control lan_fp_ips)" \
-G "$(uci_get_by_type access_control lan_gm_ips)" \
-D "$proxyport" \
$(get_arg_out) $gfwmode $ARG_UDP
return $?
}
start_pdnsd() {
local usr_dns="$1"
local usr_port="$2"
local tcp_dns_list="208.67.222.222, 208.67.220.220"
[ -z "$usr_dns" ] && usr_dns="8.8.8.8"
[ -z "$usr_port" ] && usr_port="53"
[ -d /var/etc ] || mkdir -p /var/etc
if [ ! -d /var/pdnsd ]; then
mkdir -p /var/pdnsd
echo -ne "pd13\000\000\000\000" >/var/pdnsd/pdnsd.cache
chown -R nobody:nogroup /var/pdnsd
fi
cat >/var/etc/pdnsd.conf <<EOF
global {
perm_cache=1024;
cache_dir="/var/pdnsd";
pid_file = /var/run/pdnsd.pid;
run_as="nobody";
server_ip = 127.0.0.1;
server_port = 5335;
status_ctl = on;
query_method = tcp_only;
min_ttl=1h;
max_ttl=1w;
timeout=10;
neg_domain_pol=on;
proc_limit=2;
procq_limit=8;
}
server {
label= "ssr-usrdns";
ip = $usr_dns;
port = $usr_port;
timeout=6;
uptest=none;
interval=10m;
purge_cache=off;
}
server {
label= "ssr-pdnsd";
ip = $tcp_dns_list;
port = 5353;
timeout=6;
uptest=none;
interval=10m;
purge_cache=off;
}
EOF
/usr/sbin/pdnsd -c /var/etc/pdnsd.conf -d
}
start_redir() {
case "$(uci_get_by_name $GLOBAL_SERVER auth_enable)" in
1 | on | true | yes | enabled) ARG_OTA="-A" ;;
*) ARG_OTA="" ;;
esac
#deal kcp
local kcp_enable=$(uci_get_by_name $GLOBAL_SERVER kcp_enable 0)
if [ $kcp_enable = "1" ]; then
[ ! -f "/usr/bin/kcptun-client" ] && return 1
local kcp_str=$(/usr/bin/kcptun-client -v | grep kcptun | wc -l)
[ "0" = $kcp_str ] && return 1
local kcp_port=$(uci_get_by_name $GLOBAL_SERVER kcp_port)
local server_port=$(uci_get_by_name $GLOBAL_SERVER server_port)
local password=$(uci_get_by_name $GLOBAL_SERVER kcp_password)
local kcp_param=$(uci_get_by_name $GLOBAL_SERVER kcp_param)
[ "$password" != "" ] && password="--key "${password}
service_start /usr/bin/kcptun-client \
-r $kcp_server:$kcp_port \
-l :$server_port $password $kcp_param
kcp_enable_flag=1
fi
gen_config_file $GLOBAL_SERVER 0
local stype=$(uci_get_by_name $GLOBAL_SERVER type)
if [ "$stype" == "ss" -o "$stype" == "v2ray" ]; then
sscmd="/usr/bin/v2ray/v2ray"
[ ! -f "$sscmd" ] && sscmd="/usr/bin/v2ray"
elif [ "$stype" == "ssr" ]; then
sscmd="/usr/bin/ssr-redir"
elif [ "$stype" == "trojan" ]; then
sscmd="/usr/sbin/trojan"
fi
local utype=$(uci_get_by_name $UDP_RELAY_SERVER type)
if [ "$utype" == "ss" -o "$utype" == "v2ray" ]; then
ucmd="/usr/bin/v2ray/v2ray"
[ ! -f "$ucmd" ] && ucmd="/usr/bin/v2ray"
elif [ "$utype" == "ssr" ]; then
ucmd="/usr/bin/ssr-redir"
elif [ "$utype" == "trojan" ]; then
ucmd="/usr/sbin/trojan"
fi
if [ "$(uci_get_by_type global threads 0)" = "0" ]; then
threads=$(cat /proc/cpuinfo | grep 'processor' | wc -l)
else
threads=$(uci_get_by_type global threads)
fi
#转发TCP
redir_tcp=1
local last_config_file=$CONFIG_FILE
if [ "$stype" == "ssr" ]; then
local pid_file="/var/run/ssr-retcp.pid"
for i in $(seq 1 $threads); do
$sscmd -c $last_config_file $ARG_OTA -f /var/run/ssr-retcp_$i.pid >/dev/null 2>&1
done
echo "$(date "+%Y-%m-%d %H:%M:%S") SSR $threads 线程 已启动!" >>/tmp/vssr.log
elif [ "$stype" == "v2ray" -o "$stype" == "ss" ]; then
$sscmd -config $last_config_file >/dev/null 2>&1 &
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) 已启动!" >>/tmp/vssr.log
elif [ "$stype" == "trojan" ]; then
for i in $(seq 1 $threads); do
$sscmd -c $last_config_file >/dev/null 2>&1 &
done
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -V 2>&1 | head -1) $threads 线程 已启动!" >>/tmp/vssr.log
fi
#转发UDP
if [ -n "$UDP_RELAY_SERVER" ]; then
redir_udp=1
gen_config_file $UDP_RELAY_SERVER 1
last_config_file=$CONFIG_UDP_FILE
echo $utype
if [ "$utype" == "ssr" ]; then
case "$(uci_get_by_name $UDP_RELAY_SERVER auth_enable)" in
1 | on | true | yes | enabled) ARG_OTA="-A" ;;
*) ARG_OTA="" ;;
esac
pid_file="/var/run/ssr-reudp.pid"
#echo $ucmd >> /tmp/vssr.log
$ucmd -c $last_config_file $ARG_OTA -U -f /var/run/ssr-reudp.pid >/tmp/vssr.log 2>&1
#echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) UDP已启动!" >> /tmp/vssr.log
elif [ "$utype" == "ss" -o "$utype" == "v2ray" ]; then
$ucmd -config $last_config_file >/dev/null 2>&1 &
elif [ "$stype" == "trojan" ]; then
$ucmd --config $last_config_file >/dev/null 2>&1 &
ipt2socks -U -4 -b 0.0.0.0 -s 127.0.0.1 -p 10801 -l $(uci_get_by_name $UDP_RELAY_SERVER local_port) >/dev/null 2>&1 &
fi
fi
#deal with dns
if [ "$(uci_get_by_type global pdnsd_enable)" = "1" ]; then
local dnsstr="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)"
local dnsserver=$(echo "$dnsstr" | awk -F ':' '{print $1}')
local dnsport=$(echo "$dnsstr" | awk -F ':' '{print $2}')
if [ "$run_mode" = "gfw" ]; then
ipset add gfwlist $dnsserver 2>/dev/null
elif [ "$run_mode" = "oversea" ]; then
ipset add oversea $dnsserver 2>/dev/null
else
ipset add ss_spec_wan_ac $dnsserver nomatch 2>/dev/null
fi
start_pdnsd $dnsserver $dnsport
pdnsd_enable_flag=1
fi
if [ "$(uci_get_by_type global enable_switch)" = "1" ]; then
if [ "$(uci_get_by_name $GLOBAL_SERVER switch_enable)" = "1" ]; then
if [ -z "$switch_server" ]; then
local switch_time=$(uci_get_by_type global switch_time)
local switch_timeout=$(uci_get_by_type global switch_timeout)
service_start /usr/bin/vssr-switch start $switch_time $switch_timeout
switch_enable=1
fi
fi
fi
add_cron
return $?
}
gen_service_file() {
if [ $(uci_get_by_name $1 fast_open) = "1" ]; then
fastopen="true"
else
fastopen="false"
fi
cat <<-EOF >$2
{
"server": "0.0.0.0",
"server_port": $(uci_get_by_name $1 server_port),
"password": "$(uci_get_by_name $1 password)",
"timeout": $(uci_get_by_name $1 timeout 60),
"method": "$(uci_get_by_name $1 encrypt_method)",
"protocol": "$(uci_get_by_name $1 protocol)",
"protocol_param": "$(uci_get_by_name $1 protocol_param)",
"obfs": "$(uci_get_by_name $1 obfs)",
"obfs_param": "$(uci_get_by_name $1 obfs_param)",
"fast_open": $fastopen
}
EOF
}
start_service() {
[ $(uci_get_by_name $1 enable) = "0" ] && return 1
let server_count=server_count+1
if [ $server_count = 1 ]; then
iptables -N SSR-SERVER-RULE &&
iptables -t filter -I INPUT -j SSR-SERVER-RULE
fi
gen_service_file $1 /var/etc/${NAME}_${server_count}.json
/usr/bin/ssr-server -c /var/etc/${NAME}_${server_count}.json -u -f /var/run/ssr-server${server_count}.pid >/dev/null 2>&1
iptables -t filter -A SSR-SERVER-RULE -p tcp --dport $(uci_get_by_name $1 server_port) -j ACCEPT
iptables -t filter -A SSR-SERVER-RULE -p udp --dport $(uci_get_by_name $1 server_port) -j ACCEPT
return 0
}
gen_serv_include() {
FWI=$(uci get firewall.vssr.path 2>/dev/null)
[ -n "$FWI" ] || return 0
if [ ! -f $FWI ]; then
echo '#!/bin/sh' >$FWI
fi
extract_rules() {
echo "*filter"
iptables-save -t filter | grep SSR-SERVER-RULE | sed -e "s/^-A INPUT/-I INPUT/"
echo 'COMMIT'
}
cat <<-EOF >>$FWI
iptables-save -c | grep -v "SSR-SERVER" | iptables-restore -c
iptables-restore -n <<-EOT
$(extract_rules)
EOT
EOF
}
start_server() {
SERVER_ENABLE=$(uci_get_by_type server_global enable_server)
[ "$SERVER_ENABLE" = 0 ] && return 0
mkdir -p /var/run /var/etc
config_load $NAME
config_foreach start_service server_config
gen_serv_include
return 0
}
start_local() {
local local_server=$(uci_get_by_type socks5_proxy enable_server)
[ "$local_server" = "0" ] && return 0
mkdir -p /var/run /var/etc
lua /usr/share/vssr/genconfig_v2ray_s.lua >$CONFIG_SOCK5_FILE
sed -i 's/\\//g' $config_file
/usr/bin/v2ray/v2ray -config $CONFIG_SOCK5_FILE >/dev/null 2>&1 &
local_enable=1
}
rules() {
[ "$GLOBAL_SERVER" = "nil" ] && return 1
mkdir -p /var/run /var/etc
UDP_RELAY_SERVER=$(uci_get_by_type global udp_relay_server)
[ "$UDP_RELAY_SERVER" = "same" ] && UDP_RELAY_SERVER=$GLOBAL_SERVER
if start_rules; then
return 0
else
return 1
fi
}
start() {
if [ -z "$switch_server" ]; then
GLOBAL_SERVER=$(uci_get_by_type global global_server)
else
GLOBAL_SERVER=$switch_server
switch_enable=1
fi
if rules; then
start_redir
mkdir -p /tmp/dnsmasq.d && cp -a /etc/dnsmasq.ssr /tmp/ && cp -a /etc/dnsmasq.oversea /tmp/
if ! [ "$run_mode" = "oversea" ]; then
cat >/tmp/dnsmasq.d/dnsmasq-ssr.conf <<EOF
conf-dir=/tmp/dnsmasq.ssr
EOF
else
cat >/tmp/dnsmasq.d/dnsmasq-ssr.conf <<EOF
conf-dir=/tmp/dnsmasq.oversea
EOF
fi
if [ $(uci_get_by_type global adblock) = 0 ]; then
rm -f /tmp/dnsmasq.ssr/ad.conf
fi
/usr/share/vssr/gfw2ipset.sh
/etc/init.d/dnsmasq restart >/dev/null 2>&1
fi
start_server
start_local
if [ $(uci_get_by_type global monitor_enable) = 1 ]; then
let total_count=server_count+redir_tcp+redir_udp+tunnel_enable+kcp_enable_flag+local_enable+pdnsd_enable_flag+switch_enable
if [ $total_count -gt 0 ]; then
#param:server(count) redir_tcp(0:no,1:yes) redir_udp tunnel kcp local gfw
service_start /usr/bin/vssr-monitor $server_count $redir_tcp $redir_udp $tunnel_enable $kcp_enable_flag $local_enable $pdnsd_enable_flag $switch_enable
fi
fi
ENABLE_SERVER=$(uci_get_by_type global global_server)
[ "$ENABLE_SERVER" = "nil" ] && return 1
}
boot() {
(/usr/share/vssr/chinaipset.sh && sleep 5 && start >/dev/null 2>&1) &
}
stop() {
/usr/bin/vssr-rules -f
srulecount=$(iptables -L | grep SSR-SERVER-RULE | wc -l)
if [ $srulecount -gt 0 ]; then
iptables -F SSR-SERVER-RULE
iptables -t filter -D INPUT -j SSR-SERVER-RULE
iptables -X SSR-SERVER-RULE 2>/dev/null
fi
if [ -z "$switch_server" ]; then
kill -9 $(busybox ps -w | grep vssr-switch | grep -v grep | awk '{print $1}') >/dev/null 2>&1
fi
if [ $(uci_get_by_type global monitor_enable) = 1 ]; then
kill -9 $(busybox ps -w | grep vssr-monitor | grep -v grep | awk '{print $1}') >/dev/null 2>&1
fi
killall -q -9 vssr-monitor
killall -q -9 ssr-redir
killall -q -9 v2ray
killall -q -9 trojan
killall -q -9 ipt2socks
killall -q -9 ssr-server
killall -q -9 kcptun-client
killall -q -9 ssr-local
killall -q -9 pdnsd
if [ -f "/tmp/dnsmasq.d/dnsmasq-ssr.conf" ]; then
rm -f /tmp/dnsmasq.d/dnsmasq-ssr.conf
/etc/init.d/dnsmasq restart >/dev/null 2>&1
fi
del_cron
}

View File

@ -0,0 +1,18 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@vssr[-1]
add ucitrack vssr
set ucitrack.@vssr[-1].init=vssr
commit ucitrack
delete firewall.vssr
set firewall.vssr=include
set firewall.vssr.type=script
set firewall.vssr.path=/var/etc/vssr.include
set firewall.vssr.reload=1
commit firewall
EOF
/usr/share/vssr/gfw2ipset.sh
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,5 @@
#!/bin/sh -e
if [ -f /tmp/adnew.conf ]; then
cat /tmp/adnew.conf | grep ^\|\|[^\*]*\^$ | sed -e 's:||:address\=\/:' -e 's:\^:/0\.0\.0\.0:' > /tmp/ad.conf
fi

View File

@ -0,0 +1,31 @@
#!/bin/sh -e
generate_china_banned()
{
cat $1 | base64 -d > /tmp/gfwlist.txt
rm -f $1
sed -i '/^@@|/d' /tmp/gfwlist.txt
cat /tmp/gfwlist.txt | sort -u |
sed 's#!.\+##; s#|##g; s#@##g; s#http:\/\/##; s#https:\/\/##;' |
sed '/\*/d; /apple\.com/d; /sina\.cn/d; /sina\.com\.cn/d; /baidu\.com/d; /byr\.cn/d; /jlike\.com/d; /weibo\.com/d; /zhongsou\.com/d; /youdao\.com/d; /sogou\.com/d; /so\.com/d; /soso\.com/d; /aliyun\.com/d; /taobao\.com/d; /jd\.com/d; /qq\.com/d' |
sed '/^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$/d' |
grep '^[0-9a-zA-Z\.-]\+$' | grep '\.' | sed 's#^\.\+##' | sort -u |
awk '
BEGIN { prev = "________"; } {
cur = $0;
if (index(cur, prev) == 1 && substr(cur, 1 + length(prev) ,1) == ".") {
} else {
print cur;
prev = cur;
}
}' | sort -u
}
generate_china_banned /tmp/gfw.b64 > /tmp/gfw.txt
rm -f /tmp/gfwlist.txt
sed '/.*/s/.*/server=\/\.&\/127.0.0.1#5335\nipset=\/\.&\/gfwlist/' /tmp/gfw.txt >/tmp/gfwnew.txt
rm -f /tmp/gfw.txt

View File

@ -0,0 +1,126 @@
#!/bin/sh
#
# Copyright (C) 2017 openwrt-ssr
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
NAME=vssr
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
server_process_count=$1
redir_tcp_process=$2
redir_udp_process=$3
tunnel_process=$4
kcp_process=$5
local_process=$6
pdnsd_process=$7
if [ -z "$pdnsd_process" ]; then
pdnsd_process=0
fi
i=0
GLOBAL_SERVER=$(uci_get_by_type global global_server)
server=$(uci_get_by_name $GLOBAL_SERVER server)
lkcp_port=$(uci_get_by_name $GLOBAL_SERVER kcp_port)
server_port=$(uci_get_by_name $GLOBAL_SERVER server_port)
password=$(uci_get_by_name $GLOBAL_SERVER kcp_password)
kcp_param=$(uci_get_by_name $GLOBAL_SERVER kcp_param)
[ "$password" != "" ] && password="--key "${password}
sock5_port=$(uci_get_by_type socks5_proxy local_port 1080)
if echo $server | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
server=${server}
else
server=$(cat /etc/ssr_ip)
fi
while [ "1" = "1" ]; do #这里是个死循环
sleep 30
#redir tcp
if [ $redir_tcp_process -gt 0 ]; then
icount=$(busybox ps -w | grep vssr_t | grep -v grep | wc -l)
if [ $icount = 0 ]; then
logger -t "$NAME" "ssr redir tcp error.restart!"
/etc/init.d/vssr restart
exit 0
fi
fi
#redir udp
if [ $redir_udp_process -gt 0 ]; then
icount=$(busybox ps -w | grep vssr_u | grep -v grep | wc -l)
if [ $icount = 0 ]; then
logger -t "$NAME" "ssr redir udp error.restart!"
/etc/init.d/vssr restart
exit 0
fi
fi
#tunnel
if [ $tunnel_process -gt 0 ]; then
icount=$(busybox ps -w | grep ssr-tunnel | grep -v grep | wc -l)
if [ $icount = 0 ]; then
logger -t "$NAME" "ssr tunnel error.restart!"
/etc/init.d/vssr restart
exit 0
fi
fi
#server
if [ $server_process_count -gt 0 ]; then
icount=$(busybox ps -w | grep ssr-server | grep -v grep | wc -l)
if [ $icount -lt $server_process_count ]; then
logger -t "$NAME" "ssr server error.restart!"
killall -q -9 ssr-server
for i in $(seq $server_process_count); do
/usr/bin/ssr-server -c /var/etc/vssr_$i.json -u -f /var/run/ssr-server$i.pid
done
fi
fi
#kcptun
if [ $kcp_process -gt 0 ]; then
icount=$(busybox ps -w | grep kcptun-client | grep -v grep | wc -l)
if [ $icount -lt $kcp_process ]; then
logger -t "$NAME" "ssr kcptun error.restart!"
killall -q -9 kcptun-client
(/usr/bin/kcptun-client -r $server:$kcp_port -l :$server_port $password $kcp_param &)
fi
fi
#local
# if [ $local_process -gt 0 ] ;then
# icount=`busybox ps -w | grep ssr-local |grep -v grep| wc -l`
# if [ $icount -lt $local_process ]
# then
# logger -t "$NAME" "ssr local error.restart!"
# killall -q -9 ssr-local
# ( /usr/bin/ssr-local -c /var/etc/vssr_s.json -u -l $sock5_port -f /var/run/ssr-local.pid &)
# fi
# fi
#pdnsd
if [ $pdnsd_process -gt 0 ]; then
icount=$(busybox ps -w | grep pdnsd | grep -v grep | wc -l)
if [ $icount -lt $pdnsd_process ]; then #如果进程挂掉就重启它
logger -t "$NAME" "pdnsd tunnel error.restart!"
if [ -f /var/run/pdnsd.pid ]; then
kill $(cat /var/run/pdnsd.pid) >/dev/null 2>&1
else
kill -9 $(ps | grep pdnsd | grep -v grep | awk '{print $1}') >/dev/null 2>&1
fi
(/usr/sbin/pdnsd -c /var/etc/pdnsd.conf -d &)
fi
fi
done

View File

@ -0,0 +1,180 @@
#!/bin/sh
#
# Copyright (C) 2019 vssr
# Copyright (C) 2019 jerrykuku <jerrykuku@qq.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
NAME=vssr
#定义配置文件名称
CONFIG_FILE=/var/etc/${NAME}_t.json
CONFIG_UDP_FILE=/var/etc/${NAME}_u.json
CONFIG_SOCK5_FILE=/var/etc/${NAME}_s.json
switch_server=$1
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
gen_config_file() {
local host=$(uci_get_by_name $1 server)
if echo $host | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
hostip=${host}
elif [ "$host" != "${host#*:[0-9a-fA-F]}" ]; then
hostip=${host}
else
hostip=$(ping ${host} -s 1 -c 1 | grep PING | cut -d'(' -f 2 | cut -d')' -f1)
if echo $hostip | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
hostip=${hostip}
else
hostip=$(cat /etc/ssr_ip)
fi
fi
[ $2 = "0" -a $kcp_flag = "1" ] && hostip="127.0.0.1"
if [ $2 = "0" ]; then
re_type="tcp"
config_file=$CONFIG_FILE
server_obj=$GLOBAL_SERVER
elif [ $2 = "1" ]; then
re_type="udp"
config_file=$CONFIG_UDP_FILE
server_obj=$UDP_RELAY_SERVER
fi
if [ $(uci_get_by_name $1 fast_open 0) = "1" ]; then
fastopen="true"
else
fastopen="false"
fi
local stype=$(uci_get_by_name $1 type)
local port=$(uci_get_by_name $1 local_port)
if [ "$stype" == "trojan" ]; then
if [ "$re_type" == "udp" ]; then
re_type="client"
port="10801"
else
re_type="nat"
fi
fi
lua /usr/share/vssr/genconfig_${stype}.lua ${server_obj} ${re_type} ${port} ${hostip} >${config_file}
sed -i 's/\\//g' $config_file
}
start_local() {
local local_server=$(uci_get_by_type socks5_proxy enable_server)
local local_port=$(uci_get_by_type socks5_proxy local_port)
local Socks_user=$(uci_get_by_type socks5_proxy Socks_user)
local Socks_pass=$(uci_get_by_type socks5_proxy Socks_pass)
[ "$local_server" = "0" ] && return 1
mkdir -p /var/run /var/etc
lua /usr/share/vssr/genconfig_v2ray_s.lua "socks" ${local_port} ${Socks_user} ${Socks_pass} >$CONFIG_SOCK5_FILE
sed -i 's/\\//g' $config_file
/usr/bin/v2ray/v2ray -config $CONFIG_SOCK5_FILE >/dev/null 2>&1 &
}
killall -q -9 ssr-redir
killall -q -9 v2ray
killall -q -9 trojan
killall -q -9 ipt2socks
case "$(uci_get_by_name $GLOBAL_SERVER auth_enable)" in
1 | on | true | yes | enabled) ARG_OTA="-A" ;;
*) ARG_OTA="" ;;
esac
if [ -z "$switch_server" ]; then
GLOBAL_SERVER=$(uci_get_by_type global global_server)
else
GLOBAL_SERVER=$switch_server
fi
gen_config_file $GLOBAL_SERVER 0
stype=$(uci_get_by_name $GLOBAL_SERVER type)
local stype=$(uci_get_by_name $GLOBAL_SERVER type)
if [ "$stype" == "ss" -o "$stype" == "v2ray" ]; then
sscmd="/usr/bin/v2ray/v2ray"
[ ! -f "$sscmd" ] && sscmd="/usr/bin/v2ray"
elif [ "$stype" == "ssr" ]; then
sscmd="/usr/bin/ssr-redir"
elif [ "$stype" == "trojan" ]; then
sscmd="/usr/sbin/trojan"
fi
local utype=$(uci_get_by_name $UDP_RELAY_SERVER type)
if [ "$utype" == "ss" -o "$utype" == "v2ray" ]; then
ucmd="/usr/bin/v2ray/v2ray"
[ ! -f "$ucmd" ] && ucmd="/usr/bin/v2ray"
elif [ "$utype" == "ssr" ]; then
ucmd="/usr/bin/ssr-redir"
elif [ "$utype" == "trojan" ]; then
ucmd="/usr/sbin/trojan"
fi
if [ "$(uci_get_by_type global threads 0)" = "0" ]; then
threads=$(cat /proc/cpuinfo | grep 'processor' | wc -l)
else
threads=$(uci_get_by_type global threads)
fi
#转发TCP
if [ "$(uci_get_by_type global threads 0)" = "0" ]; then
threads=$(cat /proc/cpuinfo | grep 'processor' | wc -l)
else
threads=$(uci_get_by_type global threads)
fi
#转发TCP
redir_tcp=1
local last_config_file=$CONFIG_FILE
if [ "$stype" == "ssr" ]; then
local pid_file="/var/run/ssr-retcp.pid"
for i in $(seq 1 $threads); do
$sscmd -c $last_config_file $ARG_OTA -f /var/run/ssr-retcp_$i.pid >/dev/null 2>&1
done
echo "$(date "+%Y-%m-%d %H:%M:%S") SSR $threads 线程 已启动!" >>/tmp/vssr.log
elif [ "$stype" == "v2ray" -o "$stype" == "ss" ]; then
$sscmd -config $last_config_file >/dev/null 2>&1 &
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) 已启动!" >>/tmp/vssr.log
elif [ "$stype" == "trojan" ]; then
$sscmd -c $last_config_file >/dev/null 2>&1 &
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -V 2>&1 | head -1) 已启动!" >>/tmp/vssr.log
fi
#转发UDP
if [ -n "$UDP_RELAY_SERVER" ]; then
redir_udp=1
gen_config_file $UDP_RELAY_SERVER 1
last_config_file=$CONFIG_UDP_FILE
echo $utype
if [ "$utype" == "ssr" ]; then
case "$(uci_get_by_name $UDP_RELAY_SERVER auth_enable)" in
1 | on | true | yes | enabled) ARG_OTA="-A" ;;
*) ARG_OTA="" ;;
esac
pid_file="/var/run/ssr-reudp.pid"
#echo $ucmd >> /tmp/vssr.log
$ucmd -c $last_config_file $ARG_OTA -U -f /var/run/ssr-reudp.pid >/tmp/vssr.log 2>&1
#echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) UDP已启动!" >> /tmp/vssr.log
elif [ "$utype" == "ss" -o "$utype" == "v2ray" ]; then
$ucmd -config $last_config_file >/dev/null 2>&1 &
elif [ "$stype" == "trojan" ]; then
$ucmd --config $last_config_file >/dev/null 2>&1 &
ipt2socks -U -4 -b 0.0.0.0 -s 127.0.0.1 -p 10801 -l $(uci_get_by_name $UDP_RELAY_SERVER local_port) >/dev/null 2>&1 &
fi
fi
start_local

View File

@ -0,0 +1,389 @@
#!/bin/sh
#
# Copyright (C) 2017 openwrt-ssr
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
TAG="_SS_SPEC_RULE_" # comment tag
IPT="iptables -t nat" # alias of iptables
FWI=$(uci get firewall.vssr.path 2>/dev/null) # firewall include file
usage() {
cat <<-EOF
Usage: vssr-rules [options]
Valid options are:
-s <server_ip> ip address of vssr remote server
-l <local_port> port number of vssr local server
-S <server_ip> ip address of vssr remote UDP server
-L <local_port> port number of vssr local UDP server
-i <ip_list_file> a file content is bypassed ip list
-a <lan_ips> lan ip of access control, need a prefix to
define access control mode
-b <wan_ips> wan ip of will be bypassed
-w <wan_ips> wan ip of will be forwarded
-p <fp_lan_ips> lan ip of will be global proxy
-G <gm_lan_ips> lan ip of will be game mode proxy
-D <proxy_ports> proxy ports
-e <extra_options> extra options for iptables
-o apply the rules to the OUTPUT chain
-O apply the global rules to the OUTPUT chain
-u enable udprelay mode, TPROXY is required
-U enable udprelay mode, using different IP
and ports for TCP and UDP
-f flush the rules
-g gfw list mode
-r return china mode
-h show this help message and exit
EOF
exit $1
}
loger() {
# 1.alert 2.crit 3.err 4.warn 5.notice 6.info 7.debug
logger -st vssr-rules[$$] -p$1 $2
}
flush_r() {
flush_iptables() {
local ipt="iptables -t $1"
local DAT=$(iptables-save -t $1)
eval $(echo "$DAT" | grep "$TAG" | sed -e 's/^-A/$ipt -D/' -e 's/$/;/')
for chain in $(echo "$DAT" | awk '/^:SS_SPEC/{print $1}'); do
$ipt -F ${chain:1} 2>/dev/null && $ipt -X ${chain:1}
done
}
flush_iptables nat
flush_iptables mangle
ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
ipset -X ss_spec_lan_ac 2>/dev/null
ipset -X ss_spec_wan_ac 2>/dev/null
ipset -X ssr_gen_router 2>/dev/null
ipset -X fplan 2>/dev/null
ipset -X gmlan 2>/dev/null
ipset -X oversea 2>/dev/null
ipset -X whitelist 2>/dev/null
ipset -X blacklist 2>/dev/null
[ -n "$FWI" ] && echo '#!/bin/sh' >$FWI
return 0
}
ipset_r() {
ipset -N gmlan hash:net 2>/dev/null
for ip in $LAN_GM_IP; do ipset -! add gmlan $ip ; done
if [ "$RUNMODE" = "router" ] ;then
ipset -! -R <<-EOF || return 1
create ss_spec_wan_ac hash:net
$(gen_iplist | sed -e "s/^/add ss_spec_wan_ac /")
EOF
ipset -N gfwlist hash:net 2>/dev/null
$IPT -N SS_SPEC_WAN_AC
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
$IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
elif [ "$RUNMODE" = "gfw" ] ;then
ipset -N gfwlist hash:net 2>/dev/null
$IPT -N SS_SPEC_WAN_AC
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
elif [ "$RUNMODE" = "oversea" ] ;then
ipset -N oversea hash:net 2>/dev/null
$IPT -N SS_SPEC_WAN_AC
ipset -N gmlan hash:net 2>/dev/null
for ip in $LAN_GM_IP; do ipset -! add gmlan $ip ; done
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j SS_SPEC_WAN_FW
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
elif [ "$RUNMODE" = "all" ] ;then
$IPT -N SS_SPEC_WAN_AC
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
fi
ipset -N fplan hash:net 2>/dev/null
for ip in $LAN_FP_IP; do ipset -! add fplan $ip ; done
$IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
ipset -N whitelist hash:net 2>/dev/null
ipset -N blacklist hash:net 2>/dev/null
$IPT -I SS_SPEC_WAN_AC -m set --match-set blacklist dst -j SS_SPEC_WAN_FW
$IPT -I SS_SPEC_WAN_AC -m set --match-set whitelist dst -j RETURN
for ip in $WAN_BP_IP; do ipset -! add whitelist $ip; done
for ip in $WAN_FW_IP; do ipset -! add blacklist $ip; done
return $?
}
fw_rule() {
$IPT -N SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_FW -d 0.0.0.0/8 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 10.0.0.0/8 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 127.0.0.0/8 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 169.254.0.0/16 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 172.16.0.0/12 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 192.168.0.0/16 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 224.0.0.0/4 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 240.0.0.0/4 -j RETURN
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS \
-j REDIRECT --to-ports $local_port 2>/dev/null || {
loger 3 "Can't redirect, please check the iptables."
exit 1
}
return $?
}
ac_rule() {
if [ -n "$LAN_AC_IP" ]; then
case "${LAN_AC_IP:0:1}" in
w|W)
MATCH_SET="-m set --match-set ss_spec_lan_ac src"
;;
b|B)
MATCH_SET="-m set ! --match-set ss_spec_lan_ac src"
;;
*)
loger 3 "Bad argument \`-a $LAN_AC_IP\`."
return 2
;;
esac
fi
IFNAME=$(uci get -P/var/state network.lan.ifname 2>/dev/null)
ipset -! -R <<-EOF || return 1
create ss_spec_lan_ac hash:net
$(for ip in ${LAN_AC_IP:1}; do echo "add ss_spec_lan_ac $ip"; done)
EOF
$IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET \
-m comment --comment "$TAG" -j SS_SPEC_WAN_AC
if [ "$OUTPUT" = 1 ]; then
$IPT -I OUTPUT 1 -p tcp $EXT_ARGS \
-m comment --comment "$TAG" -j SS_SPEC_WAN_AC
elif [ "$OUTPUT" = 2 ]; then
ipset -! -R <<-EOF || return 1
create ssr_gen_router hash:net
$(gen_spec_iplist | sed -e "s/^/add ssr_gen_router /")
EOF
$IPT -N SS_SPEC_ROUTER && \
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \
$IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW
$IPT -I OUTPUT 1 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER
fi
return $?
}
tp_rule() {
[ -n "$TPROXY" ] || return 0
ip rule add fwmark 0x01/0x01 table 100
ip route add local 0.0.0.0/0 dev lo table 100
local ipt="iptables -t mangle"
$ipt -N SS_SPEC_TPROXY
$ipt -A SS_SPEC_TPROXY -p udp --dport 53 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 0.0.0.0/8 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 10.0.0.0/8 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 127.0.0.0/8 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 169.254.0.0/16 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 172.16.0.0/12 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 192.168.0.0/16 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 224.0.0.0/4 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d 240.0.0.0/4 -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -d $SERVER -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
if [ "$RUNMODE" = "router" ] ;then
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set ! --match-set ss_spec_wan_ac dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
elif [ "$RUNMODE" = "gfw" ] ;then
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp -m set $PROXY_PORTS --match-set gfwlist dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
elif [ "$RUNMODE" = "oversea" ] ;then
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
elif [ "$RUNMODE" = "all" ] ;then
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
fi
$ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET \
-m comment --comment "$TAG" -j SS_SPEC_TPROXY
return $?
}
get_wan_ip() {
cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
$server
$SERVER
$WAN_BP_IP
EOF
}
gen_iplist() {
cat <<-EOF
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/24
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.255.255.255
$(get_wan_ip)
$(cat ${IGNORE_LIST:=/dev/null} 2>/dev/null)
EOF
}
gen_spec_iplist() {
cat <<-EOF
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/24
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.255.255.255
$(get_wan_ip)
EOF
}
gen_include() {
[ -n "$FWI" ] || return 0
extract_rules() {
echo "*$1"
iptables-save -t $1 | grep SS_SPEC_ |\
sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
echo 'COMMIT'
}
cat <<-EOF >>$FWI
iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c
iptables-restore -n <<-EOT
$(extract_rules nat)
$(extract_rules mangle)
EOT
EOF
return 0
}
while getopts ":s:l:S:L:i:e:a:b:w:p:G:D:oOuUfgrczh" arg; do
case "$arg" in
s)
server=$OPTARG
;;
l)
local_port=$OPTARG
;;
S)
SERVER=$OPTARG
;;
L)
LOCAL_PORT=$OPTARG
;;
i)
IGNORE_LIST=$OPTARG
;;
e)
EXT_ARGS=$OPTARG
;;
a)
LAN_AC_IP=$OPTARG
;;
b)
WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
;;
w)
WAN_FW_IP=$OPTARG
;;
p)
LAN_FP_IP=$OPTARG
;;
G)
LAN_GM_IP=$OPTARG
;;
D)
PROXY_PORTS=$OPTARG
;;
o)
OUTPUT=1
;;
O)
OUTPUT=2
;;
u)
TPROXY=1
;;
U)
TPROXY=2
;;
g)
RUNMODE=gfw
;;
r)
RUNMODE=router
;;
c)
RUNMODE=oversea
;;
z)
RUNMODE=all
;;
f)
flush_r
exit 0
;;
h)
usage 0
;;
esac
done
if [ -z "$server" -o -z "$local_port" ]; then
usage 2
fi
if [ "$TPROXY" = 1 ]; then
SERVER=$server
LOCAL_PORT=$local_port
elif [ "$TPROXY" = 2 ]; then
: ${SERVER:?"You must assign an ip for the udp relay server."}
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
fi
flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include
[ "$?" = 0 ] || loger 3 "Start failed!"
exit $?

View File

@ -0,0 +1,176 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2017 openwrt-ssr
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
cycle_time=60
switch_time=3
normal_flag=0
server_locate=0
server_count=0
NAME=vssr
ENABLE_SERVER=nil
CONFIG_SWTICH_FILE=/var/etc/${NAME}_t.json
[ -n "$1" ] && cycle_time=$1
[ -n "$2" ] && switch_time=$2
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
DEFAULT_SERVER=$(uci_get_by_type global global_server)
CURRENT_SERVER=$DEFAULT_SERVER
#判断代理是否正常
check_proxy() {
local result=0
local try_count=$(uci_get_by_type global switch_try_count 3)
for i in $(seq 1 $try_count); do
/usr/bin/ssr-check www.google.com 80 $switch_time 1
if [ "$?" == "0" ]; then
result=0
break
else
/usr/bin/ssr-check www.baidu.com 80 $switch_time 1
if [ "$?" == "0" ]; then
result=1
else
result=2
fi
fi
sleep 1
done
return $result
}
test_proxy() {
local servername=$(uci_get_by_name $1 server)
local serverport=$(uci_get_by_name $1 server_port)
ret=$(ping -c 3 $servername | grep 'loss' | awk -F ',' '{ print $3 }' | awk -F "%" '{ print $1 }')
[ -z "$ret" ] && return 1
[ "$ret" -gt "50" ] && return 1
ipset add ss_spec_wan_ac $servername 2>/dev/null
ret=$?
/usr/bin/ssr-check $servername $serverport $switch_time
local ret2=$?
if [ "$ret" = "0" ]; then
ipset del ss_spec_wan_ac $servername 2>/dev/null
fi
if [ "$ret2" = "0" ]; then
return 0
else
return 1
fi
}
search_proxy() {
let server_count=server_count+1
[ "$normal_flag" = "1" -a "$server_count" -le "$server_locate" ] && return 0
[ "$(uci_get_by_name $1 switch_enable)" != "1" ] && return 1
[ $ENABLE_SERVER != nil ] && return 0
[ "$1" = "$CURRENT_SERVER" ] && return 0
local servername=$(uci_get_by_name $1 server)
local serverport=$(uci_get_by_name $1 server_port)
ipset add ss_spec_wan_ac $servername 2>/dev/null
ret=$?
/usr/bin/ssr-check $servername $serverport $switch_time
local ret2=$?
if [ "$ret" = "0" ]; then
ipset del ss_spec_wan_ac $servername 2>/dev/null
fi
if [ "$ret2" = "0" ]; then
server_locate=$server_count
ENABLE_SERVER=$1
return 0
else
return 1
fi
}
#选择可用的代理
select_proxy() {
config_load $NAME
ENABLE_SERVER=nil
mkdir -p /var/run /var/etc
server_count=0
config_foreach search_proxy servers
}
#切换代理
switch_proxy() {
/etc/init.d/vssr restart $1
return 0
}
start() {
#不支持kcptun启用时的切换
[ $(uci_get_by_name $DEFAULT_SERVER kcp_enable) = "1" ] && return 1
while [ "1" = "1" ]; do #死循环
sleep $cycle_time
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
#判断当前代理是否为缺省服务器
if [ "$CURRENT_SERVER" != "$DEFAULT_SERVER" ]; then
#echo "not default proxy"
echo "$(date "+%Y-%m-%d %H:%M:%S") 当前为备用节点,尝试切换为主节点。" >>/tmp/vssr.log
#检查缺省服务器是否正常
if test_proxy $DEFAULT_SERVER; then
#echo "switch to default proxy"
echo "$(date "+%Y-%m-%d %H:%M:%S") 主节点不可用." >>/tmp/vssr.log
#缺省服务器正常,切换回来
CURRENT_SERVER=$DEFAULT_SERVER
switch_proxy $CURRENT_SERVER
echo "$(date "+%Y-%m-%d %H:%M:%S") 切换为默认节点 ["$(uci_get_by_name $CURRENT_SERVER server)"]" >>/tmp/vssr.log
continue
else
echo "$(date "+%Y-%m-%d %H:%M:%S") 主节点不可用,继续使用当前备用节点。" >>/tmp/vssr.log
fi
fi
#判断当前代理是否正常
check_proxy
current_ret=$?
if [ "$current_ret" = "1" ]; then
#当前代理错误,判断有无可用的服务器
#echo "current error"
echo "$(date "+%Y-%m-%d %H:%M:%S") 当前节点不可用,尝试切换其他节点。" >>/tmp/vssr.log
select_proxy
if [ "$ENABLE_SERVER" != nil ]; then
#有其他服务器可用,进行切换
#echo $(uci_get_by_name $new_proxy server)
echo "$(date "+%Y-%m-%d %H:%M:%S") 另外一个节点可用,即将切换节点。" >>/tmp/vssr.log
CURRENT_SERVER=$ENABLE_SERVER
switch_proxy $CURRENT_SERVER
normal_flag=1
echo "$(date "+%Y-%m-%d %H:%M:%S") 切换节点成功。" >>/tmp/vssr.log
else
switch_proxy $CURRENT_SERVER
normal_flag=1
echo "$(date "+%Y-%m-%d %H:%M:%S") 尝试重启当前节点。" >>/tmp/vssr.log
fi
else
normal_flag=0
#echo "$(date "+%Y-%m-%d %H:%M:%S") vssr No Problem." >> /tmp/vssr.log
fi
done
}

View File

@ -0,0 +1,11 @@
{
"luci-app-vssr": {
"description": "Grant UCI access for luci-app-vssr",
"read": {
"uci": [ "vssr" ]
},
"write": {
"uci": [ "vssr" ]
}
}
}

View File

@ -0,0 +1,5 @@
echo "create china hash:net family inet hashsize 1024 maxelem 65536" > /tmp/china.ipset
awk '!/^$/&&!/^#/{printf("add china %s'" "'\n",$0)}' /etc/china_ssr.txt >> /tmp/china.ipset
ipset -! flush china
ipset -! restore < /tmp/china.ipset 2>/dev/null
rm -f /tmp/china.ipset

View File

@ -0,0 +1,61 @@
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

@ -0,0 +1,25 @@
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

@ -0,0 +1,40 @@
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 server = ucursor:get_all("vssr", server_section)
local trojan = {
log_level = 3,
run_type = proto,
local_addr = "0.0.0.0",
local_port = tonumber(local_port),
remote_addr = server.server,
remote_port = tonumber(server.server_port),
udp_timeout = 60,
-- 传入连接
password = {server.password},
-- 传出连接
ssl = {
verify = false,
verify_hostname = (server.tls == "1") and false or true,
cert = "",
cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA",
cipher_tls13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384",
sni = server.peer,
alpn = {"h2", "http/1.1"},
curve = "",
reuse_session = true,
session_ticket = false,
},
tcp = {
no_delay = true,
keep_alive = true,
reuse_port = true,
fast_open = (server.fast_open == "1") and true or false,
fast_open_qlen = 20
}
}
print(json.stringify(trojan, 1))

View File

@ -0,0 +1,176 @@
local ucursor = require"luci.model.uci".cursor()
local name = "vssr"
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3]
local host = arg[4]
local v2ray_flow = ucursor:get_first(name, 'global', 'v2ray_flow', '0')
local youtube_server = ucursor:get_first(name, 'global', 'youtube_server')
local tw_video_server = ucursor:get_first(name, 'global', 'tw_video_server')
local netflix_server = ucursor:get_first(name, 'global', 'netflix_server')
local disney_server = ucursor:get_first(name, 'global', 'disney_server')
local prime_server = ucursor:get_first(name, 'global', 'prime_server')
function gen_outbound(server_node, tags)
local bound = {}
if server_node == "nil" then
bound = nil
else
local server = ucursor:get_all(name, server_node)
bound = {
tag = tags,
protocol = "vmess",
settings = {
vnext = {
{
address = server.server,
port = tonumber(server.server_port),
users = {
{
id = server.vmess_id,
alterId = tonumber(server.alter_id),
security = server.security
}
}
}
}
},
-- 底层传输配置
streamSettings = {
network = server.transport,
security = (server.tls == '1') and "tls" or "none",
tlsSettings = {
allowInsecure = (server.insecure == "1") and true or false,
serverName = server.ws_host
},
kcpSettings = (server.transport == "kcp") and {
mtu = tonumber(server.mtu),
tti = tonumber(server.tti),
uplinkCapacity = tonumber(server.uplink_capacity),
downlinkCapacity = tonumber(server.downlink_capacity),
congestion = (server.congestion == "1") and true or false,
readBufferSize = tonumber(server.read_buffer_size),
writeBufferSize = tonumber(server.write_buffer_size),
header = {type = server.kcp_guise}
} or nil,
wsSettings = (server.transport == "ws") and
(server.ws_path ~= nil or server.ws_host ~= nil) and {
path = server.ws_path,
headers = (server.ws_host ~= nil) and
{Host = server.ws_host} or nil
} or nil,
httpSettings = (server.transport == "h2") and
{path = server.h2_path, host = server.h2_host} or nil,
quicSettings = (server.transport == "quic") and {
security = server.quic_security,
key = server.quic_key,
header = {type = server.quic_guise}
} or nil
},
mux = {
enabled = (server.mux == "1") and true or false,
concurrency = tonumber(server.concurrency)
}
}
end
return bound
end
local outbounds_table = {}
table.insert(outbounds_table, gen_outbound(server_section, "main"))
if v2ray_flow == "1" then
table.insert(outbounds_table, gen_outbound(youtube_server, "youtube"))
table.insert(outbounds_table, gen_outbound(tw_video_server, "twvideo"))
table.insert(outbounds_table, gen_outbound(netflix_server, "netflix"))
table.insert(outbounds_table, gen_outbound(disney_server, "disney"))
table.insert(outbounds_table, gen_outbound(prime_server, "prime"))
end
-- rules gen
local youtube_rule = {
type = "field",
domain = {"youtube", "googlevideo.com", "gvt2.com", "youtu.be"},
outboundTag = "youtube"
}
local tw_video_rule = {
type = "field",
domain = {
"vidol.tv", "hinet.net", "books.com", "litv.tv", "pstatic.net",
"app-measurement.com", "kktv.com.tw", "gamer.com.tw","wetv.vip"
},
outboundTag = "twvideo"
}
local netflix_rule = {
type = "field",
domain = {
"netflix", "nflxso.net", "nflxext.com",
"nflximg.com", "nflximg.net", "nflxvideo.net"
},
outboundTag = "netflix"
}
local disney_rule = {
type = "field",
domain = {
"cdn.registerdisney.go.com", "disneyplus.com", "disney-plus.net",
"dssott.com", "bamgrid.com", "execute-api.us-east-1.amazonaws.com"
},
outboundTag = "disney"
}
local prime_rule = {
type = "field",
domain = {"aiv-cdn.net", "amazonaws.com", "amazonvideo.com", "llnwd.net"},
outboundTag = "prime"
}
local rules_table = {}
if (youtube_server ~= "nil" and v2ray_flow == "1") then
table.insert(rules_table, youtube_rule)
end
if (tw_video_server ~= "nil" and v2ray_flow == "1") then
table.insert(rules_table, tw_video_rule)
end
if (netflix_server ~= "nil" and v2ray_flow == "1") then
table.insert(rules_table, netflix_rule)
end
if (disney_server ~= "nil" and v2ray_flow == "1") then
table.insert(rules_table, disney_rule)
end
if (prime_server ~= "nil" and v2ray_flow == "1") then
table.insert(rules_table, prime_rule)
end
local v2ray = {
log = {
-- error = "/var/ssrplus.log",
-- access = "/var/v2rays.log",
loglevel = "warning"
},
-- 传入连接
inbounds = {
{
port = local_port,
protocol = "dokodemo-door",
settings = {network = proto, followRedirect = true},
sniffing = {enabled = true, destOverride = {"http", "tls"}}
}
},
-- 传出连接
outbounds = outbounds_table,
routing = {domainStrategy = "IPIfNonMatch", rules = rules_table}
}
print(json.stringify(v2ray, 1))

View File

@ -0,0 +1,42 @@
local ucursor = require "luci.model.uci".cursor()
local name = "vssr"
local json = require "luci.jsonc"
local proto = "socks"
local auth_type = ucursor:get_first(name, 'socks5_proxy', 'enable_auth')
local local_port = ucursor:get_first(name, 'socks5_proxy', 'local_port')
local Socks_user = ucursor:get_first(name, 'socks5_proxy', 'Socks_user')
local Socks_pass = ucursor:get_first(name, 'socks5_proxy', 'Socks_pass')
local v2ray = {
log = {
--error = "/var/log/v2ray.log",
loglevel = "warning"
},
-- 传入连接
inbound = {
port = local_port,
protocol = proto,
settings = {
auth = (auth_type == '1') and "password" or "noauth",
accounts =(auth_type == "1") and {
{
user = (auth_type == '1') and Socks_user,
pass = Socks_pass
}
} or nil,
}
},
-- 传出连接
outbound = {
protocol = "freedom"
},
-- 额外传出连接
outboundDetour = {
{
protocol = "blackhole",
tag = "blocked"
}
}
}
print(json.stringify(v2ray,1))

View File

@ -0,0 +1,37 @@
#!/bin/bash
# Copyright (C) 2019 Jerryk <jerrykuku@qq.com>
lua=/usr/bin/lua
name="$1"
host=$2
code=''
iso_array=(AC AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CP CR CU CV CW CX CY CZ DE DG DJ DK DM DO DZ EA EC EE EG EH ER ES ET EU FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU IC ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TA TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM UN US UY UZ VA VC VE VG VI VN VU WF WS XK YE YT ZA ZM ZW US HK TW JP GB GB DE FR IN TR SG KR RU IE)
emoji_array=(🇦🇨 🇦🇩 🇦🇪 🇦🇫 🇦🇬 🇦🇮 🇦🇱 🇦🇲 🇦🇴 🇦🇶 🇦🇷 🇦🇸 🇦🇹 🇦🇺 🇦🇼 🇦🇽 🇦🇿 🇧🇦 🇧🇧 🇧🇩 🇧🇪 🇧🇫 🇧🇬 🇧🇭 🇧🇮 🇧🇯 🇧🇱 🇧🇲 🇧🇳 🇧🇴 🇧🇶 🇧🇷 🇧🇸 🇧🇹 🇧🇻 🇧🇼 🇧🇾 🇧🇿 🇨🇦 🇨🇨 🇨🇩 🇨🇫 🇨🇬 🇨🇭 🇨🇮 🇨🇰 🇨🇱 🇨🇲 🇨🇳 🇨🇴 🇨🇵 🇨🇷 🇨🇺 🇨🇻 🇨🇼 🇨🇽 🇨🇾 🇨🇿 🇩🇪 🇩🇬 🇩🇯 🇩🇰 🇩🇲 🇩🇴 🇩🇿 🇪🇦 🇪🇨 🇪🇪 🇪🇬 🇪🇭 🇪🇷 🇪🇸 🇪🇹 🇪🇺 🇫🇮 🇫🇯 🇫🇰 🇫🇲 🇫🇴 🇫🇷 🇬🇦 🇬🇧 🇬🇩 🇬🇪 🇬🇫 🇬🇬 🇬🇭 🇬🇮 🇬🇱 🇬🇲 🇬🇳 🇬🇵 🇬🇶 🇬🇷 🇬🇸 🇬🇹 🇬🇺 🇬🇼 🇬🇾 🇭🇰 🇭🇲 🇭🇳 🇭🇷 🇭🇹 🇭🇺 🇮🇨 🇮🇩 🇮🇪 🇮🇱 🇮🇲 🇮🇳 🇮🇴 🇮🇶 🇮🇷 🇮🇸 🇮🇹 🇯🇪 🇯🇲 🇯🇴 🇯🇵 🇰🇪 🇰🇬 🇰🇭 🇰🇮 🇰🇲 🇰🇳 🇰🇵 🇰🇷 🇰🇼 🇰🇾 🇰🇿 🇱🇦 🇱🇧 🇱🇨 🇱🇮 🇱🇰 🇱🇷 🇱🇸 🇱🇹 🇱🇺 🇱🇻 🇱🇾 🇲🇦 🇲🇨 🇲🇩 🇲🇪 🇲🇫 🇲🇬 🇲🇭 🇲🇰 🇲🇱 🇲🇲 🇲🇳 🇲🇴 🇲🇵 🇲🇶 🇲🇷 🇲🇸 🇲🇹 🇲🇺 🇲🇻 🇲🇼 🇲🇽 🇲🇾 🇲🇿 🇳🇦 🇳🇨 🇳🇪 🇳🇫 🇳🇬 🇳🇮 🇳🇱 🇳🇴 🇳🇵 🇳🇷 🇳🇺 🇳🇿 🇴🇲 🇵🇦 🇵🇪 🇵🇫 🇵🇬 🇵🇭 🇵🇰 🇵🇱 🇵🇲 🇵🇳 🇵🇷 🇵🇸 🇵🇹 🇵🇼 🇵🇾 🇶🇦 🇷🇪 🇷🇴 🇷🇸 🇷🇺 🇷🇼 🇸🇦 🇸🇧 🇸🇨 🇸🇩 🇸🇪 🇸🇬 🇸🇭 🇸🇮 🇸🇯 🇸🇰 🇸🇱 🇸🇲 🇸🇳 🇸🇴 🇸🇷 🇸🇸 🇸🇹 🇸🇻 🇸🇽 🇸🇾 🇸🇿 🇹🇦 🇹🇨 🇹🇩 🇹🇫 🇹🇬 🇹🇭 🇹🇯 🇹🇰 🇹🇱 🇹🇲 🇹🇳 🇹🇴 🇹🇷 🇹🇹 🇹🇻 🇹🇼 🇹🇿 🇺🇦 🇺🇬 🇺🇲 🇺🇳 🇺🇸 🇺🇾 🇺🇿 🇻🇦 🇻🇨 🇻🇪 🇻🇬 🇻🇮 🇻🇳 🇻🇺 🇼🇫 🇼🇸 🇽🇰 🇾🇪 🇾🇹 🇿🇦 🇿🇲 🇿🇼 美国 香港 台湾 日本 英国 UK 德国 法国 印度 土耳其 新加坡 韩国 俄罗斯 爱尔兰)
for i in "${!emoji_array[@]}"; do
if [[ $name == *${emoji_array[$i]}* ]]; then
code=${iso_array[$i]}
break
fi
done
if [ ! -z $code ]; then
echo $code | tr "[A-Z]" "[a-z]"
else
if echo $host | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
hostip=${host}
elif [ "$host" != "${host#*:[0-9a-fA-F]}" ]; then
hostip=${host}
else
hostip=$(nslookup ${host} | grep 'Address 1' | sed 's/Address 1: //g')
if echo $hostip | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then
hostip=${hostip}
else
hostip=$(cat /etc/ssr_ip)
fi
fi
lua /usr/share/vssr/iso_code.lua $hostip
fi

View File

@ -0,0 +1,11 @@
#!/bin/sh
mkdir -p /tmp/dnsmasq.ssr
awk '!/^$/&&!/^#/{printf("ipset=/.%s/'"gfwlist"'\n",$0)}' /etc/config/gfw.list > /tmp/dnsmasq.ssr/custom_forward.conf
awk '!/^$/&&!/^#/{printf("server=/.%s/'"127.0.0.1#5335"'\n",$0)}' /etc/config/gfw.list >> /tmp/dnsmasq.ssr/custom_forward.conf
awk '!/^$/&&!/^#/{printf("ipset=/.%s/'"blacklist"'\n",$0)}' /etc/config/black.list > /tmp/dnsmasq.ssr/blacklist_forward.conf
awk '!/^$/&&!/^#/{printf("server=/.%s/'"127.0.0.1#5335"'\n",$0)}' /etc/config/black.list >> /tmp/dnsmasq.ssr/blacklist_forward.conf
awk '!/^$/&&!/^#/{printf("ipset=/.%s/'"whitelist"'\n",$0)}' /etc/config/white.list > /tmp/dnsmasq.ssr/whitelist_forward.conf

View File

@ -0,0 +1,10 @@
#!/usr/bin/lua
------------------------------------------------
-- This file is converter ip to country iso code
-- @author Jerryk <jerrykuku@qq.com>
------------------------------------------------
local mm = require 'maxminddb'
local db = mm.open('/usr/share/vssr/GeoLite2-Country.mmdb')
local res = db:lookup(arg[1])
print(string.lower(res:get("country", "iso_code")))

View File

@ -0,0 +1,434 @@
#!/usr/bin/lua
------------------------------------------------
-- This file is part of the luci-app-ssr-plus 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 switch = '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 == "trojan" 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(info, "@")
local host = split(hostInfo[2], ":")
local password = hostInfo[1]
result.alias = UrlDecode(alias)
result.type = "trojan"
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, '=')
if t[1] == 'peer' then
result.peer = t[2]
result.tls = "1"
end
end
else
result.server_port = host[2]
end
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
local switch_enable = result.switch_enable
result.switch_enable = nil
result.hashkey = md5(jsonStringify(result))
result.alias = alias
result.switch_enable = switch_enable
local flag = luci.sys.exec('/usr/share/' .. name .. '/getflag.sh "' ..
result.alias .. '" ' .. result.server)
result.flag = string.gsub(flag, '\n', '')
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
if (old.alias ~= nil) then
log('忽略手动添加的节点: ' .. old.alias)
else
log('忽略手动添加的无效节点')
end
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)
ucic:set(name, section, "switch_enable", switch)
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('更新成功服务正在启动')
log("END SUBSCRIBE")
end
end
if subscribe_url and #subscribe_url > 0 then
xpcall(execute, function(e)
log(e)
log(debug.traceback())
log('发生错误, 正在恢复服务')
log("END SUBSCRIBE")
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

@ -0,0 +1,107 @@
#!/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', '') == '1' then
log('正在更新【广告屏蔽】数据库')
local need_process = 0
if nixio.fs.access("/usr/bin/wget-ssl") then
refresh_cmd="wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf"
need_process = 1
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
if need_process == 1 then
luci.sys.call("/usr/bin/vssr-ad")
end
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
-- --]]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,700 @@
@import url("flag-icon.min.css");
/*!
Pure v1.0.1
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/pure-css/pure/blob/master/LICENSE.md
*/
.pure-g {
letter-spacing: -.31em;
text-rendering: optimizespeed;
font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-webkit-flex-flow: row wrap;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
-webkit-align-content: flex-start;
-ms-flex-line-pack: start;
align-content: flex-start
}
@media all and (-ms-high-contrast:none),
(-ms-high-contrast:active) {
table .pure-g {
display: block
}
}
.opera-only :-o-prefocus,
.pure-g {
word-spacing: -.43em
}
.pure-u {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-g [class*=pure-u] {
font-family: sans-serif
}
.pure-u-1,
.pure-u-1-1,
.pure-u-1-12,
.pure-u-1-2,
.pure-u-1-24,
.pure-u-1-3,
.pure-u-1-4,
.pure-u-1-5,
.pure-u-1-6,
.pure-u-1-8,
.pure-u-10-24,
.pure-u-11-12,
.pure-u-11-24,
.pure-u-12-24,
.pure-u-13-24,
.pure-u-14-24,
.pure-u-15-24,
.pure-u-16-24,
.pure-u-17-24,
.pure-u-18-24,
.pure-u-19-24,
.pure-u-2-24,
.pure-u-2-3,
.pure-u-2-5,
.pure-u-20-24,
.pure-u-21-24,
.pure-u-22-24,
.pure-u-23-24,
.pure-u-24-24,
.pure-u-3-24,
.pure-u-3-4,
.pure-u-3-5,
.pure-u-3-8,
.pure-u-4-24,
.pure-u-4-5,
.pure-u-5-12,
.pure-u-5-24,
.pure-u-5-5,
.pure-u-5-6,
.pure-u-5-8,
.pure-u-6-24,
.pure-u-7-12,
.pure-u-7-24,
.pure-u-7-8,
.pure-u-8-24,
.pure-u-9-24 {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-u-1-24 {
width: 4.1667%
}
.pure-u-1-12,
.pure-u-2-24 {
width: 8.3333%
}
.pure-u-1-8,
.pure-u-3-24 {
width: 12.5%
}
.pure-u-1-6,
.pure-u-4-24 {
width: 16.6667%
}
.pure-u-1-5 {
width: 20%
}
.pure-u-5-24 {
width: 20.8333%
}
.pure-u-1-4,
.pure-u-6-24 {
width: 25%
}
.pure-u-7-24 {
width: 29.1667%
}
.pure-u-1-3,
.pure-u-8-24 {
width: 33.3333%
}
.pure-u-3-8,
.pure-u-9-24 {
width: 37.5%
}
.pure-u-2-5 {
width: 40%
}
.pure-u-10-24,
.pure-u-5-12 {
width: 41.6667%
}
.pure-u-11-24 {
width: 45.8333%
}
.pure-u-1-2,
.pure-u-12-24 {
width: 50%
}
.pure-u-13-24 {
width: 54.1667%
}
.pure-u-14-24,
.pure-u-7-12 {
width: 58.3333%
}
.pure-u-3-5 {
width: 60%
}
.pure-u-15-24,
.pure-u-5-8 {
width: 62.5%
}
.pure-u-16-24,
.pure-u-2-3 {
width: 66.6667%
}
.pure-u-17-24 {
width: 70.8333%
}
.pure-u-18-24,
.pure-u-3-4 {
width: 75%
}
.pure-u-19-24 {
width: 79.1667%
}
.pure-u-4-5 {
width: 80%
}
.pure-u-20-24,
.pure-u-5-6 {
width: 83.3333%
}
.pure-u-21-24,
.pure-u-7-8 {
width: 87.5%
}
.pure-u-11-12,
.pure-u-22-24 {
width: 91.6667%
}
.pure-u-23-24 {
width: 95.8333%
}
.pure-u-1,
.pure-u-1-1,
.pure-u-24-24,
.pure-u-5-5 {
width: 100%
}
.status {
margin: 1rem -0.5rem 1rem -0.5rem;
}
.block {
margin: 0.5rem 0.5rem;
padding: 0;
font-weight: normal;
font-style: normal;
line-height: 1;
font-family: inherit;
min-width: inherit;
overflow-x: auto;
overflow-y: hidden;
border: 1px solid rgba(0, 0, 0, .05);
border-radius: .375rem;
background-color: #fff;
box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15);
}
.img-con {
margin: 1rem;
}
.pure-img{
max-height: 100%;
width: auto;
}
.pure-imgw{
max-width: 100%;
height: auto;
}
.green {
font-size: 1.25rem;
color: #2dce89;
}
.red {
font-size: 1.25rem;
color: #fb6340;
}
.sk-text-success {
color: #2dce89;
}
.sk-text-error {
color: #fb6340;
}
.gap {
margin-right: 0.5rem;
}
.block img {
width: 48px;
height: auto;
float: right;
}
.pure-u-5-8 {
display: flex;
align-items: center;
}
.block h4 {
font-size: .8125rem;
font-weight: 600;
margin: 1rem;
color: #8898aa !important;
line-height: 1.8em;
}
.p-in5{
padding: 8px;
}
.flag-icon:before{
position: absolute;
}
.cbi-section-table-row {
position: relative;
background-color: #edf3f8;
margin: 10px !important;
padding: 8px 15px 8px 70px;
box-shadow: 0 0 5px 0 rgba(136, 152, 170, .75);
border-radius: .5rem;
border: 0;
color: #525f7f;
text-align: left;
line-height: 1.7em;
overflow: hidden;
letter-spacing: normal;
}
.cbi-section-table-row:hover{
background: #fff;
}
.cbi-section-table-row.fast{
background: #5e72e4;
color: #fff;
}
.cbi-section-table-row.fast .ssr-button{
color: #fff;
}
.cbi-section-table-row.fast .ssr-button:hover,
.cbi-section-table-row.fast .ssr-button:focus,
.cbi-section-table-row.fast .ssr-button:active {
color: #fff;
outline: 0;
text-decoration: none;
box-shadow: none;
}
.cbi-section-table-row.fast .host_con{
color: #fff !important;
}
.host_con.fast {
color: #6f9a37;
}
.host_con.nopass {
color: #dc3545;
}
.host_con.middle {
color: #fbc658;
}
.host_con.slow {
color: #fb6340;
}
.loadings {
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.7);
border-radius: .5rem;
left: 0;
top: 0;
z-index: 10;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
transition: all 0.3s;
}
.loadings.hide {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.loadings span {
animation: anim-rotate 2s infinite linear;
height: 30px;
}
.alias{
margin-bottom:1px;
}
.incon{
cursor: pointer;
}
.incon .pure-imgw{
transition: all 0.2s;
}
.incon:nth-child(2) {
position: absolute;
left: 0;
top: 0;
width: 61px;
height: 100%;
border: 0;
border-right: #d1dfed 1px solid;
background-position: top;
}
.incon:nth-child(2) .tp{
position: absolute;
left: 0;
bottom: 0;
height: 25%;
width: 100%;
text-align: center;
font-size: 12px;
line-height: 15px;
color: #fff;
background: #525f7f;
}
.incon:nth-child(3) {
padding-left: 0px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.incon:nth-child(5),
.incon:nth-child(6) {
display: none;
}
.cbi-section-table-cell {
text-align: right;
}
.ssr-button {
background: none;
color: #525f7f;
padding: 0 0 0 0;
height: auto;
line-height: 1.5em;
}
.ssr-button:hover,
.ssr-button:focus,
.ssr-button:active {
color: #525f7f;
outline: 0;
text-decoration: none;
box-shadow: none;
}
.cbi-section h3 {
margin-left: 10px;
margin-top: 1rem;
}
.cbi-button-check {
color: #fff !important;
background-color: #337ab7 !important;
border-color: #2e6da4 !important;
float: right;
margin-right: 18px;
margin-top: 1rem;
}
.cbi-section-table-cell {
position: relative;
}
.host_con {
position: absolute;
left: 0;
width: 70px;
text-align: left;
height: 22px;
font-weight: bold;
}
.host_ok {
color: #fff;
background: #2dce89;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
.host_error {
color: #fff;
background: #f5365c;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
footer.mobile-hide{
display: block;
}
.mar-10 {margin-left: 50px; margin-right: 10px;}
.status-bar{
position: fixed;
bottom: 0;
right: 0;
box-shadow: 0 0 2rem 0 rgba(136,152,170,.3);
color: #525f7f;
background: #fff;
z-index: 5;
box-sizing: border-box;
}
.status-bar .inner{
margin: 1em;
}
.status-bar .inner .flag{
height: 3em;
display: block;
float: left;
margin-right: 1em;
}
.status-bar .inner .status-info{
font-weight: bold;
}
.status-bar .icon-con{
height: 3em;
text-align: right;
}
@media screen and (max-width: 2000px) {
.pure-u-1-5 {
width: 25%;
}
}
@media screen and (max-width: 1600px) {
.pure-u-1-5 {
width: 33.33333333%;
}
}
@media screen and (max-width: 1200px) {
.pure-u-1-5 {
width: 50%;
}
.status .pure-u-1-5 {
width: 33.333%;
}
}
@media screen and (max-width: 700px) {
.pure-u-1-4 {
width: 50%;
}
.pure-u-1-2 {
width: 100%;
}
.pure-u-1-5 {
width: 100%;
font-size: 14px;
}
.status .pure-u-1-5 {
width: 50%;
}
.cbi-button-add {
position: fixed;
padding: 0.3rem 0.5rem;
z-index: 1000;
width: 50px !important;
height: 50px;
bottom: 90px;
right: 5px;
font-size: 16px;
border-radius: 50%;
display: block;
background-color: #fb6340 !important;
border-color: #fb6340 !important;
box-shadow: 0 0 1rem 0 rgba(136, 152, 170, .75);
}
}
.modals-bg {
position: fixed;
z-index: 999;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: rgba(255, 255, 255, 0.8);
display: none;
}
.modals {
position: fixed;
z-index: 100;
width: 60%;
height: 500px;
background: #172b4d;
left: 20%;
top: 15%;
color: #fff;
border-radius: 10px;
padding: 20px;
box-sizing: border-box;
-moz-box-sizing: border-box;
/* Firefox */
-webkit-box-sizing: border-box;
/* Safari */
}
.modals h2 {
padding: 0 !important;
}
.modals h3 {
font-size: 14px;
color: #f5365c !important;
background: transparent;
margin: 0;
padding: 0;
}
#log_content3 {
border: 0;
width: 99%;
height: calc(100% - 4rem);
font-family: 'Lucida Console';
font-size: 11px;
background: transparent;
color: #FFFFFF;
outline: none;
padding-left: 3px;
padding-right: 22px;
overflow: hidden
}
@media screen and (max-width: 1024px) {
.modals {
position: fixed;
z-index: 100;
width: 80%;
height: 500px;
background: #172b4d;
left: 10%;
top: 15%;
color: #fff;
border-radius: 10px;
padding: 20px;
}
}
@media screen and (max-width: 700px) {
.modals-bg {
position: fixed;
z-index: 100000;
}
.modals {
width: 100%;
height: 100%;
left: 0;
top: 0;
}
.status-bar .pure-u-1-2{
width: 50%;
}
.status-bar .inner .flag{
height: 3em;
display: block;
float: left;
margin-right: 1em;
}
.status-bar .icon-con{
height: 2.5em;
text-align: right;
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Some files were not shown because too many files have changed in this diff Show More