mirror of
https://github.com/kenzok8/small.git
synced 2025-01-09 12:38:15 +08:00
update 2023-09-12 23:31:11
This commit is contained in:
parent
74f4d48114
commit
f9241b3635
@ -1,61 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021-2023 sirpdboy <herboy2008@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ddns-go
|
||||
PKG_VERSION:=5.6.2
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/jeessy2/ddns-go.git
|
||||
PKG_MIRROR_HASH:=ac74c1ef96688838ffeab18dd011616b49b156c533cecf859eeee01e5c1a6e40
|
||||
PKG_SOURCE_VERSION:=5b77583b498dc2e893cd77533b5a15c0e1620aef
|
||||
|
||||
PKG_LICENSE:=AGPL-3.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=herboy2008 <herboy2008@gmail.com>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/jeessy2/ddns-go/v5
|
||||
GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/ddns-go
|
||||
TITLE:=A Linux web GUI client of ddns-go
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Web Servers/Proxies
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
URL:=https://github.com/jeessy2/ddns-go
|
||||
USERID:=ddns-go:ddns-go
|
||||
endef
|
||||
|
||||
define Package/ddns-go/description
|
||||
ddns-go is a automatically obtain your public IPv4 or IPv6 address and resolve it to the corresponding domain name service,
|
||||
support Alidns Dnspod Cloudflare Hicloud Callback Baiducloud porkbun GoDaddy Google Domains.
|
||||
endef
|
||||
|
||||
|
||||
define Package/ddns-go/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./file/ddns-go.init $(1)/etc/init.d/ddns-go
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./file/luci-ddns-go.uci-default $(1)/etc/uci-defaults/luci-ddns-go
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,ddns-go))
|
||||
$(eval $(call BuildPackage,ddns-go))
|
@ -1,46 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
#
|
||||
# Copyright (C) 2021-2023 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-ddns-go
|
||||
#
|
||||
# This file is part of ddns-go .
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
|
||||
START=99
|
||||
USE_PROCD=1
|
||||
|
||||
PROG=/usr/bin/ddns-go
|
||||
CONFDIR=/etc/ddns-go
|
||||
CONF=$CONFDIR/ddns-go-config.yaml
|
||||
|
||||
get_config() {
|
||||
config_get_bool enabled $1 enabled 1
|
||||
config_get_bool logger $1 logger 1
|
||||
config_get port $1 port 9876
|
||||
config_get time $1 time 300
|
||||
}
|
||||
|
||||
init_yaml(){
|
||||
[ -d $CONFDIR ] || mkdir -p $CONFDIR 2>/dev/null
|
||||
cat /usr/share/ddns-go/ddns-go-default.yaml > $CONF
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load ddns-go
|
||||
config_foreach get_config basic
|
||||
[ x$enabled == x1 ] || return 1
|
||||
[ -s ${CONF} ] || init_yaml
|
||||
logger -t ddns-go -p warn "ddns-go is start."
|
||||
echo "ddns-go is start."
|
||||
procd_open_instance
|
||||
procd_set_param command $PROG -l :$port -f $time -c "$CONF"
|
||||
[ "x$logger" == x1 ] && procd_set_param stderr 1
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "ddns-go"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -s "/etc/ddns-go/localtime" ] && mv -f /etc/ddns-go/localtime /etc/localtime
|
||||
/etc/init.d/ddns-go enable
|
||||
/etc/init.d/ddns-go start
|
||||
rm -f /tmp/luci*
|
||||
exit 0
|
@ -1,191 +0,0 @@
|
||||
# Copyright (C) 2018-2020 L-WRT Team
|
||||
# Copyright (C) 2021-2023 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.70-4
|
||||
PKG_RELEASE:=
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Brook \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
|
||||
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_SingBox \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata \
|
||||
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 \
|
||||
+chinadns-ng +dns2socks +dns2tcp +ip-full +libuci-lua +lua +luci-compat +luci-lib-jsonc \
|
||||
+microsocks +resolveip +tcping +unzip \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Brook:brook \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy:haproxy \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \
|
||||
+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_SingBox:sing-box \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO:trojan-go \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus:trojan-plus \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client:tuic-client \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata:v2ray-geoip \
|
||||
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata:v2ray-geosite \
|
||||
+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)_Iptables_Transparent_Proxy
|
||||
bool "Iptables Transparent Proxy"
|
||||
select PACKAGE_dnsmasq-full
|
||||
select PACKAGE_ipset
|
||||
select PACKAGE_ipt2socks
|
||||
select PACKAGE_iptables
|
||||
select PACKAGE_iptables-zz-legacy
|
||||
select PACKAGE_iptables-mod-conntrack-extra
|
||||
select PACKAGE_iptables-mod-iprange
|
||||
select PACKAGE_iptables-mod-socket
|
||||
select PACKAGE_iptables-mod-tproxy
|
||||
select PACKAGE_kmod-ipt-nat
|
||||
depends on PACKAGE_$(PKG_NAME)
|
||||
default y if ! PACKAGE_firewall4
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy
|
||||
bool "Nftables Transparent Proxy"
|
||||
select PACKAGE_dnsmasq-full
|
||||
select PACKAGE_ipt2socks
|
||||
select PACKAGE_nftables
|
||||
select PACKAGE_kmod-nft-socket
|
||||
select PACKAGE_kmod-nft-tproxy
|
||||
select PACKAGE_kmod-nft-nat
|
||||
depends on PACKAGE_$(PKG_NAME)
|
||||
default y if PACKAGE_firewall4
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Brook
|
||||
bool "Include Brook"
|
||||
default n
|
||||
|
||||
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_NaiveProxy
|
||||
bool "Include NaiveProxy"
|
||||
depends on !(arc||(arm&&TARGET_gemini)||armeb||mips||mips64||powerpc)
|
||||
default n
|
||||
|
||||
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_Shadowsocks_Rust_Server
|
||||
bool "Include Shadowsocks Rust Server"
|
||||
depends on aarch64||arm||i386||mips||mipsel||x86_64
|
||||
default n
|
||||
|
||||
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_SingBox
|
||||
bool "Include Sing-Box"
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
|
||||
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_tuic_client
|
||||
bool "Include tuic-client"
|
||||
depends on aarch64||arm||i386||x86_64
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata
|
||||
bool "Include V2ray_Geodata"
|
||||
default n
|
||||
|
||||
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
|
@ -1,431 +0,0 @@
|
||||
-- Copyright (C) 2018-2020 L-WRT Team
|
||||
-- Copyright (C) 2021-2023 xiaorouji
|
||||
|
||||
module("luci.controller.passwall", package.seeall)
|
||||
local api = require "luci.passwall.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"
|
||||
|
||||
function index()
|
||||
appname = require "luci.passwall.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, "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, "socks_config"}, cbi(appname .. "/client/socks_config")).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, "socks_autoswitch_add_node"}, call("socks_autoswitch_add_node")).leaf = true
|
||||
entry({"admin", "services", appname, "socks_autoswitch_remove_node"}, call("socks_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
|
||||
|
||||
--[[Components update]]
|
||||
entry({"admin", "services", appname, "check_passwall"}, call("app_check")).leaf = true
|
||||
local coms = require "luci.passwall.com"
|
||||
local com
|
||||
for com, _ in pairs(coms) do
|
||||
entry({"admin", "services", appname, "check_" .. com}, call("com_check", com)).leaf = true
|
||||
entry({"admin", "services", appname, "update_" .. com}, call("com_update", com)).leaf = true
|
||||
end
|
||||
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 socks_autoswitch_add_node()
|
||||
local id = luci.http.formvalue("id")
|
||||
local key = luci.http.formvalue("key")
|
||||
if id and id ~= "" and key and key ~= "" then
|
||||
local new_list = ucic:get(appname, id, "autoswitch_backup_node") or {}
|
||||
for i = #new_list, 1, -1 do
|
||||
if (ucic:get(appname, new_list[i], "remarks") or ""):find(key) then
|
||||
table.remove(new_list, i)
|
||||
end
|
||||
end
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and e["remark"]:find(key) then
|
||||
table.insert(new_list, e.id)
|
||||
end
|
||||
end
|
||||
ucic:set_list(appname, id, "autoswitch_backup_node", new_list)
|
||||
ucic:commit(appname)
|
||||
end
|
||||
luci.http.redirect(api.url("socks_config", id))
|
||||
end
|
||||
|
||||
function socks_autoswitch_remove_node()
|
||||
local id = luci.http.formvalue("id")
|
||||
local key = luci.http.formvalue("key")
|
||||
if id and id ~= "" and key and key ~= "" then
|
||||
local new_list = ucic:get(appname, id, "autoswitch_backup_node") or {}
|
||||
for i = #new_list, 1, -1 do
|
||||
if (ucic:get(appname, new_list[i], "remarks") or ""):find(key) then
|
||||
table.remove(new_list, i)
|
||||
end
|
||||
end
|
||||
ucic:set_list(appname, id, "autoswitch_backup_node", new_list)
|
||||
ucic:commit(appname)
|
||||
end
|
||||
luci.http.redirect(api.url("socks_config", id))
|
||||
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 ':15353 ' >/dev/null") == 0
|
||||
e.haproxy_status = luci.sys.call(string.format("top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
|
||||
e["tcp_node_status"] = luci.sys.call(string.format("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("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("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("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("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 -sk -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_short_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:foreach(appname, "socks", function(t)
|
||||
ucic:delete(appname, t[".name"])
|
||||
ucic:set_list(appname, t[".name"], "autoswitch_backup_node", {})
|
||||
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")
|
||||
string.gsub(ids, '[^' .. "," .. ']+', function(w)
|
||||
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
|
||||
local auto_switch_node_list = ucic:get(appname, t[".name"], "autoswitch_backup_node") or {}
|
||||
for i = #auto_switch_node_list, 1, -1 do
|
||||
if w == auto_switch_node_list[i] then
|
||||
table.remove(auto_switch_node_list, i)
|
||||
end
|
||||
end
|
||||
ucic:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list)
|
||||
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("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 app_check()
|
||||
local json = api.to_check_self()
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function com_check(comname)
|
||||
local json = api.to_check("",comname)
|
||||
http_write_json(json)
|
||||
end
|
||||
|
||||
function com_update(comname)
|
||||
local json = nil
|
||||
local task = http.formvalue("task")
|
||||
if task == "extract" then
|
||||
json = api.to_extract(comname, http.formvalue("file"), http.formvalue("subfix"))
|
||||
elseif task == "move" then
|
||||
json = api.to_move(comname, http.formvalue("file"))
|
||||
else
|
||||
json = api.to_download(comname, http.formvalue("url"), http.formvalue("size"))
|
||||
end
|
||||
|
||||
http_write_json(json)
|
||||
end
|
@ -1,130 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local has_chnlist = api.fs.access("/usr/share/passwall/rules/chnlist")
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
s = m:section(TypedSection, "global", translate("ACLs"), "<font color='red'>" .. translate("ACLs is a tools which used to designate specific IP proxy mode.") .. "</font>")
|
||||
s.anonymous = true
|
||||
|
||||
o = s:option(Flag, "acl_enable", translate("Main switch"))
|
||||
o.rmempty = false
|
||||
o.default = false
|
||||
|
||||
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")
|
||||
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", "TCP " .. translate("Proxy Mode"))
|
||||
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", "UDP " .. translate("Proxy Mode"))
|
||||
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
|
@ -1,336 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local sys = api.sys
|
||||
local has_singbox = api.finded_com("singbox")
|
||||
local has_xray = api.finded_com("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 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 Proxy Mode
|
||||
tcp_proxy_mode = s:option(ListValue, "tcp_proxy_mode", "TCP " .. translate("Proxy Mode"))
|
||||
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", "UDP " .. translate("Proxy Mode"))
|
||||
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_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
|
||||
|
||||
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
|
||||
o.default = "0"
|
||||
o:depends({ tcp_node = "default", ['!reverse'] = true })
|
||||
|
||||
---- 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_singbox then
|
||||
o:value("sing-box", "Sing-Box")
|
||||
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", "sing-box")
|
||||
o:depends("dns_mode", "xray")
|
||||
|
||||
---- DNS Forward
|
||||
o = s:option(Value, "remote_dns", translate("Remote DNS"))
|
||||
o.default = "1.1.1.1"
|
||||
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)")
|
||||
o:value("1.1.1.2", "1.1.1.2 (CloudFlare-Security)")
|
||||
o:value("8.8.4.4", "8.8.4.4 (Google)")
|
||||
o:value("8.8.8.8", "8.8.8.8 (Google)")
|
||||
o:value("9.9.9.9", "9.9.9.9 (Quad9-Recommended)")
|
||||
o:value("208.67.220.220", "208.67.220.220 (OpenDNS)")
|
||||
o:value("208.67.222.222", "208.67.222.222 (OpenDNS)")
|
||||
o:depends("dns_mode", "dns2socks")
|
||||
o:depends("v2ray_dns_mode", "tcp")
|
||||
|
||||
if has_singbox or has_xray then
|
||||
o = s:option(Value, "remote_dns_doh", translate("Remote DNS DoH"))
|
||||
o:value("https://1.1.1.1/dns-query", "CloudFlare")
|
||||
o:value("https://1.1.1.2/dns-query", "CloudFlare-Security")
|
||||
o:value("https://8.8.4.4/dns-query", "Google 8844")
|
||||
o:value("https://8.8.8.8/dns-query", "Google 8888")
|
||||
o:value("https://9.9.9.9/dns-query", "Quad9-Recommended")
|
||||
o:value("https://208.67.222.222/dns-query", "OpenDNS")
|
||||
o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard")
|
||||
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.default = "https://1.1.1.1/dns-query"
|
||||
o.validate = function(self, value, t)
|
||||
if value ~= "" then
|
||||
value = api.trim(value)
|
||||
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")
|
||||
|
||||
if has_xray then
|
||||
o = s:option(Value, "dns_client_ip", translate("EDNS Client Subnet"))
|
||||
o.datatype = "ipaddr"
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "xray", v2ray_dns_mode = "doh"})
|
||||
end
|
||||
end
|
||||
|
||||
if api.is_finded("chinadns-ng") then
|
||||
o = s:option(Flag, "chinadns_ng", translate("ChinaDNS-NG"), translate("The effect is better, but will increase the memory."))
|
||||
o.default = "0"
|
||||
o:depends({ tcp_proxy_mode = "gfwlist", dns_mode = "dns2socks"})
|
||||
o:depends({ tcp_proxy_mode = "gfwlist", dns_mode = "xray"})
|
||||
o:depends({ tcp_proxy_mode = "chnroute", dns_mode = "dns2socks"})
|
||||
o:depends({ tcp_proxy_mode = "chnroute", dns_mode = "xray"})
|
||||
end
|
||||
|
||||
if has_chnlist then
|
||||
when_chnroute_default_dns = s:option(ListValue, "when_chnroute_default_dns", translate("When using the chnroute list the default DNS"))
|
||||
when_chnroute_default_dns.default = "direct"
|
||||
when_chnroute_default_dns:value("remote", translate("Remote DNS"))
|
||||
when_chnroute_default_dns:value("direct", translate("Direct DNS"))
|
||||
when_chnroute_default_dns.description = "<ul>"
|
||||
.. "<li>" .. translate("Remote DNS can avoid more DNS leaks, but some domestic domain names maybe to proxy!") .. "</li>"
|
||||
.. "<li>" .. translate("Direct DNS Internet experience may be better, but DNS will be leaked!") .. "</li>"
|
||||
.. "</ul>"
|
||||
local _depends = {
|
||||
{ dns_mode = "dns2socks" },
|
||||
{ dns_mode = "xray" }
|
||||
}
|
||||
for i, d in ipairs(_depends) do
|
||||
d["tcp_proxy_mode"] = "chnroute"
|
||||
if api.is_finded("chinadns-ng") then
|
||||
d["chinadns_ng"] = false
|
||||
end
|
||||
when_chnroute_default_dns:depends(d)
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
@ -1,28 +0,0 @@
|
||||
local api = require "luci.passwall.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/app_version"))
|
||||
|
||||
local k, v
|
||||
local com = require "luci.passwall.com"
|
||||
for k, v in pairs(com) do
|
||||
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
|
||||
o.default = v.default_path or ("/usr/bin/" .. k)
|
||||
o.rmempty = false
|
||||
end
|
||||
|
||||
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
|
@ -1,550 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
local datatypes = api.datatypes
|
||||
local has_singbox = api.finded_com("singbox")
|
||||
local has_xray = api.finded_com("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 tcp_socks_server = "127.0.0.1" .. ":" .. (uci:get(appname, "@global[0]", "tcp_node_socks_port") or "1070")
|
||||
local socks_table = {}
|
||||
socks_table[#socks_table + 1] = {
|
||||
id = tcp_socks_server,
|
||||
remarks = tcp_socks_server .. " - " .. translate("TCP Node")
|
||||
}
|
||||
uci:foreach(appname, "socks", function(s)
|
||||
if s.enabled == "1" and s.node then
|
||||
local id, remarks
|
||||
for k, n in pairs(nodes_table) do
|
||||
if (s.node == n.id) then
|
||||
remarks = n["remark"]; break
|
||||
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
|
||||
value = api.trim(value)
|
||||
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:value("nil", translate("Close"))
|
||||
|
||||
---- UDP Node
|
||||
udp_node = s:taboption("Main", ListValue, "udp_node", "<a style='color: red'>" .. translate("UDP Node") .. "</a>")
|
||||
udp_node:value("nil", translate("Close"))
|
||||
udp_node:value("tcp", translate("Same as the tcp node"))
|
||||
|
||||
-- 分流
|
||||
if (has_singbox or has_xray) and #nodes_table > 0 then
|
||||
local normal_list = {}
|
||||
local balancing_list = {}
|
||||
local shunt_list = {}
|
||||
local iface_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 == "_balancing" then
|
||||
balancing_list[#balancing_list + 1] = v
|
||||
end
|
||||
if v.protocol and v.protocol == "_shunt" then
|
||||
shunt_list[#shunt_list + 1] = v
|
||||
end
|
||||
if v.protocol and v.protocol == "_iface" then
|
||||
iface_list[#iface_list + 1] = v
|
||||
end
|
||||
end
|
||||
|
||||
local function get_cfgvalue(shunt_node_id, option)
|
||||
return function(self, section)
|
||||
return m:get(shunt_node_id, option) or "nil"
|
||||
end
|
||||
end
|
||||
local function get_write(shunt_node_id, option)
|
||||
return function(self, section, value)
|
||||
m:set(shunt_node_id, option, value)
|
||||
end
|
||||
end
|
||||
if #normal_list > 0 then
|
||||
for k, v in pairs(shunt_list) do
|
||||
local vid = v.id
|
||||
-- shunt node type, Sing-Box or Xray
|
||||
local type = s:taboption("Main", ListValue, vid .. "-type", translate("Type"))
|
||||
if has_singbox then
|
||||
type:value("sing-box", "Sing-Box")
|
||||
end
|
||||
if has_xray then
|
||||
type:value("Xray", translate("Xray"))
|
||||
end
|
||||
type.cfgvalue = get_cfgvalue(v.id, "type")
|
||||
type.write = get_write(v.id, "type")
|
||||
|
||||
-- pre-proxy
|
||||
o = s:taboption("Main", Flag, vid .. "-preproxy_enabled", translate("Preproxy"))
|
||||
o:depends("tcp_node", v.id)
|
||||
o.rmempty = false
|
||||
o.cfgvalue = get_cfgvalue(v.id, "preproxy_enabled")
|
||||
o.write = get_write(v.id, "preproxy_enabled")
|
||||
|
||||
o = s:taboption("Main", Value, vid .. "-main_node", string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
|
||||
o:depends(vid .. "-preproxy_enabled", "1")
|
||||
for k1, v1 in pairs(balancing_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(iface_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
o.cfgvalue = get_cfgvalue(v.id, "main_node")
|
||||
o.write = get_write(v.id, "main_node")
|
||||
|
||||
if (has_singbox and has_xray) or (v.type == "sing-box" and not has_singbox) or (v.type == "Xray" and not has_xray) then
|
||||
type:depends("tcp_node", v.id)
|
||||
else
|
||||
type:depends("tcp_node", "hide") --不存在的依赖,即始终隐藏
|
||||
end
|
||||
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
local id = e[".name"]
|
||||
local node_option = vid .. "-" .. id .. "_node"
|
||||
if id and e.remarks then
|
||||
o = s:taboption("Main", Value, node_option, string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", id), e.remarks))
|
||||
o.cfgvalue = get_cfgvalue(v.id, id)
|
||||
o.write = get_write(v.id, id)
|
||||
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"))
|
||||
|
||||
local pt = s:taboption("Main", ListValue, vid .. "-".. id .. "_proxy_tag", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
|
||||
pt.cfgvalue = get_cfgvalue(v.id, id .. "_proxy_tag")
|
||||
pt.write = get_write(v.id, id .. "_proxy_tag")
|
||||
pt:value("nil", translate("Close"))
|
||||
pt:value("main", translate("Preproxy Node"))
|
||||
pt.default = "nil"
|
||||
for k1, v1 in pairs(balancing_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(iface_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
pt:depends({ [node_option] = v1.id, [vid .. "-preproxy_enabled"] = "1" })
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local id = "default_node"
|
||||
o = s:taboption("Main", Value, vid .. "-" .. id, string.format('* <a style="color:red">%s</a>', translate("Default")))
|
||||
o.cfgvalue = get_cfgvalue(v.id, id)
|
||||
o.write = get_write(v.id, id)
|
||||
o:depends("tcp_node", v.id)
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
for k1, v1 in pairs(balancing_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(iface_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
o:value(v1.id, v1.remark)
|
||||
end
|
||||
|
||||
local id = "default_proxy_tag"
|
||||
o = s:taboption("Main", ListValue, vid .. "-" .. 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.cfgvalue = get_cfgvalue(v.id, id)
|
||||
o.write = get_write(v.id, id)
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("main", translate("Preproxy Node"))
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
if v1.protocol ~= "_balancing" then
|
||||
o:depends({ [vid .. "-default_node"] = v1.id, [vid .. "-preproxy_enabled"] = "1" })
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local tips = s:taboption("Main", DummyValue, "tips", " ")
|
||||
tips.rawhtml = true
|
||||
tips.cfgvalue = function(t, n)
|
||||
return string.format('<a style="color: red">%s</a>', translate("There are no available nodes, please add or subscribe nodes first."))
|
||||
end
|
||||
tips:depends({ tcp_node = "nil", ["!reverse"] = true })
|
||||
for k, v in pairs(shunt_list) do
|
||||
tips:depends("udp_node", v.id)
|
||||
end
|
||||
for k, v in pairs(balancing_list) do
|
||||
tips:depends("udp_node", v.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tcp_node_socks_port = s:taboption("Main", Value, "tcp_node_socks_port", translate("TCP Node") .. " Socks " .. translate("Listen Port"))
|
||||
tcp_node_socks_port.default = 1070
|
||||
tcp_node_socks_port.datatype = "port"
|
||||
tcp_node_socks_port:depends({ tcp_node = "nil", ["!reverse"] = true })
|
||||
--[[
|
||||
if has_singbox or has_xray then
|
||||
tcp_node_http_port = s:taboption("Main", Value, "tcp_node_http_port", translate("TCP Node") .. " HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use"))
|
||||
tcp_node_http_port.default = 0
|
||||
tcp_node_http_port.datatype = "port"
|
||||
end
|
||||
]]--
|
||||
|
||||
|
||||
s:tab("DNS", translate("DNS"))
|
||||
|
||||
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("dns2tcp") then
|
||||
dns_mode:value("dns2tcp", translatef("Requery DNS By %s", "TCP"))
|
||||
end
|
||||
if api.is_finded("dns2socks") then
|
||||
dns_mode:value("dns2socks", "dns2socks")
|
||||
end
|
||||
if has_singbox then
|
||||
dns_mode:value("sing-box", "Sing-Box")
|
||||
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", "sing-box")
|
||||
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.default = socks_table[1].id
|
||||
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"})
|
||||
|
||||
---- DNS Forward
|
||||
o = s:taboption("DNS", Value, "remote_dns", translate("Remote DNS"))
|
||||
o.datatype = "or(ipaddr,ipaddrport)"
|
||||
o.default = "1.1.1.1"
|
||||
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)")
|
||||
o:value("1.1.1.2", "1.1.1.2 (CloudFlare-Security)")
|
||||
o:value("8.8.4.4", "8.8.4.4 (Google)")
|
||||
o:value("8.8.8.8", "8.8.8.8 (Google)")
|
||||
o:value("9.9.9.9", "9.9.9.9 (Quad9-Recommended)")
|
||||
o:value("208.67.220.220", "208.67.220.220 (OpenDNS)")
|
||||
o:value("208.67.222.222", "208.67.222.222 (OpenDNS)")
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
o:depends({dns_mode = "dns2tcp"})
|
||||
o:depends({dns_mode = "udp"})
|
||||
o:depends({v2ray_dns_mode = "tcp"})
|
||||
|
||||
---- DoH
|
||||
o = s:taboption("DNS", Value, "remote_dns_doh", translate("Remote DNS DoH"))
|
||||
o.default = "https://1.1.1.1/dns-query"
|
||||
o:value("https://1.1.1.1/dns-query", "CloudFlare")
|
||||
o:value("https://1.1.1.2/dns-query", "CloudFlare-Security")
|
||||
o:value("https://8.8.4.4/dns-query", "Google 8844")
|
||||
o:value("https://8.8.8.8/dns-query", "Google 8888")
|
||||
o:value("https://9.9.9.9/dns-query", "Quad9-Recommended")
|
||||
o:value("https://208.67.222.222/dns-query", "OpenDNS")
|
||||
o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard")
|
||||
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.validate = doh_validate
|
||||
o:depends("v2ray_dns_mode", "doh")
|
||||
|
||||
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({dns_mode = "xray", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "xray", 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 = "sing-box", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "sing-box", 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 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"
|
||||
o:depends({dns_mode = "dns2socks"})
|
||||
o:depends({dns_mode = "dns2tcp"})
|
||||
o:depends({dns_mode = "sing-box", v2ray_dns_mode = "tcp"})
|
||||
o:depends({dns_mode = "sing-box", 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
|
||||
|
||||
if has_chnlist then
|
||||
when_chnroute_default_dns = s:taboption("DNS", ListValue, "when_chnroute_default_dns", translate("When using the chnroute list the default DNS"))
|
||||
when_chnroute_default_dns.default = "direct"
|
||||
when_chnroute_default_dns:value("remote", translate("Remote DNS"))
|
||||
when_chnroute_default_dns:value("direct", translate("Direct DNS"))
|
||||
when_chnroute_default_dns.description = "<ul>"
|
||||
.. "<li>" .. translate("Remote DNS can avoid more DNS leaks, but some domestic domain names maybe to proxy!") .. "</li>"
|
||||
.. "<li>" .. translate("Direct DNS Internet experience may be better, but DNS will be leaked!") .. "</li>"
|
||||
.. "</ul>"
|
||||
if api.is_finded("chinadns-ng") then
|
||||
when_chnroute_default_dns:depends("chinadns_ng", false)
|
||||
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("[ -n \"$(nft list sets 2>/dev/null | grep \"passwall_\")\" ] && sh /usr/share/" .. appname .. "/nftables.sh flush_nftset || sh /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 Proxy Mode"))
|
||||
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 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"))
|
||||
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", "Sing-Box/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")
|
||||
|
||||
o = s:taboption("log", Flag, "advanced_log_feature", translate("Advanced log feature"), translate("For professionals only."))
|
||||
o.default = "0"
|
||||
o.rmempty = false
|
||||
local syslog = s:taboption("log", Flag, "sys_log", translate("Logging to system log"), translate("Logging to the system log for more advanced functions. For example, send logs to a dedicated log server."))
|
||||
syslog:depends("advanced_log_feature", "1")
|
||||
syslog.default = "0"
|
||||
syslog.rmempty = false
|
||||
local logpath = s:taboption("log", Value, "persist_log_path", translate("Persist log file directory"), translate("The path to the directory used to store persist log files, the \"/\" at the end can be omitted. Leave it blank to disable this feature."))
|
||||
logpath:depends({ ["advanced_log_feature"] = 1, ["sys_log"] = 0 })
|
||||
|
||||
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.template = "cbi/tblsection"
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.extedit = api.url("socks_config", "%s")
|
||||
function s.create(e, t)
|
||||
local uuid = api.gen_short_uuid()
|
||||
t = uuid
|
||||
TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
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"))
|
||||
|
||||
local n = 1
|
||||
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_singbox 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_singbox 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
|
@ -1,140 +0,0 @@
|
||||
local api = require "luci.passwall.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)
|
||||
|
||||
---- Health Check Type
|
||||
o = s:option(ListValue, "health_check_type", translate("Health Check Type"))
|
||||
o.default = "passwall_logic"
|
||||
o:value("tcp", "TCP")
|
||||
o:value("passwall_logic", translate("Availability test") .. string.format("(passwall %s)", translate("Inner implement")))
|
||||
o:depends("balancing_enable", true)
|
||||
|
||||
---- Health Check Inter
|
||||
o = s:option(Value, "health_check_inter", translate("Health Check Inter"), translate("Units:seconds"))
|
||||
o.default = "60"
|
||||
o:depends("balancing_enable", true)
|
||||
|
||||
o = s:option(DummyValue, "health_check_tips", " ")
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
return string.format('<span style="color: red">%s</span>', translate("When the availability test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid."))
|
||||
end
|
||||
o:depends("health_check_type", "passwall_logic")
|
||||
|
||||
-- [[ 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 when use TCP health check type, otherwise it cannot be used normally!") ..
|
||||
"</font>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.sortable = true
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
|
||||
s.create = function(e, t)
|
||||
TypedSection.create(e, api.gen_short_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
|
@ -1,8 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
|
||||
f = SimpleForm(appname)
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
f:append(Template(appname .. "/log/log"))
|
||||
return f
|
@ -1,82 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
local fs = require "nixio.fs"
|
||||
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall/client/type/"
|
||||
|
||||
if not arg[1] or not uci:get(appname, arg[1]) then
|
||||
luci.http.redirect(api.url("node_list"))
|
||||
end
|
||||
|
||||
m = Map(appname, translate("Node Config"))
|
||||
m.redirect = api.url()
|
||||
|
||||
s = m:section(NamedSection, arg[1], "nodes", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
o = s:option(DummyValue, "passwall", " ")
|
||||
o.rawhtml = true
|
||||
o.template = "passwall/node_list/link_share_man"
|
||||
o.value = arg[1]
|
||||
|
||||
o = s:option(Value, "remarks", translate("Node Remarks"))
|
||||
o.default = translate("Remarks")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "type", translate("Type"))
|
||||
|
||||
if api.is_finded("ipt2socks") then
|
||||
s.fields["type"]:value("Socks", translate("Socks"))
|
||||
|
||||
o = s:option(Value, "socks_address", translate("Address (Support Domain Name)"))
|
||||
o:depends("type", "Socks")
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "address")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
m:set(section, "address", value)
|
||||
end
|
||||
|
||||
o = s:option(Value, "socks_port", translate("Port"))
|
||||
o.datatype = "port"
|
||||
o:depends("type", "Socks")
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "port")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
m:set(section, "port", value)
|
||||
end
|
||||
|
||||
o = s:option(Value, "socks_username", translate("Username"))
|
||||
o:depends("type", "Socks")
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "username")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
m:set(section, "username", value)
|
||||
end
|
||||
|
||||
o = s:option(Value, "socks_password", translate("Password"))
|
||||
o.password = true
|
||||
o:depends("type", "Socks")
|
||||
function o.cfgvalue(self, section)
|
||||
return m:get(section, "password")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
m:set(section, "password", value)
|
||||
end
|
||||
end
|
||||
|
||||
local type_table = {}
|
||||
for filename in fs.dir(types_dir) do
|
||||
table.insert(type_table, filename)
|
||||
end
|
||||
table.sort(type_table)
|
||||
|
||||
for index, value in ipairs(type_table) do
|
||||
local p_func = loadfile(types_dir .. value)
|
||||
setfenv(p_func, getfenv(1))(m, s)
|
||||
end
|
||||
|
||||
return m
|
@ -1,151 +0,0 @@
|
||||
local api = require "luci.passwall.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_short_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
|
||||
for k, v in ipairs(m:get(s[".name"], "autoswitch_backup_node") or {}) do
|
||||
if v and v == t then
|
||||
sys.call(string.format("uci -q del_list %s.%s.autoswitch_backup_node='%s'", appname, s[".name"], v))
|
||||
end
|
||||
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)
|
||||
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 == "sing-box" 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
|
@ -1,137 +0,0 @@
|
||||
local api = require "luci.passwall.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_singbox = api.finded_com("singbox")
|
||||
local has_xray = api.finded_com("xray")
|
||||
local has_trojan_go = api.finded_com("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_singbox then
|
||||
trojan_type[#trojan_type + 1] = "sing-box"
|
||||
ss_aead_type[#ss_aead_type + 1] = "sing-box"
|
||||
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
|
||||
|
||||
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
|
@ -1,110 +0,0 @@
|
||||
local api = require "luci.passwall.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_singbox = api.finded_com("singbox")
|
||||
local has_xray = api.finded_com("xray")
|
||||
local has_trojan_go = api.finded_com("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_singbox then
|
||||
trojan_type[#trojan_type + 1] = "sing-box"
|
||||
ss_aead_type[#ss_aead_type + 1] = "sing-box"
|
||||
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 = "0"
|
||||
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
|
@ -1,204 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local has_singbox = api.finded_com("singbox")
|
||||
local has_xray = api.finded_com("xray")
|
||||
local has_fw3 = api.is_finded("fw3")
|
||||
local has_fw4 = api.is_finded("fw4")
|
||||
|
||||
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 = "443"
|
||||
o:value("disable", translate("No patterns are used"))
|
||||
o:value("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"))
|
||||
|
||||
---- 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")
|
||||
|
||||
---- Use nftables
|
||||
o = s:option(ListValue, "use_nft", translate("Firewall tools"))
|
||||
o.default = "0"
|
||||
if has_fw3 then
|
||||
o:value("0", "IPtables")
|
||||
end
|
||||
if has_fw4 then
|
||||
o:value("1", "NFtables")
|
||||
end
|
||||
|
||||
if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod | grep -i TPROXY >/dev/null") == 0) or (os.execute("lsmod | grep -i nft_redir >/dev/null") == 0 and os.execute("lsmod | grep -i nft_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 or os.execute("lsmod | grep -i nft_tproxy >/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
|
||||
|
||||
if has_xray then
|
||||
s_xray = m:section(TypedSection, "global_xray", "Xray " .. translate("Settings"))
|
||||
s_xray.anonymous = true
|
||||
s_xray.addremove = false
|
||||
|
||||
o = s_xray:option(Flag, "sniffing", translate("Sniffing"), translate("When using the shunt, must be enabled, otherwise the shunt will invalid."))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
if has_xray then
|
||||
o = s_xray:option(Flag, "route_only", translate("Sniffing Route Only"))
|
||||
o.default = 0
|
||||
o:depends("sniffing", true)
|
||||
|
||||
local domains_excluded = string.format("/usr/share/%s/rules/domains_excluded", appname)
|
||||
o = s_xray:option(TextValue, "no_sniffing_hosts", translate("No Sniffing Lists"), translate("Hosts added into No Sniffing Lists will not resolve again on server."))
|
||||
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)
|
||||
local route_only_value = s_xray.fields["route_only"]:formvalue(section)
|
||||
if not route_only_value or route_only_value == "0" then
|
||||
fs.writefile(domains_excluded, "")
|
||||
end
|
||||
end
|
||||
o:depends({sniffing = true, route_only = false})
|
||||
|
||||
o = s_xray:option(Value, "buffer_size", translate("Buffer Size"), translate("Buffer size for every connection (kB)"))
|
||||
o.datatype = "uinteger"
|
||||
end
|
||||
end
|
||||
|
||||
if has_singbox then
|
||||
s = m:section(TypedSection, "global_singbox", "Sing-Box " .. translate("Settings"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(Flag, "sniff_override_destination", translate("Override the connection destination address"), translate("Override the connection destination address with the sniffed domain."))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "geoip_path", translate("Custom geoip Path"))
|
||||
o.default = "/usr/share/singbox/geoip.db"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "geoip_url", translate("Custom geoip URL"))
|
||||
o.default = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db"
|
||||
o:value("https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "geosite_path", translate("Custom geosite Path"))
|
||||
o.default = "/usr/share/singbox/geosite.db"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "geosite_url", translate("Custom geosite URL"))
|
||||
o.default = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db"
|
||||
o:value("https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db")
|
||||
o.rmempty = false
|
||||
end
|
||||
|
||||
return m
|
@ -1,90 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local has_xray = api.finded_com("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://fastly.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
|
||||
o.default = "https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
|
||||
|
||||
----chnroute URL
|
||||
o = s:option(DynamicList, "chnroute_url", translate("China IPs(chnroute) Update URL"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt", translate("gaoyifan/china-operator-ip/china"))
|
||||
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://fastly.jsdelivr.net/gh/soffchen/GeoIP2-CN@release/CN-ip-cidr.txt", translate("soffchen/GeoIP2-CN"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Hackl0us/GeoIP2-CN@release/CN-ip-cidr.txt", translate("Hackl0us/GeoIP2-CN"))
|
||||
|
||||
----chnroute6 URL
|
||||
o = s:option(DynamicList, "chnroute6_url", translate("China IPv6s(chnroute6) Update URL"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt", translate("gaoyifan/china-operator-ip/china6"))
|
||||
o:value("https://ispip.clang.cn/all_cn_ipv6.txt", translate("Clang.CN.IPv6"))
|
||||
|
||||
----chnlist URL
|
||||
o = s:option(DynamicList, "chnlist_url", translate("China List(Chnlist) Update URL"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf", translate("felixonmars/domains.china"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf", translate("felixonmars/apple.china"))
|
||||
o:value("https://fastly.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_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", "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
|
@ -1,271 +0,0 @@
|
||||
local api = require "luci.passwall.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 = Map(appname)
|
||||
|
||||
-- [[ 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
|
||||
|
||||
if sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0 then
|
||||
m.apply_on_parse = true
|
||||
function m.on_apply(self)
|
||||
luci.sys.call("/etc/init.d/passwall reload > /dev/null 2>&1 &")
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
@ -1,79 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local datatypes = api.datatypes
|
||||
|
||||
m = Map(appname, "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
|
@ -1,118 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
local has_xray = api.finded_com("xray")
|
||||
|
||||
m = Map(appname)
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
nodes_table[#nodes_table + 1] = e
|
||||
end
|
||||
|
||||
s = m:section(NamedSection, arg[1], translate("Socks Config"), translate("Socks Config"))
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
|
||||
---- Enable
|
||||
o = s:option(Flag, "enabled", translate("Enable"))
|
||||
o.default = 1
|
||||
o.rmempty = false
|
||||
|
||||
local auto_switch_tip
|
||||
local current_node_file = string.format("/tmp/etc/%s/id/socks_%s", appname, arg[1])
|
||||
local current_node = luci.sys.exec(string.format("[ -f '%s' ] && echo -n $(cat %s)", current_node_file, current_node_file))
|
||||
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(arg[1], "enable_autoswitch") or 0) == 1 then
|
||||
if n then
|
||||
local remarks = api.get_node_remarks(n)
|
||||
local url = api.url("node_config", n[".name"])
|
||||
auto_switch_tip = translatef("Current node: %s", string.format('<a href="%s">%s</a>', url, remarks)) .. "<br />"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
socks_node = s:option(ListValue, "node", translate("Node"))
|
||||
if auto_switch_tip then
|
||||
socks_node.description = auto_switch_tip
|
||||
end
|
||||
|
||||
local n = 1
|
||||
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_xray then
|
||||
o = s:option(Value, "http_port", "HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use"))
|
||||
o.default = 0
|
||||
o.datatype = "port"
|
||||
end
|
||||
|
||||
o = s:option(Flag, "enable_autoswitch", translate("Auto Switch"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "autoswitch_testing_time", translate("How often to test"), translate("Units:seconds"))
|
||||
o.datatype = "min(10)"
|
||||
o.default = 30
|
||||
o:depends("enable_autoswitch", true)
|
||||
|
||||
o = s:option(Value, "autoswitch_connect_timeout", translate("Timeout seconds"), translate("Units:seconds"))
|
||||
o.datatype = "min(1)"
|
||||
o.default = 3
|
||||
o:depends("enable_autoswitch", true)
|
||||
|
||||
o = s:option(Value, "autoswitch_retry_num", translate("Timeout retry num"))
|
||||
o.datatype = "min(1)"
|
||||
o.default = 1
|
||||
o:depends("enable_autoswitch", true)
|
||||
|
||||
autoswitch_backup_node = s:option(DynamicList, "autoswitch_backup_node", translate("List of backup nodes"))
|
||||
autoswitch_backup_node:depends("enable_autoswitch", true)
|
||||
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, "autoswitch_restore_switch", translate("Restore Switch"), translate("When detects main node is available, switch back to the main node."))
|
||||
o:depends("enable_autoswitch", true)
|
||||
|
||||
o = s:option(Value, "autoswitch_probe_url", translate("Probe URL"), translate("The URL used to detect the connection status."))
|
||||
o.default = "https://www.google.com/generate_204"
|
||||
o:depends("enable_autoswitch", true)
|
||||
|
||||
for k, v in pairs(nodes_table) do
|
||||
if v.node_type == "normal" then
|
||||
autoswitch_backup_node:value(v.id, v["remark"])
|
||||
socks_node:value(v.id, v["remark"])
|
||||
end
|
||||
end
|
||||
|
||||
m:append(Template(appname .. "/socks_auto_switch/footer"))
|
||||
|
||||
return m
|
@ -1,40 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("brook") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Brook"
|
||||
|
||||
local option_prefix = "brook_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Brook ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Brook"))
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("client", translate("Brook"))
|
||||
o:value("wsclient", translate("WebSocket"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("protocol")] = "wsclient" })
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("Use TLS"))
|
||||
o:depends({ [option_name("protocol")] = "wsclient" })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,89 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("hysteria") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Hysteria"
|
||||
|
||||
local option_prefix = "hysteria_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Hysteria ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Hysteria"))
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("udp", "UDP")
|
||||
o:value("faketcp", "faketcp")
|
||||
o:value("wechat-video", "wechat-video")
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("hop"), translate("Additional ports for hysteria hop"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("obfs"), translate("Obfs Password"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(ListValue, option_name("auth_type"), translate("Auth Type"))
|
||||
o:value("disable", translate("Disable"))
|
||||
o:value("string", translate("STRING"))
|
||||
o:value("base64", translate("BASE64"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("auth_type")] = "string"})
|
||||
o:depends({ [option_name("auth_type")] = "base64"})
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("alpn"), translate("QUIC TLS ALPN"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("fast_open"), translate("Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Value, option_name("tls_serverName"), translate("Domain"))
|
||||
|
||||
o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Value, option_name("up_mbps"), translate("Max upload Mbps"))
|
||||
o.default = "10"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("down_mbps"), translate("Max download Mbps"))
|
||||
o.default = "50"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("recv_window_conn"), translate("QUIC stream receive window"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("recv_window"), translate("QUIC connection receive window"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("handshake_timeout"), translate("Handshake Timeout"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("idle_timeout"), translate("Idle Timeout"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("hop_interval"), translate("Hop Interval"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("lazy_start"), translate("Lazy Start"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,35 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("naive") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Naiveproxy"
|
||||
|
||||
local option_prefix = "naive_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Naive ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("NaiveProxy"))
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("https", translate("HTTPS"))
|
||||
o:value("quic", translate("QUIC"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,519 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("xray") then
|
||||
return
|
||||
end
|
||||
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
|
||||
local type_name = "Xray"
|
||||
|
||||
local option_prefix = "xray_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local x_ss_encrypt_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" }
|
||||
|
||||
local header_type_list = {
|
||||
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
|
||||
}
|
||||
|
||||
-- [[ Xray ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Xray")
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("vmess", translate("Vmess"))
|
||||
o:value("vless", translate("VLESS"))
|
||||
o:value("http", translate("HTTP"))
|
||||
o:value("socks", translate("Socks"))
|
||||
o:value("shadowsocks", translate("Shadowsocks"))
|
||||
o:value("trojan", translate("Trojan"))
|
||||
o:value("wireguard", translate("WireGuard"))
|
||||
o:value("_balancing", translate("Balancing"))
|
||||
o:value("_shunt", translate("Shunt"))
|
||||
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
|
||||
|
||||
o = s:option(Value, option_name("iface"), translate("Interface"))
|
||||
o.default = "eth1"
|
||||
o:depends({ [option_name("protocol")] = "_iface" })
|
||||
|
||||
local nodes_table = {}
|
||||
local balancers_table = {}
|
||||
local iface_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
|
||||
if e.protocol == "_balancing" then
|
||||
balancers_table[#balancers_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
if e.protocol == "_iface" then
|
||||
iface_table[#iface_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- 负载均衡列表
|
||||
local o = s:option(DynamicList, option_name("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>"))
|
||||
o:depends({ [option_name("protocol")] = "_balancing" })
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
|
||||
local o = s:option(ListValue, option_name("balancingStrategy"), translate("Balancing Strategy"))
|
||||
o:depends({ [option_name("protocol")] = "_balancing" })
|
||||
o:value("random")
|
||||
o:value("leastPing")
|
||||
o.default = "random"
|
||||
|
||||
-- 探测地址
|
||||
local o = s:option(Flag, option_name("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
|
||||
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
|
||||
|
||||
local o = s:option(Value, option_name("probeUrl"), translate("Probe URL"))
|
||||
o:depends({ [option_name("useCustomProbeUrl")] = true })
|
||||
o.default = "https://www.google.com/generate_204"
|
||||
o.description = translate("The URL used to detect the connection status.")
|
||||
|
||||
-- 探测间隔
|
||||
local o = s:option(Value, option_name("probeInterval"), translate("Probe Interval"))
|
||||
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
|
||||
o.default = "1m"
|
||||
o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
|
||||
|
||||
-- [[ 分流模块 ]]
|
||||
if #nodes_table > 0 then
|
||||
o = s:option(Flag, option_name("preproxy_enabled"), translate("Preproxy"))
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
o = s:option(Value, option_name("main_node"), string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
|
||||
o:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true })
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
o.default = "nil"
|
||||
end
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if e[".name"] and e.remarks then
|
||||
o = s:option(Value, option_name(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({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
if #nodes_table > 0 then
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
local pt = s:option(ListValue, option_name(e[".name"] .. "_proxy_tag"), string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
|
||||
pt:value("nil", translate("Close"))
|
||||
pt:value("main", translate("Preproxy Node"))
|
||||
pt.default = "nil"
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
pt:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true, [option_name(e[".name"])] = v.id })
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
o = s:option(DummyValue, option_name("shunt_tips"), " ")
|
||||
o.not_rewrite = true
|
||||
o.rawhtml = true
|
||||
o.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
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
local o = s:option(Value, option_name("default_node"), string.format('* <a style="color:red">%s</a>', translate("Default")))
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
|
||||
if #nodes_table > 0 then
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
local dpt = s:option(ListValue, option_name("default_proxy_tag"), 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."))
|
||||
dpt:value("nil", translate("Close"))
|
||||
dpt:value("main", translate("Preproxy Node"))
|
||||
dpt.default = "nil"
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
dpt:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true, [option_name("default_node")] = v.id })
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("domainStrategy"), translate("Domain Strategy"))
|
||||
o:value("AsIs")
|
||||
o:value("IPIfNonMatch")
|
||||
o:value("IPOnDemand")
|
||||
o.default = "IPOnDemand"
|
||||
o.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>"
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
o = s:option(ListValue, option_name("domainMatcher"), translate("Domain matcher"))
|
||||
o:value("hybrid")
|
||||
o:value("linear")
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
-- [[ 分流模块 End ]]
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
local protocols = s.fields[option_name("protocol")].keylist
|
||||
if #protocols > 0 then
|
||||
for index, value in ipairs(protocols) do
|
||||
if not value:find("_") then
|
||||
s.fields[option_name("address")]:depends({ [option_name("protocol")] = value })
|
||||
s.fields[option_name("port")]:depends({ [option_name("protocol")] = value })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
o = s:option(ListValue, option_name("security"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(security_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
|
||||
o = s:option(Value, option_name("encryption"), translate("Encrypt Method"))
|
||||
o.default = "none"
|
||||
o:value("none")
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
|
||||
o = s:option(ListValue, option_name("x_ss_encrypt_method"), translate("Encrypt Method"))
|
||||
o.rewrite_option = "method"
|
||||
for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(Flag, option_name("iv_check"), translate("IV Check"))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "aes-128-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "aes-256-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "chacha20-poly1305" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "xchacha20-poly1305" })
|
||||
|
||||
o = s:option(Flag, option_name("uot"), translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side."))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "2022-blake3-aes-128-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "2022-blake3-aes-256-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "2022-blake3-chacha20-poly1305" })
|
||||
|
||||
o = s:option(Value, option_name("uuid"), translate("ID"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
|
||||
o = s:option(ListValue, option_name("flow"), translate("flow"))
|
||||
o.default = ""
|
||||
o:value("", translate("Disable"))
|
||||
o:value("xtls-rprx-vision")
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("tls")] = true, [option_name("transport")] = "tcp" })
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(Flag, option_name("reality"), translate("REALITY"), translate("Only recommend to use with VLESS-TCP-XTLS-Vision."))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "tcp" })
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "h2" })
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(ListValue, option_name("alpn"), translate("alpn"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("h2,http/1.1")
|
||||
o:value("h2")
|
||||
o:value("http/1.1")
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = false })
|
||||
|
||||
-- o = s:option(Value, option_name("minversion"), translate("minversion"))
|
||||
-- o.default = "1.3"
|
||||
-- o:value("1.3")
|
||||
-- o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Value, option_name("tls_serverName"), translate("Domain"))
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = false })
|
||||
|
||||
-- [[ REALITY部分 ]] --
|
||||
o = s:option(Value, option_name("reality_publicKey"), translate("Public Key"))
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = true })
|
||||
|
||||
o = s:option(Value, option_name("reality_shortId"), translate("Short Id"))
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = true })
|
||||
|
||||
o = s:option(Value, option_name("reality_spiderX"), translate("Spider X"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("utls"), translate("uTLS"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = false })
|
||||
|
||||
o = s:option(ListValue, option_name("fingerprint"), translate("Finger Print"))
|
||||
o:value("chrome")
|
||||
o:value("firefox")
|
||||
o:value("edge")
|
||||
o:value("safari")
|
||||
o:value("360")
|
||||
o:value("qq")
|
||||
o:value("ios")
|
||||
o:value("android")
|
||||
o:value("random")
|
||||
o:value("randomized")
|
||||
o.default = "chrome"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("utls")] = true })
|
||||
o:depends({ [option_name("tls")] = true, [option_name("reality")] = true })
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("mkcp", "mKCP")
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("h2", "HTTP/2")
|
||||
o:value("ds", "DomainSocket")
|
||||
o:value("quic", "QUIC")
|
||||
o:value("grpc", "gRPC")
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
--[[
|
||||
o = s:option(ListValue, option_name("ss_transport"), translate("Transport"))
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("h2", "HTTP/2")
|
||||
o:value("h2+ws", "HTTP/2 & WebSocket")
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
]]--
|
||||
|
||||
o = s:option(Value, option_name("wireguard_public_key"), translate("Public Key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_secret_key"), translate("Private Key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_preSharedKey"), translate("Pre shared key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(DynamicList, option_name("wireguard_local_address"), translate("Local Address"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_mtu"), translate("MTU"))
|
||||
o.default = "1420"
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then
|
||||
o = s:option(Value, option_name("wireguard_reserved"), translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
end
|
||||
|
||||
o = s:option(Value, option_name("wireguard_keepAlive"), translate("Keep Alive"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
-- [[ TCP部分 ]]--
|
||||
|
||||
-- TCP伪装
|
||||
o = s:option(ListValue, option_name("tcp_guise"), translate("Camouflage Type"))
|
||||
o:value("none", "none")
|
||||
o:value("http", "http")
|
||||
o:depends({ [option_name("transport")] = "tcp" })
|
||||
|
||||
-- HTTP域名
|
||||
o = s:option(DynamicList, option_name("tcp_guise_http_host"), translate("HTTP Host"))
|
||||
o:depends({ [option_name("tcp_guise")] = "http" })
|
||||
|
||||
-- HTTP路径
|
||||
o = s:option(DynamicList, option_name("tcp_guise_http_path"), translate("HTTP Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("tcp_guise")] = "http" })
|
||||
|
||||
-- [[ mKCP部分 ]]--
|
||||
|
||||
o = s:option(ListValue, option_name("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 o:value(t) end
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_mtu"), translate("KCP MTU"))
|
||||
o.default = "1350"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_tti"), translate("KCP TTI"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_uplinkCapacity"), translate("KCP uplinkCapacity"))
|
||||
o.default = "5"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_downlinkCapacity"), translate("KCP downlinkCapacity"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Flag, option_name("mkcp_congestion"), translate("KCP Congestion"))
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_readBufferSize"), translate("KCP readBufferSize"))
|
||||
o.default = "1"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_writeBufferSize"), translate("KCP writeBufferSize"))
|
||||
o.default = "1"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_seed"), translate("KCP Seed"))
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
o:depends({ [option_name("ss_transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
o:depends({ [option_name("ss_transport")] = "ws" })
|
||||
|
||||
-- [[ HTTP/2部分 ]]--
|
||||
o = s:option(Value, option_name("h2_host"), translate("HTTP/2 Host"))
|
||||
o:depends({ [option_name("transport")] = "h2" })
|
||||
o:depends({ [option_name("ss_transport")] = "h2" })
|
||||
|
||||
o = s:option(Value, option_name("h2_path"), translate("HTTP/2 Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("transport")] = "h2" })
|
||||
o:depends({ [option_name("ss_transport")] = "h2" })
|
||||
|
||||
o = s:option(Flag, option_name("h2_health_check"), translate("Health check"))
|
||||
o:depends({ [option_name("transport")] = "h2" })
|
||||
|
||||
o = s:option(Value, option_name("h2_read_idle_timeout"), translate("Idle timeout"))
|
||||
o.default = "10"
|
||||
o:depends({ [option_name("h2_health_check")] = true })
|
||||
|
||||
o = s:option(Value, option_name("h2_health_check_timeout"), translate("Health check timeout"))
|
||||
o.default = "15"
|
||||
o:depends({ [option_name("h2_health_check")] = true })
|
||||
|
||||
-- [[ DomainSocket部分 ]]--
|
||||
o = s:option(Value, option_name("ds_path"), "Path", translate("A legal file path. This file must not exist before running."))
|
||||
o:depends({ [option_name("transport")] = "ds" })
|
||||
|
||||
-- [[ QUIC部分 ]]--
|
||||
o = s:option(ListValue, option_name("quic_security"), translate("Encrypt Method"))
|
||||
o:value("none")
|
||||
o:value("aes-128-gcm")
|
||||
o:value("chacha20-poly1305")
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
o = s:option(Value, option_name("quic_key"), translate("Encrypt Method") .. translate("Key"))
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
o = s:option(ListValue, option_name("quic_guise"), translate("Camouflage Type"))
|
||||
for a, t in ipairs(header_type_list) do o:value(t) end
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
o = s:option(Value, option_name("grpc_serviceName"), "ServiceName")
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(ListValue, option_name("grpc_mode"), "gRPC " .. translate("Transfer mode"))
|
||||
o:value("gun")
|
||||
o:value("multi")
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Flag, option_name("grpc_health_check"), translate("Health check"))
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Value, option_name("grpc_idle_timeout"), translate("Idle timeout"))
|
||||
o.default = "10"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
|
||||
o = s:option(Value, option_name("grpc_health_check_timeout"), translate("Health check timeout"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("grpc_permit_without_stream"), translate("Permit without stream"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
|
||||
o = s:option(Value, option_name("grpc_initial_windows_size"), translate("Initial Windows Size"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
-- [[ Mux ]]--
|
||||
o = s:option(Flag, option_name("mux"), translate("Mux"))
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "" })
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
o = s:option(Value, option_name("mux_concurrency"), translate("Mux concurrency"))
|
||||
o.default = 8
|
||||
o:depends({ [option_name("mux")] = true })
|
||||
|
||||
-- [[ XUDP Mux ]]--
|
||||
o = s:option(Flag, option_name("xmux"), translate("xMux"))
|
||||
o.default = 1
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "xtls-rprx-vision" })
|
||||
|
||||
o = s:option(Value, option_name("xudp_concurrency"), translate("XUDP Mux concurrency"))
|
||||
o.default = 8
|
||||
o:depends({ [option_name("xmux")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,533 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
local singbox_bin = api.finded_com("singbox")
|
||||
|
||||
if not singbox_bin then
|
||||
return
|
||||
end
|
||||
|
||||
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
||||
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
|
||||
local type_name = "sing-box"
|
||||
|
||||
local option_prefix = "singbox_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local ss_method_new_list = {
|
||||
"none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
local ss_method_old_list = {
|
||||
"aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20",
|
||||
}
|
||||
|
||||
local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" }
|
||||
|
||||
-- [[ sing-box ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Sing-Box")
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("socks", "Socks")
|
||||
o:value("http", "HTTP")
|
||||
o:value("shadowsocks", "Shadowsocks")
|
||||
if singbox_tags:find("with_shadowsocksr") then
|
||||
o:value("shadowsocksr", "ShadowsocksR")
|
||||
end
|
||||
o:value("vmess", "Vmess")
|
||||
o:value("trojan", "Trojan")
|
||||
if singbox_tags:find("with_wireguard") then
|
||||
o:value("wireguard", "WireGuard")
|
||||
end
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("hysteria", "Hysteria")
|
||||
end
|
||||
o:value("shadowtls", "ShadowTLS")
|
||||
o:value("vless", "VLESS")
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("tuic", "TUIC")
|
||||
end
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("hysteria2", "Hysteria2")
|
||||
end
|
||||
o:value("_shunt", translate("Shunt"))
|
||||
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
|
||||
|
||||
o = s:option(Value, option_name("iface"), translate("Interface"))
|
||||
o.default = "eth1"
|
||||
o:depends({ [option_name("protocol")] = "_iface" })
|
||||
|
||||
local nodes_table = {}
|
||||
local balancers_table = {}
|
||||
local iface_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
|
||||
if e.protocol == "_iface" then
|
||||
iface_table[#iface_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- [[ 分流模块 ]]
|
||||
if #nodes_table > 0 then
|
||||
o = s:option(Flag, option_name("preproxy_enabled"), translate("Preproxy"))
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
o = s:option(Value, option_name("main_node"), string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
|
||||
o:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true })
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
o.default = "nil"
|
||||
end
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if e[".name"] and e.remarks then
|
||||
o = s:option(Value, option_name(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({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
if #nodes_table > 0 then
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
local pt = s:option(ListValue, option_name(e[".name"] .. "_proxy_tag"), string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
|
||||
pt:value("nil", translate("Close"))
|
||||
pt:value("main", translate("Preproxy Node"))
|
||||
pt.default = "nil"
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
pt:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true, [option_name(e[".name"])] = v.id })
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
o = s:option(DummyValue, option_name("shunt_tips"), " ")
|
||||
o.not_rewrite = true
|
||||
o.rawhtml = true
|
||||
o.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
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
|
||||
local o = s:option(Value, option_name("default_node"), string.format('* <a style="color:red">%s</a>', translate("Default")))
|
||||
o:depends({ [option_name("protocol")] = "_shunt" })
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
|
||||
if #nodes_table > 0 then
|
||||
for k, v in pairs(balancers_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
for k, v in pairs(iface_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
end
|
||||
local dpt = s:option(ListValue, option_name("default_proxy_tag"), 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."))
|
||||
dpt:value("nil", translate("Close"))
|
||||
dpt:value("main", translate("Preproxy Node"))
|
||||
dpt.default = "nil"
|
||||
for k, v in pairs(nodes_table) do
|
||||
o:value(v.id, v.remarks)
|
||||
dpt:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true, [option_name("default_node")] = v.id })
|
||||
end
|
||||
end
|
||||
|
||||
-- [[ 分流模块 End ]]
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
local protocols = s.fields[option_name("protocol")].keylist
|
||||
if #protocols > 0 then
|
||||
for index, value in ipairs(protocols) do
|
||||
if not value:find("_") then
|
||||
s.fields[option_name("address")]:depends({ [option_name("protocol")] = value })
|
||||
s.fields[option_name("port")]:depends({ [option_name("protocol")] = value })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("shadowtls_version"), translate("Version"))
|
||||
o.default = "1"
|
||||
o:value("1", "ShadowTLS v1")
|
||||
o:value("2", "ShadowTLS v2")
|
||||
o:value("3", "ShadowTLS v3")
|
||||
o:depends({ [option_name("protocol")] = "shadowtls" })
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
o:depends({ [option_name("protocol")] = "shadowtls", [option_name("shadowtls_version")] = "2" })
|
||||
o:depends({ [option_name("protocol")] = "shadowtls", [option_name("shadowtls_version")] = "3" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(ListValue, option_name("security"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(security_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
|
||||
o = s:option(ListValue, option_name("ss_method"), translate("Encrypt Method"))
|
||||
o.rewrite_option = "method"
|
||||
for a, t in ipairs(ss_method_new_list) do o:value(t) end
|
||||
for a, t in ipairs(ss_method_old_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
if singbox_tags:find("with_shadowsocksr") then
|
||||
o = s:option(ListValue, option_name("ssr_method"), translate("Encrypt Method"))
|
||||
o.rewrite_option = "method"
|
||||
for a, t in ipairs(ss_method_old_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
o = s:option(ListValue, option_name("ssr_protocol"), translate("Protocol"))
|
||||
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
|
||||
o = s:option(Value, option_name("ssr_protocol_param"), translate("Protocol_param"))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
|
||||
local ssr_obfs_list = {
|
||||
"plain", "http_simple", "http_post", "random_head", "tls_simple",
|
||||
"tls1.0_session_auth", "tls1.2_ticket_auth"
|
||||
}
|
||||
|
||||
o = s:option(ListValue, option_name("ssr_obfs"), translate("Obfs"))
|
||||
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
|
||||
o = s:option(Value, option_name("ssr_obfs_param"), translate("Obfs_param"))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocksr" })
|
||||
end
|
||||
|
||||
o = s:option(Flag, option_name("uot"), translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side."))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "2022-blake3-aes-128-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "2022-blake3-aes-256-gcm" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "2022-blake3-chacha20-poly1305" })
|
||||
|
||||
o = s:option(Value, option_name("uuid"), translate("ID"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(ListValue, option_name("flow"), translate("flow"))
|
||||
o.default = ""
|
||||
o:value("", translate("Disable"))
|
||||
o:value("xtls-rprx-vision")
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("tls")] = true })
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(Value, option_name("hysteria_obfs"), translate("Obfs Password"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(ListValue, option_name("hysteria_auth_type"), translate("Auth Type"))
|
||||
o:value("disable", translate("Disable"))
|
||||
o:value("string", translate("STRING"))
|
||||
o:value("base64", translate("BASE64"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "hysteria", [option_name("hysteria_auth_type")] = "string"})
|
||||
o:depends({ [option_name("protocol")] = "hysteria", [option_name("hysteria_auth_type")] = "base64"})
|
||||
|
||||
o = s:option(Value, option_name("hysteria_up_mbps"), translate("Max upload Mbps"))
|
||||
o.default = "10"
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_down_mbps"), translate("Max download Mbps"))
|
||||
o.default = "50"
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_recv_window_conn"), translate("QUIC stream receive window"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_recv_window"), translate("QUIC connection receive window"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Flag, option_name("hysteria_disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_alpn"), translate("QUIC TLS ALPN"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
end
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(ListValue, option_name("tuic_congestion_control"), translate("Congestion control algorithm"))
|
||||
o.default = "cubic"
|
||||
o:value("bbr", translate("BBR"))
|
||||
o:value("cubic", translate("CUBIC"))
|
||||
o:value("new_reno", translate("New Reno"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(ListValue, option_name("tuic_udp_relay_mode"), translate("UDP relay mode"))
|
||||
o.default = "native"
|
||||
o:value("native", translate("native"))
|
||||
o:value("quic", translate("QUIC"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
--[[
|
||||
o = s:option(Flag, option_name("tuic_udp_over_stream"), translate("UDP over stream"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
]]--
|
||||
|
||||
o = s:option(Flag, option_name("tuic_zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(Value, option_name("tuic_heartbeat"), translate("Heartbeat interval(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "3"
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(Value, option_name("tuic_alpn"), translate("QUIC TLS ALPN"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
end
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(Value, option_name("hysteria2_up_mbps"), translate("Max upload Mbps"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_down_mbps"), translate("Max download Mbps"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(ListValue, option_name("hysteria2_obfs_type"), translate("Obfs Type"))
|
||||
o:value("", translate("Disable"))
|
||||
o:value("salamander")
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_obfs_password"), translate("Obfs Password"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "hysteria2"})
|
||||
end
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowtls" })
|
||||
|
||||
o = s:option(ListValue, option_name("alpn"), translate("alpn"))
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
o:value("h2,http/1.1")
|
||||
o:value("h2")
|
||||
o:value("http/1.1")
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Value, option_name("tls_serverName"), translate("Domain"))
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "hysteria"})
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "hysteria"})
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
if singbox_tags:find("with_utls") then
|
||||
o = s:option(Flag, option_name("utls"), translate("uTLS"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(ListValue, option_name("fingerprint"), translate("Finger Print"))
|
||||
o:value("chrome")
|
||||
o:value("firefox")
|
||||
o:value("edge")
|
||||
o:value("safari")
|
||||
-- o:value("360")
|
||||
o:value("qq")
|
||||
o:value("ios")
|
||||
-- o:value("android")
|
||||
o:value("random")
|
||||
-- o:value("randomized")
|
||||
o.default = "chrome"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("utls")] = true })
|
||||
|
||||
-- [[ REALITY部分 ]] --
|
||||
o = s:option(Flag, option_name("reality"), translate("REALITY"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("utls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "vmess", [option_name("utls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("utls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "socks", [option_name("utls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "trojan", [option_name("utls")] = true })
|
||||
|
||||
o = s:option(Value, option_name("reality_publicKey"), translate("Public Key"))
|
||||
o:depends({ [option_name("utls")] = true, [option_name("reality")] = true })
|
||||
|
||||
o = s:option(Value, option_name("reality_shortId"), translate("Short Id"))
|
||||
o:depends({ [option_name("utls")] = true, [option_name("reality")] = true })
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("http", "HTTP")
|
||||
o:value("ws", "WebSocket")
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("quic", "QUIC")
|
||||
end
|
||||
if singbox_tags:find("with_grpc") then
|
||||
o:value("grpc", "gRPC")
|
||||
end
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
if singbox_tags:find("with_wireguard") then
|
||||
o = s:option(Value, option_name("wireguard_public_key"), translate("Public Key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_secret_key"), translate("Private Key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_preSharedKey"), translate("Pre shared key"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(DynamicList, option_name("wireguard_local_address"), translate("Local Address"))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_mtu"), translate("MTU"))
|
||||
o.default = "1420"
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
|
||||
o = s:option(Value, option_name("wireguard_reserved"), translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
|
||||
o:depends({ [option_name("protocol")] = "wireguard" })
|
||||
end
|
||||
|
||||
-- [[ HTTP部分 ]]--
|
||||
o = s:option(Value, option_name("http_host"), translate("HTTP Host"))
|
||||
o:depends({ [option_name("transport")] = "http" })
|
||||
|
||||
o = s:option(Value, option_name("http_path"), translate("HTTP Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("transport")] = "http" })
|
||||
|
||||
o = s:option(Flag, option_name("http_h2_health_check"), translate("Health check"))
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "http" })
|
||||
|
||||
o = s:option(Value, option_name("http_h2_read_idle_timeout"), translate("Idle timeout"))
|
||||
o.default = "10"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "http", [option_name("http_h2_health_check")] = true })
|
||||
|
||||
o = s:option(Value, option_name("http_h2_health_check_timeout"), translate("Health check timeout"))
|
||||
o.default = "15"
|
||||
o:depends({ [option_name("tls")] = true, [option_name("transport")] = "http", [option_name("http_h2_health_check")] = true })
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Flag, option_name("ws_enableEarlyData"), translate("Enable early data"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_maxEarlyData"), translate("Early data length"))
|
||||
o.default = "1024"
|
||||
o:depends({ [option_name("ws_enableEarlyData")] = true })
|
||||
|
||||
o = s:option(Value, option_name("ws_earlyDataHeaderName"), translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
|
||||
o:depends({ [option_name("ws_enableEarlyData")] = true })
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
if singbox_tags:find("with_grpc") then
|
||||
o = s:option(Value, option_name("grpc_serviceName"), "ServiceName")
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Flag, option_name("grpc_health_check"), translate("Health check"))
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Value, option_name("grpc_idle_timeout"), translate("Idle timeout"))
|
||||
o.default = "10"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
|
||||
o = s:option(Value, option_name("grpc_health_check_timeout"), translate("Health check timeout"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("grpc_permit_without_stream"), translate("Permit without stream"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("grpc_health_check")] = true })
|
||||
end
|
||||
|
||||
-- [[ Mux ]]--
|
||||
o = s:option(Flag, option_name("mux"), translate("Mux"))
|
||||
o.rmempty = false
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "" })
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("uot")] = "" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
o = s:option(ListValue, option_name("mux_type"), translate("Mux"))
|
||||
o:value("smux")
|
||||
o:value("yamux")
|
||||
o:value("h2mux")
|
||||
o:depends({ [option_name("mux")] = true })
|
||||
|
||||
o = s:option(Value, option_name("mux_concurrency"), translate("Mux concurrency"))
|
||||
o.default = 8
|
||||
o:depends({ [option_name("mux")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("mux_padding"), translate("Padding"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("mux")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,57 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("sslocal") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SS-Rust"
|
||||
|
||||
local option_prefix = "ssrust_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local ssrust_encrypt_method_list = {
|
||||
"plain", "none",
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
-- [[ Shadowsocks Rust ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(Value, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(ListValue, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
|
||||
o:value("false")
|
||||
o:value("true")
|
||||
|
||||
o = s:option(ListValue, option_name("plugin"), translate("plugin"))
|
||||
o:value("none", translate("none"))
|
||||
if api.is_finded("xray-plugin") then o:value("xray-plugin") end
|
||||
if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
|
||||
if api.is_finded("obfs-local") then o:value("obfs-local") end
|
||||
|
||||
o = s:option(Value, option_name("plugin_opts"), translate("opts"))
|
||||
o:depends({ [option_name("plugin")] = "xray-plugin"})
|
||||
o:depends({ [option_name("plugin")] = "v2ray-plugin"})
|
||||
o:depends({ [option_name("plugin")] = "obfs-local"})
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,58 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("ss-local") and not api.is_finded("ss-redir") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SS"
|
||||
|
||||
local option_prefix = "ss_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
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"
|
||||
}
|
||||
|
||||
-- [[ Shadowsocks Libev ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Shadowsocks Libev"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(Value, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(ListValue, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
|
||||
o:value("false")
|
||||
o:value("true")
|
||||
|
||||
o = s:option(ListValue, option_name("plugin"), translate("plugin"))
|
||||
o:value("none", translate("none"))
|
||||
if api.is_finded("xray-plugin") then o:value("xray-plugin") end
|
||||
if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
|
||||
if api.is_finded("obfs-local") then o:value("obfs-local") end
|
||||
|
||||
o = s:option(Value, option_name("plugin_opts"), translate("opts"))
|
||||
o:depends({ [option_name("plugin")] = "xray-plugin"})
|
||||
o:depends({ [option_name("plugin")] = "v2ray-plugin"})
|
||||
o:depends({ [option_name("plugin")] = "obfs-local"})
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,69 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("ssr-local") and not api.is_finded("ssr-redir")then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SSR"
|
||||
|
||||
local option_prefix = "ssr_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
-- [[ ShadowsocksR Libev ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("ShadowsocksR Libev"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("protocol_param"), translate("Protocol_param"))
|
||||
|
||||
o = s:option(ListValue, option_name("obfs"), translate("Obfs"))
|
||||
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("obfs_param"), translate("Obfs_param"))
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(ListValue, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
|
||||
o:value("false")
|
||||
o:value("true")
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,119 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("trojan-go") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Trojan-Go"
|
||||
|
||||
local option_prefix = "trojan_go_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local encrypt_methods_ss_aead = {
|
||||
"chacha20-ietf-poly1305",
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
}
|
||||
|
||||
-- [[ Trojan Go ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Trojan-Go")
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
|
||||
o:value("false")
|
||||
o:value("true")
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 1
|
||||
|
||||
o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Value, option_name("tls_serverName"), translate("Domain"))
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("tls_sessionTicket"), translate("Session Ticket"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(ListValue, option_name("fingerprint"), translate("Finger Print"))
|
||||
o:value("disable", translate("Disable"))
|
||||
o:value("firefox")
|
||||
o:value("chrome")
|
||||
o:value("ios")
|
||||
o.default = "disable"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("original", translate("Original"))
|
||||
o:value("ws", "WebSocket")
|
||||
o.default = "original"
|
||||
o.rewrite_option = "trojan_transport"
|
||||
|
||||
o = s:option(ListValue, option_name("plugin_type"), translate("Transport Plugin"))
|
||||
o:value("plaintext", "Plain Text")
|
||||
o:value("shadowsocks", "ShadowSocks")
|
||||
o:value("other", "Other")
|
||||
o.default = "plaintext"
|
||||
o:depends({ [option_name("tls")] = false, [option_name("transport")] = "original" })
|
||||
|
||||
o = s:option(Value, option_name("plugin_cmd"), translate("Plugin Binary"))
|
||||
o.placeholder = "eg: /usr/bin/v2ray-plugin"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(Value, option_name("plugin_option"), translate("Plugin Option"))
|
||||
o.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(DynamicList, option_name("plugin_arg"), translate("Plugin Option Args"))
|
||||
o.placeholder = "eg: [\"-config\", \"test.json\"]"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o.placeholder = "/"
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
-- [[ Shadowsocks2 ]] --
|
||||
o = s:option(Flag, option_name("ss_aead"), translate("Shadowsocks secondary encryption"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(ListValue, option_name("ss_aead_method"), translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss_aead) do o:value(v, v) end
|
||||
o.default = "aes-128-gcm"
|
||||
o:depends({ [option_name("ss_aead")] = true })
|
||||
|
||||
o = s:option(Value, option_name("ss_aead_pwd"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("ss_aead")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("smux"), translate("Smux"))
|
||||
|
||||
o = s:option(Value, option_name("mux_concurrency"), translate("Mux concurrency"))
|
||||
o.default = 8
|
||||
o:depends({ [option_name("smux")] = true })
|
||||
|
||||
o = s:option(Value, option_name("smux_idle_timeout"), translate("Mux idle timeout"))
|
||||
o.default = 60
|
||||
o:depends({ [option_name("smux")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,56 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("trojan-plus") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Trojan-Plus"
|
||||
|
||||
local option_prefix = "trojan_plus_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Trojan Plus ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Trojan-Plus")
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
|
||||
o:value("false")
|
||||
o:value("true")
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
local type = s.fields["type"]:formvalue(t) or ""
|
||||
if value == "0" and type == type_name then
|
||||
return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Value, option_name("tls_serverName"), translate("Domain"))
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("tls_sessionTicket"), translate("Session Ticket"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,133 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("tuic-client") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "TUIC"
|
||||
|
||||
local option_prefix = "tuic_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ TUIC ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("TUIC"))
|
||||
|
||||
o = s:option(Value, option_name("address"), translate("Address (Support Domain Name)"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("uuid"), translate("ID"))
|
||||
o.password = true
|
||||
|
||||
-- Tuic Password for remote server connect
|
||||
o = s:option(Value, option_name("password"), translate("TUIC User Password For Connect Remote Server"))
|
||||
o.password = true
|
||||
o.rmempty = true
|
||||
o.default = ""
|
||||
o.rewrite_option = o.option
|
||||
|
||||
--[[
|
||||
-- Tuic username for local socks connect
|
||||
o = s:option(Value, option_name("socks_username"), translate("TUIC UserName For Local Socks"))
|
||||
o.rmempty = true
|
||||
o.default = ""
|
||||
o.rewrite_option = o.option
|
||||
|
||||
-- Tuic Password for local socks connect
|
||||
o = s:option(Value, option_name("socks_password"), translate("TUIC Password For Local Socks"))
|
||||
o.password = true
|
||||
o.rmempty = true
|
||||
o.default = ""
|
||||
o.rewrite_option = o.option
|
||||
--]]
|
||||
|
||||
o = s:option(Value, option_name("ip"), translate("Set the TUIC proxy server ip address"))
|
||||
o.datatype = "ipaddr"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(ListValue, option_name("udp_relay_mode"), translate("UDP relay mode"))
|
||||
o:value("native", translate("native"))
|
||||
o:value("quic", translate("QUIC"))
|
||||
o.default = "native"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(ListValue, option_name("congestion_control"), translate("Congestion control algorithm"))
|
||||
o:value("bbr", translate("BBR"))
|
||||
o:value("cubic", translate("CUBIC"))
|
||||
o:value("new_reno", translate("New Reno"))
|
||||
o.default = "cubic"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("heartbeat"), translate("Heartbeat interval(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "3"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Timeout for establishing a connection to server(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "8"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("gc_interval"), translate("Garbage collection interval(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "3"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("gc_lifetime"), translate("Garbage collection lifetime(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "15"
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("send_window"), translate("TUIC send window"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 20971520
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("receive_window"), translate("TUIC receive window"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 10485760
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("max_package_size"), translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 1500
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
--Tuic settings for the local inbound socks5 server
|
||||
o = s:option(Flag, option_name("dual_stack"), translate("Set if the listening socket should be dual-stack"))
|
||||
o.default = 0
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("disable_sni"), translate("Disable SNI"))
|
||||
o.default = 0
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake"))
|
||||
o.default = 0
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(DynamicList, option_name("tls_alpn"), translate("TLS ALPN"))
|
||||
o.rmempty = true
|
||||
o.rewrite_option = o.option
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,65 +0,0 @@
|
||||
local api = require "luci.passwall.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 == "sing-box" or v == "Xray" then
|
||||
local protocol = m:get(n, "protocol")
|
||||
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
|
@ -1,41 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("brook") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Brook"
|
||||
|
||||
local option_prefix = "brook_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Brook ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Brook"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("server", "Brook")
|
||||
o:value("wsserver", "WebSocket")
|
||||
|
||||
--o = s:option(Flag, option_name("tls"), translate("Use TLS"))
|
||||
--o:depends({ [option_name("protocol")] = "wsserver" })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o:depends({ [option_name("protocol")] = "wsserver" })
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,113 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("hysteria") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Hysteria"
|
||||
|
||||
local option_prefix = "hysteria_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Hysteria ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Hysteria"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("udp", "UDP")
|
||||
o:value("faketcp", "faketcp")
|
||||
o:value("wechat-video", "wechat-video")
|
||||
|
||||
o = s:option(Value, option_name("obfs"), translate("Obfs Password"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(ListValue, option_name("auth_type"), translate("Auth Type"))
|
||||
o:value("disable", translate("Disable"))
|
||||
o:value("string", translate("STRING"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("auth_type")] = "string" })
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("alpn"), translate("QUIC TLS ALPN"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("udp"), translate("UDP"))
|
||||
o.default = "1"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("up_mbps"), translate("Max upload Mbps"))
|
||||
o.default = "10"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("down_mbps"), translate("Max download Mbps"))
|
||||
o.default = "50"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("recv_window_conn"), translate("QUIC stream receive window"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, option_name("recv_window"), translate("QUIC connection receive window"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
if value == "1" then
|
||||
local ca = s.fields[option_name("tls_certificateFile")]:formvalue(t) or ""
|
||||
local key = s.fields[option_name("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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,366 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("xray") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Xray"
|
||||
|
||||
local option_prefix = "xray_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local x_ss_method_list = {
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
local header_type_list = {
|
||||
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
|
||||
}
|
||||
|
||||
-- [[ Xray ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Xray")
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("vmess", "Vmess")
|
||||
o:value("vless", "VLESS")
|
||||
o:value("http", "HTTP")
|
||||
o:value("socks", "Socks")
|
||||
o:value("shadowsocks", "Shadowsocks")
|
||||
o:value("trojan", "Trojan")
|
||||
o:value("dokodemo-door", "dokodemo-door")
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Flag, option_name("auth"), translate("Auth"))
|
||||
o.validate = function(self, value, t)
|
||||
if value and value == "1" then
|
||||
local user_v = s.fields[option_name("username")]:formvalue(t) or ""
|
||||
local pass_v = s.fields[option_name("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
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(ListValue, option_name("d_protocol"), translate("Destination protocol"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("udp", "UDP")
|
||||
o:value("tcp,udp", "TCP,UDP")
|
||||
o:depends({ [option_name("protocol")] = "dokodemo-door" })
|
||||
|
||||
o = s:option(Value, option_name("d_address"), translate("Destination address"))
|
||||
o:depends({ [option_name("protocol")] = "dokodemo-door" })
|
||||
|
||||
o = s:option(Value, option_name("d_port"), translate("Destination port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ [option_name("protocol")] = "dokodemo-door" })
|
||||
|
||||
o = s:option(Value, option_name("decryption"), translate("Encrypt Method"))
|
||||
o.default = "none"
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
|
||||
o = s:option(ListValue, option_name("x_ss_method"), translate("Encrypt Method"))
|
||||
o.rewrite_option = "method"
|
||||
for a, t in ipairs(x_ss_method_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(Flag, option_name("iv_check"), translate("IV Check"))
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(ListValue, option_name("ss_network"), translate("Transport"))
|
||||
o.default = "tcp,udp"
|
||||
o:value("tcp", "TCP")
|
||||
o:value("udp", "UDP")
|
||||
o:value("tcp,udp", "TCP,UDP")
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(Flag, option_name("udp_forward"), translate("UDP Forward"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
|
||||
o = s:option(DynamicList, option_name("uuid"), translate("ID") .. "/" .. translate("Password"))
|
||||
for i = 1, 3 do
|
||||
o:value(api.gen_uuid(1))
|
||||
end
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
o = s:option(ListValue, option_name("flow"), translate("flow"))
|
||||
o.default = ""
|
||||
o:value("", translate("Disable"))
|
||||
o:value("xtls-rprx-vision")
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("tls")] = true, [option_name("transport")] = "tcp" })
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
if value == "1" then
|
||||
local ca = s.fields[option_name("tls_certificateFile")]:formvalue(t) or ""
|
||||
local key = s.fields[option_name("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
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
o = s:option(ListValue, option_name("alpn"), translate("alpn"))
|
||||
o.default = "h2,http/1.1"
|
||||
o:value("h2,http/1.1")
|
||||
o:value("h2")
|
||||
o:value("http/1.1")
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
-- o = s:option(Value, option_name("minversion"), translate("minversion"))
|
||||
-- o.default = "1.3"
|
||||
-- o:value("1.3")
|
||||
--o:depends({ [option_name("tls")] = true })
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("mkcp", "mKCP")
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("h2", "HTTP/2")
|
||||
o:value("ds", "DomainSocket")
|
||||
o:value("quic", "QUIC")
|
||||
o:value("grpc", "gRPC")
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
-- [[ HTTP/2部分 ]]--
|
||||
|
||||
o = s:option(Value, option_name("h2_host"), translate("HTTP/2 Host"))
|
||||
o:depends({ [option_name("transport")] = "h2" })
|
||||
|
||||
o = s:option(Value, option_name("h2_path"), translate("HTTP/2 Path"))
|
||||
o:depends({ [option_name("transport")] = "h2" })
|
||||
|
||||
-- [[ TCP部分 ]]--
|
||||
|
||||
-- TCP伪装
|
||||
o = s:option(ListValue, option_name("tcp_guise"), translate("Camouflage Type"))
|
||||
o:value("none", "none")
|
||||
o:value("http", "http")
|
||||
o:depends({ [option_name("transport")] = "tcp" })
|
||||
|
||||
-- HTTP域名
|
||||
o = s:option(DynamicList, option_name("tcp_guise_http_host"), translate("HTTP Host"))
|
||||
o:depends({ [option_name("tcp_guise")] = "http" })
|
||||
|
||||
-- HTTP路径
|
||||
o = s:option(DynamicList, option_name("tcp_guise_http_path"), translate("HTTP Path"))
|
||||
o:depends({ [option_name("tcp_guise")] = "http" })
|
||||
|
||||
-- [[ mKCP部分 ]]--
|
||||
|
||||
o = s:option(ListValue, option_name("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 o:value(t) end
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_mtu"), translate("KCP MTU"))
|
||||
o.default = "1350"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_tti"), translate("KCP TTI"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_uplinkCapacity"), translate("KCP uplinkCapacity"))
|
||||
o.default = "5"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_downlinkCapacity"), translate("KCP downlinkCapacity"))
|
||||
o.default = "20"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Flag, option_name("mkcp_congestion"), translate("KCP Congestion"))
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_readBufferSize"), translate("KCP readBufferSize"))
|
||||
o.default = "1"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_writeBufferSize"), translate("KCP writeBufferSize"))
|
||||
o.default = "1"
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
o = s:option(Value, option_name("mkcp_seed"), translate("KCP Seed"))
|
||||
o:depends({ [option_name("transport")] = "mkcp" })
|
||||
|
||||
-- [[ DomainSocket部分 ]]--
|
||||
|
||||
o = s:option(Value, option_name("ds_path"), "Path", translate("A legal file path. This file must not exist before running."))
|
||||
o:depends({ [option_name("transport")] = "ds" })
|
||||
|
||||
-- [[ QUIC部分 ]]--
|
||||
o = s:option(ListValue, option_name("quic_security"), translate("Encrypt Method"))
|
||||
o:value("none")
|
||||
o:value("aes-128-gcm")
|
||||
o:value("chacha20-poly1305")
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
o = s:option(Value, option_name("quic_key"), translate("Encrypt Method") .. translate("Key"))
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
o = s:option(ListValue, option_name("quic_guise"), translate("Camouflage Type"))
|
||||
for a, t in ipairs(header_type_list) do o:value(t) end
|
||||
o:depends({ [option_name("transport")] = "quic" })
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
o = s:option(Value, option_name("grpc_serviceName"), "ServiceName")
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Flag, option_name("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."))
|
||||
o:depends({ [option_name("transport")] = "tcp" })
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
-- [[ Fallback部分 ]]--
|
||||
o = s:option(Flag, option_name("fallback"), translate("Fallback"))
|
||||
o:depends({ [option_name("protocol")] = "vless", [option_name("transport")] = "tcp" })
|
||||
o:depends({ [option_name("protocol")] = "trojan", [option_name("transport")] = "tcp" })
|
||||
|
||||
--[[
|
||||
o = s:option(Value, option_name("fallback_alpn"), "Fallback alpn")
|
||||
o:depends({ [option_name("fallback")] = true })
|
||||
|
||||
o = s:option(Value, option_name("fallback_path"), "Fallback path")
|
||||
o:depends({ [option_name("fallback")] = true })
|
||||
|
||||
o = s:option(Value, option_name("fallback_dest"), "Fallback dest")
|
||||
o:depends({ [option_name("fallback")] = true })
|
||||
|
||||
o = s:option(Value, option_name("fallback_xver"), "Fallback xver")
|
||||
o.default = 0
|
||||
o:depends({ [option_name("fallback")] = true })
|
||||
]]--
|
||||
|
||||
o = s:option(DynamicList, option_name("fallback_list"), "Fallback", translate("dest,path"))
|
||||
o:depends({ [option_name("fallback")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("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."))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
|
||||
o.default = "0"
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and e.type == type_name then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("outbound_node"), translate("outbound node"))
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_socks", translate("Custom Socks"))
|
||||
o:value("_http", translate("Custom HTTP"))
|
||||
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
o.default = "nil"
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_address"), translate("Address (Support Domain Name)"))
|
||||
o:depends({ [option_name("outbound_node")] = "_socks"})
|
||||
o:depends({ [option_name("outbound_node")] = "_http"})
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ [option_name("outbound_node")] = "_socks"})
|
||||
o:depends({ [option_name("outbound_node")] = "_http"})
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_username"), translate("Username"))
|
||||
o:depends({ [option_name("outbound_node")] = "_socks"})
|
||||
o:depends({ [option_name("outbound_node")] = "_http"})
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("outbound_node")] = "_socks"})
|
||||
o:depends({ [option_name("outbound_node")] = "_http"})
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_iface"), translate("Interface"))
|
||||
o.default = "eth1"
|
||||
o:depends({ [option_name("outbound_node")] = "_iface"})
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, option_name("loglevel"), translate("Log Level"))
|
||||
o.default = "warning"
|
||||
o:value("debug")
|
||||
o:value("info")
|
||||
o:value("warning")
|
||||
o:value("error")
|
||||
o:depends({ [option_name("log")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,346 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
local singbox_bin = api.finded_com("singbox")
|
||||
|
||||
if not singbox_bin then
|
||||
return
|
||||
end
|
||||
|
||||
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
||||
|
||||
local type_name = "sing-box"
|
||||
|
||||
local option_prefix = "singbox_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local ss_method_list = {
|
||||
"none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
-- [[ Sing-Box ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Sing-Box")
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
o:value("mixed", "Mixed")
|
||||
o:value("socks", "Socks")
|
||||
o:value("http", "HTTP")
|
||||
o:value("shadowsocks", "Shadowsocks")
|
||||
o:value("vmess", "Vmess")
|
||||
o:value("vless", "VLESS")
|
||||
o:value("trojan", "Trojan")
|
||||
o:value("naive", "Naive")
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("hysteria", "Hysteria")
|
||||
end
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("tuic", "TUIC")
|
||||
end
|
||||
if singbox_tags:find("with_quic") then
|
||||
o:value("hysteria2", "Hysteria2")
|
||||
end
|
||||
o:value("direct", "Direct")
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Flag, option_name("auth"), translate("Auth"))
|
||||
o.validate = function(self, value, t)
|
||||
if value and value == "1" then
|
||||
local user_v = s.fields[option_name("username")]:formvalue(t) or ""
|
||||
local pass_v = s.fields[option_name("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
|
||||
o:depends({ [option_name("protocol")] = "mixed" })
|
||||
o:depends({ [option_name("protocol")] = "socks" })
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
o:depends({ [option_name("protocol")] = "naive" })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "naive" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(Value, option_name("hysteria_up_mbps"), translate("Max upload Mbps"))
|
||||
o.default = "100"
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_down_mbps"), translate("Max download Mbps"))
|
||||
o.default = "100"
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_obfs"), translate("Obfs Password"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(ListValue, option_name("hysteria_auth_type"), translate("Auth Type"))
|
||||
o:value("disable", translate("Disable"))
|
||||
o:value("string", translate("STRING"))
|
||||
o:value("base64", translate("BASE64"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "hysteria", [option_name("hysteria_auth_type")] = "string"})
|
||||
o:depends({ [option_name("protocol")] = "hysteria", [option_name("hysteria_auth_type")] = "base64"})
|
||||
|
||||
o = s:option(Value, option_name("hysteria_recv_window_conn"), translate("QUIC stream receive window"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_recv_window_client"), translate("QUIC connection receive window"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_max_conn_client"), translate("QUIC concurrent bidirectional streams"))
|
||||
o.default = "1024"
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Flag, option_name("hysteria_disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria_alpn"), translate("QUIC TLS ALPN"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
end
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(ListValue, option_name("tuic_congestion_control"), translate("Congestion control algorithm"))
|
||||
o.default = "cubic"
|
||||
o:value("bbr", translate("BBR"))
|
||||
o:value("cubic", translate("CUBIC"))
|
||||
o:value("new_reno", translate("New Reno"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(Flag, option_name("tuic_zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(Value, option_name("tuic_heartbeat"), translate("Heartbeat interval(second)"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "3"
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(Value, option_name("tuic_alpn"), translate("QUIC TLS ALPN"))
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
end
|
||||
|
||||
if singbox_tags:find("with_quic") then
|
||||
o = s:option(Flag, option_name("hysteria2_ignore_client_bandwidth"), translate("Commands the client to use the BBR flow control algorithm"))
|
||||
o.default = 0
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_up_mbps"), translate("Max upload Mbps"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2", [option_name("hysteria2_ignore_client_bandwidth")] = false })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_down_mbps"), translate("Max download Mbps"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2", [option_name("hysteria2_ignore_client_bandwidth")] = false })
|
||||
|
||||
o = s:option(ListValue, option_name("hysteria2_obfs_type"), translate("Obfs Type"))
|
||||
o:value("", translate("Disable"))
|
||||
o:value("salamander")
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_obfs_password"), translate("Obfs Password"))
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, option_name("hysteria2_auth_password"), translate("Auth Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("protocol")] = "hysteria2"})
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("d_protocol"), translate("Destination protocol"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("udp", "UDP")
|
||||
o:value("tcp,udp", "TCP,UDP")
|
||||
o:depends({ [option_name("protocol")] = "direct" })
|
||||
|
||||
o = s:option(Value, option_name("d_address"), translate("Destination address"))
|
||||
o:depends({ [option_name("protocol")] = "direct" })
|
||||
|
||||
o = s:option(Value, option_name("d_port"), translate("Destination port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ [option_name("protocol")] = "direct" })
|
||||
|
||||
o = s:option(Value, option_name("decryption"), translate("Encrypt Method"))
|
||||
o.default = "none"
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
|
||||
o = s:option(ListValue, option_name("ss_method"), translate("Encrypt Method"))
|
||||
o.rewrite_option = "method"
|
||||
for a, t in ipairs(ss_method_list) do o:value(t) end
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
|
||||
o = s:option(DynamicList, option_name("uuid"), translate("ID") .. "/" .. translate("Password"))
|
||||
for i = 1, 3 do
|
||||
o:value(api.gen_uuid(1))
|
||||
end
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
|
||||
o = s:option(ListValue, option_name("flow"), translate("flow"))
|
||||
o.default = ""
|
||||
o:value("", translate("Disable"))
|
||||
o:value("xtls-rprx-vision")
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
if value == "1" then
|
||||
local ca = s.fields[option_name("tls_certificateFile")]:formvalue(t) or ""
|
||||
local key = s.fields[option_name("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
|
||||
o:depends({ [option_name("protocol")] = "http" })
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
-- [[ TLS部分 ]] --
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
o.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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o:depends({ [option_name("protocol")] = "hysteria" })
|
||||
o:depends({ [option_name("protocol")] = "tuic" })
|
||||
o:depends({ [option_name("protocol")] = "hysteria2" })
|
||||
o.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
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("http", "HTTP")
|
||||
o:value("ws", "WebSocket")
|
||||
o:value("quic", "QUIC")
|
||||
o:value("grpc", "gRPC")
|
||||
o:depends({ [option_name("protocol")] = "shadowsocks" })
|
||||
o:depends({ [option_name("protocol")] = "vmess" })
|
||||
o:depends({ [option_name("protocol")] = "vless" })
|
||||
o:depends({ [option_name("protocol")] = "trojan" })
|
||||
|
||||
-- [[ HTTP部分 ]]--
|
||||
|
||||
o = s:option(Value, option_name("http_host"), translate("HTTP Host"))
|
||||
o:depends({ [option_name("transport")] = "http" })
|
||||
|
||||
o = s:option(Value, option_name("http_path"), translate("HTTP Path"))
|
||||
o:depends({ [option_name("transport")] = "http" })
|
||||
|
||||
-- [[ WebSocket部分 ]]--
|
||||
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
-- [[ gRPC部分 ]]--
|
||||
o = s:option(Value, option_name("grpc_serviceName"), "ServiceName")
|
||||
o:depends({ [option_name("transport")] = "grpc" })
|
||||
|
||||
o = s:option(Flag, option_name("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."))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
|
||||
o.default = "0"
|
||||
|
||||
local nodes_table = {}
|
||||
for k, e in ipairs(api.get_valid_nodes()) do
|
||||
if e.node_type == "normal" and e.type == type_name then
|
||||
nodes_table[#nodes_table + 1] = {
|
||||
id = e[".name"],
|
||||
remarks = e["remark"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(ListValue, option_name("outbound_node"), translate("outbound node"))
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_socks", translate("Custom Socks"))
|
||||
o:value("_http", translate("Custom HTTP"))
|
||||
o:value("_iface", translate("Custom Interface"))
|
||||
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
|
||||
o.default = "nil"
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_address"), translate("Address (Support Domain Name)"))
|
||||
o:depends({ [option_name("outbound_node")] = "_socks" })
|
||||
o:depends({ [option_name("outbound_node")] = "_http" })
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_port"), translate("Port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ [option_name("outbound_node")] = "_socks" })
|
||||
o:depends({ [option_name("outbound_node")] = "_http" })
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_username"), translate("Username"))
|
||||
o:depends({ [option_name("outbound_node")] = "_socks" })
|
||||
o:depends({ [option_name("outbound_node")] = "_http" })
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("outbound_node")] = "_socks" })
|
||||
o:depends({ [option_name("outbound_node")] = "_http" })
|
||||
|
||||
o = s:option(Value, option_name("outbound_node_iface"), translate("Interface"))
|
||||
o.default = "eth1"
|
||||
o:depends({ [option_name("outbound_node")] = "_iface" })
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, option_name("loglevel"), translate("Log Level"))
|
||||
o.default = "info"
|
||||
o:value("debug")
|
||||
o:value("info")
|
||||
o:value("warn")
|
||||
o:value("error")
|
||||
o:depends({ [option_name("log")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,46 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("microsocks") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Socks"
|
||||
|
||||
local option_prefix = "socks_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ microsocks ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Socks")
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Flag, option_name("auth"), translate("Auth"))
|
||||
o.validate = function(self, value, t)
|
||||
if value and value == "1" then
|
||||
local user_v = s.fields[option_name("username")]:formvalue(t) or ""
|
||||
local pass_v = s.fields[option_name("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
|
||||
|
||||
o = s:option(Value, option_name("username"), translate("Username"))
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("auth")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,47 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("ssserver") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SS-Rust"
|
||||
|
||||
local option_prefix = "ssrust_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local ssrust_encrypt_method_list = {
|
||||
"plain", "none",
|
||||
"aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
|
||||
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
|
||||
}
|
||||
|
||||
-- [[ Shadowsocks Rust ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(Flag, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,50 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("ss-server") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SS"
|
||||
|
||||
local option_prefix = "ss_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
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", "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"
|
||||
}
|
||||
|
||||
-- [[ Shadowsocks ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("Shadowsocks"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(Flag, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,74 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("ssr-server") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "SSR"
|
||||
|
||||
local option_prefix = "ssr_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
-- [[ ShadowsocksR ]]
|
||||
|
||||
s.fields["type"]:value(type_name, translate("ShadowsocksR"))
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(Value, option_name("password"), translate("Password"))
|
||||
o.password = true
|
||||
|
||||
o = s:option(ListValue, option_name("method"), translate("Encrypt Method"))
|
||||
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
|
||||
|
||||
o = s:option(ListValue, option_name("protocol"), translate("Protocol"))
|
||||
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("protocol_param"), translate("Protocol_param"))
|
||||
|
||||
o = s:option(ListValue, option_name("obfs"), translate("Obfs"))
|
||||
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
|
||||
|
||||
o = s:option(Value, option_name("obfs_param"), translate("Obfs_param"))
|
||||
|
||||
o = s:option(Value, option_name("timeout"), translate("Connection Timeout"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = 300
|
||||
|
||||
o = s:option(Flag, option_name("tcp_fast_open"), "TCP " .. translate("Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("udp_forward"), translate("UDP Forward"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,159 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.finded_com("trojan-go") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Trojan-Go"
|
||||
|
||||
local option_prefix = "trojan_go_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
local encrypt_methods_ss_aead = {
|
||||
"chacha20-ietf-poly1305",
|
||||
"aes-128-gcm",
|
||||
"aes-256-gcm",
|
||||
}
|
||||
|
||||
-- [[ Trojan-Go ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Trojan-Go")
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(DynamicList, option_name("uuid"), translate("ID") .. "/" .. translate("Password"))
|
||||
for i = 1, 3 do
|
||||
o:value(api.gen_uuid(1))
|
||||
end
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
local type = s.fields["type"]:formvalue(t) or ""
|
||||
if value == "0" and type == type_name then
|
||||
return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
|
||||
end
|
||||
if value == "1" then
|
||||
local ca = s.fields[option_name("tls_certificateFile")]:formvalue(t) or ""
|
||||
local key = s.fields[option_name("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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(Flag, option_name("tls_sessionTicket"), translate("Session Ticket"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(ListValue, option_name("transport"), translate("Transport"))
|
||||
o:value("original", translate("Original"))
|
||||
o:value("ws", "WebSocket")
|
||||
o.default = "original"
|
||||
|
||||
o = s:option(ListValue, option_name("plugin_type"), translate("Transport Plugin"))
|
||||
o:value("plaintext", "Plain Text")
|
||||
o:value("shadowsocks", "ShadowSocks")
|
||||
o:value("other", "Other")
|
||||
o.default = "plaintext"
|
||||
o:depends({ [option_name("tls")] = false, [option_name("transport")] = "original" })
|
||||
|
||||
o = s:option(Value, option_name("plugin_cmd"), translate("Plugin Binary"))
|
||||
o.placeholder = "eg: /usr/bin/v2ray-plugin"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(Value, option_name("plugin_option"), translate("Plugin Option"))
|
||||
o.placeholder = "eg: obfs=http;obfs-host=www.baidu.com"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(DynamicList, option_name("plugin_arg"), translate("Plugin Option Args"))
|
||||
o.placeholder = "eg: [\"-config\", \"test.json\"]"
|
||||
o:depends({ [option_name("plugin_type")] = "shadowsocks" })
|
||||
o:depends({ [option_name("plugin_type")] = "other" })
|
||||
|
||||
o = s:option(Value, option_name("ws_host"), translate("WebSocket Host"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Value, option_name("ws_path"), translate("WebSocket Path"))
|
||||
o:depends({ [option_name("transport")] = "ws" })
|
||||
|
||||
o = s:option(Flag, option_name("ss_aead"), translate("Shadowsocks secondary encryption"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(ListValue, option_name("ss_aead_method"), translate("Encrypt Method"))
|
||||
for _, v in ipairs(encrypt_methods_ss_aead) do o:value(v, v) end
|
||||
o.default = "aes-128-gcm"
|
||||
o:depends({ [option_name("ss_aead")] = true })
|
||||
|
||||
o = s:option(Value, option_name("ss_aead_pwd"), translate("Password"))
|
||||
o.password = true
|
||||
o:depends({ [option_name("ss_aead")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("tcp_fast_open"), translate("TCP Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("remote_enable"), translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, option_name("remote_address"), translate("Remote Address"))
|
||||
o.default = "127.0.0.1"
|
||||
o:depends({ [option_name("remote_enable")] = true })
|
||||
|
||||
o = s:option(Value, option_name("remote_port"), translate("Remote Port"))
|
||||
o.datatype = "port"
|
||||
o.default = "80"
|
||||
o:depends({ [option_name("remote_enable")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
|
||||
o = s:option(ListValue, option_name("loglevel"), translate("Log Level"))
|
||||
o.default = "2"
|
||||
o:value("0", "all")
|
||||
o:value("1", "info")
|
||||
o:value("2", "warn")
|
||||
o:value("3", "error")
|
||||
o:value("4", "fatal")
|
||||
o:depends({ [option_name("log")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,108 +0,0 @@
|
||||
local m, s = ...
|
||||
|
||||
local api = require "luci.passwall.api"
|
||||
|
||||
if not api.is_finded("trojan-plus") then
|
||||
return
|
||||
end
|
||||
|
||||
local type_name = "Trojan-Plus"
|
||||
|
||||
local option_prefix = "trojan_plus_"
|
||||
|
||||
local function option_name(name)
|
||||
return option_prefix .. name
|
||||
end
|
||||
|
||||
-- [[ Trojan-Plus ]]
|
||||
|
||||
s.fields["type"]:value(type_name, "Trojan-Plus")
|
||||
|
||||
o = s:option(Value, option_name("port"), translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
|
||||
o = s:option(DynamicList, option_name("uuid"), translate("ID") .. "/" .. translate("Password"))
|
||||
for i = 1, 3 do
|
||||
o:value(api.gen_uuid(1))
|
||||
end
|
||||
|
||||
o = s:option(Flag, option_name("tls"), translate("TLS"))
|
||||
o.default = 0
|
||||
o.validate = function(self, value, t)
|
||||
if value then
|
||||
local type = s.fields["type"]:formvalue(t) or ""
|
||||
if value == "0" and type == type_name then
|
||||
return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.")
|
||||
end
|
||||
if value == "1" then
|
||||
local ca = s.fields[option_name("tls_certificateFile")]:formvalue(t) or ""
|
||||
local key = s.fields[option_name("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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
|
||||
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(FileUpload, option_name("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
|
||||
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
o.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
|
||||
|
||||
o = s:option(Flag, option_name("tls_sessionTicket"), translate("Session Ticket"))
|
||||
o.default = "0"
|
||||
o:depends({ [option_name("tls")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("tcp_fast_open"), translate("TCP Fast Open"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Flag, option_name("remote_enable"), translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray/Xray WebSocket and more."))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, option_name("remote_address"), translate("Remote Address"))
|
||||
o.default = "127.0.0.1"
|
||||
o:depends({ [option_name("remote_enable")] = true })
|
||||
|
||||
o = s:option(Value, option_name("remote_port"), translate("Remote Port"))
|
||||
o.datatype = "port"
|
||||
o.default = "80"
|
||||
o:depends({ [option_name("remote_enable")] = true })
|
||||
|
||||
o = s:option(Flag, option_name("log"), translate("Log"))
|
||||
o.default = "1"
|
||||
|
||||
o = s:option(ListValue, option_name("loglevel"), translate("Log Level"))
|
||||
o.default = "2"
|
||||
o:value("0", "all")
|
||||
o:value("1", "info")
|
||||
o:value("2", "warn")
|
||||
o:value("3", "error")
|
||||
o:value("4", "fatal")
|
||||
o:depends({ [option_name("log")] = true })
|
||||
|
||||
api.luci_types(arg[1], m, s, type_name, option_prefix)
|
@ -1,33 +0,0 @@
|
||||
local api = require "luci.passwall.api"
|
||||
local fs = require "nixio.fs"
|
||||
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall/server/type/"
|
||||
|
||||
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
|
||||
|
||||
o = s:option(Flag, "enable", translate("Enable"))
|
||||
o.default = "1"
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(Value, "remarks", translate("Remarks"))
|
||||
o.default = translate("Remarks")
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "type", translate("Type"))
|
||||
|
||||
local type_table = {}
|
||||
for filename in fs.dir(types_dir) do
|
||||
table.insert(type_table, filename)
|
||||
end
|
||||
table.sort(type_table)
|
||||
|
||||
for index, value in ipairs(type_table) do
|
||||
local p_func = loadfile(types_dir .. value)
|
||||
setfenv(p_func, getfenv(1))(m, s)
|
||||
end
|
||||
|
||||
return m
|
File diff suppressed because it is too large
Load Diff
@ -1,102 +0,0 @@
|
||||
local _M = {}
|
||||
|
||||
local function gh_release_url(self)
|
||||
return "https://api.github.com/repos/" .. self.repo .. "/releases/latest"
|
||||
end
|
||||
|
||||
local function gh_pre_release_url(self)
|
||||
return "https://api.github.com/repos/" .. self.repo .. "/releases?per_page=1"
|
||||
end
|
||||
|
||||
_M.brook = {
|
||||
name = "Brook",
|
||||
repo = "txthinking/brook",
|
||||
get_url = gh_release_url,
|
||||
cmd_version = "-v | awk '{print $3}'",
|
||||
zipped = false,
|
||||
default_path = "/usr/bin/brook",
|
||||
match_fmt_str = "linux_%s$",
|
||||
file_tree = {}
|
||||
}
|
||||
|
||||
_M.hysteria = {
|
||||
name = "Hysteria",
|
||||
repo = "HyNetwork/hysteria",
|
||||
get_url = gh_release_url,
|
||||
cmd_version = "-v | awk '{print $3}'",
|
||||
zipped = false,
|
||||
default_path = "/usr/bin/hysteria",
|
||||
match_fmt_str = "linux%%-%s$",
|
||||
file_tree = {
|
||||
armv6 = "arm",
|
||||
armv7 = "arm"
|
||||
}
|
||||
}
|
||||
|
||||
_M["trojan-go"] = {
|
||||
name = "Trojan-Go",
|
||||
repo = "p4gefau1t/trojan-go",
|
||||
get_url = gh_release_url,
|
||||
cmd_version = "-version | awk '{print $2}' | sed -n 1P",
|
||||
zipped = true,
|
||||
default_path = "/usr/bin/trojan-go",
|
||||
match_fmt_str = "linux%%-%s%%.zip",
|
||||
file_tree = {
|
||||
aarch64 = "armv8",
|
||||
armv8 = "armv8",
|
||||
mips = "mips%-hardfloat",
|
||||
mipsel = "mipsle%-hardfloat"
|
||||
}
|
||||
}
|
||||
|
||||
_M.singbox = {
|
||||
name = "Sing-Box",
|
||||
repo = "SagerNet/sing-box",
|
||||
get_url = gh_pre_release_url,
|
||||
cmd_version = "version | awk '{print $3}' | sed -n 1P",
|
||||
zipped = true,
|
||||
zipped_suffix = "tar.gz",
|
||||
default_path = "/usr/bin/sing-box",
|
||||
match_fmt_str = "linux%%-%s",
|
||||
file_tree = {
|
||||
x86_64 = "amd64"
|
||||
}
|
||||
}
|
||||
|
||||
_M.xray = {
|
||||
name = "Xray",
|
||||
repo = "XTLS/Xray-core",
|
||||
get_url = gh_pre_release_url,
|
||||
cmd_version = "version | awk '{print $2}' | sed -n 1P",
|
||||
zipped = true,
|
||||
default_path = "/usr/bin/xray",
|
||||
match_fmt_str = "linux%%-%s",
|
||||
file_tree = {
|
||||
x86_64 = "64",
|
||||
x86 = "32",
|
||||
mips = "mips32",
|
||||
mipsel = "mips32le"
|
||||
}
|
||||
}
|
||||
|
||||
_M["chinadns-ng"] = {
|
||||
name = "ChinaDNS-NG",
|
||||
repo = "zfl9/chinadns-ng",
|
||||
get_url = gh_release_url,
|
||||
cmd_version = "-V | awk '{print $2}'",
|
||||
zipped = false,
|
||||
default_path = "/usr/bin/chinadns-ng",
|
||||
match_fmt_str = "%s$",
|
||||
file_tree = {
|
||||
x86_64 = "x86_64",
|
||||
x86 = "i686",
|
||||
mipsel = "mipsel",
|
||||
aarch64 = "aarch64",
|
||||
armv5 = "arm%-eabi",
|
||||
armv6 = "armv6%-eabihf",
|
||||
armv7 = "armv7l%-eabihf",
|
||||
armv8 = "aarch64"
|
||||
}
|
||||
}
|
||||
|
||||
return _M
|
@ -1,235 +0,0 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local action = arg[1]
|
||||
local api = require "luci.passwall.api"
|
||||
local sys = api.sys
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
local CONFIG = "passwall_server"
|
||||
local CONFIG_PATH = "/tmp/etc/" .. CONFIG
|
||||
local NFT_INCLUDE_FILE = CONFIG_PATH .. "/" .. CONFIG .. ".nft"
|
||||
local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log"
|
||||
local TMP_BIN_PATH = CONFIG_PATH .. "/bin"
|
||||
local require_dir = "luci.passwall."
|
||||
|
||||
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 nft_flag = api.is_finded("fw4") and "1" or "0"
|
||||
|
||||
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
|
||||
if nft_flag == "0" 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()
|
||||
else
|
||||
f:write("nft -f " .. NFT_INCLUDE_FILE .. "\n")
|
||||
f:close()
|
||||
end
|
||||
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))
|
||||
if nft_flag == "0" then
|
||||
ipt("-N PSW-SERVER")
|
||||
ipt("-I INPUT -j PSW-SERVER")
|
||||
ip6t("-N PSW-SERVER")
|
||||
ip6t("-I INPUT -j PSW-SERVER")
|
||||
else
|
||||
nft_file, err = io.open(NFT_INCLUDE_FILE, "w")
|
||||
nft_file:write('#!/usr/sbin/nft -f\n')
|
||||
nft_file:write('add chain inet fw4 PSW-SERVER\n')
|
||||
nft_file:write('flush chain inet fw4 PSW-SERVER\n')
|
||||
nft_file:write('insert rule inet fw4 input position 0 jump PSW-SERVER comment "PSW-SERVER"\n')
|
||||
end
|
||||
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 .. "util_shadowsocks").gen_config_server(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 == "SS-Rust" then
|
||||
config = require(require_dir .. "util_shadowsocks").gen_config_server(user)
|
||||
bin = ln_run("/usr/bin/ssserver", "ssserver", "-c " .. config_file, log_path)
|
||||
elseif type == "sing-box" then
|
||||
config = require(require_dir .. "util_sing-box").gen_config_server(user)
|
||||
bin = ln_run(api.get_app_path("singbox"), "sing-box", "run -c " .. config_file, log_path)
|
||||
elseif type == "Xray" then
|
||||
config = require(require_dir .. "util_xray").gen_config_server(user)
|
||||
bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path)
|
||||
elseif type == "Trojan" then
|
||||
config = require(require_dir .. "util_trojan").gen_config_server(user)
|
||||
bin = ln_run("/usr/sbin/trojan", "trojan", "-c " .. config_file, log_path)
|
||||
elseif type == "Trojan-Plus" then
|
||||
config = require(require_dir .. "util_trojan").gen_config_server(user)
|
||||
bin = ln_run("/usr/sbin/trojan-plus", "trojan-plus", "-c " .. config_file, log_path)
|
||||
elseif type == "Trojan-Go" then
|
||||
config = require(require_dir .. "util_trojan").gen_config_server(user)
|
||||
bin = ln_run(api.get_app_path("trojan-go"), "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_app_path("brook"), "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 .. "util_hysteria").gen_config_server(user)
|
||||
bin = ln_run(api.get_app_path("hysteria"), "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
|
||||
if nft_flag == "0" 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
|
||||
else
|
||||
nft_file:write(string.format('add rule inet fw4 PSW-SERVER meta l4proto tcp tcp dport {%s} counter accept comment "%s"\n', port, remarks))
|
||||
if udp_forward == 1 then
|
||||
nft_file:write(string.format('add rule inet fw4 PSW-SERVER meta l4proto udp udp dport {%s} counter accept comment "%s"\n', port, remarks))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
if nft_flag == "1" then
|
||||
nft_file:write("add rule inet fw4 PSW-SERVER return\n")
|
||||
nft_file:close()
|
||||
cmd("nft -f " .. NFT_INCLUDE_FILE)
|
||||
end
|
||||
gen_include()
|
||||
end
|
||||
|
||||
local function stop()
|
||||
cmd(string.format("top -bn1 | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1", CONFIG_PATH))
|
||||
if nft_flag == "0" then
|
||||
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")
|
||||
else
|
||||
local nft_cmd = "handles=$(nft -a list chain inet fw4 input | grep -E \"PSW-SERVER\" | awk -F '# handle ' '{print$2}')\n for handle in $handles; do\n nft delete rule inet fw4 input handle ${handle} 2>/dev/null\n done"
|
||||
cmd(nft_cmd)
|
||||
cmd("nft flush chain inet fw4 PSW-SERVER 2>/dev/null")
|
||||
cmd("nft delete chain inet fw4 PSW-SERVER 2>/dev/null")
|
||||
end
|
||||
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
|
@ -1,119 +0,0 @@
|
||||
module("luci.passwall.util_hysteria", package.seeall)
|
||||
local api = require "luci.passwall.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
function gen_config_server(node)
|
||||
local config = {
|
||||
listen = ":" .. node.port,
|
||||
protocol = node.protocol or "udp",
|
||||
obfs = node.hysteria_obfs,
|
||||
cert = node.tls_certificateFile,
|
||||
key = node.tls_keyFile,
|
||||
auth = (node.hysteria_auth_type == "string") and {
|
||||
mode = "password",
|
||||
config = {
|
||||
password = node.hysteria_auth_password
|
||||
}
|
||||
} or nil,
|
||||
disable_udp = (node.hysteria_udp == "0") and true or false,
|
||||
alpn = node.hysteria_alpn or nil,
|
||||
up_mbps = tonumber(node.hysteria_up_mbps) or 10,
|
||||
down_mbps = tonumber(node.hysteria_down_mbps) or 50,
|
||||
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
|
||||
}
|
||||
return config
|
||||
end
|
||||
|
||||
function gen_config(var)
|
||||
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 tcp_proxy_way = var["-tcp_proxy_way"]
|
||||
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
|
||||
|
||||
if (node.hysteria_hop) then
|
||||
server = server .. "," .. node.hysteria_hop
|
||||
end
|
||||
|
||||
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,
|
||||
handshake_timeout = (node.hysteria_handshake_timeout) and tonumber(node.hysteria_handshake_timeout) or nil,
|
||||
idle_timeout = (node.hysteria_idle_timeout) and tonumber(node.hysteria_idle_timeout) or nil,
|
||||
hop_interval = (node.hysteria_hop_interval) and tonumber(node.hysteria_hop_interval) or nil,
|
||||
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery) and true or false,
|
||||
fast_open = (node.fast_open == "1") and true or false,
|
||||
lazy_start = (node.hysteria_lazy_start) 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,
|
||||
redirect_tcp = ("redirect" == tcp_proxy_way and local_tcp_redir_port) and {
|
||||
listen = "0.0.0.0:" .. local_tcp_redir_port,
|
||||
timeout = 300
|
||||
} or nil,
|
||||
tproxy_tcp = ("tproxy" == tcp_proxy_way and 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
|
||||
}
|
||||
|
||||
return jsonc.stringify(config, 1)
|
||||
end
|
||||
|
||||
_G.gen_config = gen_config
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
print(func(api.get_function_args(arg)))
|
||||
end
|
||||
end
|
@ -1,39 +0,0 @@
|
||||
module("luci.passwall.util_naiveproxy", package.seeall)
|
||||
local api = require "luci.passwall.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
function gen_config(var)
|
||||
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
|
||||
}
|
||||
|
||||
return jsonc.stringify(config, 1)
|
||||
end
|
||||
|
||||
_G.gen_config = gen_config
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
print(func(api.get_function_args(arg)))
|
||||
end
|
||||
end
|
@ -1,144 +0,0 @@
|
||||
module("luci.passwall.util_shadowsocks", package.seeall)
|
||||
local api = require "luci.passwall.api"
|
||||
local uci = api.uci
|
||||
local jsonc = api.jsonc
|
||||
|
||||
function gen_config_server(node)
|
||||
local config = {}
|
||||
config.server_port = tonumber(node.port)
|
||||
config.password = node.password
|
||||
config.timeout = tonumber(node.timeout)
|
||||
config.fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false
|
||||
config.method = node.method
|
||||
|
||||
if node.type == "SS-Rust" then
|
||||
config.server = "::"
|
||||
config.mode = "tcp_and_udp"
|
||||
else
|
||||
config.server = {"[::0]", "0.0.0.0"}
|
||||
end
|
||||
|
||||
if node.type == "SSR" then
|
||||
config.protocol = node.protocol
|
||||
config.protocol_param = node.protocol_param
|
||||
config.obfs = node.obfs
|
||||
config.obfs_param = node.obfs_param
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
function gen_config(var)
|
||||
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
|
||||
|
||||
return jsonc.stringify(config, 1)
|
||||
end
|
||||
|
||||
_G.gen_config = gen_config
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
print(func(api.get_function_args(arg)))
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
@ -1,158 +0,0 @@
|
||||
module("luci.passwall.util_trojan", package.seeall)
|
||||
local api = require "luci.passwall.api"
|
||||
local uci = api.uci
|
||||
local json = api.jsonc
|
||||
|
||||
function gen_config_server(node)
|
||||
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(node.port),
|
||||
remote_addr = (node.remote_enable == "1" and node.remote_address) and node.remote_address or nil,
|
||||
remote_port = (node.remote_enable == "1" and node.remote_port) and tonumber(node.remote_port) or nil,
|
||||
password = node.uuid,
|
||||
log_level = (node.log and node.log == "1") and tonumber(node.loglevel) or 5,
|
||||
ssl = {
|
||||
cert = node.tls_certificateFile,
|
||||
key = node.tls_keyFile,
|
||||
key_password = "",
|
||||
cipher = cipher,
|
||||
cipher_tls13 = cipher13,
|
||||
prefer_server_cipher = true,
|
||||
reuse_session = true,
|
||||
session_ticket = (node.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 = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false,
|
||||
fast_open_qlen = 20
|
||||
}
|
||||
}
|
||||
if node.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 = ((node.tls == nil 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
|
||||
config.websocket = (node.trojan_transport == 'ws') and {
|
||||
enabled = true,
|
||||
path = node.ws_path or "/",
|
||||
host = node.ws_host or ""
|
||||
} or nil
|
||||
config.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
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
function gen_config(var)
|
||||
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
|
||||
return json.stringify(trojan, 1)
|
||||
end
|
||||
|
||||
_G.gen_config = gen_config
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
print(func(api.get_function_args(arg)))
|
||||
end
|
||||
end
|
@ -1,57 +0,0 @@
|
||||
module("luci.passwall.util_tuic", package.seeall)
|
||||
local api = require "luci.passwall.api"
|
||||
local uci = api.uci
|
||||
local json = api.jsonc
|
||||
|
||||
function gen_config(var)
|
||||
local node_id = var["-node"]
|
||||
if not node_id then
|
||||
print("-node 不能为空")
|
||||
return
|
||||
end
|
||||
local node = uci:get_all("passwall", node_id)
|
||||
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 "warn"
|
||||
|
||||
local tuic= {
|
||||
relay = {
|
||||
server = server_host .. ":" .. server_port,
|
||||
ip = node.tuic_ip,
|
||||
uuid = node.uuid,
|
||||
password = node.tuic_password,
|
||||
-- certificates = node.tuic_certificate and { node.tuic_certpath } or nil,
|
||||
udp_relay_mode = node.tuic_udp_relay_mode,
|
||||
congestion_control = node.tuic_congestion_control,
|
||||
heartbeat = node.tuic_heartbeat .. "s",
|
||||
timeout = node.tuic_timeout .. "s",
|
||||
gc_interval = node.tuic_gc_interval .. "s",
|
||||
gc_lifetime = node.tuic_gc_lifetime .. "s",
|
||||
alpn = node.tuic_tls_alpn,
|
||||
disable_sni = (node.tuic_disable_sni == "1"),
|
||||
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1"),
|
||||
send_window = tonumber(node.tuic_send_window),
|
||||
receive_window = tonumber(node.tuic_receive_window)
|
||||
},
|
||||
["local"] = {
|
||||
server = "[::]:" .. local_port,
|
||||
username = node.tuic_socks_username,
|
||||
password = node.tuic_socks_password,
|
||||
dual_stack = (node.tuic_dual_stack == "1") and true or false,
|
||||
max_packet_size = tonumber(node.tuic_max_package_size)
|
||||
},
|
||||
log_level = loglevel
|
||||
}
|
||||
return json.stringify(tuic, 1)
|
||||
end
|
||||
|
||||
_G.gen_config = gen_config
|
||||
|
||||
if arg[1] then
|
||||
local func =_G[arg[1]]
|
||||
if func then
|
||||
print(func(api.get_function_args(arg)))
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
@ -1,204 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.api"
|
||||
local com = require "luci.passwall.com"
|
||||
local version = {}
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var appInfoList = new Array();
|
||||
var inProgressCount = 0;
|
||||
var tokenStr = '<%=token%>';
|
||||
var checkUpdateText = '<%:Check 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 () {};
|
||||
|
||||
function addPageNotice() {
|
||||
if (inProgressCount === 0) {
|
||||
window.onbeforeunload = function (e) {
|
||||
e.returnValue = updateInProgressNotice;
|
||||
return updateInProgressNotice;
|
||||
};
|
||||
}
|
||||
inProgressCount++;
|
||||
}
|
||||
|
||||
function removePageNotice() {
|
||||
inProgressCount--;
|
||||
if (inProgressCount === 0) {
|
||||
window.onbeforeunload = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateSuccess(btn) {
|
||||
if (btn) {
|
||||
btn.value = updateSuccessText;
|
||||
btn.placeholder = updateSuccessText;
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
if (inProgressCount === 0) {
|
||||
window.setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function onRequestError(btn, errorMessage) {
|
||||
btn.disabled = false;
|
||||
btn.value = checkUpdateText;
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
if (errorMessage && ckeckDetailElm) {
|
||||
ckeckDetailElm.textContent = errorMessage
|
||||
}
|
||||
}
|
||||
|
||||
function onBtnClick(btn, app) {
|
||||
if (appInfoList[app] === undefined) {
|
||||
checkUpdate(btn, app);
|
||||
} else {
|
||||
doUpdate(btn, app);
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate(btn, app) {
|
||||
btn.disabled = true;
|
||||
btn.value = inProgressText;
|
||||
|
||||
addPageNotice();
|
||||
|
||||
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
|
||||
if (ckeckDetailElm) {
|
||||
ckeckDetailElm.textContent = "";
|
||||
}
|
||||
XHR.get('<%=api.url("check_")%>' + app, {
|
||||
token: tokenStr,
|
||||
arch: ''
|
||||
}, function (x, json) {
|
||||
removePageNotice();
|
||||
if (json.code) {
|
||||
appInfoList[app] = undefined;
|
||||
onRequestError(btn, json.error);
|
||||
} else {
|
||||
if (json.has_update) {
|
||||
appInfoList[app] = 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(btn, app) {
|
||||
btn.disabled = true;
|
||||
btn.value = downloadingText;
|
||||
|
||||
addPageNotice();
|
||||
|
||||
var appUpdateUrl = '<%=api.url("update_")%>' + app;
|
||||
var appInfo = appInfoList[app];
|
||||
// Download file
|
||||
XHR.get(appUpdateUrl, {
|
||||
token: tokenStr,
|
||||
url: appInfo ? appInfo.data.browser_download_url : '',
|
||||
size: appInfo ? appInfo.data.size / 1024 : null
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice();
|
||||
onRequestError(btn, json.error);
|
||||
} else if (json.zip) {
|
||||
btn.value = decompressioningText;
|
||||
|
||||
// Extract file
|
||||
XHR.get(appUpdateUrl, {
|
||||
token: tokenStr,
|
||||
task: 'extract',
|
||||
file: json.file,
|
||||
subfix: appInfo ? appInfo.type : ''
|
||||
}, function (x, json) {
|
||||
if (json.code) {
|
||||
removePageNotice();
|
||||
onRequestError(btn, json.error);
|
||||
} else {
|
||||
move(btn, appUpdateUrl, json.file);
|
||||
}
|
||||
}, 300)
|
||||
} else {
|
||||
move(btn, appUpdateUrl, json.file);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function move(btn, url, file) {
|
||||
btn.value = movingText;
|
||||
|
||||
// Move file to target dir
|
||||
XHR.get(url, {
|
||||
token: tokenStr,
|
||||
task: 'move',
|
||||
file: file
|
||||
}, function (x, json) {
|
||||
removePageNotice();
|
||||
if (json.code) {
|
||||
onRequestError(btn, json.error);
|
||||
} else {
|
||||
onUpdateSuccess(btn);
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title">Passwall <%:Version%></label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=api.get_version()%> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="passwall-check_btn"
|
||||
onclick="onBtnClick(this,'passwall');" value="<%:Check update%>" />
|
||||
<span id="passwall-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%for k, v in pairs(com) do
|
||||
version[k] = api.get_app_version(k)%>
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title"><%=v.name%>
|
||||
<%:Version%>
|
||||
</label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-value-description">
|
||||
<span>【 <%=version[k] ~="" and version[k] or translate("Null") %> 】</span>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" id="_<%=k%>-check_btn"
|
||||
onclick="onBtnClick(this,'<%=k%>');" value="<%:Check update%>" />
|
||||
<span id="_<%=k%>-check_btn-detail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%end%>
|
@ -1,43 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
@ -1,136 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.api"
|
||||
-%>
|
||||
<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++) {
|
||||
try {
|
||||
var dom = node.childNodes[i].childNodes[k];
|
||||
if (dom.id) {
|
||||
var s = dom.id.match(reg1);
|
||||
if (s) {
|
||||
var cbi_id = global_id + "-"
|
||||
var dom_id = dom.id.split(cbi_id).join(cbi_id.split("-").join(".")).split("cbi.").join("cbid.")
|
||||
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("socks://") != 0 && 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_select.tagName == "INPUT") {
|
||||
node_select = document.getElementById("cbi.combobox." + dom_id);
|
||||
}
|
||||
|
||||
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 + "'");
|
||||
var 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
try {
|
||||
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") {
|
||||
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);
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout("go()", 1000);
|
||||
|
||||
//]]>
|
||||
</script>
|
File diff suppressed because one or more lines are too long
@ -1,26 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
@ -1,31 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
@ -1,108 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
File diff suppressed because it is too large
Load Diff
@ -1,475 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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: #94e1ff !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>
|
@ -1,76 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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.join(",")
|
||||
},
|
||||
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>
|
@ -1,35 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
@ -1,38 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.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>
|
@ -1,23 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall.api"
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let socks_id = window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)
|
||||
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("socks_autoswitch_add_node")%>' + "?id=" + socks_id + "&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("socks_autoswitch_remove_node")%>' + "?id=" + socks_id + "&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%>" />
|
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
zh-cn
|
@ -1,4 +0,0 @@
|
||||
|
||||
config global 'global'
|
||||
option enable '0'
|
||||
|
@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
[[ "$ACTION" == "ifup" && $(uci get "passwall.@global[0].enabled") == "1" ]] && [ -f /var/lock/passwall_ready.lock ] && {
|
||||
default_device=$(ip route | grep default | awk -F 'dev ' '{print $2}' | awk '{print $1}')
|
||||
[ "$default_device" == "$DEVICE" ] && {
|
||||
LOCK_FILE_DIR=/var/lock
|
||||
[ ! -d ${LOCK_FILE_DIR} ] && mkdir -p ${LOCK_FILE_DIR}
|
||||
LOCK_FILE="${LOCK_FILE_DIR}/passwall_ifup.lock"
|
||||
if [ -s ${LOCK_FILE} ]; then
|
||||
SPID=$(cat ${LOCK_FILE})
|
||||
if [ -e /proc/${SPID}/status ]; then
|
||||
exit 1
|
||||
fi
|
||||
cat /dev/null > ${LOCK_FILE}
|
||||
fi
|
||||
echo $$ > ${LOCK_FILE}
|
||||
|
||||
/etc/init.d/passwall restart
|
||||
echo "passwall: restart when $INTERFACE ifup" > /dev/kmsg
|
||||
|
||||
rm -rf ${LOCK_FILE}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#!/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() {
|
||||
local delay=$(uci -q get ${CONFIG}.@global_delay[0].start_delay || echo 1)
|
||||
if [ "$delay" -gt 0 ]; then
|
||||
$APP_FILE echolog "执行启动延时 $delay 秒后再启动!"
|
||||
sleep $delay
|
||||
fi
|
||||
restart
|
||||
touch ${LOCK_FILE_DIR}/${CONFIG}_ready.lock
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
|
||||
start() {
|
||||
lua /usr/lib/lua/luci/passwall/server_app.lua start
|
||||
}
|
||||
|
||||
stop() {
|
||||
lua /usr/lib/lua/luci/passwall/server_app.lua stop
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
#!/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
|
||||
|
||||
touch /etc/config/passwall_show >/dev/null 2>&1
|
||||
[ ! -s "/etc/config/passwall" ] && cp -f /usr/share/passwall/0_default_config /etc/config/passwall
|
||||
|
||||
use_nft=$(uci -q get passwall.@global_forwarding[0].use_nft || echo "0")
|
||||
[ "${use_nft}" = "0" ] && {
|
||||
if [ -z "$(command -v iptables-legacy || command -v iptables)" ] || [ -z "$(command -v ipset)" ] || [ -z "$(dnsmasq --version | grep 'Compile time options:.* ipset')" ]; then
|
||||
[ "$(opkg list-installed | grep "firewall4")" ] && [ "$(opkg list-installed | grep "nftables")" ] && {
|
||||
[ "$(opkg list-installed | grep "kmod\-nft\-socket")" ] && [ "$(opkg list-installed | grep "kmod\-nft\-tproxy")" ] && [ "$(opkg list-installed | grep "kmod\-nft\-nat")" ] && {
|
||||
uci -q set passwall.@global_forwarding[0].use_nft=1
|
||||
uci -q commit passwall
|
||||
sed -i "s#use_nft '0'#use_nft '1'#g" /usr/share/passwall/0_default_config
|
||||
}
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
global_xray=$(uci -q get passwall.@global_xray[0])
|
||||
[ -z "${global_xray}" ] && {
|
||||
cfgid=$(uci add passwall global_xray)
|
||||
uci -q set passwall.${cfgid}.sniffing=$(uci -q get passwall.@global_forwarding[0].sniffing || echo "1")
|
||||
uci -q set passwall.${cfgid}.route_only=$(uci -q get passwall.@global_forwarding[0].route_only || echo "0")
|
||||
uci -q set passwall.${cfgid}.buffer_size=$(uci -q get passwall.@global_forwarding[0].buffer_size || echo "")
|
||||
|
||||
uci -q delete passwall.@global_forwarding[0].sniffing
|
||||
uci -q delete passwall.@global_forwarding[0].route_only
|
||||
uci -q delete passwall.@global_forwarding[0].buffer_size
|
||||
uci -q commit passwall
|
||||
}
|
||||
|
||||
sed -i "s#option tlsflow#option flow#g" /etc/config/passwall
|
||||
|
||||
global_singbox=$(uci -q get passwall.@global_singbox[0])
|
||||
[ -z "${global_singbox}" ] && {
|
||||
cfgid=$(uci add passwall global_singbox)
|
||||
uci -q set passwall.${cfgid}.sniff_override_destination=1
|
||||
uci -q set passwall.${cfgid}.geoip_path="/tmp/singbox/geoip.db"
|
||||
uci -q set passwall.${cfgid}.geoip_url="https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db"
|
||||
uci -q set passwall.${cfgid}.geosite_path="/tmp/singbox/geosite.db"
|
||||
uci -q set passwall.${cfgid}.geosite_url="https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db"
|
||||
uci -q commit passwall
|
||||
}
|
||||
|
||||
chmod +x /usr/share/passwall/*.sh
|
||||
|
||||
rm -f /tmp/luci-indexcache
|
||||
rm -rf /tmp/luci-modulecache/
|
||||
killall -HUP rpcd 2>/dev/null
|
||||
exit 0
|
@ -1,215 +0,0 @@
|
||||
|
||||
config global
|
||||
option enabled '0'
|
||||
option socks_enabled '0'
|
||||
option tcp_node 'nil'
|
||||
option udp_node 'nil'
|
||||
option tcp_node_socks_port '1070'
|
||||
option dns_mode 'dns2tcp'
|
||||
option remote_dns '1.1.1.1'
|
||||
option filter_proxy_ipv6 '0'
|
||||
option when_chnroute_default_dns 'direct'
|
||||
option tcp_proxy_mode 'chnroute'
|
||||
option udp_proxy_mode 'chnroute'
|
||||
option localhost_tcp_proxy_mode 'default'
|
||||
option localhost_udp_proxy_mode 'default'
|
||||
option acl_enable '0'
|
||||
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 '60'
|
||||
|
||||
config global_forwarding
|
||||
option tcp_no_redir_ports 'disable'
|
||||
option udp_no_redir_ports 'disable'
|
||||
option tcp_proxy_drop_ports 'disable'
|
||||
option udp_proxy_drop_ports '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 use_nft '0'
|
||||
option tcp_proxy_way 'redirect'
|
||||
option ipv6_tproxy '0'
|
||||
|
||||
config global_xray
|
||||
option sniffing '1'
|
||||
option route_only '0'
|
||||
|
||||
config global_singbox
|
||||
option sniff_override_destination '1'
|
||||
option geoip_path '/usr/share/singbox/geoip.db'
|
||||
option geoip_url 'https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db'
|
||||
option geosite_path '/usr/share/singbox/geosite.db'
|
||||
option geosite_url 'https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db'
|
||||
|
||||
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://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt'
|
||||
list chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
list chnroute_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt'
|
||||
list chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt'
|
||||
list chnroute6_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf'
|
||||
option v2ray_location_asset '/usr/share/v2ray/'
|
||||
|
||||
config global_app
|
||||
option singbox_file '/usr/bin/sing-box'
|
||||
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'
|
||||
option chinadns_ng '/usr/bin/chinadns-ng'
|
||||
|
||||
config global_subscribe
|
||||
option filter_keyword_mode '1'
|
||||
list filter_discard_list '过期时间'
|
||||
list filter_discard_list '剩余流量'
|
||||
list filter_discard_list 'QQ群'
|
||||
list filter_discard_list '官网'
|
||||
|
||||
config nodes 'myshunt'
|
||||
option remarks '分流总节点'
|
||||
option type 'Xray'
|
||||
option protocol '_shunt'
|
||||
option DirectGame '_direct'
|
||||
option ProxyGame '_default'
|
||||
option AD '_blackhole'
|
||||
option BT '_direct'
|
||||
option Proxy '_default'
|
||||
option Netflix '_default'
|
||||
option OpenAI '_default'
|
||||
option Direct '_direct'
|
||||
option default_node 'nil'
|
||||
option domainStrategy 'IPOnDemand'
|
||||
|
||||
config shunt_rules 'DirectGame'
|
||||
option remarks 'DirectGame'
|
||||
option domain_list 'api.steampowered.com
|
||||
regexp:\.cm.steampowered.com$
|
||||
regexp:\.steamserver.net$
|
||||
geosite:category-games@cn
|
||||
'
|
||||
option ip_list '103.10.124.0/24
|
||||
103.10.125.0/24
|
||||
103.28.54.0/24
|
||||
146.66.152.0/24
|
||||
146.66.155.0/24
|
||||
153.254.86.0/24
|
||||
155.133.224.0/23
|
||||
155.133.226.0/24
|
||||
155.133.227.0/24
|
||||
155.133.230.0/24
|
||||
155.133.232.0/24
|
||||
155.133.233.0/24
|
||||
155.133.234.0/24
|
||||
155.133.236.0/23
|
||||
155.133.238.0/24
|
||||
155.133.239.0/24
|
||||
155.133.240.0/23
|
||||
155.133.245.0/24
|
||||
155.133.246.0/24
|
||||
155.133.248.0/24
|
||||
155.133.249.0/24
|
||||
155.133.250.0/24
|
||||
155.133.251.0/24
|
||||
155.133.252.0/24
|
||||
155.133.253.0/24
|
||||
155.133.254.0/24
|
||||
155.133.255.0/24
|
||||
162.254.192.0/24
|
||||
162.254.193.0/24
|
||||
162.254.194.0/23
|
||||
162.254.195.0/24
|
||||
162.254.196.0/24
|
||||
162.254.197.0/24
|
||||
162.254.198.0/24
|
||||
162.254.199.0/24
|
||||
185.25.182.0/24
|
||||
185.25.183.0/24
|
||||
190.217.33.0/24
|
||||
192.69.96.0/22
|
||||
205.185.194.0/24
|
||||
205.196.6.0/24
|
||||
208.64.200.0/24
|
||||
208.64.201.0/24
|
||||
208.64.202.0/24
|
||||
208.64.203.0/24
|
||||
208.78.164.0/22'
|
||||
|
||||
config shunt_rules 'ProxyGame'
|
||||
option remarks 'ProxyGame'
|
||||
option domain_list 'geosite:category-games@!cn
|
||||
domain:store.steampowered.com
|
||||
'
|
||||
|
||||
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 'OpenAI'
|
||||
option remarks 'OpenAI'
|
||||
option domain_list 'geosite:openai'
|
||||
|
||||
config shunt_rules 'Proxy'
|
||||
option remarks 'Proxy'
|
||||
option domain_list 'geosite:geolocation-!cn'
|
||||
option ip_list '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
|
||||
2001:4860:4860::8888
|
||||
2001:4860:4860::8844
|
||||
2606:4700:4700::1111
|
||||
2606:4700:4700::1001'
|
||||
|
||||
config shunt_rules 'Netflix'
|
||||
option remarks 'Netflix'
|
||||
option domain_list 'geosite:netflix'
|
||||
|
||||
config shunt_rules 'Direct'
|
||||
option remarks 'Direct'
|
||||
option domain_list 'geosite:cn'
|
||||
option ip_list '223.5.5.5/32
|
||||
119.29.29.29/32
|
||||
180.76.76.76/32
|
||||
114.114.114.114/32
|
||||
geoip:cn
|
||||
geoip:private'
|
File diff suppressed because it is too large
Load Diff
@ -1,219 +0,0 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local api = require ("luci.passwall.api")
|
||||
local appname = api.appname
|
||||
local fs = api.fs
|
||||
local jsonc = api.jsonc
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
|
||||
local log = function(...)
|
||||
api.log(...)
|
||||
end
|
||||
|
||||
function get_ip_port_from(str)
|
||||
local result_port = sys.exec("echo -n " .. str .. " | sed -n 's/^.*[:#]\\([0-9]*\\)$/\\1/p'")
|
||||
local result_ip = sys.exec(string.format("__host=%s;__varport=%s;", str, result_port) .. "echo -n ${__host%%${__varport:+[:#]${__varport}*}}")
|
||||
return result_ip, result_port
|
||||
end
|
||||
|
||||
local new_port
|
||||
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 var = api.get_args(arg)
|
||||
local haproxy_path = var["-path"]
|
||||
local haproxy_conf = var["-conf"]
|
||||
local haproxy_dns = var["-dns"] or "119.29.29.29:53,223.5.5.5:53"
|
||||
|
||||
local cpu_thread = sys.exec('echo -n $(cat /proc/cpuinfo | grep "processor" | wc -l)') or "1"
|
||||
local health_check_type = uci:get(appname, "@global_haproxy[0]", "health_check_type") or "tcp"
|
||||
local health_check_inter = uci:get(appname, "@global_haproxy[0]", "health_check_inter") or "10"
|
||||
|
||||
log("HAPROXY 负载均衡...")
|
||||
fs.mkdir(haproxy_path)
|
||||
local haproxy_file = haproxy_path .. "/" .. haproxy_conf
|
||||
|
||||
local f_out = io.open(haproxy_file, "a")
|
||||
|
||||
local haproxy_config = [[
|
||||
global
|
||||
daemon
|
||||
log 127.0.0.1 local2
|
||||
maxconn 60000
|
||||
stats socket {{path}}/haproxy.sock
|
||||
nbthread {{nbthread}}
|
||||
external-check
|
||||
insecure-fork-wanted
|
||||
|
||||
defaults
|
||||
mode tcp
|
||||
log global
|
||||
option tcplog
|
||||
option dontlognull
|
||||
option http-server-close
|
||||
#option forwardfor except 127.0.0.0/8
|
||||
option redispatch
|
||||
retries 2
|
||||
timeout http-request 10s
|
||||
timeout queue 1m
|
||||
timeout connect 10s
|
||||
timeout client 1m
|
||||
timeout server 1m
|
||||
timeout http-keep-alive 10s
|
||||
timeout check 10s
|
||||
maxconn 3000
|
||||
|
||||
resolvers mydns
|
||||
resolve_retries 1
|
||||
timeout resolve 5s
|
||||
hold valid 600s
|
||||
{{dns}}
|
||||
]]
|
||||
|
||||
haproxy_config = haproxy_config:gsub("{{path}}", haproxy_path)
|
||||
haproxy_config = haproxy_config:gsub("{{nbthread}}", cpu_thread)
|
||||
|
||||
local mydns = ""
|
||||
local index = 0
|
||||
string.gsub(haproxy_dns, '[^' .. "," .. ']+', function(w)
|
||||
index = index + 1
|
||||
local s = w:gsub("#", ":")
|
||||
if not s:find(":") then
|
||||
s = s .. ":53"
|
||||
end
|
||||
mydns = mydns .. (index > 1 and "\n" or "") .. " " .. string.format("nameserver dns%s %s", index, s)
|
||||
end)
|
||||
haproxy_config = haproxy_config:gsub("{{dns}}", mydns)
|
||||
|
||||
f_out:write(haproxy_config)
|
||||
|
||||
local listens = {}
|
||||
|
||||
uci:foreach(appname, "haproxy_config", function(t)
|
||||
if t.enabled == "1" then
|
||||
local server_remark
|
||||
local server_address
|
||||
local server_port
|
||||
local lbss = t.lbss
|
||||
local listen_port = tonumber(t.haproxy_port) or 0
|
||||
local server_node = uci:get_all(appname, lbss)
|
||||
if server_node and server_node.address and server_node.port then
|
||||
server_remark = server_node.address .. ":" .. server_node.port
|
||||
server_address = server_node.address
|
||||
server_port = server_node.port
|
||||
t.origin_address = server_address
|
||||
t.origin_port = server_port
|
||||
if health_check_type == "passwall_logic" then
|
||||
if server_node.type ~= "Socks" then
|
||||
local relay_port = server_node.port
|
||||
new_port = get_new_port()
|
||||
local config_file = string.format("haproxy_%s_%s.json", t[".name"], 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",
|
||||
new_port, --flag
|
||||
server_node[".name"], --node
|
||||
"127.0.0.1", --bind
|
||||
new_port, --socks port
|
||||
config_file --config file
|
||||
)
|
||||
)
|
||||
)
|
||||
server_address = "127.0.0.1"
|
||||
server_port = new_port
|
||||
end
|
||||
end
|
||||
else
|
||||
server_address, server_port = get_ip_port_from(lbss)
|
||||
server_remark = server_address .. ":" .. server_port
|
||||
t.origin_address = server_address
|
||||
t.origin_port = server_port
|
||||
end
|
||||
if server_address and server_port and listen_port > 0 then
|
||||
if not listens[listen_port] then
|
||||
listens[listen_port] = {}
|
||||
end
|
||||
t.server_remark = server_remark
|
||||
t.server_address = server_address
|
||||
t.server_port = server_port
|
||||
table.insert(listens[listen_port], t)
|
||||
else
|
||||
log(" - 丢弃1个明显无效的节点")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local sortTable = {}
|
||||
for i in pairs(listens) do
|
||||
if i ~= nil then
|
||||
table.insert(sortTable, i)
|
||||
end
|
||||
end
|
||||
table.sort(sortTable, function(a,b) return (a < b) end)
|
||||
|
||||
for i, port in pairs(sortTable) do
|
||||
log(" + 入口 0.0.0.0:%s..." % port)
|
||||
|
||||
f_out:write("\n" .. string.format([[
|
||||
listen %s
|
||||
bind 0.0.0.0:%s
|
||||
mode tcp
|
||||
balance roundrobin
|
||||
]], port, port))
|
||||
|
||||
if health_check_type == "passwall_logic" then
|
||||
f_out:write(string.format([[
|
||||
option external-check
|
||||
external-check command "/usr/share/passwall/haproxy_check.sh"
|
||||
]], port, port))
|
||||
end
|
||||
|
||||
for i, o in ipairs(listens[port]) do
|
||||
local remark = o.server_remark
|
||||
local server = o.server_address .. ":" .. o.server_port
|
||||
local server_conf = "server {{remark}} {{server}} weight {{weight}} {{resolvers}} check inter {{inter}} rise 1 fall 3 {{backup}}"
|
||||
server_conf = server_conf:gsub("{{remark}}", remark)
|
||||
server_conf = server_conf:gsub("{{server}}", server)
|
||||
server_conf = server_conf:gsub("{{weight}}", o.lbweight)
|
||||
local resolvers = "resolvers mydns"
|
||||
if api.is_ip(o.server_address) then
|
||||
resolvers = ""
|
||||
end
|
||||
server_conf = server_conf:gsub("{{resolvers}}", resolvers)
|
||||
server_conf = server_conf:gsub("{{inter}}", tonumber(health_check_inter) .. "s")
|
||||
server_conf = server_conf:gsub("{{backup}}", o.backup == "1" and "backup" or "")
|
||||
|
||||
f_out:write(" " .. server_conf .. "\n")
|
||||
|
||||
if o.export ~= "0" then
|
||||
sys.call(string.format("/usr/share/passwall/app.sh add_ip2route %s %s", o.origin_address, o.export))
|
||||
end
|
||||
|
||||
log(string.format(" | - 出口节点:%s:%s,权重:%s", o.origin_address, o.origin_port, o.lbweight))
|
||||
end
|
||||
end
|
||||
|
||||
--控制台配置
|
||||
local console_port = uci:get(appname, "@global_haproxy[0]", "console_port")
|
||||
local console_user = uci:get(appname, "@global_haproxy[0]", "console_user")
|
||||
local console_password = uci:get(appname, "@global_haproxy[0]", "console_password")
|
||||
local str = [[
|
||||
listen console
|
||||
bind 0.0.0.0:%s
|
||||
mode http
|
||||
stats refresh 30s
|
||||
stats uri /
|
||||
stats admin if TRUE
|
||||
%s
|
||||
]]
|
||||
f_out:write("\n" .. string.format(str, console_port, (console_user and console_user ~= "" and console_password and console_password ~= "") and "stats auth " .. console_user .. ":" .. console_password or ""))
|
||||
log(string.format(" * 控制台端口:%s", console_port))
|
||||
|
||||
f_out:close()
|
@ -1,18 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
listen_address=$1
|
||||
listen_port=$2
|
||||
server_address=$3
|
||||
server_port=$4
|
||||
status=$(/usr/bin/curl -I -o /dev/null -skL -x socks5h://${server_address}:${server_port} --connect-timeout 3 --retry 3 -w %{http_code} "https://www.google.com/generate_204")
|
||||
case "$status" in
|
||||
204|\
|
||||
200)
|
||||
status=200
|
||||
;;
|
||||
esac
|
||||
return_code=1
|
||||
if [ "$status" = "200" ]; then
|
||||
return_code=0
|
||||
fi
|
||||
exit ${return_code}
|
@ -1,89 +0,0 @@
|
||||
#!/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}
|
||||
}
|
||||
|
||||
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 $@
|
||||
;;
|
||||
del)
|
||||
del $@
|
||||
;;
|
||||
restart)
|
||||
restart $@
|
||||
;;
|
||||
logic_restart)
|
||||
logic_restart $@
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
@ -1,421 +0,0 @@
|
||||
require "luci.sys"
|
||||
local api = require "luci.passwall.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 CHNROUTE_MODE_DEFAULT_DNS = var["-CHNROUTE_MODE_DEFAULT_DNS"]
|
||||
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 NFTFLAG = var["-NFTFLAG"]
|
||||
local CACHE_PATH = api.CACHE_PATH
|
||||
local CACHE_FLAG = "dnsmasq_" .. FLAG
|
||||
local CACHE_DNS_PATH = CACHE_PATH .. "/" .. CACHE_FLAG
|
||||
local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt"
|
||||
|
||||
local uci = api.uci
|
||||
local sys = api.sys
|
||||
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
|
||||
api.log(...)
|
||||
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 cache_text = ""
|
||||
local new_rules = luci.sys.exec("echo -n $(find /usr/share/passwall/rules -type f | xargs md5sum)")
|
||||
local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. REMOTE_FAKEDNS .. CHNROUTE_MODE_DEFAULT_DNS .. CHINADNS_DNS .. PROXY_MODE .. NO_PROXY_IPV6 .. new_rules .. NFTFLAG
|
||||
if fs.access(CACHE_TEXT_FILE) then
|
||||
for line in io.lines(CACHE_TEXT_FILE) do
|
||||
cache_text = line
|
||||
end
|
||||
end
|
||||
|
||||
if cache_text ~= new_text then
|
||||
api.remove(CACHE_DNS_PATH .. "*")
|
||||
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
|
||||
|
||||
local dnsmasq_default_dns
|
||||
if CHNROUTE_MODE_DEFAULT_DNS ~= "nil" then
|
||||
if chnlist and CHNROUTE_MODE_DEFAULT_DNS == "remote" then
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
end
|
||||
if (chnlist or gfwlist) and CHNROUTE_MODE_DEFAULT_DNS == "chinadns_ng" and CHINADNS_DNS ~= "0" then
|
||||
dnsmasq_default_dns = CHINADNS_DNS
|
||||
end
|
||||
end
|
||||
|
||||
if global and (not returnhome and not chnlist and not gfwlist) then
|
||||
--只有全局模式时
|
||||
dnsmasq_default_dns = TUN_DNS
|
||||
only_global = 1
|
||||
end
|
||||
|
||||
local setflag_4= (NFTFLAG == "1") and "4#inet#fw4#" or ""
|
||||
local setflag_6= (NFTFLAG == "1") and "6#inet#fw4#" or ""
|
||||
|
||||
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, setflag_4 .. "passwall_vpslist," .. setflag_6 .. "passwall_vpslist6")
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 节点列表中的域名(vpslist):%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, setflag_4 .. "passwall_whitelist," .. setflag_6 .. "passwall_whitelist6")
|
||||
end
|
||||
end
|
||||
log(string.format(" - 域名白名单(whitelist):%s", LOCAL_DNS or "默认"))
|
||||
|
||||
local fwd_dns
|
||||
local ipset_flag
|
||||
local no_ipv6
|
||||
|
||||
--始终使用远程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 = setflag_4 .. "passwall_blacklist," .. setflag_6 .. "passwall_blacklist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
ipset_flag = setflag_4 .. "passwall_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 = setflag_4 .. "passwall_whitelist," .. setflag_6 .. "passwall_whitelist6"
|
||||
else
|
||||
fwd_dns = TUN_DNS
|
||||
ipset_flag = setflag_4 .. "passwall_shuntlist," .. setflag_6 .. "passwall_shuntlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = setflag_4 .. "passwall_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
|
||||
fwd_dns = TUN_DNS
|
||||
if CHNROUTE_MODE_DEFAULT_DNS == "chinadns_ng" and CHINADNS_DNS ~= "0" then
|
||||
fwd_dns = nil
|
||||
else
|
||||
local ipset_flag = setflag_4 .. "passwall_gfwlist," .. setflag_6 .. "passwall_gfwlist6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = setflag_4 .. "passwall_gfwlist"
|
||||
end
|
||||
if not only_global then
|
||||
if REMOTE_FAKEDNS == "1" then
|
||||
ipset_flag = nil
|
||||
end
|
||||
end
|
||||
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
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
if not only_global then
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, ipset_flag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
log(string.format(" - 防火墙域名表(gfwlist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
|
||||
if chnlist and fs.access("/usr/share/passwall/rules/chnlist") and (CHNROUTE_MODE_DEFAULT_DNS == "remote" or (CHNROUTE_MODE_DEFAULT_DNS == "chinadns_ng" and CHINADNS_DNS ~= "0")) then
|
||||
fwd_dns = LOCAL_DNS
|
||||
if CHNROUTE_MODE_DEFAULT_DNS == "chinadns_ng" and CHINADNS_DNS ~= "0" then
|
||||
fwd_dns = nil
|
||||
else
|
||||
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, setflag_4 .. "passwall_chnroute," .. setflag_6 .. "passwall_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 = setflag_4 .. "passwall_chnroute," .. setflag_6 .. "passwall_chnroute6"
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
ipset_flag = setflag_4 .. "passwall_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")
|
||||
local set_name = "ipset"
|
||||
if NFTFLAG == "1" then
|
||||
set_name = "nftset"
|
||||
end
|
||||
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("%s=/.%s/%s\n", set_name, key, ipsets_str))
|
||||
end
|
||||
end
|
||||
address_out:close()
|
||||
server_out:close()
|
||||
ipset_out:close()
|
||||
|
||||
local f_out = io.open(CACHE_TEXT_FILE, "a")
|
||||
f_out:write(new_text)
|
||||
f_out:close()
|
||||
end
|
||||
|
||||
if api.is_install("procd\\-ujail") then
|
||||
fs.copyr(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
|
||||
else
|
||||
api.remove(TMP_DNSMASQ_PATH)
|
||||
fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
|
||||
end
|
||||
|
||||
if DNSMASQ_CONF_FILE ~= "nil" then
|
||||
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
|
||||
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")
|
||||
conf_out:close()
|
||||
log(string.format(" - 以上所列以外及默认:%s", dnsmasq_default_dns))
|
||||
|
||||
if FLAG == "default" then
|
||||
local f_out = io.open("/tmp/etc/passwall/default_DNS", "a")
|
||||
f_out:write(DEFAULT_DNS)
|
||||
f_out:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
log(" - PassWall必须依赖于Dnsmasq,如果你自行配置了错误的DNS流程,将会导致域名(直连/代理域名)分流失效!!!")
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
#!/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
|
File diff suppressed because it is too large
Load Diff
@ -1,469 +0,0 @@
|
||||
#!/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.passwall.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://fastly.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://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf","https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf","https://fastly.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 asset_location = ucic:get_first(name, 'global_rules', "v2ray_location_asset", "/usr/share/v2ray/")
|
||||
local use_nft = ucic:get(name, "@global_forwarding[0]", "use_nft") or "0"
|
||||
|
||||
local log = function(...)
|
||||
if arg1 then
|
||||
if arg1 == "log" then
|
||||
api.log(...)
|
||||
elseif arg1 == "print" then
|
||||
local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
|
||||
print(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_nftset(set_name, ip_type, tmp_file, input_file)
|
||||
f = io.open(input_file, "r")
|
||||
local element = f:read("*all")
|
||||
f:close()
|
||||
|
||||
nft_file, err = io.open(tmp_file, "w")
|
||||
nft_file:write('#!/usr/sbin/nft -f\n')
|
||||
nft_file:write(string.format('define %s = {%s}\n', set_name, string.gsub(element, "%s*%c+", " timeout 3650d, ")))
|
||||
if luci.sys.call(string.format('nft "list set inet fw4 %s" >/dev/null 2>&1', set_name)) ~= 0 then
|
||||
nft_file:write(string.format('add set inet fw4 %s { type %s; flags interval, timeout; timeout 2d; gc-interval 2d; auto-merge; }\n', set_name, ip_type))
|
||||
end
|
||||
nft_file:write(string.format('add element inet fw4 %s $%s\n', set_name, set_name))
|
||||
nft_file:close()
|
||||
luci.sys.call(string.format('nft -f %s &>/dev/null',tmp_file))
|
||||
os.remove(tmp_file)
|
||||
end
|
||||
|
||||
--gen cache for nftset from file
|
||||
local function gen_cache(set_name, ip_type, input_file, output_file)
|
||||
local tmp_dir = "/tmp/"
|
||||
local tmp_file = output_file .. "_tmp"
|
||||
local tmp_set_name = set_name .. "_tmp"
|
||||
gen_nftset(tmp_set_name, ip_type, tmp_file, input_file)
|
||||
luci.sys.call("nft list set inet fw4 " ..tmp_set_name.. " | sed 's/" ..tmp_set_name.. "/" ..set_name.. "/g' | cat > " ..output_file)
|
||||
luci.sys.call("nft flush set inet fw4 " ..tmp_set_name)
|
||||
luci.sys.call("nft delete set inet fw4 " ..tmp_set_name)
|
||||
end
|
||||
|
||||
-- curl
|
||||
local function curl(url, file, valifile)
|
||||
local args = {
|
||||
"-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3"
|
||||
}
|
||||
if file then
|
||||
args[#args + 1] = "-o " .. file
|
||||
end
|
||||
if valifile then
|
||||
args[#args + 1] = "--dump-header " .. valifile
|
||||
end
|
||||
local return_code, result = api.curl_logic(url, nil, args)
|
||||
return tonumber(result)
|
||||
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
|
||||
|
||||
local function non_file_check(file_path, vali_file)
|
||||
if nixio.fs.readfile(file_path, 10) then
|
||||
local remote_file_size = tonumber(luci.sys.exec("cat " .. vali_file .. " | grep -i 'Content-Length' | awk '{print $2}'"))
|
||||
local local_file_size = tonumber(nixio.fs.stat(file_path, "size"))
|
||||
if remote_file_size and local_file_size then
|
||||
if remote_file_size == local_file_size then
|
||||
return nil;
|
||||
else
|
||||
log("下载文件大小校验出错,原始文件大小" .. remote_file_size .. "B,下载文件大小:" .. local_file_size .. "B。")
|
||||
return true;
|
||||
end
|
||||
else
|
||||
return nil;
|
||||
end
|
||||
else
|
||||
log("下载文件读取出错。")
|
||||
return true;
|
||||
end
|
||||
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 vali_file = "/tmp/" ..rule_name.. "_vali"
|
||||
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, vali_file..k)
|
||||
if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件过程出错,尝试重新下载。")
|
||||
os.remove(download_file_tmp..k)
|
||||
os.remove(vali_file..k)
|
||||
sret_tmp = curl(v, download_file_tmp..k, vali_file..k)
|
||||
if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then
|
||||
sret = 0
|
||||
sret_tmp = 0
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件过程出错,请检查网络或下载链接后重试!")
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
else
|
||||
sret = 0
|
||||
log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载失败,请检查网络或下载链接后重试!")
|
||||
end
|
||||
os.remove(download_file_tmp..k)
|
||||
os.remove(vali_file..k)
|
||||
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)
|
||||
|
||||
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)
|
||||
if use_nft == "1" and (rule_type == "ip6" or rule_type == "ip4") then
|
||||
local set_name = "passwall_" ..rule_name
|
||||
local output_file = file_tmp.. ".nft"
|
||||
if rule_type == "ip4" then
|
||||
gen_cache(set_name, "ipv4_addr", file_tmp, output_file)
|
||||
elseif rule_type == "ip6" then
|
||||
gen_cache(set_name, "ipv6_addr", file_tmp, output_file)
|
||||
end
|
||||
luci.sys.exec(string.format('mv -f %s %s', output_file, rule_path .. "/" ..rule_name.. ".nft"))
|
||||
os.remove(output_file)
|
||||
end
|
||||
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 return_code, content = api.curl_logic(geoip_api)
|
||||
local json = jsonc.parse(content)
|
||||
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(asset_location .. "geoip.dat") then
|
||||
luci.sys.call(string.format("cp -f %s %s", 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", asset_location, "/tmp/geoip.dat", 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 return_code, content = api.curl_logic(geosite_api)
|
||||
local json = jsonc.parse(content)
|
||||
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(asset_location .. "geosite.dat") then
|
||||
luci.sys.call(string.format("cp -f %s %s", 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", asset_location, "/tmp/geosite.dat", 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
|
||||
string.gsub(arg[2], '[^' .. "," .. ']+', function(w)
|
||||
if w == "gfwlist" then
|
||||
gfwlist_update = 1
|
||||
end
|
||||
if w == "chnroute" then
|
||||
chnroute_update = 1
|
||||
end
|
||||
if w == "chnroute6" then
|
||||
chnroute6_update = 1
|
||||
end
|
||||
if w == "chnlist" then
|
||||
chnlist_update = 1
|
||||
end
|
||||
if w == "geoip" then
|
||||
geoip_update = 1
|
||||
end
|
||||
if w == "geosite" then
|
||||
geosite_update = 1
|
||||
end
|
||||
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("重启服务,应用新的规则。")
|
||||
if use_nft == "1" then
|
||||
luci.sys.call("sh /usr/share/" .. name .. "/nftables.sh flush_nftset > /dev/null 2>&1 &")
|
||||
else
|
||||
luci.sys.call("sh /usr/share/" .. name .. "/iptables.sh flush_ipset > /dev/null 2>&1 &")
|
||||
end
|
||||
end
|
||||
log("规则更新完毕...")
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,18 +0,0 @@
|
||||
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
|
@ -1,6 +0,0 @@
|
||||
114.114.114.114
|
||||
114.114.115.115
|
||||
223.5.5.5
|
||||
223.6.6.6
|
||||
119.29.29.29
|
||||
180.76.76.76
|
@ -1,24 +0,0 @@
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
||||
0.0.0.0/8
|
||||
10.0.0.0/8
|
||||
100.64.0.0/10
|
||||
127.0.0.0/8
|
||||
169.254.0.0/16
|
||||
172.16.0.0/12
|
||||
192.0.0.0/24
|
||||
192.0.2.0/24
|
||||
192.88.99.0/24
|
||||
192.168.0.0/16
|
||||
198.19.0.0/16
|
||||
198.51.100.0/24
|
||||
203.0.113.0/24
|
||||
224.0.0.0/4
|
||||
240.0.0.0/4
|
||||
255.255.255.255/32
|
@ -1,12 +0,0 @@
|
||||
::/128
|
||||
::1/128
|
||||
::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
|
@ -1,16 +0,0 @@
|
||||
bing.com
|
||||
sspanel.net
|
||||
v2ex.com
|
||||
|
||||
#google
|
||||
googleapis.cn
|
||||
googleapis.com
|
||||
google.com.tw
|
||||
google.com.hk
|
||||
gstatic.com
|
||||
xn--ngstr-lra8j.com
|
||||
|
||||
#github
|
||||
github.com
|
||||
github.global.ssl.fastly.net
|
||||
assets-cdn.github.com
|
@ -1,19 +0,0 @@
|
||||
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
|
@ -1,180 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
CONFIG=passwall
|
||||
LOG_FILE=/tmp/log/$CONFIG.log
|
||||
LOCK_FILE_DIR=/tmp/lock
|
||||
|
||||
flag=0
|
||||
|
||||
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}"
|
||||
}
|
||||
|
||||
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 --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" ${extra_params} --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url")
|
||||
case "$status" in
|
||||
204)
|
||||
status=200
|
||||
;;
|
||||
esac
|
||||
echo $status
|
||||
}
|
||||
|
||||
test_proxy() {
|
||||
result=0
|
||||
status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x socks5h://127.0.0.1:${socks_port}")
|
||||
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
|
||||
}
|
||||
|
||||
test_node() {
|
||||
local node_id=$1
|
||||
local _type=$(echo $(config_n_get ${node_id} type nil) | tr 'A-Z' 'a-z')
|
||||
[ "${_type}" != "nil" ] && {
|
||||
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp)
|
||||
/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}"
|
||||
sleep 1s
|
||||
_proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx")
|
||||
pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.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
|
||||
}
|
||||
|
||||
test_auto_switch() {
|
||||
flag=$(expr $flag + 1)
|
||||
local b_nodes=$1
|
||||
local now_node=$2
|
||||
[ -z "$now_node" ] && {
|
||||
local f="/tmp/etc/$CONFIG/id/socks_${id}"
|
||||
if [ -f "${f}" ]; then
|
||||
now_node=$(cat ${f})
|
||||
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 "自动切换检测:${id}主节点【$(config_n_get $main_node type):[$(config_n_get $main_node remarks)]】正常,切换到主节点!"
|
||||
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${main_node}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog "自动切换检测:${id}节点切换完毕!"
|
||||
}
|
||||
return 0
|
||||
}
|
||||
fi
|
||||
|
||||
if [ "$status" == 0 ]; then
|
||||
#echolog "自动切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】正常。"
|
||||
return 0
|
||||
elif [ "$status" == 1 ]; then
|
||||
echolog "自动切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】异常,切换到下一个备用节点检测!"
|
||||
local new_node
|
||||
in_backup_nodes=$(echo $b_nodes | grep $now_node)
|
||||
# 判断当前节点是否存在于备用节点列表里
|
||||
if [ -z "$in_backup_nodes" ]; then
|
||||
# 如果不存在,设置第一个节点为新的节点
|
||||
new_node=$(echo $b_nodes | awk -F ' ' '{print $1}')
|
||||
else
|
||||
# 如果存在,设置下一个备用节点为新的节点
|
||||
#local count=$(expr $(echo $b_nodes | grep -o ' ' | wc -l) + 1)
|
||||
local next_node=$(echo $b_nodes | awk -F "$now_node" '{print $2}' | awk -F " " '{print $1}')
|
||||
if [ -z "$next_node" ]; then
|
||||
new_node=$(echo $b_nodes | awk -F ' ' '{print $1}')
|
||||
else
|
||||
new_node=$next_node
|
||||
fi
|
||||
fi
|
||||
test_node ${new_node}
|
||||
if [ $? -eq 0 ]; then
|
||||
[ "$restore_switch" == "0" ] && {
|
||||
uci set $CONFIG.${id}.node=$new_node
|
||||
[ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node
|
||||
uci commit $CONFIG
|
||||
}
|
||||
echolog "自动切换检测:${id}【$(config_n_get $new_node type):[$(config_n_get $new_node remarks)]】正常,切换到此节点!"
|
||||
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${new_node}
|
||||
[ $? -eq 0 ] && {
|
||||
echolog "自动切换检测:${id}节点切换完毕!"
|
||||
}
|
||||
return 0
|
||||
else
|
||||
test_auto_switch "${b_nodes}" ${new_node}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
id=$1
|
||||
LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_socks_auto_switch_${id}.lock
|
||||
main_node=$(config_n_get $id node nil)
|
||||
socks_port=$(config_n_get $id port 0)
|
||||
delay=$(config_n_get $id autoswitch_testing_time 30)
|
||||
sleep 5s
|
||||
connect_timeout=$(config_n_get $id autoswitch_connect_timeout 3)
|
||||
retry_num=$(config_n_get $id autoswitch_retry_num 1)
|
||||
restore_switch=$(config_n_get $id autoswitch_restore_switch 0)
|
||||
probe_url=$(config_n_get $id autoswitch_probe_url "https://www.google.com/generate_204")
|
||||
backup_node=$(config_n_get $id autoswitch_backup_node nil)
|
||||
while [ -n "$backup_node" -a "$backup_node" != "nil" ]; do
|
||||
[ -f "$LOCK_FILE" ] && {
|
||||
sleep 6s
|
||||
continue
|
||||
}
|
||||
touch $LOCK_FILE
|
||||
backup_node=$(echo $backup_node | tr -s ' ' '\n' | uniq | tr -s '\n' ' ')
|
||||
test_auto_switch "$backup_node"
|
||||
rm -f $LOCK_FILE
|
||||
sleep ${delay}
|
||||
done
|
||||
}
|
||||
|
||||
start $@
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user