提交 80b05661 编写于 作者: C coolsnowwolf

add luci-app-kcptun

from kuoruan
上级 a3aa73c3
#
# Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
# Licensed to the public under the Apache License 2.0.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-kcptun
PKG_VERSION:=1.4.3
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Xingwang Liao <kuoruan@gmail.com>
LUCI_TITLE:=LuCI support for Kcptun
LUCI_DEPENDS:=+jshn +wget +luci-lib-jsonc
LUCI_PKGARCH:=all
define Package/$(PKG_NAME)/conffiles
/etc/config/kcptun
endef
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
( . /etc/uci-defaults/40_luci-kcptun ) && rm -f /etc/uci-defaults/40_luci-kcptun
fi
chmod 755 $${IPKG_INSTROOT}/etc/init.d/kcptun >/dev/null 2>&1
$${IPKG_INSTROOT}/etc/init.d/kcptun enable >/dev/null 2>&1
exit 0
endef
# call BuildPackage - OpenWrt buildroot signature
-- Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.kcptun", package.seeall)
local http = require "luci.http"
local kcp = require "luci.model.kcptun"
function index()
if not nixio.fs.access("/etc/config/kcptun") then
return
end
entry({"admin", "services", "kcptun"},
firstchild(), _("Kcptun Client")).dependent = false
entry({"admin", "services", "kcptun", "settings"},
cbi("kcptun/settings"), _("Settings"), 1)
entry({"admin", "services", "kcptun", "servers"},
arcombine(cbi("kcptun/servers"), cbi("kcptun/servers-detail")),
_("Server Manage"), 2).leaf = true
entry({"admin", "services", "kcptun", "log"},
template("kcptun/log_view"), _("Log"), 3)
entry({"admin", "services", "kcptun", "status"}, call("action_status"))
entry({"admin", "services", "kcptun", "check"}, call("action_check")).leaf = true
entry({"admin", "services", "kcptun", "update"}, call("action_update")).leaf = true
entry({"admin", "services", "kcptun", "log", "data"}, call("action_log_data"))
entry({"admin", "services", "kcptun", "log", "clear"}, call("action_log_clear")).leaf = true
end
local function http_write_json(content)
http.prepare_content("application/json")
http.write_json(content or { code = 1 })
end
function action_status()
local client_file = kcp.get_config_option("client_file")
http_write_json({
client = kcp.is_running(client_file)
})
end
function action_check(type)
local json = nil
if type == "kcptun" then
json = kcp.check_kcptun(http.formvalue("arch"))
elseif type == "luci" then
json = kcp.check_luci()
else
http.status(500, "Bad address")
return
end
http_write_json(json)
end
function action_update(type)
local json = nil
if type == "kcptun" then
local task = http.formvalue("task")
if task == "extract" then
json = kcp.extract_kcptun(http.formvalue("file"), http.formvalue("subfix"))
elseif task == "move" then
json = kcp.move_kcptun(http.formvalue("file"))
else
json = kcp.download_kcptun(http.formvalue("url"))
end
elseif type == "luci" then
json = kcp.update_luci(http.formvalue("url"), http.formvalue("save"))
else
http.status(500, "Bad address")
return
end
http_write_json(json)
end
function action_log_data()
local util = require "luci.util"
local log_data = { }
local enable_logging = kcp.get_config_option("enable_logging", "0") == "1"
if enable_logging then
local client_log_file = kcp.get_current_log_file("client")
log_data.client = util.trim(
util.exec("tail -n 50 %s 2>/dev/null | sed 'x;1!H;$!d;x'" % client_log_file))
end
log_data.syslog = util.trim(
util.exec("logread | grep kcptun | tail -n 50 | sed 'x;1!H;$!d;x'"))
http_write_json(log_data)
end
function action_log_clear(type)
if type and type ~= "" then
local log_file = kcp.get_current_log_file(type)
local fs = require "nixio.fs"
if fs.access(log_file) then
fs.writefile(log_file, "")
else
http.status(404, "Not found")
return
end
end
http_write_json({ code = 0 })
end
-- Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local dsp = require "luci.dispatcher"
local m, s, o
local sid = arg[1]
local encrypt_methods = {
"aes",
"aes-128",
"aes-192",
"salsa20",
"blowfish",
"twofish",
"cast5",
"3des",
"tea",
"xtea",
"xor",
"none",
}
local modes = {
"normal",
"fast",
"fast2",
"fast3",
"manual",
}
m = Map("kcptun", "%s - %s" % { translate("Kcptun"), translate("Edit Server") })
m.redirect = dsp.build_url("admin/services/kcptun/servers")
if m.uci:get("kcptun", sid) ~= "servers" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, sid, "servers")
s.anonymous = true
s.addremove = false
o = s:option(Value, "alias", "%s (%s)" % { translate("Alias"), translate("optional") })
o = s:option(Value, "server_addr", translate("Server"))
o.datatype = "host"
o.rmempty = false
o = s:option(Value, "server_port", translate("Server Port"))
o.datatype = "port"
o.placeholder = "29900"
o = s:option(Value, "listen_addr", "%s (%s)" % { translate("Local Listen Host"), translate("optional") },
translate("Local listen host."))
o.datatype = "host"
o.placeholder = "0.0.0.0"
o = s:option(Value, "listen_port", translate("Local Port"), translate("Local Listen Port."))
o.datatype = "port"
o.placeholder = "12948"
o = s:option(Value, "key", "%s (%s)" % { translate("Key"), translate("optional") },
translate("Pre-shared secret for client and server."))
o.password = true
o.placeholder = "it's a secret"
o = s:option(Value, "crypt", translate("crypt"), translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods) do
o:value(v, v:upper())
end
o.default = "aes"
o = s:option(ListValue, "mode", translate("mode"), translate("Embedded Mode"))
for _, v in ipairs(modes) do
o:value(v, v:upper())
end
o.default = "fast"
o = s:option(Flag, "nodelay", translate("nodelay"), translate("Enable nodelay Mode."))
o:depends("mode", "manual")
o = s:option(Value, "interval", translate("interval"))
o:depends("mode", "manual")
o.datatype = "uinteger"
o.placeholder = "50"
o = s:option(ListValue, "resend", translate("resend"))
o:depends("mode", "manual")
o:value("0", translate("Off"))
o:value("1", translate("On"))
o:value("2", translate("2nd ACK"))
o = s:option(Flag, "nc", translate("nc"))
o:depends("mode", "manual")
o = s:option(Value, "mtu", "%s (%s)" % { translate("mtu"), translate("optional") },
translate("Maximum transmission unit of UDP packets."))
o.datatype = "range(64,9200)"
o.placeholder = "1350"
o = s:option(Value, "sndwnd", "%s (%s)" % { translate("sndwnd"), translate("optional") },
translate("Send Window Size(num of packets)."))
o.datatype = "min(1)"
o.default = "128"
o.placeholder = "128"
o = s:option(Value, "rcvwnd", "%s (%s)" % { translate("rcvwnd"), translate("optional") },
translate("Receive Window Size(num of packets)."))
o.datatype = "min(1)"
o.default = "512"
o.placeholder = "512"
o = s:option(Value, "datashard", "%s (%s)" % { translate("datashard"), translate("optional") },
translate("Reed-solomon Erasure Coding - datashard."))
o.datatype = "uinteger"
o.placeholder = "10"
o = s:option(Value, "parityshard", "%s (%s)" % { translate("parityshard"), translate("optional") },
translate("Reed-solomon Erasure Coding - parityshard."))
o.datatype = "uinteger"
o.placeholder = "3"
o = s:option(Value, "dscp", "%s (%s)" % { translate("dscp"), translate("optional") }, translate("DSCP(6bit)"))
o.datatype = "uinteger"
o.placeholder = "0"
o = s:option(Flag, "nocomp", translate("nocomp"), translate("Disable Compression?"))
o.enabled = "true"
o.disabled = "false"
o.rmempty = false
o = s:option(Flag, "acknodelay", translate("acknodelay"))
o.enabled = "true"
o.disabled = "false"
o = s:option(Value, "conn", "%s (%s)" %{ translate("conn"), translate("optional") },
translate("Number of UDP connections to server."))
o.datatype = "min(1)"
o.placeholder = "1"
o = s:option(Value, "autoexpire", "%s (%s)" % { translate("autoexpire"), translate("optional") },
translate("Auto expiration time(in seconds) for a single UDP connection, 0 to disable."))
o.datatype = "uinteger"
o.placeholder = "0"
o = s:option(Value, "scavengettl", "%s (%s)" % { translate("scavengettl"), translate("optional") },
translate("How long an expired connection can live(in sec), -1 to disable."))
o.datatype = "min(-1)"
o.placeholder = "600"
o = s:option(Value, "sockbuf", "%s (%s)" % { translate("sockbuf"), translate("optional") },
translate("Send/secv buffer size of udp sockets, default unit is MB."))
o.datatype = "uinteger"
o.placeholder = "4"
o.cfgvalue = function(...)
local value = Value.cfgvalue(...)
if value then
return tonumber(value) / 1024 / 1024
end
end
o.write = function(self, section, value)
local number = tonumber(value)
if number then
Value.write(self, section, number * 1024 * 1024)
else
Value.remove(self, section)
end
end
o = s:option(Value, "keepalive", "%s (%s)" % { translate("keepalive"), translate("optional") },
translate("NAT keepalive interval to prevent your router from removing port mapping, default unit is seconds."))
o.datatype = "uinteger"
o.placeholder = "10"
o = s:option(Value, "snmpperiod", "%s (%s)" % { translate("snmpperiod"),
translate("optional") }, translate("SNMP collect period, in seconds"))
o.datatype = "min(1)"
o.placeholder = "60"
return m
-- Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local dsp = require "luci.dispatcher"
local http = require "luci.http"
local m, s, o
local function get_ip_string(ip)
if ip and ip:find(":") then
return "[%s]" % ip
else
return ip or ""
end
end
m = Map("kcptun", "%s - %s" % { translate("Kcptun"), translate("Server List") })
s = m:section(TypedSection, "servers")
s.anonymous = true
s.addremove = true
s.sortable = true
s.template = "cbi/tblsection"
s.extedit = dsp.build_url("admin/services/kcptun/servers/%s")
function s.create(...)
local sid = TypedSection.create(...)
if sid then
m.uci:save("kcptun")
http.redirect(s.extedit % sid)
return
end
end
o = s:option(DummyValue, "alias", translate("Alias"))
function o.cfgvalue(self, section)
return Value.cfgvalue(self, section) or translate("None")
end
o = s:option(DummyValue, "_server_address", translate("Server Address"))
function o.cfgvalue(self, section)
local server = m.uci:get("kcptun", section, "server_addr") or "?"
local server_port = m.uci:get("kcptun", section, "server_port") or "29900"
return "%s:%s" % { get_ip_string(server), server_port }
end
o = s:option(DummyValue, "_listen_addres", translate("Listen Address"))
function o.cfgvalue(self, section)
local local_host = m.uci.get("kcptun", section, "listen_addr") or "0.0.0.0"
local local_port = m.uci.get("kcptun", section, "listen_port") or "12984"
return "%s:%s" % { get_ip_string(local_host), local_port }
end
o = s:option(DummyValue, "crypt", translate("Encrypt Method"))
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
return v and v:upper() or "?"
end
o = s:option(DummyValue, "mode", translate("Embedded Mode"))
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
return v and v:upper() or "?"
end
o = s:option(DummyValue, "nocomp", translate("Disable Compression"))
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
return v == "true" and translate("True") or translate("False")
end
return m
-- Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local uci = require "luci.model.uci".cursor()
local util = require "luci.util"
local sys = require "luci.sys"
local fs = require "nixio.fs"
local m, s, o
local server_table = { }
local function get_ip_string(ip)
if ip and ip:find(":") then
return "[%s]" % ip
else
return ip or ""
end
end
uci:foreach("kcptun", "servers", function(s)
if s.alias then
server_table[s[".name"]] = s.alias
elseif s.server_addr and s.server_port then
server_table[s[".name"]] = "%s:%s" % { get_ip_string(s.server_addr), s.server_port }
end
end)
m = Map("kcptun", "%s - %s" % { translate("Kcptun"), translate("Settings") })
m:append(Template("kcptun/status"))
s = m:section(NamedSection, "general", "general", translate("General Settings"))
s.anonymous = true
s.addremove = false
o = s:option(ListValue, "server", translate("Server"))
o:value("", translate("Disable"))
for k, v in pairs(server_table) do
o:value(k, v)
end
o = s:option(Value, "client_file", translate("Client File"))
o.rmempty = false
o = s:option(ListValue, "daemon_user", translate("Run Daemon as User"))
for u in util.execi("cat /etc/passwd | cut -d ':' -f1") do
o:value(u)
end
o = s:option(Flag, "enable_logging", translate("Enable Logging"))
o.rmempty = false
o = s:option(Value, "log_folder", translate("Log Folder"))
o.datatype = "directory"
o.placeholder = "/var/log/kcptun"
o:depends("enable_logging", "1")
o.formvalue = function(...)
local v = (Value.formvalue(...) or ""):trim()
if v ~= "" then
v = string.gsub(v, "\\", "/")
if v:sub(1, 1) ~= "/" then
v = "/" .. v
end
while v:sub(-1) == "/" do
v = v:sub(1, -2)
end
end
return v
end
o.validate = function(self, value, section)
if value and not fs.stat(value) then
local res, code, msg = fs.mkdir(value)
if not res then
return nil, msg
end
end
return Value.validate(self, value, section)
end
o = s:option(ListValue, "arch", translate("CPU Architecture"),
translate("The ARCH for checking updates." ..
" Note: Make sure OpenWrt/LEDE 'MIPS FPU Emulator' is enabled on MIPS/MIPSLE devices."))
o:value("", translate("Auto"))
o:value("i386", "x86")
o:value("x86_64", "x86_64")
o:value("armv5", "ARMv5")
o:value("armv6", "ARMv6")
o:value("armv7", "ARMv7+")
o:value("ar71xx", "MIPS")
o:value("ramips", "MIPSLE")
o = s:option(Button, "_check_kcptun", translate("Check Kcptun Update"),
translate("Make sure that the 'Client File' dictionary has enough space."))
o.template = "kcptun/button"
o.inputstyle = "apply"
o.placeholder = translate("Check Kcptun Update")
o.btnclick = "check_update('kcptun', this);"
o.id = "_kcptun-check_kcptun"
o = s:option(Flag, "save_config", translate("Save Config File"),
translate("Save config file while upgrade LuCI."))
o = s:option(Button, "_check_luci", translate("Check LuCI Update"),
translate("You may need to reload current page after update LuCI. Note that translation will not be updated."))
o.template = "kcptun/button"
o.inputstyle = "apply"
o.placeholder = translate("Check LuCI Update")
o.btnclick = "check_update('luci', this);"
o.id = "_kcptun-check_luci"
return m
-- Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local fs = require "nixio.fs"
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local util = require "luci.util"
local i18n = require "luci.i18n"
module("luci.model.kcptun", package.seeall)
local kcptun_api = "https://api.github.com/repos/xtaci/kcptun/releases/latest"
local luci_api = "https://api.github.com/repos/kuoruan/luci-app-kcptun/releases/latest"
local wget = "/usr/bin/wget"
local wget_args = { "--no-check-certificate", "--quiet", "--timeout=10", "--tries=2" }
local command_timeout = 40
local function _unpack(t, i)
i = i or 1
if t[i] ~= nil then
return t[i], _unpack(t, i + 1)
end
end
local function exec(cmd, args, writer, timeout)
local os = require "os"
local nixio = require "nixio"
local fdi, fdo = nixio.pipe()
local pid = nixio.fork()
if pid > 0 then
fdo:close()
if writer or timeout then
local starttime = os.time()
while true do
if timeout and os.difftime(os.time(), starttime) >= timeout then
nixio.kill(pid, nixio.const.SIGTERM)
return 1
end
if writer then
local buffer = fdi:read(2048)
if buffer and #buffer > 0 then
writer(buffer)
end
end
local wpid, stat, code = nixio.waitpid(pid, "nohang")
if wpid and stat == "exited" then
return code
end
if not writer and timeout then
nixio.nanosleep(1)
end
end
else
local wpid, stat, code = nixio.waitpid(pid)
return wpid and stat == "exited" and code
end
elseif pid == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exece(cmd, args, nil)
nixio.stdout:close()
os.exit(1)
end
end
local function compare_versions(ver1, comp, ver2)
local table = table
local av1 = util.split(ver1, "[%.%-]", nil, true)
local av2 = util.split(ver2, "[%.%-]", nil, true)
local max = table.getn(av1)
local n2 = table.getn(av2)
if (max < n2) then
max = n2
end
for i = 1, max, 1 do
local s1 = av1[i] or ""
local s2 = av2[i] or ""
if comp == "~=" and (s1 ~= s2) then return true end
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
if (s1 ~= s2) then return false end
end
return not (comp == "<" or comp == ">")
end
local function auto_get_arch()
local arch = nixio.uname().machine or ""
if arch == "mips" then
if fs.access("/usr/lib/os-release") then
arch = sys.exec("grep 'LEDE_BOARD' /usr/lib/os-release | grep -oE 'ramips|ar71xx'")
elseif fs.access("/etc/openwrt_release") then
arch = sys.exec("grep 'DISTRIB_TARGET' /etc/openwrt_release | grep -oE 'ramips|ar71xx'")
end
end
return util.trim(arch)
end
local function get_file_info(arch)
local file_tree = ""
local sub_version = ""
if arch == "x86_64" then
file_tree = "amd64"
elseif arch == "ramips" then
file_tree = "mipsle"
elseif arch == "ar71xx" then
file_tree = "mips"
elseif arch:match("^i[%d]86$") then
file_tree = "386"
elseif arch:match("^armv[5-8]") then
file_tree = "arm"
sub_version = arch:match("[5-8]")
end
return file_tree, sub_version
end
local function get_api_json(url)
local jsonc = require "luci.jsonc"
local output = { }
exec(wget, { "-O-", url, _unpack(wget_args) },
function(chunk) output[#output + 1] = chunk end)
local json_content = util.trim(table.concat(output))
if json_content == "" then
return { }
end
return jsonc.parse(json_content) or { }
end
function get_config_option(option, default)
return uci:get("kcptun", "general", option) or default
end
function get_current_log_file(type)
local log_folder = get_config_option("log_folder", "/var/log/kcptun")
return "%s/%s.%s.log" % { log_folder, type, "general" }
end
function is_running(client)
if client and client ~= "" then
local file_name = client:match(".*/([^/]+)$") or ""
if file_name ~= "" then
return sys.call("pidof %s >/dev/null" % file_name) == 0
end
end
return false
end
function get_kcptun_version(file)
if file and file ~= "" then
if not fs.access(file, "rwx", "rx", "rx") then
fs.chmod(file, 755)
end
local info = util.trim(sys.exec("%s -v 2>/dev/null" % file))
if info ~= "" then
local tb = util.split(info, "%s+", nil, true)
return tb[1] == "kcptun" and tb[3] or ""
end
end
return ""
end
function get_luci_version()
local ipkg = require "luci.model.ipkg"
local package_name = "luci-app-kcptun"
local package_info = ipkg.info(package_name) or {}
if next(package_info) ~= nil then
return package_info[package_name]["Version"]
end
return ""
end
function check_kcptun(arch)
if not arch or arch == "" then
arch = auto_get_arch()
end
local file_tree, sub_version = get_file_info(arch)
if file_tree == "" then
return {
code = 1,
error = i18n.translate("Can't determine ARCH, or ARCH not supported. Please select manually.")
}
end
local json = get_api_json(kcptun_api)
if json.tag_name == nil then
return {
code = 1,
error = i18n.translate("Get remote version info failed.")
}
end
local remote_version = json.tag_name:match("[^v]+")
local client_file = get_config_option("client_file")
local needs_update = compare_versions(get_kcptun_version(client_file), "<", remote_version)
local html_url, download_url
if needs_update then
html_url = json.html_url
for _, v in ipairs(json.assets) do
if v.name and v.name:match("linux%-" .. file_tree) then
download_url = v.browser_download_url
break
end
end
end
if needs_update and not download_url then
return {
code = 1,
version = remote_version,
html_url = html_url,
error = i18n.translate("New version found, but failed to get new version download url.")
}
end
return {
code = 0,
update = needs_update,
version = remote_version,
url = {
html = html_url,
download = download_url
},
type = file_tree .. sub_version
}
end
function check_luci()
local json = get_api_json(luci_api)
if json.tag_name == nil then
return {
code = 1,
error = i18n.translate("Get remote version info failed.")
}
end
local remote_version = json.tag_name:match("[^v]+")
local needs_update = compare_versions(get_luci_version(), "<", remote_version)
local html_url, luci_url
local i18n_urls = { }
if needs_update then
html_url = json.html_url
for _, v in ipairs(json.assets) do
local n = v.name
if n then
if n:match("luci%-app%-kcptun") then
luci_url = v.browser_download_url
elseif n:match("luci%-i18n%-kcptun") then
i18n_urls[#i18n_urls + 1] = v.browser_download_url
end
end
end
end
if needs_update and not luci_url then
return {
code = 1,
version = remote_version,
html_url = html_url,
error = i18n.translate("New version found, but failed to get new version download url.")
}
end
return {
code = 0,
update = needs_update,
version = remote_version,
url = {
html = html_url,
luci = luci_url,
i18n = i18n_urls
}
}
end
function download_kcptun(url)
if not url or url == "" then
return {
code = 1,
error = i18n.translate("Download url is required.")
}
end
sys.call("/bin/rm -f /tmp/kcptun_download.*")
local tmp_file = util.trim(util.exec("mktemp -u -t kcptun_download.XXXXXX"))
local result = exec(wget, {
"-O", tmp_file, url, _unpack(wget_args) }, nil, command_timeout) == 0
if not result then
exec("/bin/rm", { "-f", tmp_file })
return {
code = 1,
error = i18n.translatef("File download failed or timed out: %s", url)
}
end
return {
code = 0,
file = tmp_file
}
end
function extract_kcptun(file, subfix)
if not file or file == "" or not fs.access(file) then
return {
code = 1,
error = i18n.translate("File path required.")
}
end
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
local tmp_dir = util.trim(util.exec("mktemp -d -t kcptun_extract.XXXXXX"))
local output = { }
exec("/bin/tar", { "-C", tmp_dir, "-zxvf", file },
function(chunk) output[#output + 1] = chunk end)
local files = util.split(table.concat(output))
exec("/bin/rm", { "-f", file })
local new_file = nil
for _, f in pairs(files) do
if f:match("client_linux_%s" % subfix) then
new_file = tmp_dir .. "/" .. util.trim(f)
break
end
end
if not new_file then
for _, f in pairs(files) do
if f:match("client_") then
new_file = tmp_dir .. "/" .. util.trim(f)
break
end
end
end
if not new_file then
exec("/bin/rm", { "-rf", tmp_dir })
return {
code = 1,
error = i18n.translatef("Can't find client in file: %s", file)
}
end
return {
code = 0,
file = new_file
}
end
function move_kcptun(file)
if not file or file == "" or not fs.access(file) then
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
return {
code = 1,
error = i18n.translate("Client file is required.")
}
end
local version = get_kcptun_version(file)
if version == "" then
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
return {
code = 1,
error = i18n.translate("The client file is not suitable for current device. Please reselect ARCH.")
}
end
local client_file = get_config_option("client_file", "/var/kcptun_client")
local client_file_bak
if fs.access(client_file) then
client_file_bak = client_file .. ".bak"
exec("/bin/mv", { "-f", client_file, client_file_bak })
end
local result = exec("/bin/mv", { "-f", file, client_file }, nil, command_timeout) == 0
if not result or not fs.access(client_file) then
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
if client_file_bak then
exec("/bin/mv", { "-f", client_file_bak, client_file })
end
return {
code = 1,
error = i18n.translatef("Can't move new file to path: %s", client_file)
}
end
exec("/bin/chmod", { "755", client_file })
if client_file_bak then
exec("/bin/rm", { "-f", client_file_bak })
end
sys.call("/bin/rm -rf /tmp/kcptun_extract.*")
uci:set("kcptun", "general", "client_file", client_file)
uci:commit("kcptun")
return { code = 0 }
end
function update_luci(url, save)
if not url or url == "" then
return {
code = 1,
error = i18n.translate("Download url is required.")
}
end
sys.call("/bin/rm -f /tmp/luci_kcptun.*.ipk")
local tmp_file = util.trim(util.exec("mktemp -u -t luci_kcptun.XXXXXX")) .. ".ipk"
local result = exec("/usr/bin/wget", {
"-O", tmp_file, url, _unpack(wget_args) }, nil, command_timeout) == 0
if not result then
exec("/bin/rm", { "-f", tmp_file })
return {
code = 1,
error = i18n.translatef("File download failed or timed out: %s", url)
}
end
local opkg_args = { "--force-downgrade", "--force-reinstall" }
if save ~= "true" then
opkg_args[#opkg_args + 1] = "--force-maintainer"
end
result = exec("/bin/opkg", { "install", tmp_file, _unpack(opkg_args) }) == 0
if not result then
exec("/bin/rm", { "-f", tmp_file })
return {
code = 1,
error = i18n.translate("Package update failed.")
}
end
exec("/bin/rm", { "-f", tmp_file })
exec("/bin/rm", { "-rf", "/tmp/luci-indexcache", "/tmp/luci-modulecache" })
return { code = 0 }
end
<%#
Copyright 2017 Hsing-wang Liao <kuoruan@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<%+cbi/valueheader%>
<% if self:cfgvalue(section) ~= false then %>
<input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" type="button"<%=
attr("name", cbid) ..
attr("id", self.id or cbid) ..
attr("value", self.inputtitle or self.title) ..
ifattr(self.btnclick, "onclick", self.btnclick) ..
ifattr(self.placeholder, "placeholder")
%> />
<span id="<%=self.id or cbid%>-detail"></span>
<% else %>
-
<% end %>
<%+cbi/valuefooter%>
<%#
Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<% css = [[
#log_text {
padding: 10px;
text-align: left;
}
#log_text pre {
word-break: break-all;
margin: 0;
}
.description {
background-color: #33ccff;
}
]]
%>
<%+header%>
<div class="cbi-map">
<h2 name="content"><%:Kcptun%> - <%:Log Data%></h2>
<fieldset class="cbi-section">
<div class="cbi-section-descr">
<input type="button" class="cbi-button" value="<%:Clear Log File%>" placeholder="<%:Clear Log File%>" onclick="return log_clear('client', this)" />
</div>
<fieldset class="cbi-section-node">
<div id="log_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
<div style="text-align:right"><small><%:Refresh every 5 seconds.%></small></div>
</fieldset>
</fieldset>
</div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
function log_clear(type, btn) {
btn.disabled = true;
btn.value = '<%:Processing...%>';
(new XHR()).get('<%=luci.dispatcher.build_url("admin/services/kcptun/log/clear")%>/' + type,
{ token: '<%=token%>' },
function(x, json) {
btn.disabled = false;
btn.value = btn.placeholder;
}
);
return false;
}
(function(doc) {
var log_elm = doc.getElementById('log_text');
XHR.poll(5, '<%=luci.dispatcher.build_url("admin/services/kcptun/log/data")%>', null,
function(x, data) {
if (log_elm && data) {
log_elm.innerHTML = String.format(
'<pre>%s%s%s%s</pre>',
'<span class="description"><%:Last 50 lines of log file:%></span><br/><br/>',
data.client || '<%:No log data.%>',
'<br/><br/><span class="description"><%:Last 50 lines of syslog:%></span><br/><br/>',
data.syslog || '<%:No log data.%>'
);
} else if (log_elm) {
log_elm.innerHTML = '<strong><%:Error get log data.%></strong>';
}
}
);
}(document));
//]]></script>
<%+footer%>
<%#
Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<%
local kcp = require "luci.model.kcptun"
local dsp = require "luci.dispatcher"
local client_file = kcp.get_config_option("client_file")
local client_version = kcp.get_kcptun_version(client_file)
local luci_version = kcp.get_luci_version()
-%>
<fieldset class="cbi-section">
<legend><%:Running Status%></legend>
<table width="100%" cellspacing="10" id="_kcptun-status_table">
<tr>
<td width="33%"><%:Client Version%></td>
<td>
<% if client_version == "" then -%>
<em><%:Invalid Client File.%></em>
<% else -%>
<%=pcdata(client_version)%>
<%- end %>
</td>
</tr>
<tr><td width="33%"><%:Client Status%></td><td id="_kcptun-client_status"><em><%:Collecting data...%></em></td></tr>
<% if luci_version ~= "" then -%>
<tr><td width="33%"><%:LuCI Version%></td><td><%=pcdata(luci_version)%></td></tr>
<% end -%>
</table>
</fieldset>
<script type="text/javascript">//<![CDATA[
var client_status = document.getElementById('_kcptun-client_status');
XHR.poll(5, '<%=dsp.build_url("admin/services/kcptun/status")%>', null,
function(x, json) {
if (x && x.status == 200) {
client_status.innerHTML = json.client ? '<%:Running%>' : '<%:Not Running%>';
}
});
function add_remove_page_notice(isAdd) {
if (isAdd) {
window.onbeforeunload = function(e) {
var dialogText = '<%:Update in progress. Are you sure to close window?%>';
e.returnValue = dialogText;
return dialogText;
};
} else {
window.onbeforeunload = null;
}
}
function on_update_success(btn) {
add_remove_page_notice(false);
var success = '<%:Update Success.%>';
if (btn) {
btn.value = success;
btn.placeholder = success;
btn.disabled = true;
}
alert(success);
window.setTimeout(function () {
window.location.reload();
}, 500);
}
function on_request_error(btn, json) {
add_remove_page_notice(false);
if (btn) {
btn.disabled = false;
btn.value = btn.placeholder;
}
if (json && json.error) {
alert(json.error);
}
}
function createVersionUrl(version, url) {
var urlNode = '';
if (url) {
urlNode += '<a href="' + url + '" target="_blank">';
}
urlNode += '<em style="color:red;">' + version + '</em>';
if (url) {
urlNode += '</a>';
}
return urlNode;
}
var kcptun_info, luci_info;
var token_str = '<%=token%>';
var arch_select;
function init_arch_select() {
if (!arch_select) {
arch_select = document.getElementById('cbid.kcptun.general.arch');
arch_select.addEventListener('change', function() {
kcptun_info = null;
var check_kcptun_btn = document.getElementById('_kcptun-check_kcptun');
var text = '<%:Check Kcptun Update%>';
check_kcptun_btn.value = text;
check_kcptun_btn.placeholder = text;
check_kcptun_btn.setAttribute('onclick', 'check_update(\'kcptun\', this);');
var detail = document.getElementById('_kcptun-check_kcptun-detail');
detail.innerHTML = '';
});
}
}
function check_update(type, btn) {
btn.disabled = true;
btn.value = '<%:Processing...%>';
init_arch_select();
add_remove_page_notice(true);
(new XHR()).get('<%=dsp.build_url("admin/services/kcptun/check")%>/' + type, {
token: token_str,
arch: arch_select ? arch_select.value : ""
}, function(x, json) {
if (x && x.status == 200) {
var detailElm = document.getElementById(btn.id + '-detail');
if (json.code == 0) {
add_remove_page_notice(false);
if (json.update) {
eval(type + '_info = json');
btn.disabled = false;
btn.value = '<%:Click to Update%>';
btn.placeholder = '<%:Click to Update%>';
btn.setAttribute('onclick', 'do_' + type + '_update(this);');
if (detailElm) {
detailElm.innerHTML = createVersionUrl(json.version, json.url.html);
}
} else {
btn.disabled = true;
btn.value = '<%:No Update Found%>';
if (detailElm) {
detailElm.innerHTML = '';
}
}
} else {
if (detailElm && json.html_url) {
detailElm.innerHTML = createVersionUrl(json.version, json.html_url)
}
on_request_error(btn, json);
}
} else {
on_request_error(btn);
}
});
}
function do_kcptun_update(btn) {
btn.disabled = true;
btn.value = '<%:Downloading...%>';
add_remove_page_notice(true);
var kcptun_update_url = '<%=dsp.build_url("admin/services/kcptun/update/kcptun")%>';
(new XHR()).get(kcptun_update_url, {
token: token_str,
url: kcptun_info ? kcptun_info.url.download : ''
}, function (x, json) {
if (x && x.status == 200) {
if (json.code == 0) {
btn.value = '<%:Extracting...%>';
(new XHR()).get(kcptun_update_url, {
token: token_str,
task: "extract",
file: json.file,
subfix: kcptun_info ? kcptun_info.type : ''
}, function (x, json) {
if (x && x.status == 200) {
if (json.code == 0) {
btn.value = '<%:Moving...%>';
(new XHR()).get(kcptun_update_url, {
token: token_str,
task: "move",
file: json.file
}, function (x, json) {
if (x && x.status == 200) {
if (json.code == 0) {
on_update_success(btn);
} else {
on_request_error(btn, json);
}
} else {
on_request_error(btn);
}
});
} else {
on_request_error(btn, json);
}
} else {
on_request_error(btn);
}
});
} else {
on_request_error(btn, json);
}
} else {
on_request_error(btn);
}
});
}
function do_luci_update(btn) {
btn.disabled = true;
btn.value = '<%:Processing...%>';
add_remove_page_notice(true);
var save_config = document.getElementById('cbid.kcptun.general.save_config');
(new XHR()).get('<%=dsp.build_url("admin/services/kcptun/update/luci")%>', {
token: token_str,
url: luci_info ? luci_info.url.luci : '',
save: save_config ? save_config.checked : false
}, function(x, json) {
btn.disabled = false;
btn.value = btn.placeholder;
if (x && x.status == 200) {
if (json.code == 0) {
on_update_success(btn);
} else {
on_request_error(btn, json);
}
} else {
on_request_error(btn);
}
});
}
//]]></script>
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "2nd ACK"
msgstr ""
msgid "Alias"
msgstr ""
msgid "Auto"
msgstr ""
msgid ""
"Auto expiration time(in seconds) for a single UDP connection, 0 to disable."
msgstr ""
msgid "CPU Architecture"
msgstr ""
msgid "Can't determine ARCH, or ARCH not supported. Please select manually."
msgstr ""
msgid "Can't find client in file: %s"
msgstr ""
msgid "Can't move new file to path: %s"
msgstr ""
msgid "Check Kcptun Update"
msgstr ""
msgid "Check LuCI Update"
msgstr ""
msgid "Clear Log File"
msgstr ""
msgid "Click to Update"
msgstr ""
msgid "Client File"
msgstr ""
msgid "Client Status"
msgstr ""
msgid "Client Version"
msgstr ""
msgid "Client file is required."
msgstr ""
msgid "Collecting data..."
msgstr ""
msgid "DSCP(6bit)"
msgstr ""
msgid "Disable"
msgstr ""
msgid "Disable Compression"
msgstr ""
msgid "Disable Compression?"
msgstr ""
msgid "Download url is required."
msgstr ""
msgid "Downloading..."
msgstr ""
msgid "Edit Server"
msgstr ""
msgid "Embedded Mode"
msgstr ""
msgid "Enable Logging"
msgstr ""
msgid "Enable nodelay Mode."
msgstr ""
msgid "Encrypt Method"
msgstr ""
msgid "Error get log data."
msgstr ""
msgid "Extracting..."
msgstr ""
msgid "False"
msgstr ""
msgid "File download failed or timed out: %s"
msgstr ""
msgid "File path required."
msgstr ""
msgid "General Settings"
msgstr ""
msgid "Get remote version info failed."
msgstr ""
msgid "How long an expired connection can live(in sec), -1 to disable."
msgstr ""
msgid "Invalid Client File."
msgstr ""
msgid "Kcptun"
msgstr ""
msgid "Kcptun Client"
msgstr ""
msgid "Key"
msgstr ""
msgid "Last 50 lines of log file:"
msgstr ""
msgid "Last 50 lines of syslog:"
msgstr ""
msgid "Listen Address"
msgstr ""
msgid "Loading"
msgstr ""
msgid "Local Listen Host"
msgstr ""
msgid "Local Listen Port."
msgstr ""
msgid "Local Port"
msgstr ""
msgid "Local listen host."
msgstr ""
msgid "Log"
msgstr ""
msgid "Log Data"
msgstr ""
msgid "Log Folder"
msgstr ""
msgid "LuCI Version"
msgstr ""
msgid "Make sure that the 'Client File' dictionary has enough space."
msgstr ""
msgid "Maximum transmission unit of UDP packets."
msgstr ""
msgid "Moving..."
msgstr ""
msgid ""
"NAT keepalive interval to prevent your router from removing port mapping, "
"default unit is seconds."
msgstr ""
msgid "New version found, but failed to get new version download url."
msgstr ""
msgid "No Update Found"
msgstr ""
msgid "No log data."
msgstr ""
msgid "None"
msgstr ""
msgid "Not Running"
msgstr ""
msgid "Number of UDP connections to server."
msgstr ""
msgid "Off"
msgstr ""
msgid "On"
msgstr ""
msgid "Package update failed."
msgstr ""
msgid "Pre-shared secret for client and server."
msgstr ""
msgid "Processing..."
msgstr ""
msgid "Receive Window Size(num of packets)."
msgstr ""
msgid "Reed-solomon Erasure Coding - datashard."
msgstr ""
msgid "Reed-solomon Erasure Coding - parityshard."
msgstr ""
msgid "Refresh every 5 seconds."
msgstr ""
msgid "Run Daemon as User"
msgstr ""
msgid "Running"
msgstr ""
msgid "Running Status"
msgstr ""
msgid "SNMP collect period, in seconds"
msgstr ""
msgid "Save Config File"
msgstr ""
msgid "Save config file while upgrade LuCI."
msgstr ""
msgid "Send Window Size(num of packets)."
msgstr ""
msgid "Send/secv buffer size of udp sockets, default unit is MB."
msgstr ""
msgid "Server"
msgstr ""
msgid "Server Address"
msgstr ""
msgid "Server List"
msgstr ""
msgid "Server Manage"
msgstr ""
msgid "Server Port"
msgstr ""
msgid "Settings"
msgstr ""
msgid ""
"The ARCH for checking updates. Note: Make sure OpenWrt/LEDE 'MIPS FPU "
"Emulator' is enabled on MIPS/MIPSLE devices."
msgstr ""
msgid ""
"The client file is not suitable for current device. Please reselect ARCH."
msgstr ""
msgid "True"
msgstr ""
msgid "Update Success."
msgstr ""
msgid "Update in progress. Are you sure to close window?"
msgstr ""
msgid ""
"You may need to reload current page after update LuCI. Note that translation "
"will not be updated."
msgstr ""
msgid "acknodelay"
msgstr ""
msgid "autoexpire"
msgstr ""
msgid "conn"
msgstr ""
msgid "crypt"
msgstr ""
msgid "datashard"
msgstr ""
msgid "dscp"
msgstr ""
msgid "interval"
msgstr ""
msgid "keepalive"
msgstr ""
msgid "mode"
msgstr ""
msgid "mtu"
msgstr ""
msgid "nc"
msgstr ""
msgid "nocomp"
msgstr ""
msgid "nodelay"
msgstr ""
msgid "optional"
msgstr ""
msgid "parityshard"
msgstr ""
msgid "rcvwnd"
msgstr ""
msgid "resend"
msgstr ""
msgid "scavengettl"
msgstr ""
msgid "sndwnd"
msgstr ""
msgid "snmpperiod"
msgstr ""
msgid "sockbuf"
msgstr ""
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "2nd ACK"
msgstr "2次 ACK 跨越重传"
msgid "Alias"
msgstr "别名"
msgid "Auto"
msgstr "自动"
msgid ""
"Auto expiration time(in seconds) for a single UDP connection, 0 to disable."
msgstr "单个 UDP 连接的自动过期时间(秒),设置 0 来禁用"
msgid "CPU Architecture"
msgstr "CPU 架构"
msgid "Can't determine ARCH, or ARCH not supported. Please select manually."
msgstr "无法自动确定 ARCH,或者不支持该 ARCH,请手动重新选择。"
msgid "Can't find client in file: %s"
msgstr "无法在文件中找到客户端:%s"
msgid "Can't move new file to path: %s"
msgstr "无法移动新文件到:%s"
msgid "Check Kcptun Update"
msgstr "检查 Kcptun 更新"
msgid "Check LuCI Update"
msgstr "检查 LuCI 更新"
msgid "Clear Log File"
msgstr "清理日志文件"
msgid "Click to Update"
msgstr "点击更新"
msgid "Client File"
msgstr "客户端文件"
msgid "Client Status"
msgstr "客户端状态"
msgid "Client Version"
msgstr "客户端版本"
msgid "Client file is required."
msgstr "请指定客户端文件。"
msgid "Collecting data..."
msgstr "正在收集数据..."
msgid "DSCP(6bit)"
msgstr "DSCP(6bit)"
msgid "Disable"
msgstr "禁用"
msgid "Disable Compression"
msgstr "禁用压缩"
msgid "Disable Compression?"
msgstr "是否禁用压缩?"
msgid "Download url is required."
msgstr "请指定下载链接。"
msgid "Downloading..."
msgstr "正在下载..."
msgid "Edit Server"
msgstr "编辑服务端"
msgid "Embedded Mode"
msgstr "内置模式"
msgid "Enable Logging"
msgstr "启用日志记录"
msgid "Enable nodelay Mode."
msgstr "启用 nodelay 模式"
msgid "Encrypt Method"
msgstr "加密方式"
msgid "Error get log data."
msgstr "获取日志数据失败。"
msgid "Extracting..."
msgstr "正在解压..."
msgid "False"
msgstr "否"
msgid "File download failed or timed out: %s"
msgstr "文件下载失败或超时:%s"
msgid "File path required."
msgstr "请指定文件路径。"
msgid "General Settings"
msgstr "基本设置"
msgid "Get remote version info failed."
msgstr "获取远程版本信息失败。"
msgid "How long an expired connection can live(in sec), -1 to disable."
msgstr "过期连接保留多长时间(秒),设置 -1 来禁用"
msgid "Invalid Client File."
msgstr "客户端文件配置有误"
msgid "Kcptun"
msgstr ""
msgid "Kcptun Client"
msgstr "Kcptun 客户端"
msgid "Key"
msgstr "密码"
msgid "Last 50 lines of log file:"
msgstr "日志文件的最新 50 行:"
msgid "Last 50 lines of syslog:"
msgstr "系统日志的最新 50 行:"
msgid "Listen Address"
msgstr "监听地址"
msgid "Loading"
msgstr "正在加载..."
msgid "Local Listen Host"
msgstr "本地监听地址"
msgid "Local Listen Port."
msgstr "本地监听端口"
msgid "Local Port"
msgstr "本地端口"
msgid "Local listen host."
msgstr "本地监听主机"
msgid "Log"
msgstr "日志"
msgid "Log Data"
msgstr "日志数据"
msgid "Log Folder"
msgstr "日志文件夹"
msgid "LuCI Version"
msgstr "LuCI 版本"
msgid "Make sure that the 'Client File' dictionary has enough space."
msgstr "请确保“客户端文件”所在的文件夹具有足够的空间。"
msgid "Maximum transmission unit of UDP packets."
msgstr "UDP数据包的最大传输单元"
msgid "Moving..."
msgstr "正在移动..."
msgid ""
"NAT keepalive interval to prevent your router from removing port mapping, "
"default unit is seconds."
msgstr "NAT Keepalive 包间隔时间(秒), 防止路由器删除端口映射"
msgid "New version found, but failed to get new version download url."
msgstr "发现新版本,但是获取下载地址失败。"
msgid "No Update Found"
msgstr "未发现更新"
msgid "No log data."
msgstr "无日志数据。"
msgid "None"
msgstr "无"
msgid "Not Running"
msgstr "未运行"
msgid "Number of UDP connections to server."
msgstr "到服务端的UDP连接数量"
msgid "Off"
msgstr "关闭"
msgid "On"
msgstr "开启"
msgid "Package update failed."
msgstr "软件包升级失败。"
msgid "Pre-shared secret for client and server."
msgstr "客户端和服务端的通信密码"
msgid "Processing..."
msgstr "正在操作..."
msgid "Receive Window Size(num of packets)."
msgstr "接收窗口大小 (数据包数量)"
msgid "Reed-solomon Erasure Coding - datashard."
msgstr "前向纠错 - datashard"
msgid "Reed-solomon Erasure Coding - parityshard."
msgstr "前向纠错 - parityshard"
msgid "Refresh every 5 seconds."
msgstr "每 5 秒刷新。"
msgid "Run Daemon as User"
msgstr "以该用户启动"
msgid "Running"
msgstr "运行中"
msgid "Running Status"
msgstr "运行状态"
msgid "SNMP collect period, in seconds"
msgstr "SNMP采集周期(秒)"
msgid "Save Config File"
msgstr "保留配置文件"
msgid "Save config file while upgrade LuCI."
msgstr "在更新 LuCI 时保留配置文件"
msgid "Send Window Size(num of packets)."
msgstr "发送窗口大小 (数据包数量)"
msgid "Send/secv buffer size of udp sockets, default unit is MB."
msgstr "发送/接收UDP数据包的缓冲区大小(MB)"
msgid "Server"
msgstr "服务端地址"
msgid "Server Address"
msgstr "服务端地址"
msgid "Server List"
msgstr "服务端列表"
msgid "Server Manage"
msgstr "服务端管理"
msgid "Server Port"
msgstr "服务端端口"
msgid "Settings"
msgstr "设置"
msgid ""
"The ARCH for checking updates. Note: Make sure OpenWrt/LEDE 'MIPS FPU "
"Emulator' is enabled on MIPS/MIPSLE devices."
msgstr ""
"用于检查更新的 ARCH。注意: 在 MIPS/MIPSLE 设备上,请确保 OpenWrt/LEDE 的 "
"'MIPS FPU Emulator' 已经启用。"
msgid ""
"The client file is not suitable for current device. Please reselect ARCH."
msgstr "客户端文件不适用于当前设备,请重新选择 ARCH。"
msgid "True"
msgstr "是"
msgid "Update Success."
msgstr "更新成功!"
msgid "Update in progress. Are you sure to close window?"
msgstr "正在更新,确定关闭窗口?"
msgid ""
"You may need to reload current page after update LuCI. Note that translation "
"will not be updated."
msgstr "更新 LuCI 之后你可能需要手动刷新当前页面。注意:翻译不会被更新"
msgid "acknodelay"
msgstr ""
msgid "autoexpire"
msgstr ""
msgid "conn"
msgstr ""
msgid "crypt"
msgstr ""
msgid "datashard"
msgstr ""
msgid "dscp"
msgstr ""
msgid "interval"
msgstr ""
msgid "keepalive"
msgstr ""
msgid "mode"
msgstr ""
msgid "mtu"
msgstr ""
msgid "nc"
msgstr ""
msgid "nocomp"
msgstr ""
msgid "nodelay"
msgstr ""
msgid "optional"
msgstr "可选"
msgid "parityshard"
msgstr ""
msgid "rcvwnd"
msgstr ""
msgid "resend"
msgstr ""
msgid "scavengettl"
msgstr ""
msgid "sndwnd"
msgstr ""
msgid "snmpperiod"
msgstr ""
msgid "sockbuf"
msgstr ""
config general 'general'
option server ''
option client_file '/var/kcptun_client'
option daemon_user 'root'
option enable_logging '1'
config servers 'default'
option server_addr ''
option server_port '29900'
option listen_addr '0.0.0.0'
option listen_port '12948'
option crypt 'aes'
option mode 'fast'
option nocomp 'false'
#!/bin/sh /etc/rc.common
#
# Copyright 2016-2017 Xingwang Liao <kuoruan@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
START=99
USE_PROCD=1
KCPTUN=kcptun
CONFIG_FOLDER=/var/etc/$KCPTUN
if [ -r /usr/share/libubox/jshn.sh ]; then
. /usr/share/libubox/jshn.sh
elif [ -r /lib/functions/jshn.sh ]; then
. /lib/functions/jshn.sh
else
logger -p daemon.err -t "$KCPTUN" \
"Package required: jshn."
exit 1
fi
_log() {
local level="$1"
local msg="$2"
logger -p "daemon.${level}" -t "$KCPTUN" "$msg"
}
gen_client_config_file() {
local config_file="$1"
json_init
json_add_string "remoteaddr" "${server_addr}:${server_port}"
json_add_string "localaddr" "${listen_addr}:${listen_port}"
add_configs() {
local type="$1"; shift
local k v
for k in "$@"; do
v="$(eval echo "\$$k")"
if [ -n "$v" ]; then
if [ "$type" = "string" ]; then
json_add_string "$k" "$v"
elif [ "$type" = "int" ]; then
json_add_int "$k" "$v"
elif [ "$type" = "boolean" ]; then
if [ "$v" = "true" ]; then
json_add_boolean "$k" "1"
else
json_add_boolean "$k" "0"
fi
fi
fi
done
}
add_configs "string" key crypt mode
add_configs "int" conn autoexpire mtu sndwnd rcvwnd datashard parityshard dscp \
nodelay interval resend nc sockbuf keepalive scavengettl snmpperiod
add_configs "boolean" nocomp acknodelay
if [ -n "$log_file" ]; then
json_add_string "log" "$log_file"
fi
json_close_object
json_dump -i >"$config_file"
}
setup_iptables_chain() {
if ! ( iptables -nL "$KCPTUN" >/dev/null 2>&1 ); then
iptables -N "$KCPTUN" 2>/dev/null
fi
iptables -C OUTPUT -j "$KCPTUN" 2>/dev/null || \
iptables -A OUTPUT -j "$KCPTUN" 2>/dev/null
iptables -C INPUT -j "$KCPTUN" 2>/dev/null || \
iptables -A INPUT -j "$KCPTUN" 2>/dev/null
}
clear_iptables_chain() {
iptables -F "$KCPTUN" >/dev/null 2>&1
}
add_iptables_rule() {
local port="$1"
local type="$2"
if [ "$type" = "client" ]; then
( iptables -C "$KCPTUN" -p tcp --dport "$port" -m comment \
--comment "$type" -j ACCEPT 2>/dev/null ) && return 0
iptables -A "$KCPTUN" -p tcp --dport "$port" -m comment \
--comment "$type" -j ACCEPT 2>/dev/null
elif [ "$type" = "server" ]; then
( iptables -C "$KCPTUN" -p udp --dport "$port" -m comment \
--comment "$type" -j ACCEPT 2>/dev/null ) && return 0
iptables -A "$KCPTUN" -p udp --dport "$port" -m comment \
--comment "$type" -j ACCEPT 2>/dev/null
fi
}
validate_config_section() {
uci_validate_section "$KCPTUN" general "$1" \
'server:uciname' \
'client_file:string' \
'daemon_user:string:root' \
'enable_logging:bool:0' \
'log_folder:directory:/var/log/kcptun'
}
validate_server_section() {
uci_validate_section "$KCPTUN" servers "$1" \
'server_addr:host' \
'server_port:port:29900' \
'listen_addr:host:0.0.0.0' \
'listen_port:port:12948' \
'key:string' \
'crypt:string:aes' \
'mode:or("normal","fast","fast2","fast3","manual"):fast' \
'conn:min(1)' \
'autoexpire:uinteger' \
'scavengettl:min(-1)' \
'mtu:range(64,9200)' \
'sndwnd:min(1)' \
'rcvwnd:min(1)' \
'datashard:uinteger' \
'parityshard:uinteger' \
'dscp:uinteger' \
'nocomp:or("true", "false")' \
'nodelay:bool' \
'interval:uinteger' \
'resend:range(0,2)' \
'nc:bool' \
'acknodelay:or("true", "false")' \
'sockbuf:uinteger' \
'keepalive:uinteger' \
'snmpperiod:min(1)'
}
validate_client_file() {
local file="$1"
if [ ! -f "$file" ]; then
return 1
fi
[ -x "$file" ] || chmod 755 "$file"
( $file -v 2>/dev/null | grep -q "$KCPTUN" )
}
start_kcptun_instance() {
local section="$1"
if ! validate_config_section "$section" ; then
_log "err" "Config validate failed."
return 1
fi
if [ -z "$server" ] || [ "$server" = "nil" ]; then
_log "info" "No server selected, Client will stop."
return 0
elif ! validate_server_section "$server"; then
_log "err" "Server config validation failed."
return 1
elif [ -z "$server_addr" ] || [ -z "$listen_port" ]; then
_log "err" "Server config validation failed."
return 1
fi
if [ -z "$client_file" ]; then
_log "err" "Please set client file path, or use auto download."
return 1;
elif ! validate_client_file "$client_file"; then
_log "err" "Client file validation failed."
return 1
fi
is_ipv6_address() {
echo "$1" | grep -q ":"
}
is_ipv6_address "$server_addr" && server_addr="[${server_addr}]"
is_ipv6_address "$listen_addr" && listen_addr="[${listen_addr}]"
[ -d "$CONFIG_FOLDER" ] || mkdir -p "$CONFIG_FOLDER"
log_file=""
if [ "$enable_logging" = "1" ]; then
mkdir -p "$log_folder"
chown -R "$daemon_user" "$log_folder"
log_file="${log_folder}/client.${section}.log"
fi
local config_file=${CONFIG_FOLDER}/client.${section}.json
if ! ( gen_client_config_file "$config_file" ); then
_log "err" "Can't create config file".
return 1
fi
add_iptables_rule "$listen_port" "client"
procd_open_instance
procd_set_param command "$client_file"
procd_append_param command -c "$config_file"
procd_set_param respawn
procd_set_param user "$daemon_user"
procd_set_param file "$config_file"
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "$KCPTUN"
}
start_service() {
clear_iptables_chain
setup_iptables_chain
config_load "$KCPTUN"
config_foreach start_kcptun_instance "general"
}
stop_service() {
clear_iptables_chain
}
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@kcptun[-1]
add ucitrack kcptun
set ucitrack.@kcptun[-1].init=kcptun
commit ucitrack
EOF
general=$(uci -q get kcptun.@general[-1])
if [ -z "$general" ]; then
uci -q add kcptun general
fi
if [ ."$general" != ."general" ]; then
uci -q batch <<-EOF >/dev/null
rename kcptun.@general[-1]="general"
set kcptun.general.server=""
commit kcptun
EOF
fi
rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
exit 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册