🌴 Sync 2026-03-05 23:51:42
No files matched your search
@@ -0,0 +1,124 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-xclient
|
||||
PKG_VERSION:=v2.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=xcode75
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
SUBMENU:=2. XClient
|
||||
TITLE:=LuCI Support for Xray
|
||||
PKGARCH:=all
|
||||
MAINTAINER:=xcode75
|
||||
DEPENDS:=+coreutils +coreutils-base64 +pdnsd-alt +coreutils-nohup +curl +dnsmasq-full +luci-compat +dns2socks +ipset +ip-full +iptables-mod-tproxy +libuci-lua +lua +luci-lib-jsonc +microsocks +tcping +resolveip +shadowsocks-libev-ss-local +shadowsocks-libev-ss-redir +unzip +uclient-fetch +wget-ssl +libmbedtls
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/config
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/xclient
|
||||
endef
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/prerm
|
||||
#!/bin/sh
|
||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||
echo "Removing rc.d symlink for xclient"
|
||||
/etc/init.d/xclient disable
|
||||
/etc/init.d/xclient stop
|
||||
echo "Removing firewall rule for xclient"
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete firewall.xclient
|
||||
commit firewall
|
||||
chmod -R 777 /usr/share/xclient 2>/dev/null
|
||||
EOF
|
||||
fi
|
||||
|
||||
exit 0
|
||||
endef
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/preinst
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||
rm -rf /tmp/dnsmasq.xclient 2>/dev/null
|
||||
mv /etc/config/xclient /etc/config/xclient.bak 2>/dev/null
|
||||
rm -rf /usr/lib/lua/luci/model/cbi/xclient 2>/dev/null
|
||||
rm -rf /usr/lib/lua/luci/view/xclient 2>/dev/null
|
||||
chmod -R 777 /usr/share/xclient 2>/dev/null
|
||||
fi
|
||||
|
||||
exit 0
|
||||
endef
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/postinst
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||
rm -rf /tmp/luci*
|
||||
mv /etc/config/xclient.bak /etc/config/xclient 2>/dev/null
|
||||
/etc/init.d/xclient disable 2>/dev/null
|
||||
chmod +x /etc/init.d/xclient 2>/dev/null
|
||||
/etc/init.d/xclient disable 2>/dev/null
|
||||
/etc/init.d/xclient stop 2>/dev/null
|
||||
chmod -R 777 /usr/share/xclient 2>/dev/null
|
||||
fi
|
||||
|
||||
exit 0
|
||||
endef
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/xclient
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/xclient
|
||||
|
||||
$(INSTALL_DATA) ./luasrc/controller/* $(1)/usr/lib/lua/luci/controller
|
||||
$(INSTALL_DATA) ./luasrc/model/cbi/xclient/* $(1)/usr/lib/lua/luci/model/cbi/xclient
|
||||
$(INSTALL_DATA) ./luasrc/view/xclient/* $(1)/usr/lib/lua/luci/view/xclient
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
|
||||
|
||||
$(INSTALL_BIN) ./root/etc/init.d/* $(1)/etc/init.d
|
||||
$(INSTALL_CONF) ./root/etc/config/* $(1)/etc/config
|
||||
$(INSTALL_CONF) ./root/etc/uci-defaults/* $(1)/etc/uci-defaults
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_DIR) $(1)/usr/share/xclient
|
||||
|
||||
$(INSTALL_DATA) ./root/usr/bin/* $(1)/usr/bin
|
||||
$(INSTALL_DATA) ./root/usr/share/xclient/* $(1)/usr/share/xclient
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-xclient.json $(1)/usr/share/rpcd/acl.d/luci-app-xclient.json
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-xclient.json $(1)/usr/share/luci/menu.d/luci-app-xclient.json
|
||||
|
||||
$(INSTALL_DIR) $(1)/www
|
||||
$(INSTALL_DIR) $(1)/www/luci-static
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/xclient
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/xclient/img
|
||||
cp -pR ./root/www/luci-static/xclient/img/*.png $(1)/www/luci-static/xclient/img
|
||||
cp -pR ./root/www/luci-static/xclient/img/*.svg $(1)/www/luci-static/xclient/img
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -0,0 +1,525 @@
|
||||
module("luci.controller.xclient", package.seeall)
|
||||
require "nixio.fs"
|
||||
require "luci.http"
|
||||
require "luci.sys"
|
||||
require "luci.model.uci"
|
||||
local json = require "luci.jsonc"
|
||||
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/xclient") then
|
||||
return
|
||||
end
|
||||
local page = entry({"admin", "services", "xclient"},alias("admin", "services", "xclient", "overview"), "XClient", 1)
|
||||
page.dependent = true
|
||||
page.acl_depends = {"luci-app-xclient"}
|
||||
|
||||
entry({"admin", "services", "xclient", "overview"},cbi("xclient/status"), "Overview", 5).leaf = true
|
||||
entry({"admin", "services", "xclient", "servers" },cbi("xclient/servers"), "Servers", 10).leaf = true
|
||||
entry({"admin", "services", "xclient", "server"},cbi("xclient/server"), nil).leaf = true
|
||||
entry({"admin", "services", "xclient", "dns"},cbi("xclient/dns"),"DNS", 20).leaf = true
|
||||
entry({"admin", "services", "xclient", "add-dns"},cbi("xclient/add-dns"), nil).leaf = true
|
||||
entry({"admin", "services", "xclient", "routing"},cbi("xclient/rules"),"Routing", 30).leaf = true
|
||||
entry({"admin", "services", "xclient", "rule"},cbi("xclient/rule"), nil).leaf = true
|
||||
entry({"admin", "services", "xclient", "update"},cbi("xclient/update"),"Update", 40).leaf = true
|
||||
entry({"admin", "services", "xclient", "log"},form("xclient/log"),"Log", 50).leaf = true
|
||||
|
||||
entry({"admin", "services", "xclient", "ping"}, call("act_ping")).leaf=true
|
||||
entry({"admin", "services", "xclient", "run"},call("action_run")).leaf=true
|
||||
entry({"admin", "services", "xclient", "subscribe"}, call("subscribe"))
|
||||
entry({"admin", "services", "xclient", "update_geosite"}, call("update_geosite")).leaf = true
|
||||
entry({"admin", "services", "xclient", "update_geoip"}, call("update_geoip")).leaf = true
|
||||
entry({"admin", "services", "xclient", "logstatus"},call("logstatus_check")).leaf=true
|
||||
entry({"admin", "services", "xclient", "readlog"},call("action_read")).leaf=true
|
||||
entry({'admin', 'services', "xclient", 'web'}, call('web_check')).leaf=true
|
||||
entry({"admin", "services", "xclient", "delete"}, call("act_delete"))
|
||||
|
||||
entry({"admin", "services", "xclient", "info"}, call("act_info"))
|
||||
entry({"admin", "services", "xclient", "logout"}, call("act_logout"))
|
||||
entry({"admin", "services", "xclient", "login"}, call("act_login"))
|
||||
entry({"admin", "services", "xclient", "login_check"}, call("act_login_check"))
|
||||
entry({"admin", "services", "xclient", "login_info"}, call("act_login_info"))
|
||||
|
||||
--entry({"admin", "services", "xclient", "xray_check"}, call("xray_check")).leaf = true
|
||||
--entry({"admin", "services", "xclient", "xray_update"}, call("xrayx_update")).leaf = true
|
||||
entry({"admin", "services", "xclient", "geolocation"}, call("geoloc")).leaf = true
|
||||
|
||||
entry({"admin", "services", "xclient", "geoip"}, call("geoip_check")).leaf=true
|
||||
entry({"admin", "services", "xclient", "geoipupdate"}, call("geoip_update")).leaf=true
|
||||
entry({"admin", "services", "xclient", "check_geoip"}, call("check_geoip_log")).leaf=true
|
||||
|
||||
entry({"admin", "services", "xclient", "geosite"}, call("geosite_check")).leaf=true
|
||||
entry({"admin", "services", "xclient", "geositeupdate"}, call("geosite_update")).leaf=true
|
||||
entry({"admin", "services", "xclient", "check_geosite"}, call("check_geosite_log")).leaf=true
|
||||
|
||||
entry({"admin", "services", "xclient", "version"}, call("check_version")).leaf=true
|
||||
entry({"admin", "services", "xclient", "check_latest"}, call("check_latest")).leaf=true
|
||||
|
||||
entry({"admin", "services", "xclient", "traffic"}, call("statistics")).leaf=true
|
||||
end
|
||||
|
||||
local uci = luci.model.uci.cursor()
|
||||
local Process_list = luci.sys.exec("busybox ps -w")
|
||||
|
||||
local function dns_status()
|
||||
if Process_list:find("xclient/bin/pdnsd") or (Process_list:find("xclient.dns") and Process_list:find("dns2socks.127.0.0.1.*127.0.0.1.5335")) then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function tcp_status()
|
||||
if Process_list:find("local.udp.xclient.retcp") or Process_list:find("tcp.udp.xclient.retcp") or Process_list:find("local.xclient.retcp") or Process_list:find("tcp.only.xclient.retcp") then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function udp_status()
|
||||
if Process_list:find("local.udp.xclient.retcp") or Process_list:find("tcp.udp.xclient.retcp") or Process_list:find("local.xclient.retcp") or Process_list:find("udp.only.xclient.reudp") then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function socks_status()
|
||||
if Process_list:find("local.udp.xclient.retcp") or Process_list:find("tcp.udp.xclient.local") or Process_list:find("local.xclient.retcp") then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function running_status()
|
||||
return luci.sys.call("busybox ps -w | grep xclient-retcp | grep -v grep >/dev/null") == 0
|
||||
end
|
||||
|
||||
local function http_write_json(content)
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(content or {code = 1})
|
||||
end
|
||||
|
||||
function subscribe()
|
||||
luci.http.prepare_content("application/json")
|
||||
local token, details, data, suburl
|
||||
token = uci:get("xclient", "config", "token")
|
||||
local site = uci:get("xclient", "config", "site")
|
||||
details = luci.sys.exec(string.format("curl -sL -H 'Content-Type: application/json' -d '{\"token\":\"%s\"}' -X POST %s/api/v1/details", token, site))
|
||||
if details then
|
||||
data = json.parse(details)
|
||||
if data and data.ret == 1 then
|
||||
suburl = data.url.xclient
|
||||
if suburl then
|
||||
uci:set("xclient", "config", "subscribe_url", suburl)
|
||||
uci:commit("xclient")
|
||||
luci.sys.call("/usr/bin/lua /usr/share/xclient/subscribe.lua >>/var/log/xclient.log")
|
||||
luci.http.write_json({ret = 1})
|
||||
end
|
||||
else
|
||||
luci.http.write_json({ret = 0})
|
||||
end
|
||||
else
|
||||
luci.http.write_json({ret = 0})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function act_login_check()
|
||||
luci.http.prepare_content("application/json")
|
||||
local token = uci:get("xclient", "config", "token")
|
||||
if token then
|
||||
luci.http.write_json({login = 1})
|
||||
else
|
||||
luci.http.write_json({login = 0})
|
||||
end
|
||||
end
|
||||
|
||||
function act_login()
|
||||
luci.http.prepare_content("application/json")
|
||||
local info, token
|
||||
local email = uci:get("xclient", "config", "email")
|
||||
local passwd = uci:get("xclient", "config", "passwd")
|
||||
local site = uci:get("xclient", "config", "site")
|
||||
if email and passwd then
|
||||
info = luci.sys.exec(string.format("curl -sL -H 'Content-Type: application/json' -d '{\"email\":\"%s\", \"passwd\":\"%s\"}' -X POST %s/api/v1/login", email, passwd, site))
|
||||
if info then
|
||||
info = json.parse(info)
|
||||
else
|
||||
luci.http.write_json({login = 0})
|
||||
end
|
||||
if info and info.ret == 1 then
|
||||
token = info.token
|
||||
uci:set("xclient", "config", "token", token)
|
||||
uci:set("xclient", "config", "auto_update_servers", "6")
|
||||
uci:commit("xclient")
|
||||
luci.http.write_json({login = 1})
|
||||
else
|
||||
luci.http.write_json({login = 0})
|
||||
end
|
||||
else
|
||||
luci.http.write_json({login = 0})
|
||||
end
|
||||
end
|
||||
|
||||
function login_info()
|
||||
uci:set("xclient", "config", "email", luci.http.formvalue("email"))
|
||||
uci:set("xclient", "config", "passwd", luci.http.formvalue("passwd"))
|
||||
uci:set("xclient", "config", "site", luci.http.formvalue("weburl"))
|
||||
uci:commit("xclient")
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
function act_logout()
|
||||
luci.http.prepare_content("application/json")
|
||||
local token, details, data
|
||||
token = uci:get("xclient", "config", "token")
|
||||
local site = uci:get("xclient", "config", "site")
|
||||
details = luci.sys.exec(string.format("curl -sL -H 'Content-Type: application/json' -d '{\"token\":\"%s\"}' -X POST %s/api/v1/logout", token, site))
|
||||
if details then
|
||||
data = json.parse(details)
|
||||
if data.ret == 1 then
|
||||
uci:delete("xclient", "config", "token")
|
||||
uci:delete("xclient", "config", "subscribe_url")
|
||||
uci:delete("xclient", "config", "auto_update_servers")
|
||||
uci:delete_all("xclient", "servers", function(s)
|
||||
if s.hashkey or s.isSubscribe then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end)
|
||||
uci:commit("xclient")
|
||||
luci.sys.call("/etc/init.d/xclient boot &")
|
||||
luci.http.write_json({logout = 1;})
|
||||
else
|
||||
luci.http.write_json({logout = 0;})
|
||||
end
|
||||
else
|
||||
luci.http.write_json({logout = 0})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function act_login_info()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
login_info = login_info();
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function action_run()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
dns_status = dns_status(),
|
||||
tcp_status = tcp_status(),
|
||||
udp_status = udp_status(),
|
||||
socks_status = socks_status(),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function act_ping()
|
||||
local e = {}
|
||||
local domain = luci.http.formvalue("domain")
|
||||
local port = luci.http.formvalue("port")
|
||||
local transport = luci.http.formvalue("transport")
|
||||
local wsPath = luci.http.formvalue("wsPath")
|
||||
local tls = luci.http.formvalue("tls")
|
||||
e.index = luci.http.formvalue("index")
|
||||
local iret = luci.sys.call("ipset add xclient_spec_wan_ac " .. domain .. " 2>/dev/null")
|
||||
if transport == "ws" then
|
||||
local prefix = tls=='1' and "https://" or "http://"
|
||||
local address = prefix..domain..':'..port..wsPath
|
||||
local result = luci.sys.exec("curl --http1.1 -m 2 -ksN -o /dev/null -w 'time_connect=%{time_connect}\nhttp_code=%{http_code}' -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==' -H 'Sec-WebSocket-Version: 13' "..address)
|
||||
e.socket = string.match(result,"http_code=(%d+)")=="101"
|
||||
e.ping = tonumber(string.match(result, "time_connect=(%d+.%d%d%d)"))*1000
|
||||
else
|
||||
local socket = nixio.socket("inet", "stream")
|
||||
socket:setopt("socket", "rcvtimeo", 3)
|
||||
socket:setopt("socket", "sndtimeo", 3)
|
||||
e.socket = socket:connect(domain, port)
|
||||
socket:close()
|
||||
-- e.ping = luci.sys.exec("ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'" % domain)
|
||||
-- if (e.ping == "") then
|
||||
e.ping = luci.sys.exec(string.format("echo -n $(tcping -q -c 1 -i 1 -t 2 -p %s %s 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null", port, domain))
|
||||
end
|
||||
if (iret == 0) then
|
||||
luci.sys.call(" ipset del xclient_spec_wan_ac " .. domain)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
|
||||
function logstatus_check()
|
||||
luci.http.prepare_content("text/plain; charset=utf-8")
|
||||
local fdp=tonumber(nixio.fs.readfile("/usr/share/xclient/logstatus_check")) or 0
|
||||
local f=io.open("/usr/share/xclient/xclient.txt", "r+")
|
||||
f:seek("set",fdp)
|
||||
local a=f:read(2048000) or ""
|
||||
fdp=f:seek()
|
||||
nixio.fs.writefile("/usr/share/xclient/logstatus_check",tostring(fdp))
|
||||
f:close()
|
||||
if nixio.fs.access("/usr/share/xclient/logstatus_check") then
|
||||
luci.http.write(a)
|
||||
else
|
||||
luci.http.write(a.."\0")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function readlog()
|
||||
return luci.sys.exec("sed -n '$p' /usr/share/xclient/readlog.txt 2>/dev/null")
|
||||
end
|
||||
|
||||
|
||||
function action_read()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
readlog = readlog();
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function check(host, port)
|
||||
local socket = nixio.socket("inet", "stream")
|
||||
socket:setopt("socket", "rcvtimeo", 3)
|
||||
socket:setopt("socket", "sndtimeo", 3)
|
||||
local ret = socket:connect(host, port)
|
||||
socket:close()
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
function web_check()
|
||||
local e = {}
|
||||
local port = 80
|
||||
e.baidu = check('www.baidu.com', port)
|
||||
e.taobao = check('www.taobao.com', port)
|
||||
e.google = check('www.google.com', port)
|
||||
e.youtube = check('www.youtube.com', port)
|
||||
luci.http.prepare_content('application/json')
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
|
||||
function update_geoip()
|
||||
luci.sys.call("lua /usr/share/xclient/geoip.lua log > /dev/null 2>&1 &")
|
||||
http_write_json()
|
||||
end
|
||||
|
||||
function update_geosite()
|
||||
luci.sys.call("lua /usr/share/xclient/geosite.lua log > /dev/null 2>&1 &")
|
||||
http_write_json()
|
||||
end
|
||||
|
||||
function act_delete()
|
||||
luci.sys.call("/etc/init.d/xclient reload &")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "xclient", "servers"))
|
||||
end
|
||||
|
||||
|
||||
function geoloc()
|
||||
local geo, data
|
||||
geo = luci.sys.exec(string.format("curl -sL -H 'User-Agent: luci-app-xclient' 'Content-Type: application/json' -X GET https://query-geolocation.herokuapp.com/?lang=en"))
|
||||
if geo then
|
||||
data = json.parse(geo)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
code = data.code,
|
||||
location = data.location
|
||||
})
|
||||
end
|
||||
|
||||
local function geoipcheck()
|
||||
if nixio.fs.access("/var/run/geo_ip_update_error") then
|
||||
return "0"
|
||||
elseif nixio.fs.access("/var/run/geo_ip_update") then
|
||||
return "1"
|
||||
elseif nixio.fs.access("/var/run/geo_ip_down_complete") then
|
||||
return "2"
|
||||
end
|
||||
end
|
||||
|
||||
local function geositecheck()
|
||||
if nixio.fs.access("/var/run/geo_site_update_error") then
|
||||
return "0"
|
||||
elseif nixio.fs.access("/var/run/geo_site_update") then
|
||||
return "1"
|
||||
elseif nixio.fs.access("/var/run/geo_site_down_complete") then
|
||||
return "2"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function geoip_check()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
geoipcheck = geoipcheck();
|
||||
})
|
||||
end
|
||||
|
||||
function geosite_check()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
geositecheck = geositecheck();
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function geoip_update()
|
||||
nixio.fs.writefile("/var/run/geoiplog","0")
|
||||
luci.sys.exec("(rm /var/run/geo_ip_update_error ; touch /var/run/geo_ip_update ; sh /usr/share/xclient/geoip.sh >/tmp/geo_ip_update.txt 2>&1 || touch /var/run/geo_ip_update_error ;rm /var/run/geo_ip_update) &")
|
||||
end
|
||||
|
||||
function geosite_update()
|
||||
nixio.fs.writefile("/var/run/geositelog","0")
|
||||
luci.sys.exec("(rm /var/run/geo_site_update_error ; touch /var/run/geo_site_update ; sh /usr/share/xclient/geosite.sh >/tmp/geo_site_update.txt 2>&1 || touch /var/run/geo_site_update_error ;rm /var/run/geo_site_update) &")
|
||||
end
|
||||
|
||||
|
||||
function check_geoip_log()
|
||||
luci.http.prepare_content("text/plain; charset=utf-8")
|
||||
local fdp=tonumber(nixio.fs.readfile("/var/run/geoiplog")) or 0
|
||||
local f=io.open("/tmp/geo_ip_update.txt", "r+")
|
||||
f:seek("set",fdp)
|
||||
local a=f:read(2048000) or ""
|
||||
fdp=f:seek()
|
||||
nixio.fs.writefile("/var/run/geoiplog",tostring(fdp))
|
||||
f:close()
|
||||
if nixio.fs.access("/var/run/geo_ip_update") then
|
||||
luci.http.write(a)
|
||||
else
|
||||
luci.http.write(a.."\0")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function check_geosite_log()
|
||||
luci.http.prepare_content("text/plain; charset=utf-8")
|
||||
local fdp=tonumber(nixio.fs.readfile("/var/run/geositelog")) or 0
|
||||
local f=io.open("/tmp/geo_site_update.txt", "r+")
|
||||
f:seek("set",fdp)
|
||||
local a=f:read(2048000) or ""
|
||||
fdp=f:seek()
|
||||
nixio.fs.writefile("/var/run/geositelog",tostring(fdp))
|
||||
f:close()
|
||||
if nixio.fs.access("/var/run/geo_site_update") then
|
||||
luci.http.write(a)
|
||||
else
|
||||
luci.http.write(a.."\0")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function core_version()
|
||||
if nixio.fs.access("/usr/bin/xray") then
|
||||
if nixio.fs.access("/usr/share/xclient/core_version") then
|
||||
return luci.sys.exec("sed -n 1p /usr/share/xclient/core_version")
|
||||
else
|
||||
return luci.sys.exec("/usr/bin/xray -version | awk '{print $2}' | sed -n 1P")
|
||||
end
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
end
|
||||
|
||||
local function latest_core()
|
||||
if nixio.fs.access("/usr/share/xclient/new_core") then
|
||||
return luci.sys.exec("sed -n 1p /usr/share/xclient/new_core")
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function geosite_version()
|
||||
if nixio.fs.access("/usr/bin/geosite.dat") then
|
||||
if nixio.fs.access("/usr/share/xclient/geosite_version") then
|
||||
return luci.sys.exec("sed -n 1p /usr/share/xclient/geosite_version")
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
end
|
||||
|
||||
local function geoip_version()
|
||||
if nixio.fs.access("/usr/bin/geoip.dat") then
|
||||
if nixio.fs.access("/usr/share/xclient/geoip_version") then
|
||||
return luci.sys.exec("sed -n 1p /usr/share/xclient/geoip_version")
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
else
|
||||
return "--"
|
||||
end
|
||||
end
|
||||
|
||||
function check_latest()
|
||||
return luci.sys.exec("sh /usr/share/xclient/core_version.sh")
|
||||
end
|
||||
|
||||
|
||||
function check_version()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({
|
||||
core_version = core_version(),
|
||||
geoip_version = geoip_version(),
|
||||
geosite_version = geosite_version(),
|
||||
latest_core = latest_core()
|
||||
})
|
||||
end
|
||||
|
||||
function statistics()
|
||||
luci.http.prepare_content("application/json")
|
||||
|
||||
local up, check_uplink
|
||||
check_uplink = luci.sys.exec(string.format("xray api stats --server=127.0.0.1:8888 -name \"outbound>>>proxy_outbound>>>traffic>>>uplink\""))
|
||||
if check_uplink then
|
||||
up = json.parse(check_uplink)
|
||||
if up ~= nil then
|
||||
if up.stat.value ~= nil then
|
||||
local uplink = up.stat.value
|
||||
else
|
||||
local uplink = 0
|
||||
end
|
||||
else
|
||||
local uplink = 0
|
||||
end
|
||||
else
|
||||
local uplink = 0
|
||||
end
|
||||
|
||||
local down, check_downlink
|
||||
check_downlink = luci.sys.exec(string.format("xray api stats --server=127.0.0.1:8888 -name \"outbound>>>proxy_outbound>>>traffic>>>downlink\""))
|
||||
if check_downlink then
|
||||
down = json.parse(check_downlink)
|
||||
if down ~= nil then
|
||||
if down.stat.value ~= nil then
|
||||
local downlink = down.stat.value
|
||||
else
|
||||
local downlink = 0
|
||||
end
|
||||
else
|
||||
local downlink = 0
|
||||
end
|
||||
|
||||
else
|
||||
local downlink = 0
|
||||
end
|
||||
|
||||
luci.http.write_json({
|
||||
downlink = downlink,
|
||||
uplink = uplink
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
local m, s, o
|
||||
local xclient = "xclient"
|
||||
local uci = luci.model.uci.cursor()
|
||||
local fs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local sid = arg[1]
|
||||
|
||||
|
||||
m = Map(xclient, "Add/Edit DNS Rule")
|
||||
--m.pageaction = false
|
||||
m.redirect = luci.dispatcher.build_url("admin/services/xclient/dns")
|
||||
if m.uci:get(xclient, sid) ~= "dns" then
|
||||
luci.http.redirect(m.redirect)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
s = m:section(NamedSection, sid, "dns")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
|
||||
o = s:option(Value, "address", "Address")
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
o = s:option(Value, "port", "Port")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(DynamicList, "domains", "Domains")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(DynamicList, "expectIPs", "expectIPs")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "skipFallback", "skipFallback")
|
||||
o.rmempty = true
|
||||
o.description = "Refer To Documentation"
|
||||
..translatef("<a href=\"%s\" target=\"_blank\">" .. "%s".."</a>", "https://xtls.github.io/Xray-docs-next/config/dns.html", " Here")
|
||||
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,115 @@
|
||||
local m, s, o
|
||||
local xclient = "xclient"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
m = Map(xclient)
|
||||
|
||||
s = m:section(TypedSection, "global", "DNS Servers")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(DynamicList, "dns_servers", "Servers")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, 'clientIp', 'Client Ip')
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, 'queryStrategy', 'Query Strategy')
|
||||
o:value("UseIP", "UseIP")
|
||||
o:value("UseIPv4", "UseIPv4")
|
||||
o:value("UseIPv6", "UseIPv6")
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
o = s:option(ListValue, 'disableCache', 'Disable Cache')
|
||||
o:value("0", "False")
|
||||
o:value("1", "True")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, 'disableFallback', 'Disable Fallback')
|
||||
o:value("0", "False")
|
||||
o:value("1", "True")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, 'disableFallbackIfMatch', 'Disable Fallback If Match')
|
||||
o:value("0", "False")
|
||||
o:value("1", "True")
|
||||
o.rmempty = true
|
||||
o.description = "Refer To Documentation"
|
||||
..translatef("<a href=\"%s\" target=\"_blank\">" .. "%s".."</a>", "https://xtls.github.io/Xray-docs-next/config/dns.html", " Here")
|
||||
|
||||
y = Map("xclient")
|
||||
y.pageaction = true
|
||||
|
||||
x = y:section(TypedSection, "dns", "DNS Rules")
|
||||
x.anonymous = true
|
||||
x.addremove = true
|
||||
x.sortable = true
|
||||
|
||||
x.template = "cbi/tblsection"
|
||||
x.extedit = luci.dispatcher.build_url("admin/services/xclient/add-dns/%s")
|
||||
function x.create(...)
|
||||
local sid = TypedSection.create(...)
|
||||
if sid then
|
||||
luci.http.redirect(x.extedit % sid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
o = x:option(DummyValue, "address", "Address")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "port", "Port")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
|
||||
o = x:option(DummyValue, "domains", "Domains")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"domains"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "expectIPs", "ExpectIPs")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"expectIPs"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(Flag, "skipFallback", "SkipFallback")
|
||||
o.rmempty = false
|
||||
o.default = o.skipFallback
|
||||
o.cfgvalue = function(...)
|
||||
return Flag.cfgvalue(...) or "0"
|
||||
end
|
||||
|
||||
|
||||
local apply = luci.http.formvalue("cbi.apply")
|
||||
if apply then
|
||||
m.uci:commit("xclient")
|
||||
y.uci:commit("xclient")
|
||||
luci.sys.call("/etc/init.d/xclient boot >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
return m,y
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
local m
|
||||
|
||||
m = Map("xclient")
|
||||
s = m:section(TypedSection, "log")
|
||||
m.pageaction = false
|
||||
s.anonymous = true
|
||||
s.addremove=false
|
||||
|
||||
l = s:option(TextValue, "log")
|
||||
l.template = "xclient/logs"
|
||||
|
||||
return m
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
local m, s, o
|
||||
local xclient = "xclient"
|
||||
local uci = luci.model.uci.cursor()
|
||||
local fs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local sid = arg[1]
|
||||
|
||||
|
||||
m = Map(xclient, "Add/Edit Rule")
|
||||
--m.pageaction = false
|
||||
m.redirect = luci.dispatcher.build_url("admin/services/xclient/routing")
|
||||
if m.uci:get(xclient, sid) ~= "rule" then
|
||||
luci.http.redirect(m.redirect)
|
||||
return
|
||||
end
|
||||
|
||||
s = m:section(NamedSection, sid, "rule")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(ListValue, "domainMatcher", "Domain Matcher")
|
||||
o:value("linear", "linear")
|
||||
o:value("mph", "mph")
|
||||
o.default = "linear"
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(DynamicList, "domain", "Domain")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(DynamicList, "ip", "IP")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "port", "Port")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "sourcePort", "SourcePort")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "network", "Network")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(DynamicList, "source", "Source")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(DynamicList, "inboundTag", "InboundTag")
|
||||
o:value("proxy_inbound", "PROXY_INBOUND")
|
||||
o:value("dns_inbound", "DNS_INBOUND")
|
||||
o:value("socks_inbound", "SOCKS_INBOUND")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(DynamicList, "protocol", "Protocol")
|
||||
o:value("http", "HTTP")
|
||||
o:value("tls", "TLS")
|
||||
o:value("bittorrent", "BITTORRENT")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, "outboundTag", "OutboundTag")
|
||||
o:value("proxy_outbound", "PROXY_OUTBOUND")
|
||||
o:value("dns_outbound", "DNS_OUTBOUND")
|
||||
o:value("direct", "DIRECT")
|
||||
o:value("block", "BLOCK")
|
||||
o.rmempty = false
|
||||
o.description = "Refer To Documentation"
|
||||
..translatef("<a href=\"%s\" target=\"_blank\">" .. "%s".."</a>", "https://xtls.github.io/Xray-docs-next/config/routing.html", " Here")
|
||||
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,150 @@
|
||||
require "luci.http"
|
||||
local uci = luci.model.uci.cursor()
|
||||
local q, y
|
||||
|
||||
z = Map("xclient")
|
||||
s = z:section(TypedSection, "global", "Routing Settings")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(ListValue, 'routing_strategy', 'Query Strategy')
|
||||
o:value("AsIs", "AsIs")
|
||||
o:value("IPIfNonMatch", "IPIfNonMatch")
|
||||
o:value("IPOnDemand", "IPOnDemand")
|
||||
o.default = "IPIfNonMatch"
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, "domainMatcher", "Domain Matcher")
|
||||
o:value("linear", "linear")
|
||||
o:value("mph", "mph")
|
||||
o.default = "linear"
|
||||
o.rmempty = true
|
||||
o.description = "Refer To Documentation"
|
||||
..translatef("<a href=\"%s\" target=\"_blank\">" .. "%s".."</a>", "https://xtls.github.io/Xray-docs-next/config/routing.html", " Here")
|
||||
|
||||
|
||||
y = Map("xclient")
|
||||
y.pageaction = true
|
||||
|
||||
x = y:section(TypedSection, "rule", "Routing Rules")
|
||||
x.anonymous = true
|
||||
x.addremove = true
|
||||
x.sortable = true
|
||||
|
||||
x.template = "cbi/tblsection"
|
||||
x.extedit = luci.dispatcher.build_url("admin/services/xclient/rule/%s")
|
||||
function x.create(...)
|
||||
local sid = TypedSection.create(...)
|
||||
if sid then
|
||||
luci.http.redirect(x.extedit % sid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "domainMatcher", "Domain Matcher")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
|
||||
o = x:option(DummyValue, "domain", "Domain")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"domain"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "ip", "IP")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"ip"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "port", "Port")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "sourcePort", "SourcePort")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "network", "Network")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "source", "Source")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"source"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "inboundTag", "Inboundtag")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"inboundTag"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
o = x:option(DummyValue, "protocol", "Protocol")
|
||||
o.rawhtml=true
|
||||
function o.cfgvalue(t,e)
|
||||
local t,e=t.map:get(e,"protocol"),""
|
||||
if t then
|
||||
for a,t in pairs(t)do
|
||||
e=e..t.."<br>"
|
||||
end
|
||||
return e
|
||||
else
|
||||
return"—"
|
||||
end
|
||||
end
|
||||
|
||||
o = x:option(DummyValue, "outboundTag", "Outboundtag")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "—"
|
||||
end
|
||||
|
||||
|
||||
local apply = luci.http.formvalue("cbi.apply")
|
||||
if apply then
|
||||
y.uci:commit("xclient")
|
||||
z.uci:commit("xclient")
|
||||
luci.sys.call("/etc/init.d/xclient boot >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
|
||||
return z, y
|
||||
@@ -0,0 +1,378 @@
|
||||
require "nixio.fs"
|
||||
require "luci.sys"
|
||||
require "luci.http"
|
||||
local m, s, o
|
||||
local sid = arg[1]
|
||||
local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
|
||||
|
||||
function is_finded(e)
|
||||
return luci.sys.exec('type -t -p "%s"' % e) ~= "" and true or false
|
||||
end
|
||||
|
||||
local server_table = {}
|
||||
|
||||
local encrypt_methods_v2ray_ss = {
|
||||
-- xray_ss
|
||||
"none",
|
||||
"plain",
|
||||
-- aead
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-poly1305",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm",
|
||||
"2022-blake3-aes-256-gcm"
|
||||
}
|
||||
|
||||
local securitys = {
|
||||
-- vmess
|
||||
"auto",
|
||||
"aes-128-gcm",
|
||||
"chacha20-poly1305"
|
||||
}
|
||||
|
||||
local flows = {
|
||||
-- xlts
|
||||
"xtls-rprx-vision",
|
||||
"xtls-rprx-vision-udp443"
|
||||
}
|
||||
|
||||
m = Map("xclient", "Add/Edit Server")
|
||||
m.redirect = luci.dispatcher.build_url("admin/services/xclient/servers")
|
||||
if m.uci:get("xclient", 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(ListValue, "protocol", "Protocol")
|
||||
o:value("vmess", "Vmess")
|
||||
o:value("vless", "Vless")
|
||||
o:value("trojan", "Trojan")
|
||||
o:value("shadowsocks", "Shadowsocks")
|
||||
o:value("shadowsocks-plugin", "Shadowsocks-Plugin")
|
||||
|
||||
o = s:option(Value, "alias", "Remarks")
|
||||
|
||||
o = s:option(Value, "server", "Server Address")
|
||||
o.datatype = "host"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "server_port", "Server Port")
|
||||
o.datatype = "port"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "password", "Password")
|
||||
o.password = true
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "shadowsocks"})
|
||||
o:depends({protocol = "shadowsocks-plugin"})
|
||||
o:depends({protocol = "trojan"})
|
||||
|
||||
o = s:option(ListValue, "encrypt_method_v2ray_ss", "Encrypt Method")
|
||||
for _, v in ipairs(encrypt_methods_v2ray_ss) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "shadowsocks"})
|
||||
o:depends({protocol = "shadowsocks-plugin"})
|
||||
|
||||
|
||||
o = s:option(Flag, "ivCheck", "Bloom Filter")
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "shadowsocks"})
|
||||
o.default = "1"
|
||||
|
||||
-- Shadowsocks Plugin
|
||||
o = s:option(ListValue, "plugin", "Plugin")
|
||||
-- o:value("none", "None")
|
||||
if is_finded("xray-plugin") then
|
||||
o:value("xray-plugin", "xray-plugin")
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("protocol", "shadowsocks-plugin")
|
||||
|
||||
o = s:option(Value, "plugin_opts", "Plugin Opts")
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "shadowsocks-plugin", plugin = "v2ray-plugin"})
|
||||
o:depends({protocol = "shadowsocks-plugin", plugin = "xray-plugin"})
|
||||
|
||||
-- VmessId
|
||||
o = s:option(Value, "vmess_id", "UUID")
|
||||
o.rmempty = true
|
||||
o.default = uuid
|
||||
o:depends({protocol = "vmess"})
|
||||
o:depends({protocol = "vless"})
|
||||
|
||||
-- VLESS Encryption
|
||||
o = s:option(Value, "vless_encryption", "Vless Encryption")
|
||||
o.rmempty = true
|
||||
o.default = "none"
|
||||
o:depends({protocol = "vless"})
|
||||
|
||||
o = s:option(ListValue, "vmess_encryption", "Encrypt Method")
|
||||
for _, v in ipairs(securitys) do
|
||||
o:value(v, v:upper())
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "vmess"})
|
||||
|
||||
-- Transport
|
||||
o = s:option(ListValue, "transport", "Transport")
|
||||
o:value("tcp", "TCP")
|
||||
o:value("kcp", "mKCP")
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("h2", "HTTP/2")
|
||||
o:value("grpc", "gRPC")
|
||||
o:value("quic", "QUIC")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ TCP ]]--
|
||||
|
||||
o = s:option(ListValue, "tcp_guise", "Camouflage Type")
|
||||
o:value("none", "None")
|
||||
o:value("http", "HTTP")
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "vmess",transport = "tcp"})
|
||||
o:depends({protocol = "trojan" ,transport = "tcp"})
|
||||
o:depends({protocol = "vless",transport = "tcp"})
|
||||
|
||||
|
||||
o = s:option(Value, "http_host", "HTTP Host")
|
||||
o:depends("tcp_guise", "http")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "http_path", "HTTP Path")
|
||||
o:depends("tcp_guise", "http")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ WS ]]--
|
||||
|
||||
o = s:option(Value, "ws_host", "WebSocket Host")
|
||||
o:depends({transport = "ws"})
|
||||
o.datatype = "hostname"
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "ws_path", "WebSocket Path")
|
||||
o:depends("transport", "ws")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ H2 ]]--
|
||||
|
||||
o = s:option(Value, "h2_host", "HTTP/2 Host")
|
||||
o:depends("transport", "h2")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "h2_path", "HTTP/2 Path")
|
||||
o:depends("transport", "h2")
|
||||
o.rmempty = true
|
||||
|
||||
-- gRPC
|
||||
o = s:option(Value, "serviceName", "ServiceName")
|
||||
o:depends("transport", "grpc")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "initial_windows_size", "Initial Windows Size")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "grpc")
|
||||
o.default = 0
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "health_check", "Health Check")
|
||||
o:depends("transport", "h2")
|
||||
o:depends("transport", "grpc")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "read_idle_timeout", "Idle Timeout")
|
||||
o.datatype = "uinteger"
|
||||
o:depends({health_check = true, transport = "h2"})
|
||||
o.default = 60
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "idle_timeout", "Idle Timeout")
|
||||
o.datatype = "uinteger"
|
||||
o:depends({health_check = true, transport = "grpc"})
|
||||
o.default = 60
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "health_check_timeout", "Health Check Timeout")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("health_check", 1)
|
||||
o.default = 20
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "permit_without_stream", "Permit Without Stream")
|
||||
o:depends({health_check = true, transport = "grpc"})
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
-- [[ mKCP ]]--
|
||||
o = s:option(ListValue, "kcp_guise", "Camouflage Type")
|
||||
o:depends("transport", "kcp")
|
||||
o:value("none", "None")
|
||||
o:value("srtp", "VideoCall (SRTP)")
|
||||
o:value("utp", "BitTorrent (uTP)")
|
||||
o:value("wechat-video", "WechatVideo")
|
||||
o:value("dtls", "DTLS 1.2")
|
||||
o:value("wireguard", "WireGuard")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "mtu", "MTU")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 1350
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "tti", "TTI")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 50
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "uplink_capacity", "Uplink Capacity")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 5
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "downlink_capacity", "Downlink Capacity")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 20
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "read_buffer_size", "Read Buffer Size")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 2
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "write_buffer_size", "Write Buffer Size")
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 2
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "seed", "Seed (optional)")
|
||||
o:depends({protocol = "vless", transport = "kcp"})
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "congestion", "Congestion")
|
||||
o:depends("transport", "kcp")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ QUIC ]]--
|
||||
o = s.option(ListValue,"quic_security", "[quic] Security")
|
||||
o:depends("transport", "quic")
|
||||
o.default = "none"
|
||||
o:value("none", "none")
|
||||
o:value("aes-128-gcm", "aes-128-gcm")
|
||||
o:value("chacha20-poly1305", "chacha20-poly1305")
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
o = s.option(Value, "quic_key", "[quic] Key")
|
||||
o:depends("transport", "quic")
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
o = s.option(ListValue, "quic_guise", "[quic] Fake Header Type")
|
||||
o:depends("transport", "quic")
|
||||
o.default = "none"
|
||||
o:value("none", _("None"))
|
||||
o:value("srtp", _("VideoCall (SRTP)"))
|
||||
o:value("utp", _("BitTorrent (uTP)"))
|
||||
o:value("wechat-video", _("WechatVideo"))
|
||||
o:value("dtls", "DTLS 1.2")
|
||||
o:value("wireguard", "WireGuard")
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
-- [[ security ]]--
|
||||
o = s:option(ListValue, "security", "Security")
|
||||
o.default = "none"
|
||||
o:value("none", "none")
|
||||
o:value("tls", "tls")
|
||||
o:value("reality", "reality")
|
||||
o.rmempty = false
|
||||
|
||||
-- Flow
|
||||
o = s:option(Value, "vless_flow", "Flow")
|
||||
for _, v in ipairs(flows) do
|
||||
o:value(v, v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o.default = "xtls-rprx-vision"
|
||||
o:depends({protocol = "vless", transport = "tcp"})
|
||||
|
||||
o = s:option(Value, "tls_host", "TLS Host / serverName")
|
||||
o.datatype = "hostname"
|
||||
o:depends({security = 'tls', security = 'reality'})
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "rejectUnknownSni", "Reject Unknown Sni")
|
||||
o:depends({security = 'tls'})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ Alpn ]]--
|
||||
o = s:option(DynamicList, "alpn", "Alpn")
|
||||
o:value("h2", "h2")
|
||||
o:value("http/1.1", "http/1.1")
|
||||
o:depends({security = 'tls'})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ uTLS ]]--
|
||||
o = s:option(ListValue, "fingerprint", "Finger Print")
|
||||
o:value("", "disable")
|
||||
o:value("firefox", "firefox")
|
||||
o:value("chrome", "chrome")
|
||||
o:value("safari", "safari")
|
||||
o:value("randomized", "randomized")
|
||||
o:depends({security = 'tls', security = 'reality'})
|
||||
o.default = "chrome"
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ ShortId ]]--
|
||||
o = s:option(Value, "shortId", "ShortId")
|
||||
o.default = ""
|
||||
o:depends({security = 'reality'})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ publicKey ]]--
|
||||
o = s:option(Value, "publicKey", "publicKey")
|
||||
o.default = ""
|
||||
o:depends({security = 'reality'})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ spiderX ]]--
|
||||
o = s:option(Value, "spiderX", "spiderX")
|
||||
o.default = ""
|
||||
o:depends({security = 'reality'})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ allowInsecure ]]--
|
||||
o = s:option(Flag, "insecure", "AllowInsecure")
|
||||
o.rmempty = true
|
||||
o:depends({security = 'tls'})
|
||||
|
||||
-- [[ Mux ]]--
|
||||
o = s:option(Flag, "mux", "Mux")
|
||||
o.rmempty = true
|
||||
o:depends({protocol = "shadowsocks"})
|
||||
|
||||
o = s:option(Value, "concurrency", "Concurrency")
|
||||
o.datatype = "uinteger"
|
||||
o.rmempty = true
|
||||
o.default = "8"
|
||||
o:depends("mux", "1")
|
||||
|
||||
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,148 @@
|
||||
require "luci.http"
|
||||
require "luci.dispatcher"
|
||||
require "luci.model.uci"
|
||||
local m, s, o,c
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local server_count = 0
|
||||
uci:foreach("xclient", "servers", function(s)
|
||||
server_count = server_count + 1
|
||||
end)
|
||||
|
||||
font_blue = [[<b color="blue">]]
|
||||
font_green = [[<b style=color:green>]]
|
||||
font_red = [[<b style=color:red>]]
|
||||
font_off = [[</b>]]
|
||||
bold_on = [[<strong>]]
|
||||
bold_off = [[</strong>]]
|
||||
|
||||
|
||||
c = Map("xclient")
|
||||
|
||||
-- Server Subscribe
|
||||
s = c:section(TypedSection, "xclient", "Account Settings")
|
||||
s.anonymous = true
|
||||
|
||||
|
||||
o = s:option(Value, "site")
|
||||
o.title = "Website Url"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "email")
|
||||
o.title = "Email"
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "passwd")
|
||||
o.title = "Password"
|
||||
o.password = true
|
||||
o.rmempty = true
|
||||
|
||||
|
||||
if c.uci:get("xclient", "config", "token") then
|
||||
o = s:option(Value, "auto_update_servers")
|
||||
o.title = "Auto Update (h)"
|
||||
o.default=6
|
||||
o.rmempty = true
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "login", "Status")
|
||||
if not c.uci:get("xclient", "config", "token") then
|
||||
o.template = "xclient/login"
|
||||
else
|
||||
o.template = "xclient/logout"
|
||||
end
|
||||
|
||||
|
||||
local t = {
|
||||
{Delete_Servers}
|
||||
}
|
||||
|
||||
b = c:section(Table, t)
|
||||
|
||||
|
||||
o = b:option(Button,"Delete_Servers")
|
||||
o.inputtitle = "Delete All Servers"
|
||||
o.inputstyle = "delete"
|
||||
o.write = function()
|
||||
uci:delete_all("xclient", "servers", function(s)
|
||||
if s.hashkey or s.isSubscribe then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end)
|
||||
uci:save("xclient")
|
||||
uci:commit("xclient")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "xclient", "delete"))
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
m = Map("xclient")
|
||||
s = m:section(TypedSection, "servers", "Server List")
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = false
|
||||
s.extedit = luci.dispatcher.build_url("admin", "services", "xclient", "server", "%s")
|
||||
function s.create(...)
|
||||
local sid = TypedSection.create(...)
|
||||
if sid then
|
||||
luci.http.redirect(s.extedit % sid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "type", "Type")
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "protocol") or Value.cfgvalue(self, section) or "N/A"
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "alias", "Alias")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "N/A"
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "server", "Server Address")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "N/A"
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "server_port", "Server Port")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "N/A"
|
||||
end
|
||||
|
||||
|
||||
o = s:option(DummyValue, "transport", "Transport")
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "N/A"
|
||||
end
|
||||
|
||||
|
||||
o = s:option(DummyValue, "server" ,"Latency")
|
||||
o.template="xclient/ping"
|
||||
o.width="10%"
|
||||
|
||||
o = s:option(DummyValue, "server_port", "Socket Connected")
|
||||
o.template = "xclient/socket"
|
||||
o.width = "10%"
|
||||
o.render = function(self, section, scope)
|
||||
self.transport = s:cfgvalue(section).transport
|
||||
if self.transport == 'ws' then
|
||||
self.ws_path = s:cfgvalue(section).ws_path
|
||||
self.tls = s:cfgvalue(section).tls
|
||||
end
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
|
||||
m:append(Template("xclient/server_list"))
|
||||
|
||||
local apply = luci.http.formvalue("cbi.apply")
|
||||
if apply then
|
||||
c.uci:commit("xclient")
|
||||
m.uci:commit("xclient")
|
||||
luci.sys.call("/etc/init.d/xclient boot >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
return c, m
|
||||
@@ -0,0 +1,162 @@
|
||||
require "luci.sys"
|
||||
require "luci.http"
|
||||
local uci = require"luci.model.uci".cursor()
|
||||
local m, s, sec, o
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
|
||||
font_green = [[<b style=color:green>]]
|
||||
font_red = [[<b style=color:red>]]
|
||||
font_off = [[</b>]]
|
||||
bold_on = [[<strong>]]
|
||||
bold_off = [[</strong>]]
|
||||
|
||||
local server_table = {}
|
||||
|
||||
uci:foreach("xclient", "servers", function(s)
|
||||
if s.alias then
|
||||
server_table[s[".name"]] = "[%s]:%s" % {string.upper(s.protocol or s.type), s.alias}
|
||||
elseif s.server and s.server_port then
|
||||
server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.protocol or s.type), s.server, s.server_port}
|
||||
end
|
||||
end)
|
||||
|
||||
local key_table = {}
|
||||
for key,_ in pairs(server_table) do
|
||||
table.insert(key_table,key)
|
||||
end
|
||||
|
||||
table.sort(key_table)
|
||||
|
||||
|
||||
m = Map("xclient")
|
||||
m.pageaction = true
|
||||
m:append(Template("xclient/status"))
|
||||
|
||||
|
||||
s = m:section(TypedSection, "global")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
|
||||
s:tab("main", "Client")
|
||||
s:tab("settings", "Settings")
|
||||
s:tab("access", "Access")
|
||||
s:tab("ports", "Ports")
|
||||
|
||||
o = s:taboption("main", Flag, "enable", "Enable Client")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:taboption("main", ListValue, "global_server", "TCP Server")
|
||||
o:value("nil", "Disable")
|
||||
for _, key in pairs(key_table) do o:value(key, server_table[key]) end
|
||||
o.default = "nil"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("main", ListValue, "udp_relay_server", "UDP Server")
|
||||
o:value("same", "Same as TCP Server")
|
||||
for _, key in pairs(key_table) do o:value(key, server_table[key]) end
|
||||
o.default = "nil"
|
||||
|
||||
|
||||
o = s:taboption("main", ListValue, "socks5_server", "Socks5 Server")
|
||||
o:value("nil", "Disable")
|
||||
o:value("same", "Same as TCP Server")
|
||||
for _, key in pairs(key_table) do o:value(key, server_table[key]) end
|
||||
o.default = "nil"
|
||||
|
||||
o = s:taboption("settings", ListValue, 'log_level')
|
||||
o.title = "Log Level"
|
||||
o:value("debug", "Debug")
|
||||
o:value("info", "Info")
|
||||
o:value("warning", "Warning")
|
||||
o:value("error", "Error")
|
||||
o:value("none", "None")
|
||||
o.default = "info"
|
||||
|
||||
|
||||
o = s:taboption("settings", ListValue, "pdnsd_enable", "DNS Resolver")
|
||||
o:value("1", "PDNSD")
|
||||
o:value("2", "DNS2SOCKS")
|
||||
o.default = 1
|
||||
|
||||
o = s:taboption("settings", Value, "tunnel_forward", "DNS Server")
|
||||
o:value("8.8.4.4:53", "Google Public DNS (8.8.4.4:53)")
|
||||
o:value("8.8.8.8:53", "Google Public DNS (8.8.8.8:53)")
|
||||
o:value("208.67.222.222:53", "OpenDNS (208.67.222.222:53)")
|
||||
o:value("208.67.220.220:53", "OpenDNS (208.67.220.220:53)")
|
||||
o:value("209.244.0.3:53", "Level 3 Public DNS (209.244.0.3:53)")
|
||||
o:value("209.244.0.4:53", "Level 3 Public DNS (209.244.0.4:53)")
|
||||
o:value("4.2.2.1:53", "Level 3 Public DNS (4.2.2.1:53)")
|
||||
o:value("4.2.2.2:53", "Level 3 Public DNS (4.2.2.2:53)")
|
||||
o:value("4.2.2.3:53", "Level 3 Public DNS (4.2.2.3:53)")
|
||||
o:value("4.2.2.4:53", "Level 3 Public DNS (4.2.2.4:53)")
|
||||
o:value("1.1.1.1:53", "Cloudflare DNS (1.1.1.1:53)")
|
||||
o:depends("pdnsd_enable", "1")
|
||||
o:depends("pdnsd_enable", "2")
|
||||
o.datatype = "hostport"
|
||||
|
||||
|
||||
|
||||
o = s:taboption('access', ListValue, 'lan_ifaces')
|
||||
o.title = "LAN Interface"
|
||||
o:value("br-lan", "br-lan")
|
||||
for _, e in ipairs(luci.sys.net.devices()) do if e ~= "lo" then o:value(e) end end
|
||||
o.default="br-lan"
|
||||
o.rmempty = false
|
||||
|
||||
|
||||
o = s:taboption("access", ListValue, "lan_ac_mode", "LAN Access Control")
|
||||
o:value("1", "Bypassed List Mode")
|
||||
o:value("2", "Proxied List Mode")
|
||||
|
||||
o = s:taboption("access", DynamicList, "lan_fp_ips", "LAN Proxied Host List")
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
end)
|
||||
o:depends("lan_ac_mode","2")
|
||||
|
||||
|
||||
o = s:taboption("access", DynamicList, "lan_bp_ips", "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:depends("lan_ac_mode","1")
|
||||
|
||||
o = s:taboption("access", DynamicList, "wan_bp_ips", "WAN Bypassed Host List")
|
||||
o.datatype = "ip4addr"
|
||||
|
||||
|
||||
o = s:taboption("ports", Value, "tcp_port", "TCP Port")
|
||||
o.datatype = "port"
|
||||
o.default = 1234
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("ports", Value, "udp_port", "UDP Port")
|
||||
o.datatype = "port"
|
||||
o.default = 5350
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("ports", Value, "socks5_port", translate("Socks5 Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 1081
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("ports", Value, "dns2socks_port", translate("DNS2Socks Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 5351
|
||||
o.rmempty = false
|
||||
|
||||
local apply = luci.http.formvalue("cbi.apply")
|
||||
if apply then
|
||||
m.uci:commit("xclient")
|
||||
luci.sys.call("/etc/init.d/xclient boot >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
m = Map("xclient")
|
||||
|
||||
s = m:section(TypedSection, "xclient")
|
||||
s.anonymous = true
|
||||
s.addremove=false
|
||||
|
||||
s:append(Template("xclient/version"))
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,105 @@
|
||||
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
function account(btn,option)
|
||||
{
|
||||
btn.disabled = true;
|
||||
if (option == "login") {
|
||||
var s = document.getElementById(option+'-status');
|
||||
var e = document.getElementsByName('cbid.xclient.config.email');
|
||||
var p = document.getElementsByName('cbid.xclient.config.passwd');
|
||||
var u = document.getElementsByName('cbid.xclient.config.site');
|
||||
if (!u[0].value || !e[0].value || !p[0].value) {
|
||||
btn.disabled = false;
|
||||
s.innerHTML ="<font color='red'><strong>Missing field</strong></font>";
|
||||
return false;
|
||||
};
|
||||
|
||||
btn.value = 'Login...';
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "login_info")%>', {weburl: u[0].value, email: e[0].value, passwd : p[0].value}, function(x, status) {
|
||||
if (x && x.status == 200) {
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "login")%>', null, function(x, status) {
|
||||
if (s)
|
||||
{
|
||||
if (x && x.status == 200 && status.login == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Login Successful</strong></font>";
|
||||
document.getElementById('logout').hidden = false;
|
||||
document.getElementById('update').hidden = false;
|
||||
document.getElementById('login').hidden = true;
|
||||
subscribe();
|
||||
}
|
||||
else {
|
||||
s.innerHTML ="<font color='red'><strong>Login Failed</strong></font>";
|
||||
}
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.value = 'Login To Account';
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (option == "logout") {
|
||||
var s = document.getElementById('login-status');
|
||||
btn.value = '<%:Logout...%>';
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "logout")%>', null, function(x, status) {
|
||||
if (s)
|
||||
{
|
||||
if (x && x.status == 200 && status.logout == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Logout Successful</strong></font>";
|
||||
document.getElementById('logout').hidden = true;
|
||||
document.getElementById('update').hidden = true;
|
||||
document.getElementById('login').hidden = false;
|
||||
}
|
||||
else {
|
||||
s.innerHTML ="<font color='red'><strong>Logout Failed</strong></font>";
|
||||
}
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.value = 'Logout Account';
|
||||
}
|
||||
);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient","login_check")%>', null, function(x,status) {
|
||||
if (x && x.status == 200 && status.login == 1) {
|
||||
document.getElementById('login').hidden = true;
|
||||
document.getElementById('logout').hidden = false;
|
||||
document.getElementById('update').hidden = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function subscribe()
|
||||
{
|
||||
var s = document.getElementById('login-status');
|
||||
document.getElementById('update').disabled = true;
|
||||
document.getElementById('update').value = 'Updating Servers...';
|
||||
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient","subscribe")%>', null, function(x,status) {
|
||||
if (x && x.status == 200 && status.ret == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Upate Servers Successful</strong></font>";
|
||||
document.getElementById('update').disabled = false;
|
||||
document.getElementById('update').value = 'Update Servers';
|
||||
window.setTimeout("location.reload()", 2000);
|
||||
}else{
|
||||
s.innerHTML ="<font color='red'><strong>Error Upating Servers</strong></font>";
|
||||
document.getElementById('update').disabled = false;
|
||||
document.getElementById('update').value = 'Update Servers';
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//]]></script>
|
||||
<input type="button" id="login" class="btn cbi-button cbi-button-apply" value="Login To Account" onclick="return account(this,'login')" />
|
||||
<input hidden id="logout" type="button" class="btn cbi-button cbi-button-remove" value="Logout Account" onclick="return account(this,'logout')" />
|
||||
<input hidden id="update" type="button" class="btn cbi-button cbi-button-apply" value="Update Servers" onclick="return subscribe()" />
|
||||
<br/><span id="<%=self.option%>-status"><%=self.value%></span>
|
||||
<%+cbi/valuefooter%>
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
function account(btn,option)
|
||||
{
|
||||
btn.disabled = true;
|
||||
if (option == "login") {
|
||||
var s = document.getElementById(option+'-status');
|
||||
var e = document.getElementsByName('cbid.xclient.config.email');
|
||||
var p = document.getElementsByName('cbid.xclient.config.passwd');
|
||||
if (!e[0].value || !p[0].value) {
|
||||
btn.disabled = false;
|
||||
s.innerHTML ="<font color='red'><strong>Error Login Info</strong></font>";
|
||||
return false;
|
||||
};
|
||||
|
||||
btn.value = 'Login...';
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "login_info")%>', {email: e[0].value, passwd : p[0].value}, function(x, status) {
|
||||
if (x && x.status == 200) {
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "login")%>', null, function(x, status) {
|
||||
if (s)
|
||||
{
|
||||
if (x && x.status == 200 && status.login == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Login Successful</strong></font>";
|
||||
document.getElementById('logout').hidden = false;
|
||||
document.getElementById('update').hidden = false;
|
||||
document.getElementById('login').hidden = true;
|
||||
subscribe();
|
||||
}
|
||||
else {
|
||||
s.innerHTML ="<font color='red'><strong>Login Failed</strong></font>";
|
||||
}
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.value = 'Login To Account';
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
if (option == "logout") {
|
||||
var s = document.getElementById('login-status');
|
||||
btn.value = 'Logout..';
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "logout")%>', null, function(x, status) {
|
||||
if (s)
|
||||
{
|
||||
if (x && x.status == 200 && status.logout == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Logout Successful</strong></font>";
|
||||
document.getElementById('logout').hidden = true;
|
||||
document.getElementById('update').hidden = true;
|
||||
document.getElementById('login').hidden = false;
|
||||
window.setTimeout("location.reload()", 2000);
|
||||
}
|
||||
else {
|
||||
s.innerHTML ="<font color='red'><strong>Logout Failed</strong></font>";
|
||||
}
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.value = 'Logout Account';
|
||||
}
|
||||
);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
function subscribe()
|
||||
{
|
||||
var s = document.getElementById('login-status');
|
||||
document.getElementById('update').disabled = true;
|
||||
document.getElementById('update').value = 'Updating Servers...';
|
||||
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient","subscribe")%>', null, function(x,status) {
|
||||
if (x && x.status == 200 && status.ret == 1) {
|
||||
s.innerHTML ="<font color='green'><strong>Upate Servers Successful</strong></font>";
|
||||
document.getElementById('update').disabled = false;
|
||||
document.getElementById('update').value = 'Update Servers';
|
||||
window.setTimeout("location.reload()", 2000);
|
||||
}else{
|
||||
s.innerHTML ="<font color='red'><strong>Error Upating Servers</strong></font>";
|
||||
document.getElementById('update').disabled = false;
|
||||
document.getElementById('update').value = 'Update Servers';
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//]]></script>
|
||||
<input hidden type="button" id="login" class="btn cbi-button cbi-button-apply" value="Login To Account" onclick="return account(this,'login')" />
|
||||
<input id="logout" type="button" class="btn cbi-button cbi-button-remove" value="Logout Account" onclick="return account(this,'logout')" />
|
||||
<input id="update" type="button" class="btn cbi-button cbi-button-apply" value="Update Servers" onclick="return subscribe()" />
|
||||
<br/><span id="<%=self.option%>-status"><%=self.value%></span>
|
||||
<%+cbi/valuefooter%>
|
||||
@@ -0,0 +1,21 @@
|
||||
<%+cbi/valueheader%>
|
||||
|
||||
<textarea id="log" class="cbi-input-textarea" style="width: 100%;" data-update="change" rows="29" readonly="readonly" > </textarea>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
var lv = document.getElementById('log');
|
||||
lv.style.display="inline"
|
||||
XHR.poll(1, '<%=url([[admin]], [[services]], [[xclient]], [[logstatus]])%>', null,
|
||||
function(x, data) {
|
||||
var lv = document.getElementById('log');
|
||||
if (x.responseText && lv) {
|
||||
lv.innerHTML += x.responseText;
|
||||
lv.scrollTop = lv.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
<%+cbi/valuefooter%>
|
||||
@@ -0,0 +1,3 @@
|
||||
<%+cbi/valueheader%>
|
||||
<span class="pingtime" hint="<%=self:cfgvalue(section)%>">Checking...</span>
|
||||
<%+cbi/valuefooter%>
|
||||
@@ -0,0 +1,143 @@
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
window.addEventListener('load',function(){
|
||||
const doms = document.getElementsByClassName('pingtime');
|
||||
const ports = document.getElementsByClassName("socket-connected");
|
||||
const transports = document.getElementsByClassName("transport");
|
||||
const wsPaths = document.getElementsByClassName("wsPath");
|
||||
const tlss = document.getElementsByClassName("tls");
|
||||
const xhr = (index) => {
|
||||
return new Promise((res) => {
|
||||
const dom = doms[index];
|
||||
const port = ports[index];
|
||||
const transport = transports[index];
|
||||
const wsPath = wsPaths[index];
|
||||
const tls = tlss[index];
|
||||
if (!dom) res()
|
||||
port.innerHTML = '<font color="#0072c3">Checking...</font>';
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin/services/xclient/ping")%>', {
|
||||
index,
|
||||
domain: dom.getAttribute("hint"),
|
||||
port: port.getAttribute("hint"),
|
||||
transport: transport.getAttribute("hint"),
|
||||
wsPath: wsPath.getAttribute("hint"),
|
||||
tls: tls.getAttribute("hint")
|
||||
},
|
||||
(x, result) => {
|
||||
let col = '#ff0000';
|
||||
if (result.ping) {
|
||||
if (result.ping < 300) col = '#eb5949';
|
||||
if (result.ping < 250) col = '#e1ff4a';
|
||||
if (result.ping < 200) col = '#91fc62';
|
||||
if (result.ping < 100) col = '#4aff62';
|
||||
if (result.ping < 50) col = '#16a142';
|
||||
}
|
||||
dom.innerHTML = `<font color="${col}">${(result.ping ? result.ping : "--") + " ms"}</font>`
|
||||
if (result.socket) {
|
||||
port.innerHTML = '<font color="#249400">NORMAL</font>';
|
||||
} else {
|
||||
port.innerHTML = '<font color="#ff0000">ERROR</font>';
|
||||
}
|
||||
res();
|
||||
});
|
||||
})
|
||||
}
|
||||
let task = -1;
|
||||
const thread = () => {
|
||||
task = task + 1
|
||||
if (doms[task]) {
|
||||
xhr(task).then(thread);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < 20; i++) {
|
||||
thread()
|
||||
}
|
||||
})
|
||||
|
||||
function cbi_row_drop(fromId, toId, store, isToBottom) {
|
||||
var fromNode = document.getElementById(fromId);
|
||||
var toNode = document.getElementById(toId);
|
||||
if (!fromNode || !toNode) return false;
|
||||
|
||||
var table = fromNode.parentNode;
|
||||
while (table && table.nodeName.toLowerCase() != "table")
|
||||
table = table.parentNode;
|
||||
if (!table) return false;
|
||||
|
||||
var ids = [];
|
||||
if (isToBottom) {
|
||||
toNode.parentNode.appendChild(fromNode);
|
||||
} else {
|
||||
fromNode.parentNode.insertBefore(fromNode, toNode);
|
||||
}
|
||||
for (var idx = 2; idx < table.rows.length; idx++) {
|
||||
table.rows[idx].className = table.rows[idx].className.replace(
|
||||
/cbi-rowstyle-[12]/,
|
||||
"cbi-rowstyle-" + (1 + (idx % 2))
|
||||
);
|
||||
|
||||
if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/))
|
||||
ids.push(RegExp.$1);
|
||||
}
|
||||
var input = document.getElementById(store);
|
||||
if (input) input.value = ids.join(" ");
|
||||
return false;
|
||||
}
|
||||
// set tr draggable
|
||||
function enableDragForTable(table_selecter, store) {
|
||||
var trs = document.querySelectorAll(table_selecter + " tr");
|
||||
if (!trs || trs.length.length < 3) {
|
||||
return;
|
||||
}
|
||||
function ondragstart(ev) {
|
||||
ev.dataTransfer.setData("Text", ev.target.id);
|
||||
}
|
||||
function ondrop(ev) {
|
||||
var from = ev.dataTransfer.getData("Text");
|
||||
cbi_row_drop(from, this.id, store);
|
||||
}
|
||||
function ondragover(ev) {
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
function moveToTop(id) {
|
||||
var top = document.querySelectorAll(table_selecter + " tr")[2];
|
||||
cbi_row_drop(id, top.id, store);
|
||||
}
|
||||
function moveToBottom(id) {
|
||||
console.log('moveToBottom:', id);
|
||||
var trList = document.querySelectorAll(table_selecter + " tr");
|
||||
var bottom = trList[trList.length - 1];
|
||||
cbi_row_drop(id, bottom.id, store, true);
|
||||
}
|
||||
for (let index = 2; index < trs.length; index++) {
|
||||
const el = trs[index];
|
||||
el.setAttribute("draggable", true);
|
||||
el.ondragstart = ondragstart;
|
||||
el.ondrop = ondrop;
|
||||
el.ondragover = ondragover;
|
||||
// reset the behaviors of the btns
|
||||
var upBtns = el.querySelectorAll(".cbi-button.cbi-button-up");
|
||||
if (upBtns && upBtns.length > 0) {
|
||||
upBtns.forEach(function (_el) {
|
||||
_el.onclick = function () {
|
||||
moveToTop(el.id);
|
||||
};
|
||||
});
|
||||
}
|
||||
var downBtns = el.querySelectorAll(".cbi-button.cbi-button-down");
|
||||
if (downBtns && downBtns.length > 0) {
|
||||
downBtns.forEach(function (_el) {
|
||||
_el.onclick = function () {
|
||||
moveToBottom(el.id);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// enable
|
||||
enableDragForTable(
|
||||
"#cbi-xclient-servers table",
|
||||
"cbi.sts.xclient.servers"
|
||||
);
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
<%+cbi/valueheader%>
|
||||
<span class="socket-connected" hint="<%=self:cfgvalue(section)%>">Checking...</span>
|
||||
<span class="transport" hint="<%=self.transport%>"></span>
|
||||
<span class="wsPath" hint="<%=self.ws_path%>"></span>
|
||||
<span class="tls" hint="<%=self.tls%>"></span>
|
||||
<%+cbi/valuefooter%>
|
||||
@@ -0,0 +1,446 @@
|
||||
|
||||
<style>
|
||||
.pure-g{
|
||||
letter-spacing:-.31em;
|
||||
text-rendering:optimizespeed;
|
||||
font-family:FreeSans,Arimo,
|
||||
"Droid Sans",
|
||||
Helvetica,
|
||||
Arial,s
|
||||
ans-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%}
|
||||
.block{
|
||||
margin: 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;
|
||||
|
||||
}
|
||||
.green{
|
||||
font-size:.9rem;
|
||||
color: #2dce89;
|
||||
}
|
||||
.red{
|
||||
font-size:.9rem;
|
||||
color: #fb6340;
|
||||
}
|
||||
.yellow{
|
||||
font-size:.9rem;
|
||||
color: #fb9a05;
|
||||
}
|
||||
.block img{
|
||||
width: 50px;
|
||||
height: auto;
|
||||
}
|
||||
.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;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.check {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
.block{
|
||||
margin: 0.2rem;
|
||||
}
|
||||
.pure-u-1-4{
|
||||
width: 50%;
|
||||
}
|
||||
.pure-u-1-2{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@media screen and (max-width: 480px) {
|
||||
.img-con {
|
||||
margin: 1.5rem 0.5rem;
|
||||
}
|
||||
.block img {
|
||||
width: 36px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.siz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
.siz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 600px) {
|
||||
.siz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 480px) {
|
||||
.siz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (min-width: 600px) {
|
||||
.sizz {
|
||||
width: 33.33%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.sizz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.sizz {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<div class="pure-g status">
|
||||
<div class="pure-u-1-4">
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-1-3">
|
||||
<div class="img-con">
|
||||
<img src="/luci-static/xclient/img/site_icon1_01.png" style="width:60px;heiht:100%" class="baidu">
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-2-3">
|
||||
<h4>Baidu<br/><span id="status_baidu" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-4" >
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-1-3">
|
||||
<div class="img-con">
|
||||
<img src="/luci-static/xclient/img/site_icon1_02.png" style="width:60px;heiht:100%" class="taobao">
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-2-3">
|
||||
<h4 >Taobao<br/><span id="status_taobao" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pure-u-1-4 " >
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-1-3">
|
||||
<div class="img-con">
|
||||
<img src="/luci-static/xclient/img/site_icon1_03.png" style="width:60px;heiht:100%" class="google">
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-2-3">
|
||||
<h4>Google<br/><span id="status_google" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-4 ">
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-1-3">
|
||||
<div class="img-con">
|
||||
<img src="/luci-static/xclient/img/site_icon1_04.png" style="width:60px;heiht:100%" class="youtube">
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-2-3">
|
||||
<h4 >Youtube<br/><span id="status_youtube" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pure-u-1-4 ">
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-9-24">
|
||||
<div class="img-con">
|
||||
<img src="data:image/png;base64,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 truncated
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-14-24">
|
||||
<h4>TCP<br/><span id="_tcp" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-4 ">
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-9-24">
|
||||
<div class="img-con">
|
||||
<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i5Zu+5bGCXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTAwIDEwMDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCgkuc3Qwe2ZpbGw6I0ZCQjE0MDt9DQoJLnN0MXtmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjUwIiBjeT0iNTAiIHI9IjUwIi8+DQo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOCw0Ny42QzgzLjYsMzMsNzQuOCwyMi41LDY2LjUsMjNoLTAuM2MtMy4zLDAtNi41LDEuMi04LjksMy4zYy0xLjgsMi00LjQsMy4yLTcuMSwzLjINCgljLTIuNiwwLTUuMS0xLjEtNi45LTNjLTIuNS0yLjMtNS44LTMuNS05LjEtMy41aC0wLjRjLTguMi0wLjUtMTcuMSwxMC0yMC4zLDI0LjZjLTMuNSwxNS41LDAuOCwyOS44LDkuNSwzMS45DQoJYzYuMywxLjYsMTMuMy0zLjksMTcuOS0xM2gxOC43YzQuNyw5LjEsMTEuNiwxNC42LDE3LjksMTNDODYsNzcuNCw5MC4yLDYzLjEsODYuOCw0Ny42TDg2LjgsNDcuNnogTTQyLjksNDguNUgzNnY3LjJoLTUuNnYtNy4yDQoJaC02Ljl2LTUuOGg2Ljl2LTcuMkgzNnY3LjJoNi45TDQyLjksNDguNXogTTYyLjcsNDkuMmMtMiwwLjEtMy43LTEuNC0zLjgtMy40Yy0wLjEtMiwxLjQtMy43LDMuNC0zLjhjMi0wLjEsMy43LDEuNCwzLjgsMy40DQoJYzAsMC4xLDAsMC4xLDAsMC4yQzY2LjIsNDcuNSw2NC43LDQ5LjEsNjIuNyw0OS4yQzYyLjcsNDkuMiw2Mi43LDQ5LjIsNjIuNyw0OS4yeiBNNzMuNyw0OS4yYy0yLDAuMS0zLjctMS40LTMuOC0zLjQNCgljLTAuMS0yLDEuNC0zLjcsMy40LTMuOGMyLTAuMSwzLjcsMS40LDMuOCwzLjRjMCwwLjEsMCwwLjEsMCwwLjJjMC4xLDEuOS0xLjQsMy41LTMuMywzLjZINzMuN3oiLz4NCjwvc3ZnPg0K" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-14-24">
|
||||
<h4>UDP<br/><span id="_udp" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-4" >
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-9-24">
|
||||
<div class="img-con">
|
||||
<img src="data:image/png;base64,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 truncated
|
||||
"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-14-24">
|
||||
<h4 >DNS<br/><span id="_dns" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pure-u-1-4 " >
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-9-24">
|
||||
<div class="img-con">
|
||||
<img src="data:image/png;base64,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 truncated
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-14-24">
|
||||
<h4>Socks5 Server<br/><span id="_socks" class="red">Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<!--<fieldset class="cbi-section">
|
||||
<div class="pure-u-24-24" >
|
||||
<div class="block pure-g">
|
||||
<div class="pure-u-2-24">
|
||||
<div class="img-con">
|
||||
<img src="/luci-static/xclient/img/loading.svg" style="width:60px;heiht:100%">
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-22-24">
|
||||
<h4><b>GEOLOCATION </b><br ><span id="ip" >Checking...</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>-->
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<table width="100%" cellspacing="10">
|
||||
<tr><td width="25%" ><span></span></td ><td width="30%" ><b><span> ↓ </span> <span id="_speed_download">0 B/s</span></b></td > <td ><b><span> ↑ </span> <span id="_speed_upload">0 B/s</span></b></td></tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
const _IMG = '/luci-static/xclient/';
|
||||
var xclient = document.getElementById('_xclient');
|
||||
var dns = document.getElementById('_dns');
|
||||
var tcp = document.getElementById('_tcp');
|
||||
var udp = document.getElementById('_udp');
|
||||
var socks = document.getElementById('_socks');
|
||||
|
||||
function state(status) {
|
||||
if(status.dns_status == "1"){
|
||||
dns.innerHTML = '<b class="green">RUNNING</b>';
|
||||
}else{
|
||||
dns.innerHTML = '<b class="red">NOT RUNNING</b>';
|
||||
}
|
||||
|
||||
if(status.tcp_status == "1"){
|
||||
tcp.innerHTML = '<b class="green">RUNNING</b>';
|
||||
}else{
|
||||
tcp.innerHTML = '<b class="red">NOT RUNNING</b>';
|
||||
}
|
||||
|
||||
if(status.udp_status == "1"){
|
||||
udp.innerHTML = '<b class="green">RUNNING</b>';
|
||||
}else{
|
||||
udp.innerHTML = '<b class="red">NOT RUNNING</b>';
|
||||
}
|
||||
|
||||
if(status.socks_status == "1"){
|
||||
socks.innerHTML = '<b class="green">RUNNING</b>';
|
||||
}else{
|
||||
socks.innerHTML = '<b class="red">NOT RUNNING</b>';
|
||||
}
|
||||
}
|
||||
|
||||
XHR.poll(1, '<%=luci.dispatcher.build_url("admin", "services", "xclient", "run")%>', null, function(x, status) {
|
||||
state(status);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "run")%>', null, function(x, status) {
|
||||
state(status);
|
||||
}
|
||||
);
|
||||
//
|
||||
//XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "geolocation")%>', null, function(x, status) {
|
||||
// if(x && x.status == 200) {
|
||||
// document.getElementById('ip').innerHTML = "<span class='green'>" + status.location + "</span>";
|
||||
// }
|
||||
// }
|
||||
//);
|
||||
|
||||
//XHR.poll(3,'<%=luci.dispatcher.build_url("admin", "services", "xclient", "geolocation")%>', null, function(x, status) {
|
||||
// if(x && x.status == 200) {
|
||||
// document.getElementById('ip').innerHTML = "<span class='green'>" + status.location + "</span>";
|
||||
// }
|
||||
// }
|
||||
//);
|
||||
//
|
||||
function wirte(status) {
|
||||
status.baidu ? $(".baidu").attr("src", _IMG + "img/site_icon_01.png") : $(".baidu").attr("src", _IMG + "img/site_icon1_01.png");
|
||||
status.baidu ? document.getElementById('status_baidu').innerHTML = '<b class="green">NORMAL</b>' : document.getElementById('status_baidu').innerHTML = '<b class="red">NO ACCESS</b>';
|
||||
status.taobao ? $(".taobao").attr("src", _IMG + "img/site_icon_02.png") : $(".taobao").attr("src", _IMG + "img/site_icon1_02.png");
|
||||
status.taobao ? document.getElementById('status_taobao').innerHTML = '<b class="green">NORMAL</b>' : document.getElementById('status_taobao').innerHTML = '<b class="red">NO ACCESS</b>';
|
||||
status.google ? $(".google").attr("src", _IMG + "img/site_icon_03.png") : $(".google").attr("src", _IMG + "img/site_icon1_03.png");
|
||||
status.google ? document.getElementById('status_google').innerHTML = '<b class="green">NORMAL</b>' : document.getElementById('status_google').innerHTML = '<b class="red">NO ACCESS</b>';
|
||||
status.youtube ? $(".youtube").attr("src", _IMG + "img/site_icon_04.png") : $(".youtube").attr("src", _IMG + "img/site_icon1_04.png");
|
||||
status.youtube ? document.getElementById('status_youtube').innerHTML = '<b class="green">NORMAL</b>' : document.getElementById('status_youtube').innerHTML = '<b class="red">NO ACCESS</b>';
|
||||
}
|
||||
XHR.poll(3, '<%=luci.dispatcher.build_url("admin", "services", "xclient", "web")%>', null, function(x, status) {
|
||||
wirte(status);
|
||||
}
|
||||
);
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "xclient", "web")%>', null, function(x, status) {
|
||||
wirte(status);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "services", "xclient", "traffic")%>', null, function(x, status)
|
||||
{
|
||||
traffic(status.downlink, status.uplink);
|
||||
});
|
||||
|
||||
function traffic(downlink, uplink) {
|
||||
var uplspd = stats(uplink);
|
||||
var dnlspd = stats(downlink);
|
||||
document.getElementById('_speed_upload').innerHTML = uplspd;
|
||||
document.getElementById('_speed_download').innerHTML = dnlspd;
|
||||
}
|
||||
|
||||
function stats(value) {
|
||||
if (value > 1099511627776) {
|
||||
return Math.round(value/1099511627776) + " TB/s"
|
||||
} else if (value > 1073741824) {
|
||||
return Math.round(value/1073741824) + " GB/s"
|
||||
} else if (value > 1048576) {
|
||||
return Math.round(value/1048576) + " MB/s"
|
||||
} else if( value > 1024) {
|
||||
return Math.round(value/1024) + " KB/s"
|
||||
} else {
|
||||
return Math.round(value) + " B/s"
|
||||
}
|
||||
}
|
||||
//]]></script>
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<%local fs=require"nixio.fs"%>
|
||||
<fieldset class="cbi-section">
|
||||
<table width="100%" cellspacing="10">
|
||||
<thead align="center">
|
||||
<tr>
|
||||
<th align="center">Name</th>
|
||||
<th align="center">Version</th>
|
||||
<th align="center">Latest</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" width="30%">Xray-Core</td> <td align="center" width="35%" id="_core">Checkin...</td> <td width="35%" align="center" id="_newcore"><font color="red">Checkin...</font> </td>
|
||||
</tr>
|
||||
<tr><td></td><td></td><td></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<table width="100%" cellspacing="10">
|
||||
<thead align="center">
|
||||
<tr>
|
||||
<th align="center">Name</th>
|
||||
<th align="center">Version</th>
|
||||
<th align="center">Update</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" width="30%">geoip file</td> <td align="center" width="35%" id="_geoip">Checkin...</td> <td width="35%" align="center" id="_newgeoip"><font color="red">Checkin...</font> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="30%">geosite file</td> <td align="center" width="35%" id="_geosite">Checkin...</td> <td width="35%" align="center" id="_newgeosite"><font color="red">Checkin...</font> </td>
|
||||
</tr>
|
||||
<tr><td></td><td></td><td></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<textarea id="geoip_download" class="cbi-input-textarea" style="width: 100%;display:none" data-update="change" rows="20" cols="100" readonly="readonly" > </textarea>
|
||||
<textarea id="geosite_download" class="cbi-input-textarea" style="width: 100%;display:none" data-update="change" rows="20" cols="100" readonly="readonly" > </textarea>
|
||||
</fieldset>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var _core = document.getElementById('_core');
|
||||
var _geoip = document.getElementById('_geoip');
|
||||
var _geosite = document.getElementById('_geosite');
|
||||
var _newcore = document.getElementById('_newcore');
|
||||
|
||||
XHR.get('<%=url([[admin]], [[services]], [[xclient]], [[version]])%>', null, function(x, status) {
|
||||
if ( x && x.status == 200) {
|
||||
_core.innerHTML = status.core_version ? '<b><font color="green">' + status.core_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_geoip.innerHTML = status.geoip_version ? '<b><font color="green">' + status.geoip_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_geosite.innerHTML = status.geosite_version ? '<b><font color="green">' + status.geosite_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_newcore.innerHTML = status.latest_core ? '<b><font color="red"><a href="https://github.com/XTLS/Xray-core/releases/tag/' + status.latest_core + '" target="_blank">' + status.latest_core + '</a></font></b>' : '<b>--</b>';
|
||||
}
|
||||
});
|
||||
|
||||
XHR.poll(1,'<%=url([[admin]], [[services]], [[xclient]], [[check_latest]])%>', null, function(x, status) {});
|
||||
|
||||
|
||||
XHR.poll(3,'<%=url([[admin]], [[services]], [[xclient]], [[version]])%>', null, function(x, status) {
|
||||
if ( x && x.status == 200) {
|
||||
_core.innerHTML = status.core_version ? '<b><font color="green">' + status.core_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_geoip.innerHTML = status.geoip_version ? '<b><font color="green">' + status.geoip_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_geosite.innerHTML = status.geosite_version ? '<b><font color="green">' + status.geosite_version + '</font></b>' : '<b><font color="red">X</font> </b>';
|
||||
_newcore.innerHTML = status.latest_core ? '<b><font color="red"><a href="https://github.com/XTLS/Xray-core/releases/tag/' + status.latest_core + '" target="_blank">' + status.latest_core + '</a></font></b>' : '<b>--</b>';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('_newgeoip').innerHTML = '<input type="button" class="cbi-button cbi-button-apply" id="apply_update_geoip" value="Manually Update" onclick=" return update_geoip() "/>';
|
||||
document.getElementById('_newgeosite').innerHTML = '<input type="button" class="cbi-button cbi-button-apply" id="apply_update_geosite" value="Manually Update" onclick=" return update_geosite() "/>';
|
||||
|
||||
|
||||
<% if not fs.access("/var/lock/xclient.lock") then %>
|
||||
function update_geoip(){
|
||||
XHR.get('<%=url([[admin]], [[services]], [[xclient]], [[geoipupdate]])%>',null,function(x, data){});
|
||||
geoip_poll_check();
|
||||
return
|
||||
}
|
||||
<%end%>
|
||||
|
||||
function geoip_poll_check(){
|
||||
var lv = document.getElementById('geoip_download');
|
||||
lv.style.display="inline"
|
||||
XHR.poll(2, '<%=url([[admin]], [[services]], [[xclient]], [[check_geoip]])%>', null,
|
||||
function(x, data) {
|
||||
var lv = document.getElementById('geoip_download');
|
||||
if (x.responseText && lv) {
|
||||
lv.innerHTML += x.responseText;
|
||||
lv.scrollTop = lv.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
XHR.poll(3, '<%=luci.dispatcher.build_url("admin", "services", "xclient", "geoip")%>', null, function(x, status) {
|
||||
if ( x && x.status == 200 ) {
|
||||
if(status.geoipcheck == 0||status.geoipcheck == 2){
|
||||
hide_ip();
|
||||
}
|
||||
if(status.geoipcheck == 1){
|
||||
var btn = document.getElementById('apply_update_geoip');
|
||||
btn.disabled = false;
|
||||
btn.value = 'Manually Update';
|
||||
geoip_poll_check();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
<% if not fs.access("/var/lock/xclient.lock") then %>
|
||||
function update_geosite(){
|
||||
XHR.get('<%=url([[admin]], [[services]], [[xclient]], [[geositeupdate]])%>',null,function(x, data){});
|
||||
geosite_poll_check();
|
||||
return
|
||||
}
|
||||
<%end%>
|
||||
|
||||
function geosite_poll_check(){
|
||||
var lv = document.getElementById('geosite_download');
|
||||
lv.style.display="inline"
|
||||
XHR.poll(2, '<%=url([[admin]], [[services]], [[xclient]], [[check_geosite]])%>', null,
|
||||
function(x, data) {
|
||||
var lv = document.getElementById('geosite_download');
|
||||
if (x.responseText && lv) {
|
||||
lv.innerHTML += x.responseText;
|
||||
lv.scrollTop = lv.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
XHR.poll(3, '<%=luci.dispatcher.build_url("admin", "services", "xclient", "geosite")%>', null, function(x, status) {
|
||||
if ( x && x.status == 200 ) {
|
||||
if(status.geositecheck == 0||status.geositecheck == 2){
|
||||
hide_site();
|
||||
}
|
||||
if(status.geositecheck == 1){
|
||||
var btn = document.getElementById('apply_update_geosite');
|
||||
btn.disabled = false;
|
||||
btn.value = 'Manually Update';
|
||||
geosite_poll_check();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function hide_ip(){
|
||||
$('#geoip_download').hide();
|
||||
var btn = document.getElementById('apply_update_geoip');
|
||||
btn.disabled = false;
|
||||
btn.value = 'Manually Update';
|
||||
}
|
||||
|
||||
|
||||
function hide_site(){
|
||||
$('#geosite_download').hide();
|
||||
var btn = document.getElementById('apply_update_geosite');
|
||||
btn.disabled = false;
|
||||
btn.value = 'Manually Update';
|
||||
}
|
||||
|
||||
|
||||
|
||||
<% if fs.access("/var/run/geo_ip_update") then %>
|
||||
var btn = document.getElementById('apply_update_geoip');
|
||||
btn.disabled = true;
|
||||
btn.value = 'Downloading...';
|
||||
geoip_poll_check();
|
||||
<%end%>
|
||||
|
||||
|
||||
<% if fs.access("/var/run/geo_site_update") then %>
|
||||
var btn = document.getElementById('apply_update_geosite');
|
||||
btn.disabled = true;
|
||||
btn.value = 'Downloading...';
|
||||
geosite_poll_check();
|
||||
<%end%>
|
||||
|
||||
//]]></script>
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
config global
|
||||
option monitor_enable '1'
|
||||
option dports '1'
|
||||
option pdnsd_enable '1'
|
||||
option tunnel_forward '8.8.4.4:53'
|
||||
list Interface 'lan'
|
||||
option disableCache '0'
|
||||
option disableFallback '0'
|
||||
option disableFallbackIfMatch '0'
|
||||
option queryStrategy 'UseIP'
|
||||
option domainMatcher 'linear'
|
||||
option routing_strategy 'IPIfNonMatch'
|
||||
list dns_servers 'https+local://cloudflare-dns.com/dns-query'
|
||||
list dns_servers 'https://1.1.1.1/dns-query'
|
||||
list dns_servers 'https://dns.google/dns-query'
|
||||
list dns_servers '1.1.1.1'
|
||||
list dns_servers '8.8.8.8'
|
||||
option tcp_port '1234'
|
||||
option udp_port '5350'
|
||||
option socks5_port '1081'
|
||||
option dns2socks_port '5351'
|
||||
option global_server 'nil'
|
||||
option udp_relay_server 'same'
|
||||
|
||||
|
||||
config xclient 'config'
|
||||
|
||||
|
||||
config server_global
|
||||
option enable_server '0'
|
||||
|
||||
config log
|
||||
|
||||
config update
|
||||
|
||||
config rule
|
||||
option outboundTag 'block'
|
||||
list inboundTag 'proxy_inbound'
|
||||
list inboundTag 'dns_inbound'
|
||||
list inboundTag 'socks_inbound'
|
||||
option domainMatcher 'linear'
|
||||
list domain 'geosite:category-ads-all'
|
||||
list domain 'geosite:google-ads'
|
||||
list domain 'geosite:category-ads'
|
||||
list domain 'geosite:apple-ads'
|
||||
list domain 'geosite:win-spy'
|
||||
list domain 'geosite:win-extra'
|
||||
|
||||
config rule
|
||||
list inboundTag 'proxy_inbound'
|
||||
list inboundTag 'dns_inbound'
|
||||
list inboundTag 'socks_inbound'
|
||||
option outboundTag 'direct'
|
||||
option domainMatcher 'linear'
|
||||
list domain 'geosite:geolocation-cn'
|
||||
list domain 'geosite:category-games@cn'
|
||||
list domain 'geosite:cn'
|
||||
list ip 'geoip:private'
|
||||
list ip 'geoip:cn'
|
||||
|
||||
config rule
|
||||
list inboundTag 'proxy_inbound'
|
||||
list inboundTag 'dns_inbound'
|
||||
list inboundTag 'socks_inbound'
|
||||
option outboundTag 'proxy_outbound'
|
||||
option domainMatcher 'linear'
|
||||
list domain 'geosite:geolocation-!cn'
|
||||
list domain 'geosite:google'
|
||||
list domain 'geosite:gfw'
|
||||
option network 'tcp,udp'
|
||||
list ip 'geoip:!cn'
|
||||
list ip 'geoip:google'
|
||||
list ip 'geoip:facebook'
|
||||
list ip 'geoip:telegram'
|
||||
list ip 'geoip:twitter'
|
||||
|
||||
config dns
|
||||
option address '114.114.114.114'
|
||||
option port '53'
|
||||
list expectIPs 'geoip:cn'
|
||||
list domains 'geosite:cn'
|
||||
list domains 'geosite:category-games@cn'
|
||||
option skipFallback '1'
|
||||
|
||||
config dns
|
||||
option address 'https://1.1.1.1/dns-query'
|
||||
list domains 'geosite:geolocation-!cn'
|
||||
list domains 'geosite:google'
|
||||
list domains 'geosite:netflix'
|
||||
list domains 'domain:netflix.com'
|
||||
list domains 'domain:netflix.net'
|
||||
list domains 'domain:nflximg.net'
|
||||
list domains 'domain:nflxvideo.net'
|
||||
list domains 'domain:nflxso.net'
|
||||
list domains 'domain:nflxext.com'
|
||||
list domains 'domain:hulu.com'
|
||||
list domains 'domain:huluim.com'
|
||||
list domains 'domain:hbonow.com'
|
||||
list domains 'domain:hbogo.com'
|
||||
list domains 'domain:hbo.com'
|
||||
option port '53'
|
||||
list expectIPs 'geoip:!cn'
|
||||
option skipFallback '0'
|
||||
@@ -0,0 +1,473 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=95
|
||||
STOP=15
|
||||
SERVICE_DAEMONIZE=1
|
||||
NAME=xclient
|
||||
LOCK_FILE=/var/lock/$NAME.lock
|
||||
LOG_FILE=/var/log/$NAME.log
|
||||
XRAY_LOG_FILE=/usr/share/$NAME/$NAME.txt
|
||||
REAL_LOG=$XRAY_LOG_FILE
|
||||
TMP_PATH=/var/etc/$NAME
|
||||
TMP_BIN_PATH=$TMP_PATH/bin
|
||||
TMP_DNSMASQ_PATH=/tmp/dnsmasq.d/dnsmasq-$NAME.d
|
||||
XBIN=/usr/bin/xray
|
||||
tcp_config_file=
|
||||
enable_socks=0
|
||||
udp_config_file=
|
||||
local_config_file=
|
||||
ARG_UDP=
|
||||
dns_port="5335" #dns port
|
||||
server_count=0
|
||||
redir_tcp=0
|
||||
redir_udp=0
|
||||
local_enable=0
|
||||
pdnsd_enable_flag=0
|
||||
switch_server=$1
|
||||
CRON_FILE=/etc/crontabs/root
|
||||
EXTRA_COMMANDS='reset'
|
||||
EXTRA_HELP=" reset Reset to default settings"
|
||||
#extra_command "reset" "Reset to default settings"
|
||||
PS="/bin/busybox ps"
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
uci_set_by_name() {
|
||||
uci set $NAME.$1.$2=$3 2>/dev/null
|
||||
uci commit $NAME
|
||||
}
|
||||
|
||||
uci_set_by_type() {
|
||||
uci set $NAME.@$1[0].$2=$3 2>/dev/null
|
||||
uci commit $NAME
|
||||
}
|
||||
|
||||
uci_get_by_cfgid() {
|
||||
local ret=$(uci show $NAME.@$1[0].$2 | awk -F '.' '{print $2}' 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
get_host_ip() {
|
||||
local host=$(uci_get_by_name $1 server)
|
||||
local ip=$host
|
||||
if [ -z "$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")" ]; then
|
||||
if [ "$host" == "${host#*:[0-9a-fA-F]}" ]; then
|
||||
ip=$(resolveip -4 -t 3 $host | awk 'NR==1{print}')
|
||||
[ -z "$ip" ] && ip=$(uclient-fetch -q -O- http://119.29.29.29/d?dn=$host | awk -F ';' '{print $1}')
|
||||
fi
|
||||
fi
|
||||
[ -z "$ip" ] || uci_set_by_name $1 ip $ip
|
||||
echo ${ip:="$(uci_get_by_name $1 ip "ERROR")"}
|
||||
}
|
||||
|
||||
clean_log() {
|
||||
local logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l)
|
||||
[ "$logsnum" -gt 1000 ] && {
|
||||
echo "$(date "+%Y-%m-%d %H:%M:%S") The log file is too long, clear it!" >$LOG_FILE
|
||||
}
|
||||
}
|
||||
|
||||
echolog() {
|
||||
local d="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
echo -e "$d: $*" >>$LOG_FILE
|
||||
}
|
||||
|
||||
add_cron() {
|
||||
touch $CRON_FILE
|
||||
sed -i '/xclient.log/d' $CRON_FILE
|
||||
[ $(uci get xclient.config.auto_update_servers) > 0 ] && echo "0 */$(uci get xclient.config.auto_update_servers) * * * /usr/bin/lua /usr/share/xclient/subscribe.lua >$LOG_FILE" >>$CRON_FILE
|
||||
crontab $CRON_FILE
|
||||
}
|
||||
|
||||
del_cron() {
|
||||
touch $CRON_FILE
|
||||
sed -i '/xclient.log/d' $CRON_FILE
|
||||
crontab $CRON_FILE
|
||||
clean_log
|
||||
}
|
||||
|
||||
set_lock() {
|
||||
exec 1000>"$LOCK_FILE"
|
||||
flock -xn 1000
|
||||
}
|
||||
|
||||
unset_lock() {
|
||||
flock -u 1000
|
||||
rm -rf "$LOCK_FILE"
|
||||
}
|
||||
|
||||
unlock() {
|
||||
failcount=1
|
||||
while [ "$failcount" -le 10 ]; do
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
let "failcount++"
|
||||
sleep 1s
|
||||
[ "$failcount" -ge 10 ] && unset_lock
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
_exit() {
|
||||
local rc=$1
|
||||
unset_lock
|
||||
exit ${rc}
|
||||
}
|
||||
|
||||
first_type() {
|
||||
type -t -p "/bin/${1}" -p "${TMP_BIN_PATH}/${1}" -p "${1}" "$@" | head -n1
|
||||
}
|
||||
|
||||
ln_start_bin() {
|
||||
local file_func=${1}
|
||||
local ln_name=${2}
|
||||
shift 2
|
||||
if [ "${file_func%%/*}" != "${file_func}" ]; then
|
||||
[ ! -L "${file_func}" ] && {
|
||||
ln -s "${file_func}" "${TMP_BIN_PATH}/${ln_name}" >/dev/null 2>&1
|
||||
file_func="${TMP_BIN_PATH}/${ln_name}"
|
||||
}
|
||||
[ -x "${file_func}" ] || echolog "$(readlink ${file_func}) no execute permission,Unable to start:${file_func} $*"
|
||||
fi
|
||||
#echo "${file_func} $*" >&2
|
||||
[ -x "${file_func}" ] || {
|
||||
echolog "Can't find ${file_func},Unable to start..."
|
||||
echolog "-----------end------------"
|
||||
_exit 2
|
||||
}
|
||||
${file_func:-echolog " - ${ln_name}"} "$@" >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
start_dns() {
|
||||
local xclient_dns="$(uci_get_by_type global pdnsd_enable 0)"
|
||||
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}')
|
||||
start_pdnsd() {
|
||||
local usr_dns="$1"
|
||||
local usr_port="$2"
|
||||
if [ ! -f "$TMP_PATH/pdnsd/pdnsd.cache" ]; then
|
||||
mkdir -p $TMP_PATH/pdnsd
|
||||
touch $TMP_PATH/pdnsd/pdnsd.cache
|
||||
chown -R nobody:nogroup $TMP_PATH/pdnsd
|
||||
fi
|
||||
cat <<-EOF >$TMP_PATH/pdnsd.conf
|
||||
global{
|
||||
perm_cache=1024;
|
||||
cache_dir="$TMP_PATH/pdnsd";
|
||||
pid_file="/var/run/pdnsd.pid";
|
||||
run_as="nobody";
|
||||
server_ip=127.0.0.1;
|
||||
server_port=$dns_port;
|
||||
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;
|
||||
par_queries=1;
|
||||
}
|
||||
server{
|
||||
label="xclient-usrdns";
|
||||
ip=$usr_dns;
|
||||
port=$usr_port;
|
||||
timeout=6;
|
||||
uptest=none;
|
||||
interval=10m;
|
||||
purge_cache=off;
|
||||
}
|
||||
EOF
|
||||
ln_start_bin $(first_type pdnsd) pdnsd -c $TMP_PATH/pdnsd.conf
|
||||
}
|
||||
if [ "$xclient_dns" != "0" ]; then
|
||||
local dns2socks_port=$(uci_get_by_type global dns2socks_port)
|
||||
ipset add xclient_spec_wan_ac $dnsserver nomatch 2>/dev/null
|
||||
case "$xclient_dns" in
|
||||
1)
|
||||
start_pdnsd $dnsserver $dnsport
|
||||
pdnsd_enable_flag=1
|
||||
;;
|
||||
2)
|
||||
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $dns2socks_port xclient-dns
|
||||
ln_start_bin $(first_type dns2socks) dns2socks 127.0.0.1:$dns2socks_port $dnsserver:$dnsport 127.0.0.1:$dns_port -q
|
||||
pdnsd_enable_flag=2
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
gen_config_file() {
|
||||
case "$2" in
|
||||
1)
|
||||
local protocol="redir"
|
||||
config_file=$tcp_config_file
|
||||
;;
|
||||
2)
|
||||
local protocol="redir"
|
||||
config_file=$udp_config_file
|
||||
;;
|
||||
4)
|
||||
local protocol="socks"
|
||||
config_file=$local_config_file
|
||||
;;
|
||||
esac
|
||||
|
||||
lua /usr/share/xclient/gen_config.lua $1 $mode $3 $4 $protocol $5 $s5>$config_file
|
||||
|
||||
sed -i 's/\\//g' $TMP_PATH/*-xclient-*.json
|
||||
}
|
||||
|
||||
start_udp() {
|
||||
redir_udp=1
|
||||
local type=$(uci_get_by_name $UDP_RELAY_SERVER protocol)
|
||||
local udp_port=$(uci_get_by_type global udp_port)
|
||||
case "$type" in
|
||||
shadowsocks-plugin)
|
||||
gen_config_file $UDP_RELAY_SERVER 2 $udp_port 0 $type
|
||||
ss_program="$(first_type sslocal ss-redir)"
|
||||
ln_start_bin $ss_program ss-redir -c $udp_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "UDP TPROXY Relay:$(get_name ss) Started!"
|
||||
;;
|
||||
shadowsocks|vless|vmess|trojan)
|
||||
gen_config_file $UDP_RELAY_SERVER 2 $udp_port 0 $type
|
||||
sed -i "2i\ \"stats\": {}," $udp_config_file 2>/dev/null
|
||||
$XBIN -config $udp_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "UDP TPROXY Relay:$($(first_type "xray" "xray") -version | head -1) Started!"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
start_local() {
|
||||
[ "$LOCAL_SERVER" = "nil" ] && return 1
|
||||
local socks_port=$(uci_get_by_type global socks5_port)
|
||||
local type=$(uci_get_by_name $LOCAL_SERVER protocol)
|
||||
case "$type" in
|
||||
shadowsocks-plugin)
|
||||
gen_config_file $LOCAL_SERVER 4 $socks_port 0 $type
|
||||
ss_program="$(first_type sslocal ss-local)"
|
||||
ln_start_bin $ss_program ss-local -c $local_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "Global_Socks5:$(get_name ss) Started!"
|
||||
;;
|
||||
shadowsocks|vless|vmess|trojan)
|
||||
if [ "$_local" == "2" ]; then
|
||||
gen_config_file $LOCAL_SERVER 4 0 $socks_port 1 $type
|
||||
sed -i "2i\ \"stats\": {}," $local_config_file 2>/dev/null
|
||||
$XBIN -config $local_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "Global_Socks5:$($(first_type "xray" "xray") -version | head -1) Started!"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
local_enable=1
|
||||
return 0
|
||||
}
|
||||
|
||||
Start_Run() {
|
||||
if [ "$_local" == "1" ]; then
|
||||
local socks_port=$(uci_get_by_type global socks5_port)
|
||||
tcp_config_file=$TMP_PATH/local-xclient-retcp.json
|
||||
[ "$mode" == "tcp,udp" ] && tcp_config_file=$TMP_PATH/local-udp-xclient-retcp.json
|
||||
fi
|
||||
local type=$(uci_get_by_name $GLOBAL_SERVER protocol)
|
||||
local tcp_port=$(uci_get_by_type global tcp_port)
|
||||
s5=""
|
||||
case "$type" in
|
||||
shadowsocks-plugin)
|
||||
gen_config_file $GLOBAL_SERVER 1 $tcp_port $socks_port 0 $type
|
||||
ss_program="$(first_type sslocal ss-redir)"
|
||||
ln_start_bin "$ss_program" ss-redir -c $tcp_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "Main node:$(get_name $type) $threads Threads Started!"
|
||||
;;
|
||||
shadowsocks|vless|vmess|trojan)
|
||||
gen_config_file $GLOBAL_SERVER 1 $tcp_port $socks_port 0 $type
|
||||
sed -i "2i\ \"stats\": {}," $tcp_config_file 2>/dev/null
|
||||
$XBIN -config $tcp_config_file > $XRAY_LOG_FILE 2>&1 &
|
||||
echolog "Main node:$($(first_type xray xray) -version | head -1) Started!"
|
||||
;;
|
||||
esac
|
||||
redir_tcp=1
|
||||
return 0
|
||||
}
|
||||
|
||||
load_config() {
|
||||
if [ -z "$switch_server" ]; then
|
||||
GLOBAL_SERVER=$(uci_get_by_type global global_server nil)
|
||||
else
|
||||
GLOBAL_SERVER=$switch_server
|
||||
fi
|
||||
LOCAL_SERVER=$(uci_get_by_type global socks5_server nil)
|
||||
if [ "$GLOBAL_SERVER" == "nil" ]; then
|
||||
mode="tcp,udp"
|
||||
_local="2"
|
||||
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
|
||||
start_local
|
||||
return 1
|
||||
fi
|
||||
UDP_RELAY_SERVER=$(uci_get_by_type global udp_relay_server nil)
|
||||
tcp_config_file=$TMP_PATH/tcp-only-xclient-retcp.json
|
||||
|
||||
case "$UDP_RELAY_SERVER" in
|
||||
nil)
|
||||
mode="tcp"
|
||||
;;
|
||||
$GLOBAL_SERVER | same)
|
||||
mode="tcp,udp"
|
||||
tcp_config_file=$TMP_PATH/tcp-udp-xclient-retcp.json
|
||||
ARG_UDP="-u"
|
||||
UDP_RELAY_SERVER=$GLOBAL_SERVER
|
||||
;;
|
||||
*)
|
||||
mode="udp"
|
||||
udp_config_file=$TMP_PATH/udp-only-xclient-reudp.json
|
||||
ARG_UDP="-U"
|
||||
start_udp
|
||||
mode="tcp"
|
||||
;;
|
||||
esac
|
||||
case "$LOCAL_SERVER" in
|
||||
nil)
|
||||
_local="0"
|
||||
;;
|
||||
$GLOBAL_SERVER | same)
|
||||
_local="1"
|
||||
LOCAL_SERVER=$GLOBAL_SERVER
|
||||
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
|
||||
start_local
|
||||
local_enable=0
|
||||
;;
|
||||
*)
|
||||
_local="2"
|
||||
s5="none"
|
||||
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
|
||||
start_local
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
check_server() {
|
||||
ENABLE_SERVER=$(uci_get_by_type global global_server nil)
|
||||
if [ "$ENABLE_SERVER" == "nil" ]; then
|
||||
return 1
|
||||
else
|
||||
local STYPE=$(uci_get_by_name $ENABLE_SERVER type nil)
|
||||
if [ "$STYPE" == "nil" ]; then
|
||||
local CFGID=$(uci_get_by_cfgid servers type nil)
|
||||
if [ "$CFGID" == "nil" ]; then
|
||||
uci_set_by_type global global_server 'nil'
|
||||
else
|
||||
uci_set_by_type global global_server $CFGID
|
||||
fi
|
||||
/etc/init.d/xclient restart
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
start_monitor() {
|
||||
if [ $(uci_get_by_type global monitor_enable 1) == "1" ]; then
|
||||
let total_count=redir_tcp+redir_udp+pdnsd_enable_flag
|
||||
if [ $total_count -gt 0 ]; then
|
||||
service_start /usr/share/xclient/monitor.sh $redir_tcp $redir_udp $pdnsd_enable_flag
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
start_rules() {
|
||||
local server=$(get_host_ip $GLOBAL_SERVER)
|
||||
local tcp_port=$(uci_get_by_type global tcp_port)
|
||||
if [ "$redir_udp" == "1" ]; then
|
||||
local udp_server=$(get_host_ip $UDP_RELAY_SERVER)
|
||||
local udp_port=udp_port=$(uci_get_by_type global udp_port)
|
||||
fi
|
||||
|
||||
/usr/share/xclient/iptables.sh \
|
||||
-s "$server" \
|
||||
-l "$tcp_port" \
|
||||
-S "$udp_server" \
|
||||
-L "$udp_port" \
|
||||
-b "$(uci_get_by_type global wan_bp_ips)" \
|
||||
-B "$(uci_get_by_type global lan_bp_ips)" \
|
||||
-p "$(uci_get_by_type global lan_fp_ips)" \
|
||||
-m "$(uci_get_by_type global Interface)" \
|
||||
$ARG_UDP
|
||||
return $?
|
||||
}
|
||||
|
||||
start() {
|
||||
echo "Starting Client" > $REAL_LOG
|
||||
set_lock
|
||||
echolog "----------starting------------"
|
||||
mkdir -p /var/run /var/lock /var/log /tmp/dnsmasq.d $TMP_BIN_PATH $TMP_DNSMASQ_PATH
|
||||
echo "conf-dir=${TMP_DNSMASQ_PATH}" >"/tmp/dnsmasq.d/dnsmasq-xclient.conf"
|
||||
if load_config; then
|
||||
echo "Generating Client Config" > $REAL_LOG
|
||||
Start_Run
|
||||
echo "Setting up iptables rules" > $REAL_LOG
|
||||
start_rules
|
||||
echo "Enabling Dns" > $REAL_LOG
|
||||
start_dns
|
||||
add_cron
|
||||
fi
|
||||
echo "Restarting Dnsmasq " > $REAL_LOG
|
||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1
|
||||
check_server
|
||||
start_monitor
|
||||
clean_log
|
||||
echolog "-----------Running------------"
|
||||
unset_lock
|
||||
echo "XClient Started Successfully" >$REAL_LOG
|
||||
sleep 1
|
||||
echo "XClient is Running" >$REAL_LOG
|
||||
|
||||
}
|
||||
|
||||
boot() {
|
||||
ENABLE_=$(uci_get_by_type global enable 0)
|
||||
if [ "$ENABLE_" == "1" ]; then
|
||||
mkdir -p /var/run /var/lock /var/log /tmp/dnsmasq.d $TMP_BIN_PATH $TMP_DNSMASQ_PATH
|
||||
ulimit -n 65535
|
||||
start
|
||||
else
|
||||
if pidof xclient >/dev/null; then
|
||||
stop
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo "Stopping Client" > $REAL_LOG
|
||||
unlock
|
||||
set_lock
|
||||
/usr/share/xclient/iptables.sh -f
|
||||
$PS -w | grep -v "grep" | grep monitor | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
|
||||
$PS -w | grep -v "grep" | grep "$TMP_PATH" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
|
||||
killall -q -9 xray-plugin
|
||||
rm -f /var/lock/monitor.lock
|
||||
if [ -f "/tmp/dnsmasq.d/dnsmasq-xclient.conf" ]; then
|
||||
rm -rf /tmp/dnsmasq.d/dnsmasq-xclient.conf $TMP_DNSMASQ_PATH $TMP_PATH/*-xclient-*.json
|
||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1
|
||||
fi
|
||||
del_cron
|
||||
unset_lock
|
||||
echo "0" > /usr/share/$NAME/logstatus_check >/dev/null 2>&1
|
||||
echo "" > $LOG_FILE >/dev/null 2>&1
|
||||
echo "XClient Stopped Successfully" > $REAL_LOG
|
||||
}
|
||||
|
||||
reload(){
|
||||
stop >/dev/null 2>&1
|
||||
sleep 1
|
||||
boot >/dev/null 2>&1
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
[ ! -f "/usr/share/ucitrack/luci-app-xclient.json" ] && {
|
||||
cat > /usr/share/ucitrack/luci-app-xclient.json << EEOF
|
||||
{
|
||||
"config": "xclient",
|
||||
"init": "xclient"
|
||||
}
|
||||
EEOF
|
||||
}
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@xclient[-1]
|
||||
add ucitrack xclient
|
||||
set ucitrack.@xclient[-1].init=xclient
|
||||
commit ucitrack
|
||||
delete firewall.xclient
|
||||
set firewall.xclient=include
|
||||
set firewall.xclient.type=script
|
||||
set firewall.xclient.path=/var/etc/xclient.include
|
||||
set firewall.xclient.reload=1
|
||||
commit firewall
|
||||
EOF
|
||||
rm -rf /etc/config/xclient-opkg /etc/xclient/*opkg >/dev/null
|
||||
if [ -s "/etc/uwsgi/vassals/luci-webui.ini" ];then
|
||||
limit=$(cat /etc/uwsgi/vassals/luci-webui.ini | grep -Eo "limit-as.*"|grep -Eo "[0-9]+")
|
||||
[ $limit -lt 5000 ] && sed -i '/limit-as/c\limit-as = 5000' /etc/uwsgi/vassals/luci-webui.ini && \
|
||||
/etc/init.d/uwsgi restart
|
||||
fi
|
||||
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache
|
||||
exit 0
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"admin/services/xclient/overview": {
|
||||
"title": "XClient",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "xclient"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-xclient"
|
||||
],
|
||||
"uci": {
|
||||
"xclient": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"luci-app-xclient": {
|
||||
"description": "Grant UCI access for luci-app-xclient",
|
||||
"read": {
|
||||
"uci": ["xclient"]
|
||||
},
|
||||
"write": {
|
||||
"uci": ["xclient"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"config": "xclient",
|
||||
"init": "xclient"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
cur_core_version=$(/usr/bin/xray -version | awk '{print $2}' | sed -n 1P 2>&1 & >/dev/null)
|
||||
if [ "$?" -eq "0" ]; then
|
||||
rm -rf /usr/share/xclient/core_version
|
||||
if [ $cur_core_version ]; then
|
||||
echo $cur_core_version > /usr/share/xclient/core_version 2>&1 & >/dev/null
|
||||
elif [ $cur_core_version == "" ]; then
|
||||
echo "--" > /usr/share/xclient/core_version 2>&1 & >/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
new_core_version=`curl -sL "https://github.com/XTLS/Xray-core/tags"| grep "/XTLS/Xray-core/releases/"| head -n 1| awk -F "/tag/" '{print $2}'| sed 's/\">//'`
|
||||
if [ "$?" -eq "0" ]; then
|
||||
rm -rf /usr/share/xclient/new_core
|
||||
if [ $new_core_version ]; then
|
||||
echo $new_core_version > /usr/share/xclient/new_core 2>&1 & >/dev/null
|
||||
elif [ $new_core_version == "" ]; then
|
||||
echo 0 > /usr/share/xclient/new_core 2>&1 & >/dev/null
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,626 @@
|
||||
#!/usr/bin/lua
|
||||
--local ucursor = require "luci.model.uci"
|
||||
local ucursor = require "luci.model.uci".cursor()
|
||||
local json = require "luci.jsonc"
|
||||
local nixiofs = require "nixio.fs"
|
||||
|
||||
local proxy_section = ucursor:get_first("xclient", "global")
|
||||
local proxy = ucursor:get_all("xclient", proxy_section)
|
||||
|
||||
local tcp_server_section = proxy.global_server or nil
|
||||
local tcp_server = ucursor:get_all("xclient", tcp_server_section)
|
||||
|
||||
local udp_server_section = proxy.udp_relay_server or nil
|
||||
local udp_server = ucursor:get_all("xclient", udp_server_section)
|
||||
|
||||
local socks5_server_section = proxy.socks5_server or nil
|
||||
local socks5_server = ucursor:get_all("xclient", socks5_server_section)
|
||||
|
||||
local server_section = arg[1] or nil
|
||||
local node = ucursor:get_all("xclient", server_section)
|
||||
|
||||
local geoip_existence = false
|
||||
local geosite_existence = false
|
||||
|
||||
local xray_data_file_iterator = nixiofs.dir("/usr/share/xclient")
|
||||
|
||||
local proto = arg[2]
|
||||
local local_port = arg[3] or "1234"
|
||||
local socks_port = arg[4] or "1080"
|
||||
local _protocol = arg[5] or "redir"
|
||||
local _local = arg[6] or "0"
|
||||
|
||||
repeat
|
||||
local fn = xray_data_file_iterator()
|
||||
if fn == "geoip.dat" then
|
||||
geoip_existence = true
|
||||
end
|
||||
if fn == "geosite.dat" then
|
||||
geosite_existence = true
|
||||
end
|
||||
until fn == nil
|
||||
|
||||
|
||||
local function direct_outbound()
|
||||
return {
|
||||
protocol = "freedom",
|
||||
settings = {
|
||||
domainStrategy = "UseIP"
|
||||
},
|
||||
tag = "direct"
|
||||
}
|
||||
end
|
||||
|
||||
local function block_outbound()
|
||||
return {
|
||||
protocol = "blackhole",
|
||||
tag = "block",
|
||||
settings = {
|
||||
response = {
|
||||
type = "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function dns_outbound()
|
||||
return {
|
||||
protocol = "dns",
|
||||
address = "1.1.1.1",
|
||||
network = "tcp,udp",
|
||||
port = 53,
|
||||
tag = "dns_outbound"
|
||||
}
|
||||
end
|
||||
|
||||
local function stream_tcp_fake_http_request(server)
|
||||
if server.tcp_guise == "http" then
|
||||
return {
|
||||
version = "1.1",
|
||||
method = "GET",
|
||||
path = server.http_path,
|
||||
headers = {
|
||||
Host = server.http_host,
|
||||
User_Agent = {
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
|
||||
},
|
||||
Accept_Encoding = {"gzip, deflate"},
|
||||
Connection = {"keep-alive"},
|
||||
Pragma = "no-cache"
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_tcp_fake_http_response(server)
|
||||
if server.tcp_guise == "http" then
|
||||
return {
|
||||
version = "1.1",
|
||||
status = "200",
|
||||
reason = "OK",
|
||||
headers = {
|
||||
Content_Type = {"application/octet-stream", "video/mpeg"},
|
||||
Transfer_Encoding = {"chunked"},
|
||||
Connection = {"keep-alive"},
|
||||
Pragma = "no-cache"
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_tcp(server)
|
||||
if server.transport == "tcp" then
|
||||
return {
|
||||
header = {
|
||||
type = server.tcp_guise,
|
||||
request = stream_tcp_fake_http_request(server),
|
||||
response = stream_tcp_fake_http_response(server)
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_h2(server)
|
||||
if (server.transport == "h2") then
|
||||
return {
|
||||
path = server.h2_path,
|
||||
host = server.h2_host,
|
||||
read_idle_timeout = tonumber(server.read_idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(server.health_check_timeout) or nil
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_grpc(server)
|
||||
if (server.transport == "grpc") then
|
||||
return {
|
||||
serviceName = server.serviceName or "",
|
||||
multiMode = server.grpc_multi_mode == "1",
|
||||
initial_windows_size = tonumber(server.initial_windows_size) or nil,
|
||||
idle_timeout = tonumber(server.idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(server.health_check_timeout) or nil,
|
||||
permit_without_stream = (server.permit_without_stream == "1") and true or nil
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function stream_kcp(server)
|
||||
if server.transport == "mkcp" then
|
||||
local mkcp_seed = nil
|
||||
if server.seed ~= "" then
|
||||
mkcp_seed = server.seed
|
||||
end
|
||||
return {
|
||||
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),
|
||||
seed = mkcp_seed,
|
||||
header = {
|
||||
type = server.kcp_guise
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_quic(server)
|
||||
if server.transport == "quic" then
|
||||
return {
|
||||
security = server.quic_security,
|
||||
key = server.quic_key,
|
||||
header = {type = server.quic_guise}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_ws(server)
|
||||
if server.transport == "ws" then
|
||||
local headers = nil
|
||||
if (server.ws_host ~= nil) then
|
||||
headers = {
|
||||
Host = server.ws_host or server.tls_host
|
||||
}
|
||||
end
|
||||
return {
|
||||
path = server.ws_path,
|
||||
headers = headers
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function tls_settings(server)
|
||||
local result = {
|
||||
fingerprint = server.fingerprint,
|
||||
rejectUnknownSni = (server.rejectUnknownSni == "1") and true or false,
|
||||
allowInsecure = (server.insecure == "1") and true or false,
|
||||
serverName = server.tls_host,
|
||||
}
|
||||
if server.alpn ~= nil then
|
||||
local alpn = {}
|
||||
for _, x in ipairs(server.alpn) do
|
||||
table.insert(alpn, x)
|
||||
end
|
||||
result["alpn"] = alpn
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function reality_settings(server)
|
||||
local result = {
|
||||
fingerprint = server.fingerprint,
|
||||
shortId = server.shortId,
|
||||
publicKey = server.publicKey,
|
||||
serverName = server.tls_host,
|
||||
spiderX = server.spiderX,
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
local function shadowsocks_outbound(server, tag)
|
||||
return {
|
||||
protocol = "shadowsocks",
|
||||
tag = tag,
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
password = server.password,
|
||||
method = server.encrypt_method_v2ray_ss
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
tcpSettings = stream_tcp(server) and server.protocol ~= "shadowsocks" or nil,
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server)
|
||||
},
|
||||
mux = (server.mux == "1" and server.security == 'tls' or server.security == 'none') and {
|
||||
enabled = true,
|
||||
concurrency = tonumber(server.concurrency)
|
||||
} or nil
|
||||
}
|
||||
end
|
||||
|
||||
local function vmess_outbound(server, tag)
|
||||
return {
|
||||
protocol = "vmess",
|
||||
tag = tag,
|
||||
settings = {
|
||||
vnext = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
users = {
|
||||
{
|
||||
id = server.vmess_id,
|
||||
alterId = 0,
|
||||
security = server.vmess_encryption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server),
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function vless_outbound(server, tag)
|
||||
local flow = server.flow
|
||||
if server.flow == "none" then
|
||||
flow = nil
|
||||
end
|
||||
return {
|
||||
protocol = "vless",
|
||||
tag = tag,
|
||||
settings = {
|
||||
vnext = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
users = {
|
||||
{
|
||||
id = server.vmess_id,
|
||||
flow = flow,
|
||||
encryption = server.vless_encryption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'reality') and "reality" or (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
realitySettings = (server.security == 'reality') and "reality" and reality_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server),
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function trojan_outbound(server, tag)
|
||||
local flow = server.flow
|
||||
if server.flow == "none" then
|
||||
flow = nil
|
||||
end
|
||||
return {
|
||||
protocol = "trojan",
|
||||
tag = tag,
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
password = server.password,
|
||||
flow = flow,
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server) and server.protocol ~= "trojan" or nil,
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
},
|
||||
mux = (server.mux == "1" and server.security == 'tls' or server.security == 'none') and {
|
||||
enabled = true,
|
||||
concurrency = tonumber(server.concurrency)
|
||||
} or nil
|
||||
}
|
||||
end
|
||||
|
||||
local function server_outbound(server, tag)
|
||||
if server.protocol == "vmess" then
|
||||
return vmess_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "vless" then
|
||||
return vless_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "shadowsocks" then
|
||||
return shadowsocks_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "trojan" then
|
||||
return trojan_outbound(server, tag)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function proxy_inbound()
|
||||
if local_port == "0" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return {
|
||||
port = local_port,
|
||||
protocol = "dokodemo-door",
|
||||
tag = "proxy_inbound",
|
||||
sniffing = {
|
||||
enabled = true,
|
||||
destOverride = {"http", "tls"}
|
||||
} ,
|
||||
settings = {
|
||||
network = proto,
|
||||
followRedirect = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function socks_inbound()
|
||||
if socks_port == "0" then
|
||||
return nil
|
||||
end
|
||||
return {
|
||||
port = socks_port,
|
||||
protocol = "socks",
|
||||
tag = "socks_inbound",
|
||||
settings = {
|
||||
auth = "noauth",
|
||||
udp = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function dns_inbounds()
|
||||
local servers = {}
|
||||
if proxy["dns_servers"] ~= nil then
|
||||
for _, x in ipairs(proxy["dns_servers"]) do
|
||||
table.insert(servers, x)
|
||||
end
|
||||
end
|
||||
local i = 0
|
||||
ucursor:foreach("xclient", "dns", function(v)
|
||||
i = i + 1
|
||||
table.insert(servers, {
|
||||
address = v.address or nil,
|
||||
port = v.port,
|
||||
domains = v.domains or nil,
|
||||
expectIPs = v.expectIPs or nil,
|
||||
skipFallback = v.skipFallback == "1" and true or false
|
||||
})
|
||||
end)
|
||||
return servers
|
||||
end
|
||||
|
||||
local function dns()
|
||||
local result = {}
|
||||
for _, v in ipairs(dns_inbounds()) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
return {
|
||||
server = result,
|
||||
clientIp = proxy.clientIp or nil,
|
||||
queryStrategy = proxy.queryStrategy,
|
||||
disableCache = proxy.disableCache == "1" and true or false,
|
||||
disableFallback = proxy.disableFallback == "1" and true or false,
|
||||
disableFallbackIfMatch = proxy.disableFallbackIfMatch == "1" and true or false,
|
||||
tag = "dns_inbound"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function inbounds()
|
||||
local i = {}
|
||||
if proxy.global_server ~= "nil" or proxy.udp_server ~= "nil" then
|
||||
i = {
|
||||
proxy_inbound(),
|
||||
}
|
||||
if proxy.socks5_server == "same" or _local == "1" then
|
||||
table.insert(i, socks_inbound())
|
||||
end
|
||||
table.insert(i, {
|
||||
listen = "127.0.0.1",
|
||||
port = 8888,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {
|
||||
address = "127.0.0.1"
|
||||
},
|
||||
tag = "api"
|
||||
})
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
local function api()
|
||||
return {
|
||||
tag = "api",
|
||||
services = {
|
||||
"StatsService"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function routing()
|
||||
local rule = {}
|
||||
local i = 0
|
||||
ucursor:foreach("xclient", "rule", function(v)
|
||||
i = i + 1
|
||||
table.insert(rule, {
|
||||
type = "field",
|
||||
domainMatcher = v.domainMatcher or nil,
|
||||
domain = v.domain or nil,
|
||||
ip = v.ip or nil,
|
||||
port = v.port or nil,
|
||||
sourcePort = v.sourcePort or nil,
|
||||
network = v.network or nil,
|
||||
source = v.source or nil,
|
||||
inboundTag = v.inboundTag or nil,
|
||||
protocol = v.protocol or nil,
|
||||
outboundTag = v.outboundTag or nil,
|
||||
})
|
||||
end)
|
||||
table.insert(rule, {
|
||||
type = "field",
|
||||
inboundTag = {
|
||||
"api"
|
||||
},
|
||||
outboundTag = "api",
|
||||
enabled = true
|
||||
})
|
||||
return rule
|
||||
end
|
||||
|
||||
|
||||
local function routing_rules()
|
||||
local result = {}
|
||||
for _, v in ipairs(routing()) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
return {
|
||||
domainMatcher = proxy.domainMatcher,
|
||||
domainStrategy = proxy.routing_strategy or 'AsIs',
|
||||
rules = result
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function outbounds()
|
||||
local result = {}
|
||||
if arg[7] == "none" then
|
||||
result = {
|
||||
server_outbound(socks5_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "tcp" then
|
||||
result = {
|
||||
server_outbound(tcp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "udp" then
|
||||
result = {
|
||||
server_outbound(udp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "tcp,udp" then
|
||||
result = {
|
||||
server_outbound(tcp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local function logging()
|
||||
return {
|
||||
loglevel = proxy.log_level or "warning",
|
||||
}
|
||||
end
|
||||
|
||||
local function policy()
|
||||
return {
|
||||
system = {
|
||||
statsOutboundUplink = true,
|
||||
statsOutboundDownlink = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local xclient = {
|
||||
log = logging(),
|
||||
dns = dns(),
|
||||
routing = routing_rules(),
|
||||
dns = dns(),
|
||||
api = api(),
|
||||
policy = policy(),
|
||||
routing = routing_rules(),
|
||||
inbounds = inbounds(),
|
||||
outbounds = outbounds()
|
||||
}
|
||||
|
||||
|
||||
local shadowsocks_plugin = {
|
||||
server = node.server,
|
||||
server_port = tonumber(node.server_port),
|
||||
local_address = "0.0.0.0",
|
||||
local_port = tonumber(local_port),
|
||||
mode = (proto == "tcp,udp") and "tcp_and_udp" or proto .. "_only",
|
||||
password = node.password,
|
||||
method = node.encrypt_method_v2ray_ss,
|
||||
reuse_port = true,
|
||||
protocol = _protocol,
|
||||
plugin = node.plugin,
|
||||
plugin_opts = node.plugin_opts or nil
|
||||
}
|
||||
|
||||
if node.protocol == "shadowsocks-plugin" then
|
||||
print(json.stringify(shadowsocks_plugin, true))
|
||||
else
|
||||
print(json.stringify(xclient, true))
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
echo '' >/var/lock/xclient.lock 2>/dev/null
|
||||
echo '' >/tmp/geo_ip_update.txt 2>/dev/null
|
||||
echo 'Checking for latest version' >/tmp/geo_ip_update.txt 2>/dev/null
|
||||
|
||||
new_version=`wget -qO- "https://github.com/Loyalsoldier/v2ray-rules-dat/tags"| grep "/Loyalsoldier/v2ray-rules-dat/releases/"| head -n 1| awk -F "/tag/" '{print $2}'| sed 's/\">//'`
|
||||
|
||||
rm -rf /tmp/geoip.dat >/dev/null 2>&1
|
||||
echo 'Latest version: ' $new_version >/tmp/geo_ip_update.txt 2>/dev/null
|
||||
|
||||
if [ -f /var/run/geo_ip_down_complete ];then
|
||||
rm -rf /var/run/geo_ip_down_complete 2>/dev/null
|
||||
fi
|
||||
|
||||
echo 'Starting download...' >/tmp/geo_ip_update.txt 2>/dev/null
|
||||
|
||||
wget -c4 --no-check-certificate --timeout=60 --user-agent="Mozilla" https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/"$new_version"/geoip.dat -O /tmp/geoip.dat
|
||||
sleep 2
|
||||
if [ "$?" -eq "0" ] && [ "$(ls -l /tmp/geoip.dat |awk '{print int($5)}')" -ne 0 ]; then
|
||||
touch /var/run/geo_ip_down_complete >/dev/null 2>&1
|
||||
rm -rf /var/run/geo_ip_update >/dev/null 2>&1
|
||||
rm -rf /usr/bin/geoip.dat && mv /tmp/geoip.dat /usr/bin
|
||||
echo $new_version > /usr/share/xclient/geoip_version 2>/dev/null
|
||||
echo "" > /tmp/geo_ip_update.txt >/dev/null 2>&1
|
||||
rm -rf /var/lock/xclient.lock 2>/dev/null
|
||||
if pidof xclient >/dev/null; then
|
||||
/etc/init.d/xclient boot 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1 @@
|
||||
202201242210
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
echo '' >/var/lock/xclient.lock 2>/dev/null
|
||||
echo '' >/tmp/geo_site_update.txt 2>/dev/null
|
||||
echo 'Checking for latest version' >/tmp/geo_site_update.txt 2>/dev/null
|
||||
|
||||
new_version=`wget -qO- "https://github.com/Loyalsoldier/v2ray-rules-dat/tags"| grep "/Loyalsoldier/v2ray-rules-dat/releases/"| head -n 1| awk -F "/tag/" '{print $2}'| sed 's/\">//'`
|
||||
|
||||
rm -rf /tmp/geosite.dat >/dev/null 2>&1
|
||||
|
||||
echo 'Latest version: ' $new_version >/tmp/geo_site_update.txt 2>/dev/null
|
||||
|
||||
if [ -f /var/run/geo_site_down_complete ];then
|
||||
rm -rf /var/run/geo_site_down_complete 2>/dev/null
|
||||
fi
|
||||
|
||||
echo 'Starting download...' >/tmp/geo_ip_update.txt 2>/dev/null
|
||||
|
||||
wget -c4 --no-check-certificate --timeout=60 --user-agent="Mozilla" https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/"$new_version"/geosite.dat -O /tmp/geosite.dat
|
||||
if [ "$?" -eq "0" ] && [ "$(ls -l /tmp/geosite.dat |awk '{print int($5)}')" -ne 0 ]; then
|
||||
touch /var/run/geo_site_down_complete >/dev/null 2>&1
|
||||
rm -rf /var/run/geo_site_update >/dev/null 2>&1
|
||||
rm -rf /usr/bin/geosite.dat && mv /tmp/geosite.dat /usr/bin
|
||||
echo $new_version > /usr/share/xclient/geosite_version 2>/dev/null
|
||||
echo "" > /tmp/geo_site_update.txt >/dev/null 2>&1
|
||||
rm -rf /var/lock/xclient.lock 2>/dev/null
|
||||
if pidof xclient >/dev/null; then
|
||||
/etc/init.d/xclient boot 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1 @@
|
||||
202201242210
|
||||
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
TAG="_XCLIENT_SPEC_RULE_" # comment tag
|
||||
IPT="iptables -t nat" # alias of iptables
|
||||
FWI=$(uci get firewall.xclient.path 2>/dev/null) # firewall include file
|
||||
usage() {
|
||||
cat <<-EOF
|
||||
Usage: xrules [options]
|
||||
|
||||
Valid options are:
|
||||
|
||||
-s <server_ip> ip address of xclient remote server
|
||||
-l <local_port> port number of xclient local server
|
||||
-S <server_ip> ip address of xclient remote UDP server
|
||||
-L <local_port> port number of xclient local UDP server
|
||||
define access control mode
|
||||
-b <wan_ips> wan ip of will be bypassed
|
||||
-B <bp_lan_ips> lan ip of will be bypassed proxy
|
||||
-p <fp_lan_ips> lan ip of will be global proxy
|
||||
-m <Interface> Interface name
|
||||
-e <extra_options> extra options for iptables
|
||||
-u enable udprelay mode, TPROXY is required
|
||||
-U enable udprelay mode, using different IP
|
||||
and ports for TCP and UDP
|
||||
-f flush the rules
|
||||
-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 xrules[$$] -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 '/^:XCLIENT_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 xclient_spec_wan_ac 2>/dev/null
|
||||
ipset -X fplan 2>/dev/null
|
||||
ipset -X bplan 2>/dev/null
|
||||
ipset -X blacklist 2>/dev/null
|
||||
[ -n "$FWI" ] && echo '#!/bin/sh' >$FWI
|
||||
return 0
|
||||
}
|
||||
|
||||
ipset_r() {
|
||||
ipset -! -R <<-EOF || return 1
|
||||
create xclient_spec_wan_ac hash:net
|
||||
$(gen_spec_iplist | sed -e "s/^/add xclient_spec_wan_ac /")
|
||||
EOF
|
||||
$IPT -N XCLIENT_SPEC_WAN_AC
|
||||
$IPT -I XCLIENT_SPEC_WAN_AC -p tcp ! --dport 53 -d $server -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_AC -m set --match-set xclient_spec_wan_ac dst -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_AC -j XCLIENT_SPEC_WAN_FW
|
||||
ipset -N fplan hash:net 2>/dev/null
|
||||
for ip in $LAN_FP_IP; do ipset -! add fplan $ip; done
|
||||
$IPT -I XCLIENT_SPEC_WAN_AC -m set --match-set fplan src -j XCLIENT_SPEC_WAN_FW
|
||||
ipset -N bplan hash:net 2>/dev/null
|
||||
for ip in $LAN_BP_IP; do ipset -! add bplan $ip; done
|
||||
$IPT -I XCLIENT_SPEC_WAN_AC -m set --match-set bplan src -j RETURN
|
||||
ipset -N blacklist hash:net 2>/dev/null
|
||||
$IPT -I XCLIENT_SPEC_WAN_AC -m set --match-set blacklist dst -j RETURN
|
||||
if [ $(ipset list music -name -quiet | grep music) ]; then
|
||||
$IPT -I XCLIENT_SPEC_WAN_AC -m set --match-set music dst -j RETURN 2>/dev/null
|
||||
fi
|
||||
for ip in $WAN_BP_IP; do ipset -! add blacklist $ip; done
|
||||
return $?
|
||||
}
|
||||
|
||||
fw_rule() {
|
||||
$IPT -N XCLIENT_SPEC_WAN_FW
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 0.0.0.0/8 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 10.0.0.0/8 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 127.0.0.0/8 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 169.254.0.0/16 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 172.16.0.0/12 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 192.168.0.0/16 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 224.0.0.0/4 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -d 240.0.0.0/4 -j RETURN
|
||||
$IPT -A XCLIENT_SPEC_WAN_FW -p tcp -j REDIRECT --to-ports $local_port 2>/dev/null || {
|
||||
loger 3 "Can't redirect, please check the iptables."
|
||||
exit 1
|
||||
}
|
||||
return $?
|
||||
}
|
||||
|
||||
ac_rule() {
|
||||
if [ -z "$Interface" ]; then
|
||||
$IPT -I PREROUTING 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j XCLIENT_SPEC_WAN_AC
|
||||
else
|
||||
for name in $Interface; do
|
||||
local IFNAME=$(uci -P /var/state get network.$name.device 2>/dev/null)
|
||||
[ -n "$IFNAME" ] && $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS -m comment --comment "$TAG" -j XCLIENT_SPEC_WAN_AC
|
||||
done
|
||||
fi
|
||||
$IPT -I OUTPUT 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j XCLIENT_SPEC_WAN_AC
|
||||
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 XCLIENT_SPEC_TPROXY
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp --dport 53 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 0.0.0.0/8 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 10.0.0.0/8 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 127.0.0.0/8 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 169.254.0.0/16 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 172.16.0.0/12 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 192.168.0.0/16 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 224.0.0.0/4 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -d 240.0.0.0/4 -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp ! --dport 53 -d $SERVER -j RETURN
|
||||
[ "$server" != "$SERVER" ] && ipset -! add blacklist $SERVER
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -m set --match-set bplan src -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -m set --match-set fplan src -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -m set --match-set xclient_spec_wan_ac dst -j RETURN
|
||||
$ipt -A XCLIENT_SPEC_TPROXY -p udp -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
||||
if [ -z "$Interface" ]; then
|
||||
$ipt -I PREROUTING 1 -p udp $EXT_ARGS -m comment --comment "$TAG" -j XCLIENT_SPEC_TPROXY
|
||||
else
|
||||
for name in $Interface; do
|
||||
local IFNAME=$(uci -P /var/state get network.$name.device 2>/dev/null)
|
||||
[ -n "$IFNAME" ] && $ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS -m comment --comment "$TAG" -j XCLIENT_SPEC_TPROXY
|
||||
done
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
|
||||
get_wan_ip() {
|
||||
cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
|
||||
$server
|
||||
$SERVER
|
||||
$WAN_BP_IP
|
||||
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 XCLIENT_SPEC_ | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
|
||||
echo 'COMMIT'
|
||||
}
|
||||
cat <<-EOF >>$FWI
|
||||
iptables-save -c | grep -v "XCLIENT_SPEC" | iptables-restore -c
|
||||
iptables-restore -n <<-EOT
|
||||
$(extract_rules nat)
|
||||
$(extract_rules mangle)
|
||||
EOT
|
||||
EOF
|
||||
return 0
|
||||
}
|
||||
|
||||
while getopts ":m:s:l:S:L:e:B:b:p:D:uUfh" arg; do
|
||||
case "$arg" in
|
||||
m)
|
||||
Interface=$OPTARG
|
||||
;;
|
||||
s)
|
||||
server=$OPTARG
|
||||
;;
|
||||
l)
|
||||
local_port=$OPTARG
|
||||
;;
|
||||
S)
|
||||
SERVER=$OPTARG
|
||||
;;
|
||||
L)
|
||||
LOCAL_PORT=$OPTARG
|
||||
;;
|
||||
e)
|
||||
EXT_ARGS=$OPTARG
|
||||
;;
|
||||
B)
|
||||
LAN_BP_IP=$OPTARG
|
||||
;;
|
||||
b)
|
||||
WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
|
||||
;;
|
||||
p)
|
||||
LAN_FP_IP=$OPTARG
|
||||
;;
|
||||
u)
|
||||
TPROXY=1
|
||||
;;
|
||||
U)
|
||||
TPROXY=2
|
||||
;;
|
||||
f)
|
||||
flush_r
|
||||
exit 0
|
||||
;;
|
||||
h) usage 0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$server" -o -z "$local_port" ]; then
|
||||
usage 2
|
||||
fi
|
||||
|
||||
case "$TPROXY" in
|
||||
1)
|
||||
SERVER=$server
|
||||
LOCAL_PORT=$local_port
|
||||
;;
|
||||
2)
|
||||
: ${SERVER:?"You must assign an ip for the udp relay server."}
|
||||
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
|
||||
;;
|
||||
esac
|
||||
|
||||
flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include
|
||||
RET=$?
|
||||
[ "$RET" = 0 ] || loger 3 "Start failed!"
|
||||
exit $RET
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
|
||||
. $IPKG_INSTROOT/etc/init.d/xclient
|
||||
LOCK_FILE="/var/lock/monitor.lock"
|
||||
[ -f "$LOCK_FILE" ] && exit 2
|
||||
touch "$LOCK_FILE"
|
||||
redir_tcp_process=$1
|
||||
redir_udp_process=$2
|
||||
pdnsd_process=$3
|
||||
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)
|
||||
server_port=$(uci_get_by_name $GLOBAL_SERVER server_port)
|
||||
|
||||
|
||||
while [ "1" == "1" ]; do
|
||||
sleep 1
|
||||
#redir tcp
|
||||
if [ "$redir_tcp_process" -gt 0 ]; then
|
||||
icount=$(busybox ps -w | grep xclient-retcp | grep -v grep | wc -l)
|
||||
if [ "$icount" == 0 ]; then
|
||||
logger -t "$NAME" "xclient redir tcp error.restart!"
|
||||
echolog "xclient redir tcp error.restart!"
|
||||
/etc/init.d/xclient reload
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
#redir udp
|
||||
if [ "$redir_udp_process" -gt 0 ]; then
|
||||
icount=$(busybox ps -w | grep xclient-reudp | grep -v grep | wc -l)
|
||||
if [ "$icount" == 0 ]; then
|
||||
logger -t "$NAME" "xclient redir udp error.restart!"
|
||||
echolog "xclient redir udp error.restart!"
|
||||
/etc/init.d/xclient reload
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
#pdnsd
|
||||
if [ "$pdnsd_process" -eq 1 ]; then
|
||||
icount=$(busybox ps -w | grep $TMP_BIN_PATH/pdnsd | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt "$pdnsd_process" ]; then
|
||||
logger -t "$NAME" "pdnsd tunnel error.restart!"
|
||||
echolog "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 $TMP_PATH/pdnsd.conf | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
fi
|
||||
ln_start_bin $(first_type pdnsd) pdnsd -c $TMP_PATH/pdnsd.conf
|
||||
fi
|
||||
fi
|
||||
#dns2socks
|
||||
if [ "$pdnsd_process" -eq 2 ]; then
|
||||
icount=$(busybox ps -w | grep -e xclient-dns -e "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | wc -l)
|
||||
if [ "$icount" -lt 2 ]; then
|
||||
logger -t "$NAME" "dns2socks $dnsstr tunnel error.restart!"
|
||||
echolog "dns2socks $dnsstr tunnel error.restart!"
|
||||
dnsstr=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
|
||||
dnsserver=$(echo "$dnsstr" | awk -F ':' '{print $1}')
|
||||
dnsport=$(echo "$dnsstr" | awk -F ':' '{print $2}')
|
||||
kill -9 $(busybox ps -w | grep xclient-dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
kill -9 $(busybox ps -w | grep "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
|
||||
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $tmp_dns_port xclient-dns
|
||||
ln_start_bin $(first_type dns2socks) dns2socks 127.0.0.1:$tmp_dns_port $dnsserver:$dnsport 127.0.0.1:$dns_port -q
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,544 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
require "luci.model.uci"
|
||||
require "nixio"
|
||||
require "luci.util"
|
||||
require "luci.sys"
|
||||
require "luci.jsonc"
|
||||
-- these global functions are accessed all the time by the event handler
|
||||
-- so caching them is worth the effort
|
||||
|
||||
|
||||
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 = 'xclient'
|
||||
local uciType = 'servers'
|
||||
local ucic = luci.model.uci.cursor()
|
||||
local proxy = '0'
|
||||
local switch = '0'
|
||||
local subscribe_url = ucic:get(name, 'config', 'subscribe_url')
|
||||
local filter_words = '过期时间/剩余流量'
|
||||
local save_words = ''
|
||||
local v2_ss = "xray"
|
||||
local v2_tj = "xray"
|
||||
local log = function(...)
|
||||
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({...}, " "))
|
||||
end
|
||||
local encrypt_methods_ss = {
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
}
|
||||
|
||||
local function split(full, sep)
|
||||
full = full:gsub("%z", "")
|
||||
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
|
||||
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 checkTabValue(tab)
|
||||
local revtab = {}
|
||||
for k,v in pairs(tab) do
|
||||
revtab[v] = true
|
||||
end
|
||||
return revtab
|
||||
end
|
||||
|
||||
local function processData(szType, content)
|
||||
local result = {type = szType}
|
||||
if szType == 'vmess' then
|
||||
local info = jsonParse(content)
|
||||
result.type = 'xray'
|
||||
result.protocol = 'vmess'
|
||||
result.server = info.add
|
||||
result.server_port = info.port
|
||||
result.transport = info.net
|
||||
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 == 'quic' then
|
||||
result.quic_guise = "none"
|
||||
result.quic_key = ""
|
||||
result.quic_security = "none"
|
||||
end
|
||||
if info.net == 'grpc' then
|
||||
result.serviceName = info.serviceName
|
||||
end
|
||||
if info.net == 'tcp' then
|
||||
if info.type and info.type ~= "http" then
|
||||
info.type = "none"
|
||||
end
|
||||
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.security then
|
||||
result.security = info.security
|
||||
end
|
||||
if info.tls == "tls" or info.tls == "1" then
|
||||
result.security = "tls"
|
||||
result.tls_host = info.sni
|
||||
result.insecure = 1
|
||||
else
|
||||
result.security = "none"
|
||||
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 = v2_ss
|
||||
result.password = password
|
||||
result.server = host[1]
|
||||
result.transport = "tcp"
|
||||
result.protocol = "shadowsocks"
|
||||
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
|
||||
result.protocol = "shadowsocks-plugin"
|
||||
end
|
||||
else
|
||||
result.server_port = host[2]:gsub("/","")
|
||||
end
|
||||
result.encrypt_method_v2ray_ss = method
|
||||
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 userinfo = hostInfo[1]
|
||||
local password = userinfo
|
||||
result.alias = UrlDecode(alias)
|
||||
result.type = v2_tj
|
||||
result.protocol = "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, '=')
|
||||
params[t[1]] = t[2]
|
||||
end
|
||||
|
||||
if params.obfs == "grpc" and params.security == "none" then
|
||||
result.security = "none"
|
||||
else
|
||||
result.security = "tls"
|
||||
end
|
||||
|
||||
if params.sni then
|
||||
result.tls_host = params.sni
|
||||
end
|
||||
if params.allowInsecure then
|
||||
result.insecure = params.allowInsecure
|
||||
end
|
||||
if params.obfs then
|
||||
if params.obfs == "grpc" then
|
||||
result.transport = params.obfs
|
||||
result.serviceName = params.serviceName
|
||||
end
|
||||
if params.obfs == "websocket" then
|
||||
result.transport = "ws"
|
||||
result.ws_host = params.obfsParam
|
||||
result.ws_path = params.path
|
||||
end
|
||||
else
|
||||
result.transport = "tcp"
|
||||
end
|
||||
else
|
||||
result.server_port = host[2]
|
||||
end
|
||||
result.password = password
|
||||
elseif szType == "vless" 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 uuid = hostInfo[1]
|
||||
if host[2]:find("?") then
|
||||
local query = split(host[2], "?")
|
||||
local params = {}
|
||||
for _, v in pairs(split(UrlDecode(query[2]), '&')) do
|
||||
local t = split(v, '=')
|
||||
params[t[1]] = t[2]
|
||||
end
|
||||
result.alias = UrlDecode(alias)
|
||||
result.type = 'v2ray'
|
||||
result.protocol = 'vless'
|
||||
result.server = host[1]
|
||||
result.server_port = query[1]
|
||||
result.vmess_id = uuid
|
||||
result.vless_encryption = params.encryption or "none"
|
||||
result.transport = params.type and (params.type == 'http' and 'h2' or params.type) or "tcp"
|
||||
if not params.type or params.type == "tcp" then
|
||||
result.vless_flow = params.flow
|
||||
end
|
||||
if params.type == 'ws' then
|
||||
result.ws_host = params.host
|
||||
result.ws_path = params.path or "/"
|
||||
end
|
||||
if params.type == 'http' then
|
||||
result.h2_host = params.host
|
||||
result.h2_path = params.path or "/"
|
||||
end
|
||||
if params.type == 'kcp' then
|
||||
result.kcp_guise = params.headerType or "none"
|
||||
result.mtu = 1350
|
||||
result.tti = 50
|
||||
result.uplink_capacity = 5
|
||||
result.downlink_capacity = 20
|
||||
result.read_buffer_size = 2
|
||||
result.write_buffer_size = 2
|
||||
result.seed = params.seed or ""
|
||||
end
|
||||
if info.net == 'quic' then
|
||||
result.quic_guise = "none"
|
||||
result.quic_key = ""
|
||||
result.quic_security = "none"
|
||||
end
|
||||
if params.type == 'grpc' then
|
||||
result.serviceName = params.serviceName
|
||||
end
|
||||
if params.security == "tls" then
|
||||
result.security = "tls"
|
||||
result.tls_host = params.sni
|
||||
elseif params.security == "reality" then
|
||||
result.security = "reality"
|
||||
result.tls_host = params.sni
|
||||
result.publicKey = params.publicKey
|
||||
result.shortId = params.shortId
|
||||
result.fingerprint = params.fingerprint
|
||||
result.spiderX = params.spiderX
|
||||
else
|
||||
result.security = "none"
|
||||
end
|
||||
else
|
||||
result.server_port = host[2]
|
||||
end
|
||||
end
|
||||
if not result.alias then
|
||||
if result.server and result.server_port then
|
||||
result.alias = result.server .. ':' .. result.server_port
|
||||
else
|
||||
result.alias = "NULL"
|
||||
end
|
||||
end
|
||||
|
||||
local alias = result.alias
|
||||
result.alias = nil
|
||||
result.hashkey = md5(jsonStringify(result))
|
||||
result.alias = alias
|
||||
return result
|
||||
end
|
||||
-- wget
|
||||
local function wget(url)
|
||||
local stdout = luci.sys.exec('uclient-fetch -q --user-agent="Luci-app-xclient/OpenWRT" --no-check-certificate -O- "' .. url .. '"')
|
||||
return trim(stdout)
|
||||
end
|
||||
|
||||
local function check_filer(result)
|
||||
do
|
||||
|
||||
local filter_word = split(filter_words, "/")
|
||||
|
||||
local check_save = false
|
||||
if save_words ~= nil and save_words ~= "" and save_words ~= "NULL" then
|
||||
check_save = true
|
||||
end
|
||||
local save_word = split(save_words, "/")
|
||||
|
||||
|
||||
local filter_result = false
|
||||
local save_result = true
|
||||
|
||||
|
||||
for i, v in pairs(filter_word) do
|
||||
if tostring(result.alias):find(v) then
|
||||
filter_result = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if check_save == true then
|
||||
for i, v in pairs(save_word) do
|
||||
if tostring(result.alias):find(v) then
|
||||
save_result = false
|
||||
end
|
||||
end
|
||||
else
|
||||
save_result = false
|
||||
end
|
||||
|
||||
|
||||
if filter_result == true or save_result == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local execute = function()
|
||||
-- exec
|
||||
do
|
||||
if proxy == '0' then
|
||||
log('service is pausing..')
|
||||
luci.sys.init.stop(name)
|
||||
end
|
||||
if subscribe_url then
|
||||
local raw = wget(subscribe_url)
|
||||
if #raw > 0 then
|
||||
local nodes, szType
|
||||
local groupHash = md5(subscribe_url)
|
||||
cache[groupHash] = {}
|
||||
tinsert(nodeResult, {})
|
||||
local index = #nodeResult
|
||||
if jsonParse(raw) then
|
||||
nodes = jsonParse(raw).servers or jsonParse(raw)
|
||||
if nodes[1].server and nodes[1].method then
|
||||
szType = 'sip008'
|
||||
end
|
||||
else
|
||||
nodes = split(base64Decode(raw):gsub(" ", "_"), "\n")
|
||||
end
|
||||
for _, v in ipairs(nodes) do
|
||||
if v then
|
||||
local result
|
||||
if szType 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
|
||||
local dat3 = ""
|
||||
if dat[3] then
|
||||
dat3 = "://" .. dat[3]
|
||||
end
|
||||
if dat[1] == 'ss' or dat[1] == 'trojan' then
|
||||
result = processData(dat[1], dat[2] .. dat3)
|
||||
else
|
||||
result = processData(dat[1], base64Decode(dat[2]))
|
||||
end
|
||||
end
|
||||
else
|
||||
log('skip unknown types: ' .. szType)
|
||||
end
|
||||
-- log(result)
|
||||
if result then
|
||||
if not result.server or not result.server_port or result.alias == "NULL" or check_filer(result) or result.server:match("[^0-9a-zA-Z%-%.%s]") or cache[groupHash][result.hashkey] then
|
||||
log('Discard invalid nodes: 【' .. result.type .. ' 】, ' .. result.alias)
|
||||
else
|
||||
result.grouphashkey = groupHash
|
||||
tinsert(nodeResult[index], result)
|
||||
cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
log('Number of nodes successfully resolved: ' .. #nodes)
|
||||
else
|
||||
log(subscribe_url .. ': get content is empty')
|
||||
end
|
||||
end
|
||||
end
|
||||
-- diff
|
||||
do
|
||||
if next(nodeResult) == nil then
|
||||
log("Update failed, no node info available")
|
||||
return
|
||||
end
|
||||
local add, del = 0, 0
|
||||
ucic:foreach(name, uciType, function(old)
|
||||
if old.grouphashkey or old.hashkey then
|
||||
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 not old.alias then
|
||||
if old.server or old.server_port then
|
||||
old.alias = old.server .. ':' .. old.server_port
|
||||
log('Ignore manually added nodes: ' .. old.alias)
|
||||
else
|
||||
ucic:delete(name, old['.name'])
|
||||
end
|
||||
else
|
||||
log('Ignore manually added nodes: ' .. old.alias)
|
||||
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)
|
||||
add = add + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
ucic:commit(name)
|
||||
local globalServer = ucic:get_first(name, 'global', 'global_server', '')
|
||||
if globalServer ~= "nil" then
|
||||
local firstServer = ucic:get_first(name, uciType)
|
||||
if firstServer then
|
||||
if not ucic:get(name, globalServer) then
|
||||
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
|
||||
ucic:commit(name)
|
||||
ucic:set(name, ucic:get_first(name, 'global'), 'global_server', ucic:get_first(name, uciType))
|
||||
ucic:commit(name)
|
||||
log('The current active server has been deleted and is being automatically replaced with the first node。')
|
||||
luci.sys.call("/etc/init.d/" .. name .. " start > /dev/null 2>&1 &")
|
||||
else
|
||||
log('Maintain the current servers。')
|
||||
luci.sys.call("/etc/init.d/" .. name .. " boot > /dev/null 2>&1 &")
|
||||
end
|
||||
else
|
||||
log('There is no server node, stop the service')
|
||||
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
|
||||
end
|
||||
end
|
||||
log('New Nodes: ' .. add, 'Deleted Nodes: ' .. del)
|
||||
log('Subscription updated successfully')
|
||||
end
|
||||
end
|
||||
|
||||
if subscribe_url and #subscribe_url > 0 then
|
||||
xpcall(execute, function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log('An error occurred, restoring service')
|
||||
local firstServer = ucic:get_first(name, uciType)
|
||||
if firstServer then
|
||||
luci.sys.call("/etc/init.d/" .. name .. " boot > /dev/null 2>&1 &")
|
||||
log('Service Restart Successful')
|
||||
else
|
||||
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
|
||||
log('Service Stop Successful')
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<defs> <clipPath id="ldio-x3jsygn7oea-cp"> <circle cx="50" cy="50" r="35"></circle> </clipPath> </defs>
|
||||
<circle cx="50" cy="50" r="35" fill="#009ef7"></circle>
|
||||
<g clip-path="url(#ldio-x3jsygn7oea-cp)">
|
||||
<path transform="scale(2) translate(-25 -25)" fill="#dbfb54" d="M64.4,74.4c0.3,0.3,0.2,0.3-0.3,0.3C64.1,74.3,64.2,74.5,64.4,74.4z M31.4,75.4c-0.2,0.1-0.3-0.2-0.5,0.1c0.3,0.2,0.6,0.1,0.8-0.1C31.6,75.2,31.6,75.4,31.4,75.4z M84.9,36.9c-0.3,0.2-0.8-0.1-1.1-0.5 c0.1,0.2,0.1,0.6,0.3,0.9c-0.5,0.2-0.2,0.6,0.2,0.7c0-0.1,0-0.3,0-0.4c0.2,0.1,0.4,0.2,0.5,0.3c0.2-0.2,0.2-0.3,0.4-0.5 C85.1,37.2,85,37,84.9,36.9z M85.1,40.6c0-0.6-0.1-2.1-0.6-2.4c-0.6-0.4,0.5,2.1-0.6,1.6c0,0.2-0.1,0.5,0,0.7 c-0.8-0.3-0.9,0.5-1.4,0.9c0.3,0.3,0.4,0.6,0.6,0.9c0.3-0.4,0.2-0.9-0.3-1.2c0.2,0,0.4-0.2,0.6-0.2c0.2,0,0.6,0.2,0.6,0.2 C84.2,41,84.5,40.7,85.1,40.6z M83.7,41c-0.4-0.1-0.7,0.2-0.3,0.6c0.2-0.4,0-0.1,0.3-0.2C83.8,41.2,83.6,41.1,83.7,41z M93.5,62.8 c0.1,0.5,0.3,0.9,0.7,0.8C94.1,63.3,93.8,63,93.5,62.8z M93,71.2c-0.1-0.1-2.5,1.6-2.9,2.1c0.8,0.5,2.9-1.2,3.1-2.1 C93,71.5,92.9,71.2,93,71.2z M95,69.9c-0.4,0.2-0.6,0-0.5-0.4c0,0.1-0.1,0.2-0.1,0.3c0.1-0.5,0.1-0.8-0.1-1.1 c-0.3,0.7,0.1,1.4-0.7,2c0.3,0.3,0.3,0.4-0.1,0.7c0.1,0,0.1,0.1,0.1,0.2C93.9,71.3,95,70.2,95,69.9z M82,50.6 c0.2-0.2,0.3-0.2,0.3-0.5c-0.1,0-0.3-0.1-0.4-0.1C81.9,50.2,81.9,50.4,82,50.6z M82.4,50.6c-0.3-0.4-0.2,0.1-0.1,0.5 c0.2-0.3,0.3-0.5,0.2-0.9C82.5,50.4,82.4,50.5,82.4,50.6z M82.8,49.7c-0.1,0-0.2,0-0.3,0c0,0.1,0.2,0.4,0.2,0.5c-0.1,0-0.1,0-0.2,0 c0.1,0.2,0.1,0.3,0.2,0.5c0.1-0.2,0.1-0.2,0-0.5c0.1,0,0.2,0.1,0.2,0.1C83,50.1,82.9,49.9,82.8,49.7z M81,51 c0.2-0.3,0.3-0.4,0.2-0.8c-0.1,0.5-0.4,0.8-0.6,1.2C80.8,51.3,80.9,51.1,81,51z M81.2,48.6c0.1,0.2,0.1,0.5,0.3,0.3 c0,0.4,0.3,0.6,0.6,0.6c0,0,0-0.1,0.1-0.1c0.1,0.2,0.2,0.3,0.4,0.4c0-0.7-0.6-0.5-0.8-1c-0.2-0.4,0.2-0.9,0-1.4 c-0.5-0.1-0.6,0.3-0.4,1c0,0-0.1-0.1-0.1-0.1C81.1,48.3,81.1,48.4,81.2,48.6z M83.3,52.2c0.1-0.5,0.1-1.1-0.3-1.4c0,0.1,0,0.2,0,0.3 c-0.4,0.2-0.9,0.1-0.9,0.8c0.7-0.7,0.4,0.2,1,0.5c0-0.2,0.1-0.5,0.1-0.7C83.2,52,83.3,52.1,83.3,52.2z M81.5,49.6 c0.1-0.1,0.3-0.1-0.2-0.3C81.4,49.4,81.5,49.5,81.5,49.6z M91.8,57.4c0.1,0.1,0,0.2,0.2,0.1c-0.1-0.2-0.2-0.5-0.3-0.7 C91.6,57,91.7,57.1,91.8,57.4z M90.2,57.3c0.4-0.1,1.1-0.5,0.6-0.9c-0.1,0.4-0.2,0.6-0.4,0.3c-0.1,0-0.5,0.2-0.5,0.3 C89.9,57.2,90,57.2,90.2,57.3z M90.9,56.1c0.1,0.2,0.2,0.3,0.2,0.5c0.1-0.5-0.2-0.8-0.6-0.8C90.7,55.9,90.8,56,90.9,56.1z M90.3,59 c-0.2-0.4-0.7-0.7-1-1.6c0.1,0,0.2,0,0.3,0c-0.7-0.7-1.4-1.7-2.5-1.8c-0.5-0.1-1,0.5-1.3,0.3c-0.5-0.2-0.1-1.4-1.2-0.8 c0.3,0.4,0.6,0.5,0.9,0.6c-0.2,0-0.3,0.1-0.5,0.1c0.1,0.2,0.2,0.4,0.3,0.5c0.1,0,0.2-0.2,0.2-0.2c0.6,0.5,2,1,1,2 c0.6-0.3,0.7,0.4,1.2,0.3c0.4-0.1,0.4-0.5,0.6-0.5C89.2,57.9,89.5,58.9,90.3,59z M93.2,58.5c-0.1,0.1-0.2-0.2-0.2,0.1 C93.3,58.8,93.2,58.6,93.2,58.5z M92.4,57.7c0,0-0.1-0.2-0.2-0.3C92.2,57.6,92.3,57.6,92.4,57.7z M92.9,58c0.1,0.1,0.1,0.1,0.2,0.2 c-0.2-0.2-0.3-0.3-0.5-0.4C92.7,57.8,92.8,57.9,92.9,58z M93.5,59.1C93.6,58.9,93.6,58.8,93.5,59.1z M93.5,58.7c0-0.2,0-0.4-0.2-0.6 C93.3,58.3,93.4,58.5,93.5,58.7z M81,45.9c0.1-0.5,0.2-1-0.1-1.3C80.6,45,80.6,45.5,81,45.9z M94.8,61c0,0,0.1-0.2,0-0.4 C94.7,60.8,94.8,61.1,94.8,61z M94.8,61.2c0.1,0.1,0,0.3,0.2,0.1C94.9,61.1,94.8,61.2,94.8,61.2z M29.5,76.3 c-0.4-0.8-0.5-0.2-0.7-0.2c0.2,0-0.3,0.2-0.3,0.2c-0.2,0-0.6-0.3-0.9-0.4c0.9,0.9,1.9,1.2,2.6,0.7C30,76.6,29.8,76.4,29.5,76.3z M89.6,64.4c0-0.2-0.2-0.3-0.1-0.7c-0.2,0-0.1-0.1-0.3,0c0.1-0.8-0.5-1.1-0.7-1.7c-0.1-0.5,0-1-0.2-1.6c-0.1,0.1-0.1,0.1-0.2,0.2 c0-0.5-0.1-1.1-0.2-1.6c-0.5,0.9-0.5,3.8-1.7,2.2c-0.3-0.5-0.4-0.2-0.4-0.8c0-0.3,0.4-0.5,0.4-0.7c0-0.1-1.1-0.4-1.4-0.5 c0.3,0.3-0.7,0.7-0.8,1.5c-0.5-0.2-0.7-0.4-1.1-0.2c-0.4,0.1-0.7,0.9-0.7,1.1c0,0-0.1,0.1-0.1,0.1c0-0.1,0-0.2-0.1-0.3 c-0.4,0.8-0.6,1.1-1.3,1.4c-0.6,0.3-1.1,0.4-1.6,1c0-0.1,0-0.2,0-0.3c-0.3,0.6-0.5,1.1-0.3,1.8c-0.1-0.1-0.1-0.2-0.2-0.3 c0,0.1,0,0.3,0,0.4c0-0.1-0.1-0.1-0.1-0.2c0.1,1,0.1,1.7,0,2.7c0,0.4-0.6,0.5,0,0.9c0.4,0.2,0.7-0.3,1-0.4c0.8-0.2,1.4-0.5,2.3-0.7 c1.4-0.5,1.7-0.2,2.1,1.1c0.2-0.3,0.5-0.6,0.8-0.8c-0.1,0.4-0.3,0.7-0.6,1c0.2-0.1,0.3-0.2,0.5-0.4c-0.1,0.2-0.1,0.3-0.2,0.5 c0.5,0.2,0.5,2.1,1.5,0.9c0,0.1-0.1,0.1-0.1,0.2C86.9,71.3,90.8,66.1,89.6,64.4z M85.9,71.3c-0.4-0.2-1,0.2-0.7,0.8 c0.2,0.6,0.9-0.4,1.1-0.9C86.2,71.2,86.1,71.2,85.9,71.3z M24.6,44.3c0-0.2,0.2-0.3-0.1-0.5C24.5,44,24.6,44.2,24.6,44.3z M24.2,44.9c0.1,0.1,0.1,0.2,0.1,0.4c0.1-0.2,0.1-0.3,0-0.6C24.1,44.6,24.3,44.8,24.2,44.9z M24.4,43.9c-0.2,0-0.3-0.1-0.3,0.2 C24.3,44.1,24.4,43.9,24.4,43.9z M36.2,56.9c-1-0.3-1.6-1.5-2.7-1.1c0-0.7-0.8-0.9-1.2-0.6c0-0.4-0.4-0.5-0.5-0.8 c-0.1-0.1-0.1-0.7-0.2-1c-0.3-0.7-0.6-0.8-1.2-1.1c-0.6-0.3-0.6-0.2-1.1-0.8c-0.3-0.4-0.6-0.7-1-1c0.1,0,0.2-0.1,0.3-0.1 c-0.9-0.2-1.7,0.6-2.3-0.6c-0.4,0.3-0.4,0.8-0.6,1.2c0-0.3,0-0.7,0-0.9c0.4-0.6-0.1-0.2-0.5,0.1c-0.3,0.2-0.7,0.9-1,1 c0,0-1.6-0.3-1.6-0.3c-0.8-0.7,0.1-1.4-0.1-2.2c-0.2-0.8-1.1-0.3-1.6-0.5c0.2-0.3,0.3-0.7,0.3-1.1c0.1,0,0.1,0,0.1,0.1 c0.2-1.2,0.5-1.7-0.7-0.8c-0.6,0.5-1.3,1.5-1.8,0c-0.3-0.8,0-2.4,0.4-3.1c0.7-1.1,1.6-0.5,2.3-0.6c-0.1-0.7,1.4-0.4,1.6-0.3 c0.2,0.3,0.2,2.8,0.8,1.6c0.5-1-0.4-1.7,0.5-2.7c0.6-0.7,2-1.1,1.3-2.2c0.1,0,0.1,0,0.2,0.1c0-0.2,0-0.3,0.1-0.4 c0,0.4,0,0.4-0.1,0.8c0.3-0.4,0.3-0.4,0.3-0.9c0.1,0.1,0.1,0.1,0.1,0.2c0.2-0.3,0.4-0.5,0.4-0.7c0,Line truncated
|
||||
<animateTransform attributeName="transform" type="translate" repeatCount="indefinite" dur="2s" keyTimes="0;1" values="-100 0;0 0"></animateTransform>
|
||||
</path>
|
||||
<path transform="scale(2)" fill="#dbfb54" d="M64.4,74.4c0.3,0.3,0.2,0.3-0.3,0.3C64.1,74.3,64.2,74.5,64.4,74.4z M31.4,75.4c-0.2,0.1-0.3-0.2-0.5,0.1c0.3,0.2,0.6,0.1,0.8-0.1C31.6,75.2,31.6,75.4,31.4,75.4z M84.9,36.9c-0.3,0.2-0.8-0.1-1.1-0.5 c0.1,0.2,0.1,0.6,0.3,0.9c-0.5,0.2-0.2,0.6,0.2,0.7c0-0.1,0-0.3,0-0.4c0.2,0.1,0.4,0.2,0.5,0.3c0.2-0.2,0.2-0.3,0.4-0.5 C85.1,37.2,85,37,84.9,36.9z M85.1,40.6c0-0.6-0.1-2.1-0.6-2.4c-0.6-0.4,0.5,2.1-0.6,1.6c0,0.2-0.1,0.5,0,0.7 c-0.8-0.3-0.9,0.5-1.4,0.9c0.3,0.3,0.4,0.6,0.6,0.9c0.3-0.4,0.2-0.9-0.3-1.2c0.2,0,0.4-0.2,0.6-0.2c0.2,0,0.6,0.2,0.6,0.2 C84.2,41,84.5,40.7,85.1,40.6z M83.7,41c-0.4-0.1-0.7,0.2-0.3,0.6c0.2-0.4,0-0.1,0.3-0.2C83.8,41.2,83.6,41.1,83.7,41z M93.5,62.8 c0.1,0.5,0.3,0.9,0.7,0.8C94.1,63.3,93.8,63,93.5,62.8z M93,71.2c-0.1-0.1-2.5,1.6-2.9,2.1c0.8,0.5,2.9-1.2,3.1-2.1 C93,71.5,92.9,71.2,93,71.2z M95,69.9c-0.4,0.2-0.6,0-0.5-0.4c0,0.1-0.1,0.2-0.1,0.3c0.1-0.5,0.1-0.8-0.1-1.1 c-0.3,0.7,0.1,1.4-0.7,2c0.3,0.3,0.3,0.4-0.1,0.7c0.1,0,0.1,0.1,0.1,0.2C93.9,71.3,95,70.2,95,69.9z M82,50.6 c0.2-0.2,0.3-0.2,0.3-0.5c-0.1,0-0.3-0.1-0.4-0.1C81.9,50.2,81.9,50.4,82,50.6z M82.4,50.6c-0.3-0.4-0.2,0.1-0.1,0.5 c0.2-0.3,0.3-0.5,0.2-0.9C82.5,50.4,82.4,50.5,82.4,50.6z M82.8,49.7c-0.1,0-0.2,0-0.3,0c0,0.1,0.2,0.4,0.2,0.5c-0.1,0-0.1,0-0.2,0 c0.1,0.2,0.1,0.3,0.2,0.5c0.1-0.2,0.1-0.2,0-0.5c0.1,0,0.2,0.1,0.2,0.1C83,50.1,82.9,49.9,82.8,49.7z M81,51 c0.2-0.3,0.3-0.4,0.2-0.8c-0.1,0.5-0.4,0.8-0.6,1.2C80.8,51.3,80.9,51.1,81,51z M81.2,48.6c0.1,0.2,0.1,0.5,0.3,0.3 c0,0.4,0.3,0.6,0.6,0.6c0,0,0-0.1,0.1-0.1c0.1,0.2,0.2,0.3,0.4,0.4c0-0.7-0.6-0.5-0.8-1c-0.2-0.4,0.2-0.9,0-1.4 c-0.5-0.1-0.6,0.3-0.4,1c0,0-0.1-0.1-0.1-0.1C81.1,48.3,81.1,48.4,81.2,48.6z M83.3,52.2c0.1-0.5,0.1-1.1-0.3-1.4c0,0.1,0,0.2,0,0.3 c-0.4,0.2-0.9,0.1-0.9,0.8c0.7-0.7,0.4,0.2,1,0.5c0-0.2,0.1-0.5,0.1-0.7C83.2,52,83.3,52.1,83.3,52.2z M81.5,49.6 c0.1-0.1,0.3-0.1-0.2-0.3C81.4,49.4,81.5,49.5,81.5,49.6z M91.8,57.4c0.1,0.1,0,0.2,0.2,0.1c-0.1-0.2-0.2-0.5-0.3-0.7 C91.6,57,91.7,57.1,91.8,57.4z M90.2,57.3c0.4-0.1,1.1-0.5,0.6-0.9c-0.1,0.4-0.2,0.6-0.4,0.3c-0.1,0-0.5,0.2-0.5,0.3 C89.9,57.2,90,57.2,90.2,57.3z M90.9,56.1c0.1,0.2,0.2,0.3,0.2,0.5c0.1-0.5-0.2-0.8-0.6-0.8C90.7,55.9,90.8,56,90.9,56.1z M90.3,59 c-0.2-0.4-0.7-0.7-1-1.6c0.1,0,0.2,0,0.3,0c-0.7-0.7-1.4-1.7-2.5-1.8c-0.5-0.1-1,0.5-1.3,0.3c-0.5-0.2-0.1-1.4-1.2-0.8 c0.3,0.4,0.6,0.5,0.9,0.6c-0.2,0-0.3,0.1-0.5,0.1c0.1,0.2,0.2,0.4,0.3,0.5c0.1,0,0.2-0.2,0.2-0.2c0.6,0.5,2,1,1,2 c0.6-0.3,0.7,0.4,1.2,0.3c0.4-0.1,0.4-0.5,0.6-0.5C89.2,57.9,89.5,58.9,90.3,59z M93.2,58.5c-0.1,0.1-0.2-0.2-0.2,0.1 C93.3,58.8,93.2,58.6,93.2,58.5z M92.4,57.7c0,0-0.1-0.2-0.2-0.3C92.2,57.6,92.3,57.6,92.4,57.7z M92.9,58c0.1,0.1,0.1,0.1,0.2,0.2 c-0.2-0.2-0.3-0.3-0.5-0.4C92.7,57.8,92.8,57.9,92.9,58z M93.5,59.1C93.6,58.9,93.6,58.8,93.5,59.1z M93.5,58.7c0-0.2,0-0.4-0.2-0.6 C93.3,58.3,93.4,58.5,93.5,58.7z M81,45.9c0.1-0.5,0.2-1-0.1-1.3C80.6,45,80.6,45.5,81,45.9z M94.8,61c0,0,0.1-0.2,0-0.4 C94.7,60.8,94.8,61.1,94.8,61z M94.8,61.2c0.1,0.1,0,0.3,0.2,0.1C94.9,61.1,94.8,61.2,94.8,61.2z M29.5,76.3 c-0.4-0.8-0.5-0.2-0.7-0.2c0.2,0-0.3,0.2-0.3,0.2c-0.2,0-0.6-0.3-0.9-0.4c0.9,0.9,1.9,1.2,2.6,0.7C30,76.6,29.8,76.4,29.5,76.3z M89.6,64.4c0-0.2-0.2-0.3-0.1-0.7c-0.2,0-0.1-0.1-0.3,0c0.1-0.8-0.5-1.1-0.7-1.7c-0.1-0.5,0-1-0.2-1.6c-0.1,0.1-0.1,0.1-0.2,0.2 c0-0.5-0.1-1.1-0.2-1.6c-0.5,0.9-0.5,3.8-1.7,2.2c-0.3-0.5-0.4-0.2-0.4-0.8c0-0.3,0.4-0.5,0.4-0.7c0-0.1-1.1-0.4-1.4-0.5 c0.3,0.3-0.7,0.7-0.8,1.5c-0.5-0.2-0.7-0.4-1.1-0.2c-0.4,0.1-0.7,0.9-0.7,1.1c0,0-0.1,0.1-0.1,0.1c0-0.1,0-0.2-0.1-0.3 c-0.4,0.8-0.6,1.1-1.3,1.4c-0.6,0.3-1.1,0.4-1.6,1c0-0.1,0-0.2,0-0.3c-0.3,0.6-0.5,1.1-0.3,1.8c-0.1-0.1-0.1-0.2-0.2-0.3 c0,0.1,0,0.3,0,0.4c0-0.1-0.1-0.1-0.1-0.2c0.1,1,0.1,1.7,0,2.7c0,0.4-0.6,0.5,0,0.9c0.4,0.2,0.7-0.3,1-0.4c0.8-0.2,1.4-0.5,2.3-0.7 c1.4-0.5,1.7-0.2,2.1,1.1c0.2-0.3,0.5-0.6,0.8-0.8c-0.1,0.4-0.3,0.7-0.6,1c0.2-0.1,0.3-0.2,0.5-0.4c-0.1,0.2-0.1,0.3-0.2,0.5 c0.5,0.2,0.5,2.1,1.5,0.9c0,0.1-0.1,0.1-0.1,0.2C86.9,71.3,90.8,66.1,89.6,64.4z M85.9,71.3c-0.4-0.2-1,0.2-0.7,0.8 c0.2,0.6,0.9-0.4,1.1-0.9C86.2,71.2,86.1,71.2,85.9,71.3z M24.6,44.3c0-0.2,0.2-0.3-0.1-0.5C24.5,44,24.6,44.2,24.6,44.3z M24.2,44.9c0.1,0.1,0.1,0.2,0.1,0.4c0.1-0.2,0.1-0.3,0-0.6C24.1,44.6,24.3,44.8,24.2,44.9z M24.4,43.9c-0.2,0-0.3-0.1-0.3,0.2 C24.3,44.1,24.4,43.9,24.4,43.9z M36.2,56.9c-1-0.3-1.6-1.5-2.7-1.1c0-0.7-0.8-0.9-1.2-0.6c0-0.4-0.4-0.5-0.5-0.8 c-0.1-0.1-0.1-0.7-0.2-1c-0.3-0.7-0.6-0.8-1.2-1.1c-0.6-0.3-0.6-0.2-1.1-0.8c-0.3-0.4-0.6-0.7-1-1c0.1,0,0.2-0.1,0.3-0.1 c-0.9-0.2-1.7,0.6-2.3-0.6c-0.4,0.3-0.4,0.8-0.6,1.2c0-0.3,0-0.7,0-0.9c0.4-0.6-0.1-0.2-0.5,0.1c-0.3,0.2-0.7,0.9-1,1 c0,0-1.6-0.3-1.6-0.3c-0.8-0.7,0.1-1.4-0.1-2.2c-0.2-0.8-1.1-0.3-1.6-0.5c0.2-0.3,0.3-0.7,0.3-1.1c0.1,0,0.1,0,0.1,0.1 c0.2-1.2,0.5-1.7-0.7-0.8c-0.6,0.5-1.3,1.5-1.8,0c-0.3-0.8,0-2.4,0.4-3.1c0.7-1.1,1.6-0.5,2.3-0.6c-0.1-0.7,1.4-0.4,1.6-0.3 c0.2,0.3,0.2,2.8,0.8,1.6c0.5-1-0.4-1.7,0.5-2.7c0.6-0.7,2-1.1,1.3-2.2c0.1,0,0.1,0,0.2,0.1c0-0.2,0-0.3,0.1-0.4 c0,0.4,0,0.4-0.1,0.8c0.3-0.4,0.3-0.4,0.3-0.9c0.1,0.1,0.1,0.1,0.1,0.2c0.2-0.3,0.4-0.5,0.4-0.7c0,0,0.4-0.1,0.6-0.1 cLine truncated
|
||||
<animateTransform attributeName="transform" type="translate" repeatCount="indefinite" dur="2s" keyTimes="0;1" values="0 0;100 0"></animateTransform>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |