backup
This commit is contained in:
commit
12e3f6f41c
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Psswall & SSRP 插件防炸上游备份
|
||||
|
||||
## 所需依赖
|
||||
|
||||
```shell
|
||||
svn co https://github.com/immortalwrt/packages/trunk/net/dns2socks package/helloworld-deps/dns2socks
|
||||
svn co https://github.com/immortalwrt/packages/trunk/net/microsocks package/helloworld-deps/microsocks
|
||||
svn co https://github.com/immortalwrt/packages/trunk/net/ipt2socks package/helloworld-deps/ipt2socks
|
||||
svn co https://github.com/immortalwrt/packages/trunk/net/pdnsd-alt package/helloworld-deps/pdnsd
|
||||
svn co https://github.com/immortalwrt/packages/trunk/net/redsocks2 package/helloworld-deps/redsocks2
|
||||
```
|
44
brook/Makefile
Normal file
44
brook/Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=brook
|
||||
PKG_VERSION:=20220406
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=cf4433263cc755edfe56be66d206b7ee5083faaaa8b30bb4102174ad73e22764
|
||||
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/txthinking/brook
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/cli/brook
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/brook
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Web Servers/Proxies
|
||||
TITLE:=A cross-platform proxy software
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
URL:=https://github.com/txthinking/brook
|
||||
endef
|
||||
|
||||
define Package/brook/description
|
||||
Brook is a cross-platform strong encryption and not detectable proxy.
|
||||
Zero-Configuration.
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,brook))
|
||||
$(eval $(call BuildPackage,brook))
|
40
chinadns-ng/Makefile
Normal file
40
chinadns-ng/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=chinadns-ng
|
||||
PKG_VERSION:=1.0-beta.25
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/zfl9/chinadns-ng.git
|
||||
PKG_SOURCE_DATE:=2021-05-08
|
||||
PKG_SOURCE_VERSION:=14cc6348d67b09cae37d9bce554c89c2c0e0b265
|
||||
PKG_MIRROR_HASH:=3b66fc0888d9488e3b8e39df3016d51fae1b43325d292381e94aa3c7d2318282
|
||||
|
||||
PKG_LICENSE:=AGPL-3.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=pexcn <i@pexcn.me>
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_INSTALL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/chinadns-ng
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=ChinaDNS next generation, refactoring with epoll and ipset.
|
||||
URL:=https://github.com/zfl9/chinadns-ng
|
||||
DEPENDS:=+ipset
|
||||
endef
|
||||
|
||||
define Package/chinadns-ng/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/chinadns-ng $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,chinadns-ng))
|
53
hysteria/Makefile
Normal file
53
hysteria/Makefile
Normal file
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hysteria
|
||||
PKG_VERSION:=1.0.3
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/HyNetwork/hysteria/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=aa187a860d517404ec2293603c8ad47c4d06776a0e621befb0bc769c3d93779d
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/tobyxdd/hysteria
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd
|
||||
GO_PKG_LDFLAGS_X:=main.appVersion=$(PKG_VERSION)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/hysteria
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A feature-packed network utility optimized for networks of poor quality
|
||||
URL:=https://github.com/tobyxdd/hysteria
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
endef
|
||||
|
||||
define Package/hysteria/description
|
||||
Hysteria is a feature-packed network utility optimized for networks
|
||||
of poor quality (e.g. satellite connections, congested public Wi-Fi,
|
||||
connecting from China to servers abroad) powered by a custom version
|
||||
of QUIC protocol.
|
||||
endef
|
||||
|
||||
define Package/hysteria/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/cmd $(1)/usr/bin/hysteria
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,hysteria))
|
||||
$(eval $(call BuildPackage,hysteria))
|
176
luci-app-passwall/Makefile
Normal file
176
luci-app-passwall/Makefile
Normal file
@ -0,0 +1,176 @@
|
||||
# Copyright (C) 2018-2020 L-WRT Team
|
||||
# Copyright (C) 2021-2022 xiaorouji
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=4.53
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_Transparent_Proxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Brook \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_PDNSD \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
||||
|
||||
LUCI_TITLE:=LuCI support for PassWall
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +curl \
|
||||
+dns2socks +ip-full +libuci-lua +lua +luci-compat +luci-lib-jsonc \
|
||||
+microsocks +resolveip +tcping +unzip \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Brook:brook \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG:chinadns-ng \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy:haproxy \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat:ip6tables-mod-nat \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_PDNSD:pdnsd-alt \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-local \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-redir \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server:shadowsocks-libev-ss-server \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client:shadowsocks-rust-sslocal \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-redir \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server:shadowsocksr-libev-ssr-server \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs:simple-obfs \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO:trojan-go \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus:trojan-plus \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray-core \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin:v2ray-plugin \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray-core \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin:xray-plugin
|
||||
|
||||
define Package/$(PKG_NAME)/config
|
||||
menu "Configuration"
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_Transparent_Proxy
|
||||
bool "Transparent Proxy"
|
||||
select PACKAGE_dnsmasq-full
|
||||
select PACKAGE_ipset
|
||||
select PACKAGE_ipt2socks
|
||||
select PACKAGE_iptables
|
||||
select PACKAGE_iptables-legacy
|
||||
select PACKAGE_iptables-mod-iprange
|
||||
select PACKAGE_iptables-mod-socket
|
||||
select PACKAGE_iptables-mod-tproxy
|
||||
select PACKAGE_kmod-ipt-nat
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Brook
|
||||
bool "Include Brook"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG
|
||||
bool "Include ChinaDNS-NG"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
||||
bool "Include Haproxy"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria
|
||||
bool "Include Hysteria"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat
|
||||
depends on PACKAGE_ip6tables
|
||||
bool "Include IPv6 Nat"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
|
||||
bool "Include NaiveProxy"
|
||||
depends on !(arc||(arm&&TARGET_gemini)||armeb||mips||mips64||powerpc)
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_PDNSD
|
||||
bool "Include PDNSD"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client
|
||||
bool "Include Shadowsocks Libev Client"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server
|
||||
bool "Include Shadowsocks Libev Server"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client
|
||||
bool "Include Shadowsocks Rust Client"
|
||||
depends on aarch64||arm||i386||mips||mipsel||x86_64
|
||||
default y if aarch64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client
|
||||
bool "Include ShadowsocksR Libev Client"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server
|
||||
bool "Include ShadowsocksR Libev Server"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs
|
||||
bool "Include Simple-Obfs (Shadowsocks Plugin)"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO
|
||||
bool "Include Trojan-GO"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus
|
||||
bool "Include Trojan-Plus"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray
|
||||
bool "Include V2ray"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
||||
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
bool "Include Xray"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
||||
bool "Include Xray-Plugin (Shadowsocks Plugin)"
|
||||
default n
|
||||
|
||||
endmenu
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/passwall
|
||||
/etc/config/passwall_server
|
||||
/usr/share/passwall/rules/direct_host
|
||||
/usr/share/passwall/rules/direct_ip
|
||||
/usr/share/passwall/rules/proxy_host
|
||||
/usr/share/passwall/rules/proxy_ip
|
||||
/usr/share/passwall/rules/block_host
|
||||
/usr/share/passwall/rules/block_ip
|
||||
/usr/share/passwall/rules/lanlist_ipv4
|
||||
/usr/share/passwall/rules/lanlist_ipv6
|
||||
/usr/share/passwall/rules/domains_excluded
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
491
luci-app-passwall/luasrc/controller/passwall.lua
Normal file
491
luci-app-passwall/luasrc/controller/passwall.lua
Normal file
@ -0,0 +1,491 @@
|
||||
-- Copyright (C) 2018-2020 L-WRT Team
|
||||
-- Copyright (C) 2021-2022 xiaorouji
|
||||
|
||||
module("luci.controller.passwall", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local ucic = luci.model.uci.cursor()
|
||||
local http = require "luci.http"
|
||||
local util = require "luci.util"
|
||||
local i18n = require "luci.i18n"
|
||||
local brook = require("luci.model.cbi." .. appname ..".api.brook")
|
||||
local v2ray = require("luci.model.cbi." .. appname ..".api.v2ray")
|
||||
local xray = require("luci.model.cbi." .. appname ..".api.xray")
|
||||
local trojan_go = require("luci.model.cbi." .. appname ..".api.trojan_go")
|
||||
local hysteria = require("luci.model.cbi." .. appname ..".api.hysteria")
|
||||
|
||||
function index()
|
||||
appname = require "luci.model.cbi.passwall.api.api".appname
|
||||
entry({"admin", "services", appname}).dependent = true
|
||||
entry({"admin", "services", appname, "reset_config"}, call("reset_config")).leaf = true
|
||||
entry({"admin", "services", appname, "show"}, call("show_menu")).leaf = true
|
||||
entry({"admin", "services", appname, "hide"}, call("hide_menu")).leaf = true
|
||||
if not nixio.fs.access("/etc/config/passwall") then return end
|
||||
if nixio.fs.access("/etc/config/passwall_show") then
|
||||
e = entry({"admin", "services", appname}, alias("admin", "services", appname, "settings"), _("Pass Wall"), -1)
|
||||
e.dependent = true
|
||||
e.acl_depends = { "luci-app-passwall" }
|
||||
end
|
||||
--[[ Client ]]
|
||||
entry({"admin", "services", appname, "settings"}, cbi(appname .. "/client/global"), _("Basic Settings"), 1).dependent = true
|
||||
entry({"admin", "services", appname, "node_list"}, cbi(appname .. "/client/node_list"), _("Node List"), 2).dependent = true
|
||||
entry({"admin", "services", appname, "node_subscribe"}, cbi(appname .. "/client/node_subscribe"), _("Node Subscribe"), 3).dependent = true
|
||||
entry({"admin", "services", appname, "auto_switch"}, cbi(appname .. "/client/auto_switch"), _("Auto Switch"), 4).leaf = true
|
||||
entry({"admin", "services", appname, "other"}, cbi(appname .. "/client/other", {autoapply = true}), _("Other Settings"), 92).leaf = true
|
||||
if nixio.fs.access("/usr/sbin/haproxy") then
|
||||
entry({"admin", "services", appname, "haproxy"}, cbi(appname .. "/client/haproxy"), _("Load Balancing"), 93).leaf = true
|
||||
end
|
||||
entry({"admin", "services", appname, "app_update"}, cbi(appname .. "/client/app_update"), _("App Update"), 95).leaf = true
|
||||
entry({"admin", "services", appname, "rule"}, cbi(appname .. "/client/rule"), _("Rule Manage"), 96).leaf = true
|
||||
entry({"admin", "services", appname, "rule_list"}, cbi(appname .. "/client/rule_list"), _("Rule List"), 97).leaf = true
|
||||
entry({"admin", "services", appname, "node_subscribe_config"}, cbi(appname .. "/client/node_subscribe_config")).leaf = true
|
||||
entry({"admin", "services", appname, "node_config"}, cbi(appname .. "/client/node_config")).leaf = true
|
||||
entry({"admin", "services", appname, "shunt_rules"}, cbi(appname .. "/client/shunt_rules")).leaf = true
|
||||
entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true
|
||||
entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true
|
||||
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true
|
||||
|
||||
--[[ Server ]]
|
||||
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
|
||||
entry({"admin", "services", appname, "server_user"}, cbi(appname .. "/server/user")).leaf = true
|
||||
|
||||
--[[ API ]]
|
||||
entry({"admin", "services", appname, "server_user_status"}, call("server_user_status")).leaf = true
|
||||
entry({"admin", "services", appname, "server_user_log"}, call("server_user_log")).leaf = true
|
||||
entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true
|
||||
entry({"admin", "services", appname, "server_clear_log"}, call("server_clear_log")).leaf = true
|
||||
entry({"admin", "services", appname, "link_add_node"}, call("link_add_node")).leaf = true
|
||||
entry({"admin", "services", appname, "autoswitch_add_node"}, call("autoswitch_add_node")).leaf = true
|
||||
entry({"admin", "services", appname, "autoswitch_remove_node"}, call("autoswitch_remove_node")).leaf = true
|
||||
entry({"admin", "services", appname, "get_now_use_node"}, call("get_now_use_node")).leaf = true
|
||||
entry({"admin", "services", appname, "get_redir_log"}, call("get_redir_log")).leaf = true
|
||||
entry({"admin", "services", appname, "get_log"}, call("get_log")).leaf = true
|
||||
entry({"admin", "services", appname, "clear_log"}, call("clear_log")).leaf = true
|
||||
entry({"admin", "services", appname, "status"}, call("status")).leaf = true
|
||||
entry({"admin", "services", appname, "haproxy_status"}, call("haproxy_status")).leaf = true
|
||||
entry({"admin", "services", appname, "socks_status"}, call("socks_status")).leaf = true
|
||||
entry({"admin", "services", appname, "connect_status"}, call("connect_status")).leaf = true
|
||||
entry({"admin", "services", appname, "ping_node"}, call("ping_node")).leaf = true
|
||||
entry({"admin", "services", appname, "urltest_node"}, call("urltest_node")).leaf = true
|
||||
entry({"admin", "services", appname, "set_node"}, call("set_node")).leaf = true
|
||||
entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true
|
||||
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
|
||||
entry({"admin", "services", appname, "brook_check"}, call("brook_check")).leaf = true
|
||||
entry({"admin", "services", appname, "brook_update"}, call("brook_update")).leaf = true
|
||||
entry({"admin", "services", appname, "v2ray_check"}, call("v2ray_check")).leaf = true
|
||||
entry({"admin", "services", appname, "v2ray_update"}, call("v2ray_update")).leaf = true
|
||||
entry({"admin", "services", appname, "xray_check"}, call("xray_check")).leaf = true
|
||||
entry({"admin", "services", appname, "xray_update"}, call("xray_update")).leaf = true
|
||||
entry({"admin", "services", appname, "trojan_go_check"}, call("trojan_go_check")).leaf = true
|
||||
entry({"admin", "services", appname, "trojan_go_update"}, call("trojan_go_update")).leaf = true
|
||||
entry({"admin", "services", appname, "hysteria_check"}, call("hysteria_check")).leaf = true
|
||||
entry({"admin", "services", appname, "hysteria_update"}, call("hysteria_update")).leaf = true
|
||||
end
|
||||
|
||||
local function http_write_json(content)
|
||||
http.prepare_content("application/json")
|
||||
http.write_json(content or {code = 1})
|
||||
end
|
||||
|
||||
function reset_config()
|
||||
luci.sys.call('/etc/init.d/passwall stop')
|
||||
luci.sys.call('[ -f "/usr/share/passwall/0_default_config" ] && cp -f /usr/share/passwall/0_default_config /etc/config/passwall')
|
||||
luci.http.redirect(api.url())
|
||||
end
|
||||
|
||||
function show_menu()
|
||||
luci.sys.call("touch /etc/config/passwall_show")
|
||||
luci.sys.call("rm -rf /tmp/luci-*")
|
||||
luci.sys.call("/etc/init.d/rpcd restart >/dev/null")
|
||||
luci.http.redirect(api.url())
|
||||
end
|
||||
|
||||
function hide_menu()
|
||||
luci.sys.call("rm -rf /etc/config/passwall_show")
|
||||
luci.sys.call("rm -rf /tmp/luci-*")
|
||||
luci.sys.call("/etc/init.d/rpcd restart >/dev/null")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "status", "overview"))
|
||||
end
|
||||
|
||||
function link_add_node()
|
||||
local lfile = "/tmp/links.conf"
|
||||
local link = luci.http.formvalue("link")
|
||||
luci.sys.call('echo \'' .. link .. '\' > ' .. lfile)
|
||||
luci.sys.call("lua /usr/share/passwall/subscribe.lua add log")
|
||||
end
|
||||
|
||||
function autoswitch_add_node()
|
||||
local key = luci.http.formvalue("key")
|
||||
if key and key ~= "" then
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and e["remark"]:find(key) then
|
||||
luci.sys.call(string.format("uci -q del_list passwall.@auto_switch[0].tcp_node='%s' && uci -q add_list passwall.@auto_switch[0].tcp_node='%s'", e.id, e.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
luci.http.redirect(api.url("auto_switch"))
|
||||
end
|
||||
|
||||
function autoswitch_remove_node()
|
||||
local key = luci.http.formvalue("key")
|
||||
if key and key ~= "" then
|
||||
for k, e in ipairs(ucic:get(appname, "@auto_switch[0]", "tcp_node") or {}) do
|
||||
if e and (ucic:get(appname, e, "remarks") or ""):find(key) then
|
||||
luci.sys.call(string.format("uci -q del_list passwall.@auto_switch[0].tcp_node='%s'", e))
|
||||
end
|
||||
end
|
||||
end
|
||||
luci.http.redirect(api.url("auto_switch"))
|
||||
end
|
||||
|
||||
function get_now_use_node()
|
||||
local e = {}
|
||||
local data, code, msg = nixio.fs.readfile("/tmp/etc/passwall/id/TCP")
|
||||
if data then
|
||||
e["TCP"] = util.trim(data)
|
||||
end
|
||||
local data, code, msg = nixio.fs.readfile("/tmp/etc/passwall/id/UDP")
|
||||
if data then
|
||||
e["UDP"] = util.trim(data)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function get_redir_log()
|
||||
local proto = luci.http.formvalue("proto")
|
||||
proto = proto:upper()
|
||||
if proto == "UDP" and (ucic:get(appname, "@global[0]", "udp_node") or "nil") == "tcp" and not nixio.fs.access("/tmp/etc/passwall/" .. proto .. ".log") then
|
||||
proto = "TCP"
|
||||
end
|
||||
if nixio.fs.access("/tmp/etc/passwall/" .. proto .. ".log") then
|
||||
local content = luci.sys.exec("cat /tmp/etc/passwall/" .. proto .. ".log")
|
||||
content = content:gsub("\n", "<br />")
|
||||
luci.http.write(content)
|
||||
else
|
||||
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
|
||||
end
|
||||
end
|
||||
|
||||
function get_log()
|
||||
-- luci.sys.exec("[ -f /tmp/log/passwall.log ] && sed '1!G;h;$!d' /tmp/log/passwall.log > /tmp/log/passwall_show.log")
|
||||
luci.http.write(luci.sys.exec("[ -f '/tmp/log/passwall.log' ] && cat /tmp/log/passwall.log"))
|
||||
end
|
||||
|
||||
function clear_log()
|
||||
luci.sys.call("echo '' > /tmp/log/passwall.log")
|
||||
end
|
||||
|
||||
function status()
|
||||
-- local dns_mode = ucic:get(appname, "@global[0]", "dns_mode")
|
||||
local e = {}
|
||||
e.dns_mode_status = luci.sys.call("netstat -apn | grep ':7913 ' >/dev/null") == 0
|
||||
e.haproxy_status = luci.sys.call(string.format("/bin/top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
|
||||
e["tcp_node_status"] = luci.sys.call(string.format("/bin/top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep -i 'TCP' >/dev/null", appname)) == 0
|
||||
|
||||
if (ucic:get(appname, "@global[0]", "udp_node") or "nil") == "tcp" then
|
||||
e["udp_node_status"] = e["tcp_node_status"]
|
||||
else
|
||||
e["udp_node_status"] = luci.sys.call(string.format("/bin/top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep -i 'UDP' >/dev/null", appname)) == 0
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function haproxy_status()
|
||||
local e = luci.sys.call(string.format("/bin/top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function socks_status()
|
||||
local e = {}
|
||||
local index = luci.http.formvalue("index")
|
||||
local id = luci.http.formvalue("id")
|
||||
e.index = index
|
||||
e.socks_status = luci.sys.call(string.format("/bin/top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep '%s' | grep 'SOCKS_' > /dev/null", appname, id)) == 0
|
||||
local use_http = ucic:get(appname, id, "http_port") or 0
|
||||
e.use_http = 0
|
||||
if tonumber(use_http) > 0 then
|
||||
e.use_http = 1
|
||||
e.http_status = luci.sys.call(string.format("/bin/top -bn1 | grep -v -E 'grep|acl/|acl_' | grep '%s/bin/' | grep '%s' | grep -E 'HTTP_|HTTP2SOCKS' > /dev/null", appname, id)) == 0
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function connect_status()
|
||||
local e = {}
|
||||
e.use_time = ""
|
||||
local url = luci.http.formvalue("url")
|
||||
local result = luci.sys.exec('curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" ' .. url)
|
||||
local code = tonumber(luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $1}'") or "0")
|
||||
if code ~= 0 then
|
||||
local use_time = luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $2}'")
|
||||
if use_time:find("%.") then
|
||||
e.use_time = string.format("%.2f", use_time * 1000)
|
||||
else
|
||||
e.use_time = string.format("%.2f", use_time / 1000)
|
||||
end
|
||||
e.ping_type = "curl"
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function ping_node()
|
||||
local index = luci.http.formvalue("index")
|
||||
local address = luci.http.formvalue("address")
|
||||
local port = luci.http.formvalue("port")
|
||||
local e = {}
|
||||
e.index = index
|
||||
local nodes_ping = ucic:get(appname, "@global_other[0]", "nodes_ping") or ""
|
||||
if nodes_ping:find("tcping") and luci.sys.exec("echo -n $(command -v tcping)") ~= "" then
|
||||
if api.is_ipv6(address) then
|
||||
address = api.get_ipv6_only(address)
|
||||
end
|
||||
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, address))
|
||||
end
|
||||
if e.ping == nil or tonumber(e.ping) == 0 then
|
||||
e.ping = luci.sys.exec("echo -n $(ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null" % address)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function urltest_node()
|
||||
local index = luci.http.formvalue("index")
|
||||
local id = luci.http.formvalue("id")
|
||||
local e = {}
|
||||
e.index = index
|
||||
local result = luci.sys.exec(string.format("/usr/share/passwall/test.sh url_test_node %s %s", id, "urltest_node"))
|
||||
local code = tonumber(luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $1}'") or "0")
|
||||
if code ~= 0 then
|
||||
local use_time = luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $2}'")
|
||||
if use_time:find("%.") then
|
||||
e.use_time = string.format("%.2f", use_time * 1000)
|
||||
else
|
||||
e.use_time = string.format("%.2f", use_time / 1000)
|
||||
end
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function set_node()
|
||||
local protocol = luci.http.formvalue("protocol")
|
||||
local section = luci.http.formvalue("section")
|
||||
ucic:set(appname, "@global[0]", protocol .. "_node", section)
|
||||
ucic:commit(appname)
|
||||
luci.sys.call("/etc/init.d/passwall restart > /dev/null 2>&1 &")
|
||||
luci.http.redirect(api.url("log"))
|
||||
end
|
||||
|
||||
function copy_node()
|
||||
local section = luci.http.formvalue("section")
|
||||
local uuid = api.gen_uuid()
|
||||
ucic:section(appname, "nodes", uuid)
|
||||
for k, v in pairs(ucic:get_all(appname, section)) do
|
||||
local filter = k:find("%.")
|
||||
if filter and filter == 1 then
|
||||
else
|
||||
xpcall(function()
|
||||
ucic:set(appname, uuid, k, v)
|
||||
end,
|
||||
function(e)
|
||||
end)
|
||||
end
|
||||
end
|
||||
ucic:delete(appname, uuid, "add_from")
|
||||
ucic:set(appname, uuid, "add_mode", 1)
|
||||
ucic:commit(appname)
|
||||
luci.http.redirect(api.url("node_config", uuid))
|
||||
end
|
||||
|
||||
function clear_all_nodes()
|
||||
ucic:set(appname, '@global[0]', "enabled", "0")
|
||||
ucic:set(appname, '@global[0]', "tcp_node", "nil")
|
||||
ucic:set(appname, '@global[0]', "udp_node", "nil")
|
||||
ucic:set_list(appname, "@auto_switch[0]", "tcp_node", {})
|
||||
ucic:foreach(appname, "socks", function(t)
|
||||
ucic:delete(appname, t[".name"])
|
||||
end)
|
||||
ucic:foreach(appname, "haproxy_config", function(t)
|
||||
ucic:delete(appname, t[".name"])
|
||||
end)
|
||||
ucic:foreach(appname, "acl_rule", function(t)
|
||||
ucic:set(appname, t[".name"], "tcp_node", "default")
|
||||
ucic:set(appname, t[".name"], "udp_node", "default")
|
||||
end)
|
||||
ucic:foreach(appname, "nodes", function(node)
|
||||
ucic:delete(appname, node['.name'])
|
||||
end)
|
||||
|
||||
ucic:commit(appname)
|
||||
luci.sys.call("/etc/init.d/" .. appname .. " stop")
|
||||
end
|
||||
|
||||
function delete_select_nodes()
|
||||
local ids = luci.http.formvalue("ids")
|
||||
local auto_switch_tcp_node_list = ucic:get(appname, "@auto_switch[0]", "tcp_node") or {}
|
||||
string.gsub(ids, '[^' .. "," .. ']+', function(w)
|
||||
for k, v in ipairs(auto_switch_tcp_node_list) do
|
||||
if v == w then
|
||||
luci.sys.call(string.format("uci -q del_list passwall.@auto_switch[0].tcp_node='%s'", w))
|
||||
end
|
||||
end
|
||||
if (ucic:get(appname, "@global[0]", "tcp_node") or "nil") == w then
|
||||
ucic:set(appname, '@global[0]', "tcp_node", "nil")
|
||||
end
|
||||
if (ucic:get(appname, "@global[0]", "udp_node") or "nil") == w then
|
||||
ucic:set(appname, '@global[0]', "udp_node", "nil")
|
||||
end
|
||||
ucic:foreach(appname, "socks", function(t)
|
||||
if t["node"] == w then
|
||||
ucic:delete(appname, t[".name"])
|
||||
end
|
||||
end)
|
||||
ucic:foreach(appname, "haproxy_config", function(t)
|
||||
if t["lbss"] == w then
|
||||
ucic:delete(appname, t[".name"])
|
||||
end
|
||||
end)
|
||||
ucic:foreach(appname, "acl_rule", function(t)
|
||||
if t["tcp_node"] == w then
|
||||
ucic:set(appname, t[".name"], "tcp_node", "default")
|
||||
end
|
||||
if t["udp_node"] == w then
|
||||
ucic:set(appname, t[".name"], "udp_node", "default")
|
||||
end
|
||||
end)
|
||||
ucic:delete(appname, w)
|
||||
end)
|
||||
ucic:commit(appname)
|
||||
luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
function update_rules()
|
||||
local update = luci.http.formvalue("update")
|
||||
luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &")
|
||||
http_write_json()
|
||||
end
|
||||
|
||||
function server_user_status()
|
||||
local e = {}
|
||||
e.index = luci.http.formvalue("index")
|
||||
e.status = luci.sys.call(string.format("/bin/top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep -i '%s' >/dev/null", appname .. "_server", luci.http.formvalue("id"))) == 0
|
||||
http_write_json(e)
|
||||
end
|
||||
|
||||
function server_user_log()
|
||||
local id = luci.http.formvalue("id")
|
||||
if nixio.fs.access("/tmp/etc/passwall_server/" .. id .. ".log") then
|
||||
local content = luci.sys.exec("cat /tmp/etc/passwall_server/" .. id .. ".log")
|
||||
content = content:gsub("\n", "<br />")
|
||||
luci.http.write(content)
|
||||
else
|
||||
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
|
||||
end
|
||||
end
|
||||
|
||||
function server_get_log()
|
||||
luci.http.write(luci.sys.exec("[ -f '/tmp/log/passwall_server.log' ] && cat /tmp/log/passwall_server.log"))
|
||||
end
|
||||
|
||||
function server_clear_log()
|
||||
luci.sys.call("echo '' > /tmp/log/passwall_server.log")
|
||||
end
|
||||
|
||||
function brook_check()
|
||||
local json = brook.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function brook_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "move" then
|
||||
json = brook.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = brook.to_download(http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function v2ray_check()
|
||||
local json = v2ray.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function v2ray_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "extract" then
|
||||
json = v2ray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
|
||||
elseif task == "move" then
|
||||
json = v2ray.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = v2ray.to_download(http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function xray_check()
|
||||
local json = xray.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function xray_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "extract" then
|
||||
json = xray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
|
||||
elseif task == "move" then
|
||||
json = xray.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = xray.to_download(http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function trojan_go_check()
|
||||
local json = trojan_go.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function trojan_go_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "extract" then
|
||||
json = trojan_go.to_extract(http.formvalue("file"), http.formvalue("subfix"))
|
||||
elseif task == "move" then
|
||||
json = trojan_go.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = trojan_go.to_download(http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function hysteria_check()
|
||||
local json = hysteria.to_check("")
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function hysteria_update()
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "move" then
|
||||
json = hysteria.to_move(http.formvalue("file"))
|
||||
else
|
||||
json = hysteria.to_download(http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
||||
|
617
luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua
Executable file
617
luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua
Executable file
@ -0,0 +1,617 @@
|
||||
module("luci.model.cbi.passwall.api.api", package.seeall)
|
||||
fs = require "nixio.fs"
|
||||
sys = require "luci.sys"
|
||||
uci = require"luci.model.uci".cursor()
|
||||
util = require "luci.util"
|
||||
datatypes = require "luci.cbi.datatypes"
|
||||
jsonc = require "luci.jsonc"
|
||||
i18n = require "luci.i18n"
|
||||
|
||||
appname = "passwall"
|
||||
curl = "/usr/bin/curl"
|
||||
curl_args = {"-skfL", "--connect-timeout 3", "--retry 3", "-m 60"}
|
||||
command_timeout = 300
|
||||
LEDE_BOARD = nil
|
||||
DISTRIB_TARGET = nil
|
||||
|
||||
function base64Decode(text)
|
||||
local raw = text
|
||||
if not text then return '' end
|
||||
text = text:gsub("%z", "")
|
||||
text = text:gsub("%c", "")
|
||||
text = text:gsub("_", "/")
|
||||
text = text:gsub("-", "+")
|
||||
local mod4 = #text % 4
|
||||
text = text .. string.sub('====', mod4 + 1)
|
||||
local result = nixio.bin.b64decode(text)
|
||||
if result then
|
||||
return result:gsub("%z", "")
|
||||
else
|
||||
return raw
|
||||
end
|
||||
end
|
||||
|
||||
function url(...)
|
||||
local url = string.format("admin/services/%s", appname)
|
||||
local args = { ... }
|
||||
for i, v in pairs(args) do
|
||||
if v ~= "" then
|
||||
url = url .. "/" .. v
|
||||
end
|
||||
end
|
||||
return require "luci.dispatcher".build_url(url)
|
||||
end
|
||||
|
||||
function trim(s)
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function is_exist(table, value)
|
||||
for index, k in ipairs(table) do
|
||||
if k == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function repeat_exist(table, value)
|
||||
local count = 0
|
||||
for index, k in ipairs(table) do
|
||||
if k:find("-") and k == value then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count > 1 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function get_args(arg)
|
||||
local var = {}
|
||||
for i, arg_k in pairs(arg) do
|
||||
if i > 0 then
|
||||
local v = arg[i + 1]
|
||||
if v then
|
||||
if repeat_exist(arg, v) == false then
|
||||
var[arg_k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return var
|
||||
end
|
||||
|
||||
function strToTable(str)
|
||||
if str == nil or type(str) ~= "string" then
|
||||
return {}
|
||||
end
|
||||
|
||||
return loadstring("return " .. str)()
|
||||
end
|
||||
|
||||
function is_normal_node(e)
|
||||
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function is_special_node(e)
|
||||
return is_normal_node(e) == false
|
||||
end
|
||||
|
||||
function is_ip(val)
|
||||
if is_ipv6(val) then
|
||||
val = get_ipv6_only(val)
|
||||
end
|
||||
return datatypes.ipaddr(val)
|
||||
end
|
||||
|
||||
function is_ipv6(val)
|
||||
local str = val
|
||||
local address = val:match('%[(.*)%]')
|
||||
if address then
|
||||
str = address
|
||||
end
|
||||
if datatypes.ip6addr(str) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function is_ipv6addrport(val)
|
||||
if is_ipv6(val) then
|
||||
local address, port = val:match('%[(.*)%]:([^:]+)$')
|
||||
if port then
|
||||
return datatypes.port(port)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function get_ipv6_only(val)
|
||||
local result = ""
|
||||
if is_ipv6(val) then
|
||||
result = val
|
||||
if val:match('%[(.*)%]') then
|
||||
result = val:match('%[(.*)%]')
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function get_ipv6_full(val)
|
||||
local result = ""
|
||||
if is_ipv6(val) then
|
||||
result = val
|
||||
if not val:match('%[(.*)%]') then
|
||||
result = "[" .. result .. "]"
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function get_ip_type(val)
|
||||
if is_ipv6(val) then
|
||||
return "6"
|
||||
elseif datatypes.ip4addr(val) then
|
||||
return "4"
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function is_mac(val)
|
||||
return datatypes.macaddr(val)
|
||||
end
|
||||
|
||||
function ip_or_mac(val)
|
||||
if val then
|
||||
if get_ip_type(val) == "4" then
|
||||
return "ip"
|
||||
end
|
||||
if is_mac(val) then
|
||||
return "mac"
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function iprange(val)
|
||||
if val then
|
||||
local ipStart, ipEnd = val:match("^([^/]+)-([^/]+)$")
|
||||
if (ipStart and datatypes.ip4addr(ipStart)) and (ipEnd and datatypes.ip4addr(ipEnd)) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function get_valid_nodes()
|
||||
local nodes_ping = uci_get_type("global_other", "nodes_ping") or ""
|
||||
local nodes = {}
|
||||
uci:foreach(appname, "nodes", function(e)
|
||||
e.id = e[".name"]
|
||||
if e.type and e.remarks then
|
||||
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then
|
||||
e["remark"] = "%s:[%s] " % {i18n.translatef(e.type .. e.protocol), e.remarks}
|
||||
e["node_type"] = "special"
|
||||
nodes[#nodes + 1] = e
|
||||
end
|
||||
if e.port and e.address then
|
||||
local address = e.address
|
||||
if is_ip(address) or datatypes.hostname(address) then
|
||||
local type = e.type
|
||||
if (type == "V2ray" or type == "Xray") and e.protocol then
|
||||
local protocol = e.protocol
|
||||
if protocol == "vmess" then
|
||||
protocol = "VMess"
|
||||
elseif protocol == "vless" then
|
||||
protocol = "VLESS"
|
||||
else
|
||||
protocol = protocol:gsub("^%l",string.upper)
|
||||
end
|
||||
type = type .. " " .. protocol
|
||||
end
|
||||
if is_ipv6(address) then address = get_ipv6_full(address) end
|
||||
e["remark"] = "%s:[%s]" % {type, e.remarks}
|
||||
if nodes_ping:find("info") then
|
||||
e["remark"] = "%s:[%s] %s:%s" % {type, e.remarks, address, e.port}
|
||||
end
|
||||
e.node_type = "normal"
|
||||
nodes[#nodes + 1] = e
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
return nodes
|
||||
end
|
||||
|
||||
function get_full_node_remarks(n)
|
||||
local remarks = ""
|
||||
if n then
|
||||
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt") then
|
||||
remarks = "%s:[%s] " % {i18n.translatef(n.type .. n.protocol), n.remarks}
|
||||
else
|
||||
local type2 = n.type
|
||||
if (n.type == "V2ray" or n.type == "Xray") and n.protocol then
|
||||
local protocol = n.protocol
|
||||
if protocol == "vmess" then
|
||||
protocol = "VMess"
|
||||
elseif protocol == "vless" then
|
||||
protocol = "VLESS"
|
||||
else
|
||||
protocol = protocol:gsub("^%l",string.upper)
|
||||
end
|
||||
type2 = type2 .. " " .. protocol
|
||||
end
|
||||
remarks = "%s:[%s] %s:%s" % {type2, n.remarks, n.address, n.port}
|
||||
end
|
||||
end
|
||||
return remarks
|
||||
end
|
||||
|
||||
function gen_uuid(format)
|
||||
local uuid = sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)")
|
||||
if format == nil then
|
||||
uuid = string.gsub(uuid, "-", "")
|
||||
end
|
||||
return uuid
|
||||
end
|
||||
|
||||
function uci_get_type(type, config, default)
|
||||
local value = uci:get_first(appname, type, config, default) or sys.exec("echo -n $(uci -q get " .. appname .. ".@" .. type .."[0]." .. config .. ")")
|
||||
if (value == nil or value == "") and (default and default ~= "") then
|
||||
value = default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function uci_get_type_id(id, config, default)
|
||||
local value = uci:get(appname, id, config, default) or sys.exec("echo -n $(uci -q get " .. appname .. "." .. id .. "." .. config .. ")")
|
||||
if (value == nil or value == "") and (default and default ~= "") then
|
||||
value = default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function chmod_755(file)
|
||||
if file and file ~= "" then
|
||||
if not fs.access(file, "rwx", "rx", "rx") then
|
||||
fs.chmod(file, 755)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function get_customed_path(e)
|
||||
return uci_get_type("global_app", e .. "_file")
|
||||
end
|
||||
|
||||
function is_finded(e)
|
||||
return luci.sys.exec('type -t -p "/bin/%s" -p "%s" "%s"' % {e, get_customed_path(e), e}) ~= "" and true or false
|
||||
end
|
||||
|
||||
|
||||
function clone(org)
|
||||
local function copy(org, res)
|
||||
for k,v in pairs(org) do
|
||||
if type(v) ~= "table" then
|
||||
res[k] = v;
|
||||
else
|
||||
res[k] = {};
|
||||
copy(v, res[k])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local res = {}
|
||||
copy(org, res)
|
||||
return res
|
||||
end
|
||||
|
||||
function get_bin_version_cache(file, cmd)
|
||||
sys.call("mkdir -p /tmp/etc/passwall_tmp")
|
||||
if fs.access(file) then
|
||||
chmod_755(file)
|
||||
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
|
||||
if fs.access("/tmp/etc/passwall_tmp/" .. md5) then
|
||||
return sys.exec("echo -n $(cat /tmp/etc/passwall_tmp/%s)" % md5)
|
||||
else
|
||||
local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd))
|
||||
sys.call("echo '" .. version .. "' > " .. "/tmp/etc/passwall_tmp/" .. md5)
|
||||
return version
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function get_v2ray_path()
|
||||
local path = uci_get_type("global_app", "v2ray_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_v2ray_version(file)
|
||||
if file == nil then file = get_v2ray_path() end
|
||||
local cmd = "-version | awk '{print $2}' | sed -n 1P"
|
||||
return get_bin_version_cache(file, cmd)
|
||||
end
|
||||
|
||||
function get_xray_path()
|
||||
local path = uci_get_type("global_app", "xray_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_xray_version(file)
|
||||
if file == nil then file = get_xray_path() end
|
||||
local cmd = "-version | awk '{print $2}' | sed -n 1P"
|
||||
return get_bin_version_cache(file, cmd)
|
||||
end
|
||||
|
||||
function get_trojan_go_path()
|
||||
local path = uci_get_type("global_app", "trojan_go_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_trojan_go_version(file)
|
||||
if file == nil then file = get_trojan_go_path() end
|
||||
local cmd = "-version | awk '{print $2}' | sed -n 1P"
|
||||
return get_bin_version_cache(file, cmd)
|
||||
end
|
||||
|
||||
function get_brook_path()
|
||||
local path = uci_get_type("global_app", "brook_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_brook_version(file)
|
||||
if file == nil then file = get_brook_path() end
|
||||
local cmd = "-v | awk '{print $3}'"
|
||||
return get_bin_version_cache(file, cmd)
|
||||
end
|
||||
|
||||
function get_hysteria_path()
|
||||
local path = uci_get_type("global_app", "hysteria_file")
|
||||
return path
|
||||
end
|
||||
|
||||
function get_hysteria_version(file)
|
||||
if file == nil then file = get_hysteria_path() end
|
||||
local cmd = "-v | awk '{print $3}'"
|
||||
return get_bin_version_cache(file, cmd)
|
||||
end
|
||||
|
||||
function is_file(path)
|
||||
if path and #path > 1 then
|
||||
if sys.exec('[ -f "%s" ] && echo -n 1' % path) == "1" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function is_dir(path)
|
||||
if path and #path > 1 then
|
||||
if sys.exec('[ -d "%s" ] && echo -n 1' % path) == "1" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function get_final_dir(path)
|
||||
if is_dir(path) then
|
||||
return path
|
||||
else
|
||||
return get_final_dir(fs.dirname(path))
|
||||
end
|
||||
end
|
||||
|
||||
function get_free_space(dir)
|
||||
if dir == nil then dir = "/" end
|
||||
if sys.call("df -k " .. dir .. " >/dev/null 2>&1") == 0 then
|
||||
return tonumber(sys.exec("echo -n $(df -k " .. dir .. " | awk 'NR>1' | awk '{print $4}')"))
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function get_file_space(file)
|
||||
if file == nil then return 0 end
|
||||
if fs.access(file) then
|
||||
return tonumber(sys.exec("echo -n $(du -k " .. file .. " | awk '{print $1}')"))
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function _unpack(t, i)
|
||||
i = i or 1
|
||||
if t[i] ~= nil then return t[i], _unpack(t, i + 1) end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function compare_versions(ver1, comp, ver2)
|
||||
local table = table
|
||||
|
||||
if not ver1 then ver1 = "" end
|
||||
if not ver2 then ver2 = "" end
|
||||
|
||||
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 = tonumber(av1[i] or 0) or 0
|
||||
local s2 = tonumber(av2[i] or 0) or 0
|
||||
|
||||
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
|
||||
|
||||
function auto_get_arch()
|
||||
local arch = nixio.uname().machine or ""
|
||||
if fs.access("/usr/lib/os-release") then
|
||||
LEDE_BOARD = sys.exec("echo -n $(grep 'LEDE_BOARD' /usr/lib/os-release | awk -F '[\\042\\047]' '{print $2}')")
|
||||
end
|
||||
if fs.access("/etc/openwrt_release") then
|
||||
DISTRIB_TARGET = sys.exec("echo -n $(grep 'DISTRIB_TARGET' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}')")
|
||||
end
|
||||
|
||||
if arch == "mips" then
|
||||
if LEDE_BOARD and LEDE_BOARD ~= "" then
|
||||
if string.match(LEDE_BOARD, "ramips") == "ramips" then
|
||||
arch = "ramips"
|
||||
else
|
||||
arch = sys.exec("echo '" .. LEDE_BOARD .. "' | grep -oE 'ramips|ar71xx'")
|
||||
end
|
||||
elseif DISTRIB_TARGET and DISTRIB_TARGET ~= "" then
|
||||
if string.match(DISTRIB_TARGET, "ramips") == "ramips" then
|
||||
arch = "ramips"
|
||||
else
|
||||
arch = sys.exec("echo '" .. DISTRIB_TARGET .. "' | grep -oE 'ramips|ar71xx'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return util.trim(arch)
|
||||
end
|
||||
|
||||
function get_file_info(arch)
|
||||
local file_tree = ""
|
||||
local sub_version = ""
|
||||
|
||||
if arch == "x86_64" then
|
||||
file_tree = "amd64"
|
||||
elseif arch == "aarch64" then
|
||||
file_tree = "arm64"
|
||||
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]")
|
||||
if LEDE_BOARD and string.match(LEDE_BOARD, "bcm53xx") == "bcm53xx" then
|
||||
sub_version = "5"
|
||||
elseif DISTRIB_TARGET and string.match(DISTRIB_TARGET, "bcm53xx") ==
|
||||
"bcm53xx" then
|
||||
sub_version = "5"
|
||||
end
|
||||
sub_version = "5"
|
||||
end
|
||||
|
||||
return file_tree, sub_version
|
||||
end
|
||||
|
||||
function get_api_json(url)
|
||||
local jsonc = require "luci.jsonc"
|
||||
local json_content = luci.sys.exec(curl .. " " .. _unpack(curl_args) .. " " .. url)
|
||||
if json_content == "" then return {} end
|
||||
return jsonc.parse(json_content) or {}
|
||||
end
|
||||
|
||||
function common_to_check(api_url, local_version, match_file_name)
|
||||
local json = get_api_json(api_url)
|
||||
|
||||
if #json > 0 then
|
||||
json = json[1]
|
||||
end
|
||||
|
||||
if json.tag_name == nil then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Get remote version info failed.")
|
||||
}
|
||||
end
|
||||
|
||||
local remote_version = json.tag_name
|
||||
local has_update = compare_versions(local_version:match("[^v]+"), "<", remote_version:match("[^v]+"))
|
||||
|
||||
if not has_update then
|
||||
return {
|
||||
code = 0,
|
||||
local_version = local_version,
|
||||
remote_version = remote_version
|
||||
}
|
||||
end
|
||||
|
||||
local asset = {}
|
||||
for _, v in ipairs(json.assets) do
|
||||
if v.name and v.name:match(match_file_name) then
|
||||
asset = v
|
||||
break
|
||||
end
|
||||
end
|
||||
if not asset.browser_download_url then
|
||||
return {
|
||||
code = 1,
|
||||
local_version = local_version,
|
||||
remote_version = remote_version,
|
||||
html_url = json.html_url,
|
||||
data = asset,
|
||||
error = i18n.translate("New version found, but failed to get new version download url.")
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
code = 0,
|
||||
has_update = true,
|
||||
local_version = local_version,
|
||||
remote_version = remote_version,
|
||||
html_url = json.html_url,
|
||||
data = asset
|
||||
}
|
||||
end
|
134
luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua
Normal file
134
luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua
Normal file
@ -0,0 +1,134 @@
|
||||
module("luci.model.cbi.passwall.api.brook", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local util = api.util
|
||||
local i18n = api.i18n
|
||||
|
||||
local pre_release_url = "https://api.github.com/repos/txthinking/brook/releases?per_page=1"
|
||||
local release_url = "https://api.github.com/repos/txthinking/brook/releases/latest"
|
||||
local api_url = release_url
|
||||
local app_path = api.get_brook_path() or ""
|
||||
|
||||
function check_path()
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook")
|
||||
}
|
||||
end
|
||||
return {
|
||||
code = 0
|
||||
}
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
return api.common_to_check(api_url, api.get_brook_version(), "linux_" .. file_tree .. sub_version)
|
||||
end
|
||||
|
||||
function to_download(url, size)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/brook_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t brook_download.XXXXXX"))
|
||||
|
||||
if size then
|
||||
local kb1 = api.get_free_space("/tmp")
|
||||
if tonumber(size) > tonumber(kb1) then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.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 to_move(file)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local new_version = api.get_brook_version(file)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local flag = sys.call('pgrep -af "passwall/.*brook" >/dev/null')
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
end
|
||||
|
||||
local old_app_size = 0
|
||||
if fs.access(app_path) then
|
||||
old_app_size = api.get_file_space(app_path)
|
||||
end
|
||||
local new_app_size = api.get_file_space(file)
|
||||
local final_dir = api.get_final_dir(app_path)
|
||||
local final_dir_free_size = api.get_free_space(final_dir)
|
||||
if final_dir_free_size > 0 then
|
||||
final_dir_free_size = final_dir_free_size + old_app_size
|
||||
if new_app_size > final_dir_free_size then
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/brook_download.*")
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0}
|
||||
end
|
@ -0,0 +1,70 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local node_id = var["-node"]
|
||||
if not node_id then
|
||||
print("-node 不能为空")
|
||||
return
|
||||
end
|
||||
local node = uci:get_all("passwall", node_id)
|
||||
local local_tcp_redir_port = var["-local_tcp_redir_port"]
|
||||
local local_udp_redir_port = var["-local_udp_redir_port"]
|
||||
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
|
||||
local local_socks_port = var["-local_socks_port"]
|
||||
local local_socks_username = var["-local_socks_username"]
|
||||
local local_socks_password = var["-local_socks_password"]
|
||||
local local_http_address = var["-local_http_address"] or "0.0.0.0"
|
||||
local local_http_port = var["-local_http_port"]
|
||||
local local_http_username = var["-local_http_username"]
|
||||
local local_http_password = var["-local_http_password"]
|
||||
local server_host = var["-server_host"] or node.address
|
||||
local server_port = var["-server_port"] or node.port
|
||||
|
||||
if api.is_ipv6(server_host) then
|
||||
server_host = api.get_ipv6_full(server_host)
|
||||
end
|
||||
local server = server_host .. ":" .. server_port
|
||||
|
||||
local config = {
|
||||
server = server,
|
||||
protocol = node.protocol or "udp",
|
||||
obfs = node.hysteria_obfs,
|
||||
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil,
|
||||
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
|
||||
alpn = node.hysteria_alpn or nil,
|
||||
server_name = node.tls_serverName,
|
||||
insecure = (node.tls_allowInsecure == "1") and true or false,
|
||||
up_mbps = tonumber(node.hysteria_up_mbps) or 10,
|
||||
down_mbps = tonumber(node.hysteria_down_mbps) or 50,
|
||||
retry = -1,
|
||||
retry_interval = 5,
|
||||
recv_window_conn = (node.hysteria_recv_window_conn) and tonumber(node.hysteria_recv_window_conn) or nil,
|
||||
recv_window = (node.hysteria_recv_window) and tonumber(node.hysteria_recv_window) or nil,
|
||||
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery) and true or false,
|
||||
socks5 = (local_socks_address and local_socks_port) and {
|
||||
listen = local_socks_address .. ":" .. local_socks_port,
|
||||
timeout = 300,
|
||||
disable_udp = false,
|
||||
user = (local_socks_username and local_socks_password) and local_socks_username,
|
||||
password = (local_socks_username and local_socks_password) and local_socks_password,
|
||||
} or nil,
|
||||
http = (local_http_address and local_http_port) and {
|
||||
listen = local_http_address .. ":" .. local_http_port,
|
||||
timeout = 300,
|
||||
disable_udp = false,
|
||||
user = (local_http_username and local_http_password) and local_http_username,
|
||||
password = (local_http_username and local_http_password) and local_http_password,
|
||||
} or nil,
|
||||
tproxy_tcp = (local_tcp_redir_port) and {
|
||||
listen = "0.0.0.0:" .. local_tcp_redir_port,
|
||||
timeout = 300
|
||||
} or nil,
|
||||
tproxy_udp = (local_udp_redir_port) and {
|
||||
listen = "0.0.0.0:" .. local_udp_redir_port,
|
||||
timeout = 60
|
||||
} or nil
|
||||
}
|
||||
|
||||
print(jsonc.stringify(config, 1))
|
@ -0,0 +1,28 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local node_id = var["-node"]
|
||||
if not node_id then
|
||||
print("-node 不能为空")
|
||||
return
|
||||
end
|
||||
local node = uci:get_all("passwall", node_id)
|
||||
local run_type = var["-run_type"]
|
||||
local local_addr = var["-local_addr"]
|
||||
local local_port = var["-local_port"]
|
||||
local server_host = var["-server_host"] or node.address
|
||||
local server_port = var["-server_port"] or node.port
|
||||
|
||||
if api.is_ipv6(server_host) then
|
||||
server_host = api.get_ipv6_full(server_host)
|
||||
end
|
||||
local server = server_host .. ":" .. server_port
|
||||
|
||||
local config = {
|
||||
listen = run_type .. "://" .. local_addr .. ":" .. local_port,
|
||||
proxy = node.protocol .. "://" .. node.username .. ":" .. node.password .. "@" .. server
|
||||
}
|
||||
|
||||
print(jsonc.stringify(config, 1))
|
@ -0,0 +1,108 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local node_id = var["-node"]
|
||||
if not node_id then
|
||||
print("-node 不能为空")
|
||||
return
|
||||
end
|
||||
local node = uci:get_all("passwall", node_id)
|
||||
local server_host = var["-server_host"] or node.address
|
||||
local server_port = var["-server_port"] or node.port
|
||||
local local_addr = var["-local_addr"]
|
||||
local local_port = var["-local_port"]
|
||||
local mode = var["-mode"]
|
||||
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
|
||||
local local_socks_port = var["-local_socks_port"]
|
||||
local local_socks_username = var["-local_socks_username"]
|
||||
local local_socks_password = var["-local_socks_password"]
|
||||
local local_http_address = var["-local_http_address"] or "0.0.0.0"
|
||||
local local_http_port = var["-local_http_port"]
|
||||
local local_http_username = var["-local_http_username"]
|
||||
local local_http_password = var["-local_http_password"]
|
||||
local local_tcp_redir_port = var["-local_tcp_redir_port"]
|
||||
local local_tcp_redir_address = var["-local_tcp_redir_address"] or "0.0.0.0"
|
||||
local local_udp_redir_port = var["-local_udp_redir_port"]
|
||||
local local_udp_redir_address = var["-local_udp_redir_address"] or "0.0.0.0"
|
||||
|
||||
if api.is_ipv6(server_host) then
|
||||
server_host = api.get_ipv6_only(server_host)
|
||||
end
|
||||
local server = server_host
|
||||
|
||||
local config = {
|
||||
server = server,
|
||||
server_port = tonumber(server_port),
|
||||
local_address = local_addr,
|
||||
local_port = tonumber(local_port),
|
||||
password = node.password,
|
||||
method = node.method,
|
||||
timeout = tonumber(node.timeout),
|
||||
fast_open = (node.tcp_fast_open and node.tcp_fast_open == "true") and true or false,
|
||||
reuse_port = true,
|
||||
tcp_tproxy = var["-tcp_tproxy"] and true or nil
|
||||
}
|
||||
|
||||
if node.type == "SS" then
|
||||
if node.plugin and node.plugin ~= "none" then
|
||||
config.plugin = node.plugin
|
||||
config.plugin_opts = node.plugin_opts or nil
|
||||
end
|
||||
config.mode = mode
|
||||
elseif node.type == "SSR" then
|
||||
config.protocol = node.protocol
|
||||
config.protocol_param = node.protocol_param
|
||||
config.obfs = node.obfs
|
||||
config.obfs_param = node.obfs_param
|
||||
elseif node.type == "SS-Rust" then
|
||||
config = {
|
||||
servers = {
|
||||
{
|
||||
address = server,
|
||||
port = tonumber(server_port),
|
||||
method = node.method,
|
||||
password = node.password,
|
||||
timeout = tonumber(node.timeout),
|
||||
plugin = (node.plugin and node.plugin ~= "none") and node.plugin or nil,
|
||||
plugin_opts = (node.plugin and node.plugin ~= "none") and node.plugin_opts or nil
|
||||
}
|
||||
},
|
||||
locals = {},
|
||||
fast_open = (node.tcp_fast_open and node.tcp_fast_open == "true") and true or false
|
||||
}
|
||||
if local_socks_address and local_socks_port then
|
||||
table.insert(config.locals, {
|
||||
local_address = local_socks_address,
|
||||
local_port = tonumber(local_socks_port),
|
||||
mode = "tcp_and_udp"
|
||||
})
|
||||
end
|
||||
if local_http_address and local_http_port then
|
||||
table.insert(config.locals, {
|
||||
protocol = "http",
|
||||
local_address = local_http_address,
|
||||
local_port = tonumber(local_http_port)
|
||||
})
|
||||
end
|
||||
if local_tcp_redir_address and local_tcp_redir_port then
|
||||
table.insert(config.locals, {
|
||||
protocol = "redir",
|
||||
mode = "tcp_only",
|
||||
tcp_redir = var["-tcp_tproxy"] and "tproxy" or nil,
|
||||
local_address = local_tcp_redir_address,
|
||||
local_port = tonumber(local_tcp_redir_port)
|
||||
})
|
||||
end
|
||||
if local_udp_redir_address and local_udp_redir_port then
|
||||
table.insert(config.locals, {
|
||||
protocol = "redir",
|
||||
mode = "udp_only",
|
||||
local_address = local_udp_redir_address,
|
||||
local_port = tonumber(local_udp_redir_port)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
print(jsonc.stringify(config, 1))
|
@ -0,0 +1,86 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local uci = api.uci
|
||||
local json = api.jsonc
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local node_id = var["-node"]
|
||||
if not node_id then
|
||||
print("-node 不能为空")
|
||||
return
|
||||
end
|
||||
local node = uci:get_all("passwall", node_id)
|
||||
local run_type = var["-run_type"]
|
||||
local local_addr = var["-local_addr"]
|
||||
local local_port = var["-local_port"]
|
||||
local server_host = var["-server_host"] or node.address
|
||||
local server_port = var["-server_port"] or node.port
|
||||
local loglevel = var["-loglevel"] or 2
|
||||
local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA"
|
||||
local cipher13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"
|
||||
|
||||
if api.is_ipv6(server_host) then
|
||||
server_host = api.get_ipv6_only(server_host)
|
||||
end
|
||||
local server = server_host
|
||||
|
||||
local trojan = {
|
||||
run_type = run_type,
|
||||
local_addr = local_addr,
|
||||
local_port = tonumber(local_port),
|
||||
remote_addr = server,
|
||||
remote_port = tonumber(server_port),
|
||||
password = {node.password},
|
||||
log_level = tonumber(loglevel),
|
||||
ssl = {
|
||||
verify = (node.tls_allowInsecure ~= "1") and true or false,
|
||||
verify_hostname = true,
|
||||
cert = nil,
|
||||
cipher = cipher,
|
||||
cipher_tls13 = cipher13,
|
||||
sni = node.tls_serverName or server,
|
||||
alpn = {"h2", "http/1.1"},
|
||||
reuse_session = true,
|
||||
session_ticket = (node.tls_sessionTicket and node.tls_sessionTicket == "1") and true or false,
|
||||
curves = ""
|
||||
},
|
||||
udp_timeout = 60,
|
||||
tcp = {
|
||||
use_tproxy = (node.type == "Trojan-Plus" and var["-use_tproxy"]) and true or nil,
|
||||
no_delay = true,
|
||||
keep_alive = true,
|
||||
reuse_port = true,
|
||||
fast_open = (node.tcp_fast_open == "true") and true or false,
|
||||
fast_open_qlen = 20
|
||||
}
|
||||
}
|
||||
if node.type == "Trojan-Go" then
|
||||
trojan.ssl.cipher = nil
|
||||
trojan.ssl.cipher_tls13 = nil
|
||||
trojan.ssl.fingerprint = (node.fingerprint ~= "disable") and node.fingerprint or ""
|
||||
trojan.ssl.alpn = (node.trojan_transport == 'ws') and {} or {"h2", "http/1.1"}
|
||||
if node.tls ~= "1" and node.trojan_transport == "original" then trojan.ssl = nil end
|
||||
trojan.transport_plugin = ((not node.tls or node.tls ~= "1") and node.trojan_transport == "original") and {
|
||||
enabled = node.plugin_type ~= nil,
|
||||
type = node.plugin_type or "plaintext",
|
||||
command = node.plugin_type ~= "plaintext" and node.plugin_cmd or nil,
|
||||
option = node.plugin_type ~= "plaintext" and node.plugin_option or nil,
|
||||
arg = node.plugin_type ~= "plaintext" and { node.plugin_arg } or nil,
|
||||
env = {}
|
||||
} or nil
|
||||
trojan.websocket = (node.trojan_transport == 'ws') and {
|
||||
enabled = true,
|
||||
path = node.ws_path or "/",
|
||||
host = node.ws_host or (node.tls_serverName or server)
|
||||
} or nil
|
||||
trojan.shadowsocks = (node.ss_aead == "1") and {
|
||||
enabled = true,
|
||||
method = node.ss_aead_method or "aes_128_gcm",
|
||||
password = node.ss_aead_pwd or ""
|
||||
} or nil
|
||||
trojan.mux = (node.smux == "1") and {
|
||||
enabled = true,
|
||||
concurrency = tonumber(node.mux_concurrency),
|
||||
idle_timeout = tonumber(node.smux_idle_timeout)
|
||||
} or nil
|
||||
end
|
||||
print(json.stringify(trojan, 1))
|
709
luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua
Normal file
709
luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua
Normal file
@ -0,0 +1,709 @@
|
||||
module("luci.model.cbi.passwall.api.gen_v2ray", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local flag = var["-flag"]
|
||||
local node_id = var["-node"]
|
||||
local tcp_proxy_way = var["-tcp_proxy_way"] or "redirect"
|
||||
local tcp_redir_port = var["-tcp_redir_port"]
|
||||
local udp_redir_port = var["-udp_redir_port"]
|
||||
local sniffing = var["-sniffing"]
|
||||
local route_only = var["-route_only"]
|
||||
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
|
||||
local local_socks_port = var["-local_socks_port"]
|
||||
local local_socks_username = var["-local_socks_username"]
|
||||
local local_socks_password = var["-local_socks_password"]
|
||||
local local_http_address = var["-local_http_address"] or "0.0.0.0"
|
||||
local local_http_port = var["-local_http_port"]
|
||||
local local_http_username = var["-local_http_username"]
|
||||
local local_http_password = var["-local_http_password"]
|
||||
local dns_listen_port = var["-dns_listen_port"]
|
||||
local dns_server = var["-dns_server"]
|
||||
local dns_tcp_server = var["-dns_tcp_server"]
|
||||
local dns_cache = var["-dns_cache"]
|
||||
local doh_url = var["-doh_url"]
|
||||
local doh_host = var["-doh_host"]
|
||||
local dns_client_ip = var["-dns_client_ip"]
|
||||
local dns_query_strategy = var["-dns_query_strategy"]
|
||||
local dns_socks_address = var["-dns_socks_address"]
|
||||
local dns_socks_port = var["-dns_socks_port"]
|
||||
local dns_fakedns = var["-dns_fakedns"]
|
||||
local loglevel = var["-loglevel"] or "warning"
|
||||
local new_port
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local jsonc = api.jsonc
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local dns = nil
|
||||
local fakedns = nil
|
||||
local inbounds = {}
|
||||
local outbounds = {}
|
||||
local routing = nil
|
||||
|
||||
local function get_new_port()
|
||||
if new_port then
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1)))
|
||||
else
|
||||
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname)))
|
||||
end
|
||||
return new_port
|
||||
end
|
||||
|
||||
local function get_domain_excluded()
|
||||
local path = string.format("/usr/share/%s/rules/domains_excluded", appname)
|
||||
local content = fs.readfile(path)
|
||||
if not content then return nil end
|
||||
local hosts = {}
|
||||
string.gsub(content, '[^' .. "\n" .. ']+', function(w)
|
||||
local s = w:gsub("^%s*(.-)%s*$", "%1") -- Trim
|
||||
if s == "" then return end
|
||||
if s:find("#") and s:find("#") == 1 then return end
|
||||
if not s:find("#") or s:find("#") ~= 1 then table.insert(hosts, s) end
|
||||
end)
|
||||
if #hosts == 0 then hosts = nil end
|
||||
return hosts
|
||||
end
|
||||
|
||||
function gen_outbound(node, tag, proxy_table)
|
||||
local proxy = 0
|
||||
local proxy_tag = "nil"
|
||||
if proxy_table ~= nil and type(proxy_table) == "table" then
|
||||
proxy = proxy_table.proxy or 0
|
||||
proxy_tag = proxy_table.tag or "nil"
|
||||
end
|
||||
local result = nil
|
||||
if node and node ~= "nil" then
|
||||
local node_id = node[".name"]
|
||||
if tag == nil then
|
||||
tag = node_id
|
||||
end
|
||||
|
||||
if node.type == "V2ray" or node.type == "Xray" then
|
||||
proxy = 0
|
||||
if proxy_tag ~= "nil" then
|
||||
node.proxySettings = {
|
||||
tag = proxy_tag,
|
||||
transportLayer = true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if node.type ~= "V2ray" and node.type ~= "Xray" then
|
||||
if node.type == "Socks" then
|
||||
node.protocol = "socks"
|
||||
node.transport = "tcp"
|
||||
else
|
||||
local relay_port = node.port
|
||||
new_port = get_new_port()
|
||||
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
|
||||
appname,
|
||||
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
|
||||
new_port, --flag
|
||||
node_id, --node
|
||||
"127.0.0.1", --bind
|
||||
new_port, --socks port
|
||||
string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port), --config file
|
||||
(proxy == 1 and proxy_tag ~= "nil" and relay_port) and tostring(relay_port) or "" --relay port
|
||||
)
|
||||
)
|
||||
)
|
||||
node = {}
|
||||
node.protocol = "socks"
|
||||
node.transport = "tcp"
|
||||
node.address = "127.0.0.1"
|
||||
node.port = new_port
|
||||
end
|
||||
node.stream_security = "none"
|
||||
else
|
||||
if node.tls and node.tls == "1" then
|
||||
node.stream_security = "tls"
|
||||
if node.type == "Xray" and node.xtls and node.xtls == "1" then
|
||||
node.stream_security = "xtls"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result = {
|
||||
_flag_tag = node_id,
|
||||
_flag_proxy = proxy,
|
||||
_flag_proxy_tag = proxy_tag,
|
||||
tag = tag,
|
||||
proxySettings = node.proxySettings or nil,
|
||||
protocol = node.protocol,
|
||||
mux = (node.stream_security ~= "xtls") and {
|
||||
enabled = (node.mux == "1") and true or false,
|
||||
concurrency = (node.mux_concurrency) and tonumber(node.mux_concurrency) or 8
|
||||
} or nil,
|
||||
-- 底层传输配置
|
||||
streamSettings = (node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
|
||||
network = node.transport,
|
||||
security = node.stream_security,
|
||||
xtlsSettings = (node.stream_security == "xtls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false
|
||||
} or nil,
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
allowInsecure = (node.tls_allowInsecure == "1") and true or false,
|
||||
fingerprint = (node.type == "Xray" and node.fingerprint and node.fingerprint ~= "disable") and node.fingerprint or nil
|
||||
} or nil,
|
||||
tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and {
|
||||
header = {
|
||||
type = node.tcp_guise or "none",
|
||||
request = (node.tcp_guise == "http") and {
|
||||
path = node.tcp_guise_http_path or {"/"},
|
||||
headers = {
|
||||
Host = node.tcp_guise_http_host or {}
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
} or nil,
|
||||
kcpSettings = (node.transport == "mkcp") and {
|
||||
mtu = tonumber(node.mkcp_mtu),
|
||||
tti = tonumber(node.mkcp_tti),
|
||||
uplinkCapacity = tonumber(node.mkcp_uplinkCapacity),
|
||||
downlinkCapacity = tonumber(node.mkcp_downlinkCapacity),
|
||||
congestion = (node.mkcp_congestion == "1") and true or false,
|
||||
readBufferSize = tonumber(node.mkcp_readBufferSize),
|
||||
writeBufferSize = tonumber(node.mkcp_writeBufferSize),
|
||||
seed = (node.mkcp_seed and node.mkcp_seed ~= "") and node.mkcp_seed or nil,
|
||||
header = {type = node.mkcp_guise}
|
||||
} or nil,
|
||||
wsSettings = (node.transport == "ws") and {
|
||||
path = node.ws_path or "",
|
||||
headers = (node.ws_host ~= nil) and
|
||||
{Host = node.ws_host} or nil,
|
||||
maxEarlyData = tonumber(node.ws_maxEarlyData) or nil
|
||||
} or nil,
|
||||
httpSettings = (node.transport == "h2") and {
|
||||
path = node.h2_path,
|
||||
host = node.h2_host,
|
||||
read_idle_timeout = tonumber(node.h2_read_idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(node.h2_health_check_timeout) or nil
|
||||
} or nil,
|
||||
dsSettings = (node.transport == "ds") and
|
||||
{path = node.ds_path} or nil,
|
||||
quicSettings = (node.transport == "quic") and {
|
||||
security = node.quic_security,
|
||||
key = node.quic_key,
|
||||
header = {type = node.quic_guise}
|
||||
} or nil,
|
||||
grpcSettings = (node.transport == "grpc") and {
|
||||
serviceName = node.grpc_serviceName,
|
||||
multiMode = (node.grpc_mode == "multi") and true or nil,
|
||||
idle_timeout = tonumber(node.grpc_idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(node.grpc_health_check_timeout) or nil,
|
||||
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or nil,
|
||||
initial_windows_size = tonumber(node.grpc_initial_windows_size) or nil
|
||||
} or nil
|
||||
} or nil,
|
||||
settings = {
|
||||
vnext = (node.protocol == "vmess" or node.protocol == "vless") and {
|
||||
{
|
||||
address = node.address,
|
||||
port = tonumber(node.port),
|
||||
users = {
|
||||
{
|
||||
id = node.uuid,
|
||||
level = 0,
|
||||
security = (node.protocol == "vmess") and node.security or nil,
|
||||
encryption = node.encryption or "none",
|
||||
flow = node.flow or nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
servers = (node.protocol == "socks" or node.protocol == "http" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
|
||||
{
|
||||
address = node.address,
|
||||
port = tonumber(node.port),
|
||||
method = node.method or nil,
|
||||
flow = node.flow or nil,
|
||||
ivCheck = (node.protocol == "shadowsocks") and node.iv_check == "1" or nil,
|
||||
password = node.password or "",
|
||||
users = (node.username and node.password) and {
|
||||
{
|
||||
user = node.username,
|
||||
pass = node.password
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
local alpn = {}
|
||||
if node.alpn and node.alpn ~= "default" then
|
||||
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
|
||||
table.insert(alpn, w)
|
||||
end)
|
||||
end
|
||||
if alpn and #alpn > 0 then
|
||||
if result.streamSettings.tlsSettings then
|
||||
result.streamSettings.tlsSettings.alpn = alpn
|
||||
end
|
||||
if result.streamSettings.xtlsSettings then
|
||||
result.streamSettings.xtlsSettings.alpn = alpn
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
if node_id then
|
||||
local node = uci:get_all(appname, node_id)
|
||||
if local_socks_port then
|
||||
local inbound = {
|
||||
listen = local_socks_address,
|
||||
port = tonumber(local_socks_port),
|
||||
protocol = "socks",
|
||||
settings = {auth = "noauth", udp = true},
|
||||
sniffing = {enabled = true, destOverride = {"http", "tls"}}
|
||||
}
|
||||
if local_socks_username and local_socks_password and local_socks_username ~= "" and local_socks_password ~= "" then
|
||||
inbound.settings.auth = "password"
|
||||
inbound.settings.accounts = {
|
||||
{
|
||||
user = local_socks_username,
|
||||
pass = local_socks_password
|
||||
}
|
||||
}
|
||||
end
|
||||
table.insert(inbounds, inbound)
|
||||
end
|
||||
if local_http_port then
|
||||
local inbound = {
|
||||
listen = local_http_address,
|
||||
port = tonumber(local_http_port),
|
||||
protocol = "http",
|
||||
settings = {allowTransparent = false}
|
||||
}
|
||||
if local_http_username and local_http_password and local_http_username ~= "" and local_http_password ~= "" then
|
||||
inbound.settings.accounts = {
|
||||
{
|
||||
user = local_http_username,
|
||||
pass = local_http_password
|
||||
}
|
||||
}
|
||||
end
|
||||
table.insert(inbounds, inbound)
|
||||
end
|
||||
|
||||
if tcp_redir_port then
|
||||
table.insert(inbounds, {
|
||||
port = tonumber(tcp_redir_port),
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = "tcp", followRedirect = true},
|
||||
streamSettings = {sockopt = {tproxy = tcp_proxy_way}},
|
||||
sniffing = {enabled = sniffing and true or false, destOverride = {"http", "tls", (dns_fakedns) and "fakedns"}, metadataOnly = false, routeOnly = route_only and true or nil, domainsExcluded = (sniffing and not route_only) and get_domain_excluded() or nil}
|
||||
})
|
||||
end
|
||||
|
||||
if udp_redir_port then
|
||||
table.insert(inbounds, {
|
||||
port = tonumber(udp_redir_port),
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = "udp", followRedirect = true},
|
||||
streamSettings = {sockopt = {tproxy = "tproxy"}},
|
||||
sniffing = {enabled = sniffing and true or false, destOverride = {"http", "tls", (dns_fakedns) and "fakedns"}, metadataOnly = false, routeOnly = route_only and true or nil, domainsExcluded = (sniffing and not route_only) and get_domain_excluded() or nil}
|
||||
})
|
||||
end
|
||||
|
||||
local up_trust_doh = uci:get(appname, "@global[0]", "up_trust_doh")
|
||||
if up_trust_doh then
|
||||
local t = {}
|
||||
string.gsub(up_trust_doh, '[^' .. "," .. ']+', function (w)
|
||||
table.insert(t, w)
|
||||
end)
|
||||
if #t > 1 then
|
||||
local host = sys.exec("echo -n $(echo " .. t[1] .. " | sed 's/https:\\/\\///g' | awk -F ':' '{print $1}' | awk -F '/' '{print $1}')")
|
||||
dns = {
|
||||
hosts = {
|
||||
[host] = t[2]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if node.protocol == "_shunt" then
|
||||
table.insert(outbounds, {
|
||||
protocol = "freedom",
|
||||
tag = "direct",
|
||||
settings = {
|
||||
domainStrategy = "UseIPv4"
|
||||
},
|
||||
streamSettings = {
|
||||
sockopt = {
|
||||
mark = 255
|
||||
}
|
||||
}
|
||||
})
|
||||
table.insert(outbounds, {
|
||||
protocol = "blackhole",
|
||||
tag = "blackhole"
|
||||
})
|
||||
local rules = {}
|
||||
|
||||
local default_node_id = node.default_node or "_direct"
|
||||
local default_outboundTag
|
||||
if default_node_id == "_direct" then
|
||||
default_outboundTag = "direct"
|
||||
elseif default_node_id == "_blackhole" then
|
||||
default_outboundTag = "blackhole"
|
||||
else
|
||||
local default_node = uci:get_all(appname, default_node_id)
|
||||
local main_node_id = node.main_node or "nil"
|
||||
local proxy = 0
|
||||
local proxy_tag
|
||||
if main_node_id ~= "nil" then
|
||||
local main_node = uci:get_all(appname, main_node_id)
|
||||
if main_node and api.is_normal_node(main_node) and main_node_id ~= default_node_id then
|
||||
local main_node_outbound = gen_outbound(main_node, "main")
|
||||
if main_node_outbound then
|
||||
table.insert(outbounds, main_node_outbound)
|
||||
proxy = 1
|
||||
proxy_tag = "main"
|
||||
if default_node.type ~= "V2ray" and default_node.type ~= "Xray" then
|
||||
proxy_tag = nil
|
||||
new_port = get_new_port()
|
||||
table.insert(inbounds, {
|
||||
tag = "proxy_default",
|
||||
listen = "127.0.0.1",
|
||||
port = new_port,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = "tcp,udp", address = default_node.address, port = tonumber(default_node.port)}
|
||||
})
|
||||
if default_node.tls_serverName == nil then
|
||||
default_node.tls_serverName = default_node.address
|
||||
end
|
||||
default_node.address = "127.0.0.1"
|
||||
default_node.port = new_port
|
||||
table.insert(rules, 1, {
|
||||
type = "field",
|
||||
inboundTag = {"proxy_default"},
|
||||
outboundTag = "main"
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if default_node and api.is_normal_node(default_node) then
|
||||
local default_outbound = gen_outbound(default_node, "default", { proxy = proxy, tag = proxy_tag })
|
||||
if default_outbound then
|
||||
table.insert(outbounds, default_outbound)
|
||||
default_outboundTag = "default"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
local name = e[".name"]
|
||||
if name and e.remarks then
|
||||
local _node_id = node[name] or "nil"
|
||||
local proxy_tag = node[name .. "_proxy_tag"] or "nil"
|
||||
local outboundTag
|
||||
if _node_id == "_direct" then
|
||||
outboundTag = "direct"
|
||||
elseif _node_id == "_blackhole" then
|
||||
outboundTag = "blackhole"
|
||||
elseif _node_id == "_default" then
|
||||
outboundTag = "default"
|
||||
else
|
||||
if _node_id ~= "nil" then
|
||||
local _node = uci:get_all(appname, _node_id)
|
||||
if _node and api.is_normal_node(_node) then
|
||||
local new_outbound
|
||||
for index, value in ipairs(outbounds) do
|
||||
if value["_flag_tag"] == _node_id and value["_flag_proxy_tag"] == proxy_tag then
|
||||
new_outbound = api.clone(value)
|
||||
break
|
||||
end
|
||||
end
|
||||
if new_outbound then
|
||||
new_outbound["tag"] = name
|
||||
table.insert(outbounds, new_outbound)
|
||||
outboundTag = name
|
||||
else
|
||||
if _node.type ~= "V2ray" and _node.type ~= "Xray" then
|
||||
if proxy_tag ~= "nil" then
|
||||
new_port = get_new_port()
|
||||
table.insert(inbounds, {
|
||||
tag = "proxy_" .. name,
|
||||
listen = "127.0.0.1",
|
||||
port = new_port,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {network = "tcp,udp", address = _node.address, port = tonumber(_node.port)}
|
||||
})
|
||||
if _node.tls_serverName == nil then
|
||||
_node.tls_serverName = _node.address
|
||||
end
|
||||
_node.address = "127.0.0.1"
|
||||
_node.port = new_port
|
||||
table.insert(rules, 1, {
|
||||
type = "field",
|
||||
inboundTag = {"proxy_" .. name},
|
||||
outboundTag = proxy_tag
|
||||
})
|
||||
end
|
||||
end
|
||||
local _outbound = gen_outbound(_node, name, { proxy = (proxy_tag ~= "nil") and 1 or 0, tag = (proxy_tag ~= "nil") and proxy_tag or nil })
|
||||
if _outbound then
|
||||
table.insert(outbounds, _outbound)
|
||||
outboundTag = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if outboundTag then
|
||||
if outboundTag == "default" then
|
||||
outboundTag = default_outboundTag
|
||||
end
|
||||
local protocols = nil
|
||||
if e["protocol"] and e["protocol"] ~= "" then
|
||||
protocols = {}
|
||||
string.gsub(e["protocol"], '[^' .. " " .. ']+', function(w)
|
||||
table.insert(protocols, w)
|
||||
end)
|
||||
end
|
||||
if e.domain_list then
|
||||
local _domain = {}
|
||||
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_domain, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
domain = _domain,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
if e.ip_list then
|
||||
local _ip = {}
|
||||
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_ip, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
ip = _ip,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
if not e.domain_list and not e.ip_list and protocols then
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if default_outboundTag then
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = default_outboundTag,
|
||||
network = "tcp,udp"
|
||||
})
|
||||
end
|
||||
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
domainMatcher = node.domainMatcher or "hybrid",
|
||||
rules = rules
|
||||
}
|
||||
elseif node.protocol == "_balancing" then
|
||||
if node.balancing_node then
|
||||
local nodes = node.balancing_node
|
||||
local length = #nodes
|
||||
for i = 1, length do
|
||||
local node = uci:get_all(appname, nodes[i])
|
||||
local outbound = gen_outbound(node)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
domainMatcher = node.domainMatcher or "hybrid",
|
||||
balancers = {{tag = "balancer", selector = nodes}},
|
||||
rules = {
|
||||
{type = "field", network = "tcp,udp", balancerTag = "balancer"}
|
||||
}
|
||||
}
|
||||
end
|
||||
else
|
||||
local outbound = gen_outbound(node)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
routing = {
|
||||
domainStrategy = "AsIs",
|
||||
domainMatcher = "hybrid",
|
||||
rules = {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if dns_server or dns_fakedns then
|
||||
table.insert(outbounds, {
|
||||
protocol = "dns",
|
||||
tag = "dns-out"
|
||||
})
|
||||
local rules = {}
|
||||
|
||||
dns = {
|
||||
tag = "dns-in1",
|
||||
disableCache = (dns_cache and dns_cache == "0") and true or false,
|
||||
servers = {
|
||||
dns_server
|
||||
},
|
||||
clientIp = (dns_client_ip and dns_client_ip ~= "") and dns_client_ip or nil,
|
||||
queryStrategy = (dns_query_strategy and dns_query_strategy ~= "") and dns_query_strategy or nil
|
||||
}
|
||||
if doh_url and doh_host then
|
||||
dns.hosts = {
|
||||
[doh_host] = dns_server
|
||||
}
|
||||
if not tcp_redir_port and not dns_socks_port then
|
||||
doh_url = doh_url:gsub("https://", "https+local://")
|
||||
end
|
||||
dns.servers = {
|
||||
doh_url
|
||||
}
|
||||
end
|
||||
|
||||
if dns_tcp_server then
|
||||
if not tcp_redir_port and not dns_socks_port then
|
||||
dns_tcp_server = dns_tcp_server:gsub("tcp://", "tcp+local://")
|
||||
end
|
||||
dns.servers = {
|
||||
dns_tcp_server
|
||||
}
|
||||
end
|
||||
|
||||
if dns_fakedns then
|
||||
fakedns = {}
|
||||
fakedns[#fakedns + 1] = {
|
||||
ipPool = "198.18.0.0/16",
|
||||
poolSize = 65535
|
||||
}
|
||||
dns_server = "1.1.1.1"
|
||||
dns.servers = {
|
||||
"fakedns"
|
||||
}
|
||||
end
|
||||
|
||||
if dns_listen_port then
|
||||
table.insert(inbounds, {
|
||||
listen = "127.0.0.1",
|
||||
port = tonumber(dns_listen_port),
|
||||
protocol = "dokodemo-door",
|
||||
tag = "dns-in",
|
||||
settings = {
|
||||
address = dns_server,
|
||||
port = 53,
|
||||
network = "tcp,udp"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
inboundTag = {
|
||||
"dns-in"
|
||||
},
|
||||
outboundTag = "dns-out"
|
||||
})
|
||||
|
||||
if dns_socks_address and dns_socks_port then
|
||||
table.insert(outbounds, 1, {
|
||||
tag = "out",
|
||||
protocol = "socks",
|
||||
streamSettings = {
|
||||
network = "tcp",
|
||||
security = "none"
|
||||
},
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = dns_socks_address,
|
||||
port = tonumber(dns_socks_port)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
local outboundTag = "out"
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
inboundTag = {
|
||||
"dns-in1"
|
||||
},
|
||||
outboundTag = outboundTag
|
||||
})
|
||||
end
|
||||
|
||||
if node_id and tcp_redir_port and not dns_fakedns then
|
||||
local outboundTag = node_id
|
||||
local node = uci:get_all(appname, node_id)
|
||||
if node.protocol == "_shunt" then
|
||||
outboundTag = "default"
|
||||
end
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
inboundTag = {
|
||||
"dns-in1"
|
||||
},
|
||||
outboundTag = outboundTag
|
||||
})
|
||||
end
|
||||
|
||||
if not routing then
|
||||
routing = {
|
||||
domainStrategy = "IPOnDemand",
|
||||
rules = rules
|
||||
}
|
||||
else
|
||||
for index, value in ipairs(rules) do
|
||||
table.insert(routing.rules, 1, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if inbounds or outbounds then
|
||||
local config = {
|
||||
log = {
|
||||
-- error = string.format("/tmp/etc/%s/%s.log", appname, node[".name"]),
|
||||
loglevel = loglevel
|
||||
},
|
||||
-- DNS
|
||||
dns = dns,
|
||||
fakedns = fakedns,
|
||||
-- 传入连接
|
||||
inbounds = inbounds,
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
-- 路由
|
||||
routing = routing,
|
||||
-- 本地策略
|
||||
--[[
|
||||
policy = {
|
||||
levels = {
|
||||
[0] = {
|
||||
handshake = 4,
|
||||
connIdle = 300,
|
||||
uplinkOnly = 2,
|
||||
downlinkOnly = 5,
|
||||
bufferSize = 10240,
|
||||
statsUserUplink = false,
|
||||
statsUserDownlink = false
|
||||
}
|
||||
},
|
||||
system = {
|
||||
statsInboundUplink = false,
|
||||
statsInboundDownlink = false
|
||||
}
|
||||
}
|
||||
]]--
|
||||
}
|
||||
print(jsonc.stringify(config, 1))
|
||||
end
|
@ -0,0 +1,111 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local jsonc = api.jsonc
|
||||
local inbounds = {}
|
||||
local outbounds = {}
|
||||
local routing = nil
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
|
||||
local local_socks_port = var["-local_socks_port"]
|
||||
local local_socks_username = var["-local_socks_username"]
|
||||
local local_socks_password = var["-local_socks_password"]
|
||||
local local_http_address = var["-local_http_address"] or "0.0.0.0"
|
||||
local local_http_port = var["-local_http_port"]
|
||||
local local_http_username = var["-local_http_username"]
|
||||
local local_http_password = var["-local_http_password"]
|
||||
local server_proto = var["-server_proto"]
|
||||
local server_address = var["-server_address"]
|
||||
local server_port = var["-server_port"]
|
||||
local server_username = var["-server_username"]
|
||||
local server_password = var["-server_password"]
|
||||
|
||||
function gen_outbound(proto, address, port, username, password)
|
||||
local result = {
|
||||
protocol = proto,
|
||||
streamSettings = {
|
||||
network = "tcp",
|
||||
security = "none"
|
||||
},
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = address,
|
||||
port = tonumber(port),
|
||||
users = (username and password) and {
|
||||
{
|
||||
user = username,
|
||||
pass = password
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
if local_socks_address and local_socks_port then
|
||||
local inbound = {
|
||||
listen = local_socks_address,
|
||||
port = tonumber(local_socks_port),
|
||||
protocol = "socks",
|
||||
settings = {
|
||||
udp = true,
|
||||
auth = "noauth"
|
||||
}
|
||||
}
|
||||
if local_socks_username and local_socks_password and local_socks_username ~= "" and local_socks_password ~= "" then
|
||||
inbound.settings.auth = "password"
|
||||
inbound.settings.accounts = {
|
||||
{
|
||||
user = local_socks_username,
|
||||
pass = local_socks_password
|
||||
}
|
||||
}
|
||||
end
|
||||
table.insert(inbounds, inbound)
|
||||
end
|
||||
|
||||
if local_http_address and local_http_port then
|
||||
local inbound = {
|
||||
listen = local_http_address,
|
||||
port = tonumber(local_http_port),
|
||||
protocol = "http",
|
||||
settings = {
|
||||
allowTransparent = false
|
||||
}
|
||||
}
|
||||
if local_http_username and local_http_password and local_http_username ~= "" and local_http_password ~= "" then
|
||||
inbound.settings.accounts = {
|
||||
{
|
||||
user = local_http_username,
|
||||
pass = local_http_password
|
||||
}
|
||||
}
|
||||
end
|
||||
table.insert(inbounds, inbound)
|
||||
end
|
||||
|
||||
if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then
|
||||
local outbound = gen_outbound(server_proto, server_address, server_port, server_username, server_password)
|
||||
if outbound then table.insert(outbounds, outbound) end
|
||||
end
|
||||
|
||||
-- 额外传出连接
|
||||
table.insert(outbounds, {
|
||||
protocol = "freedom", tag = "direct", settings = {keep = ""}
|
||||
})
|
||||
|
||||
local config = {
|
||||
log = {
|
||||
-- error = string.format("/tmp/etc/passwall/%s.log", node[".name"]),
|
||||
loglevel = "warning"
|
||||
},
|
||||
-- 传入连接
|
||||
inbounds = inbounds,
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
-- 路由
|
||||
routing = routing
|
||||
}
|
||||
print(jsonc.stringify(config, 1))
|
134
luci-app-passwall/luasrc/model/cbi/passwall/api/hysteria.lua
Normal file
134
luci-app-passwall/luasrc/model/cbi/passwall/api/hysteria.lua
Normal file
@ -0,0 +1,134 @@
|
||||
module("luci.model.cbi.passwall.api.hysteria", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local util = api.util
|
||||
local i18n = api.i18n
|
||||
|
||||
local pre_release_url = "https://api.github.com/repos/HyNetwork/hysteria/releases?per_page=1"
|
||||
local release_url = "https://api.github.com/repos/HyNetwork/hysteria/releases/latest"
|
||||
local api_url = release_url
|
||||
local app_path = api.get_hysteria_path() or ""
|
||||
|
||||
function check_path()
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "hysteria")
|
||||
}
|
||||
end
|
||||
return {
|
||||
code = 0
|
||||
}
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
return api.common_to_check(api_url, api.get_hysteria_version(), "linux%-" .. file_tree .. sub_version)
|
||||
end
|
||||
|
||||
function to_download(url, size)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/hysteria_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t hysteria_download.XXXXXX"))
|
||||
|
||||
if size then
|
||||
local kb1 = api.get_free_space("/tmp")
|
||||
if tonumber(size) > tonumber(kb1) then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.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 to_move(file)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
sys.call("/bin/rm -rf /tmp/hysteria_download.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local new_version = api.get_hysteria_version(file)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/hysteria_download.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local flag = sys.call('pgrep -af "passwall/.*hysteria" >/dev/null')
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
end
|
||||
|
||||
local old_app_size = 0
|
||||
if fs.access(app_path) then
|
||||
old_app_size = api.get_file_space(app_path)
|
||||
end
|
||||
local new_app_size = api.get_file_space(file)
|
||||
local final_dir = api.get_final_dir(app_path)
|
||||
local final_dir_free_size = api.get_free_space(final_dir)
|
||||
if final_dir_free_size > 0 then
|
||||
final_dir_free_size = final_dir_free_size + old_app_size
|
||||
if new_app_size > final_dir_free_size then
|
||||
sys.call("/bin/rm -rf /tmp/hysteria_download.*")
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/hysteria_download.*")
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0}
|
||||
end
|
183
luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua
Normal file
183
luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua
Normal file
@ -0,0 +1,183 @@
|
||||
module("luci.model.cbi.passwall.api.trojan_go", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local util = api.util
|
||||
local i18n = api.i18n
|
||||
|
||||
local pre_release_url = "https://api.github.com/repos/p4gefau1t/trojan-go/releases?per_page=1"
|
||||
local release_url = "https://api.github.com/repos/p4gefau1t/trojan-go/releases/latest"
|
||||
local api_url = release_url
|
||||
local app_path = api.get_trojan_go_path() or ""
|
||||
|
||||
function check_path()
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO")
|
||||
}
|
||||
end
|
||||
return {
|
||||
code = 0
|
||||
}
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
if file_tree == "mips" then file_tree = "mips%-hardfloat" end
|
||||
if file_tree == "mipsle" then file_tree = "mipsle%-hardfloat" end
|
||||
if file_tree == "arm64" then
|
||||
file_tree = "armv8"
|
||||
else
|
||||
if sub_version and sub_version:match("^[5-8]$") then file_tree = file_tree .. "v" .. sub_version end
|
||||
end
|
||||
|
||||
return api.common_to_check(api_url, api.get_trojan_go_version(), "linux%-" .. file_tree .. "%.zip")
|
||||
end
|
||||
|
||||
function to_download(url, size)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/trojan-go_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t trojan-go_download.XXXXXX"))
|
||||
|
||||
if size then
|
||||
local kb1 = api.get_free_space("/tmp")
|
||||
if tonumber(size) > tonumber(kb1) then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.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 to_extract(file, subfix)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
|
||||
local new_file_size = api.get_file_space(file)
|
||||
local tmp_free_size = api.get_free_space("/tmp")
|
||||
if tmp_free_size <= 0 or tmp_free_size <= new_file_size then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
|
||||
local tmp_dir = util.trim(util.exec("mktemp -d -t trojan-go_extract.XXXXXX"))
|
||||
|
||||
local output = {}
|
||||
api.exec("/usr/bin/unzip", {"-o", file, "-d", tmp_dir},
|
||||
function(chunk) output[#output + 1] = chunk end)
|
||||
|
||||
local files = util.split(table.concat(output))
|
||||
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
|
||||
return {code = 0, file = tmp_dir}
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local bin_path = file .. "/trojan-go"
|
||||
|
||||
local new_version = api.get_trojan_go_version(bin_path)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local flag = sys.call('pgrep -af "passwall/.*trojan-go" >/dev/null')
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
end
|
||||
|
||||
local old_app_size = 0
|
||||
if fs.access(app_path) then
|
||||
old_app_size = api.get_file_space(app_path)
|
||||
end
|
||||
local new_app_size = api.get_file_space(bin_path)
|
||||
local final_dir = api.get_final_dir(app_path)
|
||||
local final_dir_free_size = api.get_free_space(final_dir)
|
||||
if final_dir_free_size > 0 then
|
||||
final_dir_free_size = final_dir_free_size + old_app_size
|
||||
if new_app_size > final_dir_free_size then
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec("/bin/mv", { "-f", bin_path, app_path }, nil, api.command_timeout) == 0
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/trojan-go_extract.*")
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0}
|
||||
end
|
181
luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua
Normal file
181
luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua
Normal file
@ -0,0 +1,181 @@
|
||||
module("luci.model.cbi.passwall.api.v2ray", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local util = api.util
|
||||
local i18n = api.i18n
|
||||
|
||||
local pre_release_url = "https://api.github.com/repos/v2fly/v2ray-core/releases?per_page=1"
|
||||
local release_url = "https://api.github.com/repos/v2fly/v2ray-core/releases/latest"
|
||||
local api_url = release_url
|
||||
local app_path = api.get_v2ray_path() or ""
|
||||
|
||||
function check_path()
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray")
|
||||
}
|
||||
end
|
||||
return {
|
||||
code = 0
|
||||
}
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
if file_tree == "amd64" then file_tree = "64" end
|
||||
if file_tree == "386" then file_tree = "32" end
|
||||
if file_tree == "mipsle" then file_tree = "mips32le" end
|
||||
if file_tree == "mips" then file_tree = "mips32" end
|
||||
if file_tree == "arm" then file_tree = "arm32" end
|
||||
|
||||
return api.common_to_check(api_url, api.get_v2ray_version(), "linux%-" .. file_tree .. (sub_version ~= "" and ".+" .. sub_version or ""))
|
||||
end
|
||||
|
||||
function to_download(url, size)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/v2ray_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t v2ray_download.XXXXXX"))
|
||||
|
||||
if size then
|
||||
local kb1 = api.get_free_space("/tmp")
|
||||
if tonumber(size) > tonumber(kb1) then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.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 to_extract(file, subfix)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
|
||||
local new_file_size = api.get_file_space(file)
|
||||
local tmp_free_size = api.get_free_space("/tmp")
|
||||
if tmp_free_size <= 0 or tmp_free_size <= new_file_size then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
|
||||
local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX"))
|
||||
|
||||
local output = {}
|
||||
api.exec("/usr/bin/unzip", {"-o", file, "v2ray", "-d", tmp_dir},
|
||||
function(chunk) output[#output + 1] = chunk end)
|
||||
|
||||
local files = util.split(table.concat(output))
|
||||
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
|
||||
return {code = 0, file = tmp_dir}
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local bin_path = file .. "/v2ray"
|
||||
|
||||
local new_version = api.get_v2ray_version(bin_path)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local flag = sys.call('pgrep -af "passwall/.*v2ray" >/dev/null')
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
end
|
||||
|
||||
local old_app_size = 0
|
||||
if fs.access(app_path) then
|
||||
old_app_size = api.get_file_space(app_path)
|
||||
end
|
||||
local new_app_size = api.get_file_space(bin_path)
|
||||
local final_dir = api.get_final_dir(app_path)
|
||||
local final_dir_free_size = api.get_free_space(final_dir)
|
||||
if final_dir_free_size > 0 then
|
||||
final_dir_free_size = final_dir_free_size + old_app_size
|
||||
if new_app_size > final_dir_free_size then
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec("/bin/mv", { "-f", bin_path, app_path }, nil, api.command_timeout) == 0
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0}
|
||||
end
|
181
luci-app-passwall/luasrc/model/cbi/passwall/api/xray.lua
Normal file
181
luci-app-passwall/luasrc/model/cbi/passwall/api/xray.lua
Normal file
@ -0,0 +1,181 @@
|
||||
module("luci.model.cbi.passwall.api.xray", package.seeall)
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local util = api.util
|
||||
local i18n = api.i18n
|
||||
|
||||
local pre_release_url = "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=1"
|
||||
local release_url = "https://api.github.com/repos/XTLS/Xray-core/releases/latest"
|
||||
local api_url = release_url
|
||||
local app_path = api.get_xray_path() or ""
|
||||
|
||||
function check_path()
|
||||
if app_path == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray")
|
||||
}
|
||||
end
|
||||
return {
|
||||
code = 0
|
||||
}
|
||||
end
|
||||
|
||||
function to_check(arch)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not arch or arch == "" then arch = api.auto_get_arch() end
|
||||
|
||||
local file_tree, sub_version = api.get_file_info(arch)
|
||||
|
||||
if file_tree == "" then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
|
||||
}
|
||||
end
|
||||
|
||||
if file_tree == "amd64" then file_tree = "64" end
|
||||
if file_tree == "386" then file_tree = "32" end
|
||||
if file_tree == "mipsle" then file_tree = "mips32le" end
|
||||
if file_tree == "mips" then file_tree = "mips32" end
|
||||
if file_tree == "arm" then file_tree = "arm32" end
|
||||
|
||||
return api.common_to_check(api_url, api.get_xray_version(), "linux%-" .. file_tree .. (sub_version ~= "" and ".+" .. sub_version or ""))
|
||||
end
|
||||
|
||||
function to_download(url, size)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not url or url == "" then
|
||||
return {code = 1, error = i18n.translate("Download url is required.")}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -f /tmp/xray_download.*")
|
||||
|
||||
local tmp_file = util.trim(util.exec("mktemp -u -t xray_download.XXXXXX"))
|
||||
|
||||
if size then
|
||||
local kb1 = api.get_free_space("/tmp")
|
||||
if tonumber(size) > tonumber(kb1) then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0
|
||||
|
||||
if not result then
|
||||
api.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 to_extract(file, subfix)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" or not fs.access(file) then
|
||||
return {code = 1, error = i18n.translate("File path required.")}
|
||||
end
|
||||
|
||||
if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed unzip, Can't unzip!")
|
||||
}
|
||||
end
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
|
||||
local new_file_size = api.get_file_space(file)
|
||||
local tmp_free_size = api.get_free_space("/tmp")
|
||||
if tmp_free_size <= 0 or tmp_free_size <= new_file_size then
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", "/tmp")}
|
||||
end
|
||||
|
||||
local tmp_dir = util.trim(util.exec("mktemp -d -t xray_extract.XXXXXX"))
|
||||
|
||||
local output = {}
|
||||
api.exec("/usr/bin/unzip", {"-o", file, "xray", "-d", tmp_dir},
|
||||
function(chunk) output[#output + 1] = chunk end)
|
||||
|
||||
local files = util.split(table.concat(output))
|
||||
|
||||
api.exec("/bin/rm", {"-f", file})
|
||||
|
||||
return {code = 0, file = tmp_dir}
|
||||
end
|
||||
|
||||
function to_move(file)
|
||||
local result = check_path()
|
||||
if result.code ~= 0 then
|
||||
return result
|
||||
end
|
||||
|
||||
if not file or file == "" then
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
return {code = 1, error = i18n.translate("Client file is required.")}
|
||||
end
|
||||
|
||||
local bin_path = file .. "/xray"
|
||||
|
||||
local new_version = api.get_xray_version(bin_path)
|
||||
if new_version == "" then
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("The client file is not suitable for current device.")
|
||||
}
|
||||
end
|
||||
|
||||
local flag = sys.call('pgrep -af "passwall/.*xray" >/dev/null')
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall stop")
|
||||
end
|
||||
|
||||
local old_app_size = 0
|
||||
if fs.access(app_path) then
|
||||
old_app_size = api.get_file_space(app_path)
|
||||
end
|
||||
local new_app_size = api.get_file_space(bin_path)
|
||||
local final_dir = api.get_final_dir(app_path)
|
||||
local final_dir_free_size = api.get_free_space(final_dir)
|
||||
if final_dir_free_size > 0 then
|
||||
final_dir_free_size = final_dir_free_size + old_app_size
|
||||
if new_app_size > final_dir_free_size then
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
return {code = 1, error = i18n.translatef("%s not enough space.", final_dir)}
|
||||
end
|
||||
end
|
||||
|
||||
result = api.exec("/bin/mv", { "-f", bin_path, app_path }, nil, api.command_timeout) == 0
|
||||
|
||||
sys.call("/bin/rm -rf /tmp/xray_extract.*")
|
||||
if flag == 0 then
|
||||
sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &")
|
||||
end
|
||||
|
||||
if not result or not fs.access(app_path) then
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translatef("Can't move new file to path: %s", app_path)
|
||||
}
|
||||
end
|
||||
|
||||
return {code = 0}
|
||||
end
|
123
luci-app-passwall/luasrc/model/cbi/passwall/client/acl.lua
Normal file
123
luci-app-passwall/luasrc/model/cbi/passwall/client/acl.lua
Normal file
@ -0,0 +1,123 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local has_chnlist = api.fs.access("/usr/share/passwall/rules/chnlist")
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
local global_proxy_mode = (m:get("@global[0]", "tcp_proxy_mode") or "") .. (m:get("@global[0]", "udp_proxy_mode") or "")
|
||||
|
||||
-- [[ ACLs Settings ]]--
|
||||
s = m:section(TypedSection, "acl_rule", translate("ACLs"), "<font color='red'>" .. translate("ACLs is a tools which used to designate specific IP proxy mode.") .. "</font>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = true
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.extedit = api.url("acl_config", "%s")
|
||||
function s.create(e, t)
|
||||
t = TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
end
|
||||
function s.remove(e, t)
|
||||
sys.call("rm -rf /tmp/etc/passwall_tmp/dns_" .. t .. "*")
|
||||
TypedSection.remove(e, t)
|
||||
end
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enabled", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
---- Remarks
|
||||
o = s:option(Value, "remarks", translate("Remarks"))
|
||||
o.rmempty = true
|
||||
|
||||
local mac_t = {}
|
||||
sys.net.mac_hints(function(e, t)
|
||||
mac_t[e] = {
|
||||
ip = t,
|
||||
mac = e
|
||||
}
|
||||
end)
|
||||
|
||||
o = s:option(DummyValue, "sources", translate("Source"))
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
local e = ''
|
||||
local v = Value.cfgvalue(t, n) or ''
|
||||
string.gsub(v, '[^' .. " " .. ']+', function(w)
|
||||
local a = w
|
||||
if mac_t[w] then
|
||||
a = a .. ' (' .. mac_t[w].ip .. ')'
|
||||
end
|
||||
if #e > 0 then
|
||||
e = e .. "<br />"
|
||||
end
|
||||
e = e .. a
|
||||
end)
|
||||
return e
|
||||
end
|
||||
|
||||
---- TCP Proxy Mode
|
||||
tcp_proxy_mode = s:option(ListValue, "tcp_proxy_mode", translatef("%s Proxy Mode", "TCP"))
|
||||
tcp_proxy_mode.default = "default"
|
||||
tcp_proxy_mode.rmempty = false
|
||||
tcp_proxy_mode:value("default", translate("Default"))
|
||||
tcp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
tcp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
if has_chnlist and global_proxy_mode:find("returnhome") then
|
||||
tcp_proxy_mode:value("returnhome", translate("China List"))
|
||||
else
|
||||
tcp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
tcp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
end
|
||||
tcp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
|
||||
---- UDP Proxy Mode
|
||||
udp_proxy_mode = s:option(ListValue, "udp_proxy_mode", translatef("%s Proxy Mode", "UDP"))
|
||||
udp_proxy_mode.default = "default"
|
||||
udp_proxy_mode.rmempty = false
|
||||
udp_proxy_mode:value("default", translate("Default"))
|
||||
udp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
udp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
if has_chnlist and global_proxy_mode:find("returnhome") then
|
||||
udp_proxy_mode:value("returnhome", translate("China List"))
|
||||
else
|
||||
udp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
udp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
end
|
||||
udp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
|
||||
--[[
|
||||
---- TCP No Redir Ports
|
||||
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- UDP No Redir Ports
|
||||
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- TCP Redir Ports
|
||||
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("80,443", "80,443")
|
||||
o:value("80:65535", "80 " .. translate("or more"))
|
||||
o:value("1:443", "443 " .. translate("or less"))
|
||||
|
||||
---- UDP Redir Ports
|
||||
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("53", "53")
|
||||
]]--
|
||||
|
||||
return m
|
@ -0,0 +1,296 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
local has_chnlist = api.fs.access("/usr/share/passwall/rules/chnlist")
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
nodes_table[#nodes_table + 1] = e
|
||||
end
|
||||
|
||||
local global_proxy_mode = (m:get("@global[0]", "tcp_proxy_mode") or "") .. (m:get("@global[0]", "udp_proxy_mode") or "")
|
||||
|
||||
local dynamicList_write = function(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
t = { value }
|
||||
end
|
||||
t = table.concat(t, " ")
|
||||
return DynamicList.write(self, section, t)
|
||||
end
|
||||
|
||||
-- [[ ACLs Settings ]]--
|
||||
s = m:section(NamedSection, arg[1], translate("ACLs"), translate("ACLs"))
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enabled", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
---- Remarks
|
||||
o = s:option(Value, "remarks", translate("Remarks"))
|
||||
o.default = arg[1]
|
||||
o.rmempty = true
|
||||
|
||||
local mac_t = {}
|
||||
sys.net.mac_hints(function(e, t)
|
||||
mac_t[#mac_t + 1] = {
|
||||
ip = t,
|
||||
mac = e
|
||||
}
|
||||
end)
|
||||
table.sort(mac_t, function(a,b)
|
||||
if #a.ip < #b.ip then
|
||||
return true
|
||||
elseif #a.ip == #b.ip then
|
||||
if a.ip < b.ip then
|
||||
return true
|
||||
else
|
||||
return #a.ip < #b.ip
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
---- Source
|
||||
sources = s:option(DynamicList, "sources", translate("Source"))
|
||||
sources.description = "<ul><li>" .. translate("Example:")
|
||||
.. "</li><li>" .. translate("MAC") .. ": 00:00:00:FF:FF:FF"
|
||||
.. "</li><li>" .. translate("IP") .. ": 192.168.1.100"
|
||||
.. "</li><li>" .. translate("IP CIDR") .. ": 192.168.1.0/24"
|
||||
.. "</li><li>" .. translate("IP range") .. ": 192.168.1.100-192.168.1.200"
|
||||
.. "</li><li>" .. translate("IPSet") .. ": ipset:lanlist"
|
||||
.. "</li></ul>"
|
||||
sources.cast = "string"
|
||||
for _, key in pairs(mac_t) do
|
||||
sources:value(key.mac, "%s (%s)" % {key.mac, key.ip})
|
||||
end
|
||||
sources.cfgvalue = function(self, section)
|
||||
local value
|
||||
if self.tag_error[section] then
|
||||
value = self:formvalue(section)
|
||||
else
|
||||
value = self.map:get(section, self.option)
|
||||
if type(value) == "string" then
|
||||
local value2 = {}
|
||||
string.gsub(value, '[^' .. " " .. ']+', function(w) table.insert(value2, w) end)
|
||||
value = value2
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
sources.validate = function(self, value, t)
|
||||
local err = {}
|
||||
for _, v in ipairs(value) do
|
||||
local flag = false
|
||||
if v:find("ipset:") and v:find("ipset:") == 1 then
|
||||
local ipset = v:gsub("ipset:", "")
|
||||
if ipset and ipset ~= "" then
|
||||
flag = true
|
||||
end
|
||||
end
|
||||
|
||||
if flag == false and datatypes.macaddr(v) then
|
||||
flag = true
|
||||
end
|
||||
|
||||
if flag == false and datatypes.ip4addr(v) then
|
||||
flag = true
|
||||
end
|
||||
|
||||
if flag == false and api.iprange(v) then
|
||||
flag = true
|
||||
end
|
||||
|
||||
if flag == false then
|
||||
err[#err + 1] = v
|
||||
end
|
||||
end
|
||||
|
||||
if #err > 0 then
|
||||
self:add_error(t, "invalid", translate("Not true format, please re-enter!"))
|
||||
for _, v in ipairs(err) do
|
||||
self:add_error(t, "invalid", v)
|
||||
end
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
sources.write = dynamicList_write
|
||||
|
||||
---- TCP Proxy Mode
|
||||
tcp_proxy_mode = s:option(ListValue, "tcp_proxy_mode", translatef("%s Proxy Mode", "TCP"))
|
||||
tcp_proxy_mode.default = "default"
|
||||
tcp_proxy_mode.rmempty = false
|
||||
tcp_proxy_mode:value("default", translate("Default"))
|
||||
tcp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
tcp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
if has_chnlist and global_proxy_mode:find("returnhome") then
|
||||
tcp_proxy_mode:value("returnhome", translate("China List"))
|
||||
else
|
||||
tcp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
tcp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
end
|
||||
tcp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
|
||||
---- UDP Proxy Mode
|
||||
udp_proxy_mode = s:option(ListValue, "udp_proxy_mode", translatef("%s Proxy Mode", "UDP"))
|
||||
udp_proxy_mode.default = "default"
|
||||
udp_proxy_mode.rmempty = false
|
||||
udp_proxy_mode:value("default", translate("Default"))
|
||||
udp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
udp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
if has_chnlist and global_proxy_mode:find("returnhome") then
|
||||
udp_proxy_mode:value("returnhome", translate("China List"))
|
||||
else
|
||||
udp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
udp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
end
|
||||
udp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
|
||||
---- TCP No Redir Ports
|
||||
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- UDP No Redir Ports
|
||||
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- TCP Proxy Drop Ports
|
||||
o = s:option(Value, "tcp_proxy_drop_ports", translate("TCP Proxy Drop Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
|
||||
---- UDP Proxy Drop Ports
|
||||
o = s:option(Value, "udp_proxy_drop_ports", translate("UDP Proxy Drop Ports"))
|
||||
o.default = "default"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("default", translate("Default"))
|
||||
o:value("80,443", translate("QUIC"))
|
||||
|
||||
---- TCP Redir Ports
|
||||
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("80,443", "80,443")
|
||||
o:value("80:65535", "80 " .. translate("or more"))
|
||||
o:value("1:443", "443 " .. translate("or less"))
|
||||
|
||||
---- UDP Redir Ports
|
||||
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("53", "53")
|
||||
|
||||
tcp_node = s:option(ListValue, "tcp_node", "<a style='color: red'>" .. translate("TCP Node") .. "</a>")
|
||||
tcp_node.default = "default"
|
||||
tcp_node:value("default", translate("Default"))
|
||||
|
||||
udp_node = s:option(ListValue, "udp_node", "<a style='color: red'>" .. translate("UDP Node") .. "</a>")
|
||||
udp_node.default = "default"
|
||||
udp_node:value("default", translate("Default"))
|
||||
udp_node:value("tcp", translate("Same as the tcp node"))
|
||||
|
||||
for k, v in pairs(nodes_table) do
|
||||
tcp_node:value(v.id, v["remark"])
|
||||
udp_node:value(v.id, v["remark"])
|
||||
end
|
||||
|
||||
---- DNS Forward Mode
|
||||
o = s:option(ListValue, "dns_mode", translate("Filter Mode"))
|
||||
o:depends({ tcp_node = "default", ['!reverse'] = true })
|
||||
if api.is_finded("dns2socks") then
|
||||
o:value("dns2socks", "dns2socks")
|
||||
end
|
||||
if has_v2ray then
|
||||
o:value("v2ray", "V2ray")
|
||||
end
|
||||
if has_xray then
|
||||
o:value("xray", "Xray")
|
||||
end
|
||||
|
||||
o = s:option(ListValue, "v2ray_dns_mode", " ")
|
||||
o:value("tcp", "TCP")
|
||||
o:value("doh", "DoH")
|
||||
o:depends("dns_mode", "v2ray")
|
||||
o:depends("dns_mode", "xray")
|
||||
|
||||
---- DNS Forward
|
||||
o = s:option(Value, "dns_forward", translate("Remote DNS"))
|
||||
o.default = "1.1.1.1"
|
||||
o:value("1.1.1.1", "1.1.1.1 (CloudFlare DNS)")
|
||||
o:value("1.1.1.2", "1.1.1.2 (CloudFlare DNS)")
|
||||
o:value("8.8.8.8", "8.8.8.8 (Google DNS)")
|
||||
o:value("8.8.4.4", "8.8.4.4 (Google DNS)")
|
||||
o:value("208.67.222.222", "208.67.222.222 (Open DNS)")
|
||||
o:value("208.67.220.220", "208.67.220.220 (Open DNS)")
|
||||
o:depends("dns_mode", "dns2socks")
|
||||
o:depends("v2ray_dns_mode", "tcp")
|
||||
|
||||
if has_v2ray or has_xray then
|
||||
---- DoH
|
||||
o = s:option(Value, "dns_doh", translate("DoH request address"))
|
||||
o:value("https://cloudflare-dns.com/dns-query,1.1.1.1", "CloudFlare")
|
||||
o:value("https://security.cloudflare-dns.com/dns-query,1.1.1.2", "CloudFlare-Security")
|
||||
o:value("https://doh.opendns.com/dns-query,208.67.222.222", "OpenDNS")
|
||||
o:value("https://dns.google/dns-query,8.8.8.8", "Google")
|
||||
o:value("https://doh.libredns.gr/dns-query,116.202.176.26", "LibreDNS")
|
||||
o:value("https://doh.libredns.gr/ads,116.202.176.26", "LibreDNS (No Ads)")
|
||||
o:value("https://dns.quad9.net/dns-query,9.9.9.9", "Quad9-Recommended")
|
||||
o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard")
|
||||
o.default = "https://cloudflare-dns.com/dns-query,1.1.1.1"
|
||||
o.validate = function(self, value, t)
|
||||
if value ~= "" then
|
||||
local flag = 0
|
||||
local util = require "luci.util"
|
||||
local val = util.split(value, ",")
|
||||
local url = val[1]
|
||||
val[1] = nil
|
||||
for i = 1, #val do
|
||||
local v = val[i]
|
||||
if v then
|
||||
if not api.datatypes.ipmask4(v) then
|
||||
flag = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if flag == 0 then
|
||||
return value
|
||||
end
|
||||
end
|
||||
return nil, translate("DoH request address") .. " " .. translate("Format must be:") .. " URL,IP"
|
||||
end
|
||||
o:depends("v2ray_dns_mode", "doh")
|
||||
end
|
||||
|
||||
o = s:option(Value, "dns_client_ip", translate("EDNS Client Subnet"))
|
||||
o.datatype = "ipaddr"
|
||||
o:depends("v2ray_dns_mode", "doh")
|
||||
|
||||
return m
|
@ -0,0 +1,44 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ App Settings ]]--
|
||||
s = m:section(TypedSection, "global_app", translate("App Update"),
|
||||
"<font color='red'>" ..
|
||||
translate("Please confirm that your firmware supports FPU.") ..
|
||||
"</font>")
|
||||
s.anonymous = true
|
||||
s:append(Template(appname .. "/app_update/v2ray_version"))
|
||||
s:append(Template(appname .. "/app_update/xray_version"))
|
||||
s:append(Template(appname .. "/app_update/trojan_go_version"))
|
||||
s:append(Template(appname .. "/app_update/brook_version"))
|
||||
s:append(Template(appname .. "/app_update/hysteria_version"))
|
||||
|
||||
o = s:option(Value, "v2ray_file", translatef("%s App Path", "V2ray"))
|
||||
o.default = "/usr/bin/v2ray"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "xray_file", translatef("%s App Path", "Xray"))
|
||||
o.default = "/usr/bin/xray"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "trojan_go_file", translatef("%s App Path", "Trojan-Go"))
|
||||
o.default = "/usr/bin/trojan-go"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "brook_file", translatef("%s App Path", "Brook"))
|
||||
o.default = "/usr/bin/brook"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "hysteria_file", translatef("%s App Path", "Hysteria"))
|
||||
o.default = "/usr/bin/hysteria"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(DummyValue, "tips", " ")
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
return string.format('<font color="red">%s</font>', translate("if you want to run from memory, change the path, /tmp beginning then save the application and update it manually."))
|
||||
end
|
||||
|
||||
return m
|
@ -0,0 +1,66 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
nodes_table[#nodes_table + 1] = e
|
||||
end
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ Auto Switch Settings ]]--
|
||||
s = m:section(TypedSection, "auto_switch")
|
||||
s.anonymous = true
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enable", translate("Enable"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "testing_time", translate("How often to test"), translate("Units:minutes"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 1
|
||||
|
||||
o = s:option(Value, "connect_timeout", translate("Timeout seconds"), translate("Units:seconds"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 3
|
||||
|
||||
o = s:option(Value, "retry_num", translate("Timeout retry num"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 3
|
||||
|
||||
o = s:option(DynamicList, "tcp_node", "TCP " .. translate("List of backup nodes"))
|
||||
for k, v in pairs(nodes_table) do
|
||||
if v.node_type == "normal" then
|
||||
o:value(v.id, v["remark"])
|
||||
end
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
local t = {}
|
||||
local t2 = {}
|
||||
if type(value) == "table" then
|
||||
local x
|
||||
for _, x in ipairs(value) do
|
||||
if x and #x > 0 then
|
||||
if not t2[x] then
|
||||
t2[x] = x
|
||||
t[#t+1] = x
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
t = { value }
|
||||
end
|
||||
return DynamicList.write(self, section, t)
|
||||
end
|
||||
|
||||
o = s:option(Flag, "restore_switch", "TCP " .. translate("Restore Switch"), translate("When detects main node is available, switch back to the main node."))
|
||||
|
||||
o = s:option(ListValue, "shunt_logic", "TCP " .. translate("If the main node is V2ray/Xray shunt"))
|
||||
o:value("0", translate("Switch it"))
|
||||
o:value("1", translate("Applying to the default node"))
|
||||
o:value("2", translate("Applying to the default preproxy node"))
|
||||
|
||||
m:append(Template(appname .. "/auto_switch/footer"))
|
||||
|
||||
return m
|
461
luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua
Normal file
461
luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua
Normal file
@ -0,0 +1,461 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
local datatypes = api.datatypes
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
local has_chnlist = api.fs.access("/usr/share/passwall/rules/chnlist")
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
nodes_table[#nodes_table + 1] = e
|
||||
end
|
||||
|
||||
local socks_table = {}
|
||||
uci:foreach(appname, "socks", function(s)
|
||||
if s.enabled == "1" and s.node then
|
||||
local id, remarks
|
||||
local same, i = s.node:match("^(tcp)")
|
||||
if same then
|
||||
remarks = translatef("Same as the tcp node")
|
||||
else
|
||||
for k, n in pairs(nodes_table) do
|
||||
if (s.node == n.id) then
|
||||
remarks = n["remark"]; break
|
||||
end
|
||||
end
|
||||
end
|
||||
id = "127.0.0.1" .. ":" .. s.port
|
||||
socks_table[#socks_table + 1] = {
|
||||
id = id,
|
||||
remarks = id .. " - " .. (remarks or translate("Misconfigured"))
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
local doh_validate = function(self, value, t)
|
||||
if value ~= "" then
|
||||
local flag = 0
|
||||
local util = require "luci.util"
|
||||
local val = util.split(value, ",")
|
||||
local url = val[1]
|
||||
val[1] = nil
|
||||
for i = 1, #val do
|
||||
local v = val[i]
|
||||
if v then
|
||||
if not datatypes.ipmask4(v) then
|
||||
flag = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if flag == 0 then
|
||||
return value
|
||||
end
|
||||
end
|
||||
return nil, translate("DoH request address") .. " " .. translate("Format must be:") .. " URL,IP"
|
||||
end
|
||||
|
||||
local redir_mode_validate = function(self, value, t)
|
||||
local tcp_proxy_mode_v = tcp_proxy_mode:formvalue(t) or ""
|
||||
local udp_proxy_mode_v = udp_proxy_mode:formvalue(t) or ""
|
||||
local localhost_tcp_proxy_mode_v = localhost_tcp_proxy_mode:formvalue(t) or ""
|
||||
local localhost_udp_proxy_mode_v = localhost_udp_proxy_mode:formvalue(t) or ""
|
||||
local s = tcp_proxy_mode_v .. udp_proxy_mode_v .. localhost_tcp_proxy_mode_v .. localhost_udp_proxy_mode_v
|
||||
if s:find("returnhome") then
|
||||
if s:find("chnroute") or s:find("gfwlist") then
|
||||
return nil, translate("China list or gfwlist cannot be used together with outside China list!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
m:append(Template(appname .. "/global/status"))
|
||||
|
||||
s = m:section(TypedSection, "global")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
s:tab("Main", translate("Main"))
|
||||
|
||||
-- [[ Global Settings ]]--
|
||||
o = s:taboption("Main", Flag, "enabled", translate("Main switch"))
|
||||
o.rmempty = false
|
||||
|
||||
---- TCP Node
|
||||
tcp_node = s:taboption("Main", ListValue, "tcp_node", "<a style='color: red'>" .. translate("TCP Node") .. "</a>")
|
||||
tcp_node.description = ""
|
||||
--tcp_node.description = translate("For proxy specific list.")
|
||||
--tcp_node.description = o.description .. "<br />"
|
||||
local current_node = luci.sys.exec(string.format("[ -f '/tmp/etc/%s/id/TCP' ] && echo -n $(cat /tmp/etc/%s/id/TCP)", appname, appname))
|
||||
if current_node and current_node ~= "" and current_node ~= "nil" then
|
||||
local n = uci:get_all(appname, current_node)
|
||||
if n then
|
||||
if tonumber(m:get("@auto_switch[0]", "enable") or 0) == 1 then
|
||||
local remarks = api.get_full_node_remarks(n)
|
||||
local url = api.url("node_config", current_node)
|
||||
tcp_node.description = tcp_node.description .. translatef("Current node: %s", string.format('<a href="%s">%s</a>', url, remarks)) .. "<br />"
|
||||
end
|
||||
end
|
||||
end
|
||||
tcp_node:value("nil", translate("Close"))
|
||||
|
||||
-- 分流
|
||||
if (has_v2ray or has_xray) and #nodes_table > 0 then
|
||||
local normal_list = {}
|
||||
local shunt_list = {}
|
||||
for k, v in pairs(nodes_table) do
|
||||
if v.node_type == "normal" then
|
||||
normal_list[#normal_list + 1] = v
|
||||
end
|
||||
if v.protocol and v.protocol == "_shunt" then
|
||||
shunt_list[#shunt_list + 1] = v
|
||||
end
|
||||
end
|
||||
for k, v in pairs(shunt_list) do
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
local id = e[".name"]
|
||||
if id and e.remarks then
|
||||
o = s:taboption("Main", ListValue, v.id .. "." .. id .. "_node", string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", id), e.remarks))
|
||||
o:depends("tcp_node", v.id)
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_default", translate("Default"))
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1["remark"])
|
||||
end
|
||||
o.cfgvalue = function(self, section)
|
||||
return m:get(v.id, id) or "nil"
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
m:set(v.id, id, value)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local id = "default_node"
|
||||
o = s:taboption("Main", ListValue, v.id .. "." .. id, string.format('* <a style="color:red">%s</a>', translate("Default")))
|
||||
o:depends("tcp_node", v.id)
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1["remark"])
|
||||
end
|
||||
o.cfgvalue = function(self, section)
|
||||
return m:get(v.id, id) or "nil"
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
m:set(v.id, id, value)
|
||||
end
|
||||
|
||||
local id = "main_node"
|
||||
o = s:taboption("Main", ListValue, v.id .. "." .. id, string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
|
||||
o:depends("tcp_node", v.id)
|
||||
o:value("nil", translate("Close"))
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1["remark"])
|
||||
end
|
||||
o.cfgvalue = function(self, section)
|
||||
return m:get(v.id, id) or "nil"
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
m:set(v.id, id, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
udp_node = s:taboption("Main", ListValue, "udp_node", "<a style='color: red'>" .. translate("UDP Node") .. "</a>")
|
||||
udp_node:value("nil", translate("Close"))
|
||||
--udp_node.description = translate("For proxy game network.")
|
||||
udp_node:value("tcp", translate("Same as the tcp node"))
|
||||
|
||||
s:tab("DNS", translate("DNS"))
|
||||
|
||||
if api.is_finded("smartdns") then
|
||||
dns_shunt = s:taboption("DNS", ListValue, "dns_shunt", translate("DNS Shunt"))
|
||||
dns_shunt:value("dnsmasq", "Dnsmasq")
|
||||
dns_shunt:value("smartdns", "SmartDNS")
|
||||
|
||||
group_domestic = s:taboption("DNS", Value, "group_domestic", translate("Domestic group name"))
|
||||
group_domestic.placeholder = "local"
|
||||
group_domestic:depends("dns_shunt", "smartdns")
|
||||
group_domestic.description = translate("You only need to configure domestic DNS packets in SmartDNS and set it redirect or as Dnsmasq upstream, and fill in the domestic DNS group name here.")
|
||||
end
|
||||
|
||||
o = s:taboption("DNS", Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
||||
o.default = "0"
|
||||
|
||||
---- DNS Forward Mode
|
||||
dns_mode = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode"))
|
||||
dns_mode.rmempty = false
|
||||
dns_mode:reset_values()
|
||||
if api.is_finded("pdnsd") then
|
||||
dns_mode:value("pdnsd", "pdnsd " .. translatef("Requery DNS By %s", translate("TCP Node")))
|
||||
end
|
||||
if api.is_finded("dns2socks") then
|
||||
dns_mode:value("dns2socks", "dns2socks")
|
||||
end
|
||||
if has_v2ray then
|
||||
dns_mode:value("v2ray", "V2ray")
|
||||
end
|
||||
if has_xray then
|
||||
dns_mode:value("xray", "Xray")
|
||||
end
|
||||
dns_mode:value("udp", translatef("Requery DNS By %s", "UDP"))
|
||||
|
||||
o = s:taboption("DNS", ListValue, "v2ray_dns_mode", " ")
|
||||
o:value("tcp", "TCP")
|
||||
o:value("doh", "DoH")
|
||||
o:value("fakedns", "FakeDNS")
|
||||
o:depends("dns_mode", "v2ray")
|
||||
o:depends("dns_mode", "xray")
|
||||
o.validate = function(self, value, t)
|
||||
if value == "fakedns" then
|
||||
local _dns_mode = dns_mode:formvalue(t)
|
||||
local _tcp_node = tcp_node:formvalue(t)
|
||||
if m:get(_tcp_node, "type"):lower() ~= _dns_mode then
|
||||
return nil, translatef("TCP node must be '%s' type to use FakeDNS.", _dns_mode)
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
o = s:taboption("DNS", Value, "socks_server", translate("Socks Server"), translate("Make sure socks service is available on this address."))
|
||||
for k, v in pairs(socks_table) do o:value(v.id, v.remarks) end
|
||||
o.validate = function(self, value, t)
|
||||
if not datatypes.ipaddrport(value) then
|
||||
return nil, translate("Socks Server") .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
return value
|
||||
end
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
|
||||
---- DoH
|
||||
o = s:taboption("DNS", Value, "up_trust_doh", translate("DoH request address"))
|
||||
o:value("https://cloudflare-dns.com/dns-query,1.1.1.1", "CloudFlare")
|
||||
o:value("https://security.cloudflare-dns.com/dns-query,1.1.1.2", "CloudFlare-Security")
|
||||
o:value("https://doh.opendns.com/dns-query,208.67.222.222", "OpenDNS")
|
||||
o:value("https://dns.google/dns-query,8.8.8.8", "Google")
|
||||
o:value("https://doh.libredns.gr/dns-query,116.202.176.26", "LibreDNS")
|
||||
o:value("https://doh.libredns.gr/ads,116.202.176.26", "LibreDNS (No Ads)")
|
||||
o:value("https://dns.quad9.net/dns-query,9.9.9.9", "Quad9-Recommended")
|
||||
o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard")
|
||||
o.default = "https://cloudflare-dns.com/dns-query,1.1.1.1"
|
||||
o.validate = doh_validate
|
||||
o:depends("v2ray_dns_mode", "doh")
|
||||
|
||||
---- DNS Forward
|
||||
o = s:taboption("DNS", Value, "dns_forward", translate("Remote DNS"))
|
||||
--o.description = translate("IP:Port mode acceptable, multi value split with english comma.") .. " " .. translate("If you use dns2socks, only the first one is valid.")
|
||||
o.datatype = "or(ipaddr,ipaddrport)"
|
||||
o.default = "1.1.1.1"
|
||||
o:value("1.1.1.1", "1.1.1.1 (CloudFlare DNS)")
|
||||
o:value("1.1.1.2", "1.1.1.2 (CloudFlare DNS)")
|
||||
o:value("8.8.8.8", "8.8.8.8 (Google DNS)")
|
||||
o:value("8.8.4.4", "8.8.4.4 (Google DNS)")
|
||||
o:value("208.67.222.222", "208.67.222.222 (Open DNS)")
|
||||
o:value("208.67.220.220", "208.67.220.220 (Open DNS)")
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
o:depends({dns_mode = "pdnsd"})
|
||||
o:depends({dns_mode = "udp"})
|
||||
o:depends({v2ray_dns_mode = "tcp"})
|
||||
|
||||
o = s:taboption("DNS", Value, "dns_client_ip", translate("EDNS Client Subnet"))
|
||||
o.description = translate("Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address).") .. "<br />" ..
|
||||
translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).")
|
||||
o.datatype = "ipaddr"
|
||||
o:depends("v2ray_dns_mode", "tcp")
|
||||
o:depends("v2ray_dns_mode", "doh")
|
||||
|
||||
o = s:taboption("DNS", Flag, "dns_cache", translate("Cache Resolved"))
|
||||
o.default = "1"
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
o:depends({dns_mode = "pdnsd"})
|
||||
o:depends({dns_mode = "v2ray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "v2ray", v2ray_dns_mode = "doh"})
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "doh"})
|
||||
o.rmempty = false
|
||||
|
||||
if has_chnlist and api.is_finded("chinadns-ng") then
|
||||
o = s:taboption("DNS", Flag, "chinadns_ng", translate("ChinaDNS-NG"), translate("The effect is better, but will increase the memory."))
|
||||
o.default = "0"
|
||||
if api.is_finded("smartdns") then
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "dns2socks"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "pdnsd"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "v2ray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "v2ray", v2ray_dns_mode = "doh"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "xray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "xray", v2ray_dns_mode = "doh"})
|
||||
o:depends({dns_shunt = "dnsmasq", dns_mode = "udp"})
|
||||
else
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
o:depends({dns_mode = "pdnsd"})
|
||||
o:depends({dns_mode = "v2ray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "v2ray", v2ray_dns_mode = "doh"})
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "doh"})
|
||||
o:depends({dns_mode = "udp"})
|
||||
end
|
||||
end
|
||||
|
||||
o = s:taboption("DNS", Button, "clear_ipset", translate("Clear IPSET"), translate("Try this feature if the rule modification does not take effect."))
|
||||
o.inputstyle = "remove"
|
||||
function o.write(e, e)
|
||||
luci.sys.call("/usr/share/" .. appname .. "/iptables.sh flush_ipset > /dev/null 2>&1 &")
|
||||
luci.http.redirect(api.url("log"))
|
||||
end
|
||||
|
||||
s:tab("Proxy", translate("Mode"))
|
||||
|
||||
---- TCP Default Proxy Mode
|
||||
tcp_proxy_mode = s:taboption("Proxy", ListValue, "tcp_proxy_mode", "TCP " .. translate("Default") .. translate("Proxy Mode"))
|
||||
-- o.description = translate("If not available, try clearing the cache.")
|
||||
tcp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
tcp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
tcp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
tcp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
if has_chnlist then
|
||||
tcp_proxy_mode:value("returnhome", translate("China List"))
|
||||
end
|
||||
tcp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
tcp_proxy_mode.default = "chnroute"
|
||||
--tcp_proxy_mode.validate = redir_mode_validate
|
||||
|
||||
---- UDP Default Proxy Mode
|
||||
udp_proxy_mode = s:taboption("Proxy", ListValue, "udp_proxy_mode", "UDP " .. translate("Default") .. translate("Proxy Mode"))
|
||||
udp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
udp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
udp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
udp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
if has_chnlist then
|
||||
udp_proxy_mode:value("returnhome", translate("China List"))
|
||||
end
|
||||
udp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
udp_proxy_mode.default = "chnroute"
|
||||
--udp_proxy_mode.validate = redir_mode_validate
|
||||
|
||||
---- Localhost TCP Proxy Mode
|
||||
localhost_tcp_proxy_mode = s:taboption("Proxy", ListValue, "localhost_tcp_proxy_mode", translate("Router Localhost") .. " TCP " .. translate("Proxy Mode"))
|
||||
-- o.description = translate("The server client can also use this rule to scientifically surf the Internet.")
|
||||
localhost_tcp_proxy_mode:value("default", translatef("Same as the %s default proxy mode", "TCP"))
|
||||
localhost_tcp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
localhost_tcp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
localhost_tcp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
if has_chnlist then
|
||||
localhost_tcp_proxy_mode:value("returnhome", translate("China List"))
|
||||
end
|
||||
localhost_tcp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
localhost_tcp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
localhost_tcp_proxy_mode.default = "default"
|
||||
--localhost_tcp_proxy_mode.validate = redir_mode_validate
|
||||
|
||||
---- Localhost UDP Proxy Mode
|
||||
localhost_udp_proxy_mode = s:taboption("Proxy", ListValue, "localhost_udp_proxy_mode", translate("Router Localhost") .. " UDP " .. translate("Proxy Mode"))
|
||||
localhost_udp_proxy_mode:value("default", translatef("Same as the %s default proxy mode", "UDP"))
|
||||
localhost_udp_proxy_mode:value("global", translate("Global Proxy"))
|
||||
localhost_udp_proxy_mode:value("gfwlist", translate("GFW List"))
|
||||
localhost_udp_proxy_mode:value("chnroute", translate("Not China List"))
|
||||
if has_chnlist then
|
||||
localhost_udp_proxy_mode:value("returnhome", translate("China List"))
|
||||
end
|
||||
localhost_udp_proxy_mode:value("disable", translate("No Proxy"))
|
||||
localhost_udp_proxy_mode:value("direct/proxy", translate("Only use direct/proxy list"))
|
||||
localhost_udp_proxy_mode.default = "default"
|
||||
localhost_udp_proxy_mode.validate = redir_mode_validate
|
||||
|
||||
tips = s:taboption("Proxy", DummyValue, "tips", " ")
|
||||
tips.rawhtml = true
|
||||
tips.cfgvalue = function(t, n)
|
||||
return string.format('<a style="color: red" href="%s">%s</a>', api.url("acl"), translate("Want different devices to use different proxy modes/ports/nodes? Please use access control."))
|
||||
end
|
||||
|
||||
s:tab("log", translate("Log"))
|
||||
o = s:taboption("log", Flag, "close_log_tcp", translatef("%s Node Log Close", "TCP"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("log", Flag, "close_log_udp", translatef("%s Node Log Close", "UDP"))
|
||||
o.rmempty = false
|
||||
|
||||
loglevel = s:taboption("log", ListValue, "loglevel", "V2ray/Xray" .. translate("Log Level"))
|
||||
loglevel.default = "warning"
|
||||
loglevel:value("debug")
|
||||
loglevel:value("info")
|
||||
loglevel:value("warning")
|
||||
loglevel:value("error")
|
||||
|
||||
trojan_loglevel = s:taboption("log", ListValue, "trojan_loglevel", "Trojan" .. translate("Log Level"))
|
||||
trojan_loglevel.default = "2"
|
||||
trojan_loglevel:value("0", "all")
|
||||
trojan_loglevel:value("1", "info")
|
||||
trojan_loglevel:value("2", "warn")
|
||||
trojan_loglevel:value("3", "error")
|
||||
trojan_loglevel:value("4", "fatal")
|
||||
|
||||
s:tab("faq", "FAQ")
|
||||
|
||||
o = s:taboption("faq", DummyValue, "")
|
||||
o.template = appname .. "/global/faq"
|
||||
|
||||
-- [[ Socks Server ]]--
|
||||
o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch"))
|
||||
o.rmempty = false
|
||||
|
||||
s = m:section(TypedSection, "socks", translate("Socks Config"))
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "cbi/tblsection"
|
||||
function s.create(e, t)
|
||||
TypedSection.create(e, api.gen_uuid())
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "status", translate("Status"))
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
return string.format('<div class="_status" socks_id="%s"></div>', n)
|
||||
end
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enabled", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
socks_node = s:option(ListValue, "node", translate("Socks Node"))
|
||||
socks_node:value("tcp", translate("Same as the tcp node"))
|
||||
|
||||
local n = 0
|
||||
uci:foreach(appname, "socks", function(s)
|
||||
if s[".name"] == section then
|
||||
return false
|
||||
end
|
||||
n = n + 1
|
||||
end)
|
||||
|
||||
o = s:option(Value, "port", "Socks " .. translate("Listen Port"))
|
||||
o.default = n + 1080
|
||||
o.datatype = "port"
|
||||
o.rmempty = false
|
||||
|
||||
if has_v2ray or has_xray then
|
||||
o = s:option(Value, "http_port", "HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use"))
|
||||
o.default = 0
|
||||
o.datatype = "port"
|
||||
end
|
||||
|
||||
for k, v in pairs(nodes_table) do
|
||||
tcp_node:value(v.id, v["remark"])
|
||||
udp_node:value(v.id, v["remark"])
|
||||
if v.type == "Socks" then
|
||||
if has_v2ray or has_xray then
|
||||
socks_node:value(v.id, v["remark"])
|
||||
end
|
||||
else
|
||||
socks_node:value(v.id, v["remark"])
|
||||
end
|
||||
end
|
||||
|
||||
m:append(Template(appname .. "/global/footer"))
|
||||
|
||||
return m
|
121
luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua
Normal file
121
luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua
Normal file
@ -0,0 +1,121 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local net = require "luci.model.network".init()
|
||||
local datatypes = api.datatypes
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
obj = e,
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ Haproxy Settings ]]--
|
||||
s = m:section(TypedSection, "global_haproxy")
|
||||
s.anonymous = true
|
||||
|
||||
s:append(Template(appname .. "/haproxy/status"))
|
||||
|
||||
---- Balancing Enable
|
||||
o = s:option(Flag, "balancing_enable", translate("Enable Load Balancing"))
|
||||
o.rmempty = false
|
||||
o.default = false
|
||||
|
||||
---- Console Username
|
||||
o = s:option(Value, "console_user", translate("Console Username"))
|
||||
o.default = ""
|
||||
o:depends("balancing_enable", true)
|
||||
|
||||
---- Console Password
|
||||
o = s:option(Value, "console_password", translate("Console Password"))
|
||||
o.password = true
|
||||
o.default = ""
|
||||
o:depends("balancing_enable", true)
|
||||
|
||||
---- Console Port
|
||||
o = s:option(Value, "console_port", translate("Console Port"), translate(
|
||||
"In the browser input routing IP plus port access, such as:192.168.1.1:1188"))
|
||||
o.default = "1188"
|
||||
o:depends("balancing_enable", true)
|
||||
|
||||
-- [[ Balancing Settings ]]--
|
||||
s = m:section(TypedSection, "haproxy_config", "",
|
||||
"<font color='red'>" ..
|
||||
translate("Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group.") ..
|
||||
"\n" .. translate("Note that the node configuration parameters for load balancing must be consistent, otherwise problems can arise!") ..
|
||||
"</font>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = true
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
|
||||
s.create = function(e, t)
|
||||
TypedSection.create(e, api.gen_uuid())
|
||||
end
|
||||
|
||||
s.remove = function(self, section)
|
||||
for k, v in pairs(self.children) do
|
||||
v.rmempty = true
|
||||
v.validate = nil
|
||||
end
|
||||
TypedSection.remove(self, section)
|
||||
end
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enabled", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
---- Node Address
|
||||
o = s:option(Value, "lbss", translate("Node Address"))
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
o.rmempty = false
|
||||
o.validate = function(self, value)
|
||||
if not value then return nil end
|
||||
local t = m:get(value) or nil
|
||||
if t and t[".type"] == "nodes" then
|
||||
return value
|
||||
end
|
||||
if datatypes.hostport(value) or datatypes.ip4addrport(value) then
|
||||
return value
|
||||
end
|
||||
if api.is_ipv6addrport(value) then
|
||||
return value
|
||||
end
|
||||
return nil, value
|
||||
end
|
||||
|
||||
---- Haproxy Port
|
||||
o = s:option(Value, "haproxy_port", translate("Haproxy Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 1181
|
||||
o.rmempty = false
|
||||
|
||||
---- Node Weight
|
||||
o = s:option(Value, "lbweight", translate("Node Weight"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 5
|
||||
o.rmempty = false
|
||||
|
||||
---- Export
|
||||
o = s:option(ListValue, "export", translate("Export Of Multi WAN"))
|
||||
o:value(0, translate("Auto"))
|
||||
local wa = require "luci.tools.webadmin"
|
||||
wa.cbi_add_networks(o)
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
---- Mode
|
||||
o = s:option(ListValue, "backup", translate("Mode"))
|
||||
o:value(0, translate("Primary"))
|
||||
o:value(1, translate("Standby"))
|
||||
o.rmempty = false
|
||||
|
||||
return m
|
@ -0,0 +1,8 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
|
||||
f = SimpleForm(appname)
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
f:append(Template(appname .. "/log/log"))
|
||||
return f
|
@ -0,0 +1,863 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
|
||||
if not arg[1] or not uci:get(appname, arg[1]) then
|
||||
luci.http.redirect(api.url("node_list"))
|
||||
end
|
||||
|
||||
local ss_encrypt_method_list = {
|
||||
"rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
|
||||
"aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf",
|
||||
"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
}
|
||||
|
||||
local ss_rust_encrypt_method_list = {
|
||||
"plain", "none",
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305"
|
||||
}
|
||||
|
||||
local ssr_encrypt_method_list = {
|
||||
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
|
||||
"aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
|
||||
"bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
|
||||
"cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
|
||||
"chacha20-ietf"
|
||||
}
|
||||
|
||||
local ssr_protocol_list = {
|
||||
"origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
|
||||
"auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
|
||||
"auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
|
||||
"auth_chain_d", "auth_chain_e", "auth_chain_f"
|
||||
}
|
||||
local ssr_obfs_list = {
|
||||
"plain", "http_simple", "http_post", "random_head", "tls_simple",
|
||||
"tls1.0_session_auth", "tls1.2_ticket_auth"
|
||||
}
|
||||
|
||||
local v_ss_encrypt_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
|
||||
}
|
||||
|
||||
local x_ss_encrypt_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305"
|
||||
}
|
||||
|
||||
local security_list = {"none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero"}
|
||||
|
||||
local header_type_list = {
|
||||
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
|
||||
}
|
||||
local encrypt_methods_ss_aead = {
|
||||
"chacha20-ietf-poly1305",
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
}
|
||||
|
||||
m = Map(appname, translate("Node Config"))
|
||||
m.redirect = api.url()
|
||||
|
||||
s = m:section(NamedSection, arg[1], "nodes", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
share = s:option(DummyValue, "passwall", " ")
|
||||
share.rawhtml = true
|
||||
share.template = "passwall/node_list/link_share_man"
|
||||
share.value = arg[1]
|
||||
|
||||
remarks = s:option(Value, "remarks", translate("Node Remarks"))
|
||||
remarks.default = translate("Remarks")
|
||||
remarks.rmempty = false
|
||||
|
||||
type = s:option(ListValue, "type", translate("Type"))
|
||||
if api.is_finded("ipt2socks") then
|
||||
type:value("Socks", translate("Socks"))
|
||||
end
|
||||
if api.is_finded("ss-redir") then
|
||||
type:value("SS", translate("Shadowsocks Libev"))
|
||||
end
|
||||
if api.is_finded("sslocal") then
|
||||
type:value("SS-Rust", translate("Shadowsocks Rust"))
|
||||
end
|
||||
if api.is_finded("ssr-redir") then
|
||||
type:value("SSR", translate("ShadowsocksR Libev"))
|
||||
end
|
||||
if api.is_finded("v2ray") then
|
||||
type:value("V2ray", translate("V2ray"))
|
||||
end
|
||||
if api.is_finded("xray") then
|
||||
type:value("Xray", translate("Xray"))
|
||||
end
|
||||
if api.is_finded("brook") then
|
||||
type:value("Brook", translate("Brook"))
|
||||
end
|
||||
--[[
|
||||
if api.is_finded("trojan-plus") or api.is_finded("trojan") then
|
||||
type:value("Trojan", translate("Trojan"))
|
||||
end
|
||||
]]--
|
||||
if api.is_finded("trojan-plus") then
|
||||
type:value("Trojan-Plus", translate("Trojan-Plus"))
|
||||
end
|
||||
if api.is_finded("trojan-go") then
|
||||
type:value("Trojan-Go", translate("Trojan-Go"))
|
||||
end
|
||||
if api.is_finded("naive") then
|
||||
type:value("Naiveproxy", translate("NaiveProxy"))
|
||||
end
|
||||
if api.is_finded("hysteria") then
|
||||
type:value("Hysteria", translate("Hysteria"))
|
||||
end
|
||||
|
||||
protocol = s:option(ListValue, "protocol", translate("Protocol"))
|
||||
protocol:value("vmess", translate("Vmess"))
|
||||
protocol:value("vless", translate("VLESS"))
|
||||
protocol:value("http", translate("HTTP"))
|
||||
protocol:value("socks", translate("Socks"))
|
||||
protocol:value("shadowsocks", translate("Shadowsocks"))
|
||||
protocol:value("trojan", translate("Trojan"))
|
||||
protocol:value("_balancing", translate("Balancing"))
|
||||
protocol:value("_shunt", translate("Shunt"))
|
||||
protocol:depends("type", "V2ray")
|
||||
protocol:depends("type", "Xray")
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- 负载均衡列表
|
||||
balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"))
|
||||
for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end
|
||||
balancing_node:depends("protocol", "_balancing")
|
||||
|
||||
-- 分流
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if e[".name"] and e.remarks then
|
||||
o = s:option(ListValue, e[".name"], string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", e[".name"]), e.remarks))
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_default", translate("Default"))
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
o:depends("protocol", "_shunt")
|
||||
|
||||
if #nodes_table > 0 then
|
||||
_proxy_tag = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
|
||||
_proxy_tag:value("nil", translate("Close"))
|
||||
_proxy_tag:value("default", translate("Default"))
|
||||
_proxy_tag:value("main", translate("Default Preproxy"))
|
||||
_proxy_tag.default = "nil"
|
||||
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
_proxy_tag:depends(e[".name"], v.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
shunt_tips = s:option(DummyValue, "shunt_tips", " ")
|
||||
shunt_tips.rawhtml = true
|
||||
shunt_tips.cfgvalue = function(t, n)
|
||||
return string.format('<a style="color: red" href="../rule">%s</a>', translate("No shunt rules? Click me to go to add."))
|
||||
end
|
||||
shunt_tips:depends("protocol", "_shunt")
|
||||
|
||||
default_node = s:option(ListValue, "default_node", string.format('* <a style="color:red">%s</a>', translate("Default")))
|
||||
default_node:value("_direct", translate("Direct Connection"))
|
||||
default_node:value("_blackhole", translate("Blackhole"))
|
||||
for k, v in pairs(nodes_table) do default_node:value(v.id, v.remarks) end
|
||||
default_node:depends("protocol", "_shunt")
|
||||
|
||||
if #nodes_table > 0 then
|
||||
o = s:option(ListValue, "main_node", string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
|
||||
o:value("nil", translate("Close"))
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
o:depends("default_node", v.id)
|
||||
end
|
||||
end
|
||||
|
||||
domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy"))
|
||||
domainStrategy:value("AsIs")
|
||||
domainStrategy:value("IPIfNonMatch")
|
||||
domainStrategy:value("IPOnDemand")
|
||||
domainStrategy.default = "IPOnDemand"
|
||||
domainStrategy.description = "<br /><ul><li>" .. translate("'AsIs': Only use domain for routing. Default value.")
|
||||
.. "</li><li>" .. translate("'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again.")
|
||||
.. "</li><li>" .. translate("'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately.")
|
||||
.. "</li></ul>"
|
||||
domainStrategy:depends("protocol", "_balancing")
|
||||
domainStrategy:depends("protocol", "_shunt")
|
||||
|
||||
domainMatcher = s:option(ListValue, "domainMatcher", translate("Domain matcher"))
|
||||
domainMatcher:value("hybrid")
|
||||
domainMatcher:value("linear")
|
||||
domainMatcher:depends("protocol", "_balancing")
|
||||
domainMatcher:depends("protocol", "_shunt")
|
||||
|
||||
|
||||
-- Brook协议
|
||||
brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol"))
|
||||
brook_protocol:value("client", translate("Brook"))
|
||||
brook_protocol:value("wsclient", translate("WebSocket"))
|
||||
brook_protocol:depends("type", "Brook")
|
||||
function brook_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function brook_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
brook_tls = s:option(Flag, "brook_tls", translate("Use TLS"))
|
||||
brook_tls:depends("brook_protocol", "wsclient")
|
||||
|
||||
-- Naiveproxy协议
|
||||
naiveproxy_protocol = s:option(ListValue, "naiveproxy_protocol", translate("Protocol"))
|
||||
naiveproxy_protocol:value("https", translate("HTTPS"))
|
||||
naiveproxy_protocol:value("quic", translate("QUIC"))
|
||||
naiveproxy_protocol:depends("type", "Naiveproxy")
|
||||
function naiveproxy_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function naiveproxy_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
address = s:option(Value, "address", translate("Address (Support Domain Name)"))
|
||||
address.rmempty = false
|
||||
address:depends("type", "Socks")
|
||||
address:depends("type", "SS")
|
||||
address:depends("type", "SS-Rust")
|
||||
address:depends("type", "SSR")
|
||||
address:depends("type", "Brook")
|
||||
address:depends("type", "Trojan")
|
||||
address:depends("type", "Trojan-Plus")
|
||||
address:depends("type", "Trojan-Go")
|
||||
address:depends("type", "Naiveproxy")
|
||||
address:depends("type", "Hysteria")
|
||||
address:depends({ type = "V2ray", protocol = "vmess" })
|
||||
address:depends({ type = "V2ray", protocol = "vless" })
|
||||
address:depends({ type = "V2ray", protocol = "http" })
|
||||
address:depends({ type = "V2ray", protocol = "socks" })
|
||||
address:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
address:depends({ type = "V2ray", protocol = "trojan" })
|
||||
address:depends({ type = "Xray", protocol = "vmess" })
|
||||
address:depends({ type = "Xray", protocol = "vless" })
|
||||
address:depends({ type = "Xray", protocol = "http" })
|
||||
address:depends({ type = "Xray", protocol = "socks" })
|
||||
address:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
address:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
--[[
|
||||
use_ipv6 = s:option(Flag, "use_ipv6", translate("Use IPv6"))
|
||||
use_ipv6.default = 0
|
||||
use_ipv6:depends("type", "Socks")
|
||||
use_ipv6:depends("type", "SS")
|
||||
use_ipv6:depends("type", "SS-Rust")
|
||||
use_ipv6:depends("type", "SSR")
|
||||
use_ipv6:depends("type", "Brook")
|
||||
use_ipv6:depends("type", "Trojan")
|
||||
use_ipv6:depends("type", "Trojan-Plus")
|
||||
use_ipv6:depends("type", "Trojan-Go")
|
||||
use_ipv6:depends("type", "Hysteria")
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "vmess" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "vless" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "http" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "socks" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
use_ipv6:depends({ type = "V2ray", protocol = "trojan" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "vmess" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "vless" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "http" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "socks" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
use_ipv6:depends({ type = "Xray", protocol = "trojan" })
|
||||
--]]
|
||||
|
||||
port = s:option(Value, "port", translate("Port"))
|
||||
port.datatype = "port"
|
||||
port.rmempty = false
|
||||
port:depends("type", "Socks")
|
||||
port:depends("type", "SS")
|
||||
port:depends("type", "SS-Rust")
|
||||
port:depends("type", "SSR")
|
||||
port:depends("type", "Brook")
|
||||
port:depends("type", "Trojan")
|
||||
port:depends("type", "Trojan-Plus")
|
||||
port:depends("type", "Trojan-Go")
|
||||
port:depends("type", "Naiveproxy")
|
||||
port:depends("type", "Hysteria")
|
||||
port:depends({ type = "V2ray", protocol = "vmess" })
|
||||
port:depends({ type = "V2ray", protocol = "vless" })
|
||||
port:depends({ type = "V2ray", protocol = "http" })
|
||||
port:depends({ type = "V2ray", protocol = "socks" })
|
||||
port:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
port:depends({ type = "V2ray", protocol = "trojan" })
|
||||
port:depends({ type = "Xray", protocol = "vmess" })
|
||||
port:depends({ type = "Xray", protocol = "vless" })
|
||||
port:depends({ type = "Xray", protocol = "http" })
|
||||
port:depends({ type = "Xray", protocol = "socks" })
|
||||
port:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
port:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
username = s:option(Value, "username", translate("Username"))
|
||||
username:depends("type", "Socks")
|
||||
username:depends("type", "Naiveproxy")
|
||||
username:depends({ type = "V2ray", protocol = "http" })
|
||||
username:depends({ type = "V2ray", protocol = "socks" })
|
||||
username:depends({ type = "Xray", protocol = "http" })
|
||||
username:depends({ type = "Xray", protocol = "socks" })
|
||||
|
||||
password = s:option(Value, "password", translate("Password"))
|
||||
password.password = true
|
||||
password:depends("type", "Socks")
|
||||
password:depends("type", "SS")
|
||||
password:depends("type", "SS-Rust")
|
||||
password:depends("type", "SSR")
|
||||
password:depends("type", "Brook")
|
||||
password:depends("type", "Trojan")
|
||||
password:depends("type", "Trojan-Plus")
|
||||
password:depends("type", "Trojan-Go")
|
||||
password:depends("type", "Naiveproxy")
|
||||
password:depends({ type = "V2ray", protocol = "http" })
|
||||
password:depends({ type = "V2ray", protocol = "socks" })
|
||||
password:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
password:depends({ type = "V2ray", protocol = "trojan" })
|
||||
password:depends({ type = "Xray", protocol = "http" })
|
||||
password:depends({ type = "Xray", protocol = "socks" })
|
||||
password:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
password:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
|
||||
hysteria_protocol:value("udp", "UDP")
|
||||
hysteria_protocol:value("faketcp", "faketcp")
|
||||
hysteria_protocol:value("wechat-video", "wechat-video")
|
||||
hysteria_protocol:depends("type", "Hysteria")
|
||||
function hysteria_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function hysteria_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
|
||||
hysteria_obfs:depends("type", "Hysteria")
|
||||
|
||||
hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
|
||||
hysteria_auth_type:value("disable", translate("Disable"))
|
||||
hysteria_auth_type:value("string", translate("STRING"))
|
||||
hysteria_auth_type:value("base64", translate("BASE64"))
|
||||
hysteria_auth_type:depends("type", "Hysteria")
|
||||
|
||||
hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
|
||||
hysteria_auth_password.password = true
|
||||
hysteria_auth_password:depends("hysteria_auth_type", "string")
|
||||
hysteria_auth_password:depends("hysteria_auth_type", "base64")
|
||||
|
||||
hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
|
||||
hysteria_alpn:depends("type", "Hysteria")
|
||||
|
||||
ss_encrypt_method = s:option(Value, "ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(ss_encrypt_method_list) do ss_encrypt_method:value(t) end
|
||||
ss_encrypt_method:depends("type", "SS")
|
||||
function ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
ss_rust_encrypt_method = s:option(Value, "ss_rust_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(ss_rust_encrypt_method_list) do ss_rust_encrypt_method:value(t) end
|
||||
ss_rust_encrypt_method:depends("type", "SS-Rust")
|
||||
function ss_rust_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function ss_rust_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
ssr_encrypt_method = s:option(Value, "ssr_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
|
||||
ssr_encrypt_method:depends("type", "SSR")
|
||||
function ssr_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function ssr_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
security = s:option(ListValue, "security", translate("Encrypt Method"))
|
||||
for a, t in ipairs(security_list) do security:value(t) end
|
||||
security:depends({ type = "V2ray", protocol = "vmess" })
|
||||
security:depends({ type = "Xray", protocol = "vmess" })
|
||||
|
||||
encryption = s:option(Value, "encryption", translate("Encrypt Method"))
|
||||
encryption.default = "none"
|
||||
encryption:value("none")
|
||||
encryption:depends({ type = "V2ray", protocol = "vless" })
|
||||
encryption:depends({ type = "Xray", protocol = "vless" })
|
||||
|
||||
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
|
||||
v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
function v_ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function v_ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end
|
||||
x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
function x_ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function x_ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
iv_check = s:option(Flag, "iv_check", translate("IV Check"))
|
||||
iv_check:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
iv_check:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
|
||||
ssr_protocol = s:option(Value, "ssr_protocol", translate("Protocol"))
|
||||
for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end
|
||||
ssr_protocol:depends("type", "SSR")
|
||||
function ssr_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function ssr_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
protocol_param = s:option(Value, "protocol_param", translate("Protocol_param"))
|
||||
protocol_param:depends("type", "SSR")
|
||||
|
||||
obfs = s:option(Value, "obfs", translate("Obfs"))
|
||||
for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end
|
||||
obfs:depends("type", "SSR")
|
||||
|
||||
obfs_param = s:option(Value, "obfs_param", translate("Obfs_param"))
|
||||
obfs_param:depends("type", "SSR")
|
||||
|
||||
timeout = s:option(Value, "timeout", translate("Connection Timeout"))
|
||||
timeout.datatype = "uinteger"
|
||||
timeout.default = 300
|
||||
timeout:depends("type", "SS")
|
||||
timeout:depends("type", "SS-Rust")
|
||||
timeout:depends("type", "SSR")
|
||||
|
||||
tcp_fast_open = s:option(ListValue, "tcp_fast_open", translate("TCP Fast Open"), translate("Need node support required"))
|
||||
tcp_fast_open:value("false")
|
||||
tcp_fast_open:value("true")
|
||||
tcp_fast_open:depends("type", "SS")
|
||||
tcp_fast_open:depends("type", "SS-Rust")
|
||||
tcp_fast_open:depends("type", "SSR")
|
||||
tcp_fast_open:depends("type", "Trojan")
|
||||
tcp_fast_open:depends("type", "Trojan-Plus")
|
||||
tcp_fast_open:depends("type", "Trojan-Go")
|
||||
|
||||
ss_plugin = s:option(ListValue, "ss_plugin", translate("plugin"))
|
||||
ss_plugin:value("none", translate("none"))
|
||||
if api.is_finded("xray-plugin") then ss_plugin:value("xray-plugin") end
|
||||
if api.is_finded("v2ray-plugin") then ss_plugin:value("v2ray-plugin") end
|
||||
if api.is_finded("obfs-local") then ss_plugin:value("obfs-local") end
|
||||
ss_plugin:depends("type", "SS")
|
||||
ss_plugin:depends("type", "SS-Rust")
|
||||
function ss_plugin.cfgvalue(self, section)
|
||||
return m:get(section, "plugin")
|
||||
end
|
||||
function ss_plugin.write(self, section, value)
|
||||
m:set(section, "plugin", value)
|
||||
end
|
||||
|
||||
ss_plugin_opts = s:option(Value, "ss_plugin_opts", translate("opts"))
|
||||
ss_plugin_opts:depends("ss_plugin", "xray-plugin")
|
||||
ss_plugin_opts:depends("ss_plugin", "v2ray-plugin")
|
||||
ss_plugin_opts:depends("ss_plugin", "obfs-local")
|
||||
function ss_plugin_opts.cfgvalue(self, section)
|
||||
return m:get(section, "plugin_opts")
|
||||
end
|
||||
function ss_plugin_opts.write(self, section, value)
|
||||
m:set(section, "plugin_opts", value)
|
||||
end
|
||||
|
||||
uuid = s:option(Value, "uuid", translate("ID"))
|
||||
uuid.password = true
|
||||
uuid:depends({ type = "V2ray", protocol = "vmess" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vless" })
|
||||
uuid:depends({ type = "Xray", protocol = "vmess" })
|
||||
uuid:depends({ type = "Xray", protocol = "vless" })
|
||||
|
||||
tls = s:option(Flag, "tls", translate("TLS"))
|
||||
tls.default = 0
|
||||
tls.validate = function(self, value, t)
|
||||
if value then
|
||||
local type = type:formvalue(t) or ""
|
||||
if value == "0" and (type == "Trojan" or type == "Trojan-Plus") then
|
||||
return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
tls:depends({ type = "V2ray", protocol = "vmess" })
|
||||
tls:depends({ type = "V2ray", protocol = "vless" })
|
||||
tls:depends({ type = "V2ray", protocol = "socks" })
|
||||
tls:depends({ type = "V2ray", protocol = "trojan" })
|
||||
tls:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
tls:depends({ type = "Xray", protocol = "vmess" })
|
||||
tls:depends({ type = "Xray", protocol = "vless" })
|
||||
tls:depends({ type = "Xray", protocol = "socks" })
|
||||
tls:depends({ type = "Xray", protocol = "trojan" })
|
||||
tls:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
tls:depends("type", "Trojan")
|
||||
tls:depends("type", "Trojan-Plus")
|
||||
tls:depends("type", "Trojan-Go")
|
||||
|
||||
xtls = s:option(Flag, "xtls", translate("XTLS"))
|
||||
xtls.default = 0
|
||||
xtls:depends({ type = "Xray", protocol = "vless", tls = true })
|
||||
xtls:depends({ type = "Xray", protocol = "trojan", tls = true })
|
||||
|
||||
flow = s:option(Value, "flow", translate("flow"))
|
||||
flow.default = "xtls-rprx-direct"
|
||||
flow:value("xtls-rprx-origin")
|
||||
flow:value("xtls-rprx-origin-udp443")
|
||||
flow:value("xtls-rprx-direct")
|
||||
flow:value("xtls-rprx-direct-udp443")
|
||||
flow:value("xtls-rprx-splice")
|
||||
flow:value("xtls-rprx-splice-udp443")
|
||||
flow:depends("xtls", true)
|
||||
|
||||
alpn = s:option(ListValue, "alpn", translate("alpn"))
|
||||
alpn.default = "default"
|
||||
alpn:value("default", translate("Default"))
|
||||
alpn:value("h2,http/1.1")
|
||||
alpn:value("h2")
|
||||
alpn:value("http/1.1")
|
||||
alpn:depends({ type = "V2ray", tls = true })
|
||||
alpn:depends({ type = "Xray", tls = true })
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
|
||||
tls_sessionTicket.default = "0"
|
||||
tls_sessionTicket:depends({ type = "Trojan", tls = true })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Plus", tls = true })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Go", tls = true })
|
||||
|
||||
trojan_go_fingerprint = s:option(ListValue, "trojan_go_fingerprint", translate("Finger Print"))
|
||||
trojan_go_fingerprint:value("disable", translate("Disable"))
|
||||
trojan_go_fingerprint:value("firefox")
|
||||
trojan_go_fingerprint:value("chrome")
|
||||
trojan_go_fingerprint:value("ios")
|
||||
trojan_go_fingerprint.default = "disable"
|
||||
trojan_go_fingerprint:depends({ type = "Trojan-Go", tls = true })
|
||||
function trojan_go_fingerprint.cfgvalue(self, section)
|
||||
return m:get(section, "fingerprint")
|
||||
end
|
||||
function trojan_go_fingerprint.write(self, section, value)
|
||||
m:set(section, "fingerprint", value)
|
||||
end
|
||||
|
||||
tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
|
||||
tls_serverName:depends("tls", true)
|
||||
tls_serverName:depends("type", "Hysteria")
|
||||
|
||||
tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
tls_allowInsecure.default = "0"
|
||||
tls_allowInsecure:depends("tls", true)
|
||||
tls_allowInsecure:depends("type", "Hysteria")
|
||||
|
||||
xray_fingerprint = s:option(ListValue, "xray_fingerprint", translate("Finger Print"))
|
||||
xray_fingerprint:value("disable", translate("Disable"))
|
||||
xray_fingerprint:value("chrome")
|
||||
xray_fingerprint:value("firefox")
|
||||
xray_fingerprint:value("safari")
|
||||
xray_fingerprint:value("randomized")
|
||||
xray_fingerprint.default = "disable"
|
||||
xray_fingerprint:depends({ type = "Xray", tls = true, xtls = false })
|
||||
function xray_fingerprint.cfgvalue(self, section)
|
||||
return m:get(section, "fingerprint")
|
||||
end
|
||||
function xray_fingerprint.write(self, section, value)
|
||||
m:set(section, "fingerprint", value)
|
||||
end
|
||||
|
||||
trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
|
||||
trojan_transport:value("original", translate("Original"))
|
||||
trojan_transport:value("ws", "WebSocket")
|
||||
trojan_transport.default = "original"
|
||||
trojan_transport:depends("type", "Trojan-Go")
|
||||
|
||||
trojan_plugin = s:option(ListValue, "plugin_type", translate("Transport Plugin"))
|
||||
trojan_plugin:value("plaintext", "Plain Text")
|
||||
trojan_plugin:value("shadowsocks", "ShadowSocks")
|
||||
trojan_plugin:value("other", "Other")
|
||||
trojan_plugin.default = "plaintext"
|
||||
trojan_plugin:depends({ tls = false, trojan_transport = "original" })
|
||||
|
||||
trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary"))
|
||||
trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin"
|
||||
trojan_plugin_cmd:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_cmd:depends({ plugin_type = "other" })
|
||||
|
||||
trojan_plugin_op = s:option(Value, "plugin_option", translate("Plugin Option"))
|
||||
trojan_plugin_op.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
|
||||
trojan_plugin_op:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_op:depends({ plugin_type = "other" })
|
||||
|
||||
trojan_plugin_arg = s:option(DynamicList, "plugin_arg", translate("Plugin Option Args"))
|
||||
trojan_plugin_arg.placeholder = "eg: [\"-config\", \"test.json\"]"
|
||||
trojan_plugin_arg:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_arg:depends({ plugin_type = "other" })
|
||||
|
||||
transport = s:option(ListValue, "transport", translate("Transport"))
|
||||
transport:value("tcp", "TCP")
|
||||
transport:value("mkcp", "mKCP")
|
||||
transport:value("ws", "WebSocket")
|
||||
transport:value("h2", "HTTP/2")
|
||||
transport:value("ds", "DomainSocket")
|
||||
transport:value("quic", "QUIC")
|
||||
transport:value("grpc", "gRPC")
|
||||
transport:depends({ type = "V2ray", protocol = "vmess" })
|
||||
transport:depends({ type = "V2ray", protocol = "vless" })
|
||||
transport:depends({ type = "V2ray", protocol = "socks" })
|
||||
transport:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
transport:depends({ type = "V2ray", protocol = "trojan" })
|
||||
transport:depends({ type = "Xray", protocol = "vmess" })
|
||||
transport:depends({ type = "Xray", protocol = "vless" })
|
||||
transport:depends({ type = "Xray", protocol = "socks" })
|
||||
transport:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
transport:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
--[[
|
||||
ss_transport = s:option(ListValue, "ss_transport", translate("Transport"))
|
||||
ss_transport:value("ws", "WebSocket")
|
||||
ss_transport:value("h2", "HTTP/2")
|
||||
ss_transport:value("h2+ws", "HTTP/2 & WebSocket")
|
||||
ss_transport:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
ss_transport:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
]]--
|
||||
|
||||
-- [[ TCP部分 ]]--
|
||||
|
||||
-- TCP伪装
|
||||
tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
|
||||
tcp_guise:value("none", "none")
|
||||
tcp_guise:value("http", "http")
|
||||
tcp_guise:depends("transport", "tcp")
|
||||
|
||||
-- HTTP域名
|
||||
tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
|
||||
tcp_guise_http_host:depends("tcp_guise", "http")
|
||||
|
||||
-- HTTP路径
|
||||
tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
|
||||
tcp_guise_http_path:depends("tcp_guise", "http")
|
||||
|
||||
-- [[ mKCP部分 ]]--
|
||||
|
||||
mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
|
||||
for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end
|
||||
mkcp_guise:depends("transport", "mkcp")
|
||||
|
||||
mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
|
||||
mkcp_mtu.default = "1350"
|
||||
mkcp_mtu:depends("transport", "mkcp")
|
||||
|
||||
mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
|
||||
mkcp_tti.default = "20"
|
||||
mkcp_tti:depends("transport", "mkcp")
|
||||
|
||||
mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
|
||||
mkcp_uplinkCapacity.default = "5"
|
||||
mkcp_uplinkCapacity:depends("transport", "mkcp")
|
||||
|
||||
mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
|
||||
mkcp_downlinkCapacity.default = "20"
|
||||
mkcp_downlinkCapacity:depends("transport", "mkcp")
|
||||
|
||||
mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
|
||||
mkcp_congestion:depends("transport", "mkcp")
|
||||
|
||||
mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
|
||||
mkcp_readBufferSize.default = "1"
|
||||
mkcp_readBufferSize:depends("transport", "mkcp")
|
||||
|
||||
mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
|
||||
mkcp_writeBufferSize.default = "1"
|
||||
mkcp_writeBufferSize:depends("transport", "mkcp")
|
||||
|
||||
mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed"))
|
||||
mkcp_seed:depends("transport", "mkcp")
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
|
||||
ws_host:depends("transport", "ws")
|
||||
ws_host:depends("ss_transport", "ws")
|
||||
ws_host:depends("trojan_transport", "ws")
|
||||
|
||||
ws_path = s:option(Value, "ws_path", translate("WebSocket Path"))
|
||||
ws_path:depends("transport", "ws")
|
||||
ws_path:depends("ss_transport", "ws")
|
||||
ws_path:depends("trojan_transport", "ws")
|
||||
ws_path:depends({ type = "Brook", brook_protocol = "wsclient" })
|
||||
|
||||
ws_enableEarlyData = s:option(Flag, "ws_enableEarlyData", translate("Enable early data"))
|
||||
ws_enableEarlyData:depends("transport", "ws")
|
||||
|
||||
ws_maxEarlyData = s:option(Value, "ws_maxEarlyData", translate("Early data length"))
|
||||
ws_maxEarlyData.default = "1024"
|
||||
ws_maxEarlyData:depends("ws_enableEarlyData", true)
|
||||
function ws_maxEarlyData.cfgvalue(self, section)
|
||||
return m:get(section, "ws_maxEarlyData")
|
||||
end
|
||||
function ws_maxEarlyData.write(self, section, value)
|
||||
m:set(section, "ws_maxEarlyData", value)
|
||||
end
|
||||
|
||||
-- [[ HTTP/2部分 ]]--
|
||||
h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
|
||||
h2_host:depends("transport", "h2")
|
||||
h2_host:depends("ss_transport", "h2")
|
||||
|
||||
h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path"))
|
||||
h2_path:depends("transport", "h2")
|
||||
h2_path:depends("ss_transport", "h2")
|
||||
|
||||
h2_health_check = s:option(Flag, "h2_health_check", translate("Health check"))
|
||||
h2_health_check:depends({ type = "Xray", transport = "h2"})
|
||||
|
||||
h2_read_idle_timeout = s:option(Value, "h2_read_idle_timeout", translate("Idle timeout"))
|
||||
h2_read_idle_timeout.default = "10"
|
||||
h2_read_idle_timeout:depends("h2_health_check", true)
|
||||
|
||||
h2_health_check_timeout = s:option(Value, "h2_health_check_timeout", translate("Health check timeout"))
|
||||
h2_health_check_timeout.default = "15"
|
||||
h2_health_check_timeout:depends("h2_health_check", true)
|
||||
|
||||
-- [[ DomainSocket部分 ]]--
|
||||
ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
|
||||
ds_path:depends("transport", "ds")
|
||||
|
||||
-- [[ QUIC部分 ]]--
|
||||
quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method"))
|
||||
quic_security:value("none")
|
||||
quic_security:value("aes-128-gcm")
|
||||
quic_security:value("chacha20-poly1305")
|
||||
quic_security:depends("transport", "quic")
|
||||
|
||||
quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
|
||||
quic_key:depends("transport", "quic")
|
||||
|
||||
quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
|
||||
for a, t in ipairs(header_type_list) do quic_guise:value(t) end
|
||||
quic_guise:depends("transport", "quic")
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName")
|
||||
grpc_serviceName:depends("transport", "grpc")
|
||||
|
||||
grpc_mode = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode"))
|
||||
grpc_mode:value("gun")
|
||||
grpc_mode:value("multi")
|
||||
grpc_mode:depends({ type = "Xray", transport = "grpc"})
|
||||
|
||||
grpc_health_check = s:option(Flag, "grpc_health_check", translate("Health check"))
|
||||
grpc_health_check:depends({ type = "Xray", transport = "grpc"})
|
||||
|
||||
grpc_idle_timeout = s:option(Value, "grpc_idle_timeout", translate("Idle timeout"))
|
||||
grpc_idle_timeout.default = "10"
|
||||
grpc_idle_timeout:depends("grpc_health_check", true)
|
||||
|
||||
grpc_health_check_timeout = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout"))
|
||||
grpc_health_check_timeout.default = "20"
|
||||
grpc_health_check_timeout:depends("grpc_health_check", true)
|
||||
|
||||
grpc_permit_without_stream = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream"))
|
||||
grpc_permit_without_stream.default = "0"
|
||||
grpc_permit_without_stream:depends("grpc_health_check", true)
|
||||
|
||||
grpc_initial_windows_size = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size"))
|
||||
grpc_initial_windows_size.default = "0"
|
||||
grpc_initial_windows_size:depends({ type = "Xray", transport = "grpc"})
|
||||
|
||||
-- [[ Trojan-Go Shadowsocks2 ]] --
|
||||
ss_aead = s:option(Flag, "ss_aead", translate("Shadowsocks secondary encryption"))
|
||||
ss_aead:depends("type", "Trojan-Go")
|
||||
ss_aead.default = "0"
|
||||
|
||||
ss_aead_method = s:option(ListValue, "ss_aead_method", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss_aead) do ss_aead_method:value(v, v) end
|
||||
ss_aead_method.default = "aes-128-gcm"
|
||||
ss_aead_method:depends("ss_aead", "1")
|
||||
|
||||
ss_aead_pwd = s:option(Value, "ss_aead_pwd", translate("Password"))
|
||||
ss_aead_pwd.password = true
|
||||
ss_aead_pwd:depends("ss_aead", "1")
|
||||
|
||||
-- [[ Trojan-Go Mux ]]--
|
||||
mux = s:option(Flag, "smux", translate("Smux"))
|
||||
mux:depends("type", "Trojan-Go")
|
||||
|
||||
-- [[ Mux ]]--
|
||||
mux = s:option(Flag, "mux", translate("Mux"))
|
||||
mux:depends({ type = "V2ray", protocol = "vmess" })
|
||||
mux:depends({ type = "V2ray", protocol = "vless", xtls = false })
|
||||
mux:depends({ type = "V2ray", protocol = "http" })
|
||||
mux:depends({ type = "V2ray", protocol = "socks" })
|
||||
mux:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
mux:depends({ type = "V2ray", protocol = "trojan" })
|
||||
mux:depends({ type = "Xray", protocol = "vmess" })
|
||||
mux:depends({ type = "Xray", protocol = "vless", xtls = false })
|
||||
mux:depends({ type = "Xray", protocol = "http" })
|
||||
mux:depends({ type = "Xray", protocol = "socks" })
|
||||
mux:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
mux:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
mux_concurrency = s:option(Value, "mux_concurrency", translate("Mux concurrency"))
|
||||
mux_concurrency.default = 8
|
||||
mux_concurrency:depends("mux", true)
|
||||
mux_concurrency:depends("smux", true)
|
||||
|
||||
smux_idle_timeout = s:option(Value, "smux_idle_timeout", translate("Mux idle timeout"))
|
||||
smux_idle_timeout.default = 60
|
||||
smux_idle_timeout:depends("smux", true)
|
||||
|
||||
hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
|
||||
hysteria_up_mbps.default = "10"
|
||||
hysteria_up_mbps:depends("type", "Hysteria")
|
||||
|
||||
hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
|
||||
hysteria_down_mbps.default = "50"
|
||||
hysteria_down_mbps:depends("type", "Hysteria")
|
||||
|
||||
hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
|
||||
hysteria_recv_window_conn:depends("type", "Hysteria")
|
||||
|
||||
hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
|
||||
hysteria_recv_window:depends("type", "Hysteria")
|
||||
|
||||
hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
|
||||
hysteria_disable_mtu_discovery:depends("type", "Hysteria")
|
||||
|
||||
protocol.validate = function(self, value)
|
||||
if value == "_shunt" or value == "_balancing" then
|
||||
address.rmempty = true
|
||||
port.rmempty = true
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
return m
|
151
luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua
Normal file
151
luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua
Normal file
@ -0,0 +1,151 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local datatypes = api.datatypes
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ Other Settings ]]--
|
||||
s = m:section(TypedSection, "global_other")
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(MultiValue, "nodes_ping", " ")
|
||||
o:value("auto_ping", translate("Auto Ping"), translate("This will automatically ping the node for latency"))
|
||||
o:value("tcping", translate("Tcping"), translate("This will use tcping replace ping detection of node"))
|
||||
o:value("info", translate("Show server address and port"), translate("Show server address and port"))
|
||||
|
||||
-- [[ Add the node via the link ]]--
|
||||
s:append(Template(appname .. "/node_list/link_add_node"))
|
||||
|
||||
local nodes_ping = m:get("@global_other[0]", "nodes_ping") or ""
|
||||
|
||||
-- [[ Node List ]]--
|
||||
s = m:section(TypedSection, "nodes")
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "cbi/tblsection"
|
||||
s.extedit = api.url("node_config", "%s")
|
||||
function s.create(e, t)
|
||||
local uuid = api.gen_uuid()
|
||||
t = uuid
|
||||
TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
end
|
||||
|
||||
function s.remove(e, t)
|
||||
m.uci:foreach(appname, "socks", function(s)
|
||||
if s["node"] == t then
|
||||
m:del(s[".name"])
|
||||
end
|
||||
end)
|
||||
m.uci:foreach(appname, "haproxy_config", function(s)
|
||||
if s["lbss"] and s["lbss"] == t then
|
||||
m:del(s[".name"])
|
||||
end
|
||||
end)
|
||||
m.uci:foreach(appname, "acl_rule", function(s)
|
||||
if s["tcp_node"] and s["tcp_node"] == t then
|
||||
m:set(s[".name"], "tcp_node", "default")
|
||||
end
|
||||
if s["udp_node"] and s["udp_node"] == t then
|
||||
m:set(s[".name"], "udp_node", "default")
|
||||
end
|
||||
end)
|
||||
for k, v in ipairs(m:get("@auto_switch[0]", "tcp_node") or {}) do
|
||||
if v and v == t then
|
||||
sys.call(string.format("uci -q del_list %s.@auto_switch[0].tcp_node='%s'", appname, v))
|
||||
end
|
||||
end
|
||||
TypedSection.remove(e, t)
|
||||
local new_node = "nil"
|
||||
local node0 = m:get("@nodes[0]") or nil
|
||||
if node0 then
|
||||
new_node = node0[".name"]
|
||||
end
|
||||
if (m:get("@global[0]", "tcp_node") or "nil") == t then
|
||||
m:set('@global[0]', "tcp_node", new_node)
|
||||
end
|
||||
if (m:get("@global[0]", "udp_node") or "nil") == t then
|
||||
m:set('@global[0]', "udp_node", new_node)
|
||||
end
|
||||
end
|
||||
|
||||
s.sortable = true
|
||||
-- 简洁模式
|
||||
o = s:option(DummyValue, "add_from", "")
|
||||
o.cfgvalue = function(t, n)
|
||||
local v = Value.cfgvalue(t, n)
|
||||
if v and v ~= '' then
|
||||
local group = m:get(n, "group") or ""
|
||||
if group ~= "" then
|
||||
v = v .. " " .. group
|
||||
end
|
||||
return v
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
o = s:option(DummyValue, "remarks", translate("Remarks"))
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
local str = ""
|
||||
local is_sub = m:get(n, "is_sub") or ""
|
||||
local group = m:get(n, "group") or ""
|
||||
local remarks = m:get(n, "remarks") or ""
|
||||
local type = m:get(n, "type") or ""
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.type' value='%s'/>", appname, n, type)
|
||||
if type == "V2ray" or type == "Xray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
if protocol == "_balancing" then
|
||||
protocol = translate("Balancing")
|
||||
elseif protocol == "_shunt" then
|
||||
protocol = translate("Shunt")
|
||||
elseif protocol == "vmess" then
|
||||
protocol = "VMess"
|
||||
elseif protocol == "vless" then
|
||||
protocol = "VLESS"
|
||||
else
|
||||
protocol = protocol:gsub("^%l",string.upper)
|
||||
end
|
||||
type = type .. " " .. protocol
|
||||
end
|
||||
local address = m:get(n, "address") or ""
|
||||
local port = m:get(n, "port") or ""
|
||||
str = str .. translate(type) .. ":" .. remarks
|
||||
if address ~= "" and port ~= "" then
|
||||
if nodes_ping:find("info") then
|
||||
if datatypes.ip6addr(address) then
|
||||
str = str .. string.format("([%s]:%s)", address, port)
|
||||
else
|
||||
str = str .. string.format("(%s:%s)", address, port)
|
||||
end
|
||||
end
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.address' value='%s'/>", appname, n, address)
|
||||
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.port' value='%s'/>", appname, n, port)
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
---- Ping
|
||||
o = s:option(DummyValue, "ping")
|
||||
o.width = "8%"
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
local result = "---"
|
||||
if not nodes_ping:find("auto_ping") then
|
||||
result = string.format('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'%s\',this)">Ping</a></span>', n)
|
||||
else
|
||||
result = string.format('<span class="ping_value" cbiid="%s">---</span>', n)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "_url_test")
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
return string.format('<input type="button" class="cbi-button" value="%s" onclick="javascript:urltest_node(\'%s\',this)"', translate("Availability test"), n)
|
||||
end
|
||||
|
||||
m:append(Template(appname .. "/node_list/node_list"))
|
||||
|
||||
return m
|
@ -0,0 +1,142 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local has_ss = api.is_finded("ss-redir")
|
||||
local has_ss_rust = api.is_finded("sslocal")
|
||||
local has_trojan_plus = api.is_finded("trojan-plus")
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
local has_trojan_go = api.is_finded("trojan-go")
|
||||
local ss_aead_type = {}
|
||||
local trojan_type = {}
|
||||
if has_ss then
|
||||
ss_aead_type[#ss_aead_type + 1] = "shadowsocks-libev"
|
||||
end
|
||||
if has_ss_rust then
|
||||
ss_aead_type[#ss_aead_type + 1] = "shadowsocks-rust"
|
||||
end
|
||||
if has_trojan_plus then
|
||||
trojan_type[#trojan_type + 1] = "trojan-plus"
|
||||
end
|
||||
if has_v2ray then
|
||||
trojan_type[#trojan_type + 1] = "v2ray"
|
||||
ss_aead_type[#ss_aead_type + 1] = "v2ray"
|
||||
end
|
||||
if has_xray then
|
||||
trojan_type[#trojan_type + 1] = "xray"
|
||||
ss_aead_type[#ss_aead_type + 1] = "xray"
|
||||
end
|
||||
if has_trojan_go then
|
||||
trojan_type[#trojan_type + 1] = "trojan-go"
|
||||
end
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ Subscribe Settings ]]--
|
||||
s = m:section(TypedSection, "global_subscribe", "")
|
||||
s.anonymous = true
|
||||
|
||||
---- Subscribe via proxy
|
||||
o = s:option(Flag, "subscribe_proxy", translate("Subscribe via proxy"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode"))
|
||||
o:value("0", translate("Close"))
|
||||
o:value("1", translate("Discard List"))
|
||||
o:value("2", translate("Keep List"))
|
||||
o:value("3", translate("Discard List,But Keep List First"))
|
||||
o:value("4", translate("Keep List,But Discard List First"))
|
||||
|
||||
o = s:option(DynamicList, "filter_discard_list", translate("Discard List"))
|
||||
|
||||
o = s:option(DynamicList, "filter_keep_list", translate("Keep List"))
|
||||
|
||||
if #ss_aead_type > 0 then
|
||||
o = s:option(ListValue, "ss_aead_type", translate("SS AEAD Node Use Type"))
|
||||
for key, value in pairs(ss_aead_type) do
|
||||
o:value(value, translate(value:gsub("^%l",string.upper)))
|
||||
end
|
||||
end
|
||||
|
||||
if #trojan_type > 0 then
|
||||
o = s:option(ListValue, "trojan_type", translate("Trojan Node Use Type"))
|
||||
for key, value in pairs(trojan_type) do
|
||||
o:value(value, translate(value:gsub("^%l",string.upper)))
|
||||
end
|
||||
end
|
||||
|
||||
---- Subscribe Delete All
|
||||
o = s:option(Button, "_stop", translate("Delete All Subscribe Node"))
|
||||
o.inputstyle = "remove"
|
||||
function o.write(e, e)
|
||||
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate > /dev/null 2>&1")
|
||||
end
|
||||
|
||||
o = s:option(Button, "_update", translate("Manual subscription All"))
|
||||
o.inputstyle = "apply"
|
||||
function o.write(t, n)
|
||||
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start > /dev/null 2>&1 &")
|
||||
luci.http.redirect(api.url("log"))
|
||||
end
|
||||
|
||||
s = m:section(TypedSection, "subscribe_list", "", "<font color='red'>" .. translate("Please input the subscription url first, save and submit before manual subscription.") .. "</font>")
|
||||
s.addremove = true
|
||||
s.anonymous = true
|
||||
s.sortable = true
|
||||
s.template = "cbi/tblsection"
|
||||
s.extedit = api.url("node_subscribe_config", "%s")
|
||||
function s.create(e, t)
|
||||
local id = TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(id))
|
||||
end
|
||||
|
||||
o = s:option(Value, "remark", translate("Remarks"))
|
||||
o.width = "auto"
|
||||
o.rmempty = false
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
local count = 0
|
||||
m.uci:foreach(appname, "subscribe_list", function(e)
|
||||
if e[".name"] ~= t and e["remark"] == value then
|
||||
count = count + 1
|
||||
end
|
||||
end)
|
||||
if count > 0 then
|
||||
return nil, translate("This remark already exists, please change a new remark.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "_node_count")
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
local remark = m:get(n, "remark") or ""
|
||||
local num = 0
|
||||
m.uci:foreach(appname, "nodes", function(s)
|
||||
if s["add_from"] ~= "" and s["add_from"] == remark then
|
||||
num = num + 1
|
||||
end
|
||||
end)
|
||||
return string.format("<span title='%s' style='color:red'>%s</span>", remark .. " " .. translate("Node num") .. ": " .. num, num)
|
||||
end
|
||||
|
||||
o = s:option(Value, "url", translate("Subscribe URL"))
|
||||
o.width = "auto"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Button, "_remove", translate("Delete the subscribed node"))
|
||||
o.inputstyle = "remove"
|
||||
function o.write(t, n)
|
||||
local remark = m:get(n, "remark") or ""
|
||||
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate " .. remark .. " > /dev/null 2>&1")
|
||||
end
|
||||
|
||||
o = s:option(Button, "_update", translate("Manual subscription"))
|
||||
o.inputstyle = "apply"
|
||||
function o.write(t, n)
|
||||
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start " .. n .. " > /dev/null 2>&1 &")
|
||||
luci.http.redirect(api.url("log"))
|
||||
end
|
||||
|
||||
return m
|
@ -0,0 +1,110 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local has_ss = api.is_finded("ss-redir")
|
||||
local has_ss_rust = api.is_finded("sslocal")
|
||||
local has_trojan_plus = api.is_finded("trojan-plus")
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
local has_trojan_go = api.is_finded("trojan-go")
|
||||
local ss_aead_type = {}
|
||||
local trojan_type = {}
|
||||
if has_ss then
|
||||
ss_aead_type[#ss_aead_type + 1] = "shadowsocks-libev"
|
||||
end
|
||||
if has_ss_rust then
|
||||
ss_aead_type[#ss_aead_type + 1] = "shadowsocks-rust"
|
||||
end
|
||||
if has_trojan_plus then
|
||||
trojan_type[#trojan_type + 1] = "trojan-plus"
|
||||
end
|
||||
if has_v2ray then
|
||||
trojan_type[#trojan_type + 1] = "v2ray"
|
||||
ss_aead_type[#ss_aead_type + 1] = "v2ray"
|
||||
end
|
||||
if has_xray then
|
||||
trojan_type[#trojan_type + 1] = "xray"
|
||||
ss_aead_type[#ss_aead_type + 1] = "xray"
|
||||
end
|
||||
if has_trojan_go then
|
||||
trojan_type[#trojan_type + 1] = "trojan-go"
|
||||
end
|
||||
|
||||
m = Map(appname)
|
||||
m.redirect = api.url("node_subscribe")
|
||||
|
||||
s = m:section(NamedSection, arg[1])
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
o = s:option(Value, "remark", translate("Subscribe Remark"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(TextValue, "url", translate("Subscribe URL"))
|
||||
o.rows = 5
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Flag, "allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode"))
|
||||
o.default = "5"
|
||||
o:value("0", translate("Close"))
|
||||
o:value("1", translate("Discard List"))
|
||||
o:value("2", translate("Keep List"))
|
||||
o:value("3", translate("Discard List,But Keep List First"))
|
||||
o:value("4", translate("Keep List,But Discard List First"))
|
||||
o:value("5", translate("Use global config"))
|
||||
|
||||
o = s:option(DynamicList, "filter_discard_list", translate("Discard List"))
|
||||
o:depends("filter_keyword_mode", "1")
|
||||
o:depends("filter_keyword_mode", "3")
|
||||
o:depends("filter_keyword_mode", "4")
|
||||
|
||||
o = s:option(DynamicList, "filter_keep_list", translate("Keep List"))
|
||||
o:depends("filter_keyword_mode", "2")
|
||||
o:depends("filter_keyword_mode", "3")
|
||||
o:depends("filter_keyword_mode", "4")
|
||||
|
||||
if #ss_aead_type > 0 then
|
||||
o = s:option(ListValue, "ss_aead_type", translate("SS AEAD Node Use Type"))
|
||||
o.default = "global"
|
||||
o:value("global", translate("Use global config"))
|
||||
for key, value in pairs(ss_aead_type) do
|
||||
o:value(value, translate(value:gsub("^%l",string.upper)))
|
||||
end
|
||||
end
|
||||
|
||||
if #trojan_type > 0 then
|
||||
o = s:option(ListValue, "trojan_type", translate("Trojan Node Use Type"))
|
||||
o.default = "global"
|
||||
o:value("global", translate("Use global config"))
|
||||
for key, value in pairs(trojan_type) do
|
||||
o:value(value, translate(value:gsub("^%l",string.upper)))
|
||||
end
|
||||
end
|
||||
|
||||
---- Enable auto update subscribe
|
||||
o = s:option(Flag, "auto_update", translate("Enable auto update subscribe"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
---- Week update rules
|
||||
o = s:option(ListValue, "week_update", translate("Week update rules"))
|
||||
o:value(7, translate("Every day"))
|
||||
for e = 1, 6 do o:value(e, translate("Week") .. e) end
|
||||
o:value(0, translate("Week") .. translate("day"))
|
||||
o.default = 0
|
||||
o:depends("auto_update", true)
|
||||
|
||||
---- Day update rules
|
||||
o = s:option(ListValue, "time_update", translate("Day update rules"))
|
||||
for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
o.default = 0
|
||||
o:depends("auto_update", true)
|
||||
|
||||
o = s:option(Value, "user_agent", translate("User-Agent"))
|
||||
o.default = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36"
|
||||
|
||||
return m
|
148
luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua
Normal file
148
luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua
Normal file
@ -0,0 +1,148 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
-- [[ Delay Settings ]]--
|
||||
s = m:section(TypedSection, "global_delay", translate("Delay Settings"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
---- Delay Start
|
||||
o = s:option(Value, "start_delay", translate("Delay Start"),
|
||||
translate("Units:seconds"))
|
||||
o.default = "1"
|
||||
o.rmempty = true
|
||||
|
||||
---- Open and close Daemon
|
||||
o = s:option(Flag, "start_daemon", translate("Open and close Daemon"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
--[[
|
||||
---- Open and close automatically
|
||||
o = s:option(Flag, "auto_on", translate("Open and close automatically"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
---- Automatically turn off time
|
||||
o = s:option(ListValue, "time_off", translate("Automatically turn off time"))
|
||||
o.default = nil
|
||||
o:depends("auto_on", true)
|
||||
o:value(nil, translate("Disable"))
|
||||
for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
|
||||
---- Automatically turn on time
|
||||
o = s:option(ListValue, "time_on", translate("Automatically turn on time"))
|
||||
o.default = nil
|
||||
o:depends("auto_on", true)
|
||||
o:value(nil, translate("Disable"))
|
||||
for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
|
||||
---- Automatically restart time
|
||||
o = s:option(ListValue, "time_restart", translate("Automatically restart time"))
|
||||
o.default = nil
|
||||
o:depends("auto_on", true)
|
||||
o:value(nil, translate("Disable"))
|
||||
for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
--]]
|
||||
|
||||
-- [[ Forwarding Settings ]]--
|
||||
s = m:section(TypedSection, "global_forwarding",
|
||||
translate("Forwarding Settings"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
---- TCP No Redir Ports
|
||||
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
|
||||
o.default = "disable"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- UDP No Redir Ports
|
||||
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
|
||||
"<font color='red'>" .. translate(
|
||||
"Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
|
||||
"</font>")
|
||||
o.default = "disable"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("1:65535", translate("All"))
|
||||
|
||||
---- TCP Proxy Drop Ports
|
||||
o = s:option(Value, "tcp_proxy_drop_ports", translate("TCP Proxy Drop Ports"))
|
||||
o.default = "disable"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
|
||||
---- UDP Proxy Drop Ports
|
||||
o = s:option(Value, "udp_proxy_drop_ports", translate("UDP Proxy Drop Ports"))
|
||||
o.default = "80,443"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("80,443", translate("QUIC"))
|
||||
|
||||
---- TCP Redir Ports
|
||||
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
|
||||
o.default = "22,25,53,143,465,587,853,993,995,80,443"
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("22,25,53,143,465,587,853,993,995,80,443", translate("Common Use"))
|
||||
o:value("80,443", translate("Only Web"))
|
||||
o:value("80:65535", "80 " .. translate("or more"))
|
||||
o:value("1:443", "443 " .. translate("or less"))
|
||||
|
||||
---- UDP Redir Ports
|
||||
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
|
||||
o.default = "1:65535"
|
||||
o:value("1:65535", translate("All"))
|
||||
o:value("53", "DNS")
|
||||
|
||||
if os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod | grep -i TPROXY >/dev/null") == 0 then
|
||||
o = s:option(ListValue, "tcp_proxy_way", translate("TCP Proxy Way"))
|
||||
o.default = "redirect"
|
||||
o:value("redirect", "REDIRECT")
|
||||
o:value("tproxy", "TPROXY")
|
||||
o:depends("ipv6_tproxy", false)
|
||||
|
||||
o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way"))
|
||||
o.default = "tproxy"
|
||||
o:value("tproxy", "TPROXY")
|
||||
o:depends("ipv6_tproxy", true)
|
||||
o.write = function(self, section, value)
|
||||
return self.map:set(section, "tcp_proxy_way", value)
|
||||
end
|
||||
|
||||
if os.execute("lsmod | grep -i ip6table_mangle >/dev/null") == 0 then
|
||||
---- IPv6 TProxy
|
||||
o = s:option(Flag, "ipv6_tproxy", translate("IPv6 TProxy"),
|
||||
"<font color='red'>" .. translate(
|
||||
"Experimental feature. Make sure that your node supports IPv6.") ..
|
||||
"</font>")
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(Flag, "accept_icmp", translate("Hijacking ICMP (PING)"))
|
||||
o.default = 0
|
||||
|
||||
o = s:option(Flag, "accept_icmpv6", translate("Hijacking ICMPv6 (IPv6 PING)"))
|
||||
o:depends("ipv6_tproxy", true)
|
||||
o.default = 0
|
||||
|
||||
o = s:option(Flag, "sniffing", translate("Sniffing (V2Ray/Xray)"), translate("When using the V2ray/Xray shunt, must be enabled, otherwise the shunt will invalid."))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Flag, "route_only", translate("Sniffing Route Only (Xray)"), translate("When enabled, the server not will resolve the domain name again."))
|
||||
o.default = 0
|
||||
o:depends("sniffing", true)
|
||||
|
||||
local domains_excluded = string.format("/usr/share/%s/rules/domains_excluded", appname)
|
||||
o = s:option(TextValue, "no_sniffing_hosts", translate("No Sniffing Lists"), translate("Hosts added into No Sniffing Lists will not resolve again on server (Xray only)."))
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section) return fs.readfile(domains_excluded) or "" end
|
||||
o.write = function(self, section, value) fs.writefile(domains_excluded, value:gsub("\r\n", "\n")) end
|
||||
o.remove = function(self, section, value) fs.writefile(domains_excluded, "") end
|
||||
o:depends({sniffing = true, route_only = false})
|
||||
|
||||
return m
|
91
luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua
Normal file
91
luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua
Normal file
@ -0,0 +1,91 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
|
||||
m = Map(appname)
|
||||
-- [[ Rule Settings ]]--
|
||||
s = m:section(TypedSection, "global_rules", translate("Rule status"))
|
||||
s.anonymous = true
|
||||
|
||||
--[[
|
||||
o = s:option(Flag, "adblock", translate("Enable adblock"))
|
||||
o.rmempty = false
|
||||
]]--
|
||||
|
||||
---- gfwlist URL
|
||||
o = s:option(DynamicList, "gfwlist_url", translate("GFW domains(gfwlist) Update URL"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
|
||||
o.default = "https://cdn.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt"
|
||||
|
||||
----chnroute URL
|
||||
o = s:option(DynamicList, "chnroute_url", translate("China IPs(chnroute) Update URL"))
|
||||
o:value("https://ispip.clang.cn/all_cn.txt", translate("Clang.CN"))
|
||||
o:value("https://ispip.clang.cn/all_cn_cidr.txt", translate("Clang.CN.CIDR"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/soffchen/GeoIP2-CN@release/CN-ip-cidr.txt", translate("soffchen/GeoIP2-CN"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Hackl0us/GeoIP2-CN@release/CN-ip-cidr.txt", translate("Hackl0us/GeoIP2-CN"))
|
||||
o.default = "https://ispip.clang.cn/all_cn.txt"
|
||||
|
||||
----chnroute6 URL
|
||||
o = s:option(DynamicList, "chnroute6_url", translate("China IPv6s(chnroute6) Update URL"))
|
||||
o:value("https://ispip.clang.cn/all_cn_ipv6.txt", translate("Clang.CN.IPv6"))
|
||||
o.default = "https://ispip.clang.cn/all_cn_ipv6.txt"
|
||||
|
||||
----chnlist URL
|
||||
o = s:option(DynamicList, "chnlist_url", translate("China List(Chnlist) Update URL"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf", translate("felixonmars/domains.china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf", translate("felixonmars/apple.china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf", translate("felixonmars/google.china"))
|
||||
|
||||
s:append(Template(appname .. "/rule/rule_version"))
|
||||
|
||||
---- Auto Update
|
||||
o = s:option(Flag, "auto_update", translate("Enable auto update rules"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
---- Week Update
|
||||
o = s:option(ListValue, "week_update", translate("Week update rules"))
|
||||
o:value(7, translate("Every day"))
|
||||
for e = 1, 6 do o:value(e, translate("Week") .. e) end
|
||||
o:value(0, translate("Week") .. translate("day"))
|
||||
o.default = 0
|
||||
o:depends("auto_update", true)
|
||||
|
||||
---- Time Update
|
||||
o = s:option(ListValue, "time_update", translate("Day update rules"))
|
||||
for e = 0, 23 do o:value(e, e .. translate("oclock")) end
|
||||
o.default = 0
|
||||
o:depends("auto_update", true)
|
||||
|
||||
if has_v2ray or has_xray then
|
||||
o = s:option(Value, "v2ray_location_asset", translate("Location of V2ray/Xray asset"), translate("This variable specifies a directory where geoip.dat and geosite.dat files are."))
|
||||
o.default = "/usr/share/v2ray/"
|
||||
o.rmempty = false
|
||||
|
||||
s = m:section(TypedSection, "shunt_rules", "V2ray/Xray " .. translate("Shunt Rule"), "<a style='color: red'>" .. translate("Please note attention to the priority, the higher the order, the higher the priority.") .. "</a>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.anonymous = false
|
||||
s.addremove = true
|
||||
s.sortable = true
|
||||
s.extedit = api.url("shunt_rules", "%s")
|
||||
function s.create(e, t)
|
||||
TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
end
|
||||
function s.remove(e, t)
|
||||
m.uci:foreach(appname, "nodes", function(s)
|
||||
if s["protocol"] and s["protocol"] == "_shunt" then
|
||||
m:del(s[".name"], t)
|
||||
end
|
||||
end)
|
||||
TypedSection.remove(e, t)
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "remarks", translate("Remarks"))
|
||||
end
|
||||
|
||||
return m
|
265
luci-app-passwall/luasrc/model/cbi/passwall/client/rule_list.lua
Normal file
265
luci-app-passwall/luasrc/model/cbi/passwall/client/rule_list.lua
Normal file
@ -0,0 +1,265 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local sys = api.sys
|
||||
local datatypes = api.datatypes
|
||||
local path = string.format("/usr/share/%s/rules/", appname)
|
||||
local route_hosts_path = "/etc/"
|
||||
|
||||
m = SimpleForm(appname)
|
||||
m.uci = api.uci
|
||||
|
||||
-- [[ Rule List Settings ]]--
|
||||
s = m:section(TypedSection, "global_rules")
|
||||
s.anonymous = true
|
||||
|
||||
s:tab("direct_list", translate("Direct List"))
|
||||
s:tab("proxy_list", translate("Proxy List"))
|
||||
s:tab("block_list", translate("Block List"))
|
||||
s:tab("lan_ip_list", translate("Lan IP List"))
|
||||
s:tab("route_hosts", translate("Route Hosts"))
|
||||
|
||||
---- Direct Hosts
|
||||
local direct_host = path .. "direct_host"
|
||||
o = s:taboption("direct_list", TextValue, "direct_host", "", "<font color='red'>" .. translate("Join the direct hosts list of domain names will not proxy.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(direct_host) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(direct_host, value:gsub("\r\n", "\n"))
|
||||
sys.call("rm -rf /tmp/etc/passwall_tmp/dns_*")
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(direct_host, "")
|
||||
sys.call("rm -rf /tmp/etc/passwall_tmp/dns_*")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local hosts= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end)
|
||||
for index, host in ipairs(hosts) do
|
||||
if host:find("#") and host:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not datatypes.hostname(host) then
|
||||
return nil, host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Direct IP
|
||||
local direct_ip = path .. "direct_ip"
|
||||
o = s:taboption("direct_list", TextValue, "direct_ip", "", "<font color='red'>" .. translate("These had been joined ip addresses will not proxy. Please input the ip address or ip address segment,every line can input only one ip address. For example: 192.168.0.0/24 or 223.5.5.5.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(direct_ip) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(direct_ip, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(direct_ip, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("#") and ipmask:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not ( datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask) ) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Proxy Hosts
|
||||
local proxy_host = path .. "proxy_host"
|
||||
o = s:taboption("proxy_list", TextValue, "proxy_host", "", "<font color='red'>" .. translate("These had been joined websites will use proxy. Please input the domain names of websites, every line can input only one website domain. For example: google.com.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(proxy_host) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(proxy_host, value:gsub("\r\n", "\n"))
|
||||
sys.call("rm -rf /tmp/etc/passwall_tmp/dns_*")
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(proxy_host, "")
|
||||
sys.call("rm -rf /tmp/etc/passwall_tmp/dns_*")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local hosts= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end)
|
||||
for index, host in ipairs(hosts) do
|
||||
if host:find("#") and host:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not datatypes.hostname(host) then
|
||||
return nil, host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Proxy IP
|
||||
local proxy_ip = path .. "proxy_ip"
|
||||
o = s:taboption("proxy_list", TextValue, "proxy_ip", "", "<font color='red'>" .. translate("These had been joined ip addresses will use proxy. Please input the ip address or ip address segment, every line can input only one ip address. For example: 35.24.0.0/24 or 8.8.4.4.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(proxy_ip) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(proxy_ip, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(proxy_ip, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("#") and ipmask:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not ( datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask) ) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Block Hosts
|
||||
local block_host = path .. "block_host"
|
||||
o = s:taboption("block_list", TextValue, "block_host", "", "<font color='red'>" .. translate("These had been joined websites will be block. Please input the domain names of websites, every line can input only one website domain. For example: twitter.com.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(block_host) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(block_host, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(block_host, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local hosts= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end)
|
||||
for index, host in ipairs(hosts) do
|
||||
if host:find("#") and host:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not datatypes.hostname(host) then
|
||||
return nil, host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Block IP
|
||||
local block_ip = path .. "block_ip"
|
||||
o = s:taboption("block_list", TextValue, "block_ip", "", "<font color='red'>" .. translate("These had been joined ip addresses will be block. Please input the ip address or ip address segment, every line can input only one ip address.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(block_ip) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(block_ip, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(block_ip, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("#") and ipmask:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not ( datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask) ) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Lan IPv4
|
||||
local lanlist_ipv4 = path .. "lanlist_ipv4"
|
||||
o = s:taboption("lan_ip_list", TextValue, "lanlist_ipv4", "", "<font color='red'>" .. translate("The list is the IPv4 LAN IP list, which represents the direct connection IP of the LAN. If you need the LAN IP in the proxy list, please clear it from the list. Do not modify this list by default.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(lanlist_ipv4) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(lanlist_ipv4, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(lanlist_ipv4, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("#") and ipmask:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not datatypes.ipmask4(ipmask) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IPv4 format, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Lan IPv6
|
||||
local lanlist_ipv6 = path .. "lanlist_ipv6"
|
||||
o = s:taboption("lan_ip_list", TextValue, "lanlist_ipv6", "", "<font color='red'>" .. translate("The list is the IPv6 LAN IP list, which represents the direct connection IP of the LAN. If you need the LAN IP in the proxy list, please clear it from the list. Do not modify this list by default.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(lanlist_ipv6) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(lanlist_ipv6, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(lanlist_ipv6, "")
|
||||
end
|
||||
o.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("#") and ipmask:find("#") == 1 then
|
||||
return value
|
||||
end
|
||||
if not datatypes.ipmask6(ipmask) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IPv6 format, please re-enter!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---- Route Hosts
|
||||
local hosts = route_hosts_path .. "hosts"
|
||||
o = s:taboption("route_hosts", TextValue, "hosts", "", "<font color='red'>" .. translate("Configure routing etc/hosts file, if you don't know what you are doing, please don't change the content.") .. "</font>")
|
||||
o.rows = 15
|
||||
o.wrap = "off"
|
||||
o.cfgvalue = function(self, section)
|
||||
return fs.readfile(hosts) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
fs.writefile(hosts, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
fs.writefile(hosts, "")
|
||||
end
|
||||
|
||||
return m
|
@ -0,0 +1,79 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local appname = api.appname
|
||||
local datatypes = api.datatypes
|
||||
|
||||
m = Map(appname, "V2ray/Xray " .. translate("Shunt Rule"))
|
||||
m.redirect = api.url()
|
||||
|
||||
s = m:section(NamedSection, arg[1], "shunt_rules", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
remarks = s:option(Value, "remarks", translate("Remarks"))
|
||||
remarks.default = arg[1]
|
||||
remarks.rmempty = false
|
||||
|
||||
protocol = s:option(MultiValue, "protocol", translate("Protocol"))
|
||||
protocol:value("http")
|
||||
protocol:value("tls")
|
||||
protocol:value("bittorrent")
|
||||
|
||||
domain_list = s:option(TextValue, "domain_list", translate("Domain"))
|
||||
domain_list.rows = 10
|
||||
domain_list.wrap = "off"
|
||||
domain_list.validate = function(self, value)
|
||||
local hosts= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end)
|
||||
for index, host in ipairs(hosts) do
|
||||
local flag = 1
|
||||
local tmp_host = host
|
||||
if host:find("regexp:") and host:find("regexp:") == 1 then
|
||||
flag = 0
|
||||
elseif host:find("domain:.") and host:find("domain:.") == 1 then
|
||||
tmp_host = host:gsub("domain:", "")
|
||||
elseif host:find("full:.") and host:find("full:.") == 1 then
|
||||
tmp_host = host:gsub("full:", "")
|
||||
elseif host:find("geosite:") and host:find("geosite:") == 1 then
|
||||
flag = 0
|
||||
elseif host:find("ext:") and host:find("ext:") == 1 then
|
||||
flag = 0
|
||||
end
|
||||
if flag == 1 then
|
||||
if not datatypes.hostname(tmp_host) then
|
||||
return nil, tmp_host .. " " .. translate("Not valid domain name, please re-enter!")
|
||||
end
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
domain_list.description = "<br /><ul><li>" .. translate("Plaintext: If this string matches any part of the targeting domain, this rule takes effet. Example: rule 'sina.com' matches targeting domain 'sina.com', 'sina.com.cn' and 'www.sina.com', but not 'sina.cn'.")
|
||||
.. "</li><li>" .. translate("Regular expression: Begining with 'regexp:', the rest is a regular expression. When the regexp matches targeting domain, this rule takes effect. Example: rule 'regexp:\\.goo.*\\.com$' matches 'www.google.com' and 'fonts.googleapis.com', but not 'google.com'.")
|
||||
.. "</li><li>" .. translate("Subdomain (recommended): Begining with 'domain:' and the rest is a domain. When the targeting domain is exactly the value, or is a subdomain of the value, this rule takes effect. Example: rule 'domain:v2ray.com' matches 'www.v2ray.com', 'v2ray.com', but not 'xv2ray.com'.")
|
||||
.. "</li><li>" .. translate("Full domain: Begining with 'full:' and the rest is a domain. When the targeting domain is exactly the value, the rule takes effect. Example: rule 'domain:v2ray.com' matches 'v2ray.com', but not 'www.v2ray.com'.")
|
||||
.. "</li><li>" .. translate("Pre-defined domain list: Begining with 'geosite:' and the rest is a name, such as geosite:google or geosite:cn.")
|
||||
.. "</li><li>" .. translate("Domains from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geosite.dat. The tag must exist in the file.")
|
||||
.. "</li></ul>"
|
||||
ip_list = s:option(TextValue, "ip_list", "IP")
|
||||
ip_list.rows = 10
|
||||
ip_list.wrap = "off"
|
||||
ip_list.validate = function(self, value)
|
||||
local ipmasks= {}
|
||||
string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end)
|
||||
for index, ipmask in ipairs(ipmasks) do
|
||||
if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 then
|
||||
elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 then
|
||||
else
|
||||
if not (datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask)) then
|
||||
return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
|
||||
end
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
ip_list.description = "<br /><ul><li>" .. translate("IP: such as '127.0.0.1'.")
|
||||
.. "</li><li>" .. translate("CIDR: such as '127.0.0.0/8'.")
|
||||
.. "</li><li>" .. translate("GeoIP: such as 'geoip:cn'. It begins with geoip: (lower case) and followed by two letter of country code.")
|
||||
.. "</li><li>" .. translate("IPs from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geoip.dat. The tag must exist in the file.")
|
||||
.. "</li></ul>"
|
||||
|
||||
return m
|
197
luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua
Executable file
197
luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua
Executable file
@ -0,0 +1,197 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local action = arg[1]
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local sys = api.sys
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
local CONFIG = "passwall_server"
|
||||
local CONFIG_PATH = "/tmp/etc/" .. CONFIG
|
||||
local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log"
|
||||
local TMP_BIN_PATH = CONFIG_PATH .. "/bin"
|
||||
local require_dir = "luci.model.cbi.passwall.server.api."
|
||||
|
||||
local ipt_bin = sys.exec("echo -n $(/usr/share/passwall/iptables.sh get_ipt_bin)")
|
||||
local ip6t_bin = sys.exec("echo -n $(/usr/share/passwall/iptables.sh get_ip6t_bin)")
|
||||
|
||||
local function log(...)
|
||||
local f, err = io.open(LOG_APP_FILE, "a")
|
||||
if f and err == nil then
|
||||
local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
f:write(str .. "\n")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function cmd(cmd)
|
||||
sys.call(cmd)
|
||||
end
|
||||
|
||||
local function ipt(arg)
|
||||
cmd(ipt_bin .. " -w " .. arg)
|
||||
end
|
||||
|
||||
local function ip6t(arg)
|
||||
cmd(ip6t_bin .. " -w " .. arg)
|
||||
end
|
||||
|
||||
local function ln_run(s, d, command, output)
|
||||
if not output then
|
||||
output = "/dev/null"
|
||||
end
|
||||
d = TMP_BIN_PATH .. "/" .. d
|
||||
cmd(string.format('[ ! -f "%s" ] && ln -s %s %s 2>/dev/null', d, s, d))
|
||||
return string.format("%s >%s 2>&1 &", d .. " " ..command, output)
|
||||
end
|
||||
|
||||
local function gen_include()
|
||||
cmd(string.format("echo '#!/bin/sh' > /tmp/etc/%s.include", CONFIG))
|
||||
local function extract_rules(n, a)
|
||||
local _ipt = ipt_bin
|
||||
if n == "6" then
|
||||
_ipt = ip6t_bin
|
||||
end
|
||||
local result = "*" .. a
|
||||
result = result .. "\n" .. sys.exec(_ipt .. '-save -t ' .. a .. ' | grep "PSW-SERVER" | sed -e "s/^-A \\(INPUT\\)/-I \\1 1/"')
|
||||
result = result .. "COMMIT"
|
||||
return result
|
||||
end
|
||||
local f, err = io.open("/tmp/etc/" .. CONFIG .. ".include", "a")
|
||||
if f and err == nil then
|
||||
f:write(ipt_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n")
|
||||
f:write(ipt_bin .. '-restore -n <<-EOT' .. "\n")
|
||||
f:write(extract_rules("4", "filter") .. "\n")
|
||||
f:write("EOT" .. "\n")
|
||||
f:write(ip6t_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ip6t_bin .. '-restore -c' .. "\n")
|
||||
f:write(ip6t_bin .. '-restore -n <<-EOT' .. "\n")
|
||||
f:write(extract_rules("6", "filter") .. "\n")
|
||||
f:write("EOT" .. "\n")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function start()
|
||||
local enabled = tonumber(uci:get(CONFIG, "@global[0]", "enable") or 0)
|
||||
if enabled == nil or enabled == 0 then
|
||||
return
|
||||
end
|
||||
cmd(string.format("mkdir -p %s %s", CONFIG_PATH, TMP_BIN_PATH))
|
||||
cmd(string.format("touch %s", LOG_APP_FILE))
|
||||
ipt("-N PSW-SERVER")
|
||||
ipt("-I INPUT -j PSW-SERVER")
|
||||
ip6t("-N PSW-SERVER")
|
||||
ip6t("-I INPUT -j PSW-SERVER")
|
||||
uci:foreach(CONFIG, "user", function(user)
|
||||
local id = user[".name"]
|
||||
local enable = user.enable
|
||||
if enable and tonumber(enable) == 1 then
|
||||
local enable_log = user.log
|
||||
local log_path = nil
|
||||
if enable_log and enable_log == "1" then
|
||||
log_path = CONFIG_PATH .. "/" .. id .. ".log"
|
||||
else
|
||||
log_path = nil
|
||||
end
|
||||
local remarks = user.remarks
|
||||
local port = tonumber(user.port)
|
||||
local bin
|
||||
local config = {}
|
||||
local config_file = CONFIG_PATH .. "/" .. id .. ".json"
|
||||
local udp_forward = 1
|
||||
local type = user.type or ""
|
||||
if type == "Socks" then
|
||||
local auth = ""
|
||||
if user.auth and user.auth == "1" then
|
||||
local username = user.username or ""
|
||||
local password = user.password or ""
|
||||
if username ~= "" and password ~= "" then
|
||||
username = "-u " .. username
|
||||
password = "-P " .. password
|
||||
auth = username .. " " .. password
|
||||
end
|
||||
end
|
||||
bin = ln_run("/usr/bin/microsocks", "microsocks_" .. id, string.format("-i :: -p %s %s", port, auth), log_path)
|
||||
elseif type == "SS" or type == "SSR" then
|
||||
config = require(require_dir .. "shadowsocks").gen_config(user)
|
||||
local udp_param = ""
|
||||
udp_forward = tonumber(user.udp_forward) or 1
|
||||
if udp_forward == 1 then
|
||||
udp_param = "-u"
|
||||
end
|
||||
type = type:lower()
|
||||
bin = ln_run("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param, log_path)
|
||||
elseif type == "V2ray" then
|
||||
config = require(require_dir .. "v2ray").gen_config(user)
|
||||
bin = ln_run(api.get_v2ray_path(), "v2ray", "-config=" .. config_file, log_path)
|
||||
elseif type == "Xray" then
|
||||
config = require(require_dir .. "v2ray").gen_config(user)
|
||||
bin = ln_run(api.get_xray_path(), "xray", "-config=" .. config_file, log_path)
|
||||
elseif type == "Trojan" then
|
||||
config = require(require_dir .. "trojan").gen_config(user)
|
||||
bin = ln_run("/usr/sbin/trojan", "trojan", "-c " .. config_file, log_path)
|
||||
elseif type == "Trojan-Plus" then
|
||||
config = require(require_dir .. "trojan").gen_config(user)
|
||||
bin = ln_run("/usr/sbin/trojan-plus", "trojan-plus", "-c " .. config_file, log_path)
|
||||
elseif type == "Trojan-Go" then
|
||||
config = require(require_dir .. "trojan").gen_config(user)
|
||||
bin = ln_run(api.get_trojan_go_path(), "trojan-go", "-config " .. config_file, log_path)
|
||||
elseif type == "Brook" then
|
||||
local brook_protocol = user.protocol
|
||||
local brook_password = user.password
|
||||
local brook_path = user.ws_path or "/ws"
|
||||
local brook_path_arg = ""
|
||||
if brook_protocol == "wsserver" and brook_path then
|
||||
brook_path_arg = " --path " .. brook_path
|
||||
end
|
||||
bin = ln_run(api.get_brook_path(), "brook_" .. id, string.format("--debug %s -l :%s -p %s%s", brook_protocol, port, brook_password, brook_path_arg), log_path)
|
||||
elseif type == "Hysteria" then
|
||||
config = require(require_dir .. "hysteria").gen_config(user)
|
||||
bin = ln_run(api.get_hysteria_path(), "hysteria", "-c " .. config_file .. " server", log_path)
|
||||
end
|
||||
|
||||
if next(config) then
|
||||
local f, err = io.open(config_file, "w")
|
||||
if f and err == nil then
|
||||
f:write(jsonc.stringify(config, 1))
|
||||
f:close()
|
||||
end
|
||||
log(string.format("%s %s 生成配置文件并运行 - %s", remarks, port, config_file))
|
||||
end
|
||||
|
||||
if bin then
|
||||
cmd(bin)
|
||||
end
|
||||
|
||||
local bind_local = user.bind_local or 0
|
||||
if bind_local and tonumber(bind_local) ~= 1 then
|
||||
ipt(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
||||
ip6t(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
||||
if udp_forward == 1 then
|
||||
ipt(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
||||
ip6t(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
gen_include()
|
||||
end
|
||||
|
||||
local function stop()
|
||||
cmd(string.format("/bin/top -bn1 | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1", CONFIG_PATH))
|
||||
ipt("-D INPUT -j PSW-SERVER 2>/dev/null")
|
||||
ipt("-F PSW-SERVER 2>/dev/null")
|
||||
ipt("-X PSW-SERVER 2>/dev/null")
|
||||
ip6t("-D INPUT -j PSW-SERVER 2>/dev/null")
|
||||
ip6t("-F PSW-SERVER 2>/dev/null")
|
||||
ip6t("-X PSW-SERVER 2>/dev/null")
|
||||
cmd(string.format("rm -rf %s %s /tmp/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG))
|
||||
end
|
||||
|
||||
if action then
|
||||
if action == "start" then
|
||||
start()
|
||||
elseif action == "stop" then
|
||||
stop()
|
||||
end
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
module("luci.model.cbi.passwall.server.api.hysteria", package.seeall)
|
||||
function gen_config(user)
|
||||
local config = {
|
||||
listen = ":" .. user.port,
|
||||
protocol = user.protocol or "udp",
|
||||
obfs = user.hysteria_obfs,
|
||||
cert = user.tls_certificateFile,
|
||||
key = user.tls_keyFile,
|
||||
auth = (user.hysteria_auth_type == "string") and {
|
||||
mode = "password",
|
||||
config = {
|
||||
password = user.hysteria_auth_password
|
||||
}
|
||||
} or nil,
|
||||
disable_udp = (user.hysteria_udp == "0") and true or false,
|
||||
alpn = user.hysteria_alpn or nil,
|
||||
up_mbps = tonumber(user.hysteria_up_mbps) or 10,
|
||||
down_mbps = tonumber(user.hysteria_down_mbps) or 50,
|
||||
recv_window_conn = (user.hysteria_recv_window_conn) and tonumber(user.hysteria_recv_window_conn) or nil,
|
||||
recv_window = (user.hysteria_recv_window) and tonumber(user.hysteria_recv_window) or nil,
|
||||
disable_mtu_discovery = (user.hysteria_disable_mtu_discovery) and true or false
|
||||
}
|
||||
return config
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
module("luci.model.cbi.passwall.server.api.shadowsocks", package.seeall)
|
||||
function gen_config(user)
|
||||
local config = {}
|
||||
config.server = {"[::0]", "0.0.0.0"}
|
||||
config.server_port = tonumber(user.port)
|
||||
config.password = user.password
|
||||
config.timeout = tonumber(user.timeout)
|
||||
config.fast_open = (user.tcp_fast_open and user.tcp_fast_open == "1") and true or false
|
||||
config.method = user.method
|
||||
|
||||
if user.type == "SSR" then
|
||||
config.protocol = user.protocol
|
||||
config.protocol_param = user.protocol_param
|
||||
config.obfs = user.obfs
|
||||
config.obfs_param = user.obfs_param
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
@ -0,0 +1,61 @@
|
||||
module("luci.model.cbi.passwall.server.api.trojan", package.seeall)
|
||||
function gen_config(user)
|
||||
local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA"
|
||||
local cipher13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"
|
||||
local config = {
|
||||
run_type = "server",
|
||||
local_addr = "::",
|
||||
local_port = tonumber(user.port),
|
||||
remote_addr = (user.remote_enable == "1" and user.remote_address) and user.remote_address or nil,
|
||||
remote_port = (user.remote_enable == "1" and user.remote_port) and tonumber(user.remote_port) or nil,
|
||||
password = user.uuid,
|
||||
log_level = (user.log and user.log == "1") and tonumber(user.loglevel) or 5,
|
||||
ssl = {
|
||||
cert = user.tls_certificateFile,
|
||||
key = user.tls_keyFile,
|
||||
key_password = "",
|
||||
cipher = cipher,
|
||||
cipher_tls13 = cipher13,
|
||||
prefer_server_cipher = true,
|
||||
reuse_session = true,
|
||||
session_ticket = (user.tls_sessionTicket == "1") and true or false,
|
||||
session_timeout = 600,
|
||||
plain_http_response = "",
|
||||
curves = "",
|
||||
dhparam = ""
|
||||
},
|
||||
tcp = {
|
||||
prefer_ipv4 = false,
|
||||
no_delay = true,
|
||||
keep_alive = true,
|
||||
reuse_port = false,
|
||||
fast_open = (user.tcp_fast_open and user.tcp_fast_open == "1") and true or false,
|
||||
fast_open_qlen = 20
|
||||
}
|
||||
}
|
||||
if user.type == "Trojan-Go" then
|
||||
config.ssl.cipher = nil
|
||||
config.ssl.cipher_tls13 = nil
|
||||
config.udp_timeout = 60
|
||||
config.disable_http_check = true
|
||||
config.transport_plugin = ((user.tls == nil or user.tls ~= "1") and user.trojan_transport == "original") and {
|
||||
enabled = user.plugin_type ~= nil,
|
||||
type = user.plugin_type or "plaintext",
|
||||
command = user.plugin_type ~= "plaintext" and user.plugin_cmd or nil,
|
||||
option = user.plugin_type ~= "plaintext" and user.plugin_option or nil,
|
||||
arg = user.plugin_type ~= "plaintext" and { user.plugin_arg } or nil,
|
||||
env = {}
|
||||
} or nil
|
||||
config.websocket = (user.trojan_transport == 'ws') and {
|
||||
enabled = true,
|
||||
path = user.ws_path or "/",
|
||||
host = user.ws_host or ""
|
||||
} or nil
|
||||
config.shadowsocks = (user.ss_aead == "1") and {
|
||||
enabled = true,
|
||||
method = user.ss_aead_method or "aes_128_gcm",
|
||||
password = user.ss_aead_pwd or ""
|
||||
} or nil
|
||||
end
|
||||
return config
|
||||
end
|
246
luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua
Normal file
246
luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua
Normal file
@ -0,0 +1,246 @@
|
||||
module("luci.model.cbi.passwall.server.api.v2ray", package.seeall)
|
||||
local uci = require"luci.model.uci".cursor()
|
||||
|
||||
function gen_config(user)
|
||||
local settings = nil
|
||||
local routing = nil
|
||||
local outbounds = {
|
||||
{protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"}
|
||||
}
|
||||
|
||||
if user.protocol == "vmess" or user.protocol == "vless" then
|
||||
if user.uuid then
|
||||
local clients = {}
|
||||
for i = 1, #user.uuid do
|
||||
clients[i] = {
|
||||
id = user.uuid[i],
|
||||
flow = ("1" == user.xtls) and user.flow or nil
|
||||
}
|
||||
end
|
||||
settings = {
|
||||
clients = clients,
|
||||
decryption = user.decryption or "none"
|
||||
}
|
||||
end
|
||||
elseif user.protocol == "socks" then
|
||||
settings = {
|
||||
udp = ("1" == user.udp_forward) and true or false,
|
||||
auth = ("1" == user.auth) and "password" or "noauth",
|
||||
accounts = ("1" == user.auth) and {
|
||||
{
|
||||
user = user.username,
|
||||
pass = user.password
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
elseif user.protocol == "http" then
|
||||
settings = {
|
||||
allowTransparent = false,
|
||||
accounts = ("1" == user.auth) and {
|
||||
{
|
||||
user = user.username,
|
||||
pass = user.password
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
user.transport = "tcp"
|
||||
user.tcp_guise = "none"
|
||||
elseif user.protocol == "shadowsocks" then
|
||||
settings = {
|
||||
method = user.method,
|
||||
password = user.password,
|
||||
ivCheck = ("1" == user.iv_check) and true or false,
|
||||
network = user.ss_network or "TCP,UDP"
|
||||
}
|
||||
elseif user.protocol == "trojan" then
|
||||
if user.uuid then
|
||||
local clients = {}
|
||||
for i = 1, #user.uuid do
|
||||
clients[i] = {
|
||||
flow = ("1" == user.xtls) and user.flow or nil,
|
||||
password = user.uuid[i],
|
||||
}
|
||||
end
|
||||
settings = {
|
||||
clients = clients
|
||||
}
|
||||
end
|
||||
elseif user.protocol == "mtproto" then
|
||||
settings = {
|
||||
users = {
|
||||
{
|
||||
secret = (user.password == nil) and "" or user.password
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif user.protocol == "dokodemo-door" then
|
||||
settings = {
|
||||
network = user.d_protocol,
|
||||
address = user.d_address,
|
||||
port = tonumber(user.d_port)
|
||||
}
|
||||
end
|
||||
|
||||
if user.fallback and user.fallback == "1" then
|
||||
local fallbacks = {}
|
||||
for i = 1, #user.fallback_list do
|
||||
local fallbackStr = user.fallback_list[i]
|
||||
if fallbackStr then
|
||||
local tmp = {}
|
||||
string.gsub(fallbackStr, '[^' .. "," .. ']+', function(w)
|
||||
table.insert(tmp, w)
|
||||
end)
|
||||
local dest = tmp[1] or ""
|
||||
local path = tmp[2]
|
||||
if dest:find("%.") then
|
||||
else
|
||||
dest = tonumber(dest)
|
||||
end
|
||||
fallbacks[i] = {
|
||||
path = path,
|
||||
dest = dest,
|
||||
xver = 1
|
||||
}
|
||||
end
|
||||
end
|
||||
settings.fallbacks = fallbacks
|
||||
end
|
||||
|
||||
routing = {
|
||||
domainStrategy = "IPOnDemand",
|
||||
rules = {
|
||||
{
|
||||
type = "field",
|
||||
ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"},
|
||||
outboundTag = (user.accept_lan == nil or user.accept_lan == "0") and "blocked" or "direct"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if user.transit_node and user.transit_node ~= "nil" then
|
||||
local transit_node_t = uci:get_all("passwall", user.transit_node)
|
||||
if user.transit_node == "_socks" or user.transit_node == "_http" then
|
||||
transit_node_t = {
|
||||
type = user.type,
|
||||
protocol = user.transit_node:gsub("_", ""),
|
||||
transport = "tcp",
|
||||
address = user.transit_node_address,
|
||||
port = user.transit_node_port,
|
||||
username = (user.transit_node_username and user.transit_node_username ~= "") and user.transit_node_username or nil,
|
||||
password = (user.transit_node_password and user.transit_node_password ~= "") and user.transit_node_password or nil,
|
||||
}
|
||||
end
|
||||
local outbound = require("luci.model.cbi.passwall.api.gen_v2ray").gen_outbound(transit_node_t, "transit")
|
||||
if outbound then
|
||||
table.insert(outbounds, 1, outbound)
|
||||
end
|
||||
end
|
||||
|
||||
local config = {
|
||||
log = {
|
||||
-- error = "/tmp/etc/passwall_server/log/" .. user[".name"] .. ".log",
|
||||
loglevel = ("1" == user.log) and user.loglevel or "none"
|
||||
},
|
||||
-- 传入连接
|
||||
inbounds = {
|
||||
{
|
||||
listen = (user.bind_local == "1") and "127.0.0.1" or nil,
|
||||
port = tonumber(user.port),
|
||||
protocol = user.protocol,
|
||||
settings = settings,
|
||||
streamSettings = {
|
||||
network = user.transport,
|
||||
security = "none",
|
||||
xtlsSettings = ("1" == user.tls and "1" == user.xtls) and {
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
{
|
||||
certificateFile = user.tls_certificateFile,
|
||||
keyFile = user.tls_keyFile
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
tlsSettings = ("1" == user.tls) and {
|
||||
disableSystemRoot = false,
|
||||
certificates = {
|
||||
{
|
||||
certificateFile = user.tls_certificateFile,
|
||||
keyFile = user.tls_keyFile
|
||||
}
|
||||
}
|
||||
} or nil,
|
||||
tcpSettings = (user.transport == "tcp") and {
|
||||
acceptProxyProtocol = (user.acceptProxyProtocol and user.acceptProxyProtocol == "1") and true or false,
|
||||
header = {
|
||||
type = user.tcp_guise,
|
||||
request = (user.tcp_guise == "http") and {
|
||||
path = user.tcp_guise_http_path or {"/"},
|
||||
headers = {
|
||||
Host = user.tcp_guise_http_host or {}
|
||||
}
|
||||
} or nil
|
||||
}
|
||||
} or nil,
|
||||
kcpSettings = (user.transport == "mkcp") and {
|
||||
mtu = tonumber(user.mkcp_mtu),
|
||||
tti = tonumber(user.mkcp_tti),
|
||||
uplinkCapacity = tonumber(user.mkcp_uplinkCapacity),
|
||||
downlinkCapacity = tonumber(user.mkcp_downlinkCapacity),
|
||||
congestion = (user.mkcp_congestion == "1") and true or false,
|
||||
readBufferSize = tonumber(user.mkcp_readBufferSize),
|
||||
writeBufferSize = tonumber(user.mkcp_writeBufferSize),
|
||||
seed = (user.mkcp_seed and user.mkcp_seed ~= "") and user.mkcp_seed or nil,
|
||||
header = {type = user.mkcp_guise}
|
||||
} or nil,
|
||||
wsSettings = (user.transport == "ws") and {
|
||||
acceptProxyProtocol = (user.acceptProxyProtocol and user.acceptProxyProtocol == "1") and true or false,
|
||||
headers = (user.ws_host) and {Host = user.ws_host} or nil,
|
||||
path = user.ws_path
|
||||
} or nil,
|
||||
httpSettings = (user.transport == "h2") and {
|
||||
path = user.h2_path, host = user.h2_host
|
||||
} or nil,
|
||||
dsSettings = (user.transport == "ds") and {
|
||||
path = user.ds_path
|
||||
} or nil,
|
||||
quicSettings = (user.transport == "quic") and {
|
||||
security = user.quic_security,
|
||||
key = user.quic_key,
|
||||
header = {type = user.quic_guise}
|
||||
} or nil,
|
||||
grpcSettings = (user.transport == "grpc") and {
|
||||
serviceName = user.grpc_serviceName
|
||||
} or nil
|
||||
}
|
||||
}
|
||||
},
|
||||
-- 传出连接
|
||||
outbounds = outbounds,
|
||||
routing = routing
|
||||
}
|
||||
|
||||
local alpn = {}
|
||||
if user.alpn then
|
||||
string.gsub(user.alpn, '[^' .. "," .. ']+', function(w)
|
||||
table.insert(alpn, w)
|
||||
end)
|
||||
end
|
||||
if alpn and #alpn > 0 then
|
||||
if config.inbounds[1].streamSettings.tlsSettings then
|
||||
config.inbounds[1].streamSettings.tlsSettings.alpn = alpn
|
||||
end
|
||||
if config.inbounds[1].streamSettings.xtlsSettings then
|
||||
config.inbounds[1].streamSettings.xtlsSettings.alpn = alpn
|
||||
end
|
||||
end
|
||||
|
||||
if "1" == user.tls then
|
||||
config.inbounds[1].streamSettings.security = "tls"
|
||||
if user.type == "Xray" and user.xtls and user.xtls == "1" then
|
||||
config.inbounds[1].streamSettings.security = "xtls"
|
||||
config.inbounds[1].streamSettings.tlsSettings = nil
|
||||
end
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
73
luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua
Normal file
73
luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua
Normal file
@ -0,0 +1,73 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
m = Map("passwall_server", translate("Server-Side"))
|
||||
|
||||
t = m:section(NamedSection, "global", "global")
|
||||
t.anonymous = true
|
||||
t.addremove = false
|
||||
|
||||
e = t:option(Flag, "enable", translate("Enable"))
|
||||
e.rmempty = false
|
||||
|
||||
t = m:section(TypedSection, "user", translate("Users Manager"))
|
||||
t.anonymous = true
|
||||
t.addremove = true
|
||||
t.sortable = true
|
||||
t.template = "cbi/tblsection"
|
||||
t.extedit = api.url("server_user", "%s")
|
||||
function t.create(e, t)
|
||||
local uuid = api.gen_uuid()
|
||||
t = uuid
|
||||
TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
end
|
||||
function t.remove(e, t)
|
||||
e.map.proceed = true
|
||||
e.map:del(t)
|
||||
luci.http.redirect(api.url("server"))
|
||||
end
|
||||
|
||||
e = t:option(Flag, "enable", translate("Enable"))
|
||||
e.width = "5%"
|
||||
e.rmempty = false
|
||||
|
||||
e = t:option(DummyValue, "status", translate("Status"))
|
||||
e.rawhtml = true
|
||||
e.cfgvalue = function(t, n)
|
||||
return string.format('<font class="_users_status">%s</font>', translate("Collecting data..."))
|
||||
end
|
||||
|
||||
e = t:option(DummyValue, "remarks", translate("Remarks"))
|
||||
e.width = "15%"
|
||||
|
||||
---- Type
|
||||
e = t:option(DummyValue, "type", translate("Type"))
|
||||
e.cfgvalue = function(t, n)
|
||||
local v = Value.cfgvalue(t, n)
|
||||
if v then
|
||||
if v == "V2ray" or v == "Xray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
if protocol == "vmess" then
|
||||
protocol = "VMess"
|
||||
elseif protocol == "vless" then
|
||||
protocol = "VLESS"
|
||||
else
|
||||
protocol = protocol:gsub("^%l",string.upper)
|
||||
end
|
||||
return v .. " -> " .. protocol
|
||||
end
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
e = t:option(DummyValue, "port", translate("Port"))
|
||||
|
||||
e = t:option(Flag, "log", translate("Log"))
|
||||
e.default = "1"
|
||||
e.rmempty = false
|
||||
|
||||
m:append(Template("passwall/server/log"))
|
||||
|
||||
m:append(Template("passwall/server/users_list_status"))
|
||||
return m
|
||||
|
728
luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua
Normal file
728
luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua
Normal file
@ -0,0 +1,728 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local ss_encrypt_method_list = {
|
||||
"rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
|
||||
"aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
|
||||
"camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
|
||||
"chacha20-ietf", -- aead
|
||||
"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
}
|
||||
|
||||
local ssr_encrypt_method_list = {
|
||||
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
|
||||
"aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
|
||||
"bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb",
|
||||
"cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20",
|
||||
"chacha20-ietf"
|
||||
}
|
||||
|
||||
local ssr_protocol_list = {
|
||||
"origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple",
|
||||
"auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5",
|
||||
"auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c",
|
||||
"auth_chain_d", "auth_chain_e", "auth_chain_f"
|
||||
}
|
||||
local ssr_obfs_list = {
|
||||
"plain", "http_simple", "http_post", "random_head", "tls_simple",
|
||||
"tls1.0_session_auth", "tls1.2_ticket_auth"
|
||||
}
|
||||
|
||||
local v_ss_encrypt_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305"
|
||||
}
|
||||
|
||||
local x_ss_encrypt_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305"
|
||||
}
|
||||
|
||||
local header_type_list = {
|
||||
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
|
||||
}
|
||||
|
||||
local encrypt_methods_ss_aead = {
|
||||
"chacha20-ietf-poly1305",
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
}
|
||||
|
||||
m = Map("passwall_server", translate("Server Config"))
|
||||
m.redirect = api.url("server")
|
||||
|
||||
s = m:section(NamedSection, arg[1], "user", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
enable = s:option(Flag, "enable", translate("Enable"))
|
||||
enable.default = "1"
|
||||
enable.rmempty = false
|
||||
|
||||
remarks = s:option(Value, "remarks", translate("Remarks"))
|
||||
remarks.default = translate("Remarks")
|
||||
remarks.rmempty = false
|
||||
|
||||
type = s:option(ListValue, "type", translate("Type"))
|
||||
if api.is_finded("microsocks") then
|
||||
type:value("Socks", translate("Socks"))
|
||||
end
|
||||
if api.is_finded("ss-server") then
|
||||
type:value("SS", translate("Shadowsocks"))
|
||||
end
|
||||
if api.is_finded("ssr-server") then
|
||||
type:value("SSR", translate("ShadowsocksR"))
|
||||
end
|
||||
if api.is_finded("v2ray") then
|
||||
type:value("V2ray", translate("V2ray"))
|
||||
end
|
||||
if api.is_finded("xray") then
|
||||
type:value("Xray", translate("Xray"))
|
||||
end
|
||||
if api.is_finded("brook") then
|
||||
type:value("Brook", translate("Brook"))
|
||||
end
|
||||
--[[
|
||||
if api.is_finded("trojan-plus") or api.is_finded("trojan") then
|
||||
type:value("Trojan", translate("Trojan"))
|
||||
end
|
||||
]]--
|
||||
if api.is_finded("trojan-plus") then
|
||||
type:value("Trojan-Plus", translate("Trojan-Plus"))
|
||||
end
|
||||
if api.is_finded("trojan-go") then
|
||||
type:value("Trojan-Go", translate("Trojan-Go"))
|
||||
end
|
||||
if api.is_finded("hysteria") then
|
||||
type:value("Hysteria", translate("Hysteria"))
|
||||
end
|
||||
|
||||
protocol = s:option(ListValue, "protocol", translate("Protocol"))
|
||||
protocol:value("vmess", "Vmess")
|
||||
protocol:value("vless", "VLESS")
|
||||
protocol:value("http", "HTTP")
|
||||
protocol:value("socks", "Socks")
|
||||
protocol:value("shadowsocks", "Shadowsocks")
|
||||
protocol:value("trojan", "Trojan")
|
||||
protocol:value("mtproto", "MTProto")
|
||||
protocol:value("dokodemo-door", "dokodemo-door")
|
||||
protocol:depends("type", "V2ray")
|
||||
protocol:depends("type", "Xray")
|
||||
|
||||
-- Brook协议
|
||||
brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol"))
|
||||
brook_protocol:value("server", "Brook")
|
||||
brook_protocol:value("wsserver", "WebSocket")
|
||||
brook_protocol:depends("type", "Brook")
|
||||
function brook_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function brook_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
--brook_tls = s:option(Flag, "brook_tls", translate("Use TLS"))
|
||||
--brook_tls:depends("brook_protocol", "wsserver")
|
||||
|
||||
port = s:option(Value, "port", translate("Listen Port"))
|
||||
port.datatype = "port"
|
||||
port.rmempty = false
|
||||
|
||||
auth = s:option(Flag, "auth", translate("Auth"))
|
||||
auth.validate = function(self, value, t)
|
||||
if value and value == "1" then
|
||||
local user_v = username:formvalue(t) or ""
|
||||
local pass_v = password:formvalue(t) or ""
|
||||
if user_v == "" or pass_v == "" then
|
||||
return nil, translate("Username and Password must be used together!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
auth:depends("type", "Socks")
|
||||
auth:depends({ type = "V2ray", protocol = "socks" })
|
||||
auth:depends({ type = "V2ray", protocol = "http" })
|
||||
auth:depends({ type = "Xray", protocol = "socks" })
|
||||
auth:depends({ type = "Xray", protocol = "http" })
|
||||
|
||||
username = s:option(Value, "username", translate("Username"))
|
||||
username:depends("auth", true)
|
||||
|
||||
password = s:option(Value, "password", translate("Password"))
|
||||
password.password = true
|
||||
password:depends("auth", true)
|
||||
password:depends("type", "SS")
|
||||
password:depends("type", "SSR")
|
||||
password:depends("type", "Brook")
|
||||
password:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
password:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
|
||||
mtproto_password = s:option(Value, "mtproto_password", translate("Password"), translate("The MTProto protocol must be 32 characters and can only contain characters from 0 to 9 and a to f."))
|
||||
mtproto_password:depends({ type = "V2ray", protocol = "mtproto" })
|
||||
mtproto_password:depends({ type = "Xray", protocol = "mtproto" })
|
||||
mtproto_password.default = arg[1]
|
||||
function mtproto_password.cfgvalue(self, section)
|
||||
return m:get(section, "password")
|
||||
end
|
||||
function mtproto_password.write(self, section, value)
|
||||
m:set(section, "password", value)
|
||||
end
|
||||
|
||||
d_protocol = s:option(ListValue, "d_protocol", translate("Destination protocol"))
|
||||
d_protocol:value("tcp", "TCP")
|
||||
d_protocol:value("udp", "UDP")
|
||||
d_protocol:value("tcp,udp", "TCP,UDP")
|
||||
d_protocol:depends({ type = "V2ray", protocol = "dokodemo-door" })
|
||||
d_protocol:depends({ type = "Xray", protocol = "dokodemo-door" })
|
||||
|
||||
d_address = s:option(Value, "d_address", translate("Destination address"))
|
||||
d_address:depends({ type = "V2ray", protocol = "dokodemo-door" })
|
||||
d_address:depends({ type = "Xray", protocol = "dokodemo-door" })
|
||||
|
||||
d_port = s:option(Value, "d_port", translate("Destination port"))
|
||||
d_port.datatype = "port"
|
||||
d_port:depends({ type = "V2ray", protocol = "dokodemo-door" })
|
||||
d_port:depends({ type = "Xray", protocol = "dokodemo-door" })
|
||||
|
||||
decryption = s:option(Value, "decryption", translate("Encrypt Method"))
|
||||
decryption.default = "none"
|
||||
decryption:depends({ type = "V2ray", protocol = "vless" })
|
||||
decryption:depends({ type = "Xray", protocol = "vless" })
|
||||
|
||||
hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol"))
|
||||
hysteria_protocol:value("udp", "UDP")
|
||||
hysteria_protocol:value("faketcp", "faketcp")
|
||||
hysteria_protocol:value("wechat-video", "wechat-video")
|
||||
hysteria_protocol:depends("type", "Hysteria")
|
||||
function hysteria_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function hysteria_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
|
||||
hysteria_obfs:depends("type", "Hysteria")
|
||||
|
||||
hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type"))
|
||||
hysteria_auth_type:value("disable", translate("Disable"))
|
||||
hysteria_auth_type:value("string", translate("STRING"))
|
||||
hysteria_auth_type:depends("type", "Hysteria")
|
||||
|
||||
hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password"))
|
||||
hysteria_auth_password.password = true
|
||||
hysteria_auth_password:depends("hysteria_auth_type", "string")
|
||||
|
||||
hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN"))
|
||||
hysteria_alpn:depends("type", "Hysteria")
|
||||
|
||||
hysteria_udp = s:option(Flag, "hysteria_udp", translate("UDP"))
|
||||
hysteria_udp.default = "1"
|
||||
hysteria_udp:depends("type", "Hysteria")
|
||||
|
||||
hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
|
||||
hysteria_up_mbps.default = "10"
|
||||
hysteria_up_mbps:depends("type", "Hysteria")
|
||||
|
||||
hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
|
||||
hysteria_down_mbps.default = "50"
|
||||
hysteria_down_mbps:depends("type", "Hysteria")
|
||||
|
||||
hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
|
||||
hysteria_recv_window_conn:depends("type", "Hysteria")
|
||||
|
||||
hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window"))
|
||||
hysteria_recv_window:depends("type", "Hysteria")
|
||||
|
||||
hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
|
||||
hysteria_disable_mtu_discovery:depends("type", "Hysteria")
|
||||
|
||||
ss_encrypt_method = s:option(ListValue, "ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(ss_encrypt_method_list) do ss_encrypt_method:value(t) end
|
||||
ss_encrypt_method:depends("type", "SS")
|
||||
function ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
ssr_encrypt_method = s:option(ListValue, "ssr_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
|
||||
ssr_encrypt_method:depends("type", "SSR")
|
||||
function ssr_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function ssr_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
|
||||
v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
function v_ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function v_ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method"))
|
||||
for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end
|
||||
x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
function x_ss_encrypt_method.cfgvalue(self, section)
|
||||
return m:get(section, "method")
|
||||
end
|
||||
function x_ss_encrypt_method.write(self, section, value)
|
||||
m:set(section, "method", value)
|
||||
end
|
||||
|
||||
iv_check = s:option(Flag, "iv_check", translate("IV Check"))
|
||||
iv_check:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
iv_check:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
|
||||
ss_network = s:option(ListValue, "ss_network", translate("Transport"))
|
||||
ss_network.default = "tcp,udp"
|
||||
ss_network:value("tcp", "TCP")
|
||||
ss_network:value("udp", "UDP")
|
||||
ss_network:value("tcp,udp", "TCP,UDP")
|
||||
ss_network:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
ss_network:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
|
||||
ssr_protocol = s:option(ListValue, "ssr_protocol", translate("Protocol"))
|
||||
for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end
|
||||
ssr_protocol:depends("type", "SSR")
|
||||
function ssr_protocol.cfgvalue(self, section)
|
||||
return m:get(section, "protocol")
|
||||
end
|
||||
function ssr_protocol.write(self, section, value)
|
||||
m:set(section, "protocol", value)
|
||||
end
|
||||
|
||||
protocol_param = s:option(Value, "protocol_param", translate("Protocol_param"))
|
||||
protocol_param:depends("type", "SSR")
|
||||
|
||||
obfs = s:option(ListValue, "obfs", translate("Obfs"))
|
||||
for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end
|
||||
obfs:depends("type", "SSR")
|
||||
|
||||
obfs_param = s:option(Value, "obfs_param", translate("Obfs_param"))
|
||||
obfs_param:depends("type", "SSR")
|
||||
|
||||
timeout = s:option(Value, "timeout", translate("Connection Timeout"))
|
||||
timeout.datatype = "uinteger"
|
||||
timeout.default = 300
|
||||
timeout:depends("type", "SS")
|
||||
timeout:depends("type", "SSR")
|
||||
|
||||
udp_forward = s:option(Flag, "udp_forward", translate("UDP Forward"))
|
||||
udp_forward.default = "1"
|
||||
udp_forward.rmempty = false
|
||||
udp_forward:depends("type", "SSR")
|
||||
udp_forward:depends({ type = "V2ray", protocol = "socks" })
|
||||
udp_forward:depends({ type = "Xray", protocol = "socks" })
|
||||
|
||||
uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Password"))
|
||||
for i = 1, 3 do
|
||||
uuid:value(api.gen_uuid(1))
|
||||
end
|
||||
uuid:depends({ type = "V2ray", protocol = "vmess" })
|
||||
uuid:depends({ type = "V2ray", protocol = "vless" })
|
||||
uuid:depends({ type = "V2ray", protocol = "trojan" })
|
||||
uuid:depends({ type = "Xray", protocol = "vmess" })
|
||||
uuid:depends({ type = "Xray", protocol = "vless" })
|
||||
uuid:depends({ type = "Xray", protocol = "trojan" })
|
||||
uuid:depends("type", "Trojan")
|
||||
uuid:depends("type", "Trojan-Go")
|
||||
uuid:depends("type", "Trojan-Plus")
|
||||
|
||||
tls = s:option(Flag, "tls", translate("TLS"))
|
||||
tls.default = 0
|
||||
tls.validate = function(self, value, t)
|
||||
if value then
|
||||
local type = type:formvalue(t) or ""
|
||||
if value == "0" and (type == "Trojan" or type == "Trojan-Plus") then
|
||||
return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
|
||||
end
|
||||
if value == "1" then
|
||||
local ca = tls_certificateFile:formvalue(t) or ""
|
||||
local key = tls_keyFile:formvalue(t) or ""
|
||||
if ca == "" or key == "" then
|
||||
return nil, translate("Public key and Private key path can not be empty!")
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
tls:depends({ type = "V2ray", protocol = "vmess" })
|
||||
tls:depends({ type = "V2ray", protocol = "vless" })
|
||||
tls:depends({ type = "V2ray", protocol = "socks" })
|
||||
tls:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
tls:depends({ type = "V2ray", protocol = "trojan" })
|
||||
tls:depends({ type = "Xray", protocol = "vmess" })
|
||||
tls:depends({ type = "Xray", protocol = "vless" })
|
||||
tls:depends({ type = "Xray", protocol = "socks" })
|
||||
tls:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
tls:depends({ type = "Xray", protocol = "trojan" })
|
||||
tls:depends("type", "Trojan")
|
||||
tls:depends("type", "Trojan-Plus")
|
||||
tls:depends("type", "Trojan-Go")
|
||||
|
||||
xtls = s:option(Flag, "xtls", translate("XTLS"))
|
||||
xtls.default = 0
|
||||
xtls:depends({ type = "Xray", protocol = "vless", tls = true })
|
||||
xtls:depends({ type = "Xray", protocol = "trojan", tls = true })
|
||||
|
||||
flow = s:option(Value, "flow", translate("flow"))
|
||||
flow.default = "xtls-rprx-direct"
|
||||
flow:value("xtls-rprx-origin")
|
||||
flow:value("xtls-rprx-direct")
|
||||
flow:depends("xtls", true)
|
||||
|
||||
alpn = s:option(ListValue, "alpn", translate("alpn"))
|
||||
alpn.default = "h2,http/1.1"
|
||||
alpn:value("h2,http/1.1")
|
||||
alpn:value("h2")
|
||||
alpn:value("http/1.1")
|
||||
alpn:depends({ type = "V2ray", tls = true })
|
||||
alpn:depends({ type = "Xray", tls = true })
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
|
||||
tls_certificateFile = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
tls_certificateFile.validate = function(self, value, t)
|
||||
if value and value ~= "" then
|
||||
if not nixio.fs.access(value) then
|
||||
return nil, translate("Can't find this file!")
|
||||
else
|
||||
return value
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
tls_certificateFile.default = "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
tls_certificateFile:depends("tls", true)
|
||||
tls_certificateFile:depends("type", "Hysteria")
|
||||
|
||||
tls_keyFile = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
tls_keyFile.validate = function(self, value, t)
|
||||
if value and value ~= "" then
|
||||
if not nixio.fs.access(value) then
|
||||
return nil, translate("Can't find this file!")
|
||||
else
|
||||
return value
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
tls_keyFile.default = "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
tls_keyFile:depends("tls", true)
|
||||
tls_keyFile:depends("type", "Hysteria")
|
||||
|
||||
tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
|
||||
tls_sessionTicket.default = "0"
|
||||
tls_sessionTicket:depends({ type = "Trojan", tls = true })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Plus", tls = true })
|
||||
tls_sessionTicket:depends({ type = "Trojan-Go", tls = true })
|
||||
|
||||
transport = s:option(ListValue, "transport", translate("Transport"))
|
||||
transport:value("tcp", "TCP")
|
||||
transport:value("mkcp", "mKCP")
|
||||
transport:value("ws", "WebSocket")
|
||||
transport:value("h2", "HTTP/2")
|
||||
transport:value("ds", "DomainSocket")
|
||||
transport:value("quic", "QUIC")
|
||||
transport:value("grpc", "gRPC")
|
||||
transport:depends({ type = "V2ray", protocol = "vmess" })
|
||||
transport:depends({ type = "V2ray", protocol = "vless" })
|
||||
transport:depends({ type = "V2ray", protocol = "socks" })
|
||||
transport:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
transport:depends({ type = "V2ray", protocol = "trojan" })
|
||||
transport:depends({ type = "Xray", protocol = "vmess" })
|
||||
transport:depends({ type = "Xray", protocol = "vless" })
|
||||
transport:depends({ type = "Xray", protocol = "socks" })
|
||||
transport:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
transport:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
|
||||
trojan_transport:value("original", translate("Original"))
|
||||
trojan_transport:value("ws", "WebSocket")
|
||||
trojan_transport.default = "original"
|
||||
trojan_transport:depends("type", "Trojan-Go")
|
||||
|
||||
trojan_plugin = s:option(ListValue, "plugin_type", translate("Transport Plugin"))
|
||||
trojan_plugin:value("plaintext", "Plain Text")
|
||||
trojan_plugin:value("shadowsocks", "ShadowSocks")
|
||||
trojan_plugin:value("other", "Other")
|
||||
trojan_plugin.default = "plaintext"
|
||||
trojan_plugin:depends({ tls = false, trojan_transport = "original" })
|
||||
|
||||
trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary"))
|
||||
trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin"
|
||||
trojan_plugin_cmd:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_cmd:depends({ plugin_type = "other" })
|
||||
|
||||
trojan_plugin_op = s:option(Value, "plugin_option", translate("Plugin Option"))
|
||||
trojan_plugin_op.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
|
||||
trojan_plugin_op:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_op:depends({ plugin_type = "other" })
|
||||
|
||||
trojan_plugin_arg = s:option(DynamicList, "plugin_arg", translate("Plugin Option Args"))
|
||||
trojan_plugin_arg.placeholder = "eg: [\"-config\", \"test.json\"]"
|
||||
trojan_plugin_arg:depends({ plugin_type = "shadowsocks" })
|
||||
trojan_plugin_arg:depends({ plugin_type = "other" })
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
|
||||
ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
|
||||
ws_host:depends("transport", "ws")
|
||||
ws_host:depends("ss_transport", "ws")
|
||||
ws_host:depends("trojan_transport", "ws")
|
||||
|
||||
ws_path = s:option(Value, "ws_path", translate("WebSocket Path"))
|
||||
ws_path:depends("transport", "ws")
|
||||
ws_path:depends("ss_transport", "ws")
|
||||
ws_path:depends("trojan_transport", "ws")
|
||||
ws_path:depends({ type = "Brook", brook_protocol = "wsserver" })
|
||||
|
||||
-- [[ HTTP/2部分 ]]--
|
||||
|
||||
h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
|
||||
h2_host:depends("transport", "h2")
|
||||
h2_host:depends("ss_transport", "h2")
|
||||
h2_host:depends("trojan_transport", "h2")
|
||||
|
||||
h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path"))
|
||||
h2_path:depends("transport", "h2")
|
||||
h2_path:depends("ss_transport", "h2")
|
||||
h2_path:depends("trojan_transport", "h2")
|
||||
|
||||
-- [[ TCP部分 ]]--
|
||||
|
||||
-- TCP伪装
|
||||
tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
|
||||
tcp_guise:value("none", "none")
|
||||
tcp_guise:value("http", "http")
|
||||
tcp_guise:depends("transport", "tcp")
|
||||
|
||||
-- HTTP域名
|
||||
tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
|
||||
tcp_guise_http_host:depends("tcp_guise", "http")
|
||||
|
||||
-- HTTP路径
|
||||
tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
|
||||
tcp_guise_http_path:depends("tcp_guise", "http")
|
||||
|
||||
-- [[ mKCP部分 ]]--
|
||||
|
||||
mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
|
||||
for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end
|
||||
mkcp_guise:depends("transport", "mkcp")
|
||||
|
||||
mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
|
||||
mkcp_mtu.default = "1350"
|
||||
mkcp_mtu:depends("transport", "mkcp")
|
||||
|
||||
mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
|
||||
mkcp_tti.default = "20"
|
||||
mkcp_tti:depends("transport", "mkcp")
|
||||
|
||||
mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
|
||||
mkcp_uplinkCapacity.default = "5"
|
||||
mkcp_uplinkCapacity:depends("transport", "mkcp")
|
||||
|
||||
mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
|
||||
mkcp_downlinkCapacity.default = "20"
|
||||
mkcp_downlinkCapacity:depends("transport", "mkcp")
|
||||
|
||||
mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
|
||||
mkcp_congestion:depends("transport", "mkcp")
|
||||
|
||||
mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
|
||||
mkcp_readBufferSize.default = "1"
|
||||
mkcp_readBufferSize:depends("transport", "mkcp")
|
||||
|
||||
mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
|
||||
mkcp_writeBufferSize.default = "1"
|
||||
mkcp_writeBufferSize:depends("transport", "mkcp")
|
||||
|
||||
mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed"))
|
||||
mkcp_seed:depends("transport", "mkcp")
|
||||
|
||||
-- [[ DomainSocket部分 ]]--
|
||||
|
||||
ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running."))
|
||||
ds_path:depends("transport", "ds")
|
||||
|
||||
-- [[ QUIC部分 ]]--
|
||||
quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method"))
|
||||
quic_security:value("none")
|
||||
quic_security:value("aes-128-gcm")
|
||||
quic_security:value("chacha20-poly1305")
|
||||
quic_security:depends("transport", "quic")
|
||||
|
||||
quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
|
||||
quic_key:depends("transport", "quic")
|
||||
|
||||
quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
|
||||
for a, t in ipairs(header_type_list) do quic_guise:value(t) end
|
||||
quic_guise:depends("transport", "quic")
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName")
|
||||
grpc_serviceName:depends("transport", "grpc")
|
||||
|
||||
acceptProxyProtocol = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
|
||||
acceptProxyProtocol:depends({ type = "V2ray", transport = "tcp" })
|
||||
acceptProxyProtocol:depends({ type = "V2ray", transport = "ws" })
|
||||
acceptProxyProtocol:depends({ type = "Xray", transport = "tcp" })
|
||||
acceptProxyProtocol:depends({ type = "Xray", transport = "ws" })
|
||||
|
||||
-- [[ Fallback部分 ]]--
|
||||
fallback = s:option(Flag, "fallback", translate("Fallback"))
|
||||
fallback:depends({ type = "V2ray", protocol = "vless", transport = "tcp" })
|
||||
fallback:depends({ type = "V2ray", protocol = "trojan", transport = "tcp" })
|
||||
fallback:depends({ type = "Xray", protocol = "vless", transport = "tcp" })
|
||||
fallback:depends({ type = "Xray", protocol = "trojan", transport = "tcp" })
|
||||
|
||||
--[[
|
||||
fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn")
|
||||
fallback_alpn:depends("fallback", true)
|
||||
|
||||
fallback_path = s:option(Value, "fallback_path", "Fallback path")
|
||||
fallback_path:depends("fallback", true)
|
||||
|
||||
fallback_dest = s:option(Value, "fallback_dest", "Fallback dest")
|
||||
fallback_dest:depends("fallback", true)
|
||||
|
||||
fallback_xver = s:option(Value, "fallback_xver", "Fallback xver")
|
||||
fallback_xver.default = 0
|
||||
fallback_xver:depends("fallback", true)
|
||||
]]--
|
||||
|
||||
fallback_list = s:option(DynamicList, "fallback_list", "Fallback", translate("dest,path"))
|
||||
fallback_list:depends("fallback", true)
|
||||
|
||||
ss_aead = s:option(Flag, "ss_aead", translate("Shadowsocks secondary encryption"))
|
||||
ss_aead:depends("type", "Trojan-Go")
|
||||
ss_aead.default = "0"
|
||||
|
||||
ss_aead_method = s:option(ListValue, "ss_aead_method", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss_aead) do ss_aead_method:value(v, v) end
|
||||
ss_aead_method.default = "aes-128-gcm"
|
||||
ss_aead_method:depends("ss_aead", true)
|
||||
|
||||
ss_aead_pwd = s:option(Value, "ss_aead_pwd", translate("Password"))
|
||||
ss_aead_pwd.password = true
|
||||
ss_aead_pwd:depends("ss_aead", true)
|
||||
|
||||
tcp_fast_open = s:option(Flag, "tcp_fast_open", translate("TCP Fast Open"))
|
||||
tcp_fast_open.default = "0"
|
||||
tcp_fast_open:depends("type", "SS")
|
||||
tcp_fast_open:depends("type", "SSR")
|
||||
tcp_fast_open:depends("type", "Trojan")
|
||||
tcp_fast_open:depends("type", "Trojan-Plus")
|
||||
tcp_fast_open:depends("type", "Trojan-Go")
|
||||
|
||||
remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
|
||||
remote_enable.default = "1"
|
||||
remote_enable.rmempty = false
|
||||
remote_enable:depends("type", "Trojan")
|
||||
remote_enable:depends("type", "Trojan-Plus")
|
||||
remote_enable:depends("type", "Trojan-Go")
|
||||
|
||||
remote_address = s:option(Value, "remote_address", translate("Remote Address"))
|
||||
remote_address.default = "127.0.0.1"
|
||||
remote_address:depends("remote_enable", 1)
|
||||
|
||||
remote_port = s:option(Value, "remote_port", translate("Remote Port"))
|
||||
remote_port.datatype = "port"
|
||||
remote_port.default = "80"
|
||||
remote_port:depends("remote_enable", 1)
|
||||
|
||||
bind_local = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally, It is recommended to turn on when using reverse proxies or be fallback."))
|
||||
bind_local.default = "0"
|
||||
bind_local:depends("type", "V2ray")
|
||||
bind_local:depends("type", "Xray")
|
||||
|
||||
accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
|
||||
accept_lan.default = "0"
|
||||
accept_lan:depends({ type = "V2ray", protocol = "vmess" })
|
||||
accept_lan:depends({ type = "V2ray", protocol = "vless" })
|
||||
accept_lan:depends({ type = "V2ray", protocol = "http" })
|
||||
accept_lan:depends({ type = "V2ray", protocol = "socks" })
|
||||
accept_lan:depends({ type = "V2ray", protocol = "shadowsocks" })
|
||||
accept_lan:depends({ type = "V2ray", protocol = "trojan" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "vmess" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "vless" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "http" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "socks" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
accept_lan:depends({ type = "Xray", protocol = "trojan" })
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and (e.type == "V2ray" or e.type == "Xray") then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
transit_node = s:option(ListValue, "transit_node", translate("transit node"))
|
||||
transit_node:value("nil", translate("Close"))
|
||||
transit_node:value("_socks", translate("Custom Socks"))
|
||||
transit_node:value("_http", translate("Custom HTTP"))
|
||||
for k, v in pairs(nodes_table) do transit_node:value(v.id, v.remarks) end
|
||||
transit_node.default = "nil"
|
||||
transit_node:depends("type", "V2ray")
|
||||
transit_node:depends("type", "Xray")
|
||||
|
||||
transit_node_address = s:option(Value, "transit_node_address", translate("Address (Support Domain Name)"))
|
||||
transit_node_address:depends("transit_node", "_socks")
|
||||
transit_node_address:depends("transit_node", "_http")
|
||||
|
||||
transit_node_port = s:option(Value, "transit_node_port", translate("Port"))
|
||||
transit_node_port.datatype = "port"
|
||||
transit_node_port:depends("transit_node", "_socks")
|
||||
transit_node_port:depends("transit_node", "_http")
|
||||
|
||||
transit_node_username = s:option(Value, "transit_node_username", translate("Username"))
|
||||
transit_node_username:depends("transit_node", "_socks")
|
||||
transit_node_username:depends("transit_node", "_http")
|
||||
|
||||
transit_node_password = s:option(Value, "transit_node_password", translate("Password"))
|
||||
transit_node_password.password = true
|
||||
transit_node_password:depends("transit_node", "_socks")
|
||||
transit_node_password:depends("transit_node", "_http")
|
||||
|
||||
log = s:option(Flag, "log", translate("Log"))
|
||||
log.default = "1"
|
||||
log.rmempty = false
|
||||
|
||||
loglevel = s:option(ListValue, "loglevel", translate("Log Level"))
|
||||
loglevel.default = "warning"
|
||||
loglevel:value("debug")
|
||||
loglevel:value("info")
|
||||
loglevel:value("warning")
|
||||
loglevel:value("error")
|
||||
loglevel:depends({ type = "V2ray", log = true })
|
||||
loglevel:depends({ type = "Xray", log = true })
|
||||
|
||||
trojan_loglevel = s:option(ListValue, "trojan_loglevel", translate("Log Level"))
|
||||
trojan_loglevel.default = "2"
|
||||
trojan_loglevel:value("0", "all")
|
||||
trojan_loglevel:value("1", "info")
|
||||
trojan_loglevel:value("2", "warn")
|
||||
trojan_loglevel:value("3", "error")
|
||||
trojan_loglevel:value("4", "fatal")
|
||||
function trojan_loglevel.cfgvalue(self, section)
|
||||
return m:get(section, "loglevel")
|
||||
end
|
||||
function trojan_loglevel.write(self, section, value)
|
||||
m:set(section, "loglevel", value)
|
||||
end
|
||||
trojan_loglevel:depends({ type = "Trojan", log = true })
|
||||
trojan_loglevel:depends({ type = "Trojan-Plus", log = true })
|
||||
trojan_loglevel:depends({ type = "Trojan-Go", log = true })
|
||||
|
||||
return m
|
@ -0,0 +1,159 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local brook_version = api.get_brook_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var brookInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function () {
|
||||
var brookCheckBtn = document.getElementById('_brook-check_btn');
|
||||
var brookDetailElm = document.getElementById('_brook-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_brook() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_brook() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_brook(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_brook(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if (errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_brook(btn) {
|
||||
if (brookInfo === undefined) {
|
||||
checkUpdate_brook(btn);
|
||||
} else {
|
||||
doUpdate_brook(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_brook(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_brook();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=api.url("brook_check")%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice_brook();
|
||||
|
||||
if (json.code) {
|
||||
brookInfo = undefined;
|
||||
onRequestError_brook(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
brookInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if (ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if (json.remote_version) {
|
||||
urlNode = '<em style="color:red;">' + json.remote_version + '</em>';
|
||||
if (json.html_url) {
|
||||
urlNode = '<a href="' + json.html_url + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function doUpdate_brook(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_brook();
|
||||
|
||||
var brookUpdateUrl = '<%=api.url("brook_update")%>';
|
||||
// Download file
|
||||
XHR.get(brookUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: brookInfo ? brookInfo.data.browser_download_url : '',
|
||||
size: brookInfo ? brookInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_brook();
|
||||
onRequestError_brook(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
// Move file to target dir
|
||||
XHR.get(brookUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function (x, json) {
|
||||
removePageNotice_brook();
|
||||
if (json.code) {
|
||||
onRequestError_brook(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_brook(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Brook
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=brook_version ~="" and brook_version or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_brook-check_btn"
|
||||
onclick="onBtnClick_brook(this);" value="<%:Manually update%>" />
|
||||
<span id="_brook-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,159 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local hysteria_version = api.get_hysteria_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var hysteriaInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function () {
|
||||
var hysteriaCheckBtn = document.getElementById('_hysteria-check_btn');
|
||||
var hysteriaDetailElm = document.getElementById('_hysteria-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_hysteria() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_hysteria() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_hysteria(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_hysteria(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if (errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_hysteria(btn) {
|
||||
if (hysteriaInfo === undefined) {
|
||||
checkUpdate_hysteria(btn);
|
||||
} else {
|
||||
doUpdate_hysteria(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_hysteria(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_hysteria();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=api.url("hysteria_check")%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice_hysteria();
|
||||
|
||||
if (json.code) {
|
||||
hysteriaInfo = undefined;
|
||||
onRequestError_hysteria(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
hysteriaInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if (ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if (json.remote_version) {
|
||||
urlNode = '<em style="color:red;">' + json.remote_version + '</em>';
|
||||
if (json.html_url) {
|
||||
urlNode = '<a href="' + json.html_url + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function doUpdate_hysteria(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_hysteria();
|
||||
|
||||
var hysteriaUpdateUrl = '<%=api.url("hysteria_update")%>';
|
||||
// Download file
|
||||
XHR.get(hysteriaUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: hysteriaInfo ? hysteriaInfo.data.browser_download_url : '',
|
||||
size: hysteriaInfo ? hysteriaInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_hysteria();
|
||||
onRequestError_hysteria(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
// Move file to target dir
|
||||
XHR.get(hysteriaUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function (x, json) {
|
||||
removePageNotice_hysteria();
|
||||
if (json.code) {
|
||||
onRequestError_hysteria(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_hysteria(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Hysteria
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=hysteria_version ~="" and hysteria_version or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_hysteria-check_btn"
|
||||
onclick="onBtnClick_hysteria(this);" value="<%:Manually update%>" />
|
||||
<span id="_hysteria-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,175 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local trojan_go_version = api.get_trojan_go_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var trojanInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function () {
|
||||
var trojanCheckBtn = document.getElementById('_trojan-check_btn');
|
||||
var trojanDetailElm = document.getElementById('_trojan-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_trojan() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_trojan() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_trojan(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_trojan(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if (errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_trojan(btn) {
|
||||
if (trojanInfo === undefined) {
|
||||
checkUpdate_trojan(btn);
|
||||
} else {
|
||||
doUpdate_trojan(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_trojan(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_trojan();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=api.url("trojan_go_check")%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice_trojan();
|
||||
|
||||
if (json.code) {
|
||||
trojanInfo = undefined;
|
||||
onRequestError_trojan(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
trojanInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if (ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if (json.remote_version) {
|
||||
urlNode = '<em style="color:red;">' + json.remote_version + '</em>';
|
||||
if (json.html_url) {
|
||||
urlNode = '<a href="' + json.html_url + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function doUpdate_trojan(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_trojan();
|
||||
|
||||
var trojanUpdateUrl = '<%=api.url("trojan_go_update")%>';
|
||||
// Download file
|
||||
XHR.get(trojanUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: trojanInfo ? trojanInfo.data.browser_download_url : '',
|
||||
size: trojanInfo ? trojanInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_trojan();
|
||||
onRequestError_trojan(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
|
||||
// Extract file
|
||||
XHR.get(trojanUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'extract',
|
||||
file: json.file,
|
||||
subfix: trojanInfo ? trojanInfo.type : ''
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_trojan();
|
||||
onRequestError_trojan(btn, json.error);
|
||||
} else {
|
||||
btn.value = movingText;
|
||||
|
||||
// Move file to target dir
|
||||
XHR.get(trojanUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function (x, json) {
|
||||
removePageNotice_trojan();
|
||||
if (json.code) {
|
||||
onRequestError_trojan(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_trojan(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Trojan-Go
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=trojan_go_version ~="" and trojan_go_version or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_trojan-check_btn"
|
||||
onclick="onBtnClick_trojan(this);" value="<%:Manually update%>" />
|
||||
<span id="_trojan-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,175 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local v2ray_version = api.get_v2ray_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var v2rayInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function () {
|
||||
var v2rayCheckBtn = document.getElementById('_v2ray-check_btn');
|
||||
var v2rayDetailElm = document.getElementById('_v2ray-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_v2ray() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_v2ray() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_v2ray(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_v2ray(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if (errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_v2ray(btn) {
|
||||
if (v2rayInfo === undefined) {
|
||||
checkUpdate_v2ray(btn);
|
||||
} else {
|
||||
doUpdate_v2ray(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_v2ray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_v2ray();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=api.url("v2ray_check")%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice_v2ray();
|
||||
|
||||
if (json.code) {
|
||||
v2rayInfo = undefined;
|
||||
onRequestError_v2ray(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
v2rayInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if (ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if (json.remote_version) {
|
||||
urlNode = '<em style="color:red;">' + json.remote_version + '</em>';
|
||||
if (json.html_url) {
|
||||
urlNode = '<a href="' + json.html_url + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function doUpdate_v2ray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_v2ray();
|
||||
|
||||
var v2rayUpdateUrl = '<%=api.url("v2ray_update")%>';
|
||||
// Download file
|
||||
XHR.get(v2rayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: v2rayInfo ? v2rayInfo.data.browser_download_url : '',
|
||||
size: v2rayInfo ? v2rayInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_v2ray();
|
||||
onRequestError_v2ray(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
|
||||
// Extract file
|
||||
XHR.get(v2rayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'extract',
|
||||
file: json.file,
|
||||
subfix: v2rayInfo ? v2rayInfo.type : ''
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_v2ray();
|
||||
onRequestError_v2ray(btn, json.error);
|
||||
} else {
|
||||
btn.value = movingText;
|
||||
|
||||
// Move file to target dir
|
||||
XHR.get(v2rayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function (x, json) {
|
||||
removePageNotice_v2ray();
|
||||
if (json.code) {
|
||||
onRequestError_v2ray(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_v2ray(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">V2ray
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=v2ray_version ~="" and v2ray_version or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_v2ray-check_btn"
|
||||
onclick="onBtnClick_v2ray(this);" value="<%:Manually update%>" />
|
||||
<span id="_v2ray-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,175 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local xray_version = api.get_xray_version()
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var xrayInfo;
|
||||
var tokenStr = '<%=token%>';
|
||||
var manuallyUpdateText = '<%:Manually update%>';
|
||||
var noUpdateText = '<%:It is the latest version%>';
|
||||
var updateSuccessText = '<%:Update successful%>';
|
||||
var clickToUpdateText = '<%:Click to update%>';
|
||||
var inProgressText = '<%:Updating...%>';
|
||||
var unexpectedErrorText = '<%:Unexpected error%>';
|
||||
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
|
||||
var downloadingText = '<%:Downloading...%>';
|
||||
var decompressioningText = '<%:Unpacking...%>';
|
||||
var movingText = '<%:Moving...%>';
|
||||
|
||||
window.onload = function () {
|
||||
var xrayCheckBtn = document.getElementById('_xray-check_btn');
|
||||
var xrayDetailElm = document.getElementById('_xray-check_btn-detail');
|
||||
};
|
||||
|
||||
function addPageNotice_xray() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
|
||||
function removePageNotice_xray() {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
function onUpdateSuccess_xray(btn) {
|
||||
alert(updateSuccessText);
|
||||
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onRequestError_xray(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = manuallyUpdateText;
|
||||
|
||||
if (errorMessage) {
|
||||
alert(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick_xray(btn) {
|
||||
if (xrayInfo === undefined) {
|
||||
checkUpdate_xray(btn);
|
||||
} else {
|
||||
doUpdate_xray(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate_xray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice_xray();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
|
||||
XHR.get('<%=api.url("xray_check")%>', {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice_xray();
|
||||
|
||||
if (json.code) {
|
||||
xrayInfo = undefined;
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
xrayInfo = json;
|
||||
btn.disabled = false;
|
||||
btn.value = clickToUpdateText;
|
||||
btn.placeholder = clickToUpdateText;
|
||||
|
||||
if (ckeckDetailElm) {
|
||||
var urlNode = '';
|
||||
if (json.remote_version) {
|
||||
urlNode = '<em style="color:red;">' + json.remote_version + '</em>';
|
||||
if (json.html_url) {
|
||||
urlNode = '<a href="' + json.html_url + '" target="_blank">' + urlNode + '</a>';
|
||||
}
|
||||
}
|
||||
ckeckDetailElm.innerHTML = urlNode;
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.value = noUpdateText;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function doUpdate_xray(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice_xray();
|
||||
|
||||
var xrayUpdateUrl = '<%=api.url("xray_update")%>';
|
||||
// Download file
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: xrayInfo ? xrayInfo.data.browser_download_url : '',
|
||||
size: xrayInfo ? xrayInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_xray();
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
btn.value = decompressioningText;
|
||||
|
||||
// Extract file
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'extract',
|
||||
file: json.file,
|
||||
subfix: xrayInfo ? xrayInfo.type : ''
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice_xray();
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
btn.value = movingText;
|
||||
|
||||
// Move file to target dir
|
||||
XHR.get(xrayUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: json.file
|
||||
}, function (x, json) {
|
||||
removePageNotice_xray();
|
||||
if (json.code) {
|
||||
onRequestError_xray(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess_xray(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Xray
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=xray_version ~="" and xray_version or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_xray-check_btn"
|
||||
onclick="onBtnClick_xray(this);" value="<%:Manually update%>" />
|
||||
<span id="_xray-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function add_node_by_key() {
|
||||
var key = prompt("<%:Please enter the node keyword, pay attention to distinguish between spaces, uppercase and lowercase.%>", "");
|
||||
if (key) {
|
||||
window.location.href = '<%=api.url("autoswitch_add_node")%>' + "?key=" + key;
|
||||
}
|
||||
}
|
||||
function remove_node_by_key() {
|
||||
var key = prompt("<%:Please enter the node keyword, pay attention to distinguish between spaces, uppercase and lowercase.%>", "");
|
||||
if (key) {
|
||||
window.location.href = '<%=api.url("autoswitch_remove_node")%>' + "?key=" + key;
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_node_by_key()" value="<%:Add nodes to the standby node list by keywords%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="remove_node_by_key()" value="<%:Delete nodes in the standby node list by keywords%>" />
|
43
luci-app-passwall/luasrc/view/passwall/global/faq.htm
Normal file
43
luci-app-passwall/luasrc/view/passwall/global/faq.htm
Normal file
@ -0,0 +1,43 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
<div class="cbi-section cbi-tblsection">
|
||||
<div id="dns_div">
|
||||
<ul><b style="color:red"><%:About DNS issues:%></b>
|
||||
<li style="color:red">1. <span><%:Some browsers may have built-in DNS, be sure to close. Example: Chrome. Settings - Security and Privacy - Security - Use secure DNS disabled.%></span></li>
|
||||
<li style="color:red">2. <span><%:Sometimes after restart, you can not internet, especially the GFW mode. At this time, close all browsers (important), Windows Client, please `ipconfig / flushdns`. Please close the WiFi on the phone, cut the flight mode and then cut back.%></span></li>
|
||||
<li style="color:red">3. <span><%:The client DNS and the default gateway must point to this router.%></span></li>
|
||||
<li style="color:red">4. <span><%:If you have a wrong DNS process, the consequences are at your own risk!%></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="div2"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var origin = window.location.origin;
|
||||
var reset_url = origin + "<%=api.url("reset_config")%>";
|
||||
var hide_url = origin + "<%=api.url("hide")%>";
|
||||
var show_url = origin + "<%=api.url("show")%>";
|
||||
|
||||
function reset(url) {
|
||||
if (confirm('<%:Are you sure to reset?%>') == true) {
|
||||
window.location.href = reset_url;
|
||||
}
|
||||
}
|
||||
|
||||
function hide(url) {
|
||||
if (confirm('<%:Are you sure to hide?%>') == true) {
|
||||
window.location.href = hide_url;
|
||||
}
|
||||
}
|
||||
|
||||
var dom = document.getElementById("div2");
|
||||
if (dom) {
|
||||
var li = "";
|
||||
li += "<%:You can use load balancing for failover.%>" + "<br />";
|
||||
li += "<%:Restore the default configuration method. Input example in the address bar:%>" + "<a href='#' onclick='reset()'>" + reset_url + "</a>" + "<br />";
|
||||
li += "<%:Hide menu method, input example in the address bar:%>" + "<a href='#' onclick='hide()'>" + hide_url + "</a>" + "<br />";
|
||||
li += "<%:After the hidden to the display, input example in the address bar:%>" + "<a href='#'>" + show_url + "</a>" + "<br />";
|
||||
dom.innerHTML = li;
|
||||
}
|
||||
</script>
|
143
luci-app-passwall/luasrc/view/passwall/global/footer.htm
Normal file
143
luci-app-passwall/luasrc/view/passwall/global/footer.htm
Normal file
@ -0,0 +1,143 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local auto_switch = api.uci_get_type("auto_switch", "enable", 0)
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function go() {
|
||||
var _status = document.getElementsByClassName('_status');
|
||||
for (var i = 0; i < _status.length; i++) {
|
||||
var id = _status[i].getAttribute("socks_id");
|
||||
XHR.get('<%=api.url("socks_status")%>', {
|
||||
index: i,
|
||||
id: id
|
||||
},
|
||||
function(x, result) {
|
||||
var index = result.index;
|
||||
var div = '';
|
||||
var div1 = '<font style="font-weight:bold;" color="green">✓</font> ';
|
||||
var div2 = '<font style="font-weight:bold;" color="red">X</font> ';
|
||||
|
||||
if (result.socks_status) {
|
||||
div += div1;
|
||||
} else {
|
||||
div += div2;
|
||||
}
|
||||
if (result.use_http) {
|
||||
if (result.http_status) {
|
||||
div += div1;
|
||||
} else {
|
||||
div += div2;
|
||||
}
|
||||
}
|
||||
_status[index].innerHTML = div;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var global_id = null;
|
||||
var global = document.getElementById("cbi-passwall-global");
|
||||
if (global) {
|
||||
var node = global.getElementsByClassName("cbi-section-node")[0];
|
||||
var node_id = node.getAttribute("id");
|
||||
global_id = node_id;
|
||||
var reg1 = new RegExp("(?<=" + node_id + "-).*?(?=(_node))")
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
if (node.childNodes[i].childNodes && node.childNodes[i].childNodes.length > 0) {
|
||||
for (var k = 0; k < node.childNodes[i].childNodes.length; k++) {
|
||||
var dom = node.childNodes[i].childNodes[k];
|
||||
if (dom.id) {
|
||||
var s = dom.id.match(reg1);
|
||||
if (s) {
|
||||
dom_id = dom.id.split("cbi-").join("cbid-").split("-").join(".");
|
||||
var node_select = document.getElementsByName(dom_id)[0];
|
||||
var node_select_value = node_select.value;
|
||||
if (node_select_value && node_select_value != "nil" && node_select_value.indexOf("_default") != 0 && node_select_value.indexOf("_direct") != 0 && node_select_value.indexOf("_blackhole") != 0) {
|
||||
if (global_id != null && node_select_value.indexOf("tcp") == 0) {
|
||||
var d = global_id + "-tcp_node";
|
||||
d = d.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
|
||||
var dom = document.getElementsByName(d)[0];
|
||||
var _node_select_value = dom.value;
|
||||
if (_node_select_value && _node_select_value != "nil") {
|
||||
node_select_value = _node_select_value;
|
||||
}
|
||||
}
|
||||
var v = document.getElementById(dom_id + "-" + node_select_value);
|
||||
if (v) {
|
||||
node_select.title = v.text;
|
||||
} else {
|
||||
node_select.title = node_select.options[node_select.options.selectedIndex].text;
|
||||
}
|
||||
|
||||
var new_html = "";
|
||||
|
||||
var new_a = document.createElement("a");
|
||||
new_a.innerHTML = "<%:Edit%>";
|
||||
new_a.href = "#";
|
||||
new_a.setAttribute("onclick", "location.href='" + '<%=api.url("node_config")%>' + "/" + node_select_value + "'");
|
||||
new_html = new_a.outerHTML;
|
||||
|
||||
if (s[0] == "tcp" || s[0] == "udp") {
|
||||
var log_a = document.createElement("a");
|
||||
log_a.innerHTML = "<%:Log%>";
|
||||
log_a.href = "#";
|
||||
log_a.setAttribute("onclick", "window.open('" + '<%=api.url("get_redir_log")%>' + "?proto=" + s[0] + "', '_blank')");
|
||||
new_html += "  " + log_a.outerHTML;
|
||||
}
|
||||
|
||||
node_select.insertAdjacentHTML("afterend", "  " + new_html);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var socks = document.getElementById("cbi-passwall-socks");
|
||||
if (socks) {
|
||||
var socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
|
||||
socks_enabled_dom.parentNode.removeChild(socks_enabled_dom);
|
||||
var descr = socks.getElementsByClassName("cbi-section-descr")[0];
|
||||
descr.outerHTML = socks_enabled_dom.outerHTML;
|
||||
rows = socks.getElementsByClassName("cbi-section-table-row");
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var id = row.id;
|
||||
if (!id) continue;
|
||||
var dom_id = id + "-node";
|
||||
var node = document.getElementById(dom_id);
|
||||
var dom_id = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
|
||||
var node_select = document.getElementsByName(dom_id)[0];
|
||||
var node_select_value = node_select.value;
|
||||
if (node_select_value && node_select_value != "nil") {
|
||||
if (global_id != null && node_select_value.indexOf("tcp") == 0) {
|
||||
var d = global_id + "-tcp_node";
|
||||
d = d.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
|
||||
var dom = document.getElementsByName(d)[0];
|
||||
var _node_select_value = dom.value;
|
||||
if (_node_select_value && _node_select_value != "nil") {
|
||||
node_select_value = _node_select_value;
|
||||
}
|
||||
}
|
||||
var v = document.getElementById(dom_id + "-" + node_select_value);
|
||||
if (v) {
|
||||
node_select.title = v.text;
|
||||
} else {
|
||||
node_select.title = node_select.options[node_select.options.selectedIndex].text;
|
||||
}
|
||||
|
||||
var new_a = document.createElement("a");
|
||||
new_a.innerHTML = "<%:Edit%>";
|
||||
new_a.href = "#";
|
||||
new_a.setAttribute("onclick","location.href='" + '<%=api.url("node_config")%>' + "/" + node_select_value + "'");
|
||||
|
||||
node_select.insertAdjacentHTML("afterend", "  " + new_a.outerHTML);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout("go()", 1000);
|
||||
|
||||
//]]>
|
||||
</script>
|
278
luci-app-passwall/luasrc/view/passwall/global/status.htm
Normal file
278
luci-app-passwall/luasrc/view/passwall/global/status.htm
Normal file
File diff suppressed because one or more lines are too long
26
luci-app-passwall/luasrc/view/passwall/haproxy/status.htm
Normal file
26
luci-app-passwall/luasrc/view/passwall/haproxy/status.htm
Normal file
@ -0,0 +1,26 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local console_port = api.uci_get_type("global_haproxy", "console_port", "")
|
||||
-%>
|
||||
<p id="_status"></p>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
XHR.poll(3, '<%=api.url("haproxy_status")%>', null,
|
||||
function(x, result) {
|
||||
if (x && x.status == 200) {
|
||||
var _status = document.getElementById('_status');
|
||||
if (_status) {
|
||||
if (result) {
|
||||
_status.innerHTML = '<input type="button" class="btn cbi-button cbi-button-apply" value="<%:Enter interface%>" onclick="openwebui()" />';
|
||||
} else {
|
||||
_status.innerHTML = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function openwebui(){
|
||||
var url = window.location.hostname + ":<%=console_port%>";
|
||||
window.open('http://' + url, 'target', '');
|
||||
}
|
||||
//]]></script>
|
30
luci-app-passwall/luasrc/view/passwall/log/log.htm
Normal file
30
luci-app-passwall/luasrc/view/passwall/log/log.htm
Normal file
@ -0,0 +1,30 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function clearlog(btn) {
|
||||
XHR.get('<%=api.url("clear_log")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
var log_textarea = document.getElementById('log_textarea');
|
||||
log_textarea.innerHTML = "";
|
||||
log_textarea.scrollTop = log_textarea.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
XHR.poll(5, '<%=api.url("get_log")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
var log_textarea = document.getElementById('log_textarea');
|
||||
log_textarea.innerHTML = x.responseText;
|
||||
}
|
||||
}
|
||||
);
|
||||
//]]>
|
||||
</script>
|
||||
<fieldset class="cbi-section" id="_log_fieldset">
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clearlog()" value="<%:Clear logs%>" />
|
||||
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="40" wrap="off" readonly="readonly"></textarea>
|
||||
</fieldset>
|
@ -0,0 +1,108 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
|
||||
<style>
|
||||
#add_link_div{
|
||||
display: none;
|
||||
width: auto;
|
||||
position: absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 99;
|
||||
text-align: center;
|
||||
background: white;
|
||||
box-shadow: darkgrey 10px 10px 30px 5px;
|
||||
padding: 30px 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function ajax_add_node(link) {
|
||||
if (link) {
|
||||
XHR.get('<%=api.url("link_add_node")%>', {
|
||||
'link': link
|
||||
},
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function open_add_link_div() {
|
||||
document.getElementById("add_link_div").style.display = "block";
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
|
||||
function close_add_link_div() {
|
||||
document.getElementById("add_link_div").style.display = "none";
|
||||
}
|
||||
|
||||
function add_node() {
|
||||
var nodes_link = document.getElementById("nodes_link").value;
|
||||
if (nodes_link.trim() != "") {
|
||||
var supports = "ss ssr vmess vless trojan trojan-go hysteria";
|
||||
var itype = nodes_link.split('://')[0];
|
||||
if (itype.trim() != "" && supports.indexOf(itype) >= 0) {
|
||||
ajax_add_node(nodes_link);
|
||||
}
|
||||
else {
|
||||
alert("<%:Please enter the correct link.%>");
|
||||
}
|
||||
}
|
||||
else {
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
}
|
||||
|
||||
function clear_all_nodes() {
|
||||
if (confirm('<%:Are you sure to clear all nodes?%>') == true){
|
||||
XHR.get('<%=api.url("clear_all_nodes")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div id="add_link_div">
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title"><%:SS/SSR/Vmess/VLESS/Trojan/Hysteria Link%></label>
|
||||
<div class="cbi-value-field">
|
||||
<textarea id="nodes_link" rows="5" cols="50"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbi-value">
|
||||
<div class="cbi-value-field" style="display: unset">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_node()" value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_add_link_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title"></label>
|
||||
<div class="cbi-value-field">
|
||||
<input class="btn cbi-button cbi-button-add" type="submit" name="cbi.cts.<%=api.appname%>.nodes." value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="open_add_link_div()" value="<%:Add the node via the link%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
|
||||
<input class="btn cbi-button" type="button" onclick="checked_all_node(this)" value="<%:Select all%>" />
|
||||
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
||||
<div id="div_node_count"></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,960 @@
|
||||
<%+cbi/valueheader%>
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
local has_v2ray = api.is_finded("v2ray")
|
||||
local has_xray = api.is_finded("xray")
|
||||
-%>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
function padright(str, cnt, pad) {
|
||||
return str + Array(cnt + 1).join(pad);
|
||||
}
|
||||
|
||||
function b64EncodeUnicode(str) {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
||||
return String.fromCharCode('0x' + p1);
|
||||
}));
|
||||
}
|
||||
|
||||
function b64encutf8safe(str) {
|
||||
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
|
||||
}
|
||||
|
||||
function b64DecodeUnicode(str) {
|
||||
return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
}
|
||||
|
||||
function b64decutf8safe(str) {
|
||||
var l;
|
||||
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
l = str.length;
|
||||
l = (4 - l % 4) % 4;
|
||||
if (l)
|
||||
str = padright(str, l, "=");
|
||||
return b64DecodeUnicode(str);
|
||||
}
|
||||
|
||||
function b64encsafe(str) {
|
||||
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
|
||||
}
|
||||
|
||||
function b64decsafe(str) {
|
||||
var l;
|
||||
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
l = str.length;
|
||||
l = (4 - l % 4) % 4;
|
||||
if (l)
|
||||
str = padright(str, l, "=");
|
||||
return atob(str);
|
||||
}
|
||||
|
||||
function dictvalue(d, key) {
|
||||
var v = d[key];
|
||||
if (typeof(v) === 'undefined' || v === '')
|
||||
return '';
|
||||
return b64decsafe(v);
|
||||
}
|
||||
function parseNodeUrl(url) {
|
||||
var m = url.match(/^(([^:\/?#]+:)?(?:\/\/((?:([^\/?#:]*)([^\/?#:]*)@)?([^\/?#:]*)(?::([^\/?#:]*))?)))?([^?#]*)(\?[^#]*)?(#.*)?$/),
|
||||
r = {
|
||||
hash: m[10] || "", // #asd
|
||||
host: m[3] || "", // localhost:257
|
||||
hostname: m[6] || "", // localhost
|
||||
href: m[0] || "", // http://username:password@localhost:257/deploy/?asd=asd#asd
|
||||
origin: m[1] || "", // http://username:password@localhost:257
|
||||
pathname: m[8] || (m[1] ? "/" : ""), // /deploy/
|
||||
port: m[7] || "", // 257
|
||||
protocol: m[2] || "", // http:
|
||||
search: m[9] || "", // ?asd=asd
|
||||
passwd: m[4] || "", // username
|
||||
removed: m[5] || "" // password
|
||||
};
|
||||
if (r.protocol.length === 2) {
|
||||
r.protocol = "file:///" + r.protocol.toUpperCase();
|
||||
r.origin = r.protocol + "//" + r.host;
|
||||
}
|
||||
r.href = r.origin + r.pathname + r.search + r.hash;
|
||||
return m && r;
|
||||
}
|
||||
|
||||
function buildUrl(btn, urlname, sid) {
|
||||
var opt = {
|
||||
base: "cbid.passwall",
|
||||
client : true,
|
||||
get: function(opt) {
|
||||
var id = this.base + "." + opt;
|
||||
var obj = document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
|
||||
if (obj) {
|
||||
return obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getlist: function(opt) {
|
||||
var id = this.base + "." + opt;
|
||||
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
|
||||
var ret = [];
|
||||
if (objs) {
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
ret[i] = objs[i].value;
|
||||
}
|
||||
} else {
|
||||
alert("<%:Faltal on get option, please help in debug: %>" + opt);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
query: function(param, src, default_value, tval = "1", fval = "0") {
|
||||
var ret = "&" + param + "=";
|
||||
var obj = this.get(src);
|
||||
if (obj) {
|
||||
if (obj.type === "checkbox") {
|
||||
return ret + (obj.checked === true ? tval : fval);
|
||||
} else {
|
||||
var result = encodeURIComponent(obj.value);
|
||||
if ((result == null || result.trim() == "") && default_value)
|
||||
result = default_value;
|
||||
return ret + result;
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var s = document.getElementById(urlname + "-status");
|
||||
if (!s) {
|
||||
alert("Never");
|
||||
return false;
|
||||
}
|
||||
opt.base = "cbid." + urlname + "." + sid;
|
||||
opt.client = urlname.indexOf("server") === -1;
|
||||
var v_type = opt.get("type").value;
|
||||
var v_alias = opt.get("remarks");
|
||||
var url = null;
|
||||
if (v_type === "SS") {
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
var v_method = opt.get("ss_encrypt_method");
|
||||
var v_password = opt.get("password");
|
||||
|
||||
url = b64encsafe(v_method.value + ":" + v_password.value) + "@" +
|
||||
v_server.value + ":" +
|
||||
v_port.value + "/?";
|
||||
|
||||
var params = "";
|
||||
var v_plugin = opt.get("ss_plugin").value;
|
||||
if (v_plugin && v_plugin != "none") {
|
||||
if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") {
|
||||
v_plugin = "obfs-local";
|
||||
}
|
||||
var v_plugin_opts = opt.get("ss_plugin_opts").value;
|
||||
if (v_plugin_opts && v_plugin_opts != "") {
|
||||
v_plugin += encodeURI(";" + v_plugin_opts);
|
||||
}
|
||||
params += "&plugin=" + encodeURI(v_plugin);
|
||||
}
|
||||
params += "&group="
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
if (params[0] == "&") {
|
||||
params = params.substring(1);
|
||||
}
|
||||
url += params;
|
||||
} else if (v_type === "SSR") {
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
var v_protocol = opt.get("ssr_protocol");
|
||||
var v_method = opt.get("ssr_encrypt_method");
|
||||
var v_obfs = opt.get("obfs");
|
||||
var v_password = opt.get("password");
|
||||
var v_obfs_param = opt.get("obfs_param");
|
||||
var v_protocol_param = opt.get("protocol_param");
|
||||
var ssr_str = v_server.value + ":" +
|
||||
v_port.value + ":" +
|
||||
v_protocol.value + ":" +
|
||||
v_method.value + ":" +
|
||||
v_obfs.value + ":" +
|
||||
b64encsafe(v_password.value) +
|
||||
"/?obfsparam=" + b64encsafe(v_obfs_param.value) +
|
||||
"&protoparam=" + b64encsafe(v_protocol_param.value) +
|
||||
"&remarks=" + b64encutf8safe(v_alias.value);
|
||||
url = b64encsafe(ssr_str);
|
||||
} else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vmess") {
|
||||
v_type = "vmess";
|
||||
var info = {};
|
||||
info.v = "2";
|
||||
info.ps = v_alias.value;
|
||||
info.add = opt.get("address").value;
|
||||
info.port = opt.get("port").value;
|
||||
info.id = opt.get("uuid").value;
|
||||
|
||||
var v_transport = opt.get("transport").value;
|
||||
if (v_transport === "ws") {
|
||||
info.host = opt.get("ws_host").value;
|
||||
info.path = opt.get("ws_path").value;
|
||||
} else if (v_transport === "h2") {
|
||||
info.host = opt.get("h2_host").value;
|
||||
info.path = opt.get("h2_path").value;
|
||||
} else if (v_transport === "tcp") {
|
||||
info.type = opt.get("tcp_guise").value;
|
||||
info.host = opt.get("tcp_guise_http_host").value;
|
||||
info.path = opt.get("tcp_guise_http_path").value;
|
||||
} else if (v_transport === "mkcp") {
|
||||
v_transport = "kcp";
|
||||
info.type = opt.get("mkcp_guise").value;
|
||||
} else if (v_transport === "quic") {
|
||||
info.type = opt.get("quic_guise").value;
|
||||
info.key = opt.get("quic_key").value;
|
||||
info.securty = opt.get("quic_security").value;
|
||||
} else if (v_transport === "grpc") {
|
||||
info.path = opt.get("grpc_serviceName").value;
|
||||
}
|
||||
if (info.path && info.path != "") {
|
||||
info.path = encodeURI(info.path);
|
||||
}
|
||||
info.net = v_transport;
|
||||
|
||||
info.security = opt.get("security").value || "auto";
|
||||
if (opt.get("tls").checked) {
|
||||
var v_security = "tls";
|
||||
info.tls = "tls";
|
||||
info.sni = opt.get("tls_serverName").value;
|
||||
}
|
||||
url = b64EncodeUnicode(JSON.stringify(info));
|
||||
} else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vless") {
|
||||
v_type = "vless";
|
||||
var v_password = opt.get("uuid");
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
url = encodeURIComponent(v_password.value) +
|
||||
"@" + v_server.value +
|
||||
":" + v_port.value + "?";
|
||||
|
||||
var params = "";
|
||||
var v_transport = opt.get("transport").value;
|
||||
if (v_transport === "ws") {
|
||||
params += opt.query("host", "ws_host");
|
||||
params += opt.query("path", "ws_path");
|
||||
} else if (v_transport === "h2") {
|
||||
params += opt.query("host", "h2_host");
|
||||
params += opt.query("path", "h2_path");
|
||||
} else if (v_transport === "tcp") {
|
||||
params += opt.query("headerType", "tcp_guise");
|
||||
params += opt.query("host", "tcp_guise_http_host");
|
||||
params += opt.query("path", "tcp_guise_http_path");
|
||||
} else if (v_transport === "mkcp") {
|
||||
v_transport = "kcp";
|
||||
params += opt.query("headerType", "mkcp_guise");
|
||||
} else if (v_transport === "quic") {
|
||||
params += opt.query("headerType", "quic_guise");
|
||||
params += opt.query("key", "quic_key");
|
||||
params += opt.query("quicSecurity", "quic_security");
|
||||
} else if (v_transport === "grpc") {
|
||||
//不知道是用path还是serviceName,这里先这样吧
|
||||
params += opt.query("path", "grpc_serviceName");
|
||||
params += opt.query("serviceName", "grpc_serviceName");
|
||||
}
|
||||
params += "&type=" + v_transport;
|
||||
|
||||
params += opt.query("encryption", "encryption");
|
||||
if (opt.get("tls").checked) {
|
||||
var v_security = "tls";
|
||||
if (opt.get("xtls").checked) {
|
||||
v_security = "xtls";
|
||||
var v_flow = "xtls-rprx-direct";
|
||||
if (opt.get("flow").value) {
|
||||
v_flow = opt.get("flow").value;
|
||||
}
|
||||
params += "&flow=" + v_flow;
|
||||
}
|
||||
params += "&security=" + v_security;
|
||||
params += opt.query("sni", "tls_serverName");
|
||||
}
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
if (params[0] == "&") {
|
||||
params = params.substring(1);
|
||||
}
|
||||
url += params;
|
||||
} else if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan") || v_type === "Trojan" || v_type === "Trojan-Plus" || v_type === "Trojan-Go") {
|
||||
if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan") || v_type === "Trojan-Plus") {
|
||||
v_type = "trojan";
|
||||
}
|
||||
var v_password = opt.get(!opt.client && v_type === "Trojan-Go" ? "passwords" : "password");
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
url = encodeURIComponent(v_password.value) +
|
||||
"@" + v_server.value +
|
||||
":" + v_port.value + "/?";
|
||||
var params = "";
|
||||
if (opt.get("tls").checked) {
|
||||
params += opt.query("sni", "tls_serverName");
|
||||
if (v_type !== "Trojan-Go") {
|
||||
params += "&tls=1"
|
||||
params += opt.query("allowinsecure", "tls_allowInsecure");
|
||||
}
|
||||
}
|
||||
if (v_type === "Trojan-Go") {
|
||||
if (!opt.get("tls").checked && opt.get("trojan_transport").value === "original") {
|
||||
var plugin = {};
|
||||
plugin.type = opt.get("plugin_type").value;
|
||||
if (plugin.type !== "plaintext") {
|
||||
plugin.command = opt.get("plugin_cmd").value;
|
||||
plugin.option = opt.get("plugin_option").value;
|
||||
plugin.arg = opt.getlist("plugin_arg");
|
||||
}
|
||||
params += "&plugin=" + encodeURIComponent(JSON.stringify(plugin));
|
||||
}
|
||||
params += opt.query("type", "trojan_transport");
|
||||
var ws = (opt.get("trojan_transport").value.indexOf("ws") !== -1);
|
||||
var h2 = (opt.get("trojan_transport").value.indexOf("h2") !== -1);
|
||||
if (ws) {
|
||||
params += opt.query("host", "ws_host");
|
||||
params += opt.query("path", "ws_path");
|
||||
} else if (h2) {
|
||||
params += opt.query("host", "h2_host");
|
||||
params += opt.query("path", "h2_path");
|
||||
}
|
||||
var enc = "none";
|
||||
if (opt.get("ss_aead").checked === true) {
|
||||
enc = "ss;" +
|
||||
opt.get("ss_aead_method").value +
|
||||
":" + opt.get("ss_aead_pwd").value;
|
||||
}
|
||||
params += "&encryption=" + encodeURIComponent(enc);
|
||||
}
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
if (params[0] == "&") {
|
||||
params = params.substring(1);
|
||||
}
|
||||
url += params;
|
||||
} else if (v_type === "Brook") {
|
||||
var url = "";
|
||||
var params = "?";
|
||||
var v_protocol = opt.get("brook_protocol");
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
var v_password = opt.get("password");
|
||||
var b_protocol_value = v_protocol.value.split('client').join('server');
|
||||
|
||||
var url_protocol = b_protocol_value;
|
||||
params += opt.query("password", "password");
|
||||
if (b_protocol_value == "wsserver") {
|
||||
var server = '';
|
||||
var prefix = "ws://";
|
||||
if (opt.get("brook_tls").checked) {
|
||||
prefix = "wss://";
|
||||
url_protocol = 'wssserver';
|
||||
}
|
||||
var v_path = opt.get("ws_path");
|
||||
var v_path_value = v_path.value || '/ws';
|
||||
if (v_path_value.length > 1 && v_path_value.indexOf('/') < 0) {
|
||||
v_path_value = '/' + v_path_value;
|
||||
}
|
||||
params += "&" + url_protocol + "=" + encodeURIComponent(prefix + v_server.value + ":" + v_port.value + v_path_value);
|
||||
} else {
|
||||
params += "&" + url_protocol + "=" + encodeURIComponent(v_server.value + ":" + v_port.value);
|
||||
}
|
||||
url += url_protocol;
|
||||
url += params;
|
||||
} else if (v_type === "Hysteria") {
|
||||
var v_server = opt.get("address");
|
||||
var v_port = opt.get("port");
|
||||
var params = "";
|
||||
params += opt.query("protocol", "hysteria_protocol");
|
||||
params += opt.query("auth", "hysteria_auth_password");
|
||||
params += opt.query("peer", "tls_serverName");
|
||||
params += opt.query("insecure", "tls_allowInsecure");
|
||||
params += opt.query("upmbps", "hysteria_up_mbps", 1000);
|
||||
params += opt.query("downmbps", "hysteria_down_mbps", 1000);
|
||||
params += opt.query("alpn", "hysteria_alpn");
|
||||
params += opt.query("obfsParam", "hysteria_obfs");
|
||||
var url =
|
||||
v_server.value + ":" +
|
||||
v_port.value + "?" +
|
||||
params +
|
||||
"#" + encodeURI(v_alias.value);
|
||||
}
|
||||
if (url) {
|
||||
url = v_type.toLowerCase() + "://" + url;
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.textContent = url;
|
||||
textarea.style.position = "fixed";
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand("copy"); // Security exception may be thrown by some browsers.
|
||||
s.innerHTML = "<font color='green'><%:Share URL to clipboard successfully.%></font>";
|
||||
} catch (ex) {
|
||||
s.innerHTML = "<font color='red'><%:Share URL to clipboard unable.%></font>";
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
//alert(url);
|
||||
} else {
|
||||
alert("<%:Not a supported scheme:%> " + v_type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fromUrl(btn, urlname, sid) {
|
||||
var opt = {
|
||||
base: 'cbid.passwall',
|
||||
client : true,
|
||||
get: function(opt) {
|
||||
var obj;
|
||||
var id = this.base + '.' + opt;
|
||||
obj = document.getElementsByName(id)[0] || document.getElementById(id);
|
||||
if (obj) {
|
||||
var combobox = document.getElementById('cbi.combobox.' + id);
|
||||
if (combobox) {
|
||||
obj.combobox = combobox;
|
||||
}
|
||||
var div = document.getElementById(id);
|
||||
if (div && div.getElementsByTagName("li").length > 0) {
|
||||
obj = div;
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
set: function(opt, val) {
|
||||
var obj;
|
||||
obj = this.get(opt);
|
||||
if (obj) {
|
||||
var event = document.createEvent("HTMLEvents");
|
||||
event.initEvent("change", true, true);
|
||||
if (obj.type === 'checkbox') {
|
||||
obj.checked = val;
|
||||
} else {
|
||||
obj.value = val;
|
||||
if (obj.combobox) {
|
||||
obj.combobox.value = val;
|
||||
}
|
||||
|
||||
var list = obj.getElementsByTagName("li");
|
||||
if (list.length > 0) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var li = list[i];
|
||||
var data = li.getAttribute("data-value");
|
||||
li.removeAttribute("selected");
|
||||
li.removeAttribute("display");
|
||||
if (data && data == val) {
|
||||
li.setAttribute("selected", true);
|
||||
li.setAttribute("display", "0");
|
||||
}
|
||||
}
|
||||
var input = document.getElementsByName(obj.id)[0];
|
||||
if (input) {
|
||||
input.value = val;
|
||||
} else {
|
||||
var input = document.createElement("input");
|
||||
input.setAttribute("type", "hidden") ;
|
||||
input.setAttribute("name", obj.id) ;
|
||||
input.setAttribute("value", val) ;
|
||||
obj.appendChild(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
obj.dispatchEvent(event);
|
||||
} catch(err) {
|
||||
}
|
||||
} else {
|
||||
//alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
|
||||
}
|
||||
},
|
||||
setlist: function(opt, vlist) {
|
||||
var id = this.base + "." + opt;
|
||||
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
|
||||
if (objs) {
|
||||
var values = "";
|
||||
for (var i = 0; i < vlist.length; i++) {
|
||||
values += vlist[i] + ", ";
|
||||
}
|
||||
alert("Manually input the option:\n" + opt + "s:\n[" + values + "]");
|
||||
} else {
|
||||
//alert("<%:Faltal on set option, please help in debug: %>" + opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var s = document.getElementById(urlname + '-status');
|
||||
if (!s) {
|
||||
alert("Never");
|
||||
return false;
|
||||
}
|
||||
opt.base = "cbid." + urlname + "." + sid;
|
||||
opt.client = urlname.indexOf("server") === -1;
|
||||
var ssrurl = prompt('<%:Paste Share URL Here%>', '');
|
||||
if (ssrurl === null || ssrurl === "") {
|
||||
return false;
|
||||
}
|
||||
s.innerHTML = "";
|
||||
var ssu = ssrurl.split('://');
|
||||
var event = document.createEvent("HTMLEvents");
|
||||
event.initEvent("change", true, true);
|
||||
if (ssu[0] === "ssr") {
|
||||
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/);
|
||||
var sstr = b64decsafe(ssu[1]);
|
||||
var ploc = sstr.indexOf("/?");
|
||||
var url0 = "", param = "";
|
||||
if (ploc > 0) {
|
||||
url0 = sstr.substr(0, ploc);
|
||||
param = sstr.substr(ploc + 2);
|
||||
} else {
|
||||
var url0 = sstr;
|
||||
}
|
||||
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
|
||||
if (!ssm || ssm.length < 7) {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
var pdict = {};
|
||||
if (param.length > 2) {
|
||||
var a = param.split('&');
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
var b = a[i].split('=');
|
||||
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
|
||||
}
|
||||
}
|
||||
opt.set('type', "SSR");
|
||||
opt.set('address', ssm[1]);
|
||||
opt.set('port', ssm[2]);
|
||||
opt.set('ssr_protocol', ssm[3]);
|
||||
opt.set('ssr_encrypt_method', ssm[4]);
|
||||
opt.set('obfs', ssm[5]);
|
||||
opt.set('password', b64decsafe(ssm[6]));
|
||||
opt.set('obfs_param', dictvalue(pdict, 'obfsparam'));
|
||||
opt.set('protocol_param', dictvalue(pdict, 'protoparam'));
|
||||
var rem = pdict['remarks'];
|
||||
if (typeof(rem) !== 'undefined' && rem !== '' && rem.length > 0)
|
||||
opt.set('remarks', b64decutf8safe(rem));
|
||||
} else if (ssu[0] === "ss") {
|
||||
var url0 = "", param = "";
|
||||
var sipIndex = ssu[1].indexOf("@");
|
||||
var ploc = ssu[1].indexOf("#");
|
||||
if (ploc > 0) {
|
||||
url0 = ssu[1].substr(0, ploc);
|
||||
param = ssu[1].substr(ploc + 1);
|
||||
} else {
|
||||
url0 = ssu[1];
|
||||
}
|
||||
if (sipIndex !== -1) {
|
||||
// SIP002
|
||||
var userInfo = b64decsafe(url0.substr(0, sipIndex));
|
||||
var temp = url0.substr(sipIndex + 1).split("/?");
|
||||
var serverInfo = temp[0].split(":");
|
||||
var server = serverInfo[0];
|
||||
var port = serverInfo[1];
|
||||
var method, password, plugin, pluginOpts;
|
||||
if (temp[1]) {
|
||||
var pluginInfo = decodeURIComponent(temp[1]);
|
||||
var pluginIndex = pluginInfo.indexOf(";");
|
||||
var pluginNameInfo = pluginInfo.substr(0, pluginIndex);
|
||||
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
|
||||
pluginOpts = pluginInfo.substr(pluginIndex + 1);
|
||||
}
|
||||
var userInfoSplitIndex = userInfo.indexOf(":");
|
||||
if (userInfoSplitIndex !== -1) {
|
||||
method = userInfo.substr(0, userInfoSplitIndex);
|
||||
password = userInfo.substr(userInfoSplitIndex + 1);
|
||||
}
|
||||
opt.set('type', "SS");
|
||||
opt.set('address', server);
|
||||
opt.set('port', port);
|
||||
opt.set('password', password || "");
|
||||
opt.set('ss_encrypt_method', method || "");
|
||||
opt.set('ss_plugin', plugin || "none");
|
||||
if (plugin && plugin != "none") {
|
||||
opt.set('ss_plugin_opts', pluginOpts || "");
|
||||
}
|
||||
if (param !== undefined) {
|
||||
opt.set('remarks', decodeURI(param));
|
||||
}
|
||||
} else {
|
||||
var sstr = b64decsafe(url0);
|
||||
var team = sstr.split('@');
|
||||
opt.set('type', "SS");
|
||||
var part1 = team[0].split(':');
|
||||
var part2 = team[1].split(':');
|
||||
opt.set('address', part2[0]);
|
||||
opt.set('port', part2[1]);
|
||||
opt.set('password', part1[1]);
|
||||
opt.set('ss_encrypt_method', part1[0]);
|
||||
opt.set('ss_plugin', "none");
|
||||
//opt.set('ss_plugin_opts', "");
|
||||
if (param !== undefined) {
|
||||
opt.set('remarks', decodeURI(param));
|
||||
}
|
||||
}
|
||||
} else if (ssu[0] === "trojan" || ssu[0] === "trojan-plus") {
|
||||
var stype = "Trojan-Plus";
|
||||
var m = parseNodeUrl(ssrurl);
|
||||
var password = m.passwd;
|
||||
if (password === "") {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
var queryParam = {};
|
||||
if (m.search.length > 1) {
|
||||
var query = m.search.split('?');
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
var params;
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
if (queryParam.mux || queryParam.ws || queryParam.h2 || queryParam.ss || queryParam.plugin) {
|
||||
stype = "Trojan-Go";
|
||||
}
|
||||
opt.set('type', stype);
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
opt.set(!opt.client && stype === "Trojan-Go" ? 'passwords' : 'password', decodeURIComponent(password));
|
||||
var tls = true;
|
||||
if (stype === "Trojan-Go") {
|
||||
tls = queryParam.plugin === undefined;
|
||||
}
|
||||
if (tls === false) { alert("TODO: plugin params for trojan-go."); }
|
||||
opt.set('tls', tls);
|
||||
if (tls) {
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
|
||||
}
|
||||
|
||||
if (stype === "Trojan-Go") {
|
||||
var tran = 'original';
|
||||
var ws = null;
|
||||
var h2 = null;
|
||||
if (queryParam.type) {
|
||||
ws = queryParam.type.indexOf('ws') !== -1;
|
||||
h2 = queryParam.type.indexOf('h2') !== -1;
|
||||
}
|
||||
if (ws && h2) {
|
||||
tran = 'h2+ws'
|
||||
} else {
|
||||
if (ws) tran = 'ws';
|
||||
if (h2) tran = 'h2';
|
||||
}
|
||||
opt.set('trojan_transport', 'tran');
|
||||
if (ws) {
|
||||
opt.set('ws_host', queryParam.wshost || '');
|
||||
opt.set('ws_path', queryParam.wspath || '/');
|
||||
}
|
||||
if (h2) {
|
||||
opt.set('h2_host', queryParam.h2host || '');
|
||||
opt.set('h2_path', queryParam.h2path || '/');
|
||||
}
|
||||
var ss = queryParam.ss === '1';
|
||||
opt.set('ss_aead', ss);
|
||||
if (ss) {
|
||||
opt.set('ss_aead_method', queryParam.ssmethod.toLowerCase() || '');
|
||||
opt.set('ss_aead_pwd', queryParam.sspasswd || '');
|
||||
}
|
||||
}
|
||||
opt.set('mux', queryParam.mux === '1');
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
} else if (ssu[0] === "trojan-go") {
|
||||
var m = parseNodeUrl(ssrurl);
|
||||
var password = m.passwd;
|
||||
if (password === "") {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
var queryParam = {};
|
||||
if (m.search.length > 1) {
|
||||
var query = m.search.split('?');
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
var params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
opt.set('type', 'Trojan-Go');
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
opt.set(opt.client ? 'password' : 'passwords', decodeURIComponent(password));
|
||||
opt.set('tls', '1');
|
||||
opt.set('tls_allowInsecure', '0');
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
var plugin = queryParam.plugin !== undefined;
|
||||
if (plugin) {
|
||||
opt.set('trojan_transport', 'original');
|
||||
var plugin = JSON.parse(queryParam.plugin);
|
||||
if (plugin) {
|
||||
opt.set('plugin_type', plugin.type);
|
||||
if (plugin.type !== "plaintext") {
|
||||
opt.set('plugin_cmd', plugin.command);
|
||||
opt.set('plugin_option', plugin.option);
|
||||
opt.setlist('plugin_arg', plugin.arg);
|
||||
}
|
||||
} else
|
||||
alert(queryParam.plugin);
|
||||
}
|
||||
var tran = 'original';
|
||||
var or = queryParam.type === undefined || queryParam.type === 'original';
|
||||
var ws = null;
|
||||
var h2 = null;
|
||||
if (queryParam.type) {
|
||||
ws = queryParam.type.indexOf('ws') !== -1;
|
||||
h2 = queryParam.type.indexOf('h2') !== -1;
|
||||
}
|
||||
if (ws && h2) {
|
||||
tran = 'h2+ws'
|
||||
} else {
|
||||
if (ws) tran = 'ws';
|
||||
if (h2) tran = 'h2';
|
||||
}
|
||||
opt.set('trojan_transport', tran);
|
||||
if (ws) {
|
||||
opt.set('ws_host', queryParam.host || '');
|
||||
opt.set('ws_path', queryParam.path || '/');
|
||||
}
|
||||
if (h2){
|
||||
opt.set('h2_host', queryParam.host || '');
|
||||
opt.set('h2_path', queryParam.path || '/');
|
||||
}
|
||||
var enc = {};
|
||||
var ss = false;
|
||||
if (queryParam.encryption) {
|
||||
var r = queryParam.encryption.match(/^(ss);([^;:]*)[;:](.*)$/),
|
||||
enc = {type: r[1], method: r[2], password: r[3]};
|
||||
}
|
||||
ss = enc.type === 'ss';
|
||||
opt.set('ss_aead', ss);
|
||||
if (ss) {
|
||||
opt.set('ss_aead_method', enc.method.toLowerCase() || '');
|
||||
opt.set('ss_aead_pwd', enc.password || '');
|
||||
}
|
||||
opt.set('mux', '1');
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
} else if (ssu[0] === "vmess") {
|
||||
var sstr = b64DecodeUnicode(ssu[1]);
|
||||
var ploc = sstr.indexOf("/?");
|
||||
<% if has_v2ray then %>
|
||||
opt.set('type', "V2ray");
|
||||
<% elseif has_xray then %>
|
||||
opt.set('type', "Xray");
|
||||
<% end %>
|
||||
opt.set('protocol', "vmess");
|
||||
var url0, param = "";
|
||||
if (ploc > 0) {
|
||||
url0 = sstr.substr(0, ploc);
|
||||
param = sstr.substr(ploc + 2);
|
||||
}
|
||||
var ssm = JSON.parse(sstr);
|
||||
opt.set('remarks', ssm.ps);
|
||||
opt.set('address', ssm.add);
|
||||
opt.set('port', ssm.port);
|
||||
opt.set('uuid', ssm.id);
|
||||
opt.set('tls', ssm.tls === "tls");
|
||||
if (ssm.tls === "tls") {
|
||||
var tls_serverName = ssm.host;
|
||||
if (ssm.sni) {
|
||||
tls_serverName = ssm.sni
|
||||
}
|
||||
opt.set('tls_serverName', tls_serverName);
|
||||
}
|
||||
ssm.net = ssm.net.toLowerCase();
|
||||
if (ssm.net === "kcp" || ssm.net === "mkcp")
|
||||
ssm.net = "mkcp"
|
||||
opt.set('transport', ssm.net);
|
||||
if (ssm.net === "tcp") {
|
||||
opt.set('tcp_guise', (ssm.host && ssm.path) ? "http" : "none");
|
||||
if (ssm.host && ssm.path) {
|
||||
opt.set('tcp_guise_http_host', ssm.host);
|
||||
opt.set('tcp_guise_http_path', ssm.path);
|
||||
}
|
||||
} else if (ssm.net === "ws") {
|
||||
opt.set('ws_host', ssm.host);
|
||||
opt.set('ws_path', ssm.path);
|
||||
} else if (ssm.net === "h2") {
|
||||
opt.set('h2_host', ssm.host);
|
||||
opt.set('h2_path', ssm.path);
|
||||
} else if (ssm.net === "quic") {
|
||||
opt.set('quic_security', ssm.securty);
|
||||
opt.set('quic_key', ssm.key);
|
||||
} else if (ssm.net === "kcp" || ssm.net === "mkcp") {
|
||||
opt.set('mkcp_guise', ssm.type);
|
||||
} else if (ssm.net === "grpc") {
|
||||
opt.set('grpc_serviceName', ssm.path);
|
||||
}
|
||||
} else if (ssu[0] === "vless") {
|
||||
<% if has_xray then %>
|
||||
opt.set('type', "Xray");
|
||||
<% elseif has_v2ray then %>
|
||||
opt.set('type', "V2ray");
|
||||
<% end %>
|
||||
opt.set('protocol', "vless");
|
||||
var m = parseNodeUrl(ssrurl);
|
||||
var password = m.passwd;
|
||||
if (password === "") {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
opt.set('uuid', password);
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
var queryParam = {};
|
||||
if (m.search.length > 1) {
|
||||
var query = m.search.split('?');
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
var params;
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
|
||||
opt.set('encryption', queryParam.encryption);
|
||||
if (queryParam.security) {
|
||||
if (queryParam.security == "tls" || queryParam.security == "xtls") {
|
||||
opt.set('tls', true);
|
||||
if (queryParam.security == "xtls") {
|
||||
opt.set('xtls', true);
|
||||
opt.set('flow', queryParam.flow || "xtls-rprx-direct");
|
||||
}
|
||||
opt.set('tls_serverName', queryParam.sni || '');
|
||||
|
||||
opt.set('tls_allowInsecure', true);
|
||||
if (queryParam.allowinsecure === '0') {
|
||||
opt.set('tls_allowInsecure', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queryParam.type = queryParam.type.toLowerCase();
|
||||
if (queryParam.type === "kcp" || queryParam.type === "mkcp")
|
||||
queryParam.type = "mkcp"
|
||||
opt.set('transport', queryParam.type);
|
||||
if (queryParam.type === "tcp") {
|
||||
opt.set('tcp_guise', queryParam.headerType || "none");
|
||||
if (queryParam.headerType && queryParam.headerType != "none") {
|
||||
opt.set('tcp_guise_http_host', queryParam.host || "");
|
||||
opt.set('tcp_guise_http_path', queryParam.path || "");
|
||||
}
|
||||
} else if (queryParam.type === "ws") {
|
||||
opt.set('ws_host', queryParam.host || "");
|
||||
opt.set('ws_path', queryParam.path || "");
|
||||
} else if (queryParam.type === "h2") {
|
||||
opt.set('h2_host', queryParam.host || "");
|
||||
opt.set('h2_path', queryParam.path || "");
|
||||
} else if (queryParam.type === "quic") {
|
||||
opt.set('quic_guise', queryParam.headerType || "none");
|
||||
opt.set('quic_security', queryParam.quicSecurity);
|
||||
opt.set('quic_key', queryParam.key);
|
||||
} else if (queryParam.type === "kcp" || queryParam.type === "mkcp") {
|
||||
opt.set('mkcp_guise', queryParam.headerType || "none");
|
||||
} else if (queryParam.type === "grpc") {
|
||||
opt.set('grpc_serviceName', (queryParam.serviceName || queryParam.path) || "");
|
||||
}
|
||||
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
} else if (ssu[0] === "brook") {
|
||||
var stype = "Brook";
|
||||
var m = parseNodeUrl(ssrurl);
|
||||
|
||||
var from_protocol = m.host;
|
||||
var protocol = from_protocol.split('server').join('client');
|
||||
|
||||
var queryParam = {};
|
||||
if (m.search.length > 1) {
|
||||
var query = m.search.split('?');
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
var params;
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
|
||||
var password = queryParam.password;
|
||||
if (password === "") {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
|
||||
opt.set('type', stype);
|
||||
opt.set('brook_protocol', protocol);
|
||||
opt.set('password', password);
|
||||
|
||||
if (protocol == 'wsclient' || protocol == 'wssclient') {
|
||||
opt.set('brook_protocol', 'wsclient');
|
||||
var wsserver = queryParam[from_protocol].split('://');
|
||||
wsserver = wsserver[1].split('/');
|
||||
var path = wsserver[1] && '/' + wsserver[1] || '/ws';
|
||||
var server = wsserver[0].split(':');
|
||||
opt.set('address', server[0]);
|
||||
opt.set('port', server[1]);
|
||||
opt.set('ws_path', path);
|
||||
if (protocol == 'wssclient') {
|
||||
opt.set('brook_tls', true);
|
||||
}
|
||||
} else {
|
||||
var server = queryParam[from_protocol].split(':');
|
||||
if (server.length < 2) {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||
return false;
|
||||
}
|
||||
opt.set('address', server[0]);
|
||||
opt.set('port', server[1]);
|
||||
}
|
||||
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
} else if (ssu[0] === "hysteria") {
|
||||
var stype = "Hysteria";
|
||||
var m = parseNodeUrl(ssrurl);
|
||||
var queryParam = {};
|
||||
if (m.search.length > 1) {
|
||||
var query = m.search.split('?');
|
||||
var queryParams = query[1];
|
||||
var queryArray = queryParams.split('&');
|
||||
var params;
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
params = queryArray[i].split('=');
|
||||
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
|
||||
}
|
||||
}
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
opt.set('type', stype);
|
||||
opt.set('hysteria_protocol', queryParam.protocol);
|
||||
opt.set('hysteria_obfs', queryParam.obfsParam);
|
||||
opt.set('hysteria_auth_type', "string");
|
||||
opt.set('hysteria_auth_password', queryParam.auth);
|
||||
opt.set('tls_serverName', queryParam.peer);
|
||||
if (queryParam.insecure && queryParam.insecure == "1") {
|
||||
opt.set('tls_allowInsecure', true);
|
||||
}
|
||||
opt.set('hysteria_alpn', queryParam.alpn);
|
||||
opt.set('hysteria_up_mbps', queryParam.upmbps);
|
||||
opt.set('hysteria_down_mbps', queryParam.downmbps);
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
} else {
|
||||
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>: " + ssu[0];
|
||||
return false;
|
||||
}
|
||||
s.innerHTML = "<font color='green'><%:Import Finished %></font>";
|
||||
return false;
|
||||
}
|
||||
|
||||
//]]></script>
|
||||
<input type="button" class="btn cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return fromUrl(this, '<%=self.option%>', '<%=self.value%>')" />
|
||||
<input type="button" class="btn cbi-button cbi-button-apply" value='<%:Build Share URL%>' onclick="return buildUrl(this, '<%=self.option%>', '<%=self.value%>')" />
|
||||
<span id="<%=self.option%>-status"></span>
|
||||
<%+cbi/valuefooter%>
|
475
luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm
Normal file
475
luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm
Normal file
@ -0,0 +1,475 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
|
||||
<style>
|
||||
table th, .table .th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table td, .table .td {
|
||||
text-align: center;
|
||||
/* white-space: nowrap; */
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
#set_node_div {
|
||||
display: none;
|
||||
width: 30rem;
|
||||
position: fixed;
|
||||
top:50%;
|
||||
padding-top: 30px;
|
||||
z-index: 99;
|
||||
text-align: center;
|
||||
background: white;
|
||||
box-shadow: darkgrey 10px 10px 30px 5px;
|
||||
}
|
||||
|
||||
._now_use {
|
||||
background: #9f9f9f38 !important;
|
||||
}
|
||||
|
||||
.ping a:hover{
|
||||
text-decoration : underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var node_list = {};
|
||||
var node_count = 0;
|
||||
|
||||
var ajax = {
|
||||
post: function(url, data, fn_success, timeout, fn_timeout) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var code = ajax.encode(data);
|
||||
xhr.open("POST", url, true);
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
if (timeout && timeout > 1000) {
|
||||
xhr.timeout = timeout;
|
||||
}
|
||||
if (fn_timeout) {
|
||||
xhr.ontimeout = function() {
|
||||
fn_timeout(xhr);
|
||||
}
|
||||
}
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
|
||||
var json = null;
|
||||
if (xhr.getResponseHeader("Content-Type") == "application/json") {
|
||||
try {
|
||||
json = eval('(' + xhr.responseText + ')');
|
||||
}
|
||||
catch(e) {
|
||||
json = null;
|
||||
}
|
||||
}
|
||||
fn_success(xhr, json);
|
||||
}
|
||||
};
|
||||
xhr.send(code);
|
||||
},
|
||||
encode: function(obj) {
|
||||
obj = obj ? obj : { };
|
||||
obj['_'] = Math.random();
|
||||
|
||||
if (typeof obj == 'object')
|
||||
{
|
||||
var code = '';
|
||||
var self = this;
|
||||
|
||||
for (var k in obj)
|
||||
code += (code ? '&' : '') +
|
||||
k + '=' + encodeURIComponent(obj[k]);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
function copy_node(cbi_id) {
|
||||
window.location.href = '<%=api.url("copy_node")%>' + "?section=" + cbi_id;
|
||||
}
|
||||
|
||||
var section = "";
|
||||
function open_set_node_div(cbi_id) {
|
||||
section = cbi_id;
|
||||
document.getElementById("set_node_div").style.display="block";
|
||||
var node_name = document.getElementById("cbid.passwall." + cbi_id + ".remarks").value;
|
||||
document.getElementById("set_node_name").innerHTML = node_name;
|
||||
}
|
||||
|
||||
function close_set_node_div() {
|
||||
document.getElementById("set_node_div").style.display="none";
|
||||
document.getElementById("set_node_name").innerHTML = "";
|
||||
}
|
||||
|
||||
function _cbi_row_top(id) {
|
||||
var dom = document.getElementById("cbi-passwall-" + id);
|
||||
if (dom) {
|
||||
var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row");
|
||||
if (trs && trs.length > 0) {
|
||||
for (var i = 0; i < trs.length; i++) {
|
||||
var up = dom.getElementsByClassName("cbi-button-up");
|
||||
if (up) {
|
||||
cbi_row_swap(up[0], true, 'cbi.sts.passwall.nodes');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checked_all_node(btn) {
|
||||
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (var i = 0 ; i < doms.length; i++) {
|
||||
doms[i].checked = true;
|
||||
}
|
||||
btn.value = "<%:DeSelect all%>";
|
||||
btn.setAttribute("onclick", "dechecked_all_node(this)");
|
||||
}
|
||||
}
|
||||
|
||||
function dechecked_all_node(btn) {
|
||||
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (var i = 0 ; i < doms.length; i++) {
|
||||
doms[i].checked = false;
|
||||
}
|
||||
btn.value = "<%:Select all%>";
|
||||
btn.setAttribute("onclick", "checked_all_node(this)");
|
||||
}
|
||||
}
|
||||
|
||||
function delete_select_nodes() {
|
||||
var ids = [];
|
||||
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (var i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
if (confirm('<%:Are you sure to delete select nodes?%>') == true){
|
||||
XHR.get('<%=api.url("delete_select_nodes")%>', {
|
||||
ids: ids.join()
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200) {
|
||||
/*
|
||||
for (var i = 0 ; i < ids.length; i++) {
|
||||
var box = document.getElementById("cbi-passwall-" + ids[i]);
|
||||
box.remove();
|
||||
}
|
||||
*/
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ids.length <= 0) {
|
||||
alert("<%:You no select nodes !%>");
|
||||
}
|
||||
}
|
||||
|
||||
function set_node(protocol) {
|
||||
if (confirm('<%:Are you sure set to%> ' + protocol.toUpperCase() + '<%:the server?%>')==true){
|
||||
window.location.href = '<%=api.url("set_node")%>?protocol=' + protocol + '§ion=' + section;
|
||||
}
|
||||
}
|
||||
|
||||
function get_address_full(id) {
|
||||
try {
|
||||
var address = document.getElementById("cbid.passwall." + id + ".address").value;
|
||||
var port = document.getElementById("cbid.passwall." + id + ".port").value;
|
||||
}
|
||||
catch(err){}
|
||||
//判断是否含有汉字
|
||||
var reg = new RegExp("[\\u4E00-\\u9FFF]+","g");
|
||||
if ((address != null && address != "") && (port != null && port != "") && reg.test(address) == false) {
|
||||
return { address: address, port: port };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//获取当前使用的节点
|
||||
function get_now_use_node() {
|
||||
XHR.get('<%=api.url("get_now_use_node")%>', null,
|
||||
function(x, result) {
|
||||
var id = result["TCP"];
|
||||
if (id) {
|
||||
var dom = document.getElementById("cbi-passwall-" + id);
|
||||
if (dom) {
|
||||
dom.classList.add("_now_use");
|
||||
dom.title = "当前TCP节点";
|
||||
//var v = "<a style='color: red'>当前TCP节点:</a>" + document.getElementById("cbid.passwall." + id + ".remarks").value;
|
||||
//document.getElementById("cbi-passwall-" + id + "-remarks").innerHTML = v;
|
||||
}
|
||||
}
|
||||
id = result["UDP"];
|
||||
if (id) {
|
||||
var dom = document.getElementById("cbi-passwall-" + id);
|
||||
if (dom) {
|
||||
dom.classList.add("_now_use");
|
||||
dom.title = "当前UDP节点";
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function urltest_node(cbi_id, dom) {
|
||||
if (cbi_id != null) {
|
||||
dom.disabled = true;
|
||||
dom.value = "<%:Check...%>";
|
||||
XHR.get('<%=api.url("urltest_node")%>', {
|
||||
id: cbi_id
|
||||
},
|
||||
function(x, result) {
|
||||
if(x && x.status == 200) {
|
||||
if (result.use_time == null || result.use_time.trim() == "") {
|
||||
dom.outerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
} else {
|
||||
var color = "red";
|
||||
var use_time = result.use_time;
|
||||
if (use_time < 1000) {
|
||||
color = "green";
|
||||
} else if (use_time < 2000) {
|
||||
color = "#fb9a05";
|
||||
} else {
|
||||
color = "red";
|
||||
}
|
||||
dom.outerHTML = "<font style='color:" + color + "'>" + result.use_time + " ms" + "</font>";
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function ping_node(cbi_id, dom) {
|
||||
var full = get_address_full(cbi_id);
|
||||
if (full != null) {
|
||||
XHR.get('<%=api.url("ping_node")%>', {
|
||||
address: full.address,
|
||||
port: full.port
|
||||
},
|
||||
function(x, result) {
|
||||
if(x && x.status == 200) {
|
||||
if (result.ping == null || result.ping.trim() == "") {
|
||||
dom.outerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
} else {
|
||||
var ping = parseInt(result.ping);
|
||||
if (ping < 100)
|
||||
dom.outerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping < 200)
|
||||
dom.outerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping >= 200)
|
||||
dom.outerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* 自动Ping */
|
||||
var nodes = [];
|
||||
const ping_value = document.getElementsByClassName('ping_value');
|
||||
for (var i = 0; i < ping_value.length; i++) {
|
||||
var cbi_id = ping_value[i].getAttribute("cbiid");
|
||||
var full = get_address_full(cbi_id);
|
||||
if (full != null) {
|
||||
var flag = false;
|
||||
//当有多个相同地址和端口时合在一起
|
||||
for (var j = 0; j < nodes.length; j++) {
|
||||
if (nodes[j].address == full.address && nodes[j].port == full.port) {
|
||||
nodes[j].indexs = nodes[j].indexs + "," + i;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag)
|
||||
continue;
|
||||
nodes.push({
|
||||
indexs: i + "",
|
||||
address: full.address,
|
||||
port: full.port
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get_now_use_node();
|
||||
|
||||
const _xhr = (index) => {
|
||||
return new Promise((res) => {
|
||||
const dom = nodes[index];
|
||||
if (!dom) res()
|
||||
ajax.post('<%=api.url("ping_node")%>', {
|
||||
index: dom.indexs,
|
||||
address: dom.address,
|
||||
port: dom.port
|
||||
},
|
||||
function(x, result) {
|
||||
if (x && x.status == 200) {
|
||||
var strs = dom.indexs.split(",");
|
||||
for (var i = 0; i < strs.length; i++) {
|
||||
if (result.ping == null || result.ping.trim() == "") {
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
} else {
|
||||
var ping = parseInt(result.ping);
|
||||
if (ping < 100)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping < 200)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping >= 200)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
|
||||
}
|
||||
}
|
||||
}
|
||||
res();
|
||||
},
|
||||
5000,
|
||||
function(x) {
|
||||
var strs = dom.indexs.split(",");
|
||||
for (var i = 0; i < strs.length; i++) {
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
}
|
||||
res();
|
||||
}
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
let task = -1;
|
||||
const thread = () => {
|
||||
task = task + 1
|
||||
if (nodes[task]) {
|
||||
_xhr(task).then(thread);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < 20; i++) {
|
||||
thread()
|
||||
}
|
||||
|
||||
/* 递归单请求方法
|
||||
var index = 0;
|
||||
function auto_ping() {
|
||||
if (index >= nodes.length) {
|
||||
return;
|
||||
}
|
||||
var indexs = nodes[index].indexs;
|
||||
var address = nodes[index].address;
|
||||
var port = nodes[index].port;
|
||||
ajax.post('<%=api.url("ping_node")%>', {
|
||||
index: indexs,
|
||||
address: address,
|
||||
port: port
|
||||
},
|
||||
function(x, result) {
|
||||
if (x && x.status == 200) {
|
||||
var strs = indexs.split(",");
|
||||
for (var i = 0; i < strs.length; i++) {
|
||||
if (result.ping == null || result.ping.trim() == "") {
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
} else {
|
||||
var ping = parseInt(result.ping);
|
||||
if (ping < 100)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping < 200)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
|
||||
else if (ping >= 200)
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
return auto_ping();
|
||||
},
|
||||
function(x) {
|
||||
var strs = indexs.split(",");
|
||||
for (var i = 0; i < strs.length; i++) {
|
||||
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
|
||||
}
|
||||
index++;
|
||||
return auto_ping();
|
||||
},
|
||||
);
|
||||
}
|
||||
auto_ping();
|
||||
*/
|
||||
|
||||
var edit_btn = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-button cbi-button-edit");
|
||||
for (var i = 0; i < edit_btn.length; i++) {
|
||||
try {
|
||||
var onclick_str = edit_btn[i].getAttribute("onclick");
|
||||
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
|
||||
var td = edit_btn[i].parentNode;
|
||||
var new_div = "";
|
||||
//添加"勾选"框
|
||||
new_div += '<input class="cbi-input-checkbox nodes_select" type="checkbox" cbid="' + id + '" /> ';
|
||||
//添加"置顶"按钮
|
||||
new_div += '<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top(\'' + id + '\')"/> ';
|
||||
//添加"应用"按钮
|
||||
new_div += '<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_' + id + '" onclick="open_set_node_div(\'' + id + '\')"/> ';
|
||||
//添加"复制"按钮
|
||||
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node(\'' + id + '\')"/> ';
|
||||
td.innerHTML = new_div + td.innerHTML;
|
||||
|
||||
var obj = {};
|
||||
obj.id = id;
|
||||
obj.type = document.getElementById("cbid.passwall." + id + ".type").value;
|
||||
var address_dom = document.getElementById("cbid.passwall." + id + ".address");
|
||||
var port_dom = document.getElementById("cbid.passwall." + id + ".port");
|
||||
if (address_dom && port_dom) {
|
||||
obj.address = address_dom.value;
|
||||
obj.port = port_dom.value;
|
||||
}
|
||||
|
||||
node_count++;
|
||||
var add_from = document.getElementById("cbid.passwall." + id + ".add_from").value;
|
||||
if (node_list[add_from])
|
||||
node_list[add_from].push(obj);
|
||||
else
|
||||
node_list[add_from] = [];
|
||||
|
||||
}
|
||||
catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (true) {
|
||||
var str = "";
|
||||
for (var add_from in node_list) {
|
||||
var num = node_list[add_from].length + 1;
|
||||
if (add_from == "") {
|
||||
add_from = "<%:Self add%>";
|
||||
}
|
||||
str += add_from + " " + "<%:Node num%>: <a style='color: red'>" + num + "</a>   ";
|
||||
}
|
||||
document.getElementById("div_node_count").innerHTML = "<div style='margin-top:5px'>" + str + "</div>";
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div style="display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center;">
|
||||
<div id="set_node_div">
|
||||
<div class="cbi-value"><%:You choose node is:%><a style="color: red" id="set_node_name"></a></div>
|
||||
<div class="cbi-value">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="set_node('tcp')" value="TCP" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="set_node('udp')" value="UDP" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_set_node_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
76
luci-app-passwall/luasrc/view/passwall/rule/rule_version.htm
Normal file
76
luci-app-passwall/luasrc/view/passwall/rule/rule_version.htm
Normal file
@ -0,0 +1,76 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local gfwlist_update = api.uci_get_type("global_rules", "gfwlist_update", "1") == "1" and "checked='checked'" or ""
|
||||
local chnroute_update = api.uci_get_type("global_rules", "chnroute_update", "1") == "1" and "checked='checked'" or ""
|
||||
local chnroute6_update = api.uci_get_type("global_rules", "chnroute6_update", "1") == "1" and "checked='checked'" or ""
|
||||
local chnlist_update = api.uci_get_type("global_rules", "chnlist_update", "1") == "1" and "checked='checked'" or ""
|
||||
local geoip_update = api.uci_get_type("global_rules", "geoip_update", "1") == "1" and "checked='checked'" or ""
|
||||
local geosite_update = api.uci_get_type("global_rules", "geosite_update", "1") == "1" and "checked='checked'" or ""
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function update_rules(btn) {
|
||||
btn.disabled = true;
|
||||
btn.value = '<%:Updating...%>';
|
||||
var div = document.getElementById('_rule_div');
|
||||
var domList = div.getElementsByTagName('input');
|
||||
var checkBoxList = [];
|
||||
var len = domList.length;
|
||||
while(len--) {
|
||||
var dom = domList[len];
|
||||
if(dom.type == 'checkbox' && dom.checked) {
|
||||
checkBoxList.push(dom.name);
|
||||
}
|
||||
}
|
||||
XHR.get('<%=api.url("update_rules")%>', {
|
||||
update: checkBoxList
|
||||
},
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("log")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
btn.disabled = false;
|
||||
btn.value = '<%:Manually update%>';
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
<div class="cbi-value" id="_rule_div">
|
||||
<label class="cbi-value-title">
|
||||
<%:Manually update%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="gfwlist" value="1" <%=gfwlist_update%> />
|
||||
gfwlist
|
||||
</label>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="chnroute" value="1" <%=chnroute_update%> />
|
||||
chnroute
|
||||
</label>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="chnroute6" value="1" <%=chnroute6_update%> />
|
||||
chnroute6
|
||||
</label>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="chnlist" value="1" <%=chnlist_update%> />
|
||||
chnlist
|
||||
</label>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="geoip" value="1" <%=geoip_update%> />
|
||||
geoip
|
||||
</label>
|
||||
<label>
|
||||
<input class="cbi-input-checkbox" type="checkbox" name="geosite" value="1" <%=geosite_update%> />
|
||||
geosite
|
||||
</label>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="update_rules_btn" onclick="update_rules(this)" value="<%:Manually update%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
34
luci-app-passwall/luasrc/view/passwall/server/log.htm
Normal file
34
luci-app-passwall/luasrc/view/passwall/server/log.htm
Normal file
@ -0,0 +1,34 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function clear_log(btn) {
|
||||
XHR.get('<%=api.url("server_clear_log")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
var log_textarea = document.getElementById('log_textarea');
|
||||
log_textarea.innerHTML = "";
|
||||
log_textarea.scrollTop = log_textarea.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
XHR.poll(3, '<%=api.url("server_get_log")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
var log_textarea = document.getElementById('log_textarea');
|
||||
log_textarea.innerHTML = x.responseText;
|
||||
log_textarea.scrollTop = log_textarea.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
//]]>
|
||||
</script>
|
||||
<fieldset class="cbi-section" id="_log_fieldset">
|
||||
<legend>
|
||||
<%:Logs%>
|
||||
</legend>
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_log()" value="<%:Clear logs%>" />
|
||||
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="20" wrap="off" readonly="readonly"></textarea>
|
||||
</fieldset>
|
@ -0,0 +1,38 @@
|
||||
<%
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var _users_status = document.getElementsByClassName('_users_status');
|
||||
for(var i = 0; i < _users_status.length; i++) {
|
||||
var id = _users_status[i].parentElement.parentElement.parentElement.id;
|
||||
id = id.substr(id.lastIndexOf("-") + 1);
|
||||
XHR.get('<%=api.url("server_user_status")%>', {
|
||||
index: i,
|
||||
id: id
|
||||
},
|
||||
function(x, result) {
|
||||
_users_status[result.index].setAttribute("style","font-weight:bold;");
|
||||
_users_status[result.index].setAttribute("color",result.status ? "green":"red");
|
||||
_users_status[result.index].innerHTML = (result.status ? '✓' : 'X');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var edit_btn = document.getElementById("cbi-passwall_server-user").getElementsByClassName("cbi-button cbi-button-edit");
|
||||
for (var i = 0; i < edit_btn.length; i++) {
|
||||
try {
|
||||
var onclick_str = edit_btn[i].getAttribute("onclick");
|
||||
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
|
||||
var td = edit_btn[i].parentNode;
|
||||
var new_div = "";
|
||||
//添加"日志"按钮
|
||||
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Log%>" onclick="window.open(\'' + '<%=api.url("server_user_log")%>' + '?id=' + id + '\', \'_blank\')"/> ';
|
||||
td.innerHTML = new_div + td.innerHTML;
|
||||
}
|
||||
catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
1377
luci-app-passwall/po/zh-cn/passwall.po
Normal file
1377
luci-app-passwall/po/zh-cn/passwall.po
Normal file
File diff suppressed because it is too large
Load Diff
1
luci-app-passwall/po/zh_Hans
Symbolic link
1
luci-app-passwall/po/zh_Hans
Symbolic link
@ -0,0 +1 @@
|
||||
zh-cn
|
4
luci-app-passwall/root/etc/config/passwall_server
Normal file
4
luci-app-passwall/root/etc/config/passwall_server
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
config global 'global'
|
||||
option enable '0'
|
||||
|
80
luci-app-passwall/root/etc/init.d/passwall
Executable file
80
luci-app-passwall/root/etc/init.d/passwall
Executable file
@ -0,0 +1,80 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=15
|
||||
|
||||
CONFIG=passwall
|
||||
APP_FILE=/usr/share/${CONFIG}/app.sh
|
||||
LOCK_FILE_DIR=/var/lock
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}.lock
|
||||
|
||||
set_lock() {
|
||||
[ ! -d "$LOCK_FILE_DIR" ] && mkdir -p $LOCK_FILE_DIR
|
||||
exec 999>"$LOCK_FILE"
|
||||
flock -xn 999
|
||||
}
|
||||
|
||||
unset_lock() {
|
||||
flock -u 999
|
||||
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
|
||||
}
|
||||
|
||||
boot() {
|
||||
$APP_FILE boot
|
||||
}
|
||||
|
||||
start() {
|
||||
set_lock
|
||||
[ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0
|
||||
$APP_FILE start
|
||||
unset_lock
|
||||
}
|
||||
|
||||
stop() {
|
||||
unlock
|
||||
set_lock
|
||||
[ $? == 1 ] && $APP_FILE echolog "停止脚本等待超时,不重复运行,退出." && exit 0
|
||||
$APP_FILE stop
|
||||
unset_lock
|
||||
}
|
||||
|
||||
restart() {
|
||||
set_lock
|
||||
[ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0
|
||||
$APP_FILE stop
|
||||
$APP_FILE start
|
||||
unset_lock
|
||||
}
|
||||
|
||||
disable() {
|
||||
rm -f "$IPKG_INSTROOT"/etc/rc.d/S??zzz_${CONFIG}
|
||||
rm -f "$IPKG_INSTROOT"/etc/rc.d/K??zzz_${CONFIG}
|
||||
}
|
||||
|
||||
enable() {
|
||||
err=1
|
||||
[ "$START" ] && \
|
||||
ln -sf "../init.d/${CONFIG}" "$IPKG_INSTROOT/etc/rc.d/S${START}zzz_${CONFIG}" && \
|
||||
err=0
|
||||
[ "$STOP" ] && \
|
||||
ln -sf "../init.d/${CONFIG}" "$IPKG_INSTROOT/etc/rc.d/K${STOP}zzz_${CONFIG}" && \
|
||||
err=0
|
||||
return $err
|
||||
}
|
||||
|
||||
enabled() {
|
||||
[ -x "$IPKG_INSTROOT/etc/rc.d/S${START}zzz_${CONFIG}" ]
|
||||
}
|
16
luci-app-passwall/root/etc/init.d/passwall_server
Executable file
16
luci-app-passwall/root/etc/init.d/passwall_server
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
|
||||
start() {
|
||||
lua /usr/lib/lua/luci/model/cbi/passwall/server/api/app.lua start
|
||||
}
|
||||
|
||||
stop() {
|
||||
lua /usr/lib/lua/luci/model/cbi/passwall/server/api/app.lua stop
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
35
luci-app-passwall/root/etc/uci-defaults/luci-passwall
Executable file
35
luci-app-passwall/root/etc/uci-defaults/luci-passwall
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set dhcp.@dnsmasq[0].localuse=1
|
||||
commit dhcp
|
||||
delete ucitrack.@passwall[-1]
|
||||
add ucitrack passwall
|
||||
set ucitrack.@passwall[-1].init=passwall
|
||||
commit ucitrack
|
||||
delete firewall.passwall
|
||||
set firewall.passwall=include
|
||||
set firewall.passwall.type=script
|
||||
set firewall.passwall.path=/var/etc/passwall.include
|
||||
set firewall.passwall.reload=1
|
||||
commit firewall
|
||||
delete ucitrack.@passwall_server[-1]
|
||||
add ucitrack passwall_server
|
||||
set ucitrack.@passwall_server[-1].init=passwall_server
|
||||
commit ucitrack
|
||||
delete firewall.passwall_server
|
||||
set firewall.passwall_server=include
|
||||
set firewall.passwall_server.type=script
|
||||
set firewall.passwall_server.path=/var/etc/passwall_server.include
|
||||
set firewall.passwall_server.reload=1
|
||||
commit firewall
|
||||
set uhttpd.main.max_requests=50
|
||||
commit uhttpd
|
||||
EOF
|
||||
|
||||
[ ! -s "/etc/config/passwall" ] && cp -f /usr/share/passwall/0_default_config /etc/config/passwall
|
||||
touch /etc/config/passwall_show >/dev/null 2>&1
|
||||
rm -f /tmp/luci-indexcache
|
||||
rm -rf /tmp/luci-modulecache/
|
||||
killall -HUP rpcd 2>/dev/null
|
||||
exit 0
|
156
luci-app-passwall/root/usr/share/passwall/0_default_config
Normal file
156
luci-app-passwall/root/usr/share/passwall/0_default_config
Normal file
@ -0,0 +1,156 @@
|
||||
|
||||
config global
|
||||
option enabled '0'
|
||||
option socks_enabled '0'
|
||||
option tcp_node 'nil'
|
||||
option udp_node 'nil'
|
||||
option dns_mode 'pdnsd'
|
||||
option dns_forward '1.1.1.1'
|
||||
option filter_proxy_ipv6 '0'
|
||||
option tcp_proxy_mode 'chnroute'
|
||||
option udp_proxy_mode 'chnroute'
|
||||
option localhost_tcp_proxy_mode 'default'
|
||||
option localhost_udp_proxy_mode 'default'
|
||||
option close_log_tcp '0'
|
||||
option close_log_udp '0'
|
||||
option loglevel 'error'
|
||||
option trojan_loglevel '4'
|
||||
|
||||
config global_haproxy
|
||||
option balancing_enable '0'
|
||||
|
||||
config global_delay
|
||||
option auto_on '0'
|
||||
option start_daemon '1'
|
||||
option start_delay '1'
|
||||
|
||||
config global_forwarding
|
||||
option process '0'
|
||||
option tcp_no_redir_ports 'disable'
|
||||
option udp_no_redir_ports '53'
|
||||
option tcp_proxy_drop_ports 'disable'
|
||||
option udp_proxy_drop_ports '80,443'
|
||||
option tcp_redir_ports '22,25,53,143,465,587,853,993,995,80,443'
|
||||
option udp_redir_ports '1:65535'
|
||||
option accept_icmp '0'
|
||||
option tcp_proxy_way 'redirect'
|
||||
option ipv6_tproxy '0'
|
||||
option sniffing '1'
|
||||
option route_only '0'
|
||||
|
||||
config global_other
|
||||
option nodes_ping 'auto_ping tcping'
|
||||
|
||||
config global_rules
|
||||
option auto_update '0'
|
||||
option chnlist_update '1'
|
||||
option chnroute_update '1'
|
||||
option chnroute6_update '1'
|
||||
option gfwlist_update '1'
|
||||
option geosite_update '0'
|
||||
option geoip_update '0'
|
||||
list gfwlist_url 'https://raw.cooluc.com/YW5vbnltb3Vz/domain-list-community/release/gfwlist.txt'
|
||||
list chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
list chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt'
|
||||
list chnlist_url 'https://raw.cooluc.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf'
|
||||
list chnlist_url 'https://raw.cooluc.com/felixonmars/dnsmasq-china-list/master/apple.china.conf'
|
||||
list chnlist_url 'https://raw.cooluc.com/felixonmars/dnsmasq-china-list/master/google.china.conf'
|
||||
option v2ray_location_asset '/usr/share/v2ray/'
|
||||
|
||||
config global_app
|
||||
option v2ray_file '/usr/bin/v2ray'
|
||||
option xray_file '/usr/bin/xray'
|
||||
option trojan_go_file '/usr/bin/trojan-go'
|
||||
option brook_file '/usr/bin/brook'
|
||||
option hysteria_file '/usr/bin/hysteria'
|
||||
|
||||
config global_subscribe
|
||||
option subscribe_proxy '0'
|
||||
option filter_keyword_mode '1'
|
||||
list filter_discard_list '过期时间'
|
||||
list filter_discard_list '剩余流量'
|
||||
list filter_discard_list 'QQ群'
|
||||
list filter_discard_list '官网'
|
||||
|
||||
config auto_switch
|
||||
option enable '0'
|
||||
option testing_time '1'
|
||||
option connect_timeout '3'
|
||||
option retry_num '3'
|
||||
option shunt_logic '1'
|
||||
|
||||
config nodes 'myshunt'
|
||||
option remarks '分流总节点'
|
||||
option type 'Xray'
|
||||
option protocol '_shunt'
|
||||
option AD 'nil'
|
||||
option BT '_direct'
|
||||
option Telegram 'nil'
|
||||
option Netflix 'nil'
|
||||
option TVB 'nil'
|
||||
option Cloudflare 'nil'
|
||||
option Github 'nil'
|
||||
option Apple 'nil'
|
||||
option China 'nil'
|
||||
option default_node 'nil'
|
||||
option domainStrategy 'IPOnDemand'
|
||||
|
||||
config shunt_rules 'AD'
|
||||
option remarks 'AD'
|
||||
option domain_list 'geosite:category-ads-all'
|
||||
|
||||
config shunt_rules 'BT'
|
||||
option remarks 'BT'
|
||||
option protocol 'bittorrent'
|
||||
|
||||
config shunt_rules 'Telegram'
|
||||
option remarks 'Telegram'
|
||||
option ip_list 'geoip:telegram'
|
||||
|
||||
config shunt_rules 'Netflix'
|
||||
option remarks 'Netflix'
|
||||
option domain_list 'fast.com
|
||||
netflix
|
||||
netflix.com
|
||||
netflix.net
|
||||
nflxso.net
|
||||
nflxext.com
|
||||
nflximg.com
|
||||
nflximg.net
|
||||
nflxvideo.net
|
||||
netflixdnstest0.com
|
||||
netflixdnstest1.com
|
||||
netflixdnstest2.com
|
||||
netflixdnstest3.com
|
||||
netflixdnstest4.com
|
||||
netflixdnstest5.com
|
||||
netflixdnstest6.com
|
||||
netflixdnstest7.com
|
||||
netflixdnstest8.com
|
||||
netflixdnstest9.com'
|
||||
option ip_list 'geoip:netflix'
|
||||
|
||||
config shunt_rules 'TVB'
|
||||
option remarks 'TVB'
|
||||
option domain_list 'tvb.com
|
||||
mytvsuper.com'
|
||||
|
||||
config shunt_rules 'Github'
|
||||
option remarks 'Github'
|
||||
option domain_list 'github.com
|
||||
github.io
|
||||
githubusercontent.com'
|
||||
|
||||
config shunt_rules 'Cloudflare'
|
||||
option remarks 'Cloudflare'
|
||||
option ip_list 'geoip:cloudflare'
|
||||
|
||||
config shunt_rules 'Apple'
|
||||
option remarks 'Apple'
|
||||
option domain_list 'geosite:apple-cn'
|
||||
option ip_list '17.0.0.0/8'
|
||||
|
||||
config shunt_rules 'China'
|
||||
option remarks 'China'
|
||||
option domain_list 'geosite:cn'
|
||||
option ip_list 'geoip:cn'
|
1529
luci-app-passwall/root/usr/share/passwall/app.sh
Executable file
1529
luci-app-passwall/root/usr/share/passwall/app.sh
Executable file
File diff suppressed because it is too large
Load Diff
98
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.sh
Executable file
98
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.sh
Executable file
@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
|
||||
stretch() {
|
||||
#zhenduiluanshezhiDNSderen
|
||||
local dnsmasq_server=$(uci -q get dhcp.@dnsmasq[0].server)
|
||||
local dnsmasq_noresolv=$(uci -q get dhcp.@dnsmasq[0].noresolv)
|
||||
local _flag
|
||||
for server in $dnsmasq_server; do
|
||||
[ -z "$(echo $server | grep '\/')" ] && _flag=1
|
||||
done
|
||||
[ -z "$_flag" ] && [ "$dnsmasq_noresolv" = "1" ] && {
|
||||
uci -q delete dhcp.@dnsmasq[0].noresolv
|
||||
uci -q set dhcp.@dnsmasq[0].resolvfile="$RESOLVFILE"
|
||||
uci commit dhcp
|
||||
}
|
||||
}
|
||||
|
||||
backup_servers() {
|
||||
DNSMASQ_DNS=$(uci show dhcp | grep "@dnsmasq" | grep ".server=" | awk -F '=' '{print $2}' | sed "s/'//g" | tr ' ' ',')
|
||||
if [ -n "${DNSMASQ_DNS}" ]; then
|
||||
uci -q set $CONFIG.@global[0].dnsmasq_servers="${DNSMASQ_DNS}"
|
||||
uci commit $CONFIG
|
||||
fi
|
||||
}
|
||||
|
||||
restore_servers() {
|
||||
OLD_SERVER=$(uci -q get $CONFIG.@global[0].dnsmasq_servers | tr "," " ")
|
||||
for server in $OLD_SERVER; do
|
||||
uci -q del_list dhcp.@dnsmasq[0].server=$server
|
||||
uci -q add_list dhcp.@dnsmasq[0].server=$server
|
||||
done
|
||||
uci commit dhcp
|
||||
uci -q delete $CONFIG.@global[0].dnsmasq_servers
|
||||
uci commit $CONFIG
|
||||
}
|
||||
|
||||
logic_restart() {
|
||||
local no_log
|
||||
eval_set_val $@
|
||||
_LOG_FILE=$LOG_FILE
|
||||
[ -n "$no_log" ] && LOG_FILE="/dev/null"
|
||||
if [ -f "$TMP_PATH/default_DNS" ]; then
|
||||
backup_servers
|
||||
#sed -i "/list server/d" /etc/config/dhcp >/dev/null 2>&1
|
||||
for server in $(uci -q get dhcp.@dnsmasq[0].server); do
|
||||
[ -n "$(echo $server | grep '\/')" ] || uci -q del_list dhcp.@dnsmasq[0].server="$server"
|
||||
done
|
||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1 &
|
||||
restore_servers
|
||||
else
|
||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1 &
|
||||
fi
|
||||
echolog "重启 dnsmasq 服务"
|
||||
LOG_FILE=${_LOG_FILE}
|
||||
}
|
||||
|
||||
restart() {
|
||||
local no_log
|
||||
eval_set_val $@
|
||||
_LOG_FILE=$LOG_FILE
|
||||
[ -n "$no_log" ] && LOG_FILE="/dev/null"
|
||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1 &
|
||||
echolog "重启 dnsmasq 服务"
|
||||
LOG_FILE=${_LOG_FILE}
|
||||
}
|
||||
|
||||
add() {
|
||||
local FLAG TMP_DNSMASQ_PATH DNSMASQ_CONF_FILE DEFAULT_DNS LOCAL_DNS TUN_DNS REMOTE_FAKEDNS CHINADNS_DNS TCP_NODE PROXY_MODE NO_PROXY_IPV6 NO_LOGIC_LOG
|
||||
eval_set_val $@
|
||||
lua $APP_PATH/helper_dnsmasq_add.lua -FLAG $FLAG -TMP_DNSMASQ_PATH $TMP_DNSMASQ_PATH -DNSMASQ_CONF_FILE $DNSMASQ_CONF_FILE -DEFAULT_DNS $DEFAULT_DNS -LOCAL_DNS $LOCAL_DNS -TUN_DNS $TUN_DNS -REMOTE_FAKEDNS ${REMOTE_FAKEDNS:-0} -CHINADNS_DNS ${CHINADNS_DNS:-0} -TCP_NODE $TCP_NODE -PROXY_MODE $PROXY_MODE -NO_PROXY_IPV6 ${NO_PROXY_IPV6:-0} -NO_LOGIC_LOG ${NO_LOGIC_LOG:-0}
|
||||
}
|
||||
|
||||
del() {
|
||||
rm -rf /tmp/dnsmasq.d/dnsmasq-$CONFIG.conf
|
||||
rm -rf $DNSMASQ_PATH/dnsmasq-$CONFIG.conf
|
||||
rm -rf $TMP_DNSMASQ_PATH
|
||||
}
|
||||
|
||||
arg1=$1
|
||||
shift
|
||||
case $arg1 in
|
||||
stretch)
|
||||
stretch $@
|
||||
;;
|
||||
add)
|
||||
add $@
|
||||
;;
|
||||
del)
|
||||
del $@
|
||||
;;
|
||||
restart)
|
||||
restart $@
|
||||
;;
|
||||
logic_restart)
|
||||
logic_restart $@
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
430
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua
Normal file
430
luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua
Normal file
@ -0,0 +1,430 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local FLAG = var["-FLAG"]
|
||||
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
|
||||
local DNSMASQ_CONF_FILE = var["-DNSMASQ_CONF_FILE"]
|
||||
local DEFAULT_DNS = var["-DEFAULT_DNS"]
|
||||
local LOCAL_DNS = var["-LOCAL_DNS"]
|
||||
local TUN_DNS = var["-TUN_DNS"]
|
||||
local REMOTE_FAKEDNS = var["-REMOTE_FAKEDNS"]
|
||||
local CHINADNS_DNS = var["-CHINADNS_DNS"]
|
||||
local TCP_NODE = var["-TCP_NODE"]
|
||||
local PROXY_MODE = var["-PROXY_MODE"]
|
||||
local NO_PROXY_IPV6 = var["-NO_PROXY_IPV6"]
|
||||
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
|
||||
local LOG_FILE = "/tmp/log/passwall.log"
|
||||
local CACHE_PATH = "/tmp/etc/passwall_tmp"
|
||||
local CACHE_FLAG = "dns_" .. FLAG
|
||||
local CACHE_DNS_PATH = CACHE_PATH .. "/" .. CACHE_FLAG
|
||||
local CACHE_MD5_FILE = CACHE_DNS_PATH .. ".md5"
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local jsonc = api.jsonc
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local datatypes = api.datatypes
|
||||
|
||||
local list1 = {}
|
||||
local excluded_domain = {}
|
||||
local excluded_domain_str = "!"
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
end
|
||||
local f, err = io.open(LOG_FILE, "a")
|
||||
if f and err == nil then
|
||||
local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
f:write(str .. "\n")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
--从url获取域名
|
||||
local function get_domain_from_url(url)
|
||||
if url then
|
||||
if datatypes.hostname(url) then
|
||||
return url
|
||||
end
|
||||
local domain = url:match("//([^/]+)")
|
||||
if domain then
|
||||
return domain
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function check_dns(domain, dns)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
if not dns then
|
||||
return
|
||||
end
|
||||
for k,v in ipairs(list1[domain].dns) do
|
||||
if dns == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function check_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
for k,v in ipairs(list1[domain].ipsets) do
|
||||
if ipset == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function set_domain_address(domain, address)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
if not list1[domain].address then
|
||||
list1[domain].address = address
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_dns(domain, dns)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not dns then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
for line in string.gmatch(dns, '[^' .. "," .. ']+') do
|
||||
if not check_dns(domain, line) then
|
||||
table.insert(list1[domain].dns, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
dns = {},
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
for line in string.gmatch(ipset, '[^' .. "," .. ']+') do
|
||||
if not check_ipset(domain, line) then
|
||||
table.insert(list1[domain].ipsets, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
table.insert(excluded_domain, domain)
|
||||
excluded_domain_str = excluded_domain_str .. "|" .. domain
|
||||
end
|
||||
|
||||
local function check_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
for k,v in ipairs(excluded_domain) do
|
||||
if domain:find(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local dnsmasq_default_dns
|
||||
|
||||
local cache_md5 = ""
|
||||
local str = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. REMOTE_FAKEDNS .. CHINADNS_DNS .. PROXY_MODE .. NO_PROXY_IPV6
|
||||
local md5 = luci.sys.exec("echo -n $(echo '" .. str .. "' | md5sum | awk '{print $1}')")
|
||||
if fs.access(CACHE_MD5_FILE) then
|
||||
for line in io.lines(CACHE_MD5_FILE) do
|
||||
cache_md5 = line
|
||||
end
|
||||
end
|
||||
|
||||
if cache_md5 ~= md5 then
|
||||
sys.call("rm -rf " .. CACHE_PATH .. "/" .. CACHE_FLAG .. "*")
|
||||
end
|
||||
|
||||
local global = PROXY_MODE:find("global")
|
||||
local returnhome = PROXY_MODE:find("returnhome")
|
||||
local chnlist = PROXY_MODE:find("chnroute")
|
||||
local gfwlist = PROXY_MODE:find("gfwlist")
|
||||
local only_global
|
||||
|
||||
if CHINADNS_DNS ~= "0" then
|
||||
dnsmasq_default_dns = CHINADNS_DNS
|
||||
end
|
||||
if global and (not returnhome and not chnlist and not gfwlist) then
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
only_global = 1
|
||||
end
|
||||
|
||||
if not fs.access(CACHE_DNS_PATH) then
|
||||
fs.mkdir("/tmp/dnsmasq.d")
|
||||
fs.mkdir(CACHE_DNS_PATH)
|
||||
|
||||
--屏蔽列表
|
||||
for line in io.lines("/usr/share/passwall/rules/block_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
set_domain_address(line, "0.0.0.0")
|
||||
end
|
||||
end
|
||||
|
||||
--始终用国内DNS解析节点域名
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
local address = t.address
|
||||
if datatypes.hostname(address) then
|
||||
set_domain_dns(address, LOCAL_DNS)
|
||||
set_domain_ipset(address, "vpsiplist,vpsiplist6")
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 节点列表中的域名(vpsiplist):%s", LOCAL_DNS or "默认"))
|
||||
|
||||
--始终用国内DNS解析直连(白名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/direct_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
add_excluded_domain(line)
|
||||
set_domain_dns(line, LOCAL_DNS)
|
||||
set_domain_ipset(line, "whitelist,whitelist6")
|
||||
end
|
||||
end
|
||||
log(string.format(" - 域名白名单(whitelist):%s", LOCAL_DNS or "默认"))
|
||||
|
||||
local fwd_dns = LOCAL_DNS
|
||||
local ipset_flag = "whitelist,whitelist6"
|
||||
local no_ipv6
|
||||
if uci:get(appname, "@global_subscribe[0]", "subscribe_proxy") or "0" == "1" then
|
||||
fwd_dns = TUN_DNS
|
||||
ipset_flag = "blacklist,blacklist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "blacklist"
|
||||
no_ipv6 = true
|
||||
end
|
||||
if not only_global then
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
uci:foreach(appname, "subscribe_list", function(t)
|
||||
local domain = get_domain_from_url(t.url)
|
||||
if domain then
|
||||
if no_ipv6 then
|
||||
set_domain_address(domain, "::")
|
||||
end
|
||||
set_domain_dns(domain, fwd_dns)
|
||||
set_domain_ipset(domain, ipset_flag)
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 节点订阅域名(blacklist):%s", fwd_dns or "默认"))
|
||||
|
||||
--始终使用远程DNS解析代理(黑名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/proxy_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
add_excluded_domain(line)
|
||||
local ipset_flag = "blacklist,blacklist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
ipset_flag = "blacklist"
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_dns(line, TUN_DNS)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
log(string.format(" - 代理域名表(blacklist):%s", TUN_DNS or "默认"))
|
||||
|
||||
--分流规则
|
||||
if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
||||
local t = uci:get_all(appname, TCP_NODE)
|
||||
local default_node_id = t["default_node"] or "_direct"
|
||||
uci:foreach(appname, "shunt_rules", function(s)
|
||||
local _node_id = t[s[".name"]] or "nil"
|
||||
if _node_id ~= "nil" and _node_id ~= "_blackhole" then
|
||||
if _node_id == "_default" then
|
||||
_node_id = default_node_id
|
||||
end
|
||||
|
||||
fwd_dns = nil
|
||||
ipset_flag = nil
|
||||
no_ipv6 = nil
|
||||
|
||||
if _node_id == "_direct" then
|
||||
fwd_dns = LOCAL_DNS
|
||||
ipset_flag = "whitelist,whitelist6"
|
||||
else
|
||||
fwd_dns = TUN_DNS
|
||||
ipset_flag = "shuntlist,shuntlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "shuntlist"
|
||||
no_ipv6 = true
|
||||
end
|
||||
if not only_global then
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("geosite:") and not line:find("ext:") then
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
add_excluded_domain(line)
|
||||
|
||||
if no_ipv6 then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - V2ray/Xray分流规则(%s):%s", s.remarks, fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--如果没有使用回国模式
|
||||
if not returnhome then
|
||||
if fs.access("/usr/share/passwall/rules/gfwlist") then
|
||||
local gfwlist_str = sys.exec('cat /usr/share/passwall/rules/gfwlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(gfwlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
local ipset_flag = "gfwlist,gfwlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "gfwlist"
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
if not only_global then
|
||||
fwd_dns = TUN_DNS
|
||||
if CHINADNS_DNS ~= "0" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
end
|
||||
log(string.format(" - 防火墙域名表(gfwlist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
|
||||
if CHINADNS_DNS ~= "0" then
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") then
|
||||
fwd_dns = nil
|
||||
local chnlist_str = sys.exec('cat /usr/share/passwall/rules/chnlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(chnlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, "chnroute,chnroute6")
|
||||
end
|
||||
end
|
||||
end
|
||||
log(string.format(" - 中国域名表(chnroute):%s", fwd_dns or "默认"))
|
||||
end
|
||||
else
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") then
|
||||
local chnlist_str = sys.exec('cat /usr/share/passwall/rules/chnlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(chnlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
local ipset_flag = "chnroute,chnroute6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "chnroute"
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
if not only_global then
|
||||
set_domain_dns(line, TUN_DNS)
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
end
|
||||
log(string.format(" - 中国域名表(chnroute):%s", TUN_DNS or "默认"))
|
||||
end
|
||||
end
|
||||
|
||||
local address_out = io.open(CACHE_DNS_PATH .. "/000-address.conf", "a")
|
||||
local server_out = io.open(CACHE_DNS_PATH .. "/001-server.conf", "a")
|
||||
local ipset_out = io.open(CACHE_DNS_PATH .. "/ipset.conf", "a")
|
||||
for key, value in pairs(list1) do
|
||||
if value.address and #value.address > 0 then
|
||||
address_out:write(string.format("address=/.%s/%s\n", key, value.address))
|
||||
end
|
||||
if value.dns and #value.dns > 0 then
|
||||
for i, dns in ipairs(value.dns) do
|
||||
server_out:write(string.format("server=/.%s/%s\n", key, dns))
|
||||
end
|
||||
end
|
||||
if value.ipsets and #value.ipsets > 0 then
|
||||
local ipsets_str = ""
|
||||
for i, ipset in ipairs(value.ipsets) do
|
||||
ipsets_str = ipsets_str .. ipset .. ","
|
||||
end
|
||||
ipsets_str = ipsets_str:sub(1, #ipsets_str - 1)
|
||||
ipset_out:write(string.format("ipset=/.%s/%s\n", key, ipsets_str))
|
||||
end
|
||||
end
|
||||
address_out:close()
|
||||
server_out:close()
|
||||
ipset_out:close()
|
||||
|
||||
local f_out = io.open(CACHE_MD5_FILE, "a")
|
||||
f_out:write(md5)
|
||||
f_out:close()
|
||||
end
|
||||
fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
|
||||
local conf_out = io.open(DNSMASQ_CONF_FILE, "a")
|
||||
conf_out:write(string.format("conf-dir=%s\n", TMP_DNSMASQ_PATH))
|
||||
if dnsmasq_default_dns then
|
||||
local f_out = io.open("/tmp/etc/passwall/default_DNS", "a")
|
||||
f_out:write(DEFAULT_DNS)
|
||||
f_out:close()
|
||||
conf_out:write(string.format("server=%s\n", dnsmasq_default_dns))
|
||||
conf_out:write("all-servers\n")
|
||||
conf_out:write("no-poll\n")
|
||||
conf_out:write("no-resolv\n")
|
||||
log(string.format(" - 以上所列以外及默认:%s", dnsmasq_default_dns))
|
||||
end
|
||||
conf_out:close()
|
||||
log(" - PassWall必须依赖于Dnsmasq,如果你自行配置了错误的DNS流程,将会导致域名(直连/代理域名)分流失效!!!")
|
39
luci-app-passwall/root/usr/share/passwall/helper_smartdns.sh
Executable file
39
luci-app-passwall/root/usr/share/passwall/helper_smartdns.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
restart() {
|
||||
local no_log
|
||||
eval_set_val $@
|
||||
_LOG_FILE=$LOG_FILE
|
||||
[ -n "$no_log" ] && LOG_FILE="/dev/null"
|
||||
rm -rf /tmp/smartdns.cache
|
||||
/etc/init.d/smartdns reload >/dev/null 2>&1 &
|
||||
LOG_FILE=${_LOG_FILE}
|
||||
}
|
||||
|
||||
add() {
|
||||
local FLAG SMARTDNS_CONF LOCAL_GROUP REMOTE_GROUP REMOTE_FAKEDNS TUN_DNS TCP_NODE PROXY_MODE NO_PROXY_IPV6 NO_LOGIC_LOG
|
||||
eval_set_val $@
|
||||
lua $APP_PATH/helper_smartdns_add.lua -FLAG $FLAG -SMARTDNS_CONF $SMARTDNS_CONF -LOCAL_GROUP ${LOCAL_GROUP:-nil} -REMOTE_GROUP ${REMOTE_GROUP:-nil} -REMOTE_FAKEDNS ${REMOTE_FAKEDNS:-0} -TUN_DNS $TUN_DNS -TCP_NODE $TCP_NODE -PROXY_MODE $PROXY_MODE -NO_PROXY_IPV6 ${NO_PROXY_IPV6:-0} -NO_LOGIC_LOG ${NO_LOGIC_LOG:-0}
|
||||
}
|
||||
|
||||
del() {
|
||||
rm -rf /tmp/etc/smartdns/passwall.conf
|
||||
sed -i "/passwall/d" /etc/smartdns/custom.conf >/dev/null 2>&1
|
||||
rm -rf /tmp/smartdns.cache
|
||||
/etc/init.d/smartdns reload >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
arg1=$1
|
||||
shift
|
||||
case $arg1 in
|
||||
add)
|
||||
add $@
|
||||
;;
|
||||
del)
|
||||
del $@
|
||||
;;
|
||||
restart)
|
||||
restart $@
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
@ -0,0 +1,394 @@
|
||||
local api = require "luci.model.cbi.passwall.api.api"
|
||||
|
||||
local var = api.get_args(arg)
|
||||
local FLAG = var["-FLAG"]
|
||||
local SMARTDNS_CONF = var["-SMARTDNS_CONF"]
|
||||
local LOCAL_GROUP = var["-LOCAL_GROUP"]
|
||||
local REMOTE_GROUP = var["-REMOTE_GROUP"]
|
||||
local REMOTE_FAKEDNS = var["-REMOTE_FAKEDNS"]
|
||||
local TUN_DNS = var["-TUN_DNS"]
|
||||
local TCP_NODE = var["-TCP_NODE"]
|
||||
local PROXY_MODE = var["-PROXY_MODE"]
|
||||
local NO_PROXY_IPV6 = var["-NO_PROXY_IPV6"]
|
||||
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
|
||||
local LOG_FILE = "/tmp/log/passwall.log"
|
||||
local CACHE_PATH = "/tmp/etc/passwall_tmp"
|
||||
local CACHE_FLAG = "dns_" .. FLAG
|
||||
local CACHE_DNS_FILE = CACHE_PATH .. "/" .. CACHE_FLAG .. ".conf"
|
||||
local CACHE_MD5_FILE = CACHE_PATH .. "/" .. CACHE_FLAG .. ".md5"
|
||||
local SMARTDNS_PATH = "/tmp/etc/smartdns"
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
local jsonc = api.jsonc
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local datatypes = api.datatypes
|
||||
|
||||
local list1 = {}
|
||||
local excluded_domain = {}
|
||||
local excluded_domain_str = "!"
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
end
|
||||
local f, err = io.open(LOG_FILE, "a")
|
||||
if f and err == nil then
|
||||
local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
f:write(str .. "\n")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
--从url获取域名
|
||||
local function get_domain_from_url(url)
|
||||
if url then
|
||||
if datatypes.hostname(url) then
|
||||
return url
|
||||
end
|
||||
local domain = url:match("//([^/]+)")
|
||||
if domain then
|
||||
return domain
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function check_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
for k,v in ipairs(list1[domain].ipsets) do
|
||||
if ipset == v then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function set_domain_address(domain, address)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
if not list1[domain].address then
|
||||
list1[domain].address = address
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_group(domain, group)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not group then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
if not list1[domain].group then
|
||||
list1[domain].group = group
|
||||
if group == REMOTE_GROUP then
|
||||
list1[domain].speed_check_mode = "none"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set_domain_ipset(domain, ipset)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
if not ipset then
|
||||
return
|
||||
end
|
||||
if not list1[domain] then
|
||||
list1[domain] = {
|
||||
ipsets = {}
|
||||
}
|
||||
end
|
||||
for line in string.gmatch(ipset, '[^' .. "," .. ']+') do
|
||||
if not check_ipset(domain, line) then
|
||||
table.insert(list1[domain].ipsets, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return
|
||||
end
|
||||
table.insert(excluded_domain, domain)
|
||||
excluded_domain_str = excluded_domain_str .. "|" .. domain
|
||||
end
|
||||
|
||||
local function check_excluded_domain(domain)
|
||||
if domain == "" or domain:find("#") then
|
||||
return false
|
||||
end
|
||||
for k,v in ipairs(excluded_domain) do
|
||||
if domain:find(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local cache_md5 = ""
|
||||
local str = SMARTDNS_CONF .. LOCAL_GROUP .. REMOTE_GROUP .. REMOTE_FAKEDNS .. TUN_DNS .. PROXY_MODE .. NO_PROXY_IPV6
|
||||
local md5 = luci.sys.exec("echo -n $(echo '" .. str .. "' | md5sum | awk '{print $1}')")
|
||||
if fs.access(CACHE_MD5_FILE) then
|
||||
for line in io.lines(CACHE_MD5_FILE) do
|
||||
cache_md5 = line
|
||||
end
|
||||
end
|
||||
|
||||
if cache_md5 ~= md5 then
|
||||
sys.call("rm -rf " .. CACHE_PATH .. "/" .. CACHE_FLAG .. "*")
|
||||
end
|
||||
|
||||
local global = PROXY_MODE:find("global")
|
||||
local returnhome = PROXY_MODE:find("returnhome")
|
||||
local chnlist = PROXY_MODE:find("chnroute")
|
||||
local gfwlist = PROXY_MODE:find("gfwlist")
|
||||
|
||||
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
||||
REMOTE_GROUP = "passwall_proxy"
|
||||
if TUN_DNS then
|
||||
TUN_DNS = TUN_DNS:gsub("#", ":")
|
||||
end
|
||||
sys.call('sed -i "/passwall/d" /etc/smartdns/custom.conf >/dev/null 2>&1')
|
||||
end
|
||||
|
||||
if not fs.access(CACHE_DNS_FILE) then
|
||||
sys.call(string.format('echo "server %s -group %s -exclude-default-group" >> %s', TUN_DNS, REMOTE_GROUP, CACHE_DNS_FILE))
|
||||
--屏蔽列表
|
||||
for line in io.lines("/usr/share/passwall/rules/block_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
set_domain_address(line, "-")
|
||||
end
|
||||
end
|
||||
|
||||
--始终用国内DNS解析节点域名
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
local address = t.address
|
||||
if datatypes.hostname(address) then
|
||||
set_domain_group(address, LOCAL_GROUP)
|
||||
set_domain_ipset(address, "#4:vpsiplist,#6:vpsiplist6")
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 节点列表中的域名(vpsiplist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
|
||||
--始终用国内DNS解析直连(白名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/direct_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
add_excluded_domain(line)
|
||||
set_domain_group(line, LOCAL_GROUP)
|
||||
set_domain_ipset(line, "#4:whitelist,#6:whitelist6")
|
||||
end
|
||||
end
|
||||
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
|
||||
local fwd_group = LOCAL_GROUP
|
||||
local ipset_flag = "#4:whitelist,#6:whitelist6"
|
||||
local no_ipv6
|
||||
if uci:get(appname, "@global_subscribe[0]", "subscribe_proxy") or "0" == "1" then
|
||||
fwd_group = REMOTE_GROUP
|
||||
ipset_flag = "#4:blacklist,#6:blacklist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "#4:blacklist"
|
||||
no_ipv6 = true
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
end
|
||||
uci:foreach(appname, "subscribe_list", function(t)
|
||||
local domain = get_domain_from_url(t.url)
|
||||
if domain then
|
||||
if no_ipv6 then
|
||||
set_domain_address(domain, "#6")
|
||||
end
|
||||
set_domain_group(domain, fwd_group)
|
||||
set_domain_ipset(domain, ipset_flag)
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 节点订阅域名(blacklist)使用分组:%s", fwd_group or "默认"))
|
||||
|
||||
--始终使用远程DNS解析代理(黑名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/proxy_host") do
|
||||
if line ~= "" and not line:find("#") then
|
||||
add_excluded_domain(line)
|
||||
local ipset_flag = "#4:blacklist,#6:blacklist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "#6")
|
||||
ipset_flag = "#4:blacklist"
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_group(line, REMOTE_GROUP)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
log(string.format(" - 代理域名表(blacklist)使用分组:%s", REMOTE_GROUP or "默认"))
|
||||
|
||||
--分流规则
|
||||
if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
||||
local t = uci:get_all(appname, TCP_NODE)
|
||||
local default_node_id = t["default_node"] or "_direct"
|
||||
uci:foreach(appname, "shunt_rules", function(s)
|
||||
local _node_id = t[s[".name"]] or "nil"
|
||||
if _node_id ~= "nil" and _node_id ~= "_blackhole" then
|
||||
if _node_id == "_default" then
|
||||
_node_id = default_node_id
|
||||
end
|
||||
|
||||
fwd_group = nil
|
||||
ipset_flag = nil
|
||||
no_ipv6 = nil
|
||||
|
||||
if _node_id == "_direct" then
|
||||
fwd_group = LOCAL_GROUP
|
||||
ipset_flag = "#4:whitelist,#6:whitelist6"
|
||||
else
|
||||
fwd_group = REMOTE_GROUP
|
||||
ipset_flag = "#4:shuntlist,#6:shuntlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "shuntlist"
|
||||
no_ipv6 = true
|
||||
end
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("geosite:") and not line:find("ext:") then
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
add_excluded_domain(line)
|
||||
|
||||
if no_ipv6 then
|
||||
set_domain_address(line, "#6")
|
||||
end
|
||||
set_domain_group(line, fwd_group)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - V2ray/Xray分流规则(%s)使用分组:%s", s.remarks, fwd_group or "默认"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--如果没有使用回国模式
|
||||
if not returnhome then
|
||||
if fs.access("/usr/share/passwall/rules/gfwlist") then
|
||||
local gfwlist_str = sys.exec('cat /usr/share/passwall/rules/gfwlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(gfwlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
local ipset_flag = "#4:gfwlist,#6:gfwlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "#4:gfwlist"
|
||||
set_domain_address(line, "#6")
|
||||
end
|
||||
fwd_group = REMOTE_GROUP
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_group(line, fwd_group)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
log(string.format(" - 防火墙域名表(gfwlist)使用分组:%s", fwd_group or "默认"))
|
||||
end
|
||||
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") and chnlist then
|
||||
local chnlist_str = sys.exec('cat /usr/share/passwall/rules/chnlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(chnlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
set_domain_group(line, LOCAL_GROUP)
|
||||
set_domain_ipset(line, "#4:chnroute,#6:chnroute6")
|
||||
end
|
||||
end
|
||||
end
|
||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||
else
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") then
|
||||
local chnlist_str = sys.exec('cat /usr/share/passwall/rules/chnlist | grep -v -E "^#" | grep -v -E "' .. excluded_domain_str .. '"')
|
||||
for line in string.gmatch(chnlist_str, "[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
local ipset_flag = "#4:chnroute,#6:chnroute6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = "#4:chnroute"
|
||||
set_domain_address(line, "#6")
|
||||
end
|
||||
set_domain_group(line, REMOTE_GROUP)
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", REMOTE_GROUP or "默认"))
|
||||
end
|
||||
end
|
||||
|
||||
local f_out = io.open(CACHE_DNS_FILE, "a")
|
||||
for key, value in pairs(list1) do
|
||||
local group_str = ""
|
||||
local ipset_str = ""
|
||||
local speed_check_mode_str = ""
|
||||
local address_str = ""
|
||||
if value.group and #value.group > 0 then
|
||||
group_str = group_str .. value.group
|
||||
end
|
||||
if group_str ~= "" then
|
||||
group_str = " -n " .. group_str
|
||||
end
|
||||
if value.ipsets and #value.ipsets > 0 then
|
||||
for i, ipset in ipairs(value.ipsets) do
|
||||
ipset_str = ipset_str .. ipset .. ","
|
||||
end
|
||||
ipset_str = ipset_str:sub(1, #ipset_str - 1)
|
||||
end
|
||||
if ipset_str ~= "" then
|
||||
ipset_str = " -p " .. ipset_str
|
||||
end
|
||||
if value.address and #value.address > 0 then
|
||||
address_str = address_str .. value.address
|
||||
end
|
||||
if address_str ~= "" then
|
||||
address_str = " -a " .. address_str
|
||||
end
|
||||
if value.speed_check_mode and #value.speed_check_mode > 0 then
|
||||
speed_check_mode_str = value.speed_check_mode
|
||||
end
|
||||
if speed_check_mode_str ~= "" then
|
||||
speed_check_mode_str = " -c " .. speed_check_mode_str
|
||||
end
|
||||
local str = string.format("domain-rules /%s/ %s%s%s%s\n", key, group_str, ipset_str, address_str, speed_check_mode_str)
|
||||
f_out:write(str)
|
||||
end
|
||||
f_out:close()
|
||||
|
||||
f_out = io.open(CACHE_MD5_FILE, "a")
|
||||
f_out:write(md5)
|
||||
f_out:close()
|
||||
end
|
||||
fs.symlink(CACHE_DNS_FILE, SMARTDNS_CONF)
|
||||
sys.call(string.format('echo "conf-file %s" >> /etc/smartdns/custom.conf', SMARTDNS_CONF))
|
||||
log(" - 请让SmartDNS作为Dnsmasq的上游或重定向!")
|
1321
luci-app-passwall/root/usr/share/passwall/iptables.sh
Executable file
1321
luci-app-passwall/root/usr/share/passwall/iptables.sh
Executable file
File diff suppressed because it is too large
Load Diff
48
luci-app-passwall/root/usr/share/passwall/monitor.sh
Executable file
48
luci-app-passwall/root/usr/share/passwall/monitor.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
TMP_PATH=/tmp/etc/$CONFIG
|
||||
TMP_BIN_PATH=$TMP_PATH/bin
|
||||
TMP_SCRIPT_FUNC_PATH=$TMP_PATH/script_func
|
||||
TMP_ID_PATH=$TMP_PATH/id
|
||||
LOCK_FILE_DIR=/tmp/lock
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_script.lock
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get $CONFIG.$1.$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=0
|
||||
[ -n "$4" ] && index=$4
|
||||
local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
ENABLED=$(config_t_get global enabled 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
ENABLED=$(config_t_get global_delay start_daemon 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
sleep 58s
|
||||
while [ "$ENABLED" -eq 1 ]; do
|
||||
[ -f "$LOCK_FILE" ] && {
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
touch $LOCK_FILE
|
||||
|
||||
for filename in $(ls ${TMP_SCRIPT_FUNC_PATH}); do
|
||||
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
|
||||
cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
|
||||
[ -n "$(echo $cmd_check | grep "dns2socks")" ] && cmd_check=$(echo $cmd_check | sed "s#:# #g")
|
||||
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l)
|
||||
if [ $icount = 0 ]; then
|
||||
#echo "${cmd} 进程挂掉,重启" >> /tmp/log/passwall.log
|
||||
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f $LOCK_FILE
|
||||
sleep 58s
|
||||
done
|
401
luci-app-passwall/root/usr/share/passwall/rule_update.lua
Executable file
401
luci-app-passwall/root/usr/share/passwall/rule_update.lua
Executable file
@ -0,0 +1,401 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
require 'nixio'
|
||||
require 'luci.sys'
|
||||
local luci = luci
|
||||
local ucic = luci.model.uci.cursor()
|
||||
local jsonc = require "luci.jsonc"
|
||||
local name = 'passwall'
|
||||
local api = require ("luci.model.cbi." .. name .. ".api.api")
|
||||
local arg1 = arg[1]
|
||||
|
||||
local rule_path = "/usr/share/" .. name .. "/rules"
|
||||
local reboot = 0
|
||||
local gfwlist_update = 0
|
||||
local chnroute_update = 0
|
||||
local chnroute6_update = 0
|
||||
local chnlist_update = 0
|
||||
local geoip_update = 0
|
||||
local geosite_update = 0
|
||||
|
||||
-- match comments/title/whitelist/ip address/excluded_domain
|
||||
local comment_pattern = "^[!\\[@]+"
|
||||
local ip_pattern = "^%d+%.%d+%.%d+%.%d+"
|
||||
local ip4_ipset_pattern = "^%d+%.%d+%.%d+%.%d+[%/][%d]+$"
|
||||
local ip6_ipset_pattern = ":-[%x]+%:+[%x]-[%/][%d]+$"
|
||||
local domain_pattern = "([%w%-%_]+%.[%w%.%-%_]+)[%/%*]*"
|
||||
local excluded_domain = {"apple.com","sina.cn","sina.com.cn","baidu.com","byr.cn","jlike.com","weibo.com","zhongsou.com","youdao.com","sogou.com","so.com","soso.com","aliyun.com","taobao.com","jd.com","qq.com","bing.com"}
|
||||
|
||||
local gfwlist_url = ucic:get(name, "@global_rules[0]", "gfwlist_url") or {"https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"}
|
||||
local chnroute_url = ucic:get(name, "@global_rules[0]", "chnroute_url") or {"https://ispip.clang.cn/all_cn.txt"}
|
||||
local chnroute6_url = ucic:get(name, "@global_rules[0]", "chnroute6_url") or {"https://ispip.clang.cn/all_cn_ipv6.txt"}
|
||||
local chnlist_url = ucic:get(name, "@global_rules[0]", "chnlist_url") or {"https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf","https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf","https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf"}
|
||||
local geoip_api = "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest"
|
||||
local geosite_api = "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest"
|
||||
local v2ray_asset_location = ucic:get_first(name, 'global_rules', "v2ray_location_asset", "/usr/share/v2ray/")
|
||||
|
||||
local log = function(...)
|
||||
if arg1 then
|
||||
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
if arg1 == "log" then
|
||||
local f, err = io.open("/tmp/log/passwall.log", "a")
|
||||
if f and err == nil then
|
||||
f:write(result .. "\n")
|
||||
f:close()
|
||||
end
|
||||
elseif arg1 == "print" then
|
||||
print(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- trim
|
||||
local function trim(text)
|
||||
if not text or text == "" then return "" end
|
||||
return (string.gsub(text, "^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
-- curl
|
||||
local function curl(url, file)
|
||||
local cmd = "curl -skL -w %{http_code} --retry 3 --connect-timeout 3 '" .. url .. "'"
|
||||
if file then
|
||||
cmd = cmd .. " -o " .. file
|
||||
end
|
||||
local stdout = luci.sys.exec(cmd)
|
||||
|
||||
if file then
|
||||
return tonumber(trim(stdout))
|
||||
else
|
||||
return trim(stdout)
|
||||
end
|
||||
end
|
||||
|
||||
--check excluded domain
|
||||
local function check_excluded_domain(value)
|
||||
for k,v in ipairs(excluded_domain) do
|
||||
if value:find(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function line_count(file_path)
|
||||
local num = 0
|
||||
for _ in io.lines(file_path) do
|
||||
num = num + 1
|
||||
end
|
||||
return num;
|
||||
end
|
||||
|
||||
--fetch rule
|
||||
local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
local sret = 200
|
||||
local sret_tmp = 0
|
||||
local domains = {}
|
||||
local file_tmp = "/tmp/" ..rule_name.. "_tmp"
|
||||
local download_file_tmp = "/tmp/" ..rule_name.. "_dl"
|
||||
local unsort_file_tmp = "/tmp/" ..rule_name.. "_unsort"
|
||||
|
||||
log(rule_name.. " 开始更新...")
|
||||
for k,v in ipairs(url) do
|
||||
sret_tmp = curl(v, download_file_tmp..k)
|
||||
if sret_tmp == 200 then
|
||||
if rule_name == "gfwlist" then
|
||||
local domains = {}
|
||||
local gfwlist = io.open(download_file_tmp..k, "r")
|
||||
local decode = api.base64Decode(gfwlist:read("*all"))
|
||||
gfwlist:close()
|
||||
|
||||
gfwlist = io.open(download_file_tmp..k, "w")
|
||||
gfwlist:write(decode)
|
||||
gfwlist:close()
|
||||
end
|
||||
|
||||
if rule_type == "domain" and exclude_domain == true then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or check_excluded_domain(line)) then
|
||||
local start, finish, match = string.find(line, domain_pattern)
|
||||
if (start) then
|
||||
domains[match] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif rule_type == "domain" then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern)) then
|
||||
local start, finish, match = string.find(line, domain_pattern)
|
||||
if (start) then
|
||||
domains[match] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif rule_type == "ip4" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
local start, finish, match = string.find(line, ip4_ipset_pattern)
|
||||
if (start) then
|
||||
out:write(string.format("%s\n", line))
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
elseif rule_type == "ip6" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
local start, finish, match = string.find(line, ip6_ipset_pattern)
|
||||
if (start) then
|
||||
out:write(string.format("%s\n", line))
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
end
|
||||
os.remove(download_file_tmp..k)
|
||||
else
|
||||
sret = 0
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载失败!")
|
||||
end
|
||||
end
|
||||
|
||||
if sret == 200 then
|
||||
if rule_type == "domain" then
|
||||
local out = io.open(unsort_file_tmp, "w")
|
||||
for k,v in pairs(domains) do
|
||||
out:write(string.format("%s\n", k))
|
||||
end
|
||||
out:close()
|
||||
end
|
||||
luci.sys.call("cat " ..unsort_file_tmp.. " | sort -u > "..file_tmp)
|
||||
os.remove(unsort_file_tmp)
|
||||
end
|
||||
|
||||
if sret == 200 then
|
||||
local old_md5 = luci.sys.exec("echo -n $(md5sum " .. rule_path .. "/" ..rule_name.. " | awk '{print $1}')")
|
||||
local new_md5 = luci.sys.exec("echo -n $([ -f '" ..file_tmp.. "' ] && md5sum " ..file_tmp.." | awk '{print $1}')")
|
||||
if old_md5 ~= new_md5 then
|
||||
local count = line_count(file_tmp)
|
||||
luci.sys.exec("mv -f "..file_tmp .. " " ..rule_path .. "/" ..rule_name)
|
||||
reboot = 1
|
||||
log(rule_name.. " 更新成功,总规则数 " ..count.. " 条。")
|
||||
else
|
||||
log(rule_name.. " 版本一致,无需更新。")
|
||||
end
|
||||
else
|
||||
log(rule_name.. " 文件下载失败!")
|
||||
end
|
||||
os.remove(file_tmp)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function fetch_gfwlist()
|
||||
fetch_rule("gfwlist","domain",gfwlist_url,true)
|
||||
end
|
||||
|
||||
local function fetch_chnroute()
|
||||
fetch_rule("chnroute","ip4",chnroute_url,false)
|
||||
end
|
||||
|
||||
local function fetch_chnroute6()
|
||||
fetch_rule("chnroute6","ip6",chnroute6_url,false)
|
||||
end
|
||||
|
||||
local function fetch_chnlist()
|
||||
fetch_rule("chnlist","domain",chnlist_url,false)
|
||||
end
|
||||
|
||||
--获取geoip
|
||||
local function fetch_geoip()
|
||||
--请求geoip
|
||||
xpcall(function()
|
||||
local json_str = curl(geoip_api)
|
||||
local json = jsonc.parse(json_str)
|
||||
if json.tag_name and json.assets then
|
||||
for _, v in ipairs(json.assets) do
|
||||
if v.name and v.name == "geoip.dat.sha256sum" then
|
||||
local sret = curl(v.browser_download_url, "/tmp/geoip.dat.sha256sum")
|
||||
if sret == 200 then
|
||||
local f = io.open("/tmp/geoip.dat.sha256sum", "r")
|
||||
local content = f:read()
|
||||
f:close()
|
||||
f = io.open("/tmp/geoip.dat.sha256sum", "w")
|
||||
f:write(content:gsub("geoip.dat", "/tmp/geoip.dat"), "")
|
||||
f:close()
|
||||
|
||||
if nixio.fs.access(v2ray_asset_location .. "geoip.dat") then
|
||||
luci.sys.call(string.format("cp -f %s %s", v2ray_asset_location .. "geoip.dat", "/tmp/geoip.dat"))
|
||||
if luci.sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then
|
||||
log("geoip 版本一致,无需更新。")
|
||||
return 1
|
||||
end
|
||||
end
|
||||
for _2, v2 in ipairs(json.assets) do
|
||||
if v2.name and v2.name == "geoip.dat" then
|
||||
sret = curl(v2.browser_download_url, "/tmp/geoip.dat")
|
||||
if luci.sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then
|
||||
luci.sys.call(string.format("mkdir -p %s && cp -f %s %s", v2ray_asset_location, "/tmp/geoip.dat", v2ray_asset_location .. "geoip.dat"))
|
||||
reboot = 1
|
||||
log("geoip 更新成功。")
|
||||
return 1
|
||||
else
|
||||
log("geoip 更新失败,请稍后再试。")
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
function(e)
|
||||
end)
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
--获取geosite
|
||||
local function fetch_geosite()
|
||||
--请求geosite
|
||||
xpcall(function()
|
||||
local json_str = curl(geosite_api)
|
||||
local json = jsonc.parse(json_str)
|
||||
if json.tag_name and json.assets then
|
||||
for _, v in ipairs(json.assets) do
|
||||
if v.name and v.name == "geosite.dat.sha256sum" then
|
||||
local sret = curl(v.browser_download_url, "/tmp/geosite.dat.sha256sum")
|
||||
if sret == 200 then
|
||||
local f = io.open("/tmp/geosite.dat.sha256sum", "r")
|
||||
local content = f:read()
|
||||
f:close()
|
||||
f = io.open("/tmp/geosite.dat.sha256sum", "w")
|
||||
f:write(content:gsub("geosite.dat", "/tmp/geosite.dat"), "")
|
||||
f:close()
|
||||
|
||||
if nixio.fs.access(v2ray_asset_location .. "geosite.dat") then
|
||||
luci.sys.call(string.format("cp -f %s %s", v2ray_asset_location .. "geosite.dat", "/tmp/geosite.dat"))
|
||||
if luci.sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then
|
||||
log("geosite 版本一致,无需更新。")
|
||||
return 1
|
||||
end
|
||||
end
|
||||
for _2, v2 in ipairs(json.assets) do
|
||||
if v2.name and v2.name == "geosite.dat" then
|
||||
sret = curl(v2.browser_download_url, "/tmp/geosite.dat")
|
||||
if luci.sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then
|
||||
luci.sys.call(string.format("mkdir -p %s && cp -f %s %s", v2ray_asset_location, "/tmp/geosite.dat", v2ray_asset_location .. "geosite.dat"))
|
||||
reboot = 1
|
||||
log("geosite 更新成功。")
|
||||
return 1
|
||||
else
|
||||
log("geosite 更新失败,请稍后再试。")
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
function(e)
|
||||
end)
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
if arg[2] then
|
||||
if arg[2]:find("gfwlist") then
|
||||
gfwlist_update = 1
|
||||
end
|
||||
if arg[2]:find("chnroute") then
|
||||
chnroute_update = 1
|
||||
end
|
||||
if arg[2]:find("chnroute6") then
|
||||
chnroute6_update = 1
|
||||
end
|
||||
if arg[2]:find("chnlist") then
|
||||
chnlist_update = 1
|
||||
end
|
||||
if arg[2]:find("geoip") then
|
||||
geoip_update = 1
|
||||
end
|
||||
if arg[2]:find("geosite") then
|
||||
geosite_update = 1
|
||||
end
|
||||
else
|
||||
gfwlist_update = ucic:get_first(name, 'global_rules', "gfwlist_update", 1)
|
||||
chnroute_update = ucic:get_first(name, 'global_rules', "chnroute_update", 1)
|
||||
chnroute6_update = ucic:get_first(name, 'global_rules', "chnroute6_update", 1)
|
||||
chnlist_update = ucic:get_first(name, 'global_rules', "chnlist_update", 1)
|
||||
geoip_update = ucic:get_first(name, 'global_rules', "geoip_update", 1)
|
||||
geosite_update = ucic:get_first(name, 'global_rules', "geosite_update", 1)
|
||||
end
|
||||
if gfwlist_update == 0 and chnroute_update == 0 and chnroute6_update == 0 and chnlist_update == 0 and geoip_update == 0 and geosite_update == 0 then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
log("开始更新规则...")
|
||||
if tonumber(gfwlist_update) == 1 then
|
||||
xpcall(fetch_gfwlist,function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log('更新gfwlist发生错误...')
|
||||
end)
|
||||
end
|
||||
|
||||
if tonumber(chnroute_update) == 1 then
|
||||
xpcall(fetch_chnroute,function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log('更新chnroute发生错误...')
|
||||
end)
|
||||
end
|
||||
|
||||
if tonumber(chnroute6_update) == 1 then
|
||||
xpcall(fetch_chnroute6,function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log('更新chnroute6发生错误...')
|
||||
end)
|
||||
end
|
||||
|
||||
if tonumber(chnlist_update) == 1 then
|
||||
xpcall(fetch_chnlist,function(e)
|
||||
log(e)
|
||||
log(debug.traceback())
|
||||
log('更新chnlist发生错误...')
|
||||
end)
|
||||
end
|
||||
|
||||
if tonumber(geoip_update) == 1 then
|
||||
log("geoip 开始更新...")
|
||||
local status = fetch_geoip()
|
||||
os.remove("/tmp/geoip.dat")
|
||||
os.remove("/tmp/geoip.dat.sha256sum")
|
||||
end
|
||||
|
||||
if tonumber(geosite_update) == 1 then
|
||||
log("geosite 开始更新...")
|
||||
local status = fetch_geosite()
|
||||
os.remove("/tmp/geosite.dat")
|
||||
os.remove("/tmp/geosite.dat.sha256sum")
|
||||
end
|
||||
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "gfwlist_update", gfwlist_update)
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "chnroute_update", chnroute_update)
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "chnroute6_update", chnroute6_update)
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "chnlist_update", chnlist_update)
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "geoip_update", geoip_update)
|
||||
ucic:set(name, ucic:get_first(name, 'global_rules'), "geosite_update", geosite_update)
|
||||
ucic:save(name)
|
||||
luci.sys.call("uci commit " .. name)
|
||||
|
||||
if reboot == 1 then
|
||||
log("重启服务,应用新的规则。")
|
||||
luci.sys.call("/usr/share/" .. name .. "/iptables.sh flush_ipset > /dev/null 2>&1 &")
|
||||
end
|
||||
log("规则更新完毕...")
|
67226
luci-app-passwall/root/usr/share/passwall/rules/chnlist
Normal file
67226
luci-app-passwall/root/usr/share/passwall/rules/chnlist
Normal file
File diff suppressed because it is too large
Load Diff
8632
luci-app-passwall/root/usr/share/passwall/rules/chnroute
Normal file
8632
luci-app-passwall/root/usr/share/passwall/rules/chnroute
Normal file
File diff suppressed because it is too large
Load Diff
1944
luci-app-passwall/root/usr/share/passwall/rules/chnroute6
Normal file
1944
luci-app-passwall/root/usr/share/passwall/rules/chnroute6
Normal file
File diff suppressed because it is too large
Load Diff
27
luci-app-passwall/root/usr/share/passwall/rules/direct_host
Normal file
27
luci-app-passwall/root/usr/share/passwall/rules/direct_host
Normal file
@ -0,0 +1,27 @@
|
||||
apple.com
|
||||
microsoft.com
|
||||
dyndns.com
|
||||
douyucdn.cn
|
||||
douyucdn2.cn
|
||||
|
||||
#steam
|
||||
steamcontent.com
|
||||
dl.steam.clngaa.com
|
||||
dl.steam.ksyna.com
|
||||
st.dl.bscstorage.net
|
||||
st.dl.eccdnx.com
|
||||
st.dl.pinyuncloud.com
|
||||
cdn.mileweb.cs.steampowered.com.8686c.com
|
||||
cdn-ws.content.steamchina.com
|
||||
cdn-qc.content.steamchina.com
|
||||
cdn-ali.content.steamchina.com
|
||||
epicgames-download1-1251447533.file.myqcloud.com
|
||||
|
||||
# Synology ddns
|
||||
account.synology.com
|
||||
checkip.dyndns.org
|
||||
checkip.synology.com
|
||||
checkipv6.dyndns.org
|
||||
checkipv6.synology.com
|
||||
checkport.synology.com
|
||||
ddns.synology.com
|
@ -0,0 +1,6 @@
|
||||
114.114.114.114
|
||||
114.114.115.115
|
||||
223.5.5.5
|
||||
223.6.6.6
|
||||
119.29.29.29
|
||||
180.76.76.76
|
@ -0,0 +1,24 @@
|
||||
courier.push.apple.com
|
||||
rbsxbxp-mim.vivox.com
|
||||
rbsxbxp.www.vivox.com
|
||||
rbsxbxp-ws.vivox.com
|
||||
rbspsxp.www.vivox.com
|
||||
rbspsxp-mim.vivox.com
|
||||
rbspsxp-ws.vivox.com
|
||||
rbswxp.www.vivox.com
|
||||
rbswxp-mim.vivox.com
|
||||
disp-rbspsp-5-1.vivox.com
|
||||
disp-rbsxbp-5-1.vivox.com
|
||||
proxy.rbsxbp.vivox.com
|
||||
proxy.rbspsp.vivox.com
|
||||
proxy.rbswp.vivox.com
|
||||
rbswp.vivox.com
|
||||
rbsxbp.vivox.com
|
||||
rbspsp.vivox.com
|
||||
rbspsp.www.vivox.com
|
||||
rbswp.www.vivox.com
|
||||
rbsxbp.www.vivox.com
|
||||
rbsxbxp.vivox.com
|
||||
rbspsxp.vivox.com
|
||||
rbswxp.vivox.com
|
||||
Mijia Cloud
|
12219
luci-app-passwall/root/usr/share/passwall/rules/gfwlist
Normal file
12219
luci-app-passwall/root/usr/share/passwall/rules/gfwlist
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
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.168.0.0/16
|
||||
224.0.0.0/4
|
||||
240.0.0.0/4
|
13
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6
Normal file
13
luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6
Normal file
@ -0,0 +1,13 @@
|
||||
::/128
|
||||
::1/128
|
||||
::ffff:0:0/96
|
||||
::ffff:0:0:0/96
|
||||
64:ff9b::/96
|
||||
100::/64
|
||||
2001::/32
|
||||
2001:20::/28
|
||||
2001:db8::/32
|
||||
2002::/16
|
||||
fc00::/7
|
||||
fe80::/10
|
||||
ff00::/8
|
13
luci-app-passwall/root/usr/share/passwall/rules/proxy_host
Normal file
13
luci-app-passwall/root/usr/share/passwall/rules/proxy_host
Normal file
@ -0,0 +1,13 @@
|
||||
sspanel.net
|
||||
v2ex.com
|
||||
|
||||
#google
|
||||
googleapis.cn
|
||||
googleapis.com
|
||||
google.com.tw
|
||||
google.com.hk
|
||||
gstatic.com
|
||||
xn--ngstr-lra8j.com
|
||||
|
||||
#github
|
||||
github.com
|
19
luci-app-passwall/root/usr/share/passwall/rules/proxy_ip
Normal file
19
luci-app-passwall/root/usr/share/passwall/rules/proxy_ip
Normal file
@ -0,0 +1,19 @@
|
||||
149.154.160.0/20
|
||||
91.108.4.0/22
|
||||
91.108.56.0/24
|
||||
109.239.140.0/24
|
||||
67.198.55.0/24
|
||||
8.8.4.4
|
||||
8.8.8.8
|
||||
208.67.222.222
|
||||
208.67.220.220
|
||||
1.1.1.1
|
||||
1.1.1.2
|
||||
1.0.0.1
|
||||
9.9.9.9
|
||||
149.112.112.112
|
||||
2001:67c:4e8::/48
|
||||
2001:b28:f23c::/48
|
||||
2001:b28:f23d::/48
|
||||
2001:b28:f23f::/48
|
||||
2001:b28:f242::/48
|
1226
luci-app-passwall/root/usr/share/passwall/subscribe.lua
Executable file
1226
luci-app-passwall/root/usr/share/passwall/subscribe.lua
Executable file
File diff suppressed because it is too large
Load Diff
274
luci-app-passwall/root/usr/share/passwall/test.sh
Executable file
274
luci-app-passwall/root/usr/share/passwall/test.sh
Executable file
@ -0,0 +1,274 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
LOG_FILE=/tmp/log/$CONFIG.log
|
||||
LOCK_FILE_DIR=/tmp/lock
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_script.lock
|
||||
|
||||
echolog() {
|
||||
local d="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
#echo -e "$d: $1"
|
||||
echo -e "$d: $1" >> $LOG_FILE
|
||||
}
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=0
|
||||
[ -n "$4" ] && index=$4
|
||||
local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
test_url() {
|
||||
local url=$1
|
||||
local try=1
|
||||
[ -n "$2" ] && try=$2
|
||||
local timeout=2
|
||||
[ -n "$3" ] && timeout=$3
|
||||
local extra_params=$4
|
||||
curl --help all | grep "\-\-retry-all-errors" > /dev/null
|
||||
[ $? == 0 ] && extra_params="--retry-all-errors ${extra_params}"
|
||||
status=$(/usr/bin/curl -I -o /dev/null -skL $extra_params --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url")
|
||||
case "$status" in
|
||||
204|\
|
||||
200)
|
||||
status=200
|
||||
;;
|
||||
esac
|
||||
echo $status
|
||||
}
|
||||
|
||||
test_proxy() {
|
||||
result=0
|
||||
status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout})
|
||||
if [ "$status" = "200" ]; then
|
||||
result=0
|
||||
else
|
||||
status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout})
|
||||
if [ "$status2" = "200" ]; then
|
||||
result=1
|
||||
else
|
||||
result=2
|
||||
ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1
|
||||
[ $? -eq 0 ] && {
|
||||
result=1
|
||||
}
|
||||
fi
|
||||
fi
|
||||
echo $result
|
||||
}
|
||||
|
||||
url_test_node() {
|
||||
result=0
|
||||
local node_id=$1
|
||||
local _type=$(echo $(config_n_get ${node_id} type nil) | tr 'A-Z' 'a-z')
|
||||
[ "${_type}" != "nil" ] && {
|
||||
if [ "${_type}" == "socks" ]; then
|
||||
local _address=$(config_n_get ${node_id} address)
|
||||
local _port=$(config_n_get ${node_id} port)
|
||||
[ -n "${_address}" ] && [ -n "${_port}" ] && {
|
||||
local curlx="socks5h://${_address}:${_port}"
|
||||
local _username=$(config_n_get ${node_id} username)
|
||||
local _password=$(config_n_get ${node_id} password)
|
||||
[ -n "${_username}" ] && [ -n "${_password}" ] && curlx="socks5h://${_username}:${_password}@${_address}:${_port}"
|
||||
}
|
||||
else
|
||||
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp)
|
||||
/usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json
|
||||
local curlx="socks5h://127.0.0.1:${_tmp_port}"
|
||||
fi
|
||||
sleep 1s
|
||||
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204")
|
||||
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}.json"
|
||||
}
|
||||
echo $result
|
||||
}
|
||||
|
||||
test_node() {
|
||||
local node_id=$1
|
||||
local _type=$(echo $(config_n_get ${node_id} type nil) | tr 'A-Z' 'a-z')
|
||||
[ "${_type}" != "nil" ] && {
|
||||
if [ "${_type}" == "socks" ]; then
|
||||
local _address=$(config_n_get ${node_id} address)
|
||||
local _port=$(config_n_get ${node_id} port)
|
||||
[ -n "${_address}" ] && [ -n "${_port}" ] && {
|
||||
local curlx="socks5h://${_address}:${_port}"
|
||||
local _username=$(config_n_get ${node_id} username)
|
||||
local _password=$(config_n_get ${node_id} password)
|
||||
[ -n "${_username}" ] && [ -n "${_password}" ] && curlx="socks5h://${_username}:${_password}@${_address}:${_port}"
|
||||
}
|
||||
else
|
||||
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp)
|
||||
/usr/share/${CONFIG}/app.sh run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json
|
||||
local curlx="socks5h://127.0.0.1:${_tmp_port}"
|
||||
fi
|
||||
sleep 1s
|
||||
_proxy_status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout} "-x $curlx")
|
||||
pgrep -af "test_node_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
|
||||
rm -rf "/tmp/etc/${CONFIG}/test_node_${node_id}.json"
|
||||
if [ "${_proxy_status}" -eq 200 ]; then
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
flag=0
|
||||
main_node=$(config_t_get global tcp_node nil)
|
||||
|
||||
test_auto_switch() {
|
||||
flag=$(expr $flag + 1)
|
||||
local TYPE=$1
|
||||
local b_tcp_nodes=$2
|
||||
local now_node=$3
|
||||
[ -z "$now_node" ] && {
|
||||
if [ -f "/tmp/etc/$CONFIG/id/${TYPE}" ]; then
|
||||
now_node=$(cat /tmp/etc/$CONFIG/id/${TYPE})
|
||||
if [ "$(config_n_get $now_node protocol nil)" = "_shunt" ]; then
|
||||
if [ "$shunt_logic" == "1" ] && [ -f "/tmp/etc/$CONFIG/id/${TYPE}_default" ]; then
|
||||
now_node=$(cat /tmp/etc/$CONFIG/id/${TYPE}_default)
|
||||
elif [ "$shunt_logic" == "2" ] && [ -f "/tmp/etc/$CONFIG/id/${TYPE}_main" ]; then
|
||||
now_node=$(cat /tmp/etc/$CONFIG/id/${TYPE}_main)
|
||||
else
|
||||
shunt_logic=0
|
||||
fi
|
||||
else
|
||||
shunt_logic=0
|
||||
fi
|
||||
else
|
||||
#echolog "自动切换检测:未知错误"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
[ $flag -le 1 ] && {
|
||||
main_node=$now_node
|
||||
}
|
||||
|
||||
status=$(test_proxy)
|
||||
if [ "$status" == 2 ]; then
|
||||
echolog "自动切换检测:无法连接到网络,请检查网络是否正常!"
|
||||
return 2
|
||||
fi
|
||||
|
||||
#检测主节点是否能使用
|
||||
if [ "$restore_switch" == "1" ] && [ "$main_node" != "nil" ] && [ "$now_node" != "$main_node" ]; then
|
||||
test_node ${main_node}
|
||||
[ $? -eq 0 ] && {
|
||||
#主节点正常,切换到主节点
|
||||
echolog "自动切换检测:${TYPE}主节点【$(config_n_get $main_node type):[$(config_n_get $main_node remarks)]】正常,切换到主节点!"
|
||||
/usr/share/${CONFIG}/app.sh node_switch flag=${TYPE} new_node=${main_node} shunt_logic=${shunt_logic}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog "自动切换检测:${TYPE}节点切换完毕!"
|
||||
[ "$shunt_logic" != "0" ] && {
|
||||
local tcp_node=$(config_t_get global tcp_node nil)
|
||||
[ "$(config_n_get $tcp_node protocol nil)" = "_shunt" ] && {
|
||||
if [ "$shunt_logic" == "1" ]; then
|
||||
uci set $CONFIG.$tcp_node.default_node="$main_node"
|
||||
elif [ "$shunt_logic" == "2" ]; then
|
||||
uci set $CONFIG.$tcp_node.main_node="$main_node"
|
||||
fi
|
||||
uci commit $CONFIG
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
fi
|
||||
|
||||
if [ "$status" == 0 ]; then
|
||||
#echolog "自动切换检测:${TYPE}节点【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】正常。"
|
||||
return 0
|
||||
elif [ "$status" == 1 ]; then
|
||||
echolog "自动切换检测:${TYPE}节点【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】异常,切换到下一个备用节点检测!"
|
||||
local new_node
|
||||
in_backup_nodes=$(echo $b_tcp_nodes | grep $now_node)
|
||||
# 判断当前节点是否存在于备用节点列表里
|
||||
if [ -z "$in_backup_nodes" ]; then
|
||||
# 如果不存在,设置第一个节点为新的节点
|
||||
new_node=$(echo $b_tcp_nodes | awk -F ' ' '{print $1}')
|
||||
else
|
||||
# 如果存在,设置下一个备用节点为新的节点
|
||||
#local count=$(expr $(echo $b_tcp_nodes | grep -o ' ' | wc -l) + 1)
|
||||
local next_node=$(echo $b_tcp_nodes | awk -F "$now_node" '{print $2}' | awk -F " " '{print $1}')
|
||||
if [ -z "$next_node" ]; then
|
||||
new_node=$(echo $b_tcp_nodes | awk -F ' ' '{print $1}')
|
||||
else
|
||||
new_node=$next_node
|
||||
fi
|
||||
fi
|
||||
test_node ${new_node}
|
||||
if [ $? -eq 0 ]; then
|
||||
[ "$restore_switch" == "0" ] && {
|
||||
[ "$shunt_logic" == "0" ] && uci set $CONFIG.@global[0].tcp_node=$new_node
|
||||
[ -z "$(echo $b_tcp_nodes | grep $main_node)" ] && uci add_list $CONFIG.@auto_switch[0].tcp_node=$main_node
|
||||
uci commit $CONFIG
|
||||
}
|
||||
echolog "自动切换检测:${TYPE}节点【$(config_n_get $new_node type):[$(config_n_get $new_node remarks)]】正常,切换到此节点!"
|
||||
/usr/share/${CONFIG}/app.sh node_switch flag=${TYPE} new_node=${new_node} shunt_logic=${shunt_logic}
|
||||
[ $? -eq 0 ] && {
|
||||
[ "$restore_switch" == "1" ] && [ "$shunt_logic" != "0" ] && {
|
||||
local tcp_node=$(config_t_get global tcp_node nil)
|
||||
[ "$(config_n_get $tcp_node protocol nil)" = "_shunt" ] && {
|
||||
if [ "$shunt_logic" == "1" ]; then
|
||||
uci set $CONFIG.$tcp_node.default_node="$main_node"
|
||||
elif [ "$shunt_logic" == "2" ]; then
|
||||
uci set $CONFIG.$tcp_node.main_node="$main_node"
|
||||
fi
|
||||
uci commit $CONFIG
|
||||
}
|
||||
}
|
||||
echolog "自动切换检测:${TYPE}节点切换完毕!"
|
||||
}
|
||||
return 0
|
||||
else
|
||||
test_auto_switch ${TYPE} "${b_tcp_nodes}" ${new_node}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
ENABLED=$(config_t_get global enabled 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
ENABLED=$(config_t_get auto_switch enable 0)
|
||||
[ "$ENABLED" != 1 ] && return 1
|
||||
delay=$(config_t_get auto_switch testing_time 1)
|
||||
#sleep 9s
|
||||
connect_timeout=$(config_t_get auto_switch connect_timeout 3)
|
||||
retry_num=$(config_t_get auto_switch retry_num 3)
|
||||
restore_switch=$(config_t_get auto_switch restore_switch 0)
|
||||
shunt_logic=$(config_t_get auto_switch shunt_logic 0)
|
||||
while [ "$ENABLED" -eq 1 ]; do
|
||||
[ -f "$LOCK_FILE" ] && {
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
touch $LOCK_FILE
|
||||
TCP_NODE=$(config_t_get auto_switch tcp_node nil)
|
||||
[ -n "$TCP_NODE" -a "$TCP_NODE" != "nil" ] && {
|
||||
TCP_NODE=$(echo $TCP_NODE | tr -s ' ' '\n' | uniq | tr -s '\n' ' ')
|
||||
test_auto_switch TCP "$TCP_NODE"
|
||||
}
|
||||
rm -f $LOCK_FILE
|
||||
sleep ${delay}m
|
||||
done
|
||||
}
|
||||
|
||||
arg1=$1
|
||||
shift
|
||||
case $arg1 in
|
||||
test_url)
|
||||
test_url $@
|
||||
;;
|
||||
url_test_node)
|
||||
url_test_node $@
|
||||
;;
|
||||
*)
|
||||
start
|
||||
;;
|
||||
esac
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"luci-app-passwall": {
|
||||
"description": "Grant UCI access for luci-app-passwall",
|
||||
"read": {
|
||||
"uci": [ "passwall", "passwall_server" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "passwall", "passwall_server" ]
|
||||
}
|
||||
}
|
||||
}
|
119
luci-app-ssr-plus/Makefile
Normal file
119
luci-app-ssr-plus/Makefile
Normal file
@ -0,0 +1,119 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-ssr-plus
|
||||
PKG_VERSION:=185
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Kcptun \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_IPT2Socks \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Redsocks2 \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
|
||||
LUCI_TITLE:=SS/SSR/V2Ray/Trojan/NaiveProxy/Socks5/Tun LuCI interface
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:= \
|
||||
@(PACKAGE_libustream-mbedtls||PACKAGE_libustream-openssl||PACKAGE_libustream-wolfssl) \
|
||||
+coreutils +coreutils-base64 +dns2socks +dnsmasq-full +ipset +kmod-ipt-nat \
|
||||
+ip-full +iptables-mod-tproxy +lua +libuci-lua +microsocks +pdnsd-alt \
|
||||
+tcping +resolveip +shadowsocksr-libev-ssr-check +uclient-fetch \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Kcptun:kcptun-client \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_IPT2Socks:ipt2socks \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Redsocks2:redsocks2 \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-local \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-redir \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server:shadowsocks-libev-ss-server \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client:shadowsocks-rust-sslocal \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server:shadowsocks-rust-ssserver \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-redir \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server:shadowsocksr-libev-ssr-server \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs:simple-obfs \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan:trojan \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin:v2ray-plugin \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:curl \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray-core
|
||||
|
||||
define Package/$(PKG_NAME)/config
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Kcptun
|
||||
bool "Include Kcptun"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
|
||||
bool "Include NaiveProxy"
|
||||
depends on !(arc||armeb||mips||mips64||powerpc||TARGET_gemini)
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_IPT2Socks
|
||||
bool "Include ipt2socks"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Redsocks2
|
||||
bool "Include Redsocks2"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client
|
||||
bool "Include Shadowsocks Libev Client"
|
||||
default y if i386||x86_64||arm
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server
|
||||
bool "Include Shadowsocks Libev Server"
|
||||
default y if i386||x86_64||arm
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client
|
||||
bool "Include Shadowsocks Rust Client"
|
||||
depends on aarch64||arm||i386||mips||mipsel||x86_64
|
||||
depends on !(TARGET_x86_geode||TARGET_x86_legacy)
|
||||
default y if aarch64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server
|
||||
bool "Include Shadowsocks Rust Server"
|
||||
depends on aarch64||arm||i386||mips||mipsel||x86_64
|
||||
depends on !(TARGET_x86_geode||TARGET_x86_legacy)
|
||||
default y if aarch64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client
|
||||
bool "Include ShadowsocksR Libev Client"
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server
|
||||
bool "Include ShadowsocksR Libev Server"
|
||||
default y if i386||x86_64||arm
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs
|
||||
bool "Include Shadowsocks Simple Obfs Plugin"
|
||||
default y if i386||x86_64||arm
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan
|
||||
bool "Include Trojan"
|
||||
select PACKAGE_$(PKG_NAME)_INCLUDE_IPT2Socks
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
||||
bool "Include Shadowsocks V2ray Plugin"
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
bool "Include Xray"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/shadowsocksr
|
||||
/etc/ssrplus/
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
143
luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua
Normal file
143
luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua
Normal file
@ -0,0 +1,143 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
module("luci.controller.shadowsocksr", package.seeall)
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/shadowsocksr") then
|
||||
call("act_reset")
|
||||
end
|
||||
local page
|
||||
page = entry({"admin", "services", "shadowsocksr"}, alias("admin", "services", "shadowsocksr", "client"), _("ShadowSocksR Plus+"), 10)
|
||||
page.dependent = true
|
||||
page.acl_depends = { "luci-app-ssr-plus" }
|
||||
entry({"admin", "services", "shadowsocksr", "client"}, cbi("shadowsocksr/client"), _("SSR Client"), 10).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "servers"}, arcombine(cbi("shadowsocksr/servers", {autoapply = true}), cbi("shadowsocksr/client-config")), _("Severs Nodes"), 20).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "control"}, cbi("shadowsocksr/control"), _("Access Control"), 30).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "advanced"}, cbi("shadowsocksr/advanced"), _("Advanced Settings"), 50).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "server"}, arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")), _("SSR Server"), 60).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "status"}, form("shadowsocksr/status"), _("Status"), 70).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "check"}, call("check_status"))
|
||||
entry({"admin", "services", "shadowsocksr", "refresh"}, call("refresh_data"))
|
||||
entry({"admin", "services", "shadowsocksr", "subscribe"}, call("subscribe"))
|
||||
entry({"admin", "services", "shadowsocksr", "checkport"}, call("check_port"))
|
||||
entry({"admin", "services", "shadowsocksr", "log"}, form("shadowsocksr/log"), _("Log"), 80).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "run"}, call("act_status"))
|
||||
entry({"admin", "services", "shadowsocksr", "ping"}, call("act_ping"))
|
||||
entry({"admin", "services", "shadowsocksr", "reset"}, call("act_reset"))
|
||||
entry({"admin", "services", "shadowsocksr", "restart"}, call("act_restart"))
|
||||
entry({"admin", "services", "shadowsocksr", "delete"}, call("act_delete"))
|
||||
entry({"admin", "services", "shadowsocksr", "cache"}, call("act_cache"))
|
||||
end
|
||||
|
||||
function subscribe()
|
||||
luci.sys.call("/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >>/var/log/ssrplus.log")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ret = 1})
|
||||
end
|
||||
|
||||
function act_status()
|
||||
local e = {}
|
||||
e.running = luci.sys.call("busybox ps -w | grep ssr-retcp | grep -v grep >/dev/null") == 0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
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 ss_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
|
||||
end
|
||||
if (iret == 0) then
|
||||
luci.sys.call(" ipset del ss_spec_wan_ac " .. domain)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function check_status()
|
||||
local e = {}
|
||||
e.ret = luci.sys.call("/usr/bin/ssr-check www." .. luci.http.formvalue("set") .. ".com 80 3 1")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function refresh_data()
|
||||
local set = luci.http.formvalue("set")
|
||||
local retstring = loadstring("return " .. luci.sys.exec("/usr/bin/lua /usr/share/shadowsocksr/update.lua " .. set))()
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(retstring)
|
||||
end
|
||||
|
||||
function check_port()
|
||||
local retstring = "<br /><br />"
|
||||
local s
|
||||
local server_name = ""
|
||||
local uci = luci.model.uci.cursor()
|
||||
local iret = 1
|
||||
uci:foreach("shadowsocksr", "servers", function(s)
|
||||
if s.alias then
|
||||
server_name = s.alias
|
||||
elseif s.server and s.server_port then
|
||||
server_name = "%s:%s" % {s.server, s.server_port}
|
||||
end
|
||||
iret = luci.sys.call("ipset add ss_spec_wan_ac " .. s.server .. " 2>/dev/null")
|
||||
socket = nixio.socket("inet", "stream")
|
||||
socket:setopt("socket", "rcvtimeo", 3)
|
||||
socket:setopt("socket", "sndtimeo", 3)
|
||||
ret = socket:connect(s.server, s.server_port)
|
||||
if tostring(ret) == "true" then
|
||||
socket:close()
|
||||
retstring = retstring .. "<font color = 'green'>[" .. server_name .. "] OK.</font><br />"
|
||||
else
|
||||
retstring = retstring .. "<font color = 'red'>[" .. server_name .. "] Error.</font><br />"
|
||||
end
|
||||
if iret == 0 then
|
||||
luci.sys.call("ipset del ss_spec_wan_ac " .. s.server)
|
||||
end
|
||||
end)
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ret = retstring})
|
||||
end
|
||||
|
||||
function act_reset()
|
||||
luci.sys.call("/etc/init.d/shadowsocksr reset &")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr"))
|
||||
end
|
||||
|
||||
function act_restart()
|
||||
luci.sys.call("/etc/init.d/shadowsocksr restart &")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr"))
|
||||
end
|
||||
|
||||
function act_delete()
|
||||
luci.sys.call("/etc/init.d/shadowsocksr restart &")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
|
||||
end
|
||||
|
||||
function act_cache()
|
||||
local e = {}
|
||||
e.ret = luci.sys.call("pdnsd-ctl -c /var/etc/ssrplus/pdnsd empty-cache >/dev/null")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
102
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua
Normal file
102
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua
Normal file
@ -0,0 +1,102 @@
|
||||
local uci = luci.model.uci.cursor()
|
||||
local server_table = {}
|
||||
|
||||
uci:foreach("shadowsocksr", "servers", function(s)
|
||||
if s.alias then
|
||||
server_table[s[".name"]] = "[%s]:%s" % {string.upper(s.v2ray_protocol or s.type), s.alias}
|
||||
elseif s.server and s.server_port then
|
||||
server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.v2ray_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("shadowsocksr")
|
||||
-- [[ global ]]--
|
||||
s = m:section(TypedSection, "global", translate("Server failsafe auto swith and custom update settings"))
|
||||
s.anonymous = true
|
||||
|
||||
-- o = s:option(Flag, "monitor_enable", translate("Enable Process Deamon"))
|
||||
-- o.rmempty = false
|
||||
-- o.default = "1"
|
||||
|
||||
o = s:option(Flag, "enable_switch", translate("Enable Auto Switch"))
|
||||
o.rmempty = false
|
||||
o.default = "1"
|
||||
|
||||
o = s:option(Value, "switch_time", translate("Switch check cycly(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("enable_switch", "1")
|
||||
o.default = 667
|
||||
|
||||
o = s:option(Value, "switch_timeout", translate("Check timout(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("enable_switch", "1")
|
||||
o.default = 5
|
||||
|
||||
o = s:option(Value, "switch_try_count", translate("Check Try Count"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("enable_switch", "1")
|
||||
o.default = 3
|
||||
|
||||
o = s:option(Value, "gfwlist_url", translate("gfwlist Update url"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
|
||||
o.default = "https://cdn.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt"
|
||||
|
||||
o = s:option(Value, "chnroute_url", translate("Chnroute Update url"))
|
||||
o:value("https://ispip.clang.cn/all_cn.txt", translate("Clang.CN"))
|
||||
o:value("https://ispip.clang.cn/all_cn_cidr.txt", translate("Clang.CN.CIDR"))
|
||||
o.default = "https://ispip.clang.cn/all_cn.txt"
|
||||
|
||||
o = s:option(Flag, "netflix_enable", translate("Enable Netflix Mode"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "nfip_url", translate("nfip_url"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt", translate("Netflix IP Only"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/getflix.txt", translate("Netflix and AWS"))
|
||||
o.default = "https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt"
|
||||
o.description = translate("Customize Netflix IP Url")
|
||||
o:depends("netflix_enable", "1")
|
||||
|
||||
o = s:option(Flag, "adblock", translate("Enable adblock"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "adblock_url", translate("adblock_url"))
|
||||
o:value("https://raw.githubusercontent.com/neodevpro/neodevhost/master/lite_dnsmasq.conf", translate("NEO DEV HOST Lite"))
|
||||
o:value("https://raw.githubusercontent.com/neodevpro/neodevhost/master/dnsmasq.conf", translate("NEO DEV HOST Full"))
|
||||
o:value("https://anti-ad.net/anti-ad-for-dnsmasq.conf", translate("anti-AD"))
|
||||
o.default = "https://raw.githubusercontent.com/neodevpro/neodevhost/master/lite_dnsmasq.conf"
|
||||
o:depends("adblock", "1")
|
||||
o.description = translate("Support AdGuardHome and DNSMASQ format list")
|
||||
|
||||
o = s:option(Button, "reset", translate("Reset to defaults"))
|
||||
o.rawhtml = true
|
||||
o.template = "shadowsocksr/reset"
|
||||
|
||||
-- [[ SOCKS5 Proxy ]]--
|
||||
s = m:section(TypedSection, "socks5_proxy", translate("Global SOCKS5 Proxy Server"))
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(ListValue, "server", translate("Server"))
|
||||
o:value("nil", translate("Disable"))
|
||||
o:value("same", translate("Same as Global Server"))
|
||||
for _, key in pairs(key_table) do
|
||||
o:value(key, server_table[key])
|
||||
end
|
||||
o.default = "nil"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "local_port", translate("Local Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 1080
|
||||
o.rmempty = false
|
||||
|
||||
return m
|
@ -0,0 +1,655 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
require "nixio.fs"
|
||||
require "luci.sys"
|
||||
require "luci.http"
|
||||
local m, s, o, kcp_enable
|
||||
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 = {
|
||||
-- ssr
|
||||
"none",
|
||||
"table",
|
||||
"rc4",
|
||||
"rc4-md5-6",
|
||||
"rc4-md5",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"cast5-cfb",
|
||||
"des-cfb",
|
||||
"idea-cfb",
|
||||
"rc2-cfb",
|
||||
"seed-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf"
|
||||
}
|
||||
|
||||
local encrypt_methods_ss = {
|
||||
-- aead
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
--[[ stream
|
||||
"none",
|
||||
"plain",
|
||||
"table",
|
||||
"rc4",
|
||||
"rc4-md5",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf" ]]
|
||||
}
|
||||
|
||||
local encrypt_methods_v2ray_ss = {
|
||||
-- xray_ss
|
||||
"none",
|
||||
"plain",
|
||||
-- aead
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-poly1305",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305",
|
||||
"aead_aes_128_gcm",
|
||||
"aead_aes_256_gcm",
|
||||
"aead_chacha20_poly1305",
|
||||
"aead_xchacha20_poly1305"
|
||||
}
|
||||
|
||||
local protocol = {
|
||||
-- ssr
|
||||
"origin",
|
||||
"verify_deflate",
|
||||
"auth_sha1_v4",
|
||||
"auth_aes128_sha1",
|
||||
"auth_aes128_md5",
|
||||
"auth_chain_a",
|
||||
"auth_chain_b",
|
||||
"auth_chain_c",
|
||||
"auth_chain_d",
|
||||
"auth_chain_e",
|
||||
"auth_chain_f"
|
||||
}
|
||||
|
||||
obfs = {
|
||||
-- ssr
|
||||
"plain",
|
||||
"http_simple",
|
||||
"http_post",
|
||||
"random_head",
|
||||
"tls1.2_ticket_auth"
|
||||
}
|
||||
|
||||
local securitys = {
|
||||
-- vmess
|
||||
"auto",
|
||||
"none",
|
||||
"zero",
|
||||
"aes-128-gcm",
|
||||
"chacha20-poly1305"
|
||||
}
|
||||
|
||||
local flows = {
|
||||
-- xlts
|
||||
"xtls-rprx-origin",
|
||||
"xtls-rprx-origin-udp443",
|
||||
"xtls-rprx-direct",
|
||||
"xtls-rprx-direct-udp443",
|
||||
"xtls-rprx-splice",
|
||||
"xtls-rprx-splice-udp443"
|
||||
}
|
||||
|
||||
m = Map("shadowsocksr", translate("Edit ShadowSocksR Server"))
|
||||
m.redirect = luci.dispatcher.build_url("admin/services/shadowsocksr/servers")
|
||||
if m.uci:get("shadowsocksr", sid) ~= "servers" then
|
||||
luci.http.redirect(m.redirect)
|
||||
return
|
||||
end
|
||||
|
||||
-- [[ Servers Setting ]]--
|
||||
s = m:section(NamedSection, sid, "servers")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(DummyValue, "ssr_url", "SS/SSR/V2RAY/TROJAN URL")
|
||||
o.rawhtml = true
|
||||
o.template = "shadowsocksr/ssrurl"
|
||||
o.value = sid
|
||||
|
||||
o = s:option(ListValue, "type", translate("Server Node Type"))
|
||||
if is_finded("xray") or is_finded("v2ray") then
|
||||
o:value("v2ray", translate("V2Ray/XRay"))
|
||||
end
|
||||
if is_finded("ssr-redir") then
|
||||
o:value("ssr", translate("ShadowsocksR"))
|
||||
end
|
||||
if is_finded("sslocal") or is_finded("ss-redir") then
|
||||
o:value("ss", translate("Shadowsocks New Version"))
|
||||
end
|
||||
if is_finded("trojan") then
|
||||
o:value("trojan", translate("Trojan"))
|
||||
end
|
||||
if is_finded("naive") then
|
||||
o:value("naiveproxy", translate("NaiveProxy"))
|
||||
end
|
||||
if is_finded("ipt2socks") then
|
||||
o:value("socks5", translate("Socks5"))
|
||||
end
|
||||
if is_finded("redsocks2") then
|
||||
o:value("tun", translate("Network Tunnel"))
|
||||
end
|
||||
|
||||
o.description = translate("Using incorrect encryption mothod may causes service fail to start")
|
||||
|
||||
o = s:option(Value, "alias", translate("Alias(optional)"))
|
||||
|
||||
o = s:option(ListValue, "iface", translate("Network interface to use"))
|
||||
for _, e in ipairs(luci.sys.net.devices()) do
|
||||
if e ~= "lo" then
|
||||
o:value(e)
|
||||
end
|
||||
end
|
||||
o:depends("type", "tun")
|
||||
o.description = translate("Redirect traffic to this network interface")
|
||||
|
||||
o = s:option(ListValue, "v2ray_protocol", translate("V2Ray/XRay protocol"))
|
||||
o:value("vless", translate("VLESS"))
|
||||
o:value("vmess", translate("VMess"))
|
||||
o:value("trojan", translate("Trojan"))
|
||||
o:value("shadowsocks", translate("Shadowsocks"))
|
||||
o:value("socks", translate("Socks"))
|
||||
o:value("http", translate("HTTP"))
|
||||
o:depends("type", "v2ray")
|
||||
|
||||
o = s:option(Value, "server", translate("Server Address"))
|
||||
o.datatype = "host"
|
||||
o.rmempty = false
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "v2ray")
|
||||
o:depends("type", "trojan")
|
||||
o:depends("type", "naiveproxy")
|
||||
o:depends("type", "socks5")
|
||||
|
||||
o = s:option(Value, "server_port", translate("Server Port"))
|
||||
o.datatype = "port"
|
||||
o.rmempty = false
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "v2ray")
|
||||
o:depends("type", "trojan")
|
||||
o:depends("type", "naiveproxy")
|
||||
o:depends("type", "socks5")
|
||||
|
||||
o = s:option(Flag, "auth_enable", translate("Enable Authentication"))
|
||||
o.rmempty = false
|
||||
o.default = "0"
|
||||
o:depends("type", "socks5")
|
||||
o:depends({type = "v2ray", v2ray_protocol = "http"})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "socks"})
|
||||
|
||||
o = s:option(Value, "username", translate("Username"))
|
||||
o.rmempty = true
|
||||
o:depends("type", "naiveproxy")
|
||||
o:depends({type = "socks5", auth_enable = true})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "http", auth_enable = true})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "socks", auth_enable = true})
|
||||
|
||||
o = s:option(Value, "password", translate("Password"))
|
||||
o.password = true
|
||||
o.rmempty = true
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "trojan")
|
||||
o:depends("type", "naiveproxy")
|
||||
o:depends({type = "socks5", auth_enable = true})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "http", auth_enable = true})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "socks", auth_enable = true})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "trojan"})
|
||||
|
||||
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(ListValue, "encrypt_method_ss", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("type", "ss")
|
||||
|
||||
o = s:option(ListValue, "encrypt_method_v2ray_ss", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_v2ray_ss) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"})
|
||||
|
||||
o = s:option(Flag, "ivCheck", translate("Bloom Filter"))
|
||||
o.rmempty = true
|
||||
o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"})
|
||||
o.default = "1"
|
||||
|
||||
-- Shadowsocks Plugin
|
||||
o = s:option(Value, "plugin", translate("Obfs"))
|
||||
o:value("none", translate("None"))
|
||||
if is_finded("obfs-local") then
|
||||
o:value("obfs-local", translate("obfs-local"))
|
||||
end
|
||||
if is_finded("v2ray-plugin") then
|
||||
o:value("v2ray-plugin", translate("v2ray-plugin"))
|
||||
end
|
||||
if is_finded("xray-plugin") then
|
||||
o:value("xray-plugin", translate("xray-plugin"))
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("type", "ss")
|
||||
|
||||
o = s:option(Value, "plugin_opts", translate("Plugin Opts"))
|
||||
o.rmempty = true
|
||||
o:depends({type = "ss", plugin = "obfs-local"})
|
||||
o:depends({type = "ss", plugin = "v2ray-plugin"})
|
||||
o:depends({type = "ss", plugin = "xray-plugin"})
|
||||
|
||||
o = s:option(ListValue, "protocol", translate("Protocol"))
|
||||
for _, v in ipairs(protocol) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(Value, "protocol_param", translate("Protocol param(optional)"))
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(ListValue, "obfs", translate("Obfs"))
|
||||
for _, v in ipairs(obfs) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(Value, "obfs_param", translate("Obfs param(optional)"))
|
||||
o:depends("type", "ssr")
|
||||
|
||||
-- VmessId
|
||||
o = s:option(Value, "vmess_id", translate("Vmess/VLESS ID (UUID)"))
|
||||
o.rmempty = true
|
||||
o.default = uuid
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vmess"})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless"})
|
||||
|
||||
-- VLESS Encryption
|
||||
o = s:option(Value, "vless_encryption", translate("VLESS Encryption"))
|
||||
o.rmempty = true
|
||||
o.default = "none"
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless"})
|
||||
|
||||
-- 加密方式
|
||||
o = s:option(ListValue, "security", translate("Encrypt Method"))
|
||||
for _, v in ipairs(securitys) do
|
||||
o:value(v, v:upper())
|
||||
end
|
||||
o.rmempty = true
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vmess"})
|
||||
|
||||
-- 传输协议
|
||||
o = s:option(ListValue, "transport", translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("kcp", "mKCP")
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("h2", "HTTP/2")
|
||||
o:value("quic", "QUIC")
|
||||
o:value("grpc", "gRPC")
|
||||
o.rmempty = true
|
||||
o:depends("type", "v2ray")
|
||||
|
||||
-- [[ TCP部分 ]]--
|
||||
-- TCP伪装
|
||||
o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
|
||||
o:depends("transport", "tcp")
|
||||
o:value("none", translate("None"))
|
||||
o:value("http", "HTTP")
|
||||
o.rmempty = true
|
||||
|
||||
-- HTTP域名
|
||||
o = s:option(Value, "http_host", translate("HTTP Host"))
|
||||
o:depends("tcp_guise", "http")
|
||||
o.rmempty = true
|
||||
|
||||
-- HTTP路径
|
||||
o = s:option(Value, "http_path", translate("HTTP Path"))
|
||||
o:depends("tcp_guise", "http")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ WS部分 ]]--
|
||||
-- WS域名
|
||||
o = s:option(Value, "ws_host", translate("WebSocket Host"))
|
||||
o:depends({transport = "ws", tls = false})
|
||||
o.datatype = "hostname"
|
||||
o.rmempty = true
|
||||
|
||||
-- WS路径
|
||||
o = s:option(Value, "ws_path", translate("WebSocket Path"))
|
||||
o:depends("transport", "ws")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ H2部分 ]]--
|
||||
|
||||
-- H2域名
|
||||
o = s:option(Value, "h2_host", translate("HTTP/2 Host"))
|
||||
o:depends("transport", "h2")
|
||||
o.rmempty = true
|
||||
|
||||
-- H2路径
|
||||
o = s:option(Value, "h2_path", translate("HTTP/2 Path"))
|
||||
o:depends("transport", "h2")
|
||||
o.rmempty = true
|
||||
|
||||
-- gRPC
|
||||
o = s:option(Value, "serviceName", translate("serviceName"))
|
||||
o:depends("transport", "grpc")
|
||||
o.rmempty = true
|
||||
|
||||
-- gRPC初始窗口
|
||||
o = s:option(Value, "initial_windows_size", translate("Initial Windows Size"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "grpc")
|
||||
o.default = 0
|
||||
o.rmempty = true
|
||||
|
||||
-- H2/gRPC健康检查
|
||||
o = s:option(Flag, "health_check", translate("H2/gRPC Health Check"))
|
||||
o:depends("transport", "h2")
|
||||
o:depends("transport", "grpc")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "read_idle_timeout", translate("H2 Read Idle Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends({health_check = true, transport = "h2"})
|
||||
o.default = 60
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "idle_timeout", translate("gRPC 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", translate("Health Check Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("health_check", 1)
|
||||
o.default = 20
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "permit_without_stream", translate("Permit Without Stream"))
|
||||
o:depends({health_check = true, transport = "grpc"})
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ QUIC部分 ]]--
|
||||
o = s:option(ListValue, "quic_security", translate("QUIC Security"))
|
||||
o:depends("transport", "quic")
|
||||
o:value("none", translate("None"))
|
||||
o:value("aes-128-gcm", translate("aes-128-gcm"))
|
||||
o:value("chacha20-poly1305", translate("chacha20-poly1305"))
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "quic_key", translate("QUIC Key"))
|
||||
o:depends("transport", "quic")
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(ListValue, "quic_guise", translate("Header"))
|
||||
o:depends("transport", "quic")
|
||||
o.rmempty = true
|
||||
o:value("none", translate("None"))
|
||||
o:value("srtp", translate("VideoCall (SRTP)"))
|
||||
o:value("utp", translate("BitTorrent (uTP)"))
|
||||
o:value("wechat-video", translate("WechatVideo"))
|
||||
o:value("dtls", translate("DTLS 1.2"))
|
||||
o:value("wireguard", translate("WireGuard"))
|
||||
|
||||
-- [[ mKCP部分 ]]--
|
||||
o = s:option(ListValue, "kcp_guise", translate("Camouflage Type"))
|
||||
o:depends("transport", "kcp")
|
||||
o:value("none", translate("None"))
|
||||
o:value("srtp", translate("VideoCall (SRTP)"))
|
||||
o:value("utp", translate("BitTorrent (uTP)"))
|
||||
o:value("wechat-video", translate("WechatVideo"))
|
||||
o:value("dtls", translate("DTLS 1.2"))
|
||||
o:value("wireguard", translate("WireGuard"))
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "mtu", translate("MTU"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 1350
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "tti", translate("TTI"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 50
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "uplink_capacity", translate("Uplink Capacity"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 5
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "downlink_capacity", translate("Downlink Capacity"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 20
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "read_buffer_size", translate("Read Buffer Size"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 2
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "write_buffer_size", translate("Write Buffer Size"))
|
||||
o.datatype = "uinteger"
|
||||
o:depends("transport", "kcp")
|
||||
o.default = 2
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "seed", translate("Obfuscate password (optional)"))
|
||||
o:depends({v2ray_protocol = "vless", transport = "kcp"})
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Flag, "congestion", translate("Congestion"))
|
||||
o:depends("transport", "kcp")
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ TLS ]]--
|
||||
o = s:option(Flag, "tls", translate("TLS"))
|
||||
o.rmempty = true
|
||||
o.default = "0"
|
||||
o:depends({type = "v2ray", xtls = false})
|
||||
-- o:depends({type = "v2ray", v2ray_protocol = "vless", xtls = false})
|
||||
o:depends("type", "trojan")
|
||||
|
||||
-- XTLS
|
||||
if is_finded("xray") then
|
||||
o = s:option(Flag, "xtls", translate("XTLS"))
|
||||
o.rmempty = true
|
||||
o.default = "0"
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "tcp", tls = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "kcp", tls = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "trojan", transport = "tcp", tls = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "trojan", transport = "kcp", tls = false})
|
||||
end
|
||||
|
||||
-- Flow
|
||||
o = s:option(Value, "vless_flow", translate("Flow"))
|
||||
for _, v in ipairs(flows) do
|
||||
o:value(v, translate(v))
|
||||
end
|
||||
o.rmempty = true
|
||||
o.default = "xtls-rprx-splice"
|
||||
o:depends("xtls", true)
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
o = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
|
||||
o:depends({type = "trojan", tls = true})
|
||||
o.default = "0"
|
||||
|
||||
-- [[ uTLS ]]--
|
||||
o = s:option(ListValue, "fingerprint", translate("Finger Print"))
|
||||
o:value("disable", translate("disable"))
|
||||
o:value("firefox", translate("firefox"))
|
||||
o:value("chrome", translate("chrome"))
|
||||
o:value("safari", translate("safari"))
|
||||
o:value("randomized", translate("randomized"))
|
||||
o:depends({type = "v2ray", tls = true})
|
||||
o.default = "disable"
|
||||
|
||||
o = s:option(Value, "tls_host", translate("TLS Host"))
|
||||
o.datatype = "hostname"
|
||||
o:depends("tls", true)
|
||||
o:depends("xtls", true)
|
||||
o.rmempty = true
|
||||
|
||||
-- [[ allowInsecure ]]--
|
||||
o = s:option(Flag, "insecure", translate("allowInsecure"))
|
||||
o.rmempty = false
|
||||
o:depends("tls", true)
|
||||
o:depends("xtls", true)
|
||||
o.description = translate("If true, allowss insecure connection at TLS client, e.g., TLS server uses unverifiable certificates.")
|
||||
|
||||
-- [[ Mux ]]--
|
||||
o = s:option(Flag, "mux", translate("Mux"))
|
||||
o.rmempty = false
|
||||
o:depends({type = "v2ray", xtls = false})
|
||||
|
||||
o = s:option(Value, "concurrency", translate("Concurrency"))
|
||||
o.datatype = "uinteger"
|
||||
o.rmempty = true
|
||||
o.default = "4"
|
||||
o:depends("mux", "1")
|
||||
o:depends("type", "naiveproxy")
|
||||
|
||||
-- [[ Cert ]]--
|
||||
o = s:option(Flag, "certificate", translate("Self-signed Certificate"))
|
||||
o.rmempty = true
|
||||
o.default = "0"
|
||||
o:depends({type = "trojan", tls = true, insecure = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vmess", tls = true, insecure = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless", tls = true, insecure = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vmess", xtls = true, insecure = false})
|
||||
o:depends({type = "v2ray", v2ray_protocol = "vless", xtls = true, insecure = false})
|
||||
o.description = translate("If you have a self-signed certificate,please check the box")
|
||||
|
||||
o = s:option(DummyValue, "upload", translate("Upload"))
|
||||
o.template = "shadowsocksr/certupload"
|
||||
o:depends("certificate", 1)
|
||||
|
||||
cert_dir = "/etc/ssl/private/"
|
||||
local path
|
||||
|
||||
luci.http.setfilehandler(function(meta, chunk, eof)
|
||||
if not fd then
|
||||
if (not meta) or (not meta.name) or (not meta.file) then
|
||||
return
|
||||
end
|
||||
fd = nixio.open(cert_dir .. meta.file, "w")
|
||||
if not fd then
|
||||
path = translate("Create upload file error.")
|
||||
return
|
||||
end
|
||||
end
|
||||
if chunk and fd then
|
||||
fd:write(chunk)
|
||||
end
|
||||
if eof and fd then
|
||||
fd:close()
|
||||
fd = nil
|
||||
path = '/etc/ssl/private/' .. meta.file .. ''
|
||||
end
|
||||
end)
|
||||
if luci.http.formvalue("upload") then
|
||||
local f = luci.http.formvalue("ulfile")
|
||||
if #f <= 0 then
|
||||
path = translate("No specify upload file.")
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(Value, "certpath", translate("Current Certificate Path"))
|
||||
o:depends("certificate", 1)
|
||||
o:value("/etc/ssl/private/ca.pem")
|
||||
o.description = translate("Please confirm the current certificate path")
|
||||
o.default = "/etc/ssl/private/ca.pem"
|
||||
|
||||
o = s:option(Flag, "fast_open", translate("TCP Fast Open"))
|
||||
o.rmempty = true
|
||||
o.default = "0"
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "trojan")
|
||||
|
||||
o = s:option(Flag, "switch_enable", translate("Enable Auto Switch"))
|
||||
o.rmempty = false
|
||||
o.default = "1"
|
||||
|
||||
o = s:option(Value, "local_port", translate("Local Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 1234
|
||||
o.rmempty = false
|
||||
|
||||
if is_finded("kcptun-client") then
|
||||
kcp_enable = s:option(Flag, "kcp_enable", translate("KcpTun Enable"))
|
||||
kcp_enable.rmempty = true
|
||||
kcp_enable.default = "0"
|
||||
kcp_enable:depends("type", "ssr")
|
||||
kcp_enable:depends("type", "ss")
|
||||
|
||||
o = s:option(Value, "kcp_port", translate("KcpTun Port"))
|
||||
o.datatype = "port"
|
||||
o.default = 4000
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
|
||||
o = s:option(Value, "kcp_password", translate("KcpTun Password"))
|
||||
o.password = true
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
|
||||
o = s:option(Value, "kcp_param", translate("KcpTun Param"))
|
||||
o.default = "--nocomp"
|
||||
o:depends("type", "ssr")
|
||||
o:depends("type", "ss")
|
||||
end
|
||||
|
||||
return m
|
112
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua
Normal file
112
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua
Normal file
@ -0,0 +1,112 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
|
||||
-- Copyright (C) 2018 lean <coolsnowwolf@gmail.com> github.com/coolsnowwolf
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
local m, s, sec, o, kcp_enable
|
||||
local uci = luci.model.uci.cursor()
|
||||
m = Map("shadowsocksr", translate("ShadowSocksR Plus+ Settings"))
|
||||
|
||||
m:section(SimpleSection).template = "shadowsocksr/status"
|
||||
|
||||
local server_table = {}
|
||||
uci:foreach("shadowsocksr", "servers", function(s)
|
||||
if s.alias then
|
||||
server_table[s[".name"]] = "[%s]:%s" % {string.upper(s.v2ray_protocol or s.type), s.alias}
|
||||
elseif s.server and s.server_port then
|
||||
server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.v2ray_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)
|
||||
|
||||
-- [[ Global Setting ]]--
|
||||
s = m:section(TypedSection, "global")
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(ListValue, "global_server", translate("Main Server"))
|
||||
o:value("nil", translate("Disable"))
|
||||
for _, key in pairs(key_table) do
|
||||
o:value(key, server_table[key])
|
||||
end
|
||||
o.default = "nil"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "udp_relay_server", translate("Game Mode UDP Server"))
|
||||
o:value("", translate("Disable"))
|
||||
o:value("same", translate("Same as Global Server"))
|
||||
for _, key in pairs(key_table) do
|
||||
o:value(key, server_table[key])
|
||||
end
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'netflix_enable', '0') ~= '0' then
|
||||
o = s:option(ListValue, "netflix_server", translate("Netflix Node"))
|
||||
o:value("nil", translate("Disable"))
|
||||
o:value("same", translate("Same as Global Server"))
|
||||
for _, key in pairs(key_table) do
|
||||
o:value(key, server_table[key])
|
||||
end
|
||||
o.default = "nil"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Flag, "netflix_proxy", translate("External Proxy Mode"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Forward Netflix Proxy through Main Proxy")
|
||||
o.default = "0"
|
||||
end
|
||||
|
||||
o = s:option(ListValue, "threads", translate("Multi Threads Option"))
|
||||
o:value("0", translate("Auto Threads"))
|
||||
o:value("1", translate("1 Thread"))
|
||||
o:value("2", translate("2 Threads"))
|
||||
o:value("4", translate("4 Threads"))
|
||||
o:value("8", translate("8 Threads"))
|
||||
o:value("16", translate("16 Threads"))
|
||||
o:value("32", translate("32 Threads"))
|
||||
o:value("64", translate("64 Threads"))
|
||||
o:value("128", translate("128 Threads"))
|
||||
o.default = "0"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "run_mode", translate("Running Mode"))
|
||||
o:value("gfw", translate("GFW List Mode"))
|
||||
o:value("router", translate("IP Route Mode"))
|
||||
o:value("all", translate("Global Mode"))
|
||||
o:value("oversea", translate("Oversea Mode"))
|
||||
o.default = gfw
|
||||
|
||||
o = s:option(ListValue, "dports", translate("Proxy Ports"))
|
||||
o:value("1", translate("All Ports"))
|
||||
o:value("2", translate("Only Common Ports"))
|
||||
o.default = 1
|
||||
|
||||
o = s:option(ListValue, "pdnsd_enable", translate("Resolve Dns Mode"))
|
||||
o:value("1", translate("Use Pdnsd tcp query and cache"))
|
||||
o:value("2", translate("Use DNS2SOCKS query and cache"))
|
||||
o:value("0", translate("Use Local DNS Service listen port 5335"))
|
||||
o.default = 1
|
||||
|
||||
o = s:option(Value, "tunnel_forward", translate("Anti-pollution DNS Server"))
|
||||
o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)"))
|
||||
o:value("8.8.8.8:53", translate("Google Public DNS (8.8.8.8)"))
|
||||
o:value("208.67.222.222:53", translate("OpenDNS (208.67.222.222)"))
|
||||
o:value("208.67.220.220:53", translate("OpenDNS (208.67.220.220)"))
|
||||
o:value("209.244.0.3:53", translate("Level 3 Public DNS (209.244.0.3)"))
|
||||
o:value("209.244.0.4:53", translate("Level 3 Public DNS (209.244.0.4)"))
|
||||
o:value("4.2.2.1:53", translate("Level 3 Public DNS (4.2.2.1)"))
|
||||
o:value("4.2.2.2:53", translate("Level 3 Public DNS (4.2.2.2)"))
|
||||
o:value("4.2.2.3:53", translate("Level 3 Public DNS (4.2.2.3)"))
|
||||
o:value("4.2.2.4:53", translate("Level 3 Public DNS (4.2.2.4)"))
|
||||
o:value("1.1.1.1:53", translate("Cloudflare DNS (1.1.1.1)"))
|
||||
o:value("114.114.114.114:53", translate("Oversea Mode DNS-1 (114.114.114.114)"))
|
||||
o:value("114.114.115.115:53", translate("Oversea Mode DNS-2 (114.114.115.115)"))
|
||||
o:depends("pdnsd_enable", "1")
|
||||
o:depends("pdnsd_enable", "2")
|
||||
o.description = translate("Custom DNS Server format as IP:PORT (default: 8.8.4.4:53)")
|
||||
o.datatype = "hostport"
|
||||
|
||||
return m
|
||||
|
143
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua
Normal file
143
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua
Normal file
@ -0,0 +1,143 @@
|
||||
require "luci.ip"
|
||||
require "nixio.fs"
|
||||
local m, s, o
|
||||
|
||||
m = Map("shadowsocksr")
|
||||
|
||||
s = m:section(TypedSection, "access_control")
|
||||
s.anonymous = true
|
||||
|
||||
-- Interface control
|
||||
s:tab("Interface", translate("Interface control"))
|
||||
o = s:taboption("Interface", DynamicList, "Interface", translate("Interface"))
|
||||
o.template = "cbi/network_netlist"
|
||||
o.widget = "checkbox"
|
||||
o.nocreate = true
|
||||
o.unspecified = true
|
||||
o.description = translate("Listen only on the given interface or, if unspecified, on all")
|
||||
|
||||
-- Part of WAN
|
||||
s:tab("wan_ac", translate("WAN IP AC"))
|
||||
|
||||
o = s:taboption("wan_ac", DynamicList, "wan_bp_ips", translate("WAN White List IP"))
|
||||
o.datatype = "ip4addr"
|
||||
|
||||
o = s:taboption("wan_ac", DynamicList, "wan_fw_ips", translate("WAN Force Proxy IP"))
|
||||
o.datatype = "ip4addr"
|
||||
|
||||
-- Part of LAN
|
||||
s:tab("lan_ac", translate("LAN IP AC"))
|
||||
|
||||
o = s:taboption("lan_ac", ListValue, "lan_ac_mode", translate("LAN Access Control"))
|
||||
o:value("0", translate("Disable"))
|
||||
o:value("w", translate("Allow listed only"))
|
||||
o:value("b", translate("Allow all except listed"))
|
||||
o.rmempty = false
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_ac_ips", translate("LAN 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", "w")
|
||||
o:depends("lan_ac_mode", "b")
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_bp_ips", translate("LAN Bypassed Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
end)
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_fp_ips", translate("LAN Force Proxy Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
end)
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_gm_ips", translate("Game Mode Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
end)
|
||||
|
||||
-- Part of Self
|
||||
-- s:tab("self_ac", translate("Router Self AC"))
|
||||
-- o = s:taboption("self_ac",ListValue, "router_proxy", translate("Router Self Proxy"))
|
||||
-- o:value("1", translatef("Normal Proxy"))
|
||||
-- o:value("0", translatef("Bypassed Proxy"))
|
||||
-- o:value("2", translatef("Forwarded Proxy"))
|
||||
-- o.rmempty = false
|
||||
|
||||
s:tab("esc", translate("Bypass Domain List"))
|
||||
local escconf = "/etc/ssrplus/white.list"
|
||||
o = s:taboption("esc", TextValue, "escconf")
|
||||
o.rows = 13
|
||||
o.wrap = "off"
|
||||
o.rmempty = true
|
||||
o.cfgvalue = function(self, section)
|
||||
return nixio.fs.readfile(escconf) or ""
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
nixio.fs.writefile(escconf, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
nixio.fs.writefile(escconf, "")
|
||||
end
|
||||
|
||||
s:tab("block", translate("Black Domain List"))
|
||||
local blockconf = "/etc/ssrplus/black.list"
|
||||
o = s:taboption("block", TextValue, "blockconf")
|
||||
o.rows = 13
|
||||
o.wrap = "off"
|
||||
o.rmempty = true
|
||||
o.cfgvalue = function(self, section)
|
||||
return nixio.fs.readfile(blockconf) or " "
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
nixio.fs.writefile(blockconf, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
nixio.fs.writefile(blockconf, "")
|
||||
end
|
||||
|
||||
s:tab("denydomain", translate("Deny Domain List"))
|
||||
local denydomainconf = "/etc/ssrplus/deny.list"
|
||||
o = s:taboption("denydomain", TextValue, "denydomainconf")
|
||||
o.rows = 13
|
||||
o.wrap = "off"
|
||||
o.rmempty = true
|
||||
o.cfgvalue = function(self, section)
|
||||
return nixio.fs.readfile(denydomainconf) or " "
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
nixio.fs.writefile(denydomainconf, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
nixio.fs.writefile(denydomainconf, "")
|
||||
end
|
||||
|
||||
s:tab("netflix", translate("Netflix Domain List"))
|
||||
local netflixconf = "/etc/ssrplus/netflix.list"
|
||||
o = s:taboption("netflix", TextValue, "netflixconf")
|
||||
o.rows = 13
|
||||
o.wrap = "off"
|
||||
o.rmempty = true
|
||||
o.cfgvalue = function(self, section)
|
||||
return nixio.fs.readfile(netflixconf) or " "
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
nixio.fs.writefile(netflixconf, value:gsub("\r\n", "\n"))
|
||||
end
|
||||
o.remove = function(self, section, value)
|
||||
nixio.fs.writefile(netflixconf, "")
|
||||
end
|
||||
|
||||
return m
|
20
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua
Normal file
20
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua
Normal file
@ -0,0 +1,20 @@
|
||||
require "luci.util"
|
||||
require "nixio.fs"
|
||||
f = SimpleForm("logview")
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
t = f:field(TextValue, "conf")
|
||||
t.rmempty = true
|
||||
t.rows = 20
|
||||
function t.cfgvalue()
|
||||
if nixio.fs.access("/var/log/ssrplus.log") then
|
||||
local logs = luci.util.execi("cat /var/log/ssrplus.log")
|
||||
local s = ""
|
||||
for line in logs do
|
||||
s = line .. "\n" .. s
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
t.readonly = "readonly"
|
||||
return f
|
@ -0,0 +1,150 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
require "luci.http"
|
||||
require "luci.dispatcher"
|
||||
require "nixio.fs"
|
||||
|
||||
local m, s, o
|
||||
local sid = arg[1]
|
||||
|
||||
local encrypt_methods = {
|
||||
"rc4-md5",
|
||||
"rc4-md5-6",
|
||||
"rc4",
|
||||
"table",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"cast5-cfb",
|
||||
"des-cfb",
|
||||
"idea-cfb",
|
||||
"rc2-cfb",
|
||||
"seed-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf"
|
||||
}
|
||||
|
||||
local encrypt_methods_ss = {
|
||||
-- aead
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
--[[ stream
|
||||
"table",
|
||||
"rc4",
|
||||
"rc4-md5",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf" ]]
|
||||
}
|
||||
|
||||
local protocol = {"origin"}
|
||||
|
||||
obfs = {"plain", "http_simple", "http_post"}
|
||||
|
||||
m = Map("shadowsocksr", translate("Edit ShadowSocksR Server"))
|
||||
|
||||
m.redirect = luci.dispatcher.build_url("admin/services/shadowsocksr/server")
|
||||
if m.uci:get("shadowsocksr", sid) ~= "server_config" then
|
||||
luci.http.redirect(m.redirect)
|
||||
return
|
||||
end
|
||||
|
||||
-- [[ Server Setting ]]--
|
||||
s = m:section(NamedSection, sid, "server_config")
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(Flag, "enable", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "type", translate("Server Type"))
|
||||
o:value("socks5", translate("Socks5"))
|
||||
if nixio.fs.access("/usr/bin/ssserver") or nixio.fs.access("/usr/bin/ss-server") then
|
||||
o:value("ss", translate("Shadowsocks"))
|
||||
end
|
||||
if nixio.fs.access("/usr/bin/ssr-server") then
|
||||
o:value("ssr", translate("ShadowsocksR"))
|
||||
end
|
||||
o.default = "socks5"
|
||||
|
||||
o = s:option(Value, "server_port", translate("Server Port"))
|
||||
o.datatype = "port"
|
||||
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
|
||||
o.default = math.random(10240, 20480)
|
||||
o.rmempty = false
|
||||
o.description = translate("warning! Please do not reuse the port!")
|
||||
|
||||
o = s:option(Value, "timeout", translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 60
|
||||
o.rmempty = false
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(Value, "username", translate("Username"))
|
||||
o.rmempty = false
|
||||
o:depends("type", "socks5")
|
||||
|
||||
o = s:option(Value, "password", translate("Password"))
|
||||
o.password = true
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = false
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(ListValue, "encrypt_method_ss", translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = false
|
||||
o:depends("type", "ss")
|
||||
|
||||
o = s:option(ListValue, "protocol", translate("Protocol"))
|
||||
for _, v in ipairs(protocol) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = false
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(ListValue, "obfs", translate("Obfs"))
|
||||
for _, v in ipairs(obfs) do
|
||||
o:value(v)
|
||||
end
|
||||
o.rmempty = false
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(Value, "obfs_param", translate("Obfs param(optional)"))
|
||||
o:depends("type", "ssr")
|
||||
|
||||
o = s:option(Flag, "fast_open", translate("TCP Fast Open"))
|
||||
o.rmempty = false
|
||||
o:depends("type", "ss")
|
||||
o:depends("type", "ssr")
|
||||
|
||||
return m
|
140
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua
Normal file
140
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua
Normal file
@ -0,0 +1,140 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
require "luci.http"
|
||||
require "luci.dispatcher"
|
||||
local m, sec, o
|
||||
local encrypt_methods = {
|
||||
"table",
|
||||
"rc4",
|
||||
"rc4-md5",
|
||||
"rc4-md5-6",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"cast5-cfb",
|
||||
"des-cfb",
|
||||
"idea-cfb",
|
||||
"rc2-cfb",
|
||||
"seed-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf"
|
||||
}
|
||||
|
||||
local encrypt_methods_ss = {
|
||||
-- aead
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"chacha20-ietf-poly1305",
|
||||
"xchacha20-ietf-poly1305"
|
||||
--[[ stream
|
||||
"table",
|
||||
"rc4",
|
||||
"rc4-md5",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf" ]]
|
||||
}
|
||||
|
||||
local protocol = {
|
||||
"origin",
|
||||
"verify_deflate",
|
||||
"auth_sha1_v4",
|
||||
"auth_aes128_sha1",
|
||||
"auth_aes128_md5",
|
||||
"auth_chain_a"
|
||||
}
|
||||
|
||||
obfs = {
|
||||
"plain",
|
||||
"http_simple",
|
||||
"http_post",
|
||||
"random_head",
|
||||
"tls1.2_ticket_auth",
|
||||
"tls1.2_ticket_fastauth"
|
||||
}
|
||||
|
||||
m = Map("shadowsocksr")
|
||||
-- [[ Global Setting ]]--
|
||||
sec = m:section(TypedSection, "server_global", translate("Global Setting"))
|
||||
sec.anonymous = true
|
||||
|
||||
o = sec:option(Flag, "enable_server", translate("Enable Server"))
|
||||
o.rmempty = false
|
||||
|
||||
-- [[ Server Setting ]]--
|
||||
sec = m:section(TypedSection, "server_config", translate("Server Setting"))
|
||||
sec.anonymous = true
|
||||
sec.addremove = true
|
||||
sec.template = "cbi/tblsection"
|
||||
sec.extedit = luci.dispatcher.build_url("admin/services/shadowsocksr/server/%s")
|
||||
function sec.create(...)
|
||||
local sid = TypedSection.create(...)
|
||||
if sid then
|
||||
luci.http.redirect(sec.extedit % sid)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = sec:option(Flag, "enable", translate("Enable"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or translate("0")
|
||||
end
|
||||
o.rmempty = false
|
||||
|
||||
o = sec:option(DummyValue, "type", translate("Server Type"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "ss"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "server_port", translate("Server Port"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "-"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "username", translate("Username"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "-"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "encrypt_method", translate("Encrypt Method"))
|
||||
function o.cfgvalue(...)
|
||||
local v = Value.cfgvalue(...)
|
||||
return v and v:upper() or "-"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "encrypt_method_ss", translate("Encrypt Method"))
|
||||
function o.cfgvalue(...)
|
||||
local v = Value.cfgvalue(...)
|
||||
return v and v:upper() or "-"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "protocol", translate("Protocol"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "-"
|
||||
end
|
||||
|
||||
o = sec:option(DummyValue, "obfs", translate("Obfs"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "-"
|
||||
end
|
||||
|
||||
return m
|
151
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua
Normal file
151
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua
Normal file
@ -0,0 +1,151 @@
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
require "luci.http"
|
||||
require "luci.dispatcher"
|
||||
require "luci.model.uci"
|
||||
local m, s, o
|
||||
local uci = luci.model.uci.cursor()
|
||||
local server_count = 0
|
||||
uci:foreach("shadowsocksr", "servers", function(s)
|
||||
server_count = server_count + 1
|
||||
end)
|
||||
|
||||
m = Map("shadowsocksr", translate("Servers subscription and manage"))
|
||||
|
||||
-- Server Subscribe
|
||||
s = m:section(TypedSection, "server_subscribe")
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(Flag, "auto_update", translate("Auto Update"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Auto Update Server subscription, GFW list and CHN route")
|
||||
|
||||
o = s:option(ListValue, "auto_update_time", translate("Update time (every day)"))
|
||||
for t = 0, 23 do
|
||||
o:value(t, t .. ":00")
|
||||
end
|
||||
o.default = 2
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL"))
|
||||
o.rmempty = true
|
||||
|
||||
o = s:option(Value, "filter_words", translate("Subscribe Filter Words"))
|
||||
o.rmempty = true
|
||||
o.description = translate("Filter Words splited by /")
|
||||
|
||||
o = s:option(Value, "save_words", translate("Subscribe Save Words"))
|
||||
o.rmempty = true
|
||||
o.description = translate("Save Words splited by /")
|
||||
|
||||
o = s:option(Button, "update_Sub", translate("Update Subscribe List"))
|
||||
o.inputstyle = "reload"
|
||||
o.description = translate("Update subscribe url list first")
|
||||
o.write = function()
|
||||
uci:commit("shadowsocksr")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
|
||||
end
|
||||
|
||||
o = s:option(Flag, "switch", translate("Subscribe Default Auto-Switch"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Subscribe new add server default Auto-Switch on")
|
||||
o.default = "1"
|
||||
|
||||
o = s:option(Flag, "proxy", translate("Through proxy update"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Through proxy update list, Not Recommended ")
|
||||
|
||||
o = s:option(Button, "subscribe", translate("Update All Subscribe Severs"))
|
||||
o.rawhtml = true
|
||||
o.template = "shadowsocksr/subscribe"
|
||||
|
||||
o = s:option(Button, "delete", translate("Delete All Subscribe Severs"))
|
||||
o.inputstyle = "reset"
|
||||
o.description = string.format(translate("Server Count") .. ": %d", server_count)
|
||||
o.write = function()
|
||||
uci:delete_all("shadowsocksr", "servers", function(s)
|
||||
if s.hashkey or s.isSubscribe then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end)
|
||||
uci:save("shadowsocksr")
|
||||
uci:commit("shadowsocksr")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "delete"))
|
||||
return
|
||||
end
|
||||
|
||||
-- [[ Servers Manage ]]--
|
||||
s = m:section(TypedSection, "servers")
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = true
|
||||
s.extedit = luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers", "%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", translate("Type"))
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "v2ray_protocol") or Value.cfgvalue(self, section) or translate("None")
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "alias", translate("Alias"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or translate("None")
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "server_port", translate("Server Port"))
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "N/A"
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "server_port", translate("Socket Connected"))
|
||||
o.template = "shadowsocksr/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
|
||||
|
||||
o = s:option(DummyValue, "server", translate("Ping Latency"))
|
||||
o.template = "shadowsocksr/ping"
|
||||
o.width = "10%"
|
||||
|
||||
local global_server = uci:get_first('shadowsocksr', 'global', 'global_server')
|
||||
|
||||
node = s:option(Button, "apply_node", translate("Apply"))
|
||||
node.inputstyle = "apply"
|
||||
node.render = function(self, section, scope)
|
||||
if section == global_server then
|
||||
self.title = translate("Reapply")
|
||||
else
|
||||
self.title = translate("Apply")
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
node.write = function(self, section)
|
||||
uci:set("shadowsocksr", '@global[0]', 'global_server', section)
|
||||
uci:save("shadowsocksr")
|
||||
uci:commit("shadowsocksr")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "restart"))
|
||||
end
|
||||
|
||||
o = s:option(Flag, "switch_enable", translate("Auto Switch"))
|
||||
o.rmempty = false
|
||||
function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or 1
|
||||
end
|
||||
|
||||
m:append(Template("shadowsocksr/server_list"))
|
||||
|
||||
return m
|
195
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua
Normal file
195
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua
Normal file
@ -0,0 +1,195 @@
|
||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||
-- Licensed to the public under the GNU General Public License v3.
|
||||
require "nixio.fs"
|
||||
require "luci.sys"
|
||||
require "luci.model.uci"
|
||||
local m, s, o
|
||||
local redir_run = 0
|
||||
local reudp_run = 0
|
||||
local sock5_run = 0
|
||||
local server_run = 0
|
||||
local kcptun_run = 0
|
||||
local tunnel_run = 0
|
||||
local gfw_count = 0
|
||||
local ad_count = 0
|
||||
local ip_count = 0
|
||||
local nfip_count = 0
|
||||
local Process_list = luci.sys.exec("busybox ps -w")
|
||||
local uci = luci.model.uci.cursor()
|
||||
-- html constants
|
||||
font_blue = [[<b style=color:green>]]
|
||||
style_blue = [[<b style=color:red>]]
|
||||
font_off = [[</b>]]
|
||||
bold_on = [[<strong>]]
|
||||
bold_off = [[</strong>]]
|
||||
local kcptun_version = translate("Unknown")
|
||||
local kcp_file = "/usr/bin/kcptun-client"
|
||||
if not nixio.fs.access(kcp_file) then
|
||||
kcptun_version = translate("Not exist")
|
||||
else
|
||||
if not nixio.fs.access(kcp_file, "rwx", "rx", "rx") then
|
||||
nixio.fs.chmod(kcp_file, 755)
|
||||
end
|
||||
kcptun_version = "<b>" ..luci.sys.exec(kcp_file .. " -v | awk '{printf $3}'") .. "</b>"
|
||||
if not kcptun_version or kcptun_version == "" then
|
||||
kcptun_version = translate("Unknown")
|
||||
end
|
||||
end
|
||||
|
||||
if nixio.fs.access("/etc/ssrplus/gfw_list.conf") then
|
||||
gfw_count = tonumber(luci.sys.exec("cat /etc/ssrplus/gfw_list.conf | wc -l")) / 2
|
||||
end
|
||||
|
||||
if nixio.fs.access("/etc/ssrplus/ad.conf") then
|
||||
ad_count = tonumber(luci.sys.exec("cat /etc/ssrplus/ad.conf | wc -l"))
|
||||
end
|
||||
|
||||
if nixio.fs.access("/etc/ssrplus/china_ssr.txt") then
|
||||
ip_count = tonumber(luci.sys.exec("cat /etc/ssrplus/china_ssr.txt | wc -l"))
|
||||
end
|
||||
|
||||
if nixio.fs.access("/etc/ssrplus/netflixip.list") then
|
||||
nfip_count = tonumber(luci.sys.exec("cat /etc/ssrplus/netflixip.list | wc -l"))
|
||||
end
|
||||
|
||||
if Process_list:find("udp.only.ssr.reudp") then
|
||||
reudp_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("tcp.only.ssr.retcp") then
|
||||
redir_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("tcp.udp.ssr.local") then
|
||||
sock5_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("tcp.udp.ssr.retcp") then
|
||||
redir_run = 1
|
||||
reudp_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("local.ssr.retcp") then
|
||||
redir_run = 1
|
||||
sock5_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("local.udp.ssr.retcp") then
|
||||
reudp_run = 1
|
||||
redir_run = 1
|
||||
sock5_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("kcptun.client") then
|
||||
kcptun_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("ssr.server") then
|
||||
server_run = 1
|
||||
end
|
||||
|
||||
if Process_list:find("ssrplus/bin/pdnsd") or (Process_list:find("ssrplus.dns") and Process_list:find("dns2socks.127.0.0.1.*127.0.0.1.5335")) then
|
||||
pdnsd_run = 1
|
||||
end
|
||||
|
||||
m = SimpleForm("Version")
|
||||
m.reset = false
|
||||
m.submit = false
|
||||
|
||||
s = m:field(DummyValue, "redir_run", translate("Global Client"))
|
||||
s.rawhtml = true
|
||||
if redir_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
|
||||
s = m:field(DummyValue, "reudp_run", translate("Game Mode UDP Relay"))
|
||||
s.rawhtml = true
|
||||
if reudp_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'pdnsd_enable', '0') ~= '0' then
|
||||
s = m:field(DummyValue, "pdnsd_run", translate("DNS Anti-pollution"))
|
||||
s.rawhtml = true
|
||||
if pdnsd_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
end
|
||||
|
||||
s = m:field(DummyValue, "sock5_run", translate("Global SOCKS5 Proxy Server"))
|
||||
s.rawhtml = true
|
||||
if sock5_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
|
||||
s = m:field(DummyValue, "server_run", translate("Local Servers"))
|
||||
s.rawhtml = true
|
||||
if server_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
|
||||
if nixio.fs.access("/usr/bin/kcptun-client") then
|
||||
s = m:field(DummyValue, "kcp_version", translate("KcpTun Version"))
|
||||
s.rawhtml = true
|
||||
s.value = kcptun_version
|
||||
s = m:field(DummyValue, "kcptun_run", translate("KcpTun"))
|
||||
s.rawhtml = true
|
||||
if kcptun_run == 1 then
|
||||
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
|
||||
else
|
||||
s.value = style_blue .. bold_on .. translate("Not Running") .. bold_off .. font_off
|
||||
end
|
||||
end
|
||||
|
||||
s = m:field(DummyValue, "google", translate("Google Connectivity"))
|
||||
s.value = translate("No Check")
|
||||
s.template = "shadowsocksr/check"
|
||||
|
||||
s = m:field(DummyValue, "baidu", translate("Baidu Connectivity"))
|
||||
s.value = translate("No Check")
|
||||
s.template = "shadowsocksr/check"
|
||||
|
||||
s = m:field(DummyValue, "gfw_data", translate("GFW List Data"))
|
||||
s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = gfw_count .. " " .. translate("Records")
|
||||
|
||||
s = m:field(DummyValue, "ip_data", translate("China IP Data"))
|
||||
s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = ip_count .. " " .. translate("Records")
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'netflix_enable', '0') ~= '0' then
|
||||
s = m:field(DummyValue, "nfip_data", translate("Netflix IP Data"))
|
||||
s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = nfip_count .. " " .. translate("Records")
|
||||
end
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'adblock', '0') == '1' then
|
||||
s = m:field(DummyValue, "ad_data", translate("Advertising Data"))
|
||||
s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = ad_count .. " " .. translate("Records")
|
||||
end
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'pdnsd_enable', '0') == '1' then
|
||||
s = m:field(DummyValue, "cache", translate("Reset pdnsd cache"))
|
||||
s.template = "shadowsocksr/cache"
|
||||
end
|
||||
|
||||
s = m:field(DummyValue, "check_port", translate("Check Server Port"))
|
||||
s.template = "shadowsocksr/checkport"
|
||||
s.value = translate("No Check")
|
||||
|
||||
return m
|
29
luci-app-ssr-plus/luasrc/view/shadowsocksr/cache.htm
Normal file
29
luci-app-ssr-plus/luasrc/view/shadowsocksr/cache.htm
Normal file
@ -0,0 +1,29 @@
|
||||
<%+cbi/valueheader%>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
function cache(btn,urlname)
|
||||
{
|
||||
btn.disabled = true;
|
||||
btn.value = '<%:Perform reset%>';
|
||||
murl=urlname;
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr","cache")%>',
|
||||
{ set:murl },
|
||||
function(x,rv)
|
||||
{
|
||||
var s = document.getElementById(urlname+'-status');
|
||||
if (s)
|
||||
{
|
||||
if (rv.ret=="0")
|
||||
s.innerHTML ="<font color='green'>"+"<%:Reset complete%>"+"</font>";
|
||||
else
|
||||
s.innerHTML ="<font color='red'>"+"<%:Reset Error%>"+"</font>";
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.value = '<%:Perform reset%>';
|
||||
}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
//]]></script>
|
||||
<input type="button" class="btn cbi-button cbi-button-reset" value="<%:Perform reset%>" onclick="return cache(this,'<%=self.option%>')" />
|
||||
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
|
||||
<%+cbi/valuefooter%>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user