diff --git a/package/lean/UnblockNeteaseMusic/Makefile b/package/lean/UnblockNeteaseMusic/Makefile new file mode 100644 index 0000000000..6e31863a6e --- /dev/null +++ b/package/lean/UnblockNeteaseMusic/Makefile @@ -0,0 +1,62 @@ +# +# Copyright (C) 2015-2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v3. +# +# licheng +# www.maxlicheng.com +# 2019-07-28 +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=UnblockNeteaseMusic +PKG_VERSION:=0.20.4 +PKG_RELEASE:=1 + +PKG_LICENSE:=MIT + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/nondanee/UnblockNeteaseMusic.git +PKG_SOURCE_VERSION:=023a97f0e62d322a8d64c59ef3942da7c8a1b546 + +PKG_SOURCE_SUBDIR:=$(PKG_NAME) +PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_VERSION).tar.gz +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=Revive unavailable songs for Netease Cloud Music + DEPENDS:=+node + URL:=https://github.com/nondanee/UnblockNeteaseMusic/releases + SUBMENU:=NeteaseMusic + PKGARCH:=all +endef + +define Package/$(PKG_NAME)/description +Revive unavailable songs for Netease Cloud Music +endef + +define Build/Prepare + tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR) + mkdir -p $(PKG_BUILD_DIR)/$(PKG_NAME) + echo -e $(PKG_VERSION) > $(PKG_BUILD_DIR)/$(PKG_NAME)/core_ver + echo -e $(PKG_SOURCE_VERSION) > $(PKG_BUILD_DIR)/$(PKG_NAME)/local_ver +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME) + cp -pR $(PKG_BUILD_DIR)/$(PKG_NAME)/* $(1)/usr/share/$(PKG_NAME) +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/package/lean/adbyby/Makefile b/package/lean/adbyby/Makefile new file mode 100644 index 0000000000..c01afd7499 --- /dev/null +++ b/package/lean/adbyby/Makefile @@ -0,0 +1,70 @@ +# +# Copyright (C) 2015-2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v3. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=adbyby +PKG_VERSION:=2.7 +PKG_RELEASE:=20181023 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=Powerful adblock module to block ad. + DEPENDS:= + URL:=http://www.adbyby.com/ +endef + +define Package/$(PKG_NAME)/description +Adbyby is a powerful adblock module to block ad,just like adblock. +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/share/adbyby + $(INSTALL_BIN) ./files/adbyby.sh $(1)/usr/share/adbyby/ + $(INSTALL_BIN) ./files/adbybyfirst.sh $(1)/usr/share/adbyby/ + $(INSTALL_BIN) ./files/adbybyupdate.sh $(1)/usr/share/adbyby/ + $(INSTALL_CONF) ./files/adhook.ini $(1)/usr/share/adbyby/ + $(INSTALL_CONF) ./files/user.action $(1)/usr/share/adbyby/ + + $(INSTALL_DIR) $(1)/usr/share/adbyby/data + $(INSTALL_DATA) ./files/data/* $(1)/usr/share/adbyby/data/ + + $(INSTALL_DIR) $(1)/usr/share/adbyby/doc + $(INSTALL_DATA) ./files/doc/* $(1)/usr/share/adbyby/doc/ + +ifeq ($(ARCH),mipsel) + $(INSTALL_BIN) ./files/7620n/adbyby $(1)/usr/share/adbyby/ +endif +ifeq ($(ARCH),mips) + $(INSTALL_BIN) ./files/ar71xx/adbyby $(1)/usr/share/adbyby/ +endif +ifeq ($(ARCH),i386) + $(INSTALL_BIN) ./files/x86/adbyby $(1)/usr/share/adbyby/ +endif +ifeq ($(ARCH),x86_64) + $(INSTALL_BIN) ./files/x86_64/adbyby $(1)/usr/share/adbyby/ +endif +ifeq ($(ARCH),arm) + $(INSTALL_BIN) ./files/arm/adbyby $(1)/usr/share/adbyby/ +endif +ifeq ($(ARCH),aarch64) + $(INSTALL_BIN) ./files/armv7/adbyby $(1)/usr/share/adbyby/ +endif +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/package/lean/adbyby/files/7620n/adbyby b/package/lean/adbyby/files/7620n/adbyby new file mode 100755 index 0000000000..39ab0a1ebc Binary files /dev/null and b/package/lean/adbyby/files/7620n/adbyby differ diff --git a/package/lean/adbyby/files/adbyby.sh b/package/lean/adbyby/files/adbyby.sh new file mode 100755 index 0000000000..146a070d1b --- /dev/null +++ b/package/lean/adbyby/files/adbyby.sh @@ -0,0 +1,16 @@ +#!/bin/sh +PROG_PATH=/usr/share/adbyby +err=0 +until [ $err -ge 5 ]; do + if [ -n "$(pgrep $PROG_PATH/adbyby)" ]; then + iptables-save | grep ADBYBY >/dev/null || \ + /etc/init.d/adbyby add_rule + sleep 10 + err=0 + else + $PROG_PATH/adbyby --no-daemon &>/dev/null & + sleep 1 + err=$((err+1)) + fi +done +/etc/init.d/adbyby del_rule diff --git a/package/lean/adbyby/files/adbybyfirst.sh b/package/lean/adbyby/files/adbybyfirst.sh new file mode 100755 index 0000000000..c677d1bec1 --- /dev/null +++ b/package/lean/adbyby/files/adbybyfirst.sh @@ -0,0 +1,11 @@ +#!/bin/sh +PROG_PATH=/usr/share/adbyby +if [ -z "$(dnsmasq --version | grep no-ipset)" ]; then + [ $(uci get adbyby.@adbyby[-1].wan_mode) -ne 2 ] && \ + [ $(awk -F= '/^ipset/{print $2}' $PROG_PATH/adhook.ini) -eq 1 ] && \ + { + sed -i 's/adbyby_list/adbyby_wan/' /tmp/adbyby_host.conf + echo conf-file=/tmp/adbyby_host.conf >> /etc/dnsmasq.conf + /etc/init.d/dnsmasq restart + } +fi diff --git a/package/lean/adbyby/files/adbybyupdate.sh b/package/lean/adbyby/files/adbybyupdate.sh new file mode 100755 index 0000000000..bdde944a20 --- /dev/null +++ b/package/lean/adbyby/files/adbybyupdate.sh @@ -0,0 +1,11 @@ +#!/bin/sh +PROG_PATH=/usr/share/adbyby +if [ -z "$(dnsmasq --version | grep no-ipset)" ]; then + [ $(uci get adbyby.@adbyby[-1].wan_mode) -ne 2 ] && \ + [ $(awk -F= '/^ipset/{print $2}' $PROG_PATH/adhook.ini) -eq 1 ] && \ + { + sed -i 's/adbyby_list/adbyby_wan/' /tmp/adbyby_host.conf + ipset -F adbyby_wan 2>/dev/null + /etc/init.d/dnsmasq restart + } +fi diff --git a/package/lean/adbyby/files/adhook.ini b/package/lean/adbyby/files/adhook.ini new file mode 100755 index 0000000000..6a7613ea87 --- /dev/null +++ b/package/lean/adbyby/files/adhook.ini @@ -0,0 +1,14 @@ +[cfg] +### 2.1 ### +listen-address=0.0.0.0:8118 +buffer-limit=1024 +keep-alive-timeout=30 +socket-timeout=60 +### 2.5 ### +max_client_connections=0 +### 2.6 ### +stack_size=200 +auto_restart=0 +### 2.7 ### +debug=0 +ipset=0 diff --git a/package/lean/adbyby/files/ar71xx/adbyby b/package/lean/adbyby/files/ar71xx/adbyby new file mode 100755 index 0000000000..6c8edf1cb9 Binary files /dev/null and b/package/lean/adbyby/files/ar71xx/adbyby differ diff --git a/package/lean/adbyby/files/arm/adbyby b/package/lean/adbyby/files/arm/adbyby new file mode 100755 index 0000000000..a7da1900d9 Binary files /dev/null and b/package/lean/adbyby/files/arm/adbyby differ diff --git a/package/lean/adbyby/files/armv7/adbyby b/package/lean/adbyby/files/armv7/adbyby new file mode 100755 index 0000000000..98dbfbe549 Binary files /dev/null and b/package/lean/adbyby/files/armv7/adbyby differ diff --git a/package/lean/adbyby/files/data/lazy.bin b/package/lean/adbyby/files/data/lazy.bin new file mode 100644 index 0000000000..23e6a8d70a Binary files /dev/null and b/package/lean/adbyby/files/data/lazy.bin differ diff --git a/package/lean/adbyby/files/data/lazy.txt b/package/lean/adbyby/files/data/lazy.txt new file mode 100644 index 0000000000..5d383c7114 --- /dev/null +++ b/package/lean/adbyby/files/data/lazy.txt @@ -0,0 +1,25 @@ +! -----ʱ: 2018-10-08 23:07:52 by:xwhyc----- +.php?ad= +/99rejs/js +/agetsou.js^ +/c@*!25.js|$script +/cococ-66/$script +/defaultts.tc.qq.com/*.ts?index=0&start=0& +/defaultts.tc.qq.com/*.ts?index=0&start=0&end=*&ver=4 +/gg_js/ +/imgs.js?t= +/js/adtop.js| +/js/guanggao_gg.js?d= +/js/sss/ad.js +/static/js/aall.js +/unionjs/$script +/variety.tc.qq.com/*.mp4^*fmt=hd +/variety.tc.qq.com/*.mp4^*fmt=shd +/video.dispatch.tc.qq.com/*.mp4^*sdtfrom= +/view/image/QT_HY.jpg?t= +/view/js/slade.js|$script +/vlive.qqvideo.tc.qq.com/*.mp4^*fmt=hd +/vlive.qqvideo.tc.qq.com/*.mp4^*fmt=shd +/vmind.qqvideo.tc.qq.com/*.mp4^*sdtfrom= +/vmindhls.tc.qq.com/*ugc=0&ga=0&gv=0&start= +/zzhzgg.js \ No newline at end of file diff --git a/package/lean/adbyby/files/data/rules.txt b/package/lean/adbyby/files/data/rules.txt new file mode 100644 index 0000000000..5c44b0e8c5 --- /dev/null +++ b/package/lean/adbyby/files/data/rules.txt @@ -0,0 +1,15 @@ +! ------------------------------ ADByby 自定义过滤语法简表--------------------------------- +! -------------- 规则基于abp规则,并进行了字符替换部分的扩展----------------------------- +! ABP规则请参考https://adblockplus.org/zh_CN/filters,下面为大致摘要 +! "!" 为行注释符,注释行以该符号起始作为一行注释语义,用于规则描述 +! "*" 为字符通配符,能够匹配0长度或任意长度的字符串,该通配符不能与正则语法混用。 +! "^" 为分隔符,可以是除了字母、数字或者 _ - . % 之外的任何字符。 +! "|" 为管线符号,来表示地址的最前端或最末端 +! "||" 为子域通配符,方便匹配主域名下的所有子域。 +! "~" 为排除标识符,通配符能过滤大多数广告,但同时存在误杀, 可以通过排除标识符修正误杀链接。 +! "##" 为元素选择器标识符,后面跟需要隐藏元素的CSS样式例如 #ad_id .ad_class +!! 元素隐藏暂不支持全局规则和排除规则 +!! 字符替换扩展 +! 文本替换选择器标识符,后面跟需要替换的文本数据,格式:$s@模式字符串@替换后的文本@ +! 支持通配符*和? +! ------------------------------------------------------------------------------------------- diff --git a/package/lean/adbyby/files/data/user.txt b/package/lean/adbyby/files/data/user.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package/lean/adbyby/files/data/video.txt b/package/lean/adbyby/files/data/video.txt new file mode 100644 index 0000000000..3991c48fc1 --- /dev/null +++ b/package/lean/adbyby/files/data/video.txt @@ -0,0 +1,30 @@ +! -----ʱ: 2018-10-08 23:06:11 by:xwhyc----------------------------------------------- +! -----淴:http://www.adbyby.com/help.htm QQȺ: 79547134(), 364066294()470705224()534897434,438394572-------------- + + +!------------------------------------------- +/showadv/ +3199.cn###ad640,[id^="ad0"] +76fengyun.com###down_box{display:block!important;}.gggg,.ggao +9553.com##[class="zm"] +962.net##.show_ad,.g-962-ad +amazon.cn##.adv-float-div +downsx.rocks##.adbox,.Left-ad3_fixed,.google-ad2_fixed +edu-acc.cn###doyoo_monitor +fx678.com##[class^="adv-"] +||76fengyun.com/body22233.js +||76fengyun.com/file/$s@onclick="window.open('*');startWait();"@onclick="window.open(document.getElementById('downpage_link').href);startWait();"@ +||atanx.alicdn.com/t/tanxssp.js?_v= +||biquguan.com/guan/$script +||djv99sxoqpv11.cloudfront.net +||jmxlaser.com +||jump2.bdimg.com/p/$s@
" .. translate(Value) .. "
" +end + +m = Map("amule", translate("aMule"), translate("aMule is a ED2K/KAD client for all platforms.") .. "
+local fs = require "nixio.fs"
+local CONFIG_FILE = "/tmp/dnsforwarder.conf";
+
+function sync_value_to_file(value, file)
+ value = value:gsub("\r\n?", "\n")
+ local old_value = nixio.fs.readfile(file)
+ if value ~= old_value then
+ nixio.fs.writefile(file, value)
+ end
+
+end
+local state_msg = ""
+
+local dnsforwarder_on = (luci.sys.call("pidof dnsforwarder > /dev/null") == 0)
+local resolv_file = luci.sys.exec("uci get dhcp.@dnsmasq[0].resolvfile")
+local listen_port = luci.sys.exec("uci get dhcp.@dnsmasq[0].server")
+
+if dnsforwarder_on then
+ state_msg = "" .. translate("Running") .. ""
+else
+ state_msg = "" .. translate("Not running") .. ""
+end
+
+
+if dnsforwarder_on and string.sub(listen_port,1,14) == "127.0.0.1#5053" then
+ state_msg=state_msg .. ",DNSmasq已经将流量定向至本软件"
+elseif dnsforwarder_on then
+ state_msg=state_msg .. ""
+end
+
+if resolv_file=="" then
+
+else if dnsforwarder_on then
+ state_msg=state_msg .. ""
+ end
+end
+m=Map("dnsforwarder",translate("dnsforwarder"),translate("Dnsforwarder是企业级DNS服务器,可以通过TCP协议进行DNS解析,可以方便的使用iptables进行透明代理,配合ipset、GFWList使用效果更佳。默认上游服务器为114DNS,SSR和Redsocks的GFWList模式要依赖本软件包提供的功能").. "
状态 - " .. state_msg)
+s=m:section(TypedSection,"arguments", translate("通用配置"))
+s.addremove=false
+s.anonymous=true
+ view_enable = s:option(Flag,"enabled",translate("Enable"))
+ view_dnsmasq = s:option(Flag,"dnsmasq",translate("设置成DNSmasq的上游服务器"),translate("让DNSMasq从本软件获得解析结果,支持GFWList模式"))
+ view_addr = s:option(Value,"addr",translate("转发地址"),translate("请填写dnsforwarder的监听地址,默认127.0.0.1:5053,如果填写208.67.222.222:5353那么可不通过该软件获得无污染结果"))
+ view_addr.default = "127.0.0.1:5053"
+
+-- ---------------------------------------------------
+s1=m:section(TypedSection,"config",translate("标准配置"))
+s1.addremove=false
+s1.anonymous=true
+ log_enable = s1:option(Flag,"log",translate("打开文件日志"))
+ log_enable.rmempty=false
+ log_enable.default="false"
+ log_enable.disabled="false"
+ log_enable.enabled="true"
+ log_size = s1:option(Value,"log_size",translate("单个日志文件大小的临界值(字节)"),translate("当日志文件大小超过这个临界值后,当前的日志文件将会被重命名,然后建立一个新的日志文件,继续记录日志"))
+ log_size.datatype="uinteger"
+ log_size.default="102400"
+ log_size:depends ({log="true"})
+ log_path = s1:option(Value,"log_path",translate("设定日志文件所在的文件夹"),translate("日志文件初始的文件名为 `dnsforwarder.log',当超过临界值之后,将会被重命名为 `dnsforwarder.log.1'、`dnsforwarder.log.2' 等等,然后重新建立一个 dnsforwarder.log' 文件"))
+ log_path.datatype="directory"
+ log_path.default="/var/log/"
+ log_path.readonly=true
+ log_path:depends ({log="true"})
+ gfw_enable = s1:option(Flag,"gfw",translate("使用GFW列表"))
+ gfw_enable.rmempty=false
+ gfw_enable.default="true"
+ gfw_enable.disabled="false"
+ gfw_enable.enabled="true"
+ udp_local = s1:option(DynamicList,"udp_local",translate("本地监听"),translate("设置在本地开启的接口的IP地址和端口,可以是本地回环地址 (127.0.0.1) ,本地局域网,以及互联网
如果是 IPv6 地址,请在IP两端加上方括号(不包含端口部分),例如 [::1]:53 (本地回环)、[fe80::699c:f79a:9bb6:1]:5353
如果不指定端口,则默认为 53"))
+ tcp_group = s1:option(DynamicList,"tcp_group",translate("解析策略(TCP)"),translate([[格式:<IP1[:PORT],IP2[:PORT],...> <DOMAIN1,DOMAIN2,...> <on|PROXY1[:PORT],PROXY2[:PORT],...>
+ 设置一个 TCP 上游服务器组,并指定通过这些上游服务器查询的域名
+ 第一部分参数“<IP1[:PORT],IP2[:PORT],...>”,用于指定 TCP 上游服务器,不写端口则默认使用 53 端口
+ 第二部分参数“<DOMAIN1,DOMAIN2,...>”,用于指定通过这些 TCP 上游服务器查询的域名,可以包含通配符
+ 第三部分参数“<no|PROXY1[:PORT],PROXY2[:PORT],...>”,用于指定查询时使用的 Socks5 代理,不写端口则默认使用 1080 端口。`no' 代表不使用代理]]))
+ tcp_group.placeholder = "8.8.8.8 * on"
+ tcp_group.default=tcp_group.placeholder
+ udp_group = s1:option(DynamicList,"udp_group",translate("解析策略(UDP)"),translate([[格式:<IP1[:PORT],IP2[:PORT],...> <DOMAIN1,DOMAIN2,...> <on|off>
+ 设置一个 UDP 上游服务器组,并指定通过这些上游服务器查询的域名
+ 第一部分参数“<IP1[:PORT],IP2[:PORT],...>”,用于指定 UDP 上游服务器,不写端口则默认使用 53 端口
+ 第二部分参数“<DOMAIN1,DOMAIN2,...>”,用于指定通过这些 UDP 上游服务器查询的域名,可以包含通配符
+ 第三部分参数“<on|off>”,用于指定是否开启并发查询,`on' 为开启,`off' 为不开启
+ 并发查询指的是,向所有指定的 UDP 服务器发送查询请求,取最先接受到的未屏蔽的回复作为查询结果,并丢弃其余的回复]]))
+ udp_group.placeholder = "9.9.9.9,119.29.29.29,223.5.5.5,114.114.114.114 * on"
+ udp_group.default=udp_group.placeholder
+ group_file = s1:option(DynamicList,"group_file",translate("解析策略(文件)"),translate([[
+ 从文件加载服务器组,
+ 服务器组文件的写法点击这里查看。]]))
+ group_file.datatype="file"
+ s1:option(DummyValue,"nothing"," ",translate([[
+ 解析策略优先级:解析策略(TCP)>解析策略(UDP)>解析策略(文件)
+ 1.对于没有指定服务器的域名,会随机选择一个服务器组进行查询。
+ 2.如果某域名匹配多个服务器组,则选择的顺序如下:
+ 2.1 优先选择不含通配符的匹配项,如果仍然有多条匹配,则选择匹配度最高的那个,
+ 如果匹配度相同的情况下仍然有多条匹配,则选择最后一个。
+ (例如 `ipv6.microsoft.com' 比 `microsoft.com' 对于域名 `teredo.ipv6.microsoft.com' 的匹配度更高)
+ 2.2 然后选择包含通配符的匹配项,如果有多条匹配,则选择第一个.
+ ]]))
+ block_ip = s1:option(DynamicList,"block_ip",translate("DNS黑名单"),translate("阻挡含有以上 IP 地址的 DNS 数据包,IPv6 地址不用加方括号"))
+ block_ip.datatype="ipaddr"
+ block_ip.delimiter = ","
+ ip_substituting = s1:option(DynamicList,"ip_substituting",translate("替换 DNS 数据包"),translate("替换 DNS 数据包中的 IP 地址(只支持 IPv4 地址),例如:
127.0.0.1 1.2.0.127的效果是把 DNS 数据包中所有的 127.0.0.1 地址替换为 1.2.0.127,仅替换通过服务器(TCP 和 UDP)查询而来的 DNS 数据包,对于缓存中和 Hosts 中的结果无效"))
+ ip_substituting.placeholder = "127.0.0.1 1.2.0.127"
+ block_negative_resp = s1:option(Flag,"block_negative_resp",translate("过滤失败请求"),translate("是否过滤来自上游服务器的查询未成功的响应。查询未成功指:格式错误、服务器错误、域名不存在和服务器拒绝请求等,参见 RFC 6895,`2.3. RCODE Assignment'"))
+ block_negative_resp.rmempty=false
+ block_negative_resp.default="false"
+ block_negative_resp.disabled="false"
+ block_negative_resp.enabled="true"
+ append_host = s1:option(DynamicList,"append_host",translate("附加hosts"),translate([[
+ 写法与HOSTS文件中的一样,同样支持通配符,IPv6 地址不用加方括号
+ 也可以定义 CName 的 Hosts 项,例如:www.google.cn *.google.com,这样所有匹配 *.google.com 的域名都将指向 www.google.cn 的 IP 地址
+ 如果使某些域名跳过在 hosts 中的查询,可以@@ *.012345.com这样所有匹配 *.012345.com 的域名都不会在 hosts 中查询
+ 各种 hosts 的优先级从高到低:1.带 `@@' 的禁止项 2.一般 hosts 项 3.CName hosts 项
+ ]]))
+ block_ipv6 = s1:option(Flag,"block_ipv6",translate("拒绝IPv6 地址"),translate("如果一个域名在 Hosts 被指定了一个 IPv4 地址(包括文件 Hosts 和 `AppendHosts' 指定的 Hosts),那么拒绝对这个域名的 IPv6 地址查询"))
+ block_ipv6.rmempty=false
+ block_ipv6.default="false"
+ block_ipv6.disabled="false"
+ block_ipv6.enabled="true"
+
+ cache = s1:option(Flag,"cache",translate("使用缓存"),translate("是否使用缓存"))
+ cache.rmempty=false
+ cache.default="false"
+ cache.disabled="false"
+ cache.enabled="true"
+ cache_size = s1:option(Value,"cache_size",translate("缓存大小 (字节)"),translate("缓存大小不能小于 102400 (100KB)"))
+ cache_size.datatype="min(102400)"
+ cache_size.default="1048576"
+ cache_size:depends ({cache="true"})
+ cache_file = s1:option(Value,"cache_file",translate("缓存文件"))
+ cache_file.default="/tmp/dnsforwarder.cache"
+ cache_file.readonly=true
+ cache_file:depends ({cache="true"})
+ cache_ignore = s1:option(Flag,"cache_ignore",translate("忽略TTL"),translate("为 `true' 时将忽略 TTL,此时所有的缓存条目将不会被移除,为 `false' 时不忽略 TTL,缓存条目将会按照 TTL 的时间来移除"))
+ cache_ignore.rmempty=false
+ cache_ignore.default="false"
+ cache_ignore.disabled="false"
+ cache_ignore.enabled="true"
+ cache_ignore:depends ({cache="true"})
+ cache_control = s1:option(DynamicList,"cache_control",translate("控制域名的缓存行为"),translate([[
+ 单条格式:<DOMAIN> [$ [$] ]<TYPE> [ARGUMENT]
+ <DOMAIN> 是域名;[$ [$] ] 是传染设定;<TYPE> 是类型;[ARGUMENT] 是参数
+ <DOMAIN> 支持通配符,支持尾部匹配
+ <TYPE> 有以下可选项(均为小写):
+ orig : 使用原先的 TTL,即不改变指定域名记录的 TTL,此类型无需 [ARGUMENT] 参数
+ nocache : 不缓存指定域名记录,此类型无需 [ARGUMENT] 参数
+ fixed : 将指定域名记录的 TTL 修改为一个预先指定的常数值,这个值通过 [ARGUMENT] 参数指定
+ vari : 将指定域名记录的 TTL 修改为一个预先规定的变量,这个变量通过 [ARGUMENT] 参数指定,此时 [ARGUMENT] 参数的格式为 `ax+b',其中 a、b 为非负整数(需用户手工指定),x 为记录原先的 TTL 值(不需要用户指定)。此选项必须为 `ax+b' 的形式,不能是 `ax-b' 或者其它的变体形
+
传染机制:
+ 主动传染 (此为默认情况):主动传染 : 所有的域名记录不论是否存在单独指定的控制条目,均使用上级域名控制条目
+ 被动传染(<TYPE> 前加一个 `$'): 如果存在单独指定的控制条目,那么使用单独指定的控制条目,否则使用上级域名的控制条目
+ 不传染( 前加两个 `$'): 如果存在单独指定的控制条目,那么使用单独指定的控制条目,如果没有则不修改记录的 TTL 值
+ 只有被查询的域名控制条目才具有传染性质
+
常见例子:
+ * fixed 300强制使所有缓存的条目的 TTL 为 300
+ * vari 2x+0将所有缓存条目的 TTL 加倍。即原来为300,现在为600
+ ]]))
+ cache_control:depends ({cache="true"})
+
+ domain_statistic = s1:option(Flag,"domain_statistic",translate("启用域名统计"),translate("域名信息统计会依照模板文件记录域名的查询情况目前只能生成到/root/.dnsforwarder/statistic.html会影响闪存寿命不建议开启"))
+ domain_statistic.rmempty=false
+ domain_statistic.default="false"
+ domain_statistic.disabled="false"
+ domain_statistic.enabled="true"
+
+ view_cfg = s1:option(TextValue, "1", nil)
+ view_cfg.rmempty = false
+ view_cfg.rows = 24
+ view_cfg.readonly=true
+
+ function view_cfg.cfgvalue()
+ return nixio.fs.readfile(CONFIG_FILE) or ""
+ end
+ function view_cfg.write(self, section, value)
+ end
+
+-- ---------------------------------------------------
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ os.execute("/etc/init.d/dnsforwarder makeconfig >/dev/null 2>&1 &")
+ os.execute("/etc/init.d/dnsforwarder restart >/dev/null 2>&1 &")
+end
+
+return m
diff --git a/package/lean/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua b/package/lean/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua
new file mode 100644
index 0000000000..9a4830e6b2
--- /dev/null
+++ b/package/lean/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua
@@ -0,0 +1,31 @@
+local fs = require "nixio.fs"
+local log = "/var/log/dnsforwarder.log"
+
+function sync_value_to_file(value, file)
+ value = value:gsub("\r\n?", "\n")
+ local old_value = nixio.fs.readfile(file)
+ if value ~= old_value then
+ nixio.fs.writefile(file, value)
+ end
+end
+
+m = Map("dnsforwarder", translate("运行日志"),translate("如果想观察日志,需要在配置文件中写明LogOn true 和 LogFileFolder /var/log"))
+s=m:section(TypedSection,"arguments","")
+s.addremove=false
+s.anonymous=true
+
+
+
+
+gfwlist = s:option(TextValue, "gfwlist",nil, nil)
+gfwlist.description = translate("日志文件默认位置:" .. log)
+gfwlist.rows = 26
+gfwlist.wrap = "off"
+gfwlist.cfgvalue = function(self, section)
+ return fs.readfile(log) or ""
+end
+
+
+
+
+return m
\ No newline at end of file
diff --git a/package/lean/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.zh-cn.po b/package/lean/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.zh-cn.po
new file mode 100644
index 0000000000..dbd444848c
--- /dev/null
+++ b/package/lean/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.zh-cn.po
@@ -0,0 +1,8 @@
+msgid "Enable"
+msgstr "启用"
+
+msgid "Running"
+msgstr "运行中"
+
+msgid "Not running"
+msgstr "未运行"
diff --git a/package/lean/luci-app-dnspod/Makefile b/package/lean/luci-app-dnspod/Makefile
new file mode 100644
index 0000000000..6d55ff6c6a
--- /dev/null
+++ b/package/lean/luci-app-dnspod/Makefile
@@ -0,0 +1,51 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-dnspod
+PKG_VERSION=1.0
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/host-build.mk
+
+define Package/luci-app-dnspod
+ SECTION:=luci
+ CATEGORY:=LuCI
+ SUBMENU:=3. Applications
+ TITLE:=Auto DNS for dnspod
+ PKGARCH:=all
+ DEPENDS:= +curl +libcurl
+endef
+
+define Package/luci-app-dnspod/description
+ auto get pub ip,dnspod ddns,must include curl with ssl
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/luci-app-dnspod/install
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
+
+ $(INSTALL_CONF) ./files/root/etc/config/dnspod $(1)/etc/config/dnspod
+ $(INSTALL_BIN) ./files/root/etc/init.d/dnspod $(1)/etc/init.d/dnspod
+ $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/dnspod.lua $(1)/usr/lib/lua/luci/model/cbi/dnspod.lua
+ $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/dnspod.lua $(1)/usr/lib/lua/luci/controller/dnspod.lua
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_BIN) ./files/dnspod.sh $(1)/usr/sbin
+endef
+
+$(eval $(call BuildPackage,luci-app-dnspod))
+
+
+
diff --git a/package/lean/luci-app-dnspod/files/dnspod.sh b/package/lean/luci-app-dnspod/files/dnspod.sh
new file mode 100755
index 0000000000..de0772999d
--- /dev/null
+++ b/package/lean/luci-app-dnspod/files/dnspod.sh
@@ -0,0 +1,169 @@
+#!/bin/bash
+#written by Michael Qu
+#greennyreborn@gmail.com
+
+#dnspod api token
+token=`uci get dnspod.base_arg.login_token`
+
+#域名
+domain=`uci get dnspod.base_arg.main_domain 2>/dev/null`
+
+
+#需要更新的子域名,只允许一个
+#由于*linux会被脚本解析为当前目录下的所有文件,所以请填写\*
+# sub_domain='\*'
+sub_domain=`uci get dnspod.base_arg.sub_domain 2>/dev/null`
+if [ $sub_domain = "\*" ]; then
+ sub_domain="*"
+fi
+
+#check for changed ip every 300 seconds
+wait=300
+wait_second=`uci get dnspod.base_arg.wait_second 2>/dev/null`
+if [[ $wait_second =~ ^[1-9][0-9]*$ ]]; then
+ wait=$wait_second
+fi
+
+#外网ip获取命令
+command=`uci get dnspod.base_arg.command_to_get_ip 2>/dev/null`
+if [ -z "$command" ]; then
+ command='curl -s whatismyip.akamai.com'
+fi
+
+#检查curl是否安装
+curl_status=`which curl 2>/dev/null`
+[ -n "$curl_status" ] || { echo "curl is not installed";exit 3; }
+
+os=$(uname -a | egrep -io 'openwrt' | tr [A-Z] [a-z])
+
+API_url="https://dnsapi.cn"
+LOG_FILE="/tmp/dnspod.log"
+MAX_LOG_SIZE=`expr 1 * 1024 * 1024` # 默认最大限制1M
+PROGRAM=$(basename $0)
+
+format='json'
+lang='en'
+record_type='A'
+common_options="login_token=$token&format=${format}&lang=${lang}"
+
+is_svc=0
+last_modified_ip=
+
+getFileSize() {
+ local file=$1
+ wc -c $file | awk '{print $1}'
+}
+
+clearLog() {
+ local file_size=$(getFileSize $LOG_FILE)
+ if [ $file_size -gt $MAX_LOG_SIZE ]; then
+ echo "" > $LOG_FILE
+ printMsg "Log file [$LOG_FILE], size: [$file_size bytes]."
+ printMsg "Exceeds max log size [$MAX_LOG_SIZE]. Clear the log."
+ fi
+}
+
+printMsg() {
+ local time=$(date "+%Y-%m-%d %H:%M:%S")
+ local msg="$1"
+ if [ $is_svc -eq 1 ]; then
+ echo "$time: $msg" >> $LOG_FILE
+ # logger -t ${PROGRAM} "${msg}"
+ else
+ echo "$time: $msg"
+ fi
+}
+
+getIp() {
+# curl -s http://checkip.dyndns.com | sed -n 's/.*: \([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/p'
+ eval $command
+}
+
+execAPI() {
+ local action="$1"
+ local extra_options="$2"
+
+ eval "curl -k -X POST \"${API_url}/${action}\" -d \"${common_options}&${extra_options}\""
+}
+
+getRecordList() {
+ local extra_options="domain=$domain"
+ echo $(execAPI "Record.List" "$extra_options")
+ exit
+}
+
+updateRecord() {
+ local ip=$1
+ local record_list=`getRecordList`
+ local old_ip=`jsonfilter -s "$record_list" -e "@.records[@.name='$sub_domain'].value"`
+ local id=`jsonfilter -s "$record_list" -e "@.records[@.name='$sub_domain'].id"`
+ printMsg "Change record: $sub_domain.$domain from $old_ip to $ip"
+
+ local extra_options="domain=$domain&record_id=$id&value=$ip&record_type=A&record_line_id=0&sub_domain=$sub_domain"
+ local response=$(execAPI "Record.Modify" "$extra_options")
+
+ local code=`jsonfilter -s "$response" -e "@.status.code"`
+ if [ "$code" == "1" ]; then
+ last_modified_ip=$ip
+ fi
+ local message=`jsonfilter -s "$response" -e "@.status.message"`
+ printMsg "response: $message"
+
+}
+
+checkip() {
+ local wan_ip=$1
+ printMsg "old ip: [$last_modified_ip], new ip: [$wan_ip]"
+ if [ "$wan_ip" != "$last_modified_ip" ]; then
+ return 8
+ else
+ return 3
+ fi
+}
+
+execSvc() {
+ local ip
+ #check that whether the network is ok
+ while [ 1 ];do
+ ip=$(getIp)
+ if [ -n "$ip" ]; then
+ printMsg "WAN IP: ${ip}"
+ break;
+ else
+ printMsg "Can't get wan ip"
+ sleep 30
+ fi
+ done
+
+ while [ 1 ];do
+ clearLog
+ ip=$(getIp)
+ checkip $ip
+ if [ $? -eq 8 ]; then
+ updateRecord $ip
+ #updateTunnelBroker
+ fi
+ sleep $wait
+ done
+}
+
+execUpdate() {
+ local ip=`getIp`
+ printMsg "WAN IP: ${ip}"
+ updateRecord $ip
+}
+
+case $1 in
+ --svc)
+ is_svc=1;
+ clearLog;
+ printMsg "Start in Service mode, check in every $wait seconds";
+ printMsg "domain: ${domain}, sub_domain: ${sub_domain}";
+ printMsg "Use command: [$command] to get wan ip";
+ execSvc;;
+ *)
+ is_svc=0;
+ printMsg "Start update record, domain: ${domain}, sub_domain: ${sub_domain}";
+ printMsg "Use command: [$command] to get wan ip";
+ execUpdate;;
+esac
diff --git a/package/lean/luci-app-dnspod/files/root/etc/config/dnspod b/package/lean/luci-app-dnspod/files/root/etc/config/dnspod
new file mode 100644
index 0000000000..00876ecfc5
--- /dev/null
+++ b/package/lean/luci-app-dnspod/files/root/etc/config/dnspod
@@ -0,0 +1,7 @@
+config base_arg "base_arg"
+ option main_domain ''
+ option sub_domain ''
+ option login_token ''
+ option wait_second ''
+ option command_to_get_ip 'curl -s whatismyip.akamai.com'
+
diff --git a/package/lean/luci-app-dnspod/files/root/etc/init.d/dnspod b/package/lean/luci-app-dnspod/files/root/etc/init.d/dnspod
new file mode 100755
index 0000000000..036fa308ed
--- /dev/null
+++ b/package/lean/luci-app-dnspod/files/root/etc/init.d/dnspod
@@ -0,0 +1,18 @@
+#!/bin/sh /etc/rc.common
+START=80
+
+start()
+{
+ /usr/sbin/dnspod.sh --svc &
+}
+
+stop()
+{
+ ps | grep dnspod.sh | grep -v 'grep' | awk '{print $1}' | xargs kill
+}
+
+run_reboot()
+{
+ stop
+ start
+}
diff --git a/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/controller/dnspod.lua b/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/controller/dnspod.lua
new file mode 100644
index 0000000000..56517f10f8
--- /dev/null
+++ b/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/controller/dnspod.lua
@@ -0,0 +1,5 @@
+module("luci.controller.dnspod", package.seeall)
+
+function index()
+ entry({"admin", "network", "dnspod"}, cbi("dnspod"), _("动态DNSPOD"), 100)
+end
diff --git a/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/model/cbi/dnspod.lua b/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/model/cbi/dnspod.lua
new file mode 100644
index 0000000000..437fd9076c
--- /dev/null
+++ b/package/lean/luci-app-dnspod/files/root/usr/lib/lua/luci/model/cbi/dnspod.lua
@@ -0,0 +1,32 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2010 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+]]--
+
+require("luci.sys")
+
+m = Map("dnspod", translate("动态DNSPOD"), translate("配置动态DNSPOD"))
+
+s = m:section(TypedSection, "base_arg", "")
+s.addremove = false
+s.anonymous = true
+
+email = s:option(Value, "login_token", translate("DNSPOD Token"), "格式: ID,Token")
+main = s:option(Value, "main_domain", translate("主域名"), "想要解析的主域名,例如:baidu.com")
+sub = s:option(Value, "sub_domain", translate("子域名"), "想要解析的子域名,只允许填写一个。如果想解析*子域名,请填写 \\*")
+wait = s:option(Value, "wait_second", translate("更新周期(s)"), "请填写数字,默认为300s")
+command = s:option(Value, "command_to_get_ip", translate("外网ip获取命令"), "默认为 curl -s whatismyip.akamai.com")
+
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ io.popen("/etc/init.d/dnspod restart &")
+end
+
+return m
diff --git a/package/lean/luci-app-docker/Makefile b/package/lean/luci-app-docker/Makefile
new file mode 100755
index 0000000000..686e4d6277
--- /dev/null
+++ b/package/lean/luci-app-docker/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Luci for Docker-CE
+LUCI_DEPENDS:=+docker-ce +e2fsprogs +fdisk
+LUCI_PKGARCH:=all
+PKG_VERSION:=1
+PKG_RELEASE:=9
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-docker/luasrc/controller/docker.lua b/package/lean/luci-app-docker/luasrc/controller/docker.lua
new file mode 100644
index 0000000000..93b8f6618d
--- /dev/null
+++ b/package/lean/luci-app-docker/luasrc/controller/docker.lua
@@ -0,0 +1,17 @@
+module("luci.controller.docker", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/dockerd") then
+ return
+ end
+
+ entry({"admin", "services", "docker"}, cbi("docker"), _("Docker CE Container"), 199).dependent = true
+ entry({"admin","services","docker","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep /usr/bin/dockerd >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-docker/luasrc/model/cbi/docker.lua b/package/lean/luci-app-docker/luasrc/model/cbi/docker.lua
new file mode 100644
index 0000000000..270fecbbc7
--- /dev/null
+++ b/package/lean/luci-app-docker/luasrc/model/cbi/docker.lua
@@ -0,0 +1,23 @@
+local running = (luci.sys.call("pidof portainer >/dev/null") == 0)
+local button = ""
+
+if running then
+ button = "
"
+end
+
+m = Map("dockerd", "Docker CE", translate("Docker is a set of platform-as-a-service (PaaS) products that use OS-level virtualization to deliver software in packages called containers.") .. button)
+
+
+m:section(SimpleSection).template = "docker/docker_status"
+
+s = m:section(TypedSection, "docker")
+s.anonymous = true
+
+wan_mode = s:option(Flag, "wan_mode", translate("Enable WAN access Dokcer"), translate("Enable WAN access docker mapped ports"))
+wan_mode.default = 0
+wan_mode.rmempty = false
+
+o=s:option(DummyValue,"readme",translate(" "))
+o.description=translate(""..translate("Download DockerReadme.pdf").."")
+
+return m
\ No newline at end of file
diff --git a/package/lean/luci-app-docker/luasrc/view/docker/docker_status.htm b/package/lean/luci-app-docker/luasrc/view/docker/docker_status.htm
new file mode 100644
index 0000000000..4d2c714684
--- /dev/null
+++ b/package/lean/luci-app-docker/luasrc/view/docker/docker_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-docker/po/zh-cn/docker.po b/package/lean/luci-app-docker/po/zh-cn/docker.po
new file mode 100644
index 0000000000..b95369aacf
--- /dev/null
+++ b/package/lean/luci-app-docker/po/zh-cn/docker.po
@@ -0,0 +1,39 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Luci ARP Bind\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-06-23 20:16+0800\n"
+"PO-Revision-Date: 2015-06-23 20:17+0800\n"
+"Last-Translator: coolsnowwolf \n"
+"Language-Team: PandoraBox Team\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 1.8.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+msgid "Docker CE Container"
+msgstr "Docker CE 容器"
+
+msgid "Open Portainer Docker Admin"
+msgstr "打开 Portainer Docker 管理页面"
+
+msgid "Docker is a set of platform-as-a-service (PaaS) products that use OS-level virtualization to deliver software in packages called containers."
+msgstr "Docker是一组平台即服务(platform-as-a-service,PaaS)产品,它使用操作系统级容器虚拟化来交付软件包。"
+
+msgid "Enable WAN access Dokcer"
+msgstr "允许 WAN 访问 Dokcer"
+
+msgid "Enable WAN access docker mapped ports"
+msgstr "允许 WAN 访问 Dokcer 映射后的端口(易受攻击!)。
推荐禁用该选项后,用系统防火墙选择性映射 172.17.0.X:XX 端口到 WAN"
+
+msgid "Docker Readme First"
+msgstr "Docker 初始化无脑配置教程"
+
+msgid "Download DockerReadme.pdf"
+msgstr "下载 Docker 初始化无脑配置教程"
+
+msgid "Please download DockerReadme.pdf to read when first-running"
+msgstr "初次在OpenWrt中运行Docker必读(只需执行一次流程)"
\ No newline at end of file
diff --git a/package/lean/luci-app-docker/root/etc/config/dockerd b/package/lean/luci-app-docker/root/etc/config/dockerd
new file mode 100644
index 0000000000..b73c0a3e38
--- /dev/null
+++ b/package/lean/luci-app-docker/root/etc/config/dockerd
@@ -0,0 +1,4 @@
+
+config docker
+ option wan_mode '0'
+
diff --git a/package/lean/luci-app-docker/root/etc/docker-init b/package/lean/luci-app-docker/root/etc/docker-init
new file mode 100755
index 0000000000..9bade865ff
--- /dev/null
+++ b/package/lean/luci-app-docker/root/etc/docker-init
@@ -0,0 +1,26 @@
+#!/bin/sh
+dtype=`fdisk -l /dev/sda | grep 'Disklabel type' | awk '{print $3}'`
+partid="0"
+
+if [ "$dtype" = "gpt" ]
+then
+ partid=`echo "n
+
+
+
+w
+" | fdisk /dev/sda | grep 'Created a new partition' | awk '{print $5}'`
+
+elif [ "$dtype" = "dos" ]
+then
+ partid=`echo "n
+p
+
+
+
+w
+" | fdisk /dev/sda | grep 'Created a new partition' | awk '{print $5}'`
+fi
+
+echo "y" | mkfs.ext4 /dev/sda$partid
+
diff --git a/package/lean/luci-app-docker/root/etc/docker-web b/package/lean/luci-app-docker/root/etc/docker-web
new file mode 100755
index 0000000000..010ab1eac4
--- /dev/null
+++ b/package/lean/luci-app-docker/root/etc/docker-web
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+docker run -d --restart=always --name="portainer" -p 9999:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
\ No newline at end of file
diff --git a/package/lean/luci-app-docker/root/etc/init.d/dockerd b/package/lean/luci-app-docker/root/etc/init.d/dockerd
new file mode 100755
index 0000000000..5d6f36bf2b
--- /dev/null
+++ b/package/lean/luci-app-docker/root/etc/init.d/dockerd
@@ -0,0 +1,22 @@
+#!/bin/sh /etc/rc.common
+
+USE_PROCD=1
+START=25
+
+start_service() {
+ local nofile=$(cat /proc/sys/fs/nr_open)
+ local wanmode=$(uci get dockerd.@docker[0].wan_mode)
+
+ if [ $wanmode = "1" ] ;then
+ dockerwan=" "
+ else
+ dockerwan="--iptables=false"
+ fi
+
+ procd_open_instance
+ procd_set_param stderr 1
+ procd_set_param command /usr/bin/dockerd $dockerwan
+ procd_set_param limits nofile="${nofile} ${nofile}"
+ procd_close_instance
+
+}
diff --git a/package/lean/luci-app-docker/root/etc/uci-defaults/docker b/package/lean/luci-app-docker/root/etc/uci-defaults/docker
new file mode 100755
index 0000000000..e03f477838
--- /dev/null
+++ b/package/lean/luci-app-docker/root/etc/uci-defaults/docker
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@dockerd[-1]
+ add ucitrack dockerd
+ set ucitrack.@dockerd[-1].init=dockerd
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-docker/root/www/DockerReadme.pdf b/package/lean/luci-app-docker/root/www/DockerReadme.pdf
new file mode 100644
index 0000000000..8a9a94b832
Binary files /dev/null and b/package/lean/luci-app-docker/root/www/DockerReadme.pdf differ
diff --git a/package/lean/luci-app-familycloud/Makefile b/package/lean/luci-app-familycloud/Makefile
new file mode 100644
index 0000000000..ac8eb34edf
--- /dev/null
+++ b/package/lean/luci-app-familycloud/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for FamilyCloudSpeeder
+LUCI_DEPENDS:=+coreutils +coreutils-nohup +libreadline +libcurl +libopenssl +bash +curl +wget +openssl-util +ca-certificates +ca-bundle
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-familycloud
+PKG_VERSION:=1
+PKG_RELEASE:=6
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-familycloud/luasrc/controller/familycloud.lua b/package/lean/luci-app-familycloud/luasrc/controller/familycloud.lua
new file mode 100644
index 0000000000..92cf489c49
--- /dev/null
+++ b/package/lean/luci-app-familycloud/luasrc/controller/familycloud.lua
@@ -0,0 +1,23 @@
+
+module("luci.controller.familycloud", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/familycloud") then
+ return
+ end
+
+ entry({"admin", "services", "familycloud"},firstchild(), _("天翼家庭云/云盘提速"), 80).dependent = false
+
+ entry({"admin", "services", "familycloud", "general"},cbi("familycloud"), _("Base Setting"), 1)
+ entry({"admin", "services", "familycloud", "log"},form("familycloudlog"), _("Log"), 2)
+
+ entry({"admin","services","familycloud","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("ps | grep speedup.sh | grep -v grep >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
+
diff --git a/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloud.lua b/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloud.lua
new file mode 100644
index 0000000000..d4a0b64281
--- /dev/null
+++ b/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloud.lua
@@ -0,0 +1,22 @@
+
+mp = Map("familycloud", translate("天翼家庭云/天翼云盘提速"))
+mp.description = translate("天翼家庭云/天翼云盘提速 (最高可达500Mbps)")
+
+mp:section(SimpleSection).template = "familycloud/familycloud_status"
+
+s = mp:section(TypedSection, "familycloud")
+s.anonymous=true
+s.addremove=false
+
+enabled = s:option(Flag, "enabled", translate("启用提速"))
+enabled.default = 0
+enabled.rmempty = false
+
+speedtype = s:option(ListValue, "speedertype", translate("天翼提速包类型"))
+speedtype:value("CloudDisk", translate("天翼云盘提速"))
+speedtype:value("FamilyCloud", translate("天翼家庭云提速"))
+
+account = s:option(Value, "token", translate("AccessToken"))
+account.datatype = "string"
+
+return mp
diff --git a/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloudlog.lua b/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloudlog.lua
new file mode 100644
index 0000000000..9471b410d2
--- /dev/null
+++ b/package/lean/luci-app-familycloud/luasrc/model/cbi/familycloudlog.lua
@@ -0,0 +1,14 @@
+local fs = require "nixio.fs"
+local conffile = "/tmp/familycloud.log"
+
+f = SimpleForm("logview")
+
+t = f:field(TextValue, "conf")
+t.rmempty = true
+t.rows = 15
+function t.cfgvalue()
+ return fs.readfile(conffile) or ""
+end
+t.readonly="readonly"
+
+return f
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/luasrc/view/familycloud/familycloud_status.htm b/package/lean/luci-app-familycloud/luasrc/view/familycloud/familycloud_status.htm
new file mode 100644
index 0000000000..002565430a
--- /dev/null
+++ b/package/lean/luci-app-familycloud/luasrc/view/familycloud/familycloud_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/root/etc/config/familycloud b/package/lean/luci-app-familycloud/root/etc/config/familycloud
new file mode 100644
index 0000000000..55b9172ee3
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/etc/config/familycloud
@@ -0,0 +1,6 @@
+
+config familycloud
+ option token 'f18a1952289c4659b3722ad54d3d92fb'
+ option speedertype 'CloudDisk'
+ option enabled '0'
+
diff --git a/package/lean/luci-app-familycloud/root/etc/init.d/familycloud b/package/lean/luci-app-familycloud/root/etc/init.d/familycloud
new file mode 100755
index 0000000000..974a81a240
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/etc/init.d/familycloud
@@ -0,0 +1,25 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+STOP=10
+
+TYPE=$(uci get familycloud.@familycloud[0].speedertype)
+TOKEN=$(uci get familycloud.@familycloud[0].token)
+
+
+start()
+{
+ stop
+ enable=$(uci get familycloud.@familycloud[0].enabled)
+ [ $enable -eq 0 ] && exit 0
+ sed "s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/$TOKEN/" /usr/share/familycloud/$TYPE/config.ini > /tmp/config.json
+ bash /usr/share/familycloud/$TYPE/speedup.sh > /tmp/familycloud.log &
+}
+
+stop()
+{
+ kill -9 $(ps | grep speedup.sh | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+}
+
+
+
diff --git a/package/lean/luci-app-familycloud/root/etc/ppp/ip-up.d/familycloud.sh b/package/lean/luci-app-familycloud/root/etc/ppp/ip-up.d/familycloud.sh
new file mode 100755
index 0000000000..60074df64b
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/etc/ppp/ip-up.d/familycloud.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+sleep 60 && /etc/init.d/familycloud restart
diff --git a/package/lean/luci-app-familycloud/root/etc/uci-defaults/familycloud b/package/lean/luci-app-familycloud/root/etc/uci-defaults/familycloud
new file mode 100755
index 0000000000..0251b0eeee
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/etc/uci-defaults/familycloud
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@familycloud[-1]
+ add ucitrack familycloud
+ set ucitrack.@familycloud[-1].init=familycloud
+ commit ucitrack
+EOF
+
+touch /tmp/config.json
+
+ln -s /tmp/config.json /usr/share/familycloud/CloudDisk/config.json
+
+rm -f /tmp/luci-indexcache
+
+exit 0
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/config.ini b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/config.ini
new file mode 100755
index 0000000000..31f0d788a8
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/config.ini
@@ -0,0 +1,10 @@
+{
+ "accessToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "setting": {
+ "method": "GET",
+ "rate": 600
+ },
+ "extra_header": {
+ "User-Agent": "Apache-HttpClient/UNAVAILABLE (java 1.4)"
+ }
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup.sh
new file mode 100755
index 0000000000..30006574cf
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+base_dir=`dirname $0`
+source "$base_dir/utils.sh"
+config="$base_dir/config.json"
+
+
+accessToken=`getSingleJsonValue "$config" "accessToken"`
+method=`getSingleJsonValue "$config" "method"`
+rate=`getSingleJsonValue "$config" "rate"`
+UA=`getSingleJsonValue "$config" "User-Agent"`
+extra_header="User-Agent:$UA"
+
+
+HOST="http://api.cloud.189.cn"
+LOGIN_URL="/loginByOpen189AccessToken.action"
+ACCESS_URL="/speed/startSpeedV2.action"
+count=0
+echo "*******************************************"
+while :
+do
+ count=$((count+1))
+ echo "Sending heart_beat package <$count>"
+ split="~"
+ headers_string="$extra_header"
+ headers=`formatHeaderString "$split" "$headers_string"`
+ result=`get "$HOST$LOGIN_URL?accessToken=$accessToken" "$headers"`
+ session_key=`echo "$result" | grep -Eo "sessionKey>.*//'`
+ session_secret=`echo "$result" | grep -Eo "sessionSecret>.*//' | sed 's/<\/sessionSecret//'`
+ date=`env LANG=C.UTF-8 date -u '+%a, %d %b %Y %T GMT'`
+ data="SessionKey=$session_key&Operate=$method&RequestURI=$ACCESS_URL&Date=$date"
+ key="$session_secret"
+ signature=`hashHmac "sha1" "$data" "$key"`
+ headers_string="SessionKey:$session_key"${split}"Signature:$signature"${split}"Date:$date"${split}"$extra_header"
+ headers=`formatHeaderString "$split" "$headers_string"`
+ qosClientSn="$session_key"
+ result=`get "$HOST$ACCESS_URL?qosClientSn=$qosClientSn" "$headers"`
+ echo "heart_beat:"
+ echo "date:<$date>"
+ echo -e "response:\n$result"
+ [[ "`echo ${result} | grep dialAcc`" != "" ]] && hint="succeeded" || hint="failed"
+ echo "Sending heart_beat package <$count> $hint"
+ echo "*******************************************"
+ sleep ${rate}
+done
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup_router.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup_router.sh
new file mode 100755
index 0000000000..769b5a63b5
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/speedup_router.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+base_dir=`dirname $0`
+source "$base_dir/utils.sh"
+config="$base_dir/config.json"
+
+echoToLogreader() {
+ logger -t CloudDisk "${1}"
+}
+
+accessToken=`getSingleJsonValue "$config" "accessToken"`
+method=`getSingleJsonValue "$config" "method"`
+UA=`getSingleJsonValue "$config" "User-Agent"`
+extra_header="User-Agent:$UA"
+
+
+HOST="http://api.cloud.189.cn"
+LOGIN_URL="/loginByOpen189AccessToken.action"
+ACCESS_URL="/speed/startSpeedV2.action"
+echoToLogreader "*******************************************"
+echoToLogreader "Sending Heartbeat Package ..."
+split="~"
+headers_string="$extra_header"
+headers=`formatHeaderString "$split" "$headers_string"`
+result=`get "$HOST$LOGIN_URL?accessToken=$accessToken" "$headers"`
+session_key=`echo "$result" | grep -Eo "sessionKey>.*//'`
+session_secret=`echo "$result" | grep -Eo "sessionSecret>.*//' | sed 's/<\/sessionSecret//'`
+date=`env LANG=C.UTF-8 date -u '+%a, %d %b %Y %T GMT'`
+data="SessionKey=$session_key&Operate=$method&RequestURI=$ACCESS_URL&Date=$date"
+key="$session_secret"
+signature=`hashHmac "sha1" "$data" "$key"`
+headers_string="SessionKey:$session_key"${split}"Signature:$signature"${split}"Date:$date"${split}"$extra_header"
+headers=`formatHeaderString "$split" "$headers_string"`
+qosClientSn=`cat /proc/sys/kernel/random/uuid`
+result=`get "$HOST$ACCESS_URL?qosClientSn=$qosClientSn" "$headers"`
+echoToLogreader "Heartbeat Signature: $signature"
+echoToLogreader "Date: $date"
+echoToLogreader "Response: $result"
+[[ "`echo ${result} | grep dialAcc`" != "" ]] && hint="succeeded" || hint="failed"
+echoToLogreader "Heartbeating $hint."
+echoToLogreader "*******************************************"
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/utils.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/utils.sh
new file mode 100755
index 0000000000..d54e1e3a94
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/CloudDisk/utils.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+CONNECTION_TIME="15"
+TRANSMISSION_TIME="15"
+
+formatHeaderString() {
+ OLD_IFS=$IFS
+ IFS="$1"
+ STR="$2"
+ ARRAY=(${STR})
+ for i in "${!ARRAY[@]}"
+ do
+ HEADERS="$HEADERS -H '${ARRAY[$i]}'"
+ done
+ echo ${HEADERS} | sed 's/^ //'
+ IFS=${OLD_IFS}
+}
+
+get() {
+ HEADER="$1"
+ URL="$2"
+ eval curl -s --connect-timeout "${CONNECTION_TIME}" -m "${TRANSMISSION_TIME}" "${HEADER}" "${URL}"
+}
+
+post() {
+ HEADER="$1"
+ URL="$2"
+ PAYLOAD="$3"
+ eval curl -s --connect-timeout "${CONNECTION_TIME}" -m "${TRANSMISSION_TIME}" -X POST "${URL}" "${HEADER}" -w %{http_code} -d "'$PAYLOAD'"
+}
+
+getSingleJsonValue() {
+ FILE="$1"
+ KEY="$2"
+ cat ${FILE} | grep "$KEY" | sed 's/,$//' | awk -F "[:]" '{ print $2 }' | sed 's/ //g' | sed 's/"//g'
+}
+
+hashHmac() {
+ digest="$1"
+ data="$2"
+ key="$3"
+ echo -n "$data" | openssl dgst "-$digest" -hmac "$key" | sed -e 's/^.* //' | tr 'a-z' 'A-Z'
+}
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.ini b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.ini
new file mode 100755
index 0000000000..f83cab7d17
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.ini
@@ -0,0 +1,14 @@
+{
+ "accessToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "AppKey": "600100885",
+ "setting": {
+ "method": "POST",
+ "rate": 600
+},
+ "send_data": {
+ "prodCode": "76"
+ },
+ "extra_header": {
+ "User-Agent": "Apache-HttpClient/UNAVAILABLE (java 1.4)"
+ }
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.json b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.json
new file mode 120000
index 0000000000..35e21f5b3e
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/config.json
@@ -0,0 +1 @@
+/tmp/config.json
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup.sh
new file mode 100755
index 0000000000..d7ade60cf6
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+base_dir=`dirname $0`
+source "$base_dir/utils.sh"
+config="$base_dir/config.json"
+
+
+accessToken=`getSingleJsonValue "$config" "accessToken"`
+AppKey=`getSingleJsonValue "$config" "AppKey"`
+method=`getSingleJsonValue "$config" "method"`
+rate=`getSingleJsonValue "$config" "rate"`
+prodCode=`getSingleJsonValue "$config" "prodCode"`
+UA=`getSingleJsonValue "$config" "User-Agent"`
+extra_header="User-Agent:$UA"
+
+
+HOST="http://api.cloud.189.cn"
+LOGIN_URL="/login4MergedClient.action"
+ACCESS_URL="/family/qos/startQos.action"
+count=0
+echo "*******************************************"
+while :
+do
+ count=$((count+1))
+ echo "Sending heart_beat package <$count>"
+ split="~"
+ headers_string="AppKey:$AppKey"${split}"$extra_header"
+ headers=`formatHeaderString "$split" "$headers_string"`
+ result=`post "$headers" "$HOST$LOGIN_URL?accessToken=$accessToken"`
+ session_key=`echo "$result" | grep -Eo "familySessionKey>.+//' | sed 's/<\/familySessionKey//'`
+ session_secret=`echo "$result" | grep -Eo "familySessionSecret>.+//' | sed 's/<\/familySessionSecret//'`
+ date=`env LANG=C.UTF-8 date -u '+%a, %d %b %Y %T GMT'`
+ data="SessionKey=$session_key&Operate=$method&RequestURI=$ACCESS_URL&Date=$date"
+ key="$session_secret"
+ signature=`hashHmac "sha1" "$data" "$key"`
+ headers_string="SessionKey:$session_key"${split}"Signature:$signature"${split}"Date:$date"${split}"$extra_header"
+ headers=`formatHeaderString "$split" "$headers_string"`
+ send_data="prodCode=$prodCode"
+ result=`post "$headers" "$HOST$ACCESS_URL" "$send_data"`
+ echo "heart_beat:"
+ echo "date:<$date>"
+ echo "status_code:${result: -3}"
+ echo -e "response:\n`echo ${result} | sed "s^[0-9]\{3\}$^^"`"
+ [[ "`echo ${result} | grep dialAcc`" != "" ]] && hint="succeeded" || hint="failed"
+ echo "Sending heart_beat package <$count> $hint"
+ echo "*******************************************"
+ sleep ${rate}
+done
\ No newline at end of file
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup_router.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup_router.sh
new file mode 100755
index 0000000000..721023b0cc
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/speedup_router.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+base_dir=`dirname $0`
+source "$base_dir/utils.sh"
+config="$base_dir/config.json"
+
+echoToLogreader() {
+ logger -t FamilyCloud "${1}"
+}
+
+accessToken=`getSingleJsonValue "$config" "accessToken"`
+AppKey=`getSingleJsonValue "$config" "AppKey"`
+method=`getSingleJsonValue "$config" "method"`
+prodCode=`getSingleJsonValue "$config" "prodCode"`
+UA=`getSingleJsonValue "$config" "User-Agent"`
+extra_header="User-Agent:$UA"
+
+
+HOST="http://api.cloud.189.cn"
+LOGIN_URL="/login4MergedClient.action"
+ACCESS_URL="/family/qos/startQos.action"
+echoToLogreader "*******************************************"
+echoToLogreader "Sending Heartbeat Package ..."
+split="~"
+headers_string="AppKey:$AppKey"${split}"$extra_header"
+headers=`formatHeaderString "$split" "$headers_string"`
+result=`post "$headers" "$HOST$LOGIN_URL?accessToken=$accessToken"`
+session_key=`echo "$result" | grep -Eo "familySessionKey>.+//' | sed 's/<\/familySessionKey//'`
+session_secret=`echo "$result" | grep -Eo "familySessionSecret>.+//' | sed 's/<\/familySessionSecret//'`
+date=`env LANG=C.UTF-8 date -u '+%a, %d %b %Y %T GMT'`
+data="SessionKey=$session_key&Operate=$method&RequestURI=$ACCESS_URL&Date=$date"
+key="$session_secret"
+signature=`hashHmac "sha1" "$data" "$key"`
+headers_string="SessionKey:$session_key"${split}"Signature:$signature"${split}"Date:$date"${split}"$extra_header"
+headers=`formatHeaderString "$split" "$headers_string"`
+send_data="prodCode=$prodCode"
+result=`post "$headers" "$HOST$ACCESS_URL" "$send_data"`
+echoToLogreader "Heartbeat Signature: $signature"
+echoToLogreader "Date: $date"
+echoToLogreader "Response: ${result}"
+[[ "`echo ${result} | grep dialAcc`" != "" ]] && hint="succeeded" || hint="failed"
+echoToLogreader "Heartbeating $hint."
+echoToLogreader "*******************************************"
diff --git a/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/utils.sh b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/utils.sh
new file mode 100755
index 0000000000..d54e1e3a94
--- /dev/null
+++ b/package/lean/luci-app-familycloud/root/usr/share/familycloud/FamilyCloud/utils.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+CONNECTION_TIME="15"
+TRANSMISSION_TIME="15"
+
+formatHeaderString() {
+ OLD_IFS=$IFS
+ IFS="$1"
+ STR="$2"
+ ARRAY=(${STR})
+ for i in "${!ARRAY[@]}"
+ do
+ HEADERS="$HEADERS -H '${ARRAY[$i]}'"
+ done
+ echo ${HEADERS} | sed 's/^ //'
+ IFS=${OLD_IFS}
+}
+
+get() {
+ HEADER="$1"
+ URL="$2"
+ eval curl -s --connect-timeout "${CONNECTION_TIME}" -m "${TRANSMISSION_TIME}" "${HEADER}" "${URL}"
+}
+
+post() {
+ HEADER="$1"
+ URL="$2"
+ PAYLOAD="$3"
+ eval curl -s --connect-timeout "${CONNECTION_TIME}" -m "${TRANSMISSION_TIME}" -X POST "${URL}" "${HEADER}" -w %{http_code} -d "'$PAYLOAD'"
+}
+
+getSingleJsonValue() {
+ FILE="$1"
+ KEY="$2"
+ cat ${FILE} | grep "$KEY" | sed 's/,$//' | awk -F "[:]" '{ print $2 }' | sed 's/ //g' | sed 's/"//g'
+}
+
+hashHmac() {
+ digest="$1"
+ data="$2"
+ key="$3"
+ echo -n "$data" | openssl dgst "-$digest" -hmac "$key" | sed -e 's/^.* //' | tr 'a-z' 'A-Z'
+}
diff --git a/package/lean/luci-app-filetransfer/Makefile b/package/lean/luci-app-filetransfer/Makefile
new file mode 100644
index 0000000000..fb9ecc3e9c
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/Makefile
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI page for IPK upload
+LUCI_DEPENDS:=+luci-lib-fs
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-filetransfer/luasrc/controller/filetransfer.lua b/package/lean/luci-app-filetransfer/luasrc/controller/filetransfer.lua
new file mode 100644
index 0000000000..986af58085
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/controller/filetransfer.lua
@@ -0,0 +1,12 @@
+--[[
+luci-app-filetransfer
+Description: File upload / download
+Author: yuleniwo xzm2@qq.com QQ:529698939
+Modify: ayongwifi@126.com www.openwrtdl.com
+]]--
+
+module("luci.controller.filetransfer", package.seeall)
+
+function index()
+ entry({"admin", "system", "filetransfer"}, form("updownload"), _("FileTransfer"),89)
+end
diff --git a/package/lean/luci-app-filetransfer/luasrc/model/cbi/updownload.lua b/package/lean/luci-app-filetransfer/luasrc/model/cbi/updownload.lua
new file mode 100644
index 0000000000..7b487cb215
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/model/cbi/updownload.lua
@@ -0,0 +1,148 @@
+local fs = require "luci.fs"
+local http = luci.http
+
+ful = SimpleForm("upload", translate("Upload"), nil)
+ful.reset = false
+ful.submit = false
+
+sul = ful:section(SimpleSection, "", translate("Upload file to '/tmp/upload/'"))
+fu = sul:option(FileUpload, "")
+fu.template = "cbi/other_upload"
+um = sul:option(DummyValue, "", nil)
+um.template = "cbi/other_dvalue"
+
+fdl = SimpleForm("download", translate("Download"), nil)
+fdl.reset = false
+fdl.submit = false
+sdl = fdl:section(SimpleSection, "", translate("Download file"))
+fd = sdl:option(FileUpload, "")
+fd.template = "cbi/other_download"
+dm = sdl:option(DummyValue, "", nil)
+dm.template = "cbi/other_dvalue"
+
+function Download()
+ local sPath, sFile, fd, block
+ sPath = http.formvalue("dlfile")
+ sFile = nixio.fs.basename(sPath)
+ if luci.fs.isdirectory(sPath) then
+ fd = io.popen('tar -C "%s" -cz .' % {sPath}, "r")
+ sFile = sFile .. ".tar.gz"
+ else
+ fd = nixio.open(sPath, "r")
+ end
+ if not fd then
+ dm.value = translate("Couldn't open file: ") .. sPath
+ return
+ end
+ dm.value = nil
+ http.header('Content-Disposition', 'attachment; filename="%s"' % {sFile})
+ http.prepare_content("application/octet-stream")
+ while true do
+ block = fd:read(nixio.const.buffersize)
+ if (not block) or (#block ==0) then
+ break
+ else
+ http.write(block)
+ end
+ end
+ fd:close()
+ http.close()
+end
+
+local dir, fd
+dir = "/tmp/upload/"
+nixio.fs.mkdir(dir)
+http.setfilehandler(
+ function(meta, chunk, eof)
+ if not fd then
+ if not meta then return end
+
+ if meta and chunk then fd = nixio.open(dir .. meta.file, "w") end
+
+ if not fd then
+ um.value = translate("Create upload file error.")
+ return
+ end
+ end
+ if chunk and fd then
+ fd:write(chunk)
+ end
+ if eof and fd then
+ fd:close()
+ fd = nil
+ um.value = translate("File saved to") .. ' "/tmp/upload/' .. meta.file .. '"'
+ end
+ end
+)
+
+if luci.http.formvalue("upload") then
+
+
+ local f = luci.http.formvalue("ulfile")
+ if #f <= 0 then
+ um.value = translate("No specify upload file.")
+ end
+elseif luci.http.formvalue("download") then
+ Download()
+end
+
+local inits, attr = {}
+for i, f in ipairs(fs.glob("/tmp/upload/*")) do
+ attr = fs.stat(f)
+ if attr then
+ inits[i] = {}
+ inits[i].name = fs.basename(f)
+ inits[i].mtime = os.date("%Y-%m-%d %H:%M:%S", attr.mtime)
+ inits[i].modestr = attr.modestr
+ inits[i].size = tostring(attr.size)
+ inits[i].remove = 0
+ inits[i].install = false
+ end
+end
+
+form = SimpleForm("filelist", translate("Upload file list"), nil)
+form.reset = false
+form.submit = false
+
+tb = form:section(Table, inits)
+nm = tb:option(DummyValue, "name", translate("File name"))
+mt = tb:option(DummyValue, "mtime", translate("Modify time"))
+ms = tb:option(DummyValue, "modestr", translate("Mode string"))
+sz = tb:option(DummyValue, "size", translate("Size"))
+btnrm = tb:option(Button, "remove", translate("Remove"))
+btnrm.render = function(self, section, scope)
+ self.inputstyle = "remove"
+ Button.render(self, section, scope)
+end
+
+btnrm.write = function(self, section)
+ local v = luci.fs.unlink("/tmp/upload/" .. luci.fs.basename(inits[section].name))
+ if v then table.remove(inits, section) end
+ return v
+end
+
+function IsIpkFile(name)
+ name = name or ""
+ local ext = string.lower(string.sub(name, -4, -1))
+ return ext == ".ipk"
+end
+
+btnis = tb:option(Button, "install", translate("Install"))
+btnis.template = "cbi/other_button"
+btnis.render = function(self, section, scope)
+ if not inits[section] then return false end
+ if IsIpkFile(inits[section].name) then
+ scope.display = ""
+ else
+ scope.display = "none"
+ end
+ self.inputstyle = "apply"
+ Button.render(self, section, scope)
+end
+
+btnis.write = function(self, section)
+ local r = luci.sys.exec(string.format('opkg --force-depends install "/tmp/upload/%s"', inits[section].name))
+ form.description = string.format('%s', r)
+end
+
+return ful, fdl, form
diff --git a/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_button.htm b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_button.htm
new file mode 100644
index 0000000000..1c391ad984
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_button.htm
@@ -0,0 +1,7 @@
+<%+cbi/valueheader%>
+ <% if self:cfgvalue(section) ~= false then %>
+ " style="display: <%= display %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
+ <% else %>
+ -
+ <% end %>
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_download.htm b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_download.htm
new file mode 100644
index 0000000000..c14728632e
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_download.htm
@@ -0,0 +1,5 @@
+<%+cbi/valueheader%>
+
+
+
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_dvalue.htm b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_dvalue.htm
new file mode 100644
index 0000000000..296c61e4d6
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_dvalue.htm
@@ -0,0 +1,8 @@
+<%+cbi/valueheader%>
+
+<%
+ local val = self:cfgvalue(section) or self.default or ""
+ write(pcdata(val))
+%>
+
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_upload.htm b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_upload.htm
new file mode 100644
index 0000000000..bb56f444fc
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/luasrc/view/cbi/other_upload.htm
@@ -0,0 +1,5 @@
+<%+cbi/valueheader%>
+
+
+
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-filetransfer/po/zh-cn/filetransfer.po b/package/lean/luci-app-filetransfer/po/zh-cn/filetransfer.po
new file mode 100644
index 0000000000..829c7d285b
--- /dev/null
+++ b/package/lean/luci-app-filetransfer/po/zh-cn/filetransfer.po
@@ -0,0 +1,56 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Choose local file:"
+msgstr "选择本地文件:"
+
+msgid "Couldn't open file:"
+msgstr "无法打开文件:"
+
+msgid "Create upload file error."
+msgstr "创建上传文件失败。"
+
+msgid "Download"
+msgstr "下载"
+
+msgid "Download file"
+msgstr "下载文件"
+
+msgid "File name"
+msgstr "文件名"
+
+msgid "File saved to"
+msgstr "文件保存到"
+
+msgid "FileTransfer"
+msgstr "文件传输"
+
+msgid "Install"
+msgstr "安装"
+
+msgid "Mode string"
+msgstr "模式字符串"
+
+msgid "Modify time"
+msgstr "修改时间"
+
+msgid "No specify upload file."
+msgstr "未指定上传文件"
+
+msgid "Path on Route:"
+msgstr "路由上目录"
+
+msgid "Remove"
+msgstr "移除"
+
+msgid "Size"
+msgstr "大小"
+
+msgid "Upload"
+msgstr "上传"
+
+msgid "Upload file list"
+msgstr "上传文件列表"
+
+msgid "Upload file to '/tmp/upload/'"
+msgstr "将文件上传到'/tmp/upload/'"
diff --git a/package/lean/luci-app-flowoffload/Makefile b/package/lean/luci-app-flowoffload/Makefile
new file mode 100644
index 0000000000..141eec07f8
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for Flow Offload
+LUCI_DEPENDS:=+kmod-ipt-offload +pdnsd-alt +kmod-tcp-bbr @!LINUX_3_18 @!LINUX_4_9
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=13
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-flowoffload/luasrc/controller/flowoffload.lua b/package/lean/luci-app-flowoffload/luasrc/controller/flowoffload.lua
new file mode 100644
index 0000000000..11249b76c8
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/luasrc/controller/flowoffload.lua
@@ -0,0 +1,46 @@
+module("luci.controller.flowoffload", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/flowoffload") then
+ return
+ end
+ local page
+ page = entry({"admin", "network", "flowoffload"}, cbi("flowoffload"), _("Turbo ACC Center"), 101)
+ page.i18n = "flowoffload"
+ page.dependent = true
+
+ entry({"admin", "network", "flowoffload", "status"}, call("action_status"))
+end
+
+local function is_running()
+ return luci.sys.call("[ `cat /sys/module/xt_FLOWOFFLOAD/refcnt 2>/dev/null` -gt 0 ] 2>/dev/null") == 0
+end
+
+local function is_bbr()
+ return luci.sys.call("[ `cat /proc/sys/net/ipv4/tcp_congestion_control 2>/dev/null` = bbr ] 2>/dev/null") == 0
+end
+
+local function is_fullcone()
+ return luci.sys.call("[ `cat /sys/module/xt_FULLCONENAT/refcnt 2>/dev/null` -gt 0 ] 2>/dev/null") == 0
+end
+
+local function is_dns()
+ return luci.sys.call("[ `uci get flowoffload.@flow[0].dnscache_enable 2>/dev/null` -ne 3 ] && pgrep dnscache >/dev/null || pgrep AdGuardHome >/dev/null") == 0
+end
+
+local function is_ad()
+ return luci.sys.call("pgrep AdGuardHome >/dev/null") == 0
+end
+
+function action_status()
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({
+ run_state = is_running(),
+ down_state = is_bbr(),
+ up_state = is_fullcone(),
+ dns_state = is_dns(),
+ ad_state = is_ad()
+ })
+end
+
+
diff --git a/package/lean/luci-app-flowoffload/luasrc/model/cbi/flowoffload.lua b/package/lean/luci-app-flowoffload/luasrc/model/cbi/flowoffload.lua
new file mode 100644
index 0000000000..d42fc86cfb
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/luasrc/model/cbi/flowoffload.lua
@@ -0,0 +1,66 @@
+local m,s,o
+local SYS = require "luci.sys"
+local trport = 3001
+local button = ""
+if luci.sys.call("pidof AdGuardHome >/dev/null") == 0 then
+ button = " "
+end
+
+m = Map("flowoffload")
+m.title = translate("Turbo ACC Acceleration Settings")
+m.description = translate("Opensource Linux Flow Offload driver (Fast Path or HWNAT)")
+m:append(Template("flow/status"))
+
+s = m:section(TypedSection, "flow")
+s.addremove = false
+s.anonymous = true
+
+flow = s:option(Flag, "flow_offloading", translate("Enable"))
+flow.default = 0
+flow.rmempty = false
+flow.description = translate("Enable software flow offloading for connections. (decrease cpu load / increase routing throughput)")
+
+hw = s:option(Flag, "flow_offloading_hw", translate("HWNAT"))
+hw.default = 0
+hw.rmempty = true
+hw.description = translate("Enable Hardware NAT (depends on hw capability like MTK 762x)")
+hw:depends("flow_offloading", 1)
+
+bbr = s:option(Flag, "bbr", translate("Enable BBR"))
+bbr.default = 0
+bbr.rmempty = false
+bbr.description = translate("Bottleneck Bandwidth and Round-trip propagation time (BBR)")
+
+aaaa = s:option(Flag, "filter_aaaa", translate("Filter AAAA"))
+aaaa.default = 0
+aaaa.rmempty = false
+aaaa.description = translate("Dnsmasq rejects IPv6 parsing and optimizes domestic complex dual-stack network")
+
+dns = s:option(Flag, "dns", translate("DNS Acceleration"))
+dns.default = 0
+dns.rmempty = false
+dns.description = translate("Enable DNS Cache Acceleration and anti ISP DNS pollution")
+
+o = s:option(ListValue, "dnscache_enable", translate("Resolve Dns Mode"), translate("AdGuardHome's login username/passwd: AdGuardHome, web console addr: IP:3001") .. button)
+o:value("1", translate("Use Pdnsd query and cache"))
+if nixio.fs.access("/usr/bin/dnsforwarder") then
+o:value("2", translate("Use dnsforwarder query and cache"))
+end
+if nixio.fs.access("/usr/bin/AdGuardHome") then
+o:value("3", translate("Use AdGuardHome query and cache"))
+end
+o.default = 1
+o:depends("dns", 1)
+
+o = s:option(Value, "dns_server", translate("Upsteam DNS Server"))
+o.default = "114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8"
+o.description = translate("Muitiple DNS server can saperate with ','")
+o:depends("dnscache_enable", 1)
+o:depends("dnscache_enable", 2)
+
+o = s:option(Value, "ipv6dns_server", translate("Upsteam IPV6 DNS Server"))
+o.default = "2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644"
+o.description = translate("Muitiple IPV6 DNS server can saperate with ','")
+o:depends("dnscache_enable", 2)
+
+return m
diff --git a/package/lean/luci-app-flowoffload/luasrc/view/flow/status.htm b/package/lean/luci-app-flowoffload/luasrc/view/flow/status.htm
new file mode 100644
index 0000000000..58373ef01b
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/luasrc/view/flow/status.htm
@@ -0,0 +1,27 @@
+
+
+
diff --git a/package/lean/luci-app-flowoffload/po/zh-cn/flowoffload.po b/package/lean/luci-app-flowoffload/po/zh-cn/flowoffload.po
new file mode 100644
index 0000000000..b2f0826bbc
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/po/zh-cn/flowoffload.po
@@ -0,0 +1,74 @@
+msgid "Flow Offload ACC"
+msgstr "Flow Offload 转发加速"
+
+msgid "Turbo ACC Center"
+msgstr "Turbo ACC 网络加速"
+
+msgid "Turbo ACC Acceleration Settings"
+msgstr "Turbo ACC 网络加速设置"
+
+msgid "Linux Flow Offload Forwarding Engine Settings"
+msgstr "Linux Flow Offload Forwarding 转发加速引擎设置"
+
+msgid "Opensource Linux Flow Offload driver (Fast Path or HWNAT)"
+msgstr "开源 Linux Flow Offload 驱动 (支持 Fast Path 或者 HWNAT)"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Enable software flow offloading for connections. (decrease cpu load / increase routing throughput)"
+msgstr "开启 Flow Offloading 转发加速. (降低CPU占用 / 增强路由转发性能,和多WAN或者QOS功能可能会冲突)"
+
+msgid "Enable Hardware NAT (depends on hw capability like MTK 762x)"
+msgstr "启用硬件HWNAT加速(依赖特定的硬件,例如 MTK 762x 系列)"
+
+msgid "Enable BBR"
+msgstr "启用 BBR"
+
+msgid "Bottleneck Bandwidth and Round-trip propagation time (BBR)"
+msgstr "BBR拥塞控制算法可以加强TCP应用表现,但会降低路由器本机上的WIFI吞吐性能"
+
+msgid "DNS Acceleration"
+msgstr "DNS 加速"
+
+msgid "Enable DNS Cache Acceleration and anti ISP DNS pollution"
+msgstr "启用DNS多线程查询、缓存,并防止ISP的DNS广告和域名劫持"
+
+msgid "Resolve Dns Mode"
+msgstr "DNS解析方式"
+
+msgid "Use Pdnsd query and cache"
+msgstr "使用pdnsd解析"
+
+msgid "Use dnsforwarder query and cache"
+msgstr "使用dnsforwarder解析"
+
+msgid "Use AdGuardHome query and cache"
+msgstr "使用AdGuardHome解析去广告"
+
+msgid "Upsteam DNS Server"
+msgstr "上游 DNS 服务器(国内)"
+
+msgid "Upsteam IPV6 DNS Server"
+msgstr "上游 IPV6 DNS 服务器"
+
+msgid "Muitiple DNS server can saperate with ','"
+msgstr "支持多个上游DNS服务器,用','分隔(注意用英文逗号).请填写您最快的DNS服务器"
+
+msgid "Muitiple IPV6 DNS server can saperate with ','"
+msgstr "支持dnsforwarder解析多个上游IPV6 DNS服务器,用','分隔(注意用英文逗号).请填写您最快的DNS服务器"
+
+msgid "Enable dnsmasq killqqad"
+msgstr "启用dnsmasq去广告"
+
+msgid "dnsmasq killqqad"
+msgstr "dnsmasq去广告"
+
+msgid "AdGuardHome's login username/passwd: AdGuardHome, web console addr: IP:3001"
+msgstr "AdGuardHome 网页控制台地址:IP:3001 登录账号/密码:AdGuardHome"
+
+msgid "Filter AAAA"
+msgstr "忽略域名的IPv6地址"
+
+msgid "Dnsmasq rejects IPv6 parsing and optimizes domestic complex dual-stack network"
+msgstr "dnsmasq不返回ipv6解析结果,优化国内复杂双栈网络"
diff --git a/package/lean/luci-app-flowoffload/root/etc/config/flowoffload b/package/lean/luci-app-flowoffload/root/etc/config/flowoffload
new file mode 100644
index 0000000000..1a4f0b4647
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/root/etc/config/flowoffload
@@ -0,0 +1,11 @@
+
+config flow
+ option flow_offloading '0'
+ option flow_offloading_hw '0'
+ option bbr '0'
+ option dns '0'
+ option dnscache_enable '1'
+ option dns_server '114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8'
+ option ipv6dns_server '2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644'
+
+
diff --git a/package/lean/luci-app-flowoffload/root/etc/init.d/flowoffload b/package/lean/luci-app-flowoffload/root/etc/init.d/flowoffload
new file mode 100755
index 0000000000..11d5cb60ed
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/root/etc/init.d/flowoffload
@@ -0,0 +1,285 @@
+#!/bin/sh /etc/rc.common
+# Copyright (c) 2011-2015 OpenWrt.org
+
+START=90
+
+USE_PROCD=1
+
+DNSMASQ_RESTART=N
+DNS_SERVER="114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8"
+IPV6DNS_SERVER="2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644"
+lan_addr=$(uci get network.lan.ipaddr)
+
+start_pdnsd() {
+ DNS_SERVER=$(uci get flowoffload.@flow[0].dns_server 2>/dev/null)
+ cp -a /usr/sbin/pdnsd /usr/sbin/dnscache
+ [ -d /var/etc ] || mkdir -p /var/etc
+
+ if [ ! -f /var/dnscache/pdnsd.cache ]; then
+ mkdir -p /var/dnscache
+ echo -ne "pd13\000\000\000\000" > /var/dnscache/pdnsd.cache
+ chown -R nobody.nogroup /var/dnscache
+ fi
+
+ cat > /var/etc/dnscache.conf </dev/null)
+ IPV6DNS_SERVER=$(uci get flowoffload.@flow[0].ipv6dns_server 2>/dev/null)
+ dnscache_enable=$(uci get flowoffload.@flow[0].dnscache_enable 2>/dev/null)
+ if [ $dnscache_enable = "2" ]; then
+ localserver=$(echo -e "127.0.0.1:5333")
+ elif [ $dnscache_enable = "3" ]; then
+ localserver=$(echo -e "127.0.0.1:5383")
+ fi
+
+ cp -a /usr/bin/dnsforwarder /usr/sbin/dnscache
+ sleep 1
+ DNSCACHE_CONF=/var/run/dnscache/dnscache.conf
+ PID_PATH=/var/run/dnscache
+ PID_FILE=dns.pid
+ mkdir -p $PID_PATH
+ pid=`ps | awk '$5 ~ /\[dnscache\]/ {print $1}'`
+
+ cat > /var/run/dnscache/dnscache.conf </dev/null 2>&1 || kill -9 $(ps | grep AdGuardHome | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ kill $(pidof dnscache) >/dev/null 2>&1 || kill -9 $(ps | grep dnscache | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ kill -9 $(ps | grep dnscache-while.sh | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ rm -rf /var/run/dnscache
+ rm -rf /var/dnscache
+ echo "Stop DNS Cache"
+
+}
+
+change_dns() {
+ uci delete dhcp.@dnsmasq[0].server >/dev/null 2>&1
+ uci add_list dhcp.@dnsmasq[0].server=127.0.0.1#5333
+ uci set dhcp.@dnsmasq[0].noresolv=1
+ uci commit dhcp
+
+}
+
+revert_dns() {
+ uci del_list dhcp.@dnsmasq[0].server=127.0.0.1#5333 >/dev/null 2>&1
+ uci set dhcp.@dnsmasq[0].resolvfile=/tmp/resolv.conf.auto
+ uci set dhcp.@dnsmasq[0].noresolv=0
+ uci commit dhcp
+
+}
+
+start_service(){
+ dns=$(uci get flowoffload.@flow[0].dns 2>/dev/null)
+ dnscache_enable=$(uci get flowoffload.@flow[0].dnscache_enable 2>/dev/null)
+ bbr=$(uci get flowoffload.@flow[0].bbr 2>/dev/null)
+ aaaa=$(uci get flowoffload.@flow[0].filter_aaaa 2>/dev/null)
+ if [ $aaaa -eq 1 ]; then
+ sed -i '/filter-aaaa/d' /etc/dnsmasq.conf
+ echo "filter-aaaa" >> /etc/dnsmasq.conf
+ else
+ sed -i '/filter-aaaa/d' /etc/dnsmasq.conf
+ fi
+
+ if [ $dns -eq 1 ]; then
+ stop_dnscache
+ sleep 1
+ rm -f /var/log/dnscache.file
+ rm -f /etc/AdGuardHome/data/querylog.json*
+ if [ $dnscache_enable = "1" ]; then
+ start_pdnsd
+ elif [ $dnscache_enable = "2" ]; then
+ start_dnsforwarder
+ elif [ $dnscache_enable = "3" ]; then
+ AdGuardHome -c /etc/AdGuardHome/AdGuardHome.yaml -w /etc/AdGuardHome -h ${lan_addr} -p 3001 >/dev/null 2>&1 &
+ fi
+ change_dns
+ nohup /usr/share/dnscache/dnscache-while.sh > /var/log/dnscache.file 2>&1 &
+ else
+ stop_dnscache
+ revert_dns
+ fi
+
+ if [ $bbr -eq 1 ]; then
+ sysctl -w net.ipv4.tcp_congestion_control=bbr
+ else
+ sysctl -w net.ipv4.tcp_congestion_control=cubic
+ fi
+
+ uci set firewall.@defaults[0].flow_offloading=$(uci get flowoffload.@flow[0].flow_offloading)
+ uci set firewall.@defaults[0].flow_offloading_hw=$(uci get flowoffload.@flow[0].flow_offloading_hw)
+ uci commit firewall
+ if [ "$DNSMASQ_RESTART" = N ]; then
+ /etc/init.d/dnsmasq restart && echo "DNSMASQ change"
+ /etc/init.d/firewall restart
+ fi
+}
+
+stop_service(){
+ dns=$(uci get firewall.@defaults[0].dns 2>/dev/null)
+ stop_dnscache
+ revert_dns
+ uci set firewall.@defaults[0].flow_offloading=$(uci get flowoffload.@flow[0].flow_offloading)
+ uci set firewall.@defaults[0].flow_offloading_hw=$(uci get flowoffload.@flow[0].flow_offloading_hw)
+ uci commit firewall
+ if [ "$DNSMASQ_RESTART" = N ]; then
+ /etc/init.d/dnsmasq restart && echo "DNSMASQ revert"
+ /etc/init.d/firewall restart
+ fi
+}
+
+restart(){
+ DNSMASQ_RESTART=Y
+ stop
+ start
+ /etc/init.d/dnsmasq restart && echo "DNSMASQ restart"
+ /etc/init.d/firewall restart
+}
+
diff --git a/package/lean/luci-app-flowoffload/root/etc/uci-defaults/flowoffload b/package/lean/luci-app-flowoffload/root/etc/uci-defaults/flowoffload
new file mode 100755
index 0000000000..246bccfec5
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/root/etc/uci-defaults/flowoffload
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+uci set firewall.@defaults[0].flow_offloading=1
+uci commit firewall
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@flowoffload[-1]
+ add ucitrack flowoffload
+ set ucitrack.@flowoffload[-1].init=flowoffload
+ commit ucitrack
+EOF
+
+exit 0
diff --git a/package/lean/luci-app-flowoffload/root/usr/share/dnscache/dnscache-while.sh b/package/lean/luci-app-flowoffload/root/usr/share/dnscache/dnscache-while.sh
new file mode 100755
index 0000000000..aabcbd1f48
--- /dev/null
+++ b/package/lean/luci-app-flowoffload/root/usr/share/dnscache/dnscache-while.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+sleeptime=60
+logfile="/var/log/dnscache.file"
+adg_logfile="/etc/AdGuardHome/data/querylog.json"
+dns_enable=$(uci get flowoffload.@flow[0].dns 2>/dev/null)
+dnscache_enable=$(uci get flowoffload.@flow[0].dnscache_enable 2>/dev/null)
+
+clean_log(){
+logrow=$(grep -c "" ${logfile})
+if [ $logrow -ge 500 ];then
+ cat /dev/null > ${logfile}
+ echo "$curtime Log条数超限,清空处理!" >> ${logfile}
+fi
+if [ $dnscache_enable = "3" ];then
+ adg_logrow=$(grep -c "" ${adg_logfile})
+ if [ $adg_logrow -ge 500 ];then
+ cat /dev/null > ${adg_logfile}
+ echo "$curtime Log条数超限,清空处理!" >> ${adg_logfile}
+ fi
+fi
+}
+
+while [ $dns_enable -eq 1 ];
+do
+curtime=`date "+%H:%M:%S"`
+echo "$curtime online! "
+if [ $dns_enable -eq 1 ]; then
+ if [ $dnscache_enable = "3" ];then
+ if ! pidof AdGuardHome>/dev/null;then
+ AdGuardHome -c /etc/AdGuardHome/AdGuardHome.yaml -w /etc/AdGuardHome >/dev/null 2>&1 &
+ echo "$curtime 重启服务!" >> ${logfile}
+ fi
+ else
+ if ! pidof dnscache>/dev/null; then
+ if [ $dnscache_enable = "1" ];then
+ /usr/sbin/dnscache -c /var/etc/dnscache.conf -d
+ elif [ $dnscache_enable = "2" ];then
+ dnscache -f /var/run/dnscache/dnscache.conf -d
+ fi
+ echo "$curtime 重启服务!" >> ${logfile}
+ fi
+ fi
+fi
+
+clean_log
+sleep ${sleeptime}
+continue
+done
+
diff --git a/package/lean/luci-app-frpc/Makefile b/package/lean/luci-app-frpc/Makefile
new file mode 100644
index 0000000000..911ad624d4
--- /dev/null
+++ b/package/lean/luci-app-frpc/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI for FRPC
+LUCI_DEPENDS:=+wget +frpc
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.1
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-frpc/luasrc/controller/frp.lua b/package/lean/luci-app-frpc/luasrc/controller/frp.lua
new file mode 100644
index 0000000000..41df2c8cb2
--- /dev/null
+++ b/package/lean/luci-app-frpc/luasrc/controller/frp.lua
@@ -0,0 +1,18 @@
+module("luci.controller.frp", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/frp") then
+ return
+ end
+
+ entry({"admin","services","frp"},cbi("frp/frp"), _("Frp Setting"),100).dependent=true
+ entry({"admin","services","frp","config"},cbi("frp/config")).leaf=true
+ entry({"admin","services","frp","status"},call("status")).leaf=true
+end
+
+function status()
+local e={}
+e.running=luci.sys.call("pidof frpc > /dev/null")==0
+luci.http.prepare_content("application/json")
+luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-frpc/luasrc/i18n/frp.zh-cn.lmo b/package/lean/luci-app-frpc/luasrc/i18n/frp.zh-cn.lmo
new file mode 100644
index 0000000000..f3b95348f8
Binary files /dev/null and b/package/lean/luci-app-frpc/luasrc/i18n/frp.zh-cn.lmo differ
diff --git a/package/lean/luci-app-frpc/luasrc/model/cbi/frp/config.lua b/package/lean/luci-app-frpc/luasrc/model/cbi/frp/config.lua
new file mode 100644
index 0000000000..e9499c8c97
--- /dev/null
+++ b/package/lean/luci-app-frpc/luasrc/model/cbi/frp/config.lua
@@ -0,0 +1,116 @@
+local n="frp"
+local i=require"luci.dispatcher"
+local o=require"luci.model.network".init()
+local m=require"nixio.fs"
+local a,t,e
+arg[1]=arg[1]or""
+a=Map(n,translate("Frp Domain Config"))
+a.redirect=i.build_url("admin","services","frp")
+t=a:section(NamedSection,arg[1],"frp",translate("Config Frp Protocol"))
+t.addremove=false
+t.dynamic=false
+t:tab("base",translate("Basic Settings"))
+t:tab("other",translate("Other Settings"))
+e=t:taboption("base",ListValue,"enable",translate("Enable State"))
+e.default="1"
+e.rmempty=false
+e:value("1",translate("Enable"))
+e:value("0",translate("Disable"))
+e=t:taboption("base",ListValue, "type", translate("Frp Protocol Type"))
+e:value("http",translate("HTTP"))
+e:value("https",translate("HTTPS"))
+e:value("tcp",translate("TCP"))
+e:value("udp",translate("UDP"))
+e:value("stcp",translate("STCP"))
+e = t:taboption("base",ListValue, "domain_type", translate("Domain Type"))
+e.default = "custom_domains"
+e:value("custom_domains",translate("Custom Domains"))
+e:value("subdomain",translate("SubDomain"))
+e:value("both_dtype",translate("Both the above two Domain types"))
+e:depends("type","http")
+e:depends("type","https")
+e = t:taboption("base",Value, "custom_domains", translate("Custom Domains"), translate("If SubDomain is used, Custom Domains couldn't be subdomain or wildcard domain of the maindomain(subdomain_host)."))
+e:depends("domain_type","custom_domains")
+e:depends("domain_type","both_dtype")
+e = t:taboption("base",Value, "subdomain", translate("SubDomain"), translate("subdomain_host must be configured in server: frps in advance."))
+e:depends("domain_type","subdomain")
+e:depends("domain_type","both_dtype")
+e = t:taboption("base",ListValue, "stcp_role", translate("STCP Role"))
+e.default = "server"
+e:value("server",translate("STCP Server"))
+e:value("vistor",translate("STCP Vistor"))
+e:depends("type","stcp")
+e = t:taboption("base",Value, "remote_port", translate("Remote Port"))
+e.datatype = "port"
+e:depends("type","tcp")
+e:depends("type","udp")
+e = t:taboption("other",Flag, "enable_plugin", translate("Use Plugin"),translate("If plugin is defined, local_ip and local_port is useless, plugin will handle connections got from frps."))
+e.default = "0"
+e:depends("type","tcp")
+e = t:taboption("base",Value, "local_ip", translate("Local Host Address"))
+luci.sys.net.ipv4_hints(function(x,d)
+e:value(x,"%s (%s)"%{x,d})
+end)
+e.datatype = "ip4addr"
+e:depends("type","udp")
+e:depends("type","http")
+e:depends("type","https")
+e:depends("enable_plugin",0)
+e = t:taboption("base",Value, "local_port", translate("Local Host Port"))
+e.datatype = "port"
+e:depends("type","udp")
+e:depends("type","http")
+e:depends("type","https")
+e:depends("enable_plugin",0)
+e = t:taboption("base",Value, "stcp_secretkey", translate("STCP Screct Key"))
+e.default = "abcdefg"
+e:depends("type","stcp")
+e = t:taboption("base",Value, "stcp_servername", translate("STCP Server Name"), translate("STCP Server Name is Service Remark Name of STCP Server"))
+e.default = "secret_tcp"
+e:depends("stcp_role","vistor")
+e = t:taboption("other",Flag, "enable_locations", translate("Enable URL routing"), translate("Frp support forward http requests to different backward web services by url routing."))
+e:depends("type","http")
+e = t:taboption("other",Value, "locations ", translate("URL routing"), translate("Http requests with url prefix /news will be forwarded to this service."))
+e.default="locations=/"
+e:depends("enable_locations",1)
+e = t:taboption("other",ListValue, "plugin", translate("Choose Plugin"))
+e:value("http_proxy",translate("http_proxy"))
+e:value("socks5",translate("socks5"))
+e:value("unix_domain_socket",translate("unix_domain_socket"))
+e:depends("enable_plugin",1)
+e = t:taboption("other",Flag, "enable_plugin_httpuserpw", translate("Proxy Authentication"),translate("Other PCs could access the Internet through frpc's network by using http_proxy plugin."))
+e.default = "0"
+e:depends("plugin","http_proxy")
+e = t:taboption("other",Value, "plugin_http_user", translate("HTTP Proxy UserName"))
+e.default = "abc"
+e:depends("enable_plugin_httpuserpw",1)
+e = t:taboption("other",Value, "plugin_http_passwd", translate("HTTP Proxy Password"))
+e.default = "abc"
+e:depends("enable_plugin_httpuserpw",1)
+e = t:taboption("other",Value, "plugin_unix_path", translate("Plugin Unix Sock Path"))
+e.default = "/var/run/docker.sock"
+e:depends("plugin","unix_domain_socket")
+e = t:taboption("other",Flag, "enable_http_auth", translate("Password protecting your web service"), translate("Http username and password are safety certification for http protocol."))
+e.default = "0"
+e:depends("type","http")
+e = t:taboption("other",Value, "http_user", translate("HTTP UserName"))
+e.default = "frp"
+e:depends("enable_http_auth",1)
+e = t:taboption("other",Value, "http_pwd", translate("HTTP PassWord"))
+e.default = "frp"
+e:depends("enable_http_auth",1)
+e = t:taboption("other",Flag, "enable_host_header_rewrite", translate("Rewriting the Host Header"), translate("Frp can rewrite http requests with a modified Host header."))
+e.default = "0"
+e:depends("type","http")
+e = t:taboption("other",Value, "host_header_rewrite", translate("Host Header"), translate("The Host header will be rewritten to match the hostname portion of the forwarding address."))
+e.default = "dev.yourdomain.com"
+e:depends("enable_host_header_rewrite",1)
+e = t:taboption("base",Flag, "use_encryption", translate("Use Encryption"), translate("Encrypted the communication between frpc and frps, will effectively prevent the traffic intercepted."))
+e.default = "1"
+e.rmempty = false
+e = t:taboption("base",Flag, "use_compression", translate("Use Compression"), translate("The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources."))
+e.default = "1"
+e.rmempty = false
+e = t:taboption("base",Value, "remark", translate("Service Remark Name"), translate("Please ensure the remark name is unique."))
+e.rmempty = false
+return a
diff --git a/package/lean/luci-app-frpc/luasrc/model/cbi/frp/frp.lua b/package/lean/luci-app-frpc/luasrc/model/cbi/frp/frp.lua
new file mode 100644
index 0000000000..531dd84f6c
--- /dev/null
+++ b/package/lean/luci-app-frpc/luasrc/model/cbi/frp/frp.lua
@@ -0,0 +1,160 @@
+local o=require"luci.dispatcher"
+local e=require("luci.model.ipkg")
+local s=require"nixio.fs"
+local e=luci.model.uci.cursor()
+local i="frp"
+local a,t,e
+local n={}
+a=Map(i,translate("Frp Setting"), translate("Frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet."))
+a:section(SimpleSection).template="frp/frp_status"
+t=a:section(NamedSection,"common","frp",translate("Global Setting"))
+t.anonymous=true
+t.addremove=false
+t:tab("base",translate("Basic Settings"))
+t:tab("other",translate("Other Settings"))
+t:tab("log",translate("Client Log"))
+e=t:taboption("base",Flag, "enabled", translate("Enabled"))
+e.rmempty=false
+e=t:taboption("base",Value, "server_addr", translate("Server"))
+e.optional=false
+e.rmempty=false
+e=t:taboption("base",Value, "server_port", translate("Port"))
+e.datatype = "port"
+e.optional=false
+e.rmempty=false
+e=t:taboption("base",Value, "token", translate("Token"), translate("Time duration between server of frpc and frps mustn't exceed 15 minutes."))
+e.optional=false
+e.password=true
+e.rmempty=false
+e=t:taboption("base",Value, "vhost_http_port", translate("Vhost HTTP Port"))
+e.datatype = "port"
+e.rmempty=false
+e=t:taboption("base",Value, "vhost_https_port", translate("Vhost HTTPS Port"))
+e.datatype = "port"
+e.rmempty=false
+e=t:taboption("other",Flag, "login_fail_exit", translate("Exit program when first login failed"),translate("decide if exit program when first login failed, otherwise continuous relogin to frps."))
+e.default = "1"
+e.rmempty=false
+e=t:taboption("other",Flag, "tcp_mux", translate("TCP Stream Multiplexing"), translate("Default is Ture. This feature in frps.ini and frpc.ini must be same."))
+e.default = "1"
+e.rmempty=false
+e=t:taboption("other",ListValue, "protocol", translate("Protocol Type"),translate("Frp support kcp protocol since v0.12.0"))
+e.default = "tcp"
+e:value("tcp",translate("TCP Protocol"))
+e:value("kcp",translate("KCP Protocol"))
+e=t:taboption("other",Flag, "enable_http_proxy", translate("Connect frps by HTTP PROXY"), translate("frpc can connect frps using HTTP PROXY"))
+e.default = "0"
+e.rmempty=false
+e:depends("protocol","tcp")
+e=t:taboption("other",Value, "http_proxy", translate("HTTP PROXY"))
+e.datatype="uinteger"
+e.placeholder="http://user:pwd@192.168.1.128:8080"
+e:depends("enable_http_proxy",1)
+e.optional=false
+e=t:taboption("other",Flag, "enable_cpool", translate("Enable Connection Pool"), translate("This feature is fit for a large number of short connections."))
+e.rmempty=false
+e=t:taboption("other",Value, "pool_count", translate("Connection Pool"), translate("Connections will be established in advance."))
+e.datatype="uinteger"
+e.default = "1"
+e:depends("enable_cpool",1)
+e.optional=false
+e=t:taboption("base",Value,"time",translate("Service registration interval"),translate("0 means disable this feature, unit: min"))
+e.datatype="range(0,59)"
+e.default=30
+e.rmempty=false
+e=t:taboption("other",ListValue, "log_level", translate("Log Level"))
+e.default = "warn"
+e:value("trace",translate("Trace"))
+e:value("debug",translate("Debug"))
+e:value("info",translate("Info"))
+e:value("warn",translate("Warning"))
+e:value("error",translate("Error"))
+e=t:taboption("other",Value, "log_max_days", translate("Log Keepd Max Days"))
+e.datatype = "uinteger"
+e.default = "3"
+e.rmempty=false
+e.optional=false
+e=t:taboption("log",TextValue,"log")
+e.rows=26
+e.wrap="off"
+e.readonly=true
+e.cfgvalue=function(t,t)
+return s.readfile("/var/etc/frp/frpc.log")or""
+end
+e.write=function(e,e,e)
+end
+t=a:section(TypedSection,"proxy",translate("Services List"))
+t.anonymous=true
+t.addremove=true
+t.template="cbi/tblsection"
+t.extedit=o.build_url("admin","services","frp","config","%s")
+function t.create(e,t)
+new=TypedSection.create(e,t)
+luci.http.redirect(e.extedit:format(new))
+end
+function t.remove(e,t)
+e.map.proceed=true
+e.map:del(t)
+luci.http.redirect(o.build_url("admin","services","frp"))
+end
+local o=""
+e=t:option(DummyValue,"remark",translate("Service Remark Name"))
+e.width="10%"
+e=t:option(DummyValue,"type",translate("Frp Protocol Type"))
+e.width="10%"
+e=t:option(DummyValue,"custom_domains",translate("Domain/Subdomain"))
+e.width="20%"
+e.cfgvalue=function(t,n)
+local t=a.uci:get(i,n,"domain_type")or""
+local m=a.uci:get(i,n,"type")or""
+if t=="custom_domains" then
+local b=a.uci:get(i,n,"custom_domains")or"" return b end
+if t=="subdomain" then
+local b=a.uci:get(i,n,"subdomain")or"" return b end
+if t=="both_dtype" then
+local b=a.uci:get(i,n,"custom_domains")or""
+local c=a.uci:get(i,n,"subdomain")or""
+b="%s/%s"%{b,c} return b end
+if m=="tcp" or m=="udp" then
+local b=a.uci:get(i,"common","server_addr")or"" return b end
+end
+e=t:option(DummyValue,"remote_port",translate("Remote Port"))
+e.width="10%"
+e.cfgvalue=function(t,b)
+local t=a.uci:get(i,b,"type")or""
+if t==""or b==""then return""end
+if t=="http" then
+local b=a.uci:get(i,"common","vhost_http_port")or"" return b end
+if t=="https" then
+local b=a.uci:get(i,"common","vhost_https_port")or"" return b end
+if t=="tcp" or t=="udp" then
+local b=a.uci:get(i,b,"remote_port")or"" return b end
+end
+e=t:option(DummyValue,"local_ip",translate("Local Host Address"))
+e.width="15%"
+e=t:option(DummyValue,"local_port",translate("Local Host Port"))
+e.width="10%"
+e=t:option(DummyValue,"use_encryption",translate("Use Encryption"))
+e.width="15%"
+e.cfgvalue=function(t,n)
+local t=a.uci:get(i,n,"use_encryption")or""
+local b
+if t==""or b==""then return""end
+if t=="1" then b="ON"
+else b="OFF" end
+return b
+end
+e=t:option(DummyValue,"use_compression",translate("Use Compression"))
+e.width="15%"
+e.cfgvalue=function(t,n)
+local t=a.uci:get(i,n,"use_compression")or""
+local b
+if t==""or b==""then return""end
+if t=="1" then b="ON"
+else b="OFF" end
+return b
+end
+e=t:option(Flag,"enable",translate("Enable State"))
+e.width="10%"
+e.rmempty=false
+return a
diff --git a/package/lean/luci-app-frpc/luasrc/view/frp/frp_status.htm b/package/lean/luci-app-frpc/luasrc/view/frp/frp_status.htm
new file mode 100644
index 0000000000..a344671726
--- /dev/null
+++ b/package/lean/luci-app-frpc/luasrc/view/frp/frp_status.htm
@@ -0,0 +1,23 @@
+
+
+
diff --git a/package/lean/luci-app-frpc/root/etc/config/frp b/package/lean/luci-app-frpc/root/etc/config/frp
new file mode 100644
index 0000000000..736e571f92
--- /dev/null
+++ b/package/lean/luci-app-frpc/root/etc/config/frp
@@ -0,0 +1,16 @@
+
+config frp 'common'
+ option log_max_days '3'
+ option login_fail_exit '0'
+ option enable_cpool '0'
+ option time '40'
+ option tcp_mux '1'
+ option enabled '0'
+ option vhost_http_port '80'
+ option vhost_https_port '443'
+ option server_addr 'yourdomain.com'
+ option server_port '7000'
+ option token '1234567'
+ option log_level 'info'
+ option enable_http_proxy '0'
+ option protocol 'tcp'
diff --git a/package/lean/luci-app-frpc/root/etc/init.d/frp b/package/lean/luci-app-frpc/root/etc/init.d/frp
new file mode 100755
index 0000000000..66f7881dcc
--- /dev/null
+++ b/package/lean/luci-app-frpc/root/etc/init.d/frp
@@ -0,0 +1,214 @@
+#!/bin/sh /etc/rc.common
+#Author: monokoo
+#Thanks to FW867's help
+
+START=99
+SERVICE_WRITE_PID=1
+SERVICE_DAEMONIZE=1
+
+LOGFILE="/var/etc/frp/frpc.log"
+
+echo_date(){
+ local log=$1
+ echo $(date +%Y/%m/%d\ %X): "$log" >> $LOGFILE
+}
+
+Reduce_Log(){
+ local log=$1
+ [ ! -f "$log" ] && return
+ local sc=200
+ [ -n "$2" ] && sc=$2
+ local count=$(grep -c "" $log)
+ if [ $count -gt $sc ];then
+ let count=count-$sc
+ sed -i "1,$count d" $log
+ fi
+}
+
+conf_proxy_add() {
+
+ local cfg="$1"
+ local tmpconf="$2"
+ local enable type domain_type custom_domains remote_port local_ip local_port enable_http_auth enable_host_header_rewrite host_header_rewrite
+ local subdomain use_encryption use_compression http_user http_pwd remark locations
+ local enable_plugin plugin plugin_http_user plugin_http_passwd plugin_unix_path stcp_role stcp_secretkey stcp_servername
+
+ config_get_bool enable "$cfg" enable 1
+ [ "$enable" -gt 0 ] || return 1
+
+ config_get type "$cfg" type
+ config_get custom_domains "$cfg" custom_domains
+ config_get subdomain "$cfg" subdomain
+ config_get remote_port "$cfg" remote_port
+ config_get local_ip "$cfg" local_ip
+ config_get local_port "$cfg" local_port
+ config_get locations "$cfg" locations
+ config_get host_header_rewrite "$cfg" host_header_rewrite
+ config_get http_user "$cfg" http_user
+ config_get http_pwd "$cfg" http_pwd
+ config_get remark "$cfg" remark
+ config_get plugin "$cfg" plugin
+ config_get plugin_http_user "$cfg" plugin_http_user
+ config_get plugin_http_passwd "$cfg" plugin_http_passwd
+ config_get plugin_unix_path "$cfg" plugin_unix_path
+ config_get stcp_role "$cfg" stcp_role
+ config_get stcp_secretkey "$cfg" stcp_secretkey
+ config_get stcp_servername "$cfg" stcp_servername
+
+
+ [ -n "$remark" ] && [ -n "$type" ] || return 1
+
+ echo "" >>$tmpconf
+ echo "[$remark]" >>$tmpconf
+ echo "type=$type" >>$tmpconf
+ [ -n "$custom_domains" ] && echo "custom_domains=$custom_domains" >>$tmpconf
+ [ -n "$subdomain" ] && echo "subdomain=$subdomain" >>$tmpconf
+ [ -n "$remote_port" ] && echo "remote_port=$remote_port" >>$tmpconf
+ [ -z "$stcp_role" ] && [ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
+ [ -z "$stcp_role" ] && [ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
+ [ -n "$locations" ] && echo "locations=$locations" >>$tmpconf
+ [ -n "$http_user" -a -n "$http_pwd" ] && {
+ echo "http_user=$http_user" >>$tmpconf
+ echo "http_pwd=$http_pwd" >>$tmpconf
+ }
+ [ -n "$host_header_rewrite" ] && echo "host_header_rewrite=$host_header_rewrite" >>$tmpconf
+ [ -n "$plugin" ] && echo "plugin=$plugin" >>$tmpconf
+ [ -n "$plugin_http_user" -a -n "$plugin_http_passwd" ] && {
+ echo "plugin_http_user=$plugin_http_user" >>$tmpconf
+ echo "plugin_http_passwd=$plugin_http_passwd" >>$tmpconf
+ }
+ [ -n "$plugin_unix_path" ] && echo "plugin_unix_path=$plugin_unix_path" >>$tmpconf
+
+ [ -n "$stcp_role" ] && {
+ if [ "$stcp_role" == "vistor" ]; then
+ echo "role=$stcp_role" >>$tmpconf
+ [ -n "$local_ip" ] && echo "bind_addr=$local_ip" >>$tmpconf
+ [ -n "$local_port" ] && echo "bind_port=$local_port" >>$tmpconf
+ [ -n "$stcp_servername" ] && echo "server_name=$stcp_servername" >>$tmpconf || return 1
+ else
+ [ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
+ [ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
+ fi
+ [ -n "$stcp_secretkey" ] && echo "sk=$stcp_secretkey" >>$tmpconf || return 1
+ }
+
+
+
+ frp_write_bool use_encryption $cfg 1
+ frp_write_bool use_compression $cfg 1
+
+}
+
+frp_write_bool() {
+ local opt="$1"
+ local config="$2"
+ local def="$3"
+ local val
+
+ config_get_bool val $config "$opt" "$def"
+ if [ "$val" -eq 0 ]; then
+ echo "${opt}=false" >> $tmpconf
+ else
+ echo "${opt}=true" >> $tmpconf
+ fi
+}
+
+frp_add_cru(){
+time=$1
+if [ ! -f "/etc/crontabs/root" ] || [ -z "$(cat /etc/crontabs/root | grep frp)" ]; then
+ sed -i '/frp/d' /etc/crontabs/root >/dev/null 2>&1
+ echo "*/$time * * * * /etc/init.d/frp restart" >> /etc/crontabs/root
+fi
+
+}
+
+frp_del_cru(){
+if [ ! -f "/etc/crontabs/root" ] || [ -n "$(cat /etc/crontabs/root | grep frp)" ]; then
+ sed -i '/frp/d' /etc/crontabs/root >/dev/null 2>&1
+fi
+}
+
+download_binary(){
+ echo_date "开始下载frpc二进制文件..."
+ /usr/bin/wget --no-check-certificate --timeout=10 --tries=1 -o $LOGFILE https://github.com/fatedier/frp/releases/download/v0.13.0/frp_0.13.0_linux_arm.tar.gz -O /tmp/frp_0.13.0_linux_arm.tar.gz
+ [ ! -s "/tmp/frp_0.13.0_linux_arm.tar.gz" ] && /usr/bin/wget -q --no-check-certificate --timeout=10 --tries=1 https://any.mokoo.xyz/app/frp_0.13.0_linux_arm.tar.gz -O /tmp/frp_0.13.0_linux_arm.tar.gz
+ [ -f "/tmp/frp_0.13.0_linux_arm.tar.gz" ] && tar -xf /tmp/frp_0.13.0_linux_arm.tar.gz -C /tmp && \
+ mv /tmp/frp_0.13.0_linux_arm/frpc /usr/bin/frpc
+ rm -rf /tmp/frp_0.13.0_linux_arm*
+ if [ -f "/usr/bin/frpc" ]; then
+ chmod +x /usr/bin/frpc && echo_date "成功下载frpc二进制文件"
+ else
+ echo_date "下载frpc二进制文件失败,请重试!"
+ fi
+}
+
+boot() {
+ sleep 10s
+ start
+}
+
+start() {
+
+ config_load "frp"
+ local enabled server_addr server_port time privilege_token tcp_mux enable_cpool
+ local pool_count log_level log_max_days login_fail_exit http_proxy protocol
+
+ config_get_bool enabled common enabled 1
+
+ [ "$enabled" -gt 0 ] || return 1
+
+ config_get server_addr common server_addr
+ config_get server_port common server_port
+ config_get token common token
+ config_get enable_cpool common enable_cpool
+ config_get pool_count common pool_count
+ config_get log_level common log_level
+ config_get log_max_days common log_max_days
+ config_get http_proxy common http_proxy
+ config_get protocol common protocol
+ config_get time common time
+
+ mkdir -p /var/etc/frp
+ [ ! -f "$LOGFILE" ] && touch $LOGFILE
+
+ [ ! -f "/usr/bin/frpc" ] && download_binary
+ [ ! -f "/usr/bin/frpc" ] && logger -t Frp 'Download frpc failed, please retry.' && exit 0
+
+ local tmpconf="/var/etc/frp/frpc.conf"
+
+ echo "[common]" >$tmpconf
+ echo "server_addr=${server_addr}" >>$tmpconf
+ echo "server_port=${server_port}" >>$tmpconf
+ echo "token=${token}" >>$tmpconf
+ echo "log_level=${log_level}" >>$tmpconf
+ echo "log_max_days=${log_max_days}" >>$tmpconf
+ echo "protocol=${protocol}" >>$tmpconf
+ echo "log_file=$LOGFILE" >>$tmpconf
+ [ -n "$http_proxy" ] && echo "http_proxy=$http_proxy" >>$tmpconf
+ [ -n "$pool_count" ] && echo "pool_count=$pool_count" >>$tmpconf
+
+ config_load "frp"
+ frp_write_bool tcp_mux common 1
+ frp_write_bool login_fail_exit common 1
+ config_foreach conf_proxy_add proxy "$tmpconf"
+
+ [ "$(cat "$tmpconf" | grep -c "type=")" -gt 0 ] || (echo_date "frp服务启动失败,请首先添加服务列表!" && exit 0)
+ logger -t FRPC 'Starting frp service'
+ SERVICE_DAEMONIZE=1 \
+ service_start /usr/bin/frpc -c $tmpconf
+
+ [ "$time" -gt 0 ] && frp_add_cru $time
+ [ -z "$(pgrep /usr/bin/frpc)" ] && echo_date "frp服务启动失败,请检查服务端 “TCP多路复用(tcp_mux)”设置,确保与客户端完全一致!"
+
+ return 0
+}
+
+stop() {
+ frp_del_cru
+ if [ -n "`pidof frpc`" ]; then
+ logger -t FRPC 'Shutting down frp service'
+ service_stop /usr/bin/frpc
+ Reduce_Log $LOGFILE
+ fi
+ return 0
+}
diff --git a/package/lean/luci-app-frpc/root/etc/uci-defaults/luci-frp b/package/lean/luci-app-frpc/root/etc/uci-defaults/luci-frp
new file mode 100755
index 0000000000..46016e1c5e
--- /dev/null
+++ b/package/lean/luci-app-frpc/root/etc/uci-defaults/luci-frp
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@frp[-1]
+ add ucitrack frp
+ set ucitrack.@frp[-1].init=frp
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-guest-wifi/Makefile b/package/lean/luci-app-guest-wifi/Makefile
new file mode 100644
index 0000000000..814bac85d9
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2018 By-Ameng Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=luci-app-guest-wifi
+LUCI_DEPENDS:=
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-guest-wifi/luasrc/controller/guest-wifi.lua b/package/lean/luci-app-guest-wifi/luasrc/controller/guest-wifi.lua
new file mode 100644
index 0000000000..887512f6a6
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/luasrc/controller/guest-wifi.lua
@@ -0,0 +1,14 @@
+module("luci.controller.guest-wifi", package.seeall)
+
+function index()
+ require("luci.i18n")
+ luci.i18n.loadc("guest-wifi")
+ if not nixio.fs.access("/etc/config/guest-wifi") then
+ return
+ end
+
+ local page = entry({"admin", "network", "guest-wifi"}, cbi("guest-wifi"), translate("Guest-wifi"), 19)
+ page.i18n = "guest-wifi"
+ page.dependent = true
+
+end
diff --git a/package/lean/luci-app-guest-wifi/luasrc/model/cbi/guest-wifi.lua b/package/lean/luci-app-guest-wifi/luasrc/model/cbi/guest-wifi.lua
new file mode 100644
index 0000000000..c498af3ce0
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/luasrc/model/cbi/guest-wifi.lua
@@ -0,0 +1,66 @@
+
+require("luci.tools.webadmin")
+
+m = Map("guest-wifi", translate("Guest-wifi"))
+
+s = m:section(TypedSection, "guest-wifi", translate("Config"), translate("You can set guest wifi here. The wifi will be disconnected when enabling/disabling. When modifying the password, first disable the guest wifi, and then do the modification, save and apply. Finally check both Enable and Create, save and apply."))
+s.anonymous = true
+s.addremove = false
+
+enable = s:option(Flag, "enable", translate("Enable"), translate("Enable or disable guest wifi"))
+enable.default = false
+enable.optional = false
+enable.rmempty = false
+
+create = s:option(Flag, "create", translate("Create/Remove"), translate("Check to create guest wifi when enabled, or check to remove guest wifi when disabled."))
+create.default = false
+create.optional = false
+create.rmempty = false
+
+device = s:option(ListValue, "device", translate("Define device"), translate("Define device of guest wifi"))
+device:value("radio0", "radio0")
+device:value("radio1", "radio1")
+device:value("radio2", "radio2")
+device.default = "radio0"
+
+wifi_name = s:option(Value, "wifi_name", translate("Wifi name"), translate("Define the name of guest wifi"))
+wifi_name.default = "Guest-WiFi"
+wifi_name.rmempty = true
+
+interface_name = s:option(Value, "interface_name", translate("Interface name"), translate("Define the interface name of guest wifi"))
+interface_name.default = "guest"
+interface_name.rmempty = true
+
+interface_ip = s:option(Value, "interface_ip", translate("Interface IP address"), translate("Define IP address for guest wifi"))
+interface_ip.datatype = "ip4addr"
+interface_ip.default ="192.168.4.1"
+
+encryption = s:option(Value, "encryption", translate("Encryption"), translate("Define encryption of guest wifi"))
+encryption:value("psk", "WPA-PSK")
+encryption:value("psk2", "WPA2-PSK")
+encryption:value("none", "No Encryption")
+encryption.default = "psk2"
+encryption.widget = "select"
+
+passwd = s:option(Value, "passwd", translate("Password"), translate("Define the password of guest wifi"))
+passwd.password = true
+passwd.default = "guestnetwork"
+
+isolate = s:option(ListValue, "isolate", translate("Isolation"), translate("Enalbe or disable isolation"))
+isolate:value("1", translate("YES"))
+isolate:value("0", translate("NO"))
+
+start = s:option(Value, "start", translate("Start address"), translate("Lowest leased address as offset from the network address"))
+start.default = "50"
+start.rmempty = true
+
+limit = s:option(Value, "limit", translate("Client Limit"), translate("Maximum number of leased addresses"))
+limit.default = "200"
+limit.rmempty = true
+
+leasetime = s:option(Value, "leasetime", translate("DHCP lease time"), translate("Expiry time of leased addresses, minimum is 2 minutes (2m)"))
+leasetime.default = "1h"
+leasetime.rmempty = true
+
+return m
+
diff --git a/package/lean/luci-app-guest-wifi/po/zh-cn/guest-wifi.po b/package/lean/luci-app-guest-wifi/po/zh-cn/guest-wifi.po
new file mode 100644
index 0000000000..d5971e7ab1
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/po/zh-cn/guest-wifi.po
@@ -0,0 +1,90 @@
+msgid "guest-wifi"
+msgstr "访客网络"
+
+msgid "Guest-wifi"
+msgstr "访客网络"
+
+msgid "Config"
+msgstr "设置"
+
+msgid "You can set guest wifi here. The wifi will be disconnected when enabling/disabling. When modifying the password, first disable the guest wifi, and then do the modification, save and apply. Finally check both Enable and Create, save and apply."
+msgstr "在此可以设置访客网络。启用/禁用访客网络时wifi会断开。当修改访客名称密码时,先禁用访客网络并保存应用,然后修改后保存应用,最后同时勾选启用和创建并保存应用。"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Enable or disable guest wifi"
+msgstr "启用/禁用访客网络"
+
+msgid "Create/Remove"
+msgstr"创建/删除"
+
+msgid "Check to create guest wifi when enabled, or check to remove guest wifi when disabled."
+msgstr "与启用同时勾选,可创建和启用访客网络;启用不勾选时勾选,可删除访客网络。"
+
+msgid "Define device"
+msgstr "指定设备"
+
+msgid "Define device of guest wifi"
+msgstr "指定用于访客网络的设备"
+
+msgid "Wifi name"
+msgstr "无线名称"
+
+msgid "Define the name of guest wifi"
+msgstr "指定访客网络的无线名称"
+
+msgid "Interface name"
+msgstr "接口名称"
+
+msgid "Define the interface name of guest wifi"
+msgstr "设置访客网络的接口名称"
+
+msgid "Interface IP address"
+msgstr "接口IP地址"
+
+msgid "Define IP address for guest wifi"
+msgstr "指定访客网络的IP地址(不能与其他LAN的IP地址段相同)"
+
+msgid "Encryption"
+msgstr "加密"
+
+msgid "Define encryption of guest wifi"
+msgstr "设置访客网络的无线加密方式"
+
+msgid "Password"
+msgstr "密码"
+
+msgid "Define the password of guest wifi"
+msgstr "设备访客网络的无线密码"
+
+msgid "Isolation"
+msgstr "隔离"
+
+msgid "Enalbe or disable isolation"
+msgstr "开启或关闭与其它LAN网段的隔离"
+
+msgid "Start address"
+msgstr "起始地址"
+
+msgid "Lowest leased address as offset from the network address"
+msgstr "网络地址分配的起始地址"
+
+msgid "Client Limit"
+msgstr "客户端最大数目"
+
+msgid "Maximum number of leased addresses"
+msgstr "最大地址分配数量"
+
+msgid "DHCP lease time"
+msgstr "DHCP地址租用时间"
+
+msgid "Expiry time of leased addresses, minimum is 2 minutes (2m)"
+msgstr "租用地址的到期时间,最短2分钟(2m)"
+
+msgid "YES"
+msgstr "是"
+
+msgid "NO"
+msgstr "否"
+
diff --git a/package/lean/luci-app-guest-wifi/root/etc/config/guest-wifi b/package/lean/luci-app-guest-wifi/root/etc/config/guest-wifi
new file mode 100644
index 0000000000..d43f5f5b3c
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/root/etc/config/guest-wifi
@@ -0,0 +1,14 @@
+
+config guest-wifi
+ option enable '0'
+ option wifi_name 'Guest-WiFi'
+ option interface_name 'guest'
+ option encryption 'psk2'
+ option passwd 'guestnetwork'
+ option interface_ip '192.168.4.1'
+ option isolate '1'
+ option start '50'
+ option limit '200'
+ option leasetime '1h'
+ option device 'radio0'
+ option create '0'
diff --git a/package/lean/luci-app-guest-wifi/root/etc/init.d/guest-wifi b/package/lean/luci-app-guest-wifi/root/etc/init.d/guest-wifi
new file mode 100755
index 0000000000..18ad8b164b
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/root/etc/init.d/guest-wifi
@@ -0,0 +1,213 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2015
+# Must keep author's information if you use this file.
+
+START=50
+
+HISTORY_DIR="/etc/config/guestwifi"
+[ -e /etc/config/guestwifi ]||mkdir -p /etc/config/guestwifi
+
+enabled=$(uci get guest-wifi.@guest-wifi[0].enable)
+wifi_name=$(uci get guest-wifi.@guest-wifi[0].wifi_name)
+interface_name=$(uci get guest-wifi.@guest-wifi[0].interface_name)
+encryption=$(uci get guest-wifi.@guest-wifi[0].encryption)
+passwd=$(uci get guest-wifi.@guest-wifi[0].passwd)
+interface_ip=$(uci get guest-wifi.@guest-wifi[0].interface_ip)
+isolate=$(uci get guest-wifi.@guest-wifi[0].isolate)
+start=$(uci get guest-wifi.@guest-wifi[0].start)
+limit=$(uci get guest-wifi.@guest-wifi[0].limit)
+leasetime=$(uci get guest-wifi.@guest-wifi[0].leasetime)
+device=$(uci get guest-wifi.@guest-wifi[0].device)
+create=$(uci get guest-wifi.@guest-wifi[0].create)
+
+
+start() {
+ [ $enabled = 1 ] && {
+ [ $create = 1 ] && {
+ [ -f /etc/config/guestwifi/guest_del ] || echo "#! /bin/sh" > ${HISTORY_DIR}/guest_del
+ chmod 0755 ${HISTORY_DIR}/guest_del
+ add_interface
+ add_ssid
+ mod_dhcp
+ mod_fw
+ /etc/init.d/network restart
+ }
+ uci set guest-wifi.@guest-wifi[0].create='0'
+ uci commit guest-wifi
+ uci del wireless.$interface_name.disabled
+ uci commit wireless
+ wifi
+ }
+}
+
+
+stop() {
+ [ $enabled = 0 ] && {
+ [ $create = 1 ] && {
+ ${HISTORY_DIR}/guest_del
+ rule_c=`uci show firewall |grep "Hide My LAN for $wifi_name"|grep -o "[0-9]*[0-9]"`
+ uci del firewall.@rule[$rule_c]
+ uci commit firewall
+ rule_b=`uci show firewall |grep "Allow DHCP request for $wifi_name"|grep -o "[0-9]*[0-9]"`
+ uci del firewall.@rule[$rule_b]
+ uci commit firewall
+ rule_a=`uci show firewall |grep "Allow DNS Queries for $wifi_name"|grep -o "[0-9]*[0-9]"`
+ uci del firewall.@rule[$rule_a]
+ uci commit firewall
+ /etc/config/guestwifi/guest_del
+ rm -rf /etc/config/guestwifi/guest_del
+ /etc/init.d/network restart
+ }
+ uci set guest-wifi.@guest-wifi[0].create='0'
+ uci commit guest-wifi
+ uci set wireless.$interface_name.disabled='1'
+ uci commit wireless
+ wifi
+ }
+}
+
+restart() {
+ stop
+ sleep 2
+ start
+}
+
+add_interface() {
+ name=`uci show network |grep "$interface_ip"`
+ if [ $? = 1 ]; then
+ uci set network.$interface_name=interface
+ uci set network.$interface_name.proto='static'
+ uci set network.$interface_name.ipaddr="$interface_ip"
+ uci set network.$interface_name.netmask='255.255.255.0'
+ echo "uci del network.$interface_name" >> ${HISTORY_DIR}/guest_del
+ echo "uci commit network" >> ${HISTORY_DIR}/guest_del
+ uci commit network
+ fi
+}
+
+add_ssid() {
+ check_name=`uci show wireless |grep "$wifi_name"`
+ if [ $? = 1 ]; then
+ uci set wireless.$interface_name=wifi-iface
+ uci set wireless.$interface_name.device="$device"
+ uci set wireless.$interface_name.mode='ap'
+ uci set wireless.$interface_name.network="$interface_name"
+ uci set wireless.$interface_name.ssid="$wifi_name"
+ uci set wireless.$interface_name.encryption="$encryption"
+ uci set wireless.$interface_name.isolate="$isolate"
+ if [ "$encryption" != "none" ]; then
+ uci set wireless.$interface_name.key="$passwd"
+ fi
+ echo "uci del wireless.$interface_name" >> ${HISTORY_DIR}/guest_del
+ echo "uci commit wireless" >> ${HISTORY_DIR}/guest_del
+ uci commit wireless
+ fi
+}
+
+mod_dhcp() {
+ check_dhcp=`uci show dhcp |grep "$interface_name=dhcp"`
+ if [ $? = 1 ]; then
+ uci set dhcp.$interface_name=dhcp
+ uci set dhcp.$interface_name.interface="$interface_name"
+ uci set dhcp.$interface_name.start="$start"
+ uci set dhcp.$interface_name.limit="$limit"
+ uci set dhcp.$interface_name.leasetime="$leasetime"
+ echo "uci del dhcp.$interface_name" >> ${HISTORY_DIR}/guest_del
+ echo "uci commit dhcp" >> ${HISTORY_DIR}/guest_del
+ uci commit dhcp
+ fi
+}
+
+mod_fw() {
+ num_a=`uci show firewall |grep '=zone' |wc -l`
+ num_b=`uci show firewall |grep '=forwarding' |wc -l`
+
+ check_zone=`uci show firewall |grep "name=\'$interface_name\'"`
+ if [ $? = 1 ]; then
+ uci add firewall zone
+ echo "uci del firewall.@zone[$num_a]" >> ${HISTORY_DIR}/guest_del
+ echo "uci commit firewall" >> ${HISTORY_DIR}/guest_del
+ uci set firewall.@zone[$num_a]=zone
+ uci set firewall.@zone[$num_a].name="$interface_name"
+ uci set firewall.@zone[$num_a].network="$interface_name"
+ uci set firewall.@zone[$num_a].forward='REJECT'
+ uci set firewall.@zone[$num_a].output='ACCEPT'
+ uci set firewall.@zone[$num_a].input='REJECT'
+ uci commit firewall
+ fi
+
+ check_forward=`uci show firewall |grep "forwarding\[.*\].src=\'"$interface_name\'""`
+ if [ $? = 1 ]; then
+ uci add firewall forwarding
+ echo "uci del firewall.@forwarding[$num_b]" >> ${HISTORY_DIR}/guest_del
+ echo "uci commit firewall" >> ${HISTORY_DIR}/guest_del
+ uci set firewall.@forwarding[$num_b]=forwarding
+ uci set firewall.@forwarding[$num_b].src="$interface_name"
+ uci set firewall.@forwarding[$num_b].dest='wan'
+ uci commit firewall
+ fi
+
+ check_DNS=`uci show firewall |grep "Allow DNS Queries for $wifi_name"`
+ if [ $? = 1 ]; then
+ num_c=`uci show firewall |grep '=rule' |wc -l`
+ uci add firewall rule
+ uci set firewall.@rule[$num_c]=rule
+ uci set firewall.@rule[$num_c].name="Allow DNS Queries for $wifi_name"
+ uci set firewall.@rule[$num_c].src="$interface_name"
+ uci set firewall.@rule[$num_c].dest_port='53'
+ uci set firewall.@rule[$num_c].proto='tcpudp'
+ uci set firewall.@rule[$num_c].target='ACCEPT'
+ uci commit firewall
+ unset num_c
+ fi
+
+ check_DHCP=`uci show firewall |grep "Allow DHCP request for $wifi_name"`
+ if [ $? = 1 ]; then
+ num_c=`uci show firewall |grep '=rule' |wc -l`
+ uci add firewall rule
+ uci set firewall.@rule[$num_c]=rule
+ uci set firewall.@rule[$num_c].name="Allow DHCP request for $wifi_name"
+ uci set firewall.@rule[$num_c].src="$interface_name"
+ uci set firewall.@rule[$num_c].src_port='67-68'
+ uci set firewall.@rule[$num_c].dest_port='67-68'
+ uci set firewall.@rule[$num_c].proto='udp'
+ uci set firewall.@rule[$num_c].target='ACCEPT'
+ uci commit firewall
+ unset num_c
+ fi
+
+ check_HIDE=`uci show firewall |grep "Hide My LAN for $wifi_name"`
+ if [ $? = 1 ]; then
+ num_c=`uci show firewall |grep '=rule' |wc -l`
+ uci add firewall rule
+ uci set firewall.@rule[$num_c]=rule
+ uci set firewall.@rule[$num_c].enabled='1'
+ uci set firewall.@rule[$num_c].name="Hide My LAN for $wifi_name"
+ uci set firewall.@rule[$num_c].proto='all'
+ uci set firewall.@rule[$num_c].src="$interface_name"
+ #convert netmask to cidr
+ local lan_netmask=`uci get network.lan.netmask`
+ local nbits=0
+ local IFS=.
+ for netmask_dec in $lan_netmask ; do
+ case $netmask_dec in
+ 255) let nbits+=8 ;;
+ 254) let nbits+=7 ;;
+ 252) let nbits+=6 ;;
+ 248) let nbits+=5 ;;
+ 240) let nbits+=4 ;;
+ 224) let nbits+=3 ;;
+ 192) let nbits+=2 ;;
+ 128) let nbits+=1 ;;
+ 0) ;;
+ *) echo "Error: $netmask_dec can not be recognised as netmask decimal." && exit 1 ;;
+ esac
+ done
+ unset netmask_dec
+ uci set firewall.@rule[$num_c].dest_ip="`uci get network.lan.ipaddr`/$nbits"
+ uci set firewall.@rule[$num_c].target='REJECT'
+ uci commit firewall
+ unset num_c
+ fi
+}
+
diff --git a/package/lean/luci-app-guest-wifi/root/etc/uci-defaults/luci-app-guest-wifi b/package/lean/luci-app-guest-wifi/root/etc/uci-defaults/luci-app-guest-wifi
new file mode 100755
index 0000000000..02ad18b3fe
--- /dev/null
+++ b/package/lean/luci-app-guest-wifi/root/etc/uci-defaults/luci-app-guest-wifi
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@guest-wifi[-1]
+ add ucitrack guest-wifi
+ set ucitrack.@guest-wifi[-1].init=guest-wifi
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-haproxy-tcp/Makefile b/package/lean/luci-app-haproxy-tcp/Makefile
new file mode 100644
index 0000000000..0be02c1614
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/Makefile
@@ -0,0 +1,27 @@
+# Copyright (C) 2019 Openwrt.org
+#
+# This is a free software, use it under Apache Licene 2.0 & GNU General Public License v3.0.
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=luci for haproxy and shadowsocks
+LUCI_DEPENDS:=+haproxy
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-haproxy-tcp
+PKG_VERSION=1.4
+PKG_RELEASE:=2
+PKG_MAINTAINER:=Alex Zhuo <1886090@gmail.com>
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+define Package/$(PKG_NAME)/postinst
+#!/bin/sh
+rm -rf /tmp/luci*
+echo stopping haproxy
+/etc/init.d/haproxy stop
+/etc/init.d/haproxy disable
+echo haproxy disabled
+endef
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua b/package/lean/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua
new file mode 100644
index 0000000000..5549e754b9
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua
@@ -0,0 +1,7 @@
+module("luci.controller.haproxy", package.seeall)
+function index()
+ if not nixio.fs.access("/etc/config/haproxy") then
+ return
+ end
+ entry({"admin", "services", "haproxy"}, cbi("haproxy"), _("HAProxy")).dependent = true
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua b/package/lean/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua
new file mode 100644
index 0000000000..381b1951bc
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua
@@ -0,0 +1,67 @@
+--Alex<1886090@gmail.com>
+local fs = require "nixio.fs"
+
+function sync_value_to_file(value, file) --用来写入文件的函数,目前这种方式已经弃用
+ value = value:gsub("\r\n?", "\n")
+ local old_value = nixio.fs.readfile(file)
+ if value ~= old_value then
+ nixio.fs.writefile(file, value)
+ end
+end
+local state_msg = ""
+local haproxy_on = (luci.sys.call("pidof haproxy > /dev/null") == 0)
+local router_ip = luci.sys.exec("uci get network.lan.ipaddr")
+if haproxy_on then
+ state_msg = "" .. translate("Running") .. ""
+else
+ state_msg = "" .. translate("Not running") .. ""
+end
+m=Map("haproxy",translate("HAProxy"),translate("HAProxy能够检测Shadowsocks服务器的连通情况,从而实现负载均衡和高可用的功能,支持主备用服务器宕机自动切换,并且可以设置多个主服务器用于分流,规定每个分流节点的流量比例等。前提条件是你的所有Shadowsocks服务器的【加密方式】和【密码】一致。
使用方法:配置好你的Shadowsocks服务器ip地址和端口,然后开启Shadowsocks服务,将服务器地址填写为【127.0.0.1】,端口【2222】,其他参数和之前一样即可,你可以通过访问【路由器的IP:1111/haproxy】输入用户名admin,密码root来观察各节点健康状况,红色为宕机,绿色正常,使用说明请点击这里") .. "
后台监控页面:" .. router_ip .. ":1111/haproxy 用户名admin,密码root" .. "
状态 - " .. state_msg)
+s=m:section(TypedSection,"arguments","")
+ s.addremove=false
+ s.anonymous=true
+ view_enable = s:option(Flag,"enabled",translate("Enable"))
+ --通过读写配置文件控制HAProxy这种方式已经弃用
+ --view_cfg = s:option(TextValue, "1", nil)
+ --view_cfg.rmempty = false
+ --view_cfg.rows = 43
+
+ --function view_cfg.cfgvalue()
+ -- return nixio.fs.readfile("/etc/haproxy.cfg") or ""
+ --end
+ --function view_cfg.write(self, section, value)
+ -- sync_value_to_file(value, "/etc/haproxy.cfg")
+ --end
+s=m:section(TypedSection,"main_server","" .. translate("Main Server List") .. "")
+ s.anonymous=true
+ s.addremove=true
+ o=s:option(Value,"server_name",translate("Display Name"),translate("Only English Characters,No spaces"))
+ o.rmempty = false
+
+ o=s:option(Flag,"validate",translate("validate"))
+
+ o=s:option(Value,"server_ip",translate("Proxy Server IP"))
+ o.datatype="ip4addr"
+ o=s:option(Value,"server_port",translate("Proxy Server Port"))
+ o.datatype="uinteger"
+ o=s:option(Value,"server_weight",translate("Weight"))
+ o.datatype="uinteger"
+
+s=m:section(TypedSection,"backup_server","" .. translate("Backup Server List") .. "")
+ s.anonymous=true
+ s.addremove=true
+ o=s:option(Value,"server_name",translate("Display Name"),translate("Only English Characters,No spaces"))
+ o.rmempty = false
+
+ o=s:option(Flag,"validate",translate("validate"))
+
+ o=s:option(Value,"server_ip",translate("Proxy Server IP"))
+ o.datatype="ip4addr"
+ o=s:option(Value,"server_port",translate("Proxy Server Port"))
+ o.datatype="uinteger"
+-- ---------------------------------------------------
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ os.execute("/etc/haproxy_init.sh restart >/dev/null 2>&1 &")
+end
+return m
diff --git a/package/lean/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po b/package/lean/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po
new file mode 100644
index 0000000000..19a187d9d9
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po
@@ -0,0 +1,34 @@
+msgid "Running"
+msgstr "运行中"
+
+msgid "Not running"
+msgstr "未运行"
+
+msgid "Main Server List"
+msgstr "主服务器列表"
+
+msgid "Display Name"
+msgstr "服务器名称"
+
+msgid "Only English Characters,No spaces"
+msgstr "仅限英文字母,不要有空格"
+
+msgid "Proxy Server IP"
+msgstr "代理服务器IP"
+
+msgid "Proxy Server Port"
+msgstr "代理服务器端口"
+
+msgid "Weight"
+msgstr "分流权重"
+
+msgid "Backup Server List"
+msgstr "备用服务器列表"
+
+msgid "validate"
+msgstr "生效"
+
+
+
+
+
diff --git a/package/lean/luci-app-haproxy-tcp/root/etc/config/haproxy b/package/lean/luci-app-haproxy-tcp/root/etc/config/haproxy
new file mode 100644
index 0000000000..5d324ca3fd
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/root/etc/config/haproxy
@@ -0,0 +1,29 @@
+
+config arguments
+ option enabled '0'
+
+config main_server
+ option server_weight '10'
+ option server_ip '1.2.3.4'
+ option server_port '443'
+ option server_name 'JP1'
+ option validate '1'
+
+config backup_server
+ option server_name 'JP2'
+ option server_ip '2.2.2.2'
+ option server_port '8038'
+ option validate '1'
+
+config backup_server
+ option server_name 'JP3'
+ option server_ip '3.3.3.3'
+ option server_port '443'
+ option validate '1'
+
+config backup_server
+ option server_name 'JP4'
+ option server_ip '4.4.4.4'
+ option server_port '443'
+ option validate '1'
+
diff --git a/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_init.sh b/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_init.sh
new file mode 100755
index 0000000000..cab4d1f826
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_init.sh
@@ -0,0 +1,129 @@
+#!/bin/sh /etc/rc.common
+
+CFG_FILE=/etc/haproxy.cfg
+stop(){
+ logger -t alex stopping haproxy
+ echo "stopping haproxy"
+ /etc/init.d/haproxy disable
+ /etc/init.d/haproxy stop
+ [ -f /etc/haproxy_backup ] && {
+ cp /etc/haproxy_backup /etc/init.d/haproxy
+ }
+ iptables -t nat -D OUTPUT -j HAPROXY &> /dev/null
+ iptables -t nat -F HAPROXY &> /dev/null
+ sleep 1
+ iptables -t nat -X HAPROXY &> /dev/null
+}
+start(){
+ echo "starting haproxy"
+ logger -t restarting haproxy
+ echo global > $CFG_FILE
+ cat >> $CFG_FILE </dev/null`
+ local server_name=`uci get haproxy.@main_server[$COUNTER].server_name 2>/dev/null`
+ local server_port=`uci get haproxy.@main_server[$COUNTER].server_port 2>/dev/null`
+ local server_weight=`uci get haproxy.@main_server[$COUNTER].server_weight 2>/dev/null`
+ local validate=`uci get haproxy.@main_server[$COUNTER].validate 2>/dev/null`
+ if [ -z "$server_ip" ] || [ -z "$server_name" ] || [ -z "$server_port" ] || [ -z "$server_weight" ]; then
+ echo break
+ break
+ fi
+ echo the main server $COUNTER $server_ip $server_name $server_port $server_weight
+ [ "$validate" = 1 ] && {
+ echo server $server_name $server_ip:$server_port weight $server_weight maxconn 1024 check inter 1500 rise 3 fall 3 >> $CFG_FILE
+ }
+ iptables -t nat -A HAPROXY -p tcp -d $server_ip -j ACCEPT
+ COUNTER=$(($COUNTER+1))
+ done
+ COUNTER=0
+ #添加备用服务器
+ while true
+ do
+ local server_ip=`uci get haproxy.@backup_server[$COUNTER].server_ip 2>/dev/null`
+ local server_name=`uci get haproxy.@backup_server[$COUNTER].server_name 2>/dev/null`
+ local server_port=`uci get haproxy.@backup_server[$COUNTER].server_port 2>/dev/null`
+ local validate=`uci get haproxy.@backup_server[$COUNTER].validate 2>/dev/null`
+ if [ -z "$server_ip" ] || [ -z "$server_name" ] || [ -z "$server_port" ]; then
+ echo break
+ break
+ fi
+ echo the backup server $COUNTER $server_ip $server_name $server_port
+ [ "$validate" = 1 ] && {
+ echo server $server_name $server_ip:$server_port weight 10 check backup inter 1500 rise 3 fall 3 >> $CFG_FILE
+ }
+ iptables -t nat -A HAPROXY -p tcp -d $server_ip -j ACCEPT
+ COUNTER=$(($COUNTER+1))
+ done
+ iptables -t nat -I OUTPUT -j HAPROXY
+ /etc/init.d/haproxy enable
+ /etc/init.d/haproxy restart
+ cp /etc/init.d/haproxy /etc/haproxy_backup
+ cp /etc/haproxy_start /etc/init.d/haproxy
+}
+
+restart(){
+ echo luci for haproxy
+ sleep 1s
+ local vt_enabled=`uci get haproxy.@arguments[0].enabled 2>/dev/null`
+ logger -t haproxy is initializing enabled is $vt_enabled
+ echo $vt_enabled
+ if [ "$vt_enabled" = 1 ]; then
+ [ -f /etc/haproxy_backup ] && {
+ cp /etc/haproxy_backup /etc/init.d/haproxy
+ }
+ iptables -t nat -D OUTPUT -j HAPROXY &> /dev/null
+ iptables -t nat -F HAPROXY &> /dev/null
+ sleep 1
+ iptables -t nat -X HAPROXY &> /dev/null
+ start;
+ else
+ stop;
+ fi
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_start b/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_start
new file mode 100755
index 0000000000..f0c9e52b13
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/root/etc/haproxy_start
@@ -0,0 +1,73 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2009-2010 OpenWrt.org
+
+START=99
+STOP=80
+
+SERVICE_USE_PID=1
+
+HAPROXY_BIN="/usr/sbin/haproxy"
+HAPROXY_CONFIG="/etc/haproxy.cfg"
+HAPROXY_PID="/var/run/haproxy.pid"
+
+start() {
+ service_start $HAPROXY_BIN -q -D -f "$HAPROXY_CONFIG" -p "$HAPROXY_PID"
+ local COUNTER=0
+ #添加主服务器
+ iptables -t nat -D OUTPUT -j HAPROXY &> /dev/null
+ iptables -t nat -X HAPROXY
+ iptables -t nat -N HAPROXY
+ iptables -t nat -F HAPROXY
+
+ while true
+ do
+ local server_ip=`uci get haproxy.@main_server[$COUNTER].server_ip 2>/dev/null`
+ local server_name=`uci get haproxy.@main_server[$COUNTER].server_name 2>/dev/null`
+ local server_port=`uci get haproxy.@main_server[$COUNTER].server_port 2>/dev/null`
+ local server_weight=`uci get haproxy.@main_server[$COUNTER].server_weight 2>/dev/null`
+ local validate=`uci get haproxy.@main_server[$COUNTER].validate 2>/dev/null`
+ if [ -z "$server_ip" ] || [ -z "$server_name" ] || [ -z "$server_port" ] || [ -z "$server_weight" ]; then
+ echo break
+ break
+ fi
+ echo the main2 server $COUNTER $server_ip $server_name $server_port $server_weight
+ [ "$validate" = 1 ] && {
+ iptables -t nat -A HAPROXY -p tcp -d $server_ip -j ACCEPT
+ }
+ COUNTER=$(($COUNTER+1))
+ done
+ COUNTER=0
+ #添加备用服务器
+ while true
+ do
+ local server_ip=`uci get haproxy.@backup_server[$COUNTER].server_ip 2>/dev/null`
+ local server_name=`uci get haproxy.@backup_server[$COUNTER].server_name 2>/dev/null`
+ local server_port=`uci get haproxy.@backup_server[$COUNTER].server_port 2>/dev/null`
+ local validate=`uci get haproxy.@backup_server[$COUNTER].validate 2>/dev/null`
+ if [ -z "$server_ip" ] || [ -z "$server_name" ] || [ -z "$server_port" ]; then
+ echo break
+ break
+ fi
+ echo the backup2 server $COUNTER $server_ip $server_name $server_port
+ [ "$validate" = 1 ] && {
+ iptables -t nat -A HAPROXY -p tcp -d $server_ip -j ACCEPT
+ }
+ COUNTER=$(($COUNTER+1))
+ done
+
+ iptables -t nat -I OUTPUT -j HAPROXY
+}
+
+stop() {
+ kill -9 $(cat $HAPROXY_PID | tr "\n" " ")
+ service_stop $HAPROXY_BIN
+ iptables -t nat -D OUTPUT -j HAPROXY &> /dev/null
+ iptables -t nat -F HAPROXY &> /dev/null
+ sleep 1
+ iptables -t nat -X HAPROXY &> /dev/null
+}
+
+reload() {
+ $HAPROXY_BIN -D -q -f $HAPROXY_CONFIG -p $HAPROXY_PID -sf $(cat $HAPROXY_PID | tr "\n" " ")
+ #$HAPROXY_BIN -D -q -f $HAPROXY_CONFIG -p $HAPROXY_PID -sf $(cat $HAPROXY_PID)
+}
diff --git a/package/lean/luci-app-haproxy-tcp/root/etc/uci-defaults/z99-haproxy b/package/lean/luci-app-haproxy-tcp/root/etc/uci-defaults/z99-haproxy
new file mode 100755
index 0000000000..72913f935b
--- /dev/null
+++ b/package/lean/luci-app-haproxy-tcp/root/etc/uci-defaults/z99-haproxy
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+/etc/init.d/haproxy disable
+/etc/init.d/haproxy stop
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-ipsec-vpnd/Makefile b/package/lean/luci-app-ipsec-vpnd/Makefile
new file mode 100644
index 0000000000..b7a83920f5
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for IPSec VPN Server (IKEv1 with PSK and Xauth)
+LUCI_DEPENDS:=+strongswan-minimal +strongswan-mod-xauth-generic
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=7
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-ipsec-vpnd/luasrc/controller/ipsec-server.lua b/package/lean/luci-app-ipsec-vpnd/luasrc/controller/ipsec-server.lua
new file mode 100644
index 0000000000..2925194c62
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/luasrc/controller/ipsec-server.lua
@@ -0,0 +1,19 @@
+
+module("luci.controller.ipsec-server", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/ipsec") then
+ return
+ end
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin", "vpn", "ipsec-server"}, cbi("ipsec-server/ipsec-server"), _("IPSec VPN Server"), 80).dependent=false
+ entry({"admin", "vpn", "ipsec-server","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep ipsec >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-ipsec-vpnd/luasrc/model/cbi/ipsec-server/ipsec-server.lua b/package/lean/luci-app-ipsec-vpnd/luasrc/model/cbi/ipsec-server/ipsec-server.lua
new file mode 100644
index 0000000000..79a47b8bfb
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/luasrc/model/cbi/ipsec-server/ipsec-server.lua
@@ -0,0 +1,35 @@
+
+mp = Map("ipsec", translate("IPSec VPN Server"))
+mp.description = translate("IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)")
+
+mp:section(SimpleSection).template = "ipsec/ipsec_status"
+
+s = mp:section(NamedSection, "ipsec", "service")
+s.anonymouse = true
+
+enabled = s:option(Flag, "enabled", translate("Enable"))
+enabled.default = 0
+enabled.rmempty = false
+
+clientip = s:option(Value, "clientip", translate("VPN Client IP"))
+clientip.datatype = "ip4addr"
+clientip.description = translate("LAN DHCP reserved started IP addresses with the same subnet mask")
+clientip.optional = false
+clientip.rmempty = false
+
+clientdns = s:option(Value, "clientdns", translate("VPN Client DNS"))
+clientdns.datatype = "ip4addr"
+clientdns.description = translate("DNS using in VPN tunnel.Set to the router's LAN IP is recommended")
+clientdns.optional = false
+clientdns.rmempty = false
+
+account = s:option(Value, "account", translate("Account"))
+account.datatype = "string"
+
+password = s:option(Value, "password", translate("Password"))
+password.password = true
+
+secret = s:option(Value, "secret", translate("Secret Pre-Shared Key"))
+secret.password = true
+
+return mp
diff --git a/package/lean/luci-app-ipsec-vpnd/luasrc/view/ipsec/ipsec_status.htm b/package/lean/luci-app-ipsec-vpnd/luasrc/view/ipsec/ipsec_status.htm
new file mode 100644
index 0000000000..60225b4902
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/luasrc/view/ipsec/ipsec_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-ipsec-vpnd/po/zh-cn/ipsec.po b/package/lean/luci-app-ipsec-vpnd/po/zh-cn/ipsec.po
new file mode 100644
index 0000000000..0bf6b62aaa
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/po/zh-cn/ipsec.po
@@ -0,0 +1,34 @@
+msgid "IPSec VPN Server"
+msgstr "IPSec VPN 服务器"
+
+msgid "IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)"
+msgstr "使用iOS 或者 Andriod (IKEv1 with PSK and Xauth) 原生内置 IPSec VPN 客户端进行连接"
+
+msgid "VPN Client IP"
+msgstr "VPN客户端地址段"
+
+msgid "LAN DHCP reserved started IP addresses with the same subnet mask"
+msgstr "VPN客户端使用DHCP保留空余IP的起始地址,必须和路由器LAN同一个子网掩码,例如 192.168.0.10/24"
+
+msgid "VPN Client DNS"
+msgstr "VPN客户端DNS服务器"
+
+msgid "DNS using in VPN tunnel.Set to the router's LAN IP is recommended"
+msgstr "指定VPN客户端的DNS地址。推荐设置为路由器的LAN IP,例如 192.168.0.1"
+
+msgid "Account"
+msgstr "账户"
+
+msgid "Secret Pre-Shared Key"
+msgstr "PSK密钥"
+
+msgid "IPSec VPN Server status"
+msgstr "IPSec VPN 服务器运行状态"
+
+msgid "Disable from startup"
+msgstr "禁止开机启动"
+
+msgid "Enable on startup"
+msgstr "允许开机启动"
+
+
diff --git a/package/lean/luci-app-ipsec-vpnd/root/etc/config/ipsec b/package/lean/luci-app-ipsec-vpnd/root/etc/config/ipsec
new file mode 100644
index 0000000000..93c21f2785
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/root/etc/config/ipsec
@@ -0,0 +1,9 @@
+
+config service 'ipsec'
+ option clientdns '192.168.0.1'
+ option account 'lean'
+ option secret 'myopenwrt'
+ option enabled '0'
+ option password '12345678'
+ option clientip '192.168.0.10/24'
+
diff --git a/package/lean/luci-app-ipsec-vpnd/root/etc/init.d/ipsec b/package/lean/luci-app-ipsec-vpnd/root/etc/init.d/ipsec
new file mode 100755
index 0000000000..5a4c6a2170
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/root/etc/init.d/ipsec
@@ -0,0 +1,427 @@
+#!/bin/sh /etc/rc.common
+
+START=90
+STOP=10
+
+USE_PROCD=1
+PROG=/usr/lib/ipsec/starter
+
+. $IPKG_INSTROOT/lib/functions.sh
+. $IPKG_INSTROOT/lib/functions/network.sh
+
+IPSEC_SECRETS_FILE=/etc/ipsec.secrets
+IPSEC_CONN_FILE=/etc/ipsec.conf
+STRONGSWAN_CONF_FILE=/etc/strongswan.conf
+
+IPSEC_VAR_SECRETS_FILE=/var/ipsec/ipsec.secrets
+IPSEC_VAR_CONN_FILE=/var/ipsec/ipsec.conf
+STRONGSWAN_VAR_CONF_FILE=/var/ipsec/strongswan.conf
+
+WAIT_FOR_INTF=0
+
+file_reset() {
+ : > "$1"
+}
+
+xappend() {
+ local file="$1"
+ shift
+
+ echo "${@}" >> "${file}"
+}
+
+remove_include() {
+ local file="$1"
+ local include="$2"
+
+ sed -i "\_${include}_d" "${file}"
+}
+
+remove_includes() {
+ remove_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
+ remove_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
+ remove_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
+}
+
+do_include() {
+ local conf="$1"
+ local uciconf="$2"
+ local backup=`mktemp -t -p /tmp/ ipsec-init-XXXXXX`
+
+ [ ! -f "${conf}" ] && rm -rf "${conf}"
+ touch "${conf}"
+
+ cat "${conf}" | grep -v "${uciconf}" > "${backup}"
+ mv "${backup}" "${conf}"
+ xappend "${conf}" "include ${uciconf}"
+ file_reset "${uciconf}"
+}
+
+ipsec_reset() {
+ do_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
+}
+
+ipsec_xappend() {
+ xappend "${IPSEC_VAR_CONN_FILE}" "$@"
+}
+
+swan_reset() {
+ do_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
+}
+
+swan_xappend() {
+ xappend "${STRONGSWAN_VAR_CONF_FILE}" "$@"
+}
+
+secret_reset() {
+ do_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
+}
+
+secret_xappend() {
+ xappend "${IPSEC_VAR_SECRETS_FILE}" "$@"
+}
+
+warning() {
+ echo "WARNING: $@" >&2
+}
+
+add_crypto_proposal() {
+ local encryption_algorithm
+ local hash_algorithm
+ local dh_group
+
+ config_get encryption_algorithm "$1" encryption_algorithm
+ config_get hash_algorithm "$1" hash_algorithm
+ config_get dh_group "$1" dh_group
+
+ [ -n "${encryption_algorithm}" ] && \
+ crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${dh_group:+-${dh_group}}"
+}
+
+set_crypto_proposal() {
+ local conf="$1"
+ local proposal
+
+ crypto=""
+
+ config_get crypto_proposal "$conf" crypto_proposal ""
+ for proposal in $crypto_proposal; do
+ add_crypto_proposal "$proposal"
+ done
+
+ [ -n "${crypto}" ] && {
+ local force_crypto_proposal
+
+ config_get_bool force_crypto_proposal "$conf" force_crypto_proposal
+
+ [ "${force_crypto_proposal}" = "1" ] && crypto="${crypto}!"
+ }
+
+ crypto_proposal="${crypto}"
+}
+
+config_conn() {
+ # Generic ipsec conn section shared by tunnel and transport
+ local mode
+ local local_subnet
+ local local_nat
+ local local_sourceip
+ local local_updown
+ local local_firewall
+ local remote_subnet
+ local remote_sourceip
+ local remote_updown
+ local remote_firewall
+ local ikelifetime
+ local lifetime
+ local margintime
+ local keyingtries
+ local dpdaction
+ local dpddelay
+ local inactivity
+ local keyexchange
+
+ config_get mode "$1" mode "route"
+ config_get local_subnet "$1" local_subnet ""
+ config_get local_nat "$1" local_nat ""
+ config_get local_sourceip "$1" local_sourceip ""
+ config_get local_updown "$1" local_updown ""
+ config_get local_firewall "$1" local_firewall ""
+ config_get remote_subnet "$1" remote_subnet ""
+ config_get remote_sourceip "$1" remote_sourceip ""
+ config_get remote_updown "$1" remote_updown ""
+ config_get remote_firewall "$1" remote_firewall ""
+ config_get ikelifetime "$1" ikelifetime "3h"
+ config_get lifetime "$1" lifetime "1h"
+ config_get margintime "$1" margintime "9m"
+ config_get keyingtries "$1" keyingtries "3"
+ config_get dpdaction "$1" dpdaction "none"
+ config_get dpddelay "$1" dpddelay "30s"
+ config_get inactivity "$1" inactivity
+ config_get keyexchange "$1" keyexchange "ikev2"
+
+ [ -n "$local_nat" ] && local_subnet=$local_nat
+
+ ipsec_xappend "conn $config_name-$1"
+ ipsec_xappend " left=%any"
+ ipsec_xappend " right=$remote_gateway"
+
+ [ -n "$local_sourceip" ] && ipsec_xappend " leftsourceip=$local_sourceip"
+ [ -n "$local_subnet" ] && ipsec_xappend " leftsubnet=$local_subnet"
+
+ [ -n "$local_firewall" ] && ipsec_xappend " leftfirewall=$local_firewall"
+ [ -n "$remote_firewall" ] && ipsec_xappend " rightfirewall=$remote_firewall"
+
+ ipsec_xappend " ikelifetime=$ikelifetime"
+ ipsec_xappend " lifetime=$lifetime"
+ ipsec_xappend " margintime=$margintime"
+ ipsec_xappend " keyingtries=$keyingtries"
+ ipsec_xappend " dpdaction=$dpdaction"
+ ipsec_xappend " dpddelay=$dpddelay"
+
+ [ -n "$inactivity" ] && ipsec_xappend " inactivity=$inactivity"
+
+ if [ "$auth_method" = "psk" ]; then
+ ipsec_xappend " leftauth=psk"
+ ipsec_xappend " rightauth=psk"
+
+ [ "$remote_sourceip" != "" ] && ipsec_xappend " rightsourceip=$remote_sourceip"
+ [ "$remote_subnet" != "" ] && ipsec_xappend " rightsubnet=$remote_subnet"
+
+ ipsec_xappend " auto=$mode"
+ else
+ warning "AuthenticationMethod $auth_method not supported"
+ fi
+
+ [ -n "$local_identifier" ] && ipsec_xappend " leftid=$local_identifier"
+ [ -n "$remote_identifier" ] && ipsec_xappend " rightid=$remote_identifier"
+ [ -n "$local_updown" ] && ipsec_xappend " leftupdown=$local_updown"
+ [ -n "$remote_updown" ] && ipsec_xappend " rightupdown=$remote_updown"
+ ipsec_xappend " keyexchange=$keyexchange"
+
+ set_crypto_proposal "$1"
+ [ -n "${crypto_proposal}" ] && ipsec_xappend " esp=$crypto_proposal"
+ [ -n "${ike_proposal}" ] && ipsec_xappend " ike=$ike_proposal"
+}
+
+config_tunnel() {
+ config_conn "$1"
+
+ # Specific for the tunnel part
+ ipsec_xappend " type=tunnel"
+}
+
+config_transport() {
+ config_conn "$1"
+
+ # Specific for the transport part
+ ipsec_xappend " type=transport"
+}
+
+config_remote() {
+ local enabled
+ local gateway
+ local pre_shared_key
+ local auth_method
+
+ config_name=$1
+
+ config_get_bool enabled "$1" enabled 0
+ [ $enabled -eq 0 ] && return
+
+ config_get gateway "$1" gateway
+ config_get pre_shared_key "$1" pre_shared_key
+ config_get auth_method "$1" authentication_method
+ config_get local_identifier "$1" local_identifier ""
+ config_get remote_identifier "$1" remote_identifier ""
+
+ [ "$gateway" = "any" ] && remote_gateway="%any" || remote_gateway="$gateway"
+
+ [ -z "$local_identifier" ] && {
+ local ipdest
+
+ [ "$remote_gateway" = "%any" ] && ipdest="1.1.1.1" || ipdest="$remote_gateway"
+ local_gateway=`ip route get $ipdest | awk -F"src" '/src/{gsub(/ /,"");print $2}'`
+ }
+
+ [ -n "$local_identifier" ] && secret_xappend -n "$local_identifier " || secret_xappend -n "$local_gateway "
+ [ -n "$remote_identifier" ] && secret_xappend -n "$remote_identifier " || secret_xappend -n "$remote_gateway "
+
+ secret_xappend ": PSK \"$pre_shared_key\""
+
+ set_crypto_proposal "$1"
+ ike_proposal="$crypto_proposal"
+
+ config_list_foreach "$1" tunnel config_tunnel
+
+ config_list_foreach "$1" transport config_transport
+
+ ipsec_xappend ""
+}
+
+config_ipsec() {
+ local debug
+ local rtinstall_enabled
+ local routing_tables_ignored
+ local routing_table
+ local routing_table_id
+ local interface
+ local device_list
+
+ ipsec_reset
+ secret_reset
+ swan_reset
+
+ ipsec_xappend "# generated by /etc/init.d/ipsec"
+ ipsec_xappend "version 2"
+ ipsec_xappend ""
+
+ secret_xappend "# generated by /etc/init.d/ipsec"
+
+ config_get debug "$1" debug 0
+ config_get_bool rtinstall_enabled "$1" rtinstall_enabled 1
+ [ $rtinstall_enabled -eq 1 ] && install_routes=yes || install_routes=no
+
+ # prepare extra charon config option ignore_routing_tables
+ for routing_table in $(config_get "$1" "ignore_routing_tables"); do
+ if [ "$routing_table" -ge 0 ] 2>/dev/null; then
+ routing_table_id=$routing_table
+ else
+ routing_table_id=$(sed -n '/[ \t]*[0-9]\+[ \t]\+'$routing_table'[ \t]*$/s/[ \t]*\([0-9]\+\).*/\1/p' /etc/iproute2/rt_tables)
+ fi
+
+ [ -n "$routing_table_id" ] && append routing_tables_ignored "$routing_table_id"
+ done
+
+ local interface_list=$(config_get "$1" "interface")
+ if [ -z "$interface_list" ]; then
+ WAIT_FOR_INTF=0
+ else
+ for interface in $interface_list; do
+ network_get_device device $interface
+ [ -n "$device" ] && append device_list "$device" ","
+ done
+ [ -n "$device_list" ] && WAIT_FOR_INTF=0 || WAIT_FOR_INTF=1
+ fi
+
+ swan_xappend "# generated by /etc/init.d/ipsec"
+ swan_xappend "charon {"
+ swan_xappend " load_modular = yes"
+ swan_xappend " install_routes = $install_routes"
+ [ -n "$routing_tables_ignored" ] && swan_xappend " ignore_routing_tables = $routing_tables_ignored"
+ [ -n "$device_list" ] && swan_xappend " interfaces_use = $device_list"
+ swan_xappend " plugins {"
+ swan_xappend " include /etc/strongswan.d/charon/*.conf"
+ swan_xappend " }"
+ swan_xappend " syslog {"
+ swan_xappend " identifier = ipsec"
+ swan_xappend " daemon {"
+ swan_xappend " default = $debug"
+ swan_xappend " }"
+ swan_xappend " auth {"
+ swan_xappend " default = $debug"
+ swan_xappend " }"
+ swan_xappend " }"
+ swan_xappend "}"
+}
+
+prepare_env() {
+ mkdir -p /var/ipsec
+ remove_includes
+ config_load ipsec
+ config_foreach config_ipsec ipsec
+ config_foreach config_remote remote
+}
+
+service_running() {
+ ipsec status > /dev/null 2>&1
+}
+
+reload_service() {
+ local bool vt_enabled=`uci get ipsec.@service[0].enabled 2>/dev/null`
+ [ "$vt_enabled" = 0 ] && /etc/init.d/ipsec stop && return
+ running && {
+ prepare_env
+ [ $WAIT_FOR_INTF -eq 0 ] && {
+ ipsec rereadall
+ ipsec reload
+ return
+ }
+ }
+ [ "$vt_enabled" = 1 ] && start
+}
+
+check_ipsec_interface() {
+ local intf
+
+ for intf in $(config_get "$1" interface); do
+ procd_add_interface_trigger "interface.*" "$intf" /etc/init.d/ipsec reload
+ done
+}
+
+service_triggers() {
+ procd_add_reload_trigger "ipsec"
+ config load "ipsec"
+ config_foreach check_ipsec_interface ipsec
+}
+
+start_service() {
+ local vt_enabled=`uci get ipsec.@service[0].enabled 2>/dev/null`
+ local vt_clientip=`uci get ipsec.@service[0].clientip`
+ local vt_clientdns=`uci get ipsec.@service[0].clientdns`
+ local vt_account=`uci get ipsec.@service[0].account`
+ local vt_password=`uci get ipsec.@service[0].password 2>/dev/null`
+ local vt_secret=`uci get ipsec.@service[0].secret 2>/dev/null`
+
+ [ "$vt_enabled" = 0 ] && /etc/init.d/ipsec stop && return
+
+ cat > /etc/ipsec.conf < /etc/ipsec.secrets </dev/null
+iptables -D FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT 2>/dev/null
+iptables -D INPUT -m policy --dir in --pol ipsec --proto esp -j ACCEPT 2>/dev/null
+iptables -D OUTPUT -m policy --dir out --pol ipsec --proto esp -j ACCEPT 2>/dev/null
+
+iptables -I FORWARD -m policy --dir in --pol ipsec --proto esp -j ACCEPT
+iptables -I FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT
+iptables -I INPUT -m policy --dir in --pol ipsec --proto esp -j ACCEPT
+iptables -I OUTPUT -m policy --dir out --pol ipsec --proto esp -j ACCEPT
+
+echo 1 > /proc/sys/net/ipv4/conf/br-lan/proxy_arp
diff --git a/package/lean/luci-app-ipsec-vpnd/root/etc/uci-defaults/ipsec b/package/lean/luci-app-ipsec-vpnd/root/etc/uci-defaults/ipsec
new file mode 100755
index 0000000000..b9eb66fb81
--- /dev/null
+++ b/package/lean/luci-app-ipsec-vpnd/root/etc/uci-defaults/ipsec
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.ipsecd
+ set firewall.ipsecd=include
+ set firewall.ipsecd.type=script
+ set firewall.ipsecd.path=/etc/ipsec.include
+ set firewall.ipsecd.reload=1
+ commit firewall
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.ike
+ add firewall rule
+ rename firewall.@rule[-1]="ike"
+ set firewall.@rule[-1].name="ike"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="udp"
+ set firewall.@rule[-1].dest_port="500"
+ delete firewall.ipsec
+ add firewall rule
+ rename firewall.@rule[-1]="ipsec"
+ set firewall.@rule[-1].name="ipsec"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="udp"
+ set firewall.@rule[-1].dest_port="4500"
+ delete firewall.ah
+ add firewall rule
+ rename firewall.@rule[-1]="ah"
+ set firewall.@rule[-1].name="ah"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="ah"
+ delete firewall.esp
+ add firewall rule
+ rename firewall.@rule[-1]="esp"
+ set firewall.@rule[-1].name="esp"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="esp"
+ commit firewall
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@ipsec[-1]
+ add ucitrack ipsec
+ set ucitrack.@ipsec[-1].init=ipsec
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-mwan3helper/Makefile b/package/lean/luci-app-mwan3helper/Makefile
new file mode 100644
index 0000000000..38dedff054
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for MWAN3 Helper
+LUCI_DEPENDS:=+luci-app-mwan3 +pdnsd-alt +dnsmasq-full +ipset
+LUCI_PKGARCH:=all
+PKG_VERSION:=1
+PKG_RELEASE:=3
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua b/package/lean/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua
new file mode 100644
index 0000000000..f2068d2284
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua
@@ -0,0 +1,27 @@
+
+-- Licensed to the public under the GNU General Public License v3.
+
+module("luci.controller.mwan3helper", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/mwan3helper") then
+ return
+ end
+
+
+ entry({"admin", "services", "mwan3helper"},alias("admin", "services", "mwan3helper", "client"),_("MWAN3 Helper"), 300).dependent = true
+
+ entry({"admin", "services", "mwan3helper", "client"},cbi("mwan3helper/client"),_("Settings"), 10).leaf = true
+
+ entry({"admin", "services", "mwan3helper", "lists"},cbi("mwan3helper/list"),_("IPSet Lists"), 20).leaf = true
+
+ entry({"admin","services","mwan3helper","status"},call("act_status")).leaf=true
+
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep mwan3dns >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua b/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua
new file mode 100644
index 0000000000..466b28c77e
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua
@@ -0,0 +1,30 @@
+
+
+m = Map("mwan3helper")
+m.title = translate("MWAN3 Helper")
+m.description = translate("MWAN3 Helper generate IPSets")
+
+m:section(SimpleSection).template = "mwan3helper/mwan3helper_status"
+
+s = m:section(TypedSection, "mwan3helper")
+s.addremove = false
+s.anonymous = true
+
+o = s:option(Flag, "enabled", translate("Enable GFW IPSet"))
+o.rmempty = false
+o.description = translate("启用 DNS 防污染并打开GFW IPSet(与SSR的GFW列表模式不能同时开启)")
+
+o = s:option(ListValue, "dns", translate("Anti-pollution DNS Server"))
+o:value("208.67.222.222:443", translate("OpenDNS (208.67.222.222)"))
+o:value("208.67.220.220:443", translate("OpenDNS (208.67.220.220)"))
+o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)"))
+o:value("8.8.8.8:53", translate("Google Public DNS (8.8.8.8)"))
+o:value("209.244.0.3:53", translate("Level 3 Public DNS (209.244.0.3)"))
+o:value("209.244.0.4:53", translate("Level 3 Public DNS (209.244.0.4)"))
+o:value("4.2.2.1:53", translate("Level 3 Public DNS (4.2.2.1)"))
+o:value("4.2.2.2:53", translate("Level 3 Public DNS (4.2.2.2)"))
+o:value("4.2.2.3:53", translate("Level 3 Public DNS (4.2.2.3)"))
+o:value("4.2.2.4:53", translate("Level 3 Public DNS (4.2.2.4)"))
+o:value("1.1.1.1:53", translate("Cloudflare DNS (1.1.1.1)"))
+
+return m
diff --git a/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua b/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua
new file mode 100644
index 0000000000..c908ff19ae
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua
@@ -0,0 +1,37 @@
+
+
+m = Map("mwan3helper")
+m.title = translate("MWAN3 IPSets")
+
+s = m:section(TypedSection, "mwan3helper")
+s.addremove = false
+s.anonymous = true
+
+o = s:option(DummyValue, "gfwlist", translate("GFW列表"))
+o.description = translate("GFW列表数据: 5207 条,IPSET 名称为 gfwlist")
+
+o = s:option(DummyValue, "all_cn", translate("中国所有IP地址"))
+o.description = translate("中国所有IP地址路由表数据: 8304 条, IPSET 名称为 cn")
+
+o = s:option(DummyValue, "chinatelecom", translate("中国电信"))
+o.description = translate("中国电信路由表数据: 1948 条, IPSET 名称为 ct")
+
+o = s:option(DummyValue, "unicom_cnc", translate("中国联通/网通"))
+o.description = translate("中国联通/网通路由表数据: 895 条, IPSET 名称为 cnc")
+
+o = s:option(DummyValue, "cmcc", translate("中国移动"))
+o.description = translate("中国移动路由表数据: 55 条, IPSET 名称为 cmcc")
+
+o = s:option(DummyValue, "crtc", translate("中国铁通"))
+o.description = translate("中国铁通路由表数据: 16 条, IPSET 名称为 crtc")
+
+o = s:option(DummyValue, "cernet", translate("中国教育网"))
+o.description = translate("中国教育网路由表数据: 162 条, IPSET 名称为 cernet")
+
+o = s:option(DummyValue, "gwbn", translate("长城宽带/鹏博士"))
+o.description = translate("长城宽带/鹏博士: 94 条, IPSET 名称为 gwbn")
+
+o = s:option(DummyValue, "othernet", translate("中国其他ISP"))
+o.description = translate("中国其他ISP: 5134 条, IPSET 名称为 othernet")
+
+return m
diff --git a/package/lean/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm b/package/lean/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm
new file mode 100644
index 0000000000..44c6a39363
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-mwan3helper/po/zh-cn/mwan3helper.po b/package/lean/luci-app-mwan3helper/po/zh-cn/mwan3helper.po
new file mode 100644
index 0000000000..ff41800367
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/po/zh-cn/mwan3helper.po
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgid "MWAN3 Helper"
+msgstr "MWAN3 分流助手"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "IPSet Lists"
+msgstr "IPSet列表"
+
+msgid "MWAN3 Helper generate IPSets"
+msgstr "自动生成中国地址段和各大ISP运营商的 IPSet 地址段,并解决国外域名的污染问题,帮助MWAN3分流 ISP/WireGuard 等"
+
+msgid "Enable GFW IPSet"
+msgstr "启用 GFW IPSet"
\ No newline at end of file
diff --git a/package/lean/luci-app-mwan3helper/root/etc/config/mwan3helper b/package/lean/luci-app-mwan3helper/root/etc/config/mwan3helper
new file mode 100644
index 0000000000..f47fff8c1b
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/config/mwan3helper
@@ -0,0 +1,5 @@
+
+config mwan3helper 'config'
+ option dns '208.67.222.222:443'
+ option enabled '0'
+
diff --git a/package/lean/luci-app-mwan3helper/root/etc/init.d/mwan3helper b/package/lean/luci-app-mwan3helper/root/etc/init.d/mwan3helper
new file mode 100755
index 0000000000..6247ec2308
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/init.d/mwan3helper
@@ -0,0 +1,169 @@
+#!/bin/sh /etc/rc.common
+# Copyright (c) 2011-2015 OpenWrt.org
+
+START=60
+
+DNSMASQ_RESTART=N
+DNS_SERVER="208.67.222.222"
+
+start_pdnsd() {
+# DNS_SERVER=$(uci get flowoffload.@flow[0].dns_server 2>/dev/null)
+
+ [ -d /var/etc ] || mkdir -p /var/etc
+
+ if [ ! -f /var/mwan3dns/pdnsd.cache ]; then
+ mkdir -p /var/mwan3dns
+ echo -ne "pd13\000\000\000\000" > /var/mwan3dns/pdnsd.cache
+ chown -R nobody.nogroup /var/mwan3dns
+ fi
+
+ cat > /var/etc/mwan3dns.conf </dev/null 2>&1 || kill -9 $(ps | grep mwan3dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+ echo "Stop MWAN3 Helper DNS"
+}
+
+
+start(){
+ /etc/mwan3helper/genipset.sh cn '/etc/mwan3helper/all_cn.txt'
+ /etc/mwan3helper/genipset.sh ct '/etc/mwan3helper/chinatelecom.txt'
+ /etc/mwan3helper/genipset.sh cnc '/etc/mwan3helper/unicom_cnc.txt'
+ /etc/mwan3helper/genipset.sh cmcc '/etc/mwan3helper/cmcc.txt'
+ /etc/mwan3helper/genipset.sh crtc '/etc/mwan3helper/crtc.txt'
+ /etc/mwan3helper/genipset.sh cernet '/etc/mwan3helper/cernet.txt'
+ /etc/mwan3helper/genipset.sh gwbn '/etc/mwan3helper/gwbn.txt'
+ /etc/mwan3helper/genipset.sh othernet '/etc/mwan3helper/othernet.txt'
+ en=$(uci get mwan3helper.config.enabled 2>/dev/null)
+ if [ $en -eq 1 ]; then
+ start_pdnsd
+ mkdir -p /tmp/etc/dnsmasq-mwan3.d
+ ipset -N gfwlist hash:net 2>/dev/null
+ awk '!/^$/&&!/^#/{printf("ipset=/.%s/'"gfwlist"'\n",$0)}' /etc/mwan3helper/gfw.txt > /tmp/etc/dnsmasq-mwan3.d/mwan3_gfwlist.conf
+ awk '!/^$/&&!/^#/{printf("server=/.%s/'"127.0.0.1#53000"'\n",$0)}' /etc/mwan3helper/gfw.txt >> /tmp/etc/dnsmasq-mwan3.d/mwan3_gfwlist.conf
+ cat > /tmp/dnsmasq.d/dnsmasq-mwan3.conf </dev/null
+
+echo "create $1 hash:net family inet hashsize 1024 maxelem 65536" > /tmp/mwan3.ipset
+cat $2 | sed -e "s/^/add $1 /" >> /tmp/mwan3.ipset
+ipset -! flush $1
+ipset -! restore < /tmp/mwan3.ipset 2>/dev/null
+rm -f /tmp/mwan3.ipset
diff --git a/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt
new file mode 100644
index 0000000000..073d0aa7a3
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt
@@ -0,0 +1,5205 @@
+030buy.com
+0rz.tw
+10.tt
+1000giri.net
+100ke.org
+10conditionsoflove.com
+10musume.com
+123rf.com
+12bet.com
+12vpn.com
+12vpn.net
+138.com
+141jj.com
+141tube.com
+1688.com.au
+173ng.com
+177pic.info
+17t17p.com
+18board.com
+18board.info
+18onlygirls.com
+18p2p.com
+18virginsex.com
+1949er.org
+1984bbs.com
+1984bbs.org
+1989report.hkja.org.hk
+1991way.com
+1998cdp.org
+1bao.org
+1dumb.com
+1e100.net
+1eew.com
+1mobile.com
+1pondo.tv
+2-hand.info
+2008xianzhang.info
+2017.hk
+21pron.com
+21sextury.com
+228.net.tw
+233abc.com
+24hrs.ca
+24smile.org
+25u.com
+2lipstube.com
+2shared.com
+2waky.com
+3-a.net
+30boxes.com
+315lz.com
+32red.com
+36rain.com
+3a5a.com
+3arabtv.com
+3boys2girls.com
+3d-game.com
+3proxy.ru
+3ren.ca
+3tui.net
+466453.com
+4bluestones.biz
+4chan.com
+4dq.com
+4everproxy.com
+4irc.com
+4mydomain.com
+4pu.com
+4rbtv.com
+4shared.com
+51.ca
+51jav.org
+51luoben.com
+5278.cc
+56cun04.jigsy.com
+5aimiku.com
+5i01.com
+5isotoi5.org
+5maodang.com
+63i.com
+64museum.org
+64tianwang.com
+64wiki.com
+66.ca
+666kb.com
+6park.com
+6parker.com
+7capture.com
+7cow.com
+8-d.com
+85cc.net
+85cc.us
+85st.com
+881903.com
+888.com
+888poker.com
+89-64.org
+89.64.charter.constitutionalism.solutions
+8news.com.tw
+8z1.net
+9001700.com
+91porn.com
+91vps.club
+92ccav.com
+991.com
+99btgc01.com
+99cn.info
+9bis.com
+9bis.net
+a-normal-day.com
+a248.e.akamai.net
+a5.com.ru
+aamacau.com
+abc.com
+abc.net.au
+abc.pp.ru
+abc.xyz
+abchinese.com
+abclite.net
+abematv.akamaized.net
+ablwang.com
+aboluowang.com
+aboutgfw.com
+abs.edu
+ac.jiruan.net
+accim.org
+aceros-de-hispania.com
+acevpn.com
+acg18.me
+acgkj.com
+acmedia365.com
+acmetoy.com
+acnw.com.au
+actfortibet.org
+actimes.com.au
+activpn.com
+aculo.us
+adcex.com
+addictedtocoffee.de
+admin.recaptcha.net
+admob.com
+adpl.org.hk
+ads-twitter.com
+adsense.com
+adult-sex-games.com
+adultfriendfinder.com
+advanscene.com
+advertfan.com
+ae.hao123.com
+ae.org
+aenhancers.com
+af.mil
+afantibbs.com
+aftygh.gov.tw
+agnesb.fr
+agoogleaday.com
+agro.hk
+ai-kan.net
+ai-wen.net
+ai.google
+aide.gov.tw
+aiph.net
+airasia.com
+airconsole.com
+airvpn.org
+aisex.com
+aiss.anws.gov.tw
+ait.org.tw
+aiweiwei.com
+aiweiweiblog.com
+akiba-online.com
+akiba-web.com
+akow.org
+al-islam.com
+al-qimmah.net
+alabout.com
+alanhou.com
+alarab.qa
+alasbarricadas.org
+alexlur.org
+alforattv.net
+alhayat.com
+alicejapan.co.jp
+aliengu.com
+alkasir.com
+allcoin.com
+allconnected.co
+alldrawnsex.com
+allervpn.com
+allfinegirls.com
+allgirlmassage.com
+allgirlsallowed.org
+allgravure.com
+alliance.org.hk
+allinfa.com
+alljackpotscasino.com
+allmovie.com
+allowed.org
+almasdarnews.com
+almostmy.com
+alphaporno.com
+alternate-tools.com
+altrec.com
+alvinalexander.com
+alwaysdata.com
+alwaysdata.net
+alwaysvpn.com
+am730.com.hk
+amazon.co.jp
+ameblo.jp
+americangreencard.com
+americanunfinished.com
+amiblockedornot.com
+amigobbs.net
+amitabhafoundation.us
+amnesty.org
+amnesty.tw
+amnestyusa.org
+amnyemachen.org
+amoiist.com
+ampproject.org
+amtb-taipei.org
+anchorfree.com
+ancsconf.org
+andfaraway.net
+android-x86.org
+android.com
+androidify.com
+andygod.com
+angularjs.org
+animecrazy.net
+animeshippuuden.com
+aniscartujo.com
+anobii.com
+anontext.com
+anonymise.us
+anonymitynetwork.com
+anonymizer.com
+anpopo.com
+answering-islam.org
+anthonycalzadilla.com
+anti1984.com
+antichristendom.com
+antiwave.net
+anyporn.com
+anysex.com
+aobo.com.au
+aofriend.com
+aojiao.org
+aolchannels.aol.com
+aomiwang.com
+apartmentratings.com
+apartments.com
+apetube.com
+api-secure.recaptcha.net
+api-verify.recaptcha.net
+api.ai
+api.dropboxapi.com
+api.linksalpha.com
+api.proxlet.com
+api.pureapk.com
+api.recaptcha.net
+apiary.io
+apidocs.linksalpha.com
+apigee.com
+apk-dl.com
+apkmirror.com
+apkplz.com
+apkpure.com
+aplusvpn.com
+app.box.com
+app.heywire.com
+app.smartmailcloud.com
+app.tutanota.com
+appledaily.com
+appshopper.com
+appsocks.net
+appspot.com
+appsto.re
+aptoide.com
+ar.hao123.com
+archive.fo
+archive.is
+archive.li
+archive.org
+archive.today
+archives.gov
+arctosia.com
+areca-backup.org
+arena.taipei
+arethusa.su
+arlingtoncemetery.mil
+army.mil
+art4tibet1998.org
+arte.gov.tw
+artofpeacefoundation.org
+artsy.net
+asacp.org
+asahichinese.com
+asg.to
+asia-gaming.com
+asiaharvest.org
+asianews.it
+asiansexdiary.com
+asianspiss.com
+asianwomensfilm.de
+asiatgp.com
+asiatoday.us
+askstudent.com
+askynz.net
+assembla.com
+assimp.org
+astrill.com
+atc.org.au
+atchinese.com
+atdmt.com
+atgfw.org
+athenaeizou.com
+atlaspost.com
+atnext.com
+authorizeddns.net
+authorizeddns.org
+authorizeddns.us
+autodraw.com
+av-e-body.com
+av.com
+av.movie
+av.nightlife141.com
+avaaz.org
+avbody.tv
+avcity.tv
+avcool.com
+avdb.in
+avdb.tv
+avfantasy.com
+avgle.com
+avidemux.org
+avmo.pw
+avmoo.com
+avmoo.net
+avmoo.pw
+avoision.com
+avyahoo.com
+axureformac.com
+azerbaycan.tv
+azerimix.com
+azubu.tv
+b0ne.com
+babynet.com.hk
+backchina.com
+backtotiananmen.com
+badiucao.com
+badjojo.com
+badoo.com
+baidu.jp
+baijie.org
+bailandaily.com
+baixing.me
+bakgeekhome.tk
+banana-vpn.com
+bandwagonhost.com
+bangbrosnetwork.com
+bangchen.net
+bangyoulater.com
+bankmobilevibe.com
+bannedbook.org
+bannednews.org
+banorte.com
+baramangaonline.com
+barenakedislam.com
+barnabu.co.uk
+bartvpn.com
+bash-hackers.org
+bastillepost.com
+bayvoice.net
+bb-chat.tv
+bbc.co.uk
+bbc.com
+bbc.in
+bbcchinese.com
+bbchat.tv
+bbci.co.uk
+bbg.gov
+bbnradio.org
+bbs-tw.com
+bbs.brockbbs.com
+bbs.ecstart.com
+bbs.hasi.wang
+bbs.huasing.org
+bbs.junglobal.net
+bbs.kimy.com.tw
+bbs.morbell.com
+bbs.mychat.to
+bbs.netbig.com
+bbs.ozchinese.com
+bbs.qmzdd.com
+bbs.skykiwi.com
+bbs.sou-tong.org
+bbs.tuitui.info
+bbsfeed.com
+bbsland.com
+bbsmo.com
+bbsone.com
+bbtoystore.com
+bcast.co.nz
+bcchinese.net
+bcex.ca
+bcmorning.com
+bdsmvideos.net
+beaconevents.com
+bebo.com
+beeg.com
+beevpn.com
+behance.net
+behindkink.com
+beijing1989.com
+beijingspring.com
+beijingzx.org
+belamionline.com
+bell.wiki
+bemywife.cc
+beric.me
+berlintwitterwall.com
+berm.co.nz
+bestforchina.org
+bestgore.com
+bestpornstardb.com
+bestvpn.com
+bestvpnanalysis.com
+bestvpnserver.com
+bestvpnservice.com
+bestvpnusa.com
+bet365.com
+betfair.com
+betternet.co
+bettervpn.com
+bettween.com
+betvictor.com
+bewww.net
+beyondfirewall.com
+bfnn.org
+bfsh.hk
+bgvpn.com
+bianlei.com
+biantailajiao.com
+biantailajiao.in
+biblesforamerica.org
+bibox.com
+bic2011.org
+big.one
+bigfools.com
+bigjapanesesex.com
+bigmoney.biz
+bignews.org
+bigsound.org
+biliworld.com
+binance.com
+binux.me
+bipic.net
+bird.so
+bit-z.com
+bit.do
+bit.ly
+bitcointalk.org
+bitcoinworld.com
+bitfinex.com
+bitinka.com.ar
+bitmex.com
+bitshare.com
+bitsnoop.com
+bitvise.com
+bizhat.com
+bjnewlife.org
+bjs.org
+bjzc.org
+bl-doujinsouko.com
+blacklogic.com
+blackvpn.com
+blewpass.com
+blinkx.com
+blinw.com
+blip.tv
+blockcn.com
+blockless.com
+blog.calibre-ebook.com
+blog.cnyes.com
+blog.de
+blog.exblog.co.jp
+blog.excite.co.jp
+blog.expofutures.com
+blog.fizzik.com
+blog.foolsmountain.com
+blog.fuckgfw233.org
+blog.google
+blog.inoreader.com
+blog.jackjia.com
+blog.jp
+blog.kangye.org
+blog.lester850.info
+blog.martinoei.com
+blog.ontrac.com
+blog.pathtosharepoint.com
+blog.pentalogic.net
+blog.sina.com.tw
+blog.sogoo.org
+blog.soylent.com
+blog.taragana.com
+blog.tiney.com
+blog.xuite.net
+blog.youthwant.com.tw
+blogblog.com
+blogcatalog.com
+blogcity.me
+blogdns.org
+blogger.com
+blogimg.jp
+bloglines.com
+bloglovin.com
+blogs.tampabay.com
+blogs.yahoo.co.jp
+blogspot.com
+blogspot.hk
+blogspot.jp
+blogspot.tw
+blogtd.net
+blogtd.org
+bloodshed.net
+bloomberg.cn
+bloomberg.com
+bloomberg.de
+bloombergview.com
+bloomfortune.com
+blueangellive.com
+bmfinn.com
+bnews.co
+bnrmetal.com
+boardreader.com
+bod.asia
+bodog88.com
+bolehvpn.net
+bolin.netfirms.com
+bonbonme.com
+bonbonsex.com
+bonfoundation.org
+bongacams.com
+boobstagram.com
+book.com.tw
+book.zi5.me
+bookepub.com
+books.com.tw
+booktopia.com.au
+boomssr.com
+bot.nu
+botanwang.com
+bowenpress.com
+boxpn.com
+boxun.com
+boxun.tv
+boxunblog.com
+boxunclub.com
+boyangu.com
+boyfriendtv.com
+boysfood.com
+boysmaster.com
+br.hao123.com
+br.st
+brandonhutchinson.com
+braumeister.org
+bravotube.net
+brazzers.com
+break.com
+breakgfw.com
+breaking911.com
+breakingtweets.com
+breakwall.net
+brizzly.com
+brkmd.com
+broadbook.com
+broadpressinc.com
+brucewang.net
+brutaltgp.com
+bt2mag.com
+bt95.com
+btaia.com
+btbtav.com
+btcbank.bank
+btctrade.im
+btdigg.org
+btku.me
+btku.org
+btspread.com
+btsynckeys.com
+budaedu.org
+buddhistchannel.tv
+buffered.com
+bullog.org
+bullogger.com
+bunbunhk.com
+busayari.com
+businessinsider.com
+businessweek.com
+busytrade.com
+buugaa.com
+buzzhand.com
+buzzhand.net
+buzzorange.com
+bvpn.com
+bwh1.net
+bwsj.hk
+bx.in.th
+bx.tl
+bynet.co.il
+c-est-simple.com
+c-spanvideo.org
+c100tibet.org
+c1522.mooo.com
+c2cx.com
+cablegatesearch.net
+cachinese.com
+cacnw.com
+cactusvpn.com
+cafepress.com
+cahr.org.tw
+calebelston.com
+calgarychinese.ca
+calgarychinese.com
+calgarychinese.net
+cam4.com
+cam4.jp
+cam4.sg
+camfrog.com
+cams.com
+cams.org.sg
+canadameet.com
+canalporno.com
+canyu.org
+cao.im
+caobian.info
+caochangqing.com
+cap.org.hk
+carabinasypistolas.com
+cardinalkungfoundation.org
+carfax.com
+cari.com.my
+caribbeancom.com
+carmotorshow.com
+cartoonmovement.com
+casadeltibetbcn.org
+casatibet.org.mx
+casino.williamhill.com
+casinobellini.com
+casinoking.com
+casinoriva.com
+castbox.fm
+catch22.net
+catchgod.com
+catfightpayperview.xxx
+catholic.org.hk
+catholic.org.tw
+cathvoice.org.tw
+cattt.com
+cbc.ca
+cbs.ntu.edu.tw
+cbtc.org.hk
+cccat.cc
+cccat.co
+ccdtr.org
+cchere.com
+ccim.org
+cclife.ca
+cclife.org
+cclifefl.org
+ccthere.com
+cctmweb.net
+ccue.ca
+ccue.com
+ccvoice.ca
+ccw.org.tw
+cdbook.org
+cdcparty.com
+cdef.org
+cdig.info
+cdjp.org
+cdn-images.mailchimp.com
+cdn.assets.lfpcontent.com
+cdn.helixstudios.net
+cdn.printfriendly.com
+cdn.seatguru.com
+cdn.softlayer.net
+cdn.v2ex.com
+cdn1.lp.saboom.com
+cdnews.com.tw
+cdninstagram.com
+cdp1989.org
+cdp1998.org
+cdp2006.org
+cdpa.url.tw
+cdpeu.org
+cdpusa.org
+cdpweb.org
+cdpwu.org
+cdw.com
+cecc.gov
+cellulo.info
+cenews.eu
+centauro.com.br
+centerforhumanreprod.com
+centralnation.com
+centurys.net
+certificate-transparency.org
+certificate.revocationcheck.com
+cfhks.org.hk
+cfos.de
+cftfc.com
+cgdepot.org
+cgst.edu
+ch.shvoong.com
+change.org
+changeip.name
+changeip.net
+changeip.org
+changp.com
+changsa.net
+chaoex.com
+chapm25.com
+chatnook.com
+chaturbate.com
+chengmingmag.com
+chenguangcheng.com
+chenpokong.com
+chenpokong.net
+chenshan20042005.wordpress.com
+cherrysave.com
+chhongbi.org
+chicagoncmtv.com
+china-mmm.jp.net
+china-mmm.net
+china-mmm.sa.com
+china-review.com.ua
+china-week.com
+china.hket.com
+china101.com
+china18.org
+china21.com
+china21.org
+china5000.us
+chinaaffairs.org
+chinaaid.me
+chinaaid.net
+chinaaid.org
+chinaaid.us
+chinachange.org
+chinachannel.hk
+chinacitynews.be
+chinacomments.org
+chinadialogue.net
+chinadigitaltimes.net
+chinaelections.org
+chinaeweekly.com
+chinafreepress.org
+chinagate.com
+chinageeks.org
+chinagfw.org
+chinagonet.com
+chinagreenparty.org
+chinahorizon.org
+chinahush.com
+chinainperspective.com
+chinainterimgov.org
+chinalaborwatch.org
+chinalawandpolicy.com
+chinalawtranslate.com
+chinamule.com
+chinamz.org
+chinapress.com.my
+chinarightsia.org
+chinasocialdemocraticparty.com
+chinasoul.org
+chinasucks.net
+chinatopsex.com
+chinatown.com.au
+chinatweeps.com
+chinaview.wordpress.com
+chinaway.org
+chinaworker.info
+chinayouth.org.hk
+chinayuanmin.org
+chinaz.com
+chinese-hermit.net
+chinese-leaders.org
+chinese-memorial.org
+chinese.donga.com
+chinese.engadget.com
+chinese.irib.ir
+chinese.soifind.com
+chinesedaily.com
+chinesedailynews.com
+chinesedemocracy.com
+chinesegay.org
+chinesen.de
+chinesepen.org
+chineseupress.com
+chingcheong.com
+chinman.net
+chithu.org
+chn.chosun.com
+chobit.cc
+chrdnet.com
+christianfreedom.org
+christianstudy.com
+christiantimes.org.hk
+chrlawyers.hk
+chrome.com
+chromecast.com
+chromeexperiments.com
+chromercise.com
+chromestatus.com
+chromium.org
+chuang-yen.org
+chubold.com
+chubun.com
+chuizi.net
+chukuang.gov.tw
+chushigangdrug.ch
+cienen.com
+cineastentreff.de
+cipfg.org
+circlethebayfortibet.org
+cirosantilli.com
+citizencn.com
+citizenlab.org
+citizenscommission.hk
+citizensradio.org
+city365.ca
+city9x.com
+citypopulation.de
+civicparty.hk
+civildisobediencemovement.org
+civilhrfront.org
+civiliangunner.com
+civilmedia.tw
+ck101.com
+cl.d0z.net
+classicalguitarblog.net
+clb.org.hk
+cldr.unicode.org
+cleansite.biz
+cleansite.info
+cleansite.us
+clearharmony.net
+clearsurance.com
+clearwisdom.net
+clementine-player.org
+clientservices.googleapis.com
+cling.omy.sg
+clinica-tibet.ru
+clipfish.de
+cloakpoint.com
+cloud.mail.ru
+club1069.com
+cmcn.org
+cmi.org.tw
+cmp.hku.hk
+cms.gov
+cmule.com
+cmule.org
+cmx.im
+cn-proxy.com
+cn.calameo.com
+cn.dayabook.com
+cn.fmnnow.com
+cn.freeones.com
+cn.gravatar.com
+cn.ibtimes.com
+cn.nytstyle.com
+cn.sandscotaicentral.com
+cn.shafaqna.com
+cn.thegay.com
+cn.uncyclopedia.wikia.com
+cn.uptodown.com
+cn.v2ex.com
+cn.voa.mobi
+cn2.streetvoice.com
+cn6.eu
+cna.com.tw
+cnabc.com
+cnbbnews.wordpress.com
+cnd.org
+cnex.org.cn
+cnineu.com
+cnpolitics.org
+cnproxy.com
+co.ng.mil
+coat.co.jp
+cochina.co
+cochina.org
+codeshare.io
+codeskulptor.org
+coin2co.in
+coinegg.com
+coinex.com
+coingi.com
+coinrail.co.kr
+cointobe.com
+coinut.com
+collateralmurder.com
+collateralmurder.org
+com.google
+comefromchina.com
+comic-mega.me
+commandarms.com
+commentshk.com
+communistcrimes.org
+community.windy.com
+communitychoicecu.com
+compileheart.com
+compress.to
+connect.facebook.net
+connectivitycheck.gstatic.com
+conoha.jp
+contactmagazine.net
+contests.twilio.com
+convio.net
+coobay.com
+coolaler.com
+coolder.com
+coolloud.org.tw
+coolncute.com
+coolstuffinc.com
+corumcollege.com
+cos-moe.com
+cosmic.monar.ch
+cosplayjav.pl
+costco.com
+cotweet.com
+coursehero.com
+cpj.org
+cq99.us
+crackle.com
+crazys.cc
+crazyshit.com
+crchina.org
+crd-net.org
+creaders.net
+creadersnet.com
+creativelab5.com
+crisisresponse.google
+cristyli.com
+crocotube.com
+cromotc.nat.gov.tw
+crossfire.co.kr
+crossthewall.net
+crossvpn.net
+crrev.com
+crucial.com
+csdparty.com
+csi.gstatic.com
+css.pixnet.in
+csuchen.de
+csw.org.uk
+ct.org.tw
+ctao.org
+ctfriend.net
+ctitv.com.tw
+cts.com.tw
+cuihua.org
+cuiweiping.net
+culture.tw
+cumlouder.com
+curvefish.com
+cusu.hk
+cutscenes.net
+cw.com.tw
+cwb.gov.tw
+cyberghost.natado.com
+cyberghostvpn.com
+cycab.gov.tw
+cynscribe.com
+cytode.us
+d-fukyu.com
+d100.net
+d1b183sg0nvnuh.cloudfront.net
+d1c37gjwa26taa.cloudfront.net
+d2bay.com
+d2pass.com
+d3c33hcgiwev3.cloudfront.net
+d3rhr7kgmtrq1v.cloudfront.net
+dabr.co.uk
+dabr.eu
+dabr.me
+dabr.mobi
+dadazim.com
+dadi360.com
+dafabet.com
+dafagood.com
+dafahao.com
+dafoh.org
+daftporn.com
+dagelijksestandaard.nl
+daidostup.ru
+dailidaili.com
+dailymotion.com
+daiphapinfo.net
+dajiyuan.com
+dajiyuan.de
+dajiyuan.eu
+dajusha.baywords.com
+dalailama-archives.org
+dalailama.com
+dalailama.mn
+dalailama.ru
+dalailama.usc.edu
+dalailama80.org
+dalailamacenter.org
+dalailamafellows.org
+dalailamafilm.com
+dalailamafoundation.org
+dalailamahindi.com
+dalailamainaustralia.org
+dalailamajapanese.com
+dalailamaprotesters.info
+dalailamaquotes.org
+dalailamatrust.org
+dalailamavisit.org.nz
+dalailamaworld.com
+dalianmeng.org
+daliulian.org
+danbooru.donmai.us
+danke4china.net
+danwei.org
+daodu14.jigsy.com
+daolan.net
+daozhongxing.org
+darktech.org
+darktoy.net
+darpa.mil
+dastrassi.org
+data-vocabulary.org
+data.flurry.com
+data.gov.tw
+david-kilgour.com
+dawangidc.com
+daxa.cn
+db.tt
+dbnsa.gov.tw
+dcard.tw
+dcmilitary.com
+ddc.com.tw
+ddhw.info
+ddns.info
+ddns.me.uk
+ddns.mobi
+ddns.ms
+ddns.name
+ddns.us
+de-sci.org
+deaftone.com
+debug.com
+deck.ly
+decodet.co
+deepmind.com
+deezer.com
+definebabe.com
+deja.com
+delcamp.net
+demo.opera-mini.net
+democrats.org
+depositphotos.com
+derekhsu.homeip.net
+desc.se
+design.google
+desipro.de
+dessci.com
+destroy-china.jp
+deutsche-welle.de
+developers.box.net
+devio.us
+devpn.com
+df.gov.tw
+dfas.mil
+dfn.org
+dharamsalanet.com
+dharmakara.net
+dhcp.biz
+diaoyuislands.org
+dictionary.goo.ne.jp
+difangwenge.org
+digisfera.com
+digitalnomadsproject.org
+diigo.com
+dilber.se
+dingchin.com.tw
+dipity.com
+directcreative.com
+discoins.com
+discordapp.com
+discordapp.net
+discuss.com.hk
+discuss4u.com
+dish.com
+disp.cc
+disqus.com
+dit-inc.us
+dizhidizhi.com
+dizhuzhishang.com
+djangosnippets.org
+djorz.com
+dl-laby.jp
+dl.box.net
+dl.google.com
+dlsite.com
+dlyoutube.com
+dm530.net
+dmcdn.net
+dmm.co.jp
+dns-dns.com
+dns-stuff.com
+dns04.com
+dns05.com
+dns1.us
+dns2.us
+dns2go.com
+dnscrypt.org
+dnset.com
+dnsrd.com
+dnssec.net
+dnvod.tv
+doctorvoice.org
+dojin.com
+dok-forum.net
+dolc.de
+dolf.org.hk
+dollf.com
+domain.club.tw
+domainhelp.search.com
+domains.google
+domaintoday.com.au
+dongtaiwang.com
+dongtaiwang.net
+dongyangjing.com
+dontfilter.us
+dontmovetochina.com
+dorjeshugden.com
+dotplane.com
+dotsub.com
+dotvpn.com
+doub.io
+dougscripts.com
+douhokanko.net
+doujincafe.com
+dowei.org
+download.aircrack-ng.org
+download.cnet.com
+download.ithome.com.tw
+download.syniumsoftware.com
+dphk.org
+dpp.org.tw
+dpr.info
+dragonex.io
+dragonsprings.org
+dreamamateurs.com
+drepung.org
+drgan.net
+drmingxia.org
+dropbooks.tv
+dropbox.com
+dropboxusercontent.com
+drsunacademy.com
+drtuber.com
+dscn.info
+dsmtp.com
+dstk.dk
+dtdns.net
+dtiblog.com
+dtic.mil
+dtwang.org
+duanzhihu.com
+duck.com
+duckdns.org
+duckduckgo-owned-server.yahoo.net
+duckduckgo.com
+duckmylife.com
+duga.jp
+duihua.org
+duihuahrjournal.org
+dumb1.com
+dunyabulteni.net
+duoweitimes.com
+duping.net
+duplicati.com
+dupola.com
+dupola.net
+dushi.ca
+dvdpac.com
+dvorak.org
+dw-world.com
+dw-world.de
+dw.com
+dw.de
+dwnews.com
+dwnews.net
+dynamic-dns.net
+dynamicdns.biz
+dynamicdns.co.uk
+dynamicdns.me.uk
+dynamicdns.org.uk
+dynawebinc.com
+dyndns-ip.com
+dyndns-pics.com
+dyndns.org
+dyndns.pro
+dynssl.com
+dynu.com
+dynu.net
+dzze.com
+e-classical.com.tw
+e-gold.com
+e-hentai.org
+e-hentaidb.com
+e-info.org.tw
+e123.hk
+earlytibet.com
+earthcam.com
+earthvpn.com
+eastcoast-nsa.gov.tw
+eastern-ark.com
+easternlightning.org
+eastturkestan.com
+eastturkistan-gov.org
+eastturkistancc.org
+eastturkistangovernmentinexile.us
+easyca.ca
+easypic.com
+ebony-beauty.com
+ebook.hyread.com.tw
+ebookbrowse.com
+ebookee.com
+ebtcbank.com
+ecfa.org.tw
+echofon.com
+ecimg.tw
+ecministry.net
+economist.com
+edgecastcdn.net
+edicypages.com
+edmontonchina.cn
+edmontonservice.com
+edns.biz
+edoors.com
+edubridge.com
+edupro.org
+eesti.ee
+eevpn.com
+efcc.org.hk
+effers.com
+efksoft.com
+efukt.com
+eic-av.com
+eireinikotaerukai.com
+eisbb.com
+eksisozluk.com
+electionsmeter.com
+elgoog.im
+elpais.com
+eltondisney.com
+embr.in
+emilylau.org.hk
+empfil.com
+emule-ed2k.com
+emulefans.com
+emuparadise.me
+en.favotter.net
+en.hao123.com
+enanyang.my
+enewstree.com
+enfal.de
+engagedaily.org
+englishforeveryone.org
+englishfromengland.co.uk
+englishpen.org
+enlighten.org.tw
+entermap.com
+entnt.com
+environment.google
+epa.gov.tw
+epac.to
+episcopalchurch.org
+epochhk.com
+epochtimes-bg.com
+epochtimes-romania.com
+epochtimes.co.il
+epochtimes.co.kr
+epochtimes.com
+epochtimes.cz
+epochtimes.de
+epochtimes.fr
+epochtimes.ie
+epochtimes.it
+epochtimes.jp
+epochtimes.ru
+epochtimes.se
+epochtimestr.com
+epochweek.com
+epochweekly.com
+eporner.com
+equinenow.com
+erabaru.net
+eracom.com.tw
+eraysoft.com.tr
+erepublik.com
+erights.net
+eriversoft.com
+erktv.com
+ernestmandel.org
+erodaizensyu.com
+erodoujinlog.com
+erodoujinworld.com
+eromanga-kingdom.com
+eromangadouzin.com
+eromon.net
+eroprofile.com
+eroticsaloon.net
+erv-nsa.gov.tw
+eslite.com
+esmtp.biz
+esurance.com
+etaa.org.au
+etadult.com
+etaiwannews.com
+etherdelta.com
+etizer.org
+etokki.com
+etools.ncol.com
+etowns.net
+etowns.org
+etvonline.hk
+eu.org
+eucasino.com
+eulam.com
+eurekavpt.com
+evchk.wikia.com
+evschool.net
+exblog.jp
+exchristian.hk
+exmo.com
+exmormon.org
+expatshield.com
+expecthim.com
+expekt.com
+experts-univers.com
+exploader.net
+expressvpn.com
+exrates.me
+extmatrix.com
+extremetube.com
+exx.com
+eyevio.jp
+eyny.com
+ezpeer.com
+ezua.com
+fa.gov.tw
+facebook.br
+facebook.com
+facebook.design
+facebook.hu
+facebook.in
+facebook.nl
+facebook.se
+facebookquotes4u.com
+faceless.me
+facesofnyfw.com
+facesoftibetanselfimmolators.info
+fail.hk
+faith100.org
+faithfuleye.com
+faiththedog.info
+fakku.net
+falsefire.com
+falun-co.org
+falun-ny.net
+falun.caltech.edu
+falunart.org
+falunasia.info
+falunau.org
+falunaz.net
+falundafa-dc.org
+falundafa-florida.org
+falundafa-nc.org
+falundafa-pa.net
+falundafa-sacramento.org
+falundafa.org
+falundafaindia.org
+falundafamuseum.org
+falungong.club
+falungong.de
+falungong.org.uk
+falunhr.org
+faluninfo.de
+faluninfo.net
+falunpilipinas.net
+falunworld.net
+familyfed.org
+famunion.com
+fan-qiang.com
+fangbinxing.com
+fangeming.com
+fangeqiang.com
+fanglizhi.info
+fangmincn.org
+fangong.forums-free.com
+fangong.org
+fangongheike.com
+fanhaodang.com
+fanqiang.tk
+fanqianghou.com
+fanqiangyakexi.net
+fanqiangzhe.com
+fanswong.com
+fanyue.info
+fapdu.com
+faproxy.com
+faqserv.com
+fartit.com
+farwestchina.com
+fast.wistia.com
+fastpic.ru
+fastssh.com
+faststone.org
+fatbtc.com
+favstar.fm
+fawanghuihui.org
+fb.com
+fb.me
+fbaddins.com
+fbcdn.net
+fbsbx.com
+fbworkmail.com
+fc2.com
+fc2blog.net
+fc2china.com
+fc2cn.com
+fda.gov.tw
+fdc64.de
+fdc64.org
+fdc89.jp
+feedburner.com
+feeds.fileforum.com
+feelssh.com
+feer.com
+feifeiss.com
+feitian-california.org
+feitianacademy.org
+feministteacher.com
+fengzhenghu.com
+fengzhenghu.net
+fevernet.com
+ff.im
+fffff.at
+fflick.com
+ffvpn.com
+fgmtv.net
+fgmtv.org
+fhreports.net
+figprayer.com
+fileflyer.com
+files2me.com
+filesor.com
+fillthesquare.org
+filmingfortibet.org
+filthdump.com
+financetwitter.com
+finchvpn.com
+findmespot.com
+findyoutube.com
+findyoutube.net
+fingerdaily.com
+finler.net
+firearmsworld.net
+firebaseio.com
+fireofliberty.org
+firetweet.io
+firstfivefollowers.com
+flagsonline.it
+flecheinthepeche.fr
+fleshbot.com
+fleursdeslettres.com
+flgg.us
+flgjustice.org
+flickr.com
+flickrhivemind.net
+flickriver.com
+fling.com
+flipboard.com
+flipkart.com
+flitto.com
+flnet.org
+flog.tw
+flyvpn.com
+flyzy2005.com
+fnac.be
+fnac.com
+fochk.org
+focustaiwan.tw
+focusvpn.com
+fofg-europe.net
+fofg.org
+fofldfradio.org
+fonts.googleapis.com
+fonts.gstatic.com
+fooooo.com
+footwiball.com
+foreignpolicy.com
+forum.baby-kingdom.com
+forum.cyberctm.com
+forum.idsam.com
+forum.my903.com
+forum.mymaji.com
+forum.omy.sg
+forum.palmislife.com
+forum.setty.com.tw
+forum.sina.com.hk
+forum.slime.com.tw
+forum.tvb.com
+forum4hk.com
+fotile.me
+fourface.nodesnoop.com
+fourthinternational.org
+foxdie.us
+foxgay.com
+foxsub.com
+foxtang.com
+fpmt-osel.org
+fpmt.org
+fpmt.tw
+fpmtmexico.org
+fq.wikia.com
+fqok.org
+fqrouter.com
+franklc.com
+freakshare.com
+free-gate.org
+free-hada-now.org
+free-proxy.cz
+free-ss.site
+free-ssh.com
+free4u.com.ar
+freealim.com
+freebrowser.org
+freechal.com
+freechina.net
+freechinaforum.org
+freeddns.com
+freeddns.org
+freedomchina.info
+freedomhouse.org
+freedominfonetweb.wordpress.com
+freedomsherald.org
+freeforums.org
+freefq.com
+freefuckvids.com
+freegao.com
+freeilhamtohti.org
+freekwonpyong.org
+freelotto.com
+freeman2.com
+freemoren.com
+freemorenews.com
+freenet-china.org
+freenetproject.org
+freenewscn.com
+freeopenvpn.com
+freeoz.org
+freessh.us
+freetcp.com
+freetibet.net
+freetibet.org
+freetibetanheroes.org
+freeviewmovies.com
+freevpn.me
+freevpn.nl
+freewallpaper4.me
+freewebs.com
+freewechat.com
+freewww.biz
+freewww.info
+freexinwen.com
+freeyellow.com
+freeyoutubeproxy.net
+friendfeed.com
+friends-of-tibet.org
+friendsoftibet.org
+fring.com
+fringenetwork.com
+from-pr.com
+from-sd.com
+fromchinatousa.net
+frommel.net
+frontlinedefenders.org
+frootvpn.com
+fscked.org
+fsurf.com
+ftp1.biz
+ftpserver.biz
+ftv.com.tw
+fucd.com
+fuckcnnic.net
+fuckgfw.org
+fullerconsideration.com
+fulue.com
+funf.tw
+funkyimg.com
+funp.com
+fuq.com
+furbo.org
+furhhdl.org
+furinkan.com
+furl.net
+futurechinaforum.org
+futuremessage.org
+fux.com
+fuyin.net
+fuyindiantai.org
+fuyu.org.tw
+fw.cm
+fxcm-chinese.com
+fxnetworks.com
+fzh999.com
+fzh999.net
+fzlm.com
+g-area.org
+g-queen.com
+g.co
+g6hentai.com
+gabocorp.com
+gaeproxy.com
+gaforum.org
+galaxymacau.com
+galenwu.com
+galstars.net
+game735.com
+gamebase.com.tw
+gamejolt.com
+gamer.com.tw
+gamez.com.tw
+gamousa.com
+ganges.com
+gaoming.net
+gaopi.net
+gaozhisheng.net
+gaozhisheng.org
+gardennetworks.com
+gardennetworks.org
+gartlive.com
+gate-project.com
+gate.io
+gatecoin.com
+gather.com
+gatherproxy.com
+gati.org.tw
+gaybubble.com
+gaycn.net
+gayhub.com
+gaymap.cc
+gaymenring.com
+gaytube.com
+gaywatch.com
+gazotube.com
+gcc.org.hk
+gclooney.com
+gcmasia.com
+gcpnews.com
+gcr.io
+gdzf.org
+geek-art.net
+geekheart.info
+gekikame.com
+gelbooru.com
+geocities.co.jp
+geocities.jp
+gerefoundation.org
+get.app
+get.how
+getastrill.com
+getchu.com
+getcloak.com
+getfoxyproxy.org
+getfreedur.com
+getgom.com
+geti2p.net
+getiton.com
+getlantern.org
+getmdl.io
+getoutline.org
+getsocialscope.com
+getsync.com
+gettrials.com
+gettyimages.com
+getuploader.com
+gfbv.de
+gfgold.com.hk
+gfsale.com
+gfw.org.ua
+gfw.press
+ggpht.com
+ggssl.com
+ghostpath.com
+ghut.org
+giantessnight.com
+gifree.com
+giga-web.jp
+gigporno.ru
+girlbanker.com
+gist.github.com
+git.io
+gizlen.net
+gjczz.com
+glass8.eu
+global.bing.com
+globaljihad.net
+globalmediaoutreach.com
+globalmuseumoncommunism.org
+globalrescue.net
+globaltm.org
+globalvoices.org
+globalvoicesonline.org
+globalvpn.net
+glock.com
+gloryhole.com
+glorystar.me
+glype.com
+gmail.com
+gmbd.cn
+gmhz.org
+gmll.org
+gmodules.com
+gmozomg.izihost.org
+gnci.org.hk
+go-pki.com
+go.nesnode.com
+go141.com
+goagent.biz
+goagent.codeplex.com
+goagentplus.com
+gobet.cc
+godfootsteps.org
+godns.work
+godoc.org
+godsdirectcontact.co.uk
+godsdirectcontact.org
+godsimmediatecontact.com
+gogotunnel.com
+gohappy.com.tw
+gojet.krtco.com.tw
+gokbayrak.com
+golang.org
+goldbet.com
+goldbetsports.com
+goldeneyevault.com
+goldenfrog.com
+goldjizz.com
+goldstep.net
+goldwave.com
+gongm.in
+gongmeng.info
+gongminliliang.com
+gongwt.com
+goo.gl
+gooday.xyz
+gooddns.info
+goodreaders.com
+goodreads.com
+goodtv.com.tw
+goodtv.tv
+goofind.com
+google.ae
+google.am
+google.as
+google.at
+google.az
+google.ba
+google.be
+google.bg
+google.ca
+google.calstate.edu
+google.cd
+google.ci
+google.co.id
+google.co.jp
+google.co.kr
+google.co.ma
+google.co.uk
+google.com
+google.de
+google.dj
+google.dk
+google.es
+google.fi
+google.fm
+google.fr
+google.gg
+google.gl
+google.gr
+google.ie
+google.is
+google.it
+google.jo
+google.kz
+google.lv
+google.mn
+google.ms
+google.nl
+google.no
+google.nu
+google.ro
+google.ru
+google.rw
+google.sc
+google.sh
+google.sk
+google.sm
+google.sn
+google.tk
+google.tm
+google.to
+google.tt
+google.vu
+google.ws
+googleapis.cn
+googleapis.com
+googleapps.com
+googlearth.com
+googleartproject.com
+googleblog.com
+googlebot.com
+googlechinawebmaster.com
+googlecode.com
+googlecommerce.com
+googledomains.com
+googledrive.com
+googleearth.com
+googlegroups.com
+googlehosted.com
+googleideas.com
+googleinsidesearch.com
+googlelabs.com
+googlemail.com
+googlemashups.com
+googlepagecreator.com
+googleplay.com
+googleplus.com
+googlescholar.com
+googlesile.com
+googlesource.com
+googleusercontent.com
+googlevideo.com
+googleweblight.com
+googlezip.net
+gopetition.com
+goproxing.net
+gospelherald.com
+got-game.org
+gotdns.ch
+gotgeeks.com
+gotrusted.com
+gotw.ca
+gov.taipei
+gov.tw
+gr8domain.biz
+gr8name.biz
+grammaly.com
+grandtrial.org
+grangorz.org
+graphis.ne.jp
+graphql.org
+grb.gov.tw
+greasespot.net
+great-firewall.com
+great-roc.org
+greatfire.org
+greatfire.us7.list-manage.com
+greatfirewall.biz
+greatfirewallofchina.net
+greatfirewallofchina.org
+greatroc.org
+greatroc.tw
+greatzhonghua.org
+greenfieldbookstore.com.hk
+greenparty.org.tw
+greenpeace.com.tw
+greenpeace.org
+greenvpn.net
+greenvpn.org
+grotty-monday.com
+groups.google.cn
+grow.google
+gs-discuss.com
+gsp.target.com
+gstatic.com
+gtricks.com
+gts-vpn.com
+gu-chu-sum.org
+guaguass.com
+guaguass.org
+guancha.org
+guaneryu.com
+guangming.com.my
+guardster.com
+guishan.org
+gumroad.com
+gun-world.net
+gunsamerica.com
+gunsandammo.com
+guo.media
+guruonline.hk
+gutteruncensored.com
+gvlib.com
+gvm.com.tw
+gvt0.com
+gvt1.com
+gvt3.com
+gwtproject.org
+gyalwarinpoche.com
+gyatsostudio.com
+gysd.nyc.gov.tw
+gzm.tv
+gzone-anime.info
+h-china.org
+h-moe.com
+h1n1china.org
+h528.com
+h5dm.com
+h5galgame.me
+hacg.club
+hacg.in
+hacg.li
+hacg.me
+hacg.red
+hacker.org
+hackthatphone.net
+hahaxixi.github.io
+hahlo.com
+hakkatv.org.tw
+handcraftedsoftware.org
+hanunyi.com
+haoel.github.io
+haosou.com
+happy-vpn.com
+haproxy.org
+hardsextube.com
+harunyahya.com
+hautelook.com
+hautelookcdn.com
+have8.com
+haygo.com
+hbo.com
+hchcc.gov.tw
+hclips.com
+hdlt.me
+hdtvb.net
+hdzog.com
+heartyit.com
+heavy-r.com
+hec.su
+hecaitou.net
+hechaji.com
+heeact.edu.tw
+hegre-art.com
+heix.pp.ru
+helloandroid.com
+helloqueer.com
+helloss.pw
+hellotxt.com
+help.linksalpha.com
+helpeachpeople.com
+helplinfen.com
+helpster.de
+helpzhuling.org
+hentai.to
+hentaitube.tv
+hentaivideoworld.com
+heqinglian.net
+heungkongdiscuss.com
+hexieshe.com
+hexieshe.xyz
+hexxeh.net
+heyzo.com
+hgseav.com
+hhdcb3office.org
+hhthesakyatrizin.org
+hi-on.org.tw
+hidden-advent.org
+hide.me
+hidein.net
+hideipvpn.com
+hideman.net
+hideme.nl
+hidemy.name
+hidemyass.com
+hidemycomp.com
+higfw.com
+highpeakspureearth.com
+highrockmedia.com
+hihiforum.com
+hihistory.net
+hiitch.com
+hikinggfw.org
+hilive.tv
+himalayan-foundation.org
+himalayanglacier.com
+himemix.com
+himemix.net
+hitbtc.com
+hitomi.la
+hiwifi.com
+hizb-ut-tahrir.info
+hizb-ut-tahrir.org
+hizbuttahrir.org
+hjclub.info
+hk-pub.com
+hk.geocities.com
+hk.hao123img.com
+hk.jiepang.com
+hk.knowledge.yahoo.com
+hk.myblog.yahoo.com
+hk.news.yahoo.com
+hk.rd.yahoo.com
+hk.v2ex.com
+hk.yahoo.com
+hk01.com
+hk32168.com
+hka8964.wordpress.com
+hkacg.com
+hkacg.net
+hkanews.wordpress.com
+hkatvnews.com
+hkbc.net
+hkbf.org
+hkbookcity.com
+hkchurch.org
+hkci.org.hk
+hkcmi.edu
+hkcnews.com
+hkcoc.com
+hkcoc.weather.com.hk
+hkday.net
+hkdf.org
+hkej.com
+hkfaa.com
+hkfreezone.com
+hkfront.org
+hkgolden.com
+hkhkhk.com
+hkhrc.org.hk
+hkhrm.org.hk
+hkip.org.uk
+hkjc.com
+hkjp.org
+hklft.com
+hklts.org.hk
+hkptu.org
+hkreporter.com
+hkreporter.loved.hk
+hkupop.hku.hk
+hkusu.net
+hkvwet.com
+hkwcc.org.hk
+hkzone.org
+hmonghot.com
+hmvdigital.ca
+hmvdigital.com
+hnjhj.com
+hnntube.com
+hola.com
+hola.org
+holymountaincn.com
+holyspiritspeaks.org
+home.sina.com
+homedepot.com
+homeperversion.com
+homeservershow.com
+hongkongfp.com
+hongmeimei.com
+hongzhi.li
+hootsuite.com
+hoovers.com
+hopedialogue.org
+hopto.org
+hornygamer.com
+hornytrip.com
+hotav.tv
+hotels.cn
+hotfrog.com.tw
+hotgoo.com
+hotpornshow.com
+hotpot.hk
+hotshame.com
+hotspotshield.com
+hotvpn.com
+hougaige.com
+howtoforge.com
+hoxx.com
+hpa.gov.tw
+hqcdp.org
+hqjapanesesex.com
+hqmovies.com
+hqsbnet.wordpress.com
+hqsbonline.wordpress.com
+hrcchina.org
+hrcir.com
+hrea.org
+hrichina.org
+hrtsea.com
+hrw.org
+hrweb.org
+hsinchu-cc.gov.tw
+hsjp.net
+hsselite.com
+hst.net.tw
+hstern.net
+hstt.net
+ht.ly
+htkou.net
+htl.li
+html5rocks.com
+http2.golang.org
+https443.net
+https443.org
+hua-yue.net
+huaglad.com
+huanghuagang.org
+huangyiyu.com
+huaren.us
+huaren4us.com
+huashangnews.com
+huaxia-news.com
+huaxiabao.org
+huaxin.ph
+huayuworld.org
+hudatoriq.web.id
+hudson.org
+hugoroy.eu
+huhaitai.com
+huhamhire.com
+huiyi.in
+hulkshare.com
+hulu.com
+huluim.com
+humanrightsbriefing.org
+hung-ya.com
+hungerstrikeforaids.org
+huobi.com
+huobi.pro
+huobipro.com
+huping.net
+hurgokbayrak.com
+hurriyet.com.tr
+hustlercash.com
+hut2.ru
+hutianyi.net
+hutong9.net
+huyandex.com
+hwadzan.tw
+hwayue.org.tw
+hwinfo.com
+hxwk.org
+hxwq.org
+hybrid-analysis.com
+hyperrate.com
+i-cable.com
+i-part.com.tw
+i.lithium.com
+i.v2ex.com
+i1.hk
+i2p2.de
+i2runner.com
+i818hk.com
+iam.soy
+iamtopone.com
+iask.bz
+iask.ca
+iav19.com
+iblist.com
+iblogserv-f.net
+ibros.org
+ibvpn.com
+icams.com
+ice.audionow.com
+icij.org
+icl-fi.org
+icoco.com
+iconpaper.org
+icu-project.org
+id.hao123.com
+id.heroku.com
+iddddg.com
+idemocracy.asia
+identi.ca
+idiomconnection.com
+idouga.com
+idreamx.com
+idv.tw
+ieasy5.com
+ied2k.net
+ienergy1.com
+ifan.cz.cc
+ifanqiang.com
+ifcss.org
+ifjc.org
+ifreewares.com
+ift.tt
+igcd.net
+igfw.net
+igfw.tech
+igmg.de
+ignitedetroit.net
+igoogle.com
+igotmail.com.tw
+igvita.com
+ihakka.net
+iicns.com
+iipdigital.usembassy.gov
+ikstar.com
+ikwb.com
+illusionfactory.com
+ilove80.be
+ilovelongtoes.com
+im.tv
+im88.tw
+imageab.com
+imagefap.com
+imageflea.com
+images-gaytube.com
+images.comico.tw
+imageshack.us
+imagevenue.com
+imagezilla.net
+imb.org
+ime.baidu.jp
+img.ly
+imgchili.net
+imkev.com
+imlive.com
+immigration.gov.tw
+immoral.jp
+impact.org.au
+impp.mn
+in-disguise.com
+in99.org
+incapdns.net
+incloak.com
+incredibox.fr
+indiandefensenews.in
+indiemerch.com
+iner.gov.tw
+info-graf.fr
+initiativesforchina.org
+inkui.com
+inmediahk.net
+innermongolia.org
+inote.tw
+insecam.org
+insidevoa.com
+instagram.com
+instanthq.com
+institut-tibetain.org
+international-news.newsmagazine.asia
+internetdefenseleague.org
+internetfreedom.org
+internetpopculture.com
+investigating.wordpress.com
+inxian.com
+iownyour.biz
+iownyour.org
+ip.cn
+ip6.static.sl-reverse.com
+ipalter.com
+ipfire.org
+ipfs.io
+iphone4hongkong.com
+iphonehacks.com
+iphonetaiwan.org
+iphonix.fr
+ipicture.ru
+ipjetable.net
+iportal.me
+ippotv.com
+ipredator.se
+iptv.com.tw
+iptvbin.com
+ipvanish.com
+iredmail.org
+ironbigfools.compython.net
+ironpython.net
+ironsocket.com
+is-a-hunter.com
+is.gd
+isaacmao.com
+isasecret.com
+isgreat.org
+islahhaber.net
+islam.org.hk
+islamhouse.com
+islamicity.com
+islamicpluralism.org
+islamtoday.net
+ismaelan.com
+ismalltits.com
+ismprofessional.net
+isohunt.com
+israbox.com
+issuu.com
+istars.co.nz
+istiqlalhewer.com
+istockphoto.com
+isunaffairs.com
+isuntv.com
+itaboo.info
+itaiwan.gov.tw
+italiatibet.org
+itasoftware.com
+itemdb.com
+ithelp.ithome.com.tw
+itsaol.com
+itshidden.com
+itsky.it
+itweet.net
+iu45.com
+iuhrdf.org
+iuksky.com
+ivacy.com
+iverycd.com
+ivpn.net
+ixquick.com
+ixxx.com
+iyouport.com
+izaobao.us
+izles.net
+izlesem.org
+j.mp
+ja.wikipedia.org
+jamaat.org
+jamyangnorbu.com
+jandyx.com
+janwongphoto.com
+japan-whores.com
+japantimes.co.jp
+jav.com
+jav101.com
+jav2be.com
+jav68.tv
+javakiba.org
+javbus.com
+javfor.me
+javhd.com
+javhip.com
+javhub.net
+javhuge.com
+javlibrary.com
+javmobile.net
+javmoo.com
+javmoo.xyz
+javseen.com
+javtag.com
+javzoo.com
+jbtalks.cc
+jbtalks.com
+jbtalks.my
+jcpenney.com
+jdwsy.com
+jeanyim.com
+jetos.com
+jex.com
+jfqu36.club
+jfqu37.xyz
+jgoodies.com
+jiangweiping.com
+jiaoyou8.com
+jiehua.cz
+jieshibaobao.com
+jigglegifs.com
+jigong1024.com
+jihadintel.meforum.org
+jihadology.net
+jiji.com
+jike.com
+jims.net
+jinbushe.org
+jingpin.org
+jingsim.org
+jinpianwang.com
+jinroukong.com
+jinx.com
+jitouch.com
+jizzthis.com
+jjgirls.com
+jkb.cc
+jkforum.net
+jkub.com
+jma.go.jp
+jmscult.com
+joachims.org
+jobnewera.wordpress.com
+jobso.tv
+journalchretien.net
+journalofdemocracy.org
+joymiihub.com
+joyourself.com
+jp.hao123.com
+jpl.nasa.gov
+jpopforum.net
+jpush.cn
+jtvnw.net
+jubushoushen.com
+juhuaren.com
+jukujo-club.com
+juliepost.com
+juliereyc.com
+junauza.com
+june4commemoration.org
+junefourth-20.net
+jungleheart.com
+juoaa.com
+justdied.com
+justfreevpn.com
+justicefortenzin.org
+justpaste.it
+justtristan.com
+juyuange.org
+juziyue.com
+jwmusic.org
+jyxf.net
+k-doujin.net
+ka-wai.com
+kagyu.org
+kagyumonlam.org
+kagyunews.com.hk
+kagyuoffice.org
+kaiyuan.de
+kakao.com
+kalachakralugano.org
+kankan.today
+kannewyork.com
+kanshifang.com
+kantie.org
+kanzhongguo.com
+kanzhongguo.eu
+kaotic.com
+karayou.com
+karkhung.com
+karmapa-teachings.org
+karmapa.org
+kawaiikawaii.jp
+kawase.com
+kb.monitorware.com
+kba-tx.org
+kcoolonline.com
+kebrum.com
+kechara.com
+keezmovies.com
+kendatire.com
+kendincos.net
+kenengba.com
+keontech.net
+kepard.com
+kex.com
+keycdn.com
+kh.google.com
+khabdha.org
+khatrimaza.org
+khm.google.com
+khm.googleapis.com
+khm0.google.com
+khm0.googleapis.com
+khm1.google.com
+khm1.googleapis.com
+khm2.google.com
+khm2.googleapis.com
+khm3.google.com
+khm3.googleapis.com
+khmdb.google.com
+khmdb.googleapis.com
+khmusic.com.tw
+kichiku-doujinko.com
+kik.com
+killwall.com
+kindleren.com
+kineox.free.fr
+kingdomsalvation.org
+kinghost.com
+kingstone.com.tw
+kink.com
+kinmen.org.tw
+kinmen.travel
+kir.jp
+kissbbao.cn
+kiwi.kz
+kk-whys.co.jp
+kkbox.com
+kknews.cc
+klsio.gov.tw
+kmseh.gov.tw
+kmuh.org.tw
+kobo.com
+kobobooks.com
+kodingen.com
+kompozer.net
+konachan.com
+kone.com
+koolsolutions.com
+koornk.com
+koranmandarin.com
+korenan2.com
+ksdl.org
+ksnews.com.tw
+kspcoin.com
+ktzhk.com
+kucoin.com
+kun.im
+kurashsultan.com
+kurtmunger.com
+kusocity.com
+kwcg.ca
+kwongwah.com.my
+kxsw.life
+kyofun.com
+kyohk.net
+kyoyue.com
+kyzyhello.com
+kzeng.info
+la-forum.org
+labiennale.org
+ladbrokes.com
+lagranepoca.com
+lalulalu.com
+lama.com.tw
+lamayeshe.com
+lamnia.co.uk
+lamrim.com
+lanterncn.cn
+lantosfoundation.org
+laod.cn
+laogai.org
+laomiu.com
+laoyang.info
+laptoplockdown.com
+laqingdan.net
+larsgeorge.com
+lastcombat.com
+lastfm.es
+latelinenews.com
+latibet.org
+lax.v2ex.com
+lbank.info
+ld.hao123img.com
+le-vpn.com
+leafyvpn.net
+lecloud.net
+lefora.com
+left21.hk
+legalporno.com
+legaltech.law.com
+legsjapan.com
+leirentv.ca
+leisurecafe.ca
+leisurepro.com
+lematin.ch
+lemonde.fr
+lenwhite.com
+lerosua.org
+lers.google
+lesoir.be
+letou.com
+letscorp.net
+lflink.com
+lflinkup.com
+lflinkup.net
+lflinkup.org
+lhakar.org
+lhasocialwork.org
+liangyou.net
+liangzhichuanmei.com
+lianyue.net
+liaowangxizang.net
+liberal.org.hk
+libertytimes.com.tw
+lifemiles.com
+lighten.org.tw
+lightnovel.cn
+like.com
+limiao.net
+line-apps.com
+line-scdn.net
+line.me
+line.naver.jp
+linear-abematv.akamaized.net
+linglingfa.com
+lingvodics.com
+link-o-rama.com
+linkideo.com
+linkuswell.com
+linux.org.hk
+lionsroar.com
+lipuman.com
+liquidvpn.com
+listentoyoutube.com
+listorious.com
+liu-xiaobo.org
+liudejun.com
+liuhanyu.com
+liujianshu.com
+liuxiaobo.net
+liuxiaotong.com
+livecoin.net
+livedoor.jp
+liveleak.com
+livestation.com
+livestream.com
+livevideo.com
+livingonline.us
+livingstream.com
+liwangyang.com
+lizhizhuangbi.com
+lkcn.net
+load.to
+lobsangwangyal.com
+localdomain.ws
+localpresshk.com
+lockestek.com
+logbot.net
+login.target.com
+logiqx.com
+londonchinese.ca
+longhair.hk
+longmusic.com
+longtermly.net
+longtoes.com
+lookpic.com
+looktoronto.com
+lotuslight.org.hk
+lotuslight.org.tw
+lovetvshow.com
+lpsg.com
+lrfz.com
+lrip.org
+lsd.org.hk
+lsforum.net
+lsm.org
+lsmchinese.org
+lsmkorean.org
+lsmwebcast.com
+lsxszzg.com
+ltn.com.tw
+luke54.com
+luke54.org
+lungtanhr.gov.tw
+lupm.org
+lushstories.com
+luxebc.com
+lvhai.org
+lvv2.com
+lyfhk.net
+lzmtnews.org
+m.hkgalden.com
+m.me
+m.plixi.com
+m.slandr.net
+ma.hao123.com
+macgamestore.com
+macrovpn.com
+macts.com.tw
+mad-ar.ch
+madewithcode.com
+madonna-av.com
+madrau.com
+madthumbs.com
+magazines.sina.com.tw
+magic-net.info
+mahabodhi.org
+maiio.net
+mail-archive.com
+maildns.xyz
+maiplus.com
+maizhong.org
+makemymood.com
+makkahnewspaper.com
+makzhou.warehouse333.com
+malaysiakini.com
+mamingzhe.com
+manchukuo.net
+mangafox.com
+mangafox.me
+maniash.com
+manicur4ik.ru
+mansion.com
+mansionpoker.com
+manta.com
+maolin-nsa.gov.tw
+maplew.com
+marc.info
+marguerite.su
+martau.com
+martincartoons.com
+martsangkagyuofficial.org
+marxist.com
+marxist.net
+mash.to
+maskedip.com
+mastodon.host
+matainja.com
+material.io
+mathable.io
+mathiew-badimon.com
+matome-plus.com
+matome-plus.net
+matsu-news.gov.tw
+matsu-nsa.gov.tw
+matsucc.gov.tw
+matsushimakaede.com
+mattwilcox.net
+maturejp.com
+maxing.jp
+mayimayi.com
+mcadforums.com
+mcaf.ee
+mcfog.com
+mcreasite.com
+md-t.org
+me.youthwant.com.tw
+meansys.com
+media.nu.nl
+media.org.hk
+mediachinese.com
+mediafreakcity.com
+medium.com
+meetav.com
+meetup.com
+mefeedia.com
+mefound.com
+mega.nz
+megaproxy.com
+megarotic.com
+megavideo.com
+megurineluka.com
+meirixiaochao.com
+meltoday.com
+meme.yahoo.com
+memehk.com
+memorybbs.com
+memri.org
+memrijttm.org
+mercyprophet.org
+meridian-trust.org
+meripet.biz
+meripet.com
+merit-times.com.tw
+meshrep.com
+messenger.com
+metarthunter.com
+meteorshowersonline.com
+metrolife.ca
+metroradio.com.hk
+meyou.jp
+meyul.com
+mfxmedia.com
+mgoon.com
+mgstage.com
+mh4u.org
+mhradio.org
+michaelanti.com
+michaelmarketl.com
+microvpn.com
+middle-way.net
+mihr.com
+mihua.org
+mike.cz.cc
+mikesoltys.com
+milph.net
+milsurps.com
+mimiai.net
+mimivip.com
+mimivv.com
+mindrolling.org
+minghui-a.org
+minghui-b.org
+minghui-school.org
+minghui.or.kr
+minghui.org
+minghuiyw.wordpress.com
+mingjinglishi.com
+mingjingnews.com
+mingjingtimes.com
+mingpao.com
+mingpaocanada.com
+mingpaomonthly.com
+mingpaonews.com
+mingpaony.com
+mingpaosf.com
+mingpaotor.com
+mingpaovan.com
+mingshengbao.com
+minhhue.net
+miniforum.org
+ministrybooks.org
+minzhuhua.net
+minzhuzhanxian.com
+minzhuzhongguo.org
+miroguide.com
+mirrorbooks.com
+mist.vip
+mitao.com.tw
+mitbbs.com
+mitbbsau.com
+mixero.com
+mixpod.com
+mixx.com
+mizzmona.com
+mjib.gov.tw
+mk5000.com
+mlcool.com
+mlzs.work
+mm-cg.com
+mmaaxx.com
+mmmca.com
+mnewstv.com
+mo.nightlife141.com
+mobatek.net
+mobile01.com
+mobileways.de
+moby.to
+mobypicture.com
+moe.gov.tw
+moeaic.gov.tw
+moeerolibrary.com
+mofa.gov.tw
+mofaxiehui.com
+mofos.com
+mog.com
+mohu.club
+mohu.ml
+mol.gov.tw
+molihua.org
+mondex.org
+money-link.com.tw
+moneyhome.biz
+monitorchina.org
+monster.com
+moodyz.com
+moonbbs.com
+moonbingo.com
+morningsun.org
+moroneta.com
+mos.ru
+motherless.com
+motor4ik.ru
+mousebreaker.com
+movements.org
+moviefap.com
+mp3buscador.com
+mp3ye.eu
+mpettis.com
+mpfinance.com
+mpinews.com
+mponline.hk
+mqxd.org
+mrbasic.com
+mrbonus.com
+mrface.com
+mrslove.com
+mrtweet.com
+msa-it.org
+msguancha.com
+msha.gov
+mswe1.org
+mthruf.com
+mtw.tl
+muchosucko.com
+mullvad.net
+multiply.com
+multiproxy.org
+multiupload.com
+mummysgold.com
+murmur.tw
+music.jwmusic.org
+musicade.net
+muslimvideo.com
+muzi.com
+muzi.net
+muzu.tv
+mvdis.gov.tw
+mvg.jp
+mx.hao123.com
+mx981.com
+my-formosa.com
+my-private-network.co.uk
+my-proxy.com
+my.mail.ru
+my.pcloud.com
+my03.com
+myanniu.com
+myaudiocast.com
+mybbs.us
+mybet.com
+myca168.com
+mycanadanow.com
+mychinamyhome.com
+mychinanet.com
+mychinanews.com
+mychinese.news
+mycnnews.com
+mydad.info
+myddns.com
+myeasytv.com
+myeclipseide.com
+myforum.com.hk
+myforum.com.uk
+myfreecams.com
+myfreepaysite.com
+myfreshnet.com
+myftp.info
+myftp.name
+myiphide.com
+mykomica.org
+mylftv.com
+mymoe.moe
+mymom.info
+mymusic.net.tw
+mynetav.net
+mynetav.org
+mynumber.org
+myparagliding.com
+mypicture.info
+mypop3.net
+mypop3.org
+mypopescu.com
+myreadingmanga.info
+mysecondarydns.com
+mysinablog.com
+mysite.verizon.net
+myspace.com
+myspacecdn.com
+mytalkbox.com
+mytizi.com
+myvlog.im.tv
+mywww.biz
+myz.info
+naacoalition.org
+naitik.net
+nakido.com
+nalandabodhi.org
+nalandawest.org
+namgyal.org
+namgyalmonastery.org
+namsisi.com
+nankan.gov.tw
+nanyang.com
+nanyangpost.com
+nanzao.com
+naol.ca
+naol.cc
+nat.gov.tw
+nat.moe
+national-lottery.co.uk
+nationwide.com
+naughtyamerica.com
+navyfamily.navy.mil
+navyreserve.navy.mil
+naweeklytimes.com
+nbtvpn.com
+nccwatch.org.tw
+nch.com.tw
+ncn.org
+ncree.gov.tw
+nde.de
+ndr.de
+necoast-nsa.gov.tw
+ned.org
+nekoslovakia.net
+neo-miracle.com
+nepusoku.com
+ner.gov.tw
+net-fits.pro
+netbirds.com
+netcolony.com
+netflix.com
+netme.cc
+netsneak.com
+network54.com
+networkedblogs.com
+networktunnel.net
+neue.v2ex.com
+neverforget8964.org
+new-3lunch.net
+new-akiba.com
+new96.ca
+newcenturymc.com
+newcenturynews.com
+newchen.com
+newgrounds.com
+newipnow.com
+newlandmagazine.com.au
+newnews.ca
+news.cnyes.com
+news.hk.msn.com
+news.hkpeanut.com
+news.msn.com.tw
+news.now.com
+news.omy.sg
+news.seehua.com
+news.sina.com.hk
+news.sina.com.tw
+news.sinchew.com.my
+news.singtao.ca
+news.tvbs.com.tw
+news100.com.tw
+newsancai.com
+newschinacomment.org
+newscn.org
+newsdetox.ca
+newsdh.com
+newstamago.com
+newstapa.org
+newstarnet.com
+newtaiwan.com.tw
+newtalk.tw
+newyorktimes.com
+nexon.com
+next11.co.jp
+nextmag.com.tw
+nextmedia.com
+nexton-net.jp
+nexttv.com.tw
+nf.id.au
+nfjtyd.com
+nflxext.com
+nflximg.com
+nflximg.net
+nflxso.net
+nflxvideo.net
+nga.mil
+ngensis.com
+nhentai.net
+nhi.gov.tw
+nhk-ondemand.jp
+nic.cz.cc
+nic.google
+nic.gov
+nighost.org
+nikkei.com
+ninecommentaries.com
+ninjacloak.com
+ninjaproxy.ninja
+nintendium.com
+ninth.biz
+niusnews.com
+njactb.org
+njuice.com
+nko.navy.mil
+nlfreevpn.com
+nmmba.gov.tw
+nmp.gov.tw
+nmvttc.gov.tw
+no-ip.org
+nobel.se
+nobodycanstop.us
+nofile.io
+nokogiri.org
+nokola.com
+noodlevpn.com
+norbulingka.org
+nordstrom.com
+nordstromimage.com
+nordstromrack.com
+nordvpn.com
+northguan-nsa.gov.tw
+notify.dropboxapi.com
+nottinghampost.com
+novelasia.com
+now.com
+now.im
+nownews.com
+nowtorrents.com
+noypf.com
+npa.go.jp
+npa.gov.tw
+npm.gov.tw
+npnt.me
+nps.gov
+nradio.me
+nrk.no
+ns01.biz
+ns01.info
+ns01.us
+ns02.biz
+ns02.info
+ns02.us
+ns1.name
+ns2.name
+ns3.name
+nsc.gov.tw
+nstm.gov.tw
+ntbk.gov.tw
+ntbna.gov.tw
+ntbt.gov.tw
+ntd.tv
+ntdmh.gov.tw
+ntdtv.ca
+ntdtv.co.kr
+ntdtv.com
+ntdtv.cz
+ntdtv.org
+ntdtv.ru
+ntdtvla.com
+ntl.gov.tw
+ntrfun.com
+ntsec.gov.tw
+ntsna.gov.tw
+ntuh.gov.tw
+nubiles.net
+nuexpo.com
+nukistream.com
+nurgo-software.com
+nusatrip.com
+nutaku.net
+nuuvem.com
+nuvid.com
+nuzcom.com
+nvdst.com
+nvquan.org
+nvri.gov.tw
+nvtongzhisheng.org
+nwtca.org
+ny.stgloballink.com
+ny.visiontimes.com
+nyaa.eu
+nydus.ca
+nylon-angel.com
+nylonstockingsonline.com
+nyt.com
+nytchina.com
+nytcn.me
+nytco.com
+nytimes.com
+nytimes.map.fastly.net
+nytimg.com
+nytstyle.com
+nzchinese.com
+nzchinese.net.nz
+observechina.net
+obutu.com
+ocaspro.com
+occupytiananmen.com
+oclp.hk
+ocreampies.com
+ocry.com
+october-review.org
+oculus.com
+oculuscdn.com
+oex.com
+offbeatchina.com
+officeoftibet.com
+ofile.org
+ogaoga.org
+ogate.org
+oiktv.com
+oizoblog.com
+ok.ru
+okayfreedom.com
+okex.com
+okk.tw
+old-cat.net
+old.nabble.com
+olumpo.com
+olympicwatch.org
+omgili.com
+omni7.jp
+omnitalk.com
+omnitalk.org
+on.cc
+on2.com
+onapp.com
+onedrive.live.com
+onedumb.com
+onejav.com
+onion.city
+online.recoveryversion.org
+onlinecha.com
+onlineyoutube.com
+onlytweets.com
+onmoon.com
+onmoon.net
+onmypc.biz
+onmypc.info
+onmypc.net
+onmypc.org
+onmypc.us
+onthehunt.com
+ontrac.com
+oopsforum.com
+open.com.hk
+openallweb.com
+opendemocracy.net
+opendn.xyz
+openervpn.in
+openid.net
+openleaks.org
+openvpn.net
+openwebster.com
+openwrt.org.cn
+opml.radiotime.com
+opus-gaming.com
+organcare.org.tw
+organharvestinvestigation.net
+organiccrap.com
+orgasm.com
+orgfree.com
+orient-doll.com
+orientaldaily.com.my
+orn.jp
+orzistic.org
+osfoora.com
+otcbtc.com
+otnd.org
+otto.de
+otzo.com
+ourdearamy.com
+ourhobby.com
+oursogo.com
+oursteps.com.au
+oursweb.net
+ourtv.hk
+overplay.net
+oversea.istarshine.com
+ow.ly
+owl.li
+oyax.com
+ozvoice.org
+ozxw.com
+ozyoyo.com
+pachosting.com
+pacificpoker.com
+packetix.net
+pacopacomama.com
+padmanet.com
+page.bid.yahoo.com
+page2rss.com
+pagespeed.v2ex.com
+pagodabox.com
+palacemoon.com
+paldengyal.com
+paljorpublications.com
+paltalk.com
+panamapapers.sueddeutsche.de
+pandapow.co
+pandapow.net
+pandavpn-jp.com
+pandora.com
+pandora.tv
+panluan.net
+panoramio.com
+pao-pao.net
+paper.li
+paperb.us
+paradisehill.cc
+paradisepoker.com
+parkansky.com
+partycasino.com
+partypoker.com
+passion.com
+passiontimes.hk
+paste.ee
+pastebin.com
+pastie.org
+pbwiki.com
+pbworks.com
+pbxes.com
+pbxes.org
+pcanywhere.net
+pcc.gov.tw
+pcdvd.com.tw
+pchome.com.tw
+pcij.org
+pcstore.com.tw
+pct.org.tw
+pdetails.com
+pdproxy.com
+pds.nasa.gov
+peace.ca
+peacefire.org
+peacehall.com
+pearlher.org
+peeasian.com
+pekingduck.org
+pemulihan.or.id
+pen.io
+penchinese.com
+penchinese.net
+penghu-nsa.gov.tw
+pengyulong.com
+penisbot.com
+penthouse.com
+peoplebookcafe.com
+peoplenews.tw
+peopo.org
+percy.in
+perfectgirls.net
+perfectvpn.net
+periscope.tv
+persecutionblog.com
+persiankitty.com
+pfd.org.hk
+phapluan.org
+phayul.com
+philborges.com
+philly.com
+phmsociety.org
+phncdn.com
+phosphation13.rssing.com
+photodharma.net
+photofocus.com
+phuquocservices.com
+picacomic.com
+picacomiccn.com
+picasaweb.com
+picidae.net
+pictures.playboy.com
+picturesocial.com
+pin-cong.com
+pin6.com
+ping.fm
+pinimg.com
+pinkrod.com
+pinoy-n.com
+pinterest.at
+pinterest.co.kr
+pinterest.co.uk
+pinterest.com
+pinterest.de
+pinterest.dk
+pinterest.fr
+pinterest.jp
+pinterest.nl
+pinterest.se
+pioneer-worker.forums-free.com
+pipii.tv
+piposay.com
+piraattilahti.org
+piring.com
+pixelqi.com
+pixiv.net
+pixnet.net
+pk.com
+pki.goog
+placemix.com
+playboy.com
+playboyplus.com
+player.fm
+playno1.com
+playpcesor.com
+plays.com.tw
+plm.org.hk
+plunder.com
+plurk.com
+plus28.com
+plusbb.com
+pmatehunter.com
+pmates.com
+po2b.com
+pobieramy.top
+podictionary.com
+pokerstars.com
+pokerstars.net
+politicalchina.org
+politicalconsultation.org
+politiscales.net
+poloniex.com
+polymer-project.org
+polymerhk.com
+popo.tw
+popvote.hk
+popyard.com
+popyard.org
+porn.com
+porn2.com
+porn5.com
+pornbase.org
+pornerbros.com
+pornhd.com
+pornhost.com
+pornhub.com
+pornhubdeutsch.net
+pornmm.net
+pornoxo.com
+pornrapidshare.com
+pornsharing.com
+pornsocket.com
+pornstarclub.com
+porntube.com
+porntubenews.com
+porntvblog.com
+pornvisit.com
+port25.biz
+portablevpn.nl
+poskotanews.com
+post.gov.tw
+post01.com
+post76.com
+post852.com
+postadult.com
+postimg.org
+potvpn.com
+power.com
+powercx.com
+powerphoto.org
+prayforchina.net
+premeforwindows7.com
+premproxy.com
+presentationzen.com
+presidentlee.tw
+prestige-av.com
+pride.google
+prisoneralert.com
+pritunl.com
+privacybox.de
+privateinternetaccess.com
+privatepaste.com
+privatetunnel.com
+privatevpn.com
+procopytips.com
+prosiben.de
+protonvpn.com
+provideocoalition.com
+provpnaccounts.com
+proxfree.com
+proxifier.com
+proxomitron.info
+proxpn.com
+proxyanonimo.es
+proxydns.com
+proxylist.org.uk
+proxynetwork.org.uk
+proxypy.net
+proxyroad.com
+proxytunnel.net
+proyectoclubes.com
+prozz.net
+psblog.name
+pscp.tv
+psiphon.ca
+psiphon.civisec.org
+psiphon3.com
+psiphontoday.com
+pts.org.tw
+ptt.cc
+pttvan.org
+pubu.com.tw
+puffinbrowser.com
+puffstore.com
+pullfolio.com
+pulse.yahoo.com
+pure18.com
+pureconcepts.net
+pureinsight.org
+purepdf.com
+purevpn.com
+purplelotus.org
+pursuestar.com
+pushchinawall.com
+pussyspace.com
+putihome.org
+putty.org
+puuko.com
+pwned.com
+python.com
+pytorch.org
+qanote.com
+qgirl.com.tw
+qhigh.com
+qi-gong.me
+qiandao.today
+qiangyou.org
+qidian.ca
+qienkuen.org
+qiwen.lu
+qixianglu.cn
+qkshare.com
+qoos.com
+qpoe.com
+qq.co.za
+qstatus.com
+qtrac.eu
+qtweeter.com
+quannengshen.org
+quantumbooter.net
+questvisual.com
+quitccp.net
+quitccp.org
+quora.com
+quran.com
+quranexplorer.com
+qusi8.net
+qvodzy.org
+qxbbs.org
+r18.com
+ra.gg
+radicalparty.org
+radiko.jp
+radioaustralia.net.au
+radiohilight.net
+radiovaticana.org
+radiovncr.com
+rael.org
+raggedbanner.com
+raidcall.com.tw
+raidtalk.com.tw
+raizoji.or.jp
+ramcity.com.au
+rangwang.biz
+rangzen.com
+rangzen.net
+rangzen.org
+ranyunfei.com
+rapbull.net
+rapidmoviez.com
+rapidvpn.com
+raremovie.cc
+raremovie.net
+rawgit.com
+rawgithub.com
+razyboard.com
+rcam.target.com
+rcinet.ca
+rconversation.blogs.com
+rd.com
+rdio.com
+read01.com
+read100.com
+readingtimes.com.tw
+readmoo.com
+readydown.com
+realcourage.org
+realforum.zkiz.com
+realitykings.com
+realraptalk.com
+realsexpass.com
+rebatesrule.net
+recordhistory.org
+recovery.org.tw
+recoveryversion.com.tw
+red-lang.org
+redballoonsolidarity.org
+redchinacn.net
+redchinacn.org
+redd.it
+reddit.com
+redditmedia.com
+redditstatic.com
+redhotlabs.com
+redirector.gvt1.com
+redtube.com
+referer.us
+reflectivecode.com
+registry.google
+relaxbbs.com
+relay.com.tw
+releaseinternational.org
+religioustolerance.org
+renminbao.com
+renyurenquan.org
+resilio.com
+retweeteffect.com
+retweetist.com
+retweetrank.com
+reuters.com
+reutersmedia.net
+revleft.com
+revver.com
+rfa.org
+rfachina.com
+rfalive1.akacast.akamaistream.net
+rfamobile.org
+rfaweb.org
+rferl.org
+rfi.fr
+rightbtc.com
+rigpa.org
+rileyguide.com
+riseup.net
+ritouki.jp
+ritter.vg
+rixcloud.com
+rixcloud.us
+rlwlw.com
+rmjdw.com
+rmjdw132.info
+roadshow.hk
+roboforex.com
+robustnessiskey.com
+rocket-inc.net
+rocksdb.org
+rojo.com
+rolia.net
+ronjoneswriter.com
+roodo.com
+rosechina.net
+rotten.com
+rsdlmonitor.com
+rsf-chinese.org
+rsf.org
+rsgamen.org
+rssmeme.com
+rtalabel.org
+rthk.hk
+rthk.org.hk
+rthklive2-lh.akamaihd.net
+rti.org.tw
+rtycminnesota.org
+rukor.org
+rushbee.com
+ruten.com.tw
+rutube.ru
+ruyiseek.com
+rxhj.net
+s-cute.com
+s-dragon.org
+s1.nudezz.com
+s1heng.com
+s1s1s1.com
+s3-ap-northeast-1.amazonaws.com
+s3-ap-southeast-2.amazonaws.com
+s8forum.com
+sa.hao123.com
+sacks.com
+sacom.hk
+sadistic-v.com
+sadpanda.us
+safebrowsing.googleapis.com
+safervpn.com
+safety.google
+saintyculture.com
+saiq.me
+sakuralive.com
+sakya.org
+salvation.org.hk
+sambhota.org
+sanmin.com.tw
+sapikachu.net
+saveliuxiaobo.com
+savemedia.com
+savethedate.foo
+savetibet.de
+savetibet.fr
+savetibet.nl
+savetibet.org
+savetibet.ru
+savetibetstore.org
+savevid.com
+say2.info
+sbme.me
+scache.vzw.com
+scache1.vzw.com
+scache2.vzw.com
+scasino.com
+schema.org
+sciencenets.com
+scieron.com
+scmp.com
+scmpchinese.com
+scramble.io
+scribd.com
+scriptspot.com
+seapuff.com
+search.aol.com
+search.disconnect.me
+search.yahoo.co.jp
+searchtruth.com
+secretchina.com
+secretgarden.no
+secretsline.biz
+secure.hustler.com
+secure.logmein.com
+secure.raxcdn.com
+securetunnel.com
+securityinabox.org
+securitykiss.com
+seed4.me
+seesmic.com
+seevpn.com
+seezone.net
+sejie.com
+sellclassics.com
+sendsmtp.com
+sendspace.com
+servehttp.com
+serveuser.com
+serveusers.com
+sesawe.net
+sesawe.org
+sethwklein.net
+setn.com
+settv.com.tw
+sevenload.com
+sex-11.com
+sex.com
+sex3.com
+sex8.cc
+sexandsubmission.com
+sexbot.com
+sexhu.com
+sexhuang.com
+sexidude.com
+sexinsex.net
+sextvx.com
+sexxxy.biz
+sfileydy.com
+sfshibao.com
+sftindia.org
+sftuk.org
+shadeyouvpn.com
+shadow.ma
+shadowsky.xyz
+shadowsocks-r.com
+shadowsocks.asia
+shadowsocks.be
+shadowsocks.com
+shadowsocks.org
+shadowsocks9.com
+shambalapost.com
+shambhalasun.com
+shangfang.org
+shapeservices.com
+share.dmhy.org
+share.youthwant.com.tw
+sharebee.com
+sharecool.org
+sharpdaily.com.hk
+sharpdaily.hk
+sharpdaily.tw
+shat-tibet.com
+shattered.io
+sheikyermami.com
+shellfire.de
+shenshou.org
+shenyun.com
+shenyunperformingarts.org
+shenzhoufilm.com
+sherabgyaltsen.com
+shiatv.net
+shicheng.org
+shiksha.com
+shinychan.com
+shipcamouflage.com
+shireyishunjian.com
+shitaotv.org
+shixiao.org
+shizhao.org
+shodanhq.com
+shooshtime.com
+shop2000.com.tw
+shopping.com
+showbiz.omy.sg
+showhaotu.com
+showtime.jp
+shutterstock.com
+shwchurch.org
+shwchurch3.com
+siddharthasintent.org
+sidelinesnews.com
+sidelinessportseatery.com
+sierrafriendsoftibet.org
+sijihuisuo.club
+sijihuisuo.com
+sikaozhe1997.github.io
+silkbook.com
+simbolostwitter.com
+simplecd.me
+simplecd.org
+simpleproductivityblog.com
+singaporepools.com.sg
+singfortibet.com
+singpao.com.hk
+singtao.com
+singtaousa.com
+sino-monthly.com
+sinoants.com
+sinocast.com
+sinocism.com
+sinomontreal.ca
+sinonet.ca
+sinopitt.info
+sinoquebec.com
+siraya-nsa.gov.tw
+sis.xxx
+sis001.com
+sis001.us
+site.locql.com
+site2unblock.com
+site90.net
+sitebro.tw
+sitekreator.com
+siteks.uk.to
+sitemaps.org
+sixth.biz
+sjrt.org
+sjum.cn
+sketchappsources.com
+skimtube.com
+skybet.com
+skyking.com.tw
+skyvegas.com
+skyxvpn.com
+slacker.com
+slaytizle.com
+sleazydream.com
+slheng.com
+slickvpn.com
+slideshare.net
+slinkset.com
+slutload.com
+slutmoonbeam.com
+slyip.com
+slyip.net
+sm-miracle.com
+smartdnsproxy.com
+smarthide.com
+smchbooks.com
+smhric.org
+smyxy.org
+snapchat.com
+snaptu.com
+sndcdn.com
+sneakme.net
+snowlionpub.com
+sobees.com
+soc.mil
+socialwhale.com
+socks-proxy.net
+sockscap64.com
+sockslist.net
+socrec.org
+sod.co.jp
+sodatea.github.io
+softether-download.com
+softether.co.jp
+softether.org
+softwarebychuck.com
+softwaredownload.gitbooks.io
+sogclub.com
+sogrady.me
+soh.tw
+sohcradio.com
+sohfrance.org
+sokamonline.com
+sokmil.com
+solarsystem.nasa.gov
+solidaritetibet.org
+solidfiles.com
+somee.com
+songjianjun.com
+sonicbbs.cc
+sonidodelaesperanza.org
+sopcast.com
+sopcast.org
+sorazone.net
+sorting-algorithms.com
+sos.org
+sosreader.com
+sostibet.org
+soubory.com
+soul-plus.net
+soulcaliburhentai.net
+soumo.info
+soundcloud.com
+soundofhope.kr
+soundofhope.org
+soup.io
+soupofmedia.com
+sourcewadio.com
+southnews.com.tw
+sowers.org.hk
+soylentnews.org
+spankbang.com
+spankingtube.com
+spankwire.com
+spb.com
+speakerdeck.com
+specxinzl.jigsy.com
+speedify.com
+spem.at
+spencertipping.com
+spicevpn.com
+spideroak.com
+spike.com
+sports.williamhill.com
+spotflux.com
+spotify.com
+spreadshirt.es
+spring4u.info
+springboardplatform.com
+sprite.org
+sproutcore.com
+sproxy.info
+squirly.info
+srocket.us
+ss-link.com
+ss.carryzhou.com
+ss.levyhsu.com
+ss7.vzw.com
+ssglobal.co
+ssglobal.me
+ssh91.com
+ssl.gstatic.com
+ssl.webpack.de
+ssl443.org
+sspro.ml
+ssr.tools
+ssrshare.com
+sss.camp
+sstmlt.moe
+sstmlt.net
+stage64.hk
+standupfortibet.org
+starfishfx.com
+starp2p.com
+startpage.com
+startuplivingchina.com
+stat.gov.tw
+static-economist.com
+static.comico.tw
+static.shemalez.com
+static.soup.io
+static.typepad.com
+static.v2ex.com
+static01.nyt.com
+staticflickr.com
+statueofdemocracy.org
+stc.com.sa
+stdtime.gov.tw
+steamcommunity.com
+steel-storm.com
+steganos.com
+steganos.net
+stepchina.com
+stephaniered.com
+sthoo.com
+stickam.com
+stileproject.com
+sto.cc
+stoporganharvesting.org
+stoptibetcrisis.net
+storage.googleapis.com
+storagenewsletter.com
+store.sprite.org
+stories.google
+storify.com
+storm.mg
+stormmediagroup.com
+stoweboyd.com
+stranabg.com
+straplessdildo.com
+streamingthe.net
+strikingly.com
+strongvpn.com
+strongwindpress.com
+studentsforafreetibet.org
+stumbleupon.com
+stupidvideos.com
+subacme.rerouted.org
+successfn.com
+sugarsync.com
+sugobbs.com
+sugumiru18.com
+suissl.com
+sujiatun.wordpress.com
+sukebei.nyaa.si
+sulian.me
+summify.com
+sumrando.com
+sun1911.com
+sunmedia.ca
+sunmoonlake.gov.tw
+sunporno.com
+sunskyforum.com
+sunta.com.tw
+sunvpn.net
+sunwinism.joinbbs.net
+suoluo.org
+supchina.com
+superfreevpn.com
+superokayama.com
+superpages.com
+supervpn.net
+superzooi.com
+suppig.net
+suprememastertv.com
+surfeasy.com
+suroot.com
+surrenderat20.net
+sustainability.google
+suyangg.com
+svsfx.com
+swagbucks.com
+swissvpn.net
+switch1.jp
+switchvpn.net
+sydneytoday.com
+sylfoundation.org
+syncback.com
+synergyse.com
+sysresccd.org
+sytes.net
+szbbs.net
+szetowah.org.hk
+t-g.com
+t.co
+t.me
+t.orzdream.com
+t35.com
+t66y.com
+taa-usa.org
+taaze.tw
+tabtter.jp
+tacc.cwb.gov.tw
+tacem.org
+taconet.com.tw
+taedp.org.tw
+tafm.org
+tagwa.org.au
+tagwalk.com
+tahr.org.tw
+taipei.gov.tw
+taipeisociety.org
+taitung-house.gov.tw
+taiwan-sex.com
+taiwanbible.com
+taiwancon.com
+taiwandaily.net
+taiwandc.org
+taiwanjobs.gov.tw
+taiwanjustice.com
+taiwanjustice.net
+taiwankiss.com
+taiwannation.50webs.com
+taiwannation.com
+taiwanncf.org.tw
+taiwannews.com.tw
+taiwantp.net
+taiwantt.org.tw
+taiwanus.net
+taiwanyes.com
+taiwanyes.ning.com
+talk853.com
+talkboxapp.com
+talkonly.net
+tamiaode.tk
+tanc.org
+tangben.com
+tangren.us
+taoism.net
+taolun.info
+taoyuan.gov.tw
+tapanwap.com
+tapatalk.com
+tascn.com.au
+taup.net
+taweet.com
+tax.nat.gov.tw
+tbcollege.org
+tbi.org.hk
+tbicn.org
+tbjyt.org
+tbpic.info
+tbrc.org
+tbs-rainbow.org
+tbsec.org
+tbskkinabalu.page.tl
+tbsmalaysia.org
+tbsn.org
+tbsseattle.org
+tbssqh.org
+tbswd.org
+tbtemple.org.uk
+tbthouston.org
+tccwonline.org
+tcewf.org
+tchrd.org
+tcnynj.org
+tcpspeed.co
+tcpspeed.com
+tcsofbc.org
+tcsovi.org
+tdm.com.mo
+teachparentstech.org
+teamamericany.com
+techviz.net
+teck.in
+teco-hk.org
+teco-mo.org
+teddysun.com
+teeniefuck.net
+teensinasia.com
+telecomspace.com
+telegram.dog
+telegram.me
+telegram.org
+telegramdownload.com
+telegraph.co.uk
+telesco.pe
+tellme.pw
+tenacy.com
+tensorflow.org
+tenzinpalmo.com
+terminus2049.github.io
+tew.org
+textnow.me
+th.hao123.com
+thaicn.com
+thb.gov.tw
+theatrum-belli.com
+thebcomplex.com
+theblemish.com
+thebobs.com
+thebodyshop-usa.com
+thecenter.mit.edu
+thechinabeat.org
+thedalailamamovie.com
+thedw.us
+thefacebook.com
+thegly.com
+thehots.info
+thehousenews.com
+thehun.net
+theinitium.com
+thenewslens.com
+thepiratebay.org
+theportalwiki.com
+thereallove.kr
+therock.net.nz
+thespeeder.com
+thestandnews.com
+thetibetcenter.org
+thetibetconnection.org
+thetibetmuseum.org
+thetibetpost.com
+thetinhat.com
+thetrotskymovie.com
+thevivekspot.com
+thewgo.org
+theync.com
+thinkingtaiwan.com
+thinkwithgoogle.com
+thisav.com
+thlib.org
+thomasbernhard.org
+thongdreams.com
+threatchaos.com
+throughnightsfire.com
+thumbzilla.com
+thywords.com
+tiananmenduizhi.com
+tiananmenmother.org
+tiananmenuniv.com
+tiananmenuniv.net
+tiandixing.org
+tianhuayuan.com
+tianlawoffice.com
+tianti.io
+tiantibooks.org
+tianyantong.org.cn
+tianzhu.org
+tibet-envoy.eu
+tibet-foundation.org
+tibet-house-trust.co.uk
+tibet-info.net
+tibet-initiative.de
+tibet-munich.de
+tibet.a.se
+tibet.at
+tibet.ca
+tibet.com
+tibet.fr
+tibet.net
+tibet.nu
+tibet.org
+tibet.sk
+tibet.to
+tibet3rdpole.org
+tibetaction.net
+tibetaid.org
+tibetalk.com
+tibetan-alliance.org
+tibetan.fr
+tibetanaidproject.org
+tibetanarts.org
+tibetanbuddhistinstitute.org
+tibetancommunity.org
+tibetancommunityuk.net
+tibetanculture.org
+tibetanfeministcollective.org
+tibetanjournal.com
+tibetanlanguage.org
+tibetanliberation.org
+tibetanpaintings.com
+tibetanphotoproject.com
+tibetanpoliticalreview.org
+tibetanreview.net
+tibetansports.org
+tibetanwomen.org
+tibetanyouth.org
+tibetanyouthcongress.org
+tibetcharity.dk
+tibetcharity.in
+tibetchild.org
+tibetcity.com
+tibetcollection.com
+tibetcorps.org
+tibetexpress.net
+tibetfocus.com
+tibetfund.org
+tibetgermany.com
+tibetgermany.de
+tibethaus.com
+tibetheritagefund.org
+tibethouse.jp
+tibethouse.org
+tibethouse.us
+tibetinfonet.net
+tibetjustice.org
+tibetkomite.dk
+tibetlibre.free.fr
+tibetmuseum.org
+tibetnetwork.org
+tibetoffice.ch
+tibetoffice.com.au
+tibetoffice.eu
+tibetoffice.org
+tibetonline.com
+tibetonline.tv
+tibetoralhistory.org
+tibetpolicy.eu
+tibetrelieffund.co.uk
+tibetsites.com
+tibetsociety.com
+tibetsun.com
+tibetsupportgroup.org
+tibetswiss.ch
+tibettelegraph.com
+tibettimes.net
+tibetwrites.org
+ticket.com.tw
+tigervpn.com
+tiltbrush.com
+timdir.com
+time.com
+times.hinet.net
+timsah.com
+tineye.com
+tintuc101.com
+tiny.cc
+tinychat.com
+tinypaste.com
+tipo.gov.tw
+tistory.com
+tkcs-collins.com
+tma.co.jp
+tmagazine.com
+tmdfish.com
+tmi.me
+tmpp.org
+tn1.shemalez.com
+tn2.shemalez.com
+tn3.shemalez.com
+tnaflix.com
+tngrnow.com
+tngrnow.net
+tnp.org
+to-porno.com
+togetter.com
+toh.info
+tokyo-247.com
+tokyo-hot.com
+tokyo-porn-tube.com
+tokyocn.com
+tongil.or.kr
+tono-oka.jp
+tonyyan.net
+toodoc.com
+tools.google.com
+toonel.net
+top.tv
+top81.ws
+topbtc.com
+topic.youthwant.com.tw
+topnews.in
+toppornsites.com
+topshareware.com
+topsy.com
+toptip.ca
+tor.blingblingsquad.net
+tor.updatestar.com
+tora.to
+torcn.com
+torguard.net
+torproject.org
+torrentprivacy.com
+torrentproject.se
+torrenty.org
+torrentz.eu
+torvpn.com
+tosh.comedycentral.com
+totalvpn.com
+toutiaoabc.com
+towngain.com
+toypark.in
+toythieves.com
+toytractorshow.com
+tparents.org
+tpde.aide.gov.tw
+tphcc.gov.tw
+tpi.org.tw
+tracfone.com
+traffichaus.com
+translate.google.cn
+translate.googleapis.com
+transparency.org
+treemall.com.tw
+trendsmap.com
+trialofccp.org
+trickip.net
+trickip.org
+trimt-nsa.gov.tw
+trouw.nl
+trt.net.tr
+trtc.com.tw
+truebuddha-md.org
+trulyergonomic.com
+truth101.co.tv
+truthontour.org
+truveo.com
+tryheart.jp
+tsctv.net
+tsdr.uspto.gov
+tsemtulku.com
+tsquare.tv
+tsu.org.tw
+tsunagarumon.com
+tt-rss.org
+tt1069.com
+tttan.com
+ttvnw.net
+tu8964.com
+tubaholic.com
+tube.com
+tube8.com
+tube911.com
+tubecup.com
+tubegals.com
+tubeislam.com
+tubepornclassic.com
+tubestack.com
+tubewolf.com
+tui.orzdream.com
+tuibeitu.net
+tuidang.net
+tuidang.org
+tuidang.se
+tuitwit.com
+tumblr.com
+tumutanzi.com
+tumview.com
+tunein.com
+tunnelbear.com
+tunnelr.com
+tuo8.blue
+tuo8.cc
+tuo8.club
+tuo8.fit
+tuo8.hk
+tuo8.in
+tuo8.ninja
+tuo8.org
+tuo8.pw
+tuo8.red
+tuo8.space
+turansam.org
+turbobit.net
+turbohide.com
+turbotwitter.com
+turntable.fm
+tushycash.com
+tuvpn.com
+tuzaijidi.com
+tv.com
+tvants.com
+tvboxnow.com
+tvider.com
+tvmost.com.hk
+tvplayvideos.com
+tvunetworks.com
+tw-blog.com
+tw-npo.org
+tw.answers.yahoo.com
+tw.bid.yahoo.com
+tw.gigacircle.com
+tw.hao123.com
+tw.jiepang.com
+tw.knowledge.yahoo.com
+tw.mall.yahoo.com
+tw.mobi.yahoo.com
+tw.money.yahoo.com
+tw.myblog.yahoo.com
+tw.news.yahoo.com
+tw.streetvoice.com
+tw.tomonews.net
+tw.voa.mobi
+tw.yahoo.com
+tw01.org
+twaitter.com
+twapperkeeper.com
+twaud.io
+twavi.com
+twbbs.net.tw
+twbbs.org
+twbbs.tw
+twblogger.com
+tweepguide.com
+tweeplike.me
+tweepmag.com
+tweepml.org
+tweetbackup.com
+tweetboard.com
+tweetboner.biz
+tweetcs.com
+tweetdeck.com
+tweetedtimes.com
+tweetmylast.fm
+tweetphoto.com
+tweetrans.com
+tweetree.com
+tweettunnel.com
+tweetwally.com
+tweetymail.com
+tweez.net
+twelve.today
+twerkingbutt.com
+twftp.org
+twgreatdaily.com
+twibase.com
+twibble.de
+twibbon.com
+twibs.com
+twicountry.org
+twicsy.com
+twiends.com
+twifan.com
+twiffo.com
+twiggit.org
+twilightsex.com
+twilog.org
+twimbow.com
+twimg.com
+twindexx.com
+twip.me
+twipple.jp
+twishort.com
+twistar.cc
+twister.net.co
+twisterio.com
+twisternow.com
+twistory.net
+twit2d.com
+twitbrowser.net
+twitcause.com
+twitch.tv
+twitchcdn.net
+twitgether.com
+twitgoo.com
+twitiq.com
+twitlonger.com
+twitmania.com
+twitoaster.com
+twitonmsn.com
+twitpic.com
+twitstat.com
+twittbot.net
+twitter.com
+twitter.jp
+twitter4j.org
+twittercounter.com
+twitterfeed.com
+twittergadget.com
+twitterkr.com
+twittermail.com
+twitterrific.com
+twittertim.es
+twitthat.com
+twitturk.com
+twitturly.com
+twitvid.com
+twitzap.com
+twiyia.com
+twnorth.org.tw
+twskype.com
+twstar.net
+twt.tl
+twtkr.com
+twtr2src.ogaoga.org
+twtrland.com
+twttr.com
+twurl.nl
+twyac.org
+txxx.com
+tycool.com
+typepad.com
+u9un.com
+ub0.cc
+ubddns.org
+uberproxy.net
+uc-japan.org
+ucdc1998.org
+uderzo.it
+udn.com
+uforadio.com.tw
+ufreevpn.com
+ug.m.wikipedia.org
+ugo.com
+uhdwallpapers.org
+uhrp.org
+uighur.narod.ru
+uighur.nl
+uighurbiz.net
+ukcdp.co.uk
+ukliferadio.co.uk
+uku.im
+ulike.net
+ulop.net
+ultravpn.fr
+ultraxs.com
+uluai.com.cn
+unblock-us.com
+unblock.cn.com
+unblockdmm.com
+unblocker.yt
+unblocksit.es
+uncyclomedia.org
+uncyclopedia.hk
+uncyclopedia.tw
+underwoodammo.com
+unholyknight.com
+uni.cc
+unification.net
+unification.org.tw
+unirule.cloud
+unitedsocialpress.com
+unix100.com
+unknownspace.org
+unodedos.com
+unpo.org
+unseen.is
+untraceable.us
+uocn.org
+upcoming.yahoo.com
+update.googleapis.com
+updates.tdesktop.com
+upholdjustice.org
+upload4u.info
+upmedia.mg
+upornia.com
+uproxy.org
+upwill.org
+ur7s.com
+uraban.me
+urbansurvival.com
+urchin.com
+urlborg.com
+urlparser.com
+us.to
+usacn.com
+usaip.eu
+userapi.nytlog.com
+usfk.mil
+ushuarencity.echainhost.com
+usinfo.state.gov
+usma.edu
+usmc.mil
+usno.navy.mil
+usocctn.com
+ustream.tv
+usunitednews.com
+usus.cc
+utopianpal.com
+uu-gg.com
+uvwxyz.xyz
+uwants.com
+uwants.net
+uyghur-j.org
+uyghur.co.uk
+uyghuramerican.org
+uyghurcanadiansociety.org
+uyghurcongress.org
+uyghurensemble.co.uk
+uyghurpen.org
+uyghurpress.com
+uyghurstudies.org
+uygur.org
+v2ex.com
+v2ray.com
+v6.facebook.com
+van001.com
+van698.com
+vanemu.cn
+vanilla-jp.com
+vanpeople.com
+vansky.com
+vatn.org
+vcf-online.org
+vcfbuilder.org
+vegas.williamhill.com
+vegasred.com
+velkaepocha.sk
+venbbs.com
+venchina.com
+venetianmacao.com
+ventureswell.com
+veoh.com
+vermonttibet.org
+versavpn.com
+verybs.com
+vevo.com
+vft.com.tw
+vghks.gov.tw
+vghtc.gov.tw
+vghtpe.gov.tw
+viber.com
+vica.info
+victimsofcommunism.org
+vid.me
+vidble.com
+video.aol.com
+video.ap.org
+video.fdbox.com
+video.foxbusiness.com
+video.pbs.org
+video.yahoo.com
+videobam.com
+videodetective.com
+videomega.tv
+videomo.com
+videopediaworld.com
+videopress.com
+vietdaikynguyen.com
+vijayatemple.org
+vimeo.com
+vimperator.org
+vincnd.com
+vine.co
+vinniev.com
+vip-enterprise.com
+virtualrealporn.com
+visibletweets.com
+vital247.org
+viu.com
+vivahentai4u.net
+vivatube.com
+vivthomas.com
+vizvaz.com
+vjav.com
+vjmedia.com.hk
+vllcs.org
+vlog.xuite.net
+vmixcore.com
+vmpsoft.com
+vn.hao123.com
+vnet.link
+voa-11.akacast.akamaistream.net
+voacantonese.com
+voachinese.com
+voachineseblog.com
+voagd.com
+voanews.com
+voatibetan.com
+voatibetanenglish.com
+vocativ.com
+vocn.tv
+vod-abematv.akamaized.net
+vod.wwe.com
+vot.org
+vovo2000.com
+voxer.com
+voy.com
+vpn.ac
+vpn.cjb.net
+vpn.cmu.edu
+vpn.sv.cmu.edu
+vpn4all.com
+vpnaccount.org
+vpnaccounts.com
+vpnbook.com
+vpncomparison.org
+vpncoupons.com
+vpncup.com
+vpndada.com
+vpnfan.com
+vpnfire.com
+vpnfires.biz
+vpnforgame.net
+vpngate.jp
+vpngate.net
+vpngratis.net
+vpnhq.com
+vpninja.net
+vpnintouch.com
+vpnintouch.net
+vpnjack.com
+vpnmaster.com
+vpnmentor.com
+vpnpick.com
+vpnpop.com
+vpnpronet.com
+vpnreactor.com
+vpnreviewz.com
+vpnsecure.me
+vpnshazam.com
+vpnshieldapp.com
+vpnsp.com
+vpntraffic.com
+vpntunnel.com
+vpnuk.info
+vpnunlimitedapp.com
+vpnvip.com
+vpnworldwide.com
+vporn.com
+vpser.net
+vraiesagesse.net
+vrmtr.com
+vrsmash.com
+vtunnel.com
+vuku.cc
+w3schools.com
+waffle1999.com
+wahas.com
+waigaobu.com
+wailaike.net
+waiwaier.com
+wallmama.com
+wallornot.org
+wallpapercasa.com
+wallproxy.com
+waltermartin.com
+waltermartin.org
+wanderinghorse.net
+wanfang.gov.tw
+wangafu.net
+wangjinbo.org
+wanglixiong.com
+wango.org
+wangruoshui.net
+want-daily.com
+wanz-factory.com
+warbler.iconfactory.net
+waselpro.com
+washeng.net
+watch8x.com
+watchinese.com
+watchmygf.net
+wattpad.com
+wav.tv
+waveprotocol.org
+waymo.com
+wda.gov.tw
+wdf5.com
+wearehairy.com
+wearn.com
+web.dev
+web2project.net
+webbang.net
+webevader.org
+webfreer.com
+webjb.org
+weblagu.com
+webmproject.org
+webrtc.org
+webrush.net
+webs-tv.net
+website.informer.com
+webwarper.net
+webworkerdaily.com
+weekmag.info
+wefightcensorship.org
+wefong.com
+wego.here.com
+weiboleak.com
+weiboscope.jmsc.hku.hk
+weihuo.org
+weijingsheng.org
+weiming.info
+weiquanwang.org
+weisuo.ws
+welovecock.com
+wemigrate.org
+wengewang.com
+wengewang.org
+wenhui.ch
+wenxuecity.com
+wenyunchao.com
+wenzhao.ca
+westca.com
+westernshugdensociety.org
+westernwolves.com
+westkit.net
+westpoint.edu
+wetplace.com
+wetpussygames.com
+wexiaobo.org
+wezhiyong.org
+wezone.net
+wforum.com
+wha.la
+whatblocked.com
+whatbrowser.org
+whatsapp.com
+whatsapp.net
+wheatseeds.org
+wheelockslatin.com
+whereiswerner.com
+wheretowatch.com
+whippedass.com
+whitebear.freebearblog.org
+whodns.xyz
+whoer.net
+whotalking.com
+whylover.com
+whyx.org
+widevine.com
+wikaba.com
+wiki.cnitter.com
+wiki.gamerp.jp
+wiki.jqueryui.com
+wiki.moegirl.org
+wiki.oauth.net
+wiki.phonegap.com
+wikileaks-forum.com
+wikileaks.ch
+wikileaks.com
+wikileaks.de
+wikileaks.eu
+wikileaks.lu
+wikileaks.org
+wikileaks.pl
+wikimapia.org
+wikiwiki.jp
+wildammo.com
+williamhill.com
+willw.net
+windowsphoneme.com
+windscribe.com
+wingamestore.com
+wingy.site
+winning11.com
+winwhispers.info
+wire.com
+wiredbytes.com
+wiredpen.com
+wisdompubs.org
+wisevid.com
+withgoogle.com
+withyoutube.com
+witnessleeteaching.com
+witopia.net
+wizcrafts.net
+wjbk.org
+wlcnew.jigsy.com
+wlx.sowiki.net
+wn.com
+wnacg.com
+wnacg.org
+wo.tc
+wo3ttt.wordpress.com
+woeser.com
+wokar.org
+wolfax.com
+woolyss.com
+woopie.jp
+woopie.tv
+wordpress.com
+workatruna.com
+workerdemo.org.hk
+workerempowerment.org
+workersthebig.net
+workspace.v2ex.com
+worldcat.org
+worldjournal.com
+worldvpn.net
+wow-life.net
+wow.com
+wowgirls.com
+wowlegacy.ml
+wowporn.com
+wowrk.com
+woxinghuiguo.com
+woyaolian.org
+wozy.in
+wp.com
+wpoforum.com
+wqyd.org
+wrchina.org
+wretch.cc
+writer.zoho.com
+wsgzao.github.io
+wsj.com
+wsj.net
+wsjhk.com
+wtbn.org
+wtfpeople.com
+wuerkaixi.com
+wufafangwen.com
+wufi.org.tw
+wuguoguang.com
+wujie.net
+wujieliulan.com
+wukangrui.net
+wuu.wikipedia.org
+wuw.red
+wuyanblog.com
+wwitv.com
+www.abclite.net
+www.ajsands.com
+www.americorps.gov
+www.antd.org
+www.aolnews.com
+www.citizenlab.org
+www.cmoinc.org
+www.dwheeler.com
+www.exblog.jp
+www.ftchinese.com
+www.gmiddle.com
+www.gmiddle.net
+www.hustlercash.com
+www.idlcoyote.com
+www.kindleren.com
+www.klip.me
+www.kodingen.com
+www.lamenhu.com
+www.lightnovel.cn
+www.linksalpha.com
+www.m-sport.co.uk
+www.monlamit.org
+www.moztw.org
+www.orchidbbs.com
+www.owind.com
+www.oxid.it
+www.powerpointninja.com
+www.s4miniarchive.com
+www.shadowsocks.com
+www.shwchurch.org
+www.taup.org.tw
+www.typepad.com
+www.v2ex.com
+www.wan-press.org
+www.wangruowang.org
+www.websnapr.com
+www1.biz
+wwwhost.biz
+x-art.com
+x-berry.com
+x-wall.org
+x.company
+x1949x.com
+x24hr.com
+x365x.com
+xa.yimg.com
+xanga.com
+xbabe.com
+xbookcn.com
+xbtce.com
+xcafe.in
+xcity.jp
+xcritic.com
+xerotica.com
+xfinity.com
+xfm.pp.ru
+xgmyd.com
+xhamster.com
+xianba.net
+xianchawang.net
+xianjian.tw
+xianqiao.net
+xiaobaiwu.com
+xiaochuncnjp.com
+xiaod.in
+xiaohexie.com
+xiaolan.me
+xiaoma.org
+xiezhua.com
+xihua.es
+xijie.wordpress.com
+xing.com
+xinhuanet.org
+xinmiao.com.hk
+xinqimeng.over-blog.com
+xinsheng.net
+xinshijue.com
+xinyubbs.net
+xiongpian.com
+xiuren.org
+xizang-zhiye.org
+xjp.cc
+xjtravelguide.com
+xlfmtalk.com
+xlfmwz.info
+xm.com
+xml-training-guide.com
+xmovies.com
+xn--4gq171p.com
+xn--czq75pvv1aj5c.org
+xn--i2ru8q2qg.com
+xn--ngstr-lra8j.com
+xn--oiq.cc
+xn--p8j9a0d9c9a.xn--q9jyb4c
+xnxx.com
+xpdo.net
+xpud.org
+xrentdvd.com
+xskywalker.com
+xskywalker.net
+xtube.com
+xuchao.net
+xuchao.org
+xuehua.us
+xuzhiyong.net
+xvideo.cc
+xvideos.com
+xvideos.es
+xx.wwwhost.biz
+xxbbx.com
+xxlmovies.com
+xxuz.com
+xxx.com
+xxx.xxx
+xxxfuckmom.com
+xxxx.com.au
+xxxy.biz
+xxxy.info
+xxxymovies.com
+xys.dxiong.com
+xys.org
+xysblogs.org
+xyy69.com
+xyy69.info
+yahoo.cn
+yahoo.com.hk
+yakbutterblues.com
+yam.com
+yam.org.tw
+yanghengjun.com
+yangjianli.com
+yasni.co.uk
+yatsen.gov.tw
+yda.gov.tw
+ydy.com
+yeahteentube.com
+yecl.net
+yeelou.com
+yeeyi.com
+yegle.net
+yes-news.com
+yes.xxx
+yes123.com.tw
+yesasia.com
+yespornplease.com
+yeyeclub.com
+ygto.com
+yhcw.net
+yibada.com
+yibaochina.com
+yidio.com
+yilubbs.com
+yingsuoss.com
+yipub.com
+yizhihongxing.com
+yobit.net
+yobt.com
+yobt.tv
+yogichen.org
+yolasite.com
+yomiuri.co.jp
+yong.hu
+yorkbbs.ca
+youdontcare.com
+youjizz.com
+youmaker.com
+youngpornvideos.com
+youngspiration.hk
+youpai.org
+youporn.com
+youporngay.com
+your-freedom.net
+yourepeat.com
+yourlisten.com
+yourlust.com
+yourprivatevpn.com
+yourtrap.com
+yousendit.com
+youshun12.com
+youtu.be
+youtube-nocookie.com
+youtube.com
+youtubecn.com
+youtubeeducation.com
+youtubegaming.com
+youversion.com
+youwin.com
+youxu.info
+yt.be
+ytht.net
+ytimg.com
+ytn.co.kr
+yuanming.net
+yuanzhengtang.org
+yulghun.com
+yunchao.net
+yuntipub.com
+yuvutu.com
+yvesgeleyn.com
+yx51.net
+yyii.org
+yzzk.com
+zacebook.com
+zalmos.com
+zannel.com
+zaobao.com
+zaozon.com
+zapto.org
+zattoo.com
+zb.com
+zdnet.com.tw
+zello.com
+zengjinyan.org
+zenmate.com
+zeronet.io
+zeutch.com
+zfreet.com
+zgsddh.com
+zgzcjj.net
+zh-yue.wikipedia.org
+zh.bitterwinter.org
+zh.ecdm.wikia.com
+zh.m.wikipedia.org
+zh.pokerstrategy.com
+zh.uncyclopedia.wikia.com
+zh.wikinews.org
+zh.wikipedia.org
+zh.wikisource.org
+zhanbin.net
+zhangboli.net
+zhangtianliang.com
+zhanlve.org
+zhao.1984.city
+zhao.jinhai.de
+zhenghui.org
+zhengjian.org
+zhengwunet.org
+zhenlibu.info
+zhenlibu1984.com
+zhenxiang.biz
+zhinengluyou.com
+zhongguo.ca
+zhongguorenquan.org
+zhongguotese.net
+zhongmeng.org
+zhoushuguang.com
+zhreader.com
+zhuangbi.me
+zhuanxing.cn
+zhuatieba.com
+zhuichaguoji.org
+zillionk.com
+zim.vn
+zinio.com
+ziporn.com
+zippyshare.com
+zkaip.com
+zmw.cn
+zodgame.us
+zomobo.net
+zonaeuropa.com
+zonghexinwen.com
+zonghexinwen.net
+zoogvpn.com
+zootool.com
+zoozle.net
+zorrovpn.com
+zozotown.com
+zpn.im
+zspeeder.me
+zsrhao.com
+zuo.la
+zuobiao.me
+zuola.com
+zvereff.com
+zynaima.com
+zynamics.com
+zyns.com
+zyzc9.com
+zzcartoon.com
+zzux.com
diff --git a/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt
new file mode 100644
index 0000000000..21021c28ae
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt
@@ -0,0 +1,94 @@
+42.196.0.0/14
+43.242.56.0/22
+43.242.144.0/22
+43.243.148.0/22
+43.247.208.0/22
+45.252.152.0/22
+45.252.200.0/22
+45.252.204.0/22
+45.252.208.0/22
+45.252.212.0/22
+45.252.216.0/22
+45.252.220.0/22
+45.252.224.0/22
+45.253.0.0/22
+45.253.4.0/22
+45.253.8.0/22
+45.253.12.0/22
+45.253.16.0/22
+45.253.20.0/22
+45.253.28.0/22
+45.253.36.0/22
+45.253.40.0/22
+45.253.56.0/22
+45.253.76.0/22
+45.253.80.0/22
+45.253.84.0/22
+45.253.92.0/22
+45.254.28.0/22
+45.254.56.0/22
+45.254.96.0/22
+45.254.132.0/22
+45.254.152.0/22
+45.254.180.0/22
+45.254.200.0/22
+45.254.204.0/22
+45.254.208.0/22
+45.254.212.0/22
+45.254.216.0/22
+45.254.220.0/22
+45.254.224.0/22
+45.254.228.0/22
+49.220.0.0/14
+101.130.0.0/15
+101.244.0.0/14
+103.23.56.0/22
+103.24.184.0/22
+103.38.0.0/22
+103.38.232.0/22
+103.221.88.0/22
+103.221.132.0/22
+103.221.156.0/22
+103.221.180.0/22
+103.221.184.0/22
+103.221.188.0/22
+103.221.192.0/22
+103.221.196.0/22
+103.222.0.0/22
+103.222.4.0/22
+103.222.8.0/22
+103.222.12.0/22
+103.222.16.0/22
+103.222.24.0/22
+103.222.28.0/22
+103.222.36.0/22
+103.222.44.0/22
+103.222.48.0/22
+103.222.56.0/22
+103.222.60.0/22
+103.222.64.0/22
+103.222.68.0/22
+103.222.72.0/22
+103.222.76.0/22
+103.222.80.0/22
+103.222.84.0/22
+103.222.88.0/22
+103.222.92.0/22
+103.222.96.0/22
+103.222.100.0/22
+103.222.104.0/22
+103.222.108.0/22
+103.222.200.0/22
+103.222.204.0/22
+103.222.208.0/22
+103.222.212.0/22
+115.190.0.0/15
+124.14.0.0/15
+124.254.0.0/18
+175.188.0.0/14
+180.88.0.0/14
+203.82.112.0/22
+203.86.64.0/20
+211.164.0.0/14
+220.112.0.0/14
+223.208.0.0/14
diff --git a/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt
new file mode 100644
index 0000000000..fdf3ade440
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt
@@ -0,0 +1,5134 @@
+1.1.8.0/24
+1.2.2.0/24
+1.2.4.0/24
+1.2.8.0/24
+1.4.4.0/24
+1.8.0.0/16
+1.12.0.0/14
+1.45.0.0/16
+1.88.0.0/14
+1.92.0.0/15
+1.94.0.0/15
+1.118.0.0/16
+1.119.0.0/17
+1.119.128.0/17
+14.1.24.0/22
+14.1.96.0/22
+14.1.108.0/22
+14.102.156.0/22
+14.102.180.0/22
+14.103.0.0/16
+14.130.0.0/15
+14.192.60.0/22
+14.196.0.0/15
+27.0.160.0/22
+27.0.164.0/22
+27.0.188.0/22
+27.0.204.0/22
+27.0.208.0/22
+27.0.212.0/22
+27.98.208.0/20
+27.99.128.0/17
+27.103.0.0/16
+27.106.128.0/18
+27.106.204.0/22
+27.109.32.0/19
+27.109.124.0/22
+27.112.80.0/20
+27.112.112.0/22
+27.112.116.0/22
+27.113.128.0/18
+27.116.44.0/22
+27.123.232.0/22
+27.131.220.0/22
+27.144.0.0/16
+36.0.8.0/21
+36.0.64.0/18
+36.0.128.0/17
+36.36.0.0/16
+36.51.0.0/16
+36.254.0.0/16
+36.255.116.0/22
+36.255.128.0/22
+36.255.164.0/22
+36.255.172.0/22
+36.255.176.0/22
+36.255.220.0/22
+39.96.0.0/13
+39.104.0.0/14
+39.108.0.0/16
+40.72.0.0/15
+40.125.128.0/17
+40.126.64.0/18
+42.0.128.0/17
+42.50.0.0/16
+42.62.0.0/17
+42.83.128.0/17
+42.96.128.0/17
+42.120.0.0/15
+42.123.128.0/17
+42.128.0.0/12
+42.156.128.0/17
+42.157.0.0/16
+42.158.0.0/16
+42.159.0.0/16
+42.160.0.0/12
+42.186.0.0/16
+42.187.128.0/17
+42.192.0.0/15
+42.194.12.0/22
+42.194.128.0/17
+42.195.0.0/16
+42.201.0.0/17
+42.204.0.0/14
+42.208.0.0/12
+42.240.0.0/17
+42.240.128.0/17
+43.224.44.0/22
+43.224.64.0/22
+43.224.68.0/22
+43.224.72.0/22
+43.224.100.0/22
+43.224.144.0/22
+43.224.176.0/22
+43.224.184.0/22
+43.224.200.0/22
+43.224.204.0/22
+43.224.212.0/22
+43.224.216.0/22
+43.224.240.0/22
+43.225.76.0/22
+43.225.84.0/22
+43.225.124.0/22
+43.225.140.0/22
+43.225.172.0/22
+43.225.180.0/22
+43.225.208.0/22
+43.225.216.0/22
+43.225.220.0/22
+43.225.224.0/22
+43.225.228.0/22
+43.225.232.0/22
+43.225.236.0/22
+43.225.240.0/22
+43.225.244.0/22
+43.225.252.0/22
+43.226.32.0/22
+43.226.36.0/22
+43.226.40.0/22
+43.226.44.0/22
+43.226.48.0/22
+43.226.52.0/22
+43.226.56.0/22
+43.226.60.0/22
+43.226.64.0/22
+43.226.68.0/22
+43.226.72.0/22
+43.226.76.0/22
+43.226.80.0/22
+43.226.84.0/22
+43.226.88.0/22
+43.226.92.0/22
+43.226.112.0/22
+43.226.128.0/22
+43.226.132.0/22
+43.226.136.0/22
+43.226.140.0/22
+43.226.144.0/22
+43.226.148.0/22
+43.226.152.0/22
+43.226.156.0/22
+43.226.168.0/22
+43.226.172.0/22
+43.226.176.0/22
+43.226.180.0/22
+43.226.184.0/22
+43.226.188.0/22
+43.226.192.0/22
+43.226.196.0/22
+43.226.200.0/22
+43.226.204.0/22
+43.226.208.0/22
+43.226.212.0/22
+43.226.240.0/22
+43.226.244.0/22
+43.226.248.0/22
+43.226.252.0/22
+43.227.0.0/22
+43.227.4.0/22
+43.227.8.0/22
+43.227.32.0/22
+43.227.36.0/22
+43.227.40.0/22
+43.227.44.0/22
+43.227.48.0/22
+43.227.52.0/22
+43.227.56.0/22
+43.227.60.0/22
+43.227.64.0/22
+43.227.68.0/22
+43.227.72.0/22
+43.227.76.0/22
+43.227.80.0/22
+43.227.84.0/22
+43.227.88.0/22
+43.227.92.0/22
+43.227.96.0/22
+43.227.100.0/22
+43.227.136.0/22
+43.227.140.0/22
+43.227.144.0/22
+43.227.152.0/22
+43.227.156.0/22
+43.227.160.0/22
+43.227.164.0/22
+43.227.168.0/22
+43.227.172.0/22
+43.227.176.0/22
+43.227.180.0/22
+43.227.188.0/22
+43.227.192.0/22
+43.227.196.0/22
+43.227.200.0/22
+43.227.204.0/22
+43.227.208.0/22
+43.227.212.0/22
+43.227.216.0/22
+43.227.220.0/22
+43.227.232.0/22
+43.227.248.0/22
+43.227.252.0/22
+43.228.0.0/22
+43.228.4.0/22
+43.228.8.0/22
+43.228.12.0/22
+43.228.16.0/22
+43.228.20.0/22
+43.228.24.0/22
+43.228.28.0/22
+43.228.32.0/22
+43.228.36.0/22
+43.228.40.0/22
+43.228.44.0/22
+43.228.48.0/22
+43.228.52.0/22
+43.228.56.0/22
+43.228.60.0/22
+43.228.64.0/22
+43.228.68.0/22
+43.228.76.0/22
+43.228.100.0/22
+43.228.116.0/22
+43.228.120.0/22
+43.228.132.0/22
+43.228.136.0/22
+43.228.148.0/22
+43.228.152.0/22
+43.228.188.0/22
+43.229.16.0/22
+43.229.40.0/22
+43.229.48.0/22
+43.229.56.0/22
+43.229.96.0/22
+43.229.120.0/22
+43.229.136.0/22
+43.229.140.0/22
+43.229.144.0/22
+43.229.184.0/22
+43.229.188.0/22
+43.229.192.0/22
+43.229.196.0/22
+43.229.216.0/22
+43.229.220.0/22
+43.229.232.0/22
+43.230.20.0/22
+43.230.32.0/22
+43.230.68.0/22
+43.230.72.0/22
+43.230.84.0/22
+43.230.124.0/22
+43.230.136.0/22
+43.230.168.0/22
+43.230.220.0/22
+43.230.224.0/22
+43.230.228.0/22
+43.230.232.0/22
+43.230.236.0/22
+43.230.240.0/22
+43.230.244.0/22
+43.230.248.0/22
+43.230.252.0/22
+43.231.32.0/22
+43.231.36.0/22
+43.231.40.0/22
+43.231.44.0/22
+43.231.80.0/22
+43.231.84.0/22
+43.231.88.0/22
+43.231.92.0/22
+43.231.96.0/22
+43.231.100.0/22
+43.231.104.0/22
+43.231.108.0/22
+43.231.136.0/22
+43.231.140.0/22
+43.231.144.0/22
+43.231.148.0/22
+43.231.152.0/22
+43.231.156.0/22
+43.231.160.0/22
+43.231.164.0/22
+43.231.168.0/22
+43.231.172.0/22
+43.231.176.0/22
+43.231.180.0/22
+43.236.0.0/22
+43.236.4.0/22
+43.236.8.0/22
+43.236.12.0/22
+43.236.16.0/22
+43.236.20.0/22
+43.236.24.0/22
+43.236.28.0/22
+43.236.32.0/22
+43.236.36.0/22
+43.236.40.0/22
+43.236.44.0/22
+43.236.48.0/22
+43.236.52.0/22
+43.236.56.0/22
+43.236.60.0/22
+43.236.64.0/22
+43.236.68.0/22
+43.236.72.0/22
+43.236.76.0/22
+43.236.80.0/22
+43.236.84.0/22
+43.236.88.0/22
+43.236.92.0/22
+43.236.96.0/22
+43.236.100.0/22
+43.236.104.0/22
+43.236.108.0/22
+43.236.112.0/22
+43.236.116.0/22
+43.236.120.0/22
+43.236.124.0/22
+43.236.128.0/22
+43.236.132.0/22
+43.236.136.0/22
+43.236.140.0/22
+43.236.144.0/22
+43.236.148.0/22
+43.236.152.0/22
+43.236.156.0/22
+43.236.160.0/22
+43.236.164.0/22
+43.236.168.0/22
+43.236.172.0/22
+43.236.176.0/22
+43.236.180.0/22
+43.236.184.0/22
+43.236.188.0/22
+43.236.192.0/22
+43.236.196.0/22
+43.236.200.0/22
+43.236.204.0/22
+43.236.208.0/22
+43.236.212.0/22
+43.236.216.0/22
+43.236.220.0/22
+43.236.224.0/22
+43.236.228.0/22
+43.236.232.0/22
+43.236.236.0/22
+43.236.240.0/22
+43.236.244.0/22
+43.236.248.0/22
+43.236.252.0/22
+43.237.0.0/22
+43.237.4.0/22
+43.237.8.0/22
+43.237.12.0/22
+43.237.16.0/22
+43.237.20.0/22
+43.237.24.0/22
+43.237.28.0/22
+43.237.32.0/22
+43.237.36.0/22
+43.237.40.0/22
+43.237.44.0/22
+43.237.48.0/22
+43.237.52.0/22
+43.237.56.0/22
+43.237.60.0/22
+43.237.64.0/22
+43.237.68.0/22
+43.237.72.0/22
+43.237.76.0/22
+43.237.80.0/22
+43.237.84.0/22
+43.237.88.0/22
+43.237.92.0/22
+43.237.96.0/22
+43.237.100.0/22
+43.237.104.0/22
+43.237.108.0/22
+43.237.112.0/22
+43.237.116.0/22
+43.237.120.0/22
+43.237.124.0/22
+43.237.128.0/22
+43.237.132.0/22
+43.237.136.0/22
+43.237.140.0/22
+43.237.144.0/22
+43.237.148.0/22
+43.237.152.0/22
+43.237.156.0/22
+43.237.160.0/22
+43.237.164.0/22
+43.237.168.0/22
+43.237.172.0/22
+43.237.176.0/22
+43.237.180.0/22
+43.237.184.0/22
+43.237.188.0/22
+43.237.192.0/22
+43.237.196.0/22
+43.237.200.0/22
+43.237.204.0/22
+43.237.208.0/22
+43.237.212.0/22
+43.237.216.0/22
+43.237.220.0/22
+43.237.224.0/22
+43.237.228.0/22
+43.237.232.0/22
+43.237.236.0/22
+43.237.240.0/22
+43.237.244.0/22
+43.237.248.0/22
+43.237.252.0/22
+43.238.0.0/22
+43.238.4.0/22
+43.238.8.0/22
+43.238.12.0/22
+43.238.16.0/22
+43.238.20.0/22
+43.238.24.0/22
+43.238.28.0/22
+43.238.32.0/22
+43.238.36.0/22
+43.238.40.0/22
+43.238.44.0/22
+43.238.48.0/22
+43.238.52.0/22
+43.238.56.0/22
+43.238.60.0/22
+43.238.64.0/22
+43.238.68.0/22
+43.238.72.0/22
+43.238.76.0/22
+43.238.80.0/22
+43.238.84.0/22
+43.238.88.0/22
+43.238.92.0/22
+43.238.96.0/22
+43.238.100.0/22
+43.238.104.0/22
+43.238.108.0/22
+43.238.112.0/22
+43.238.116.0/22
+43.238.120.0/22
+43.238.124.0/22
+43.238.128.0/22
+43.238.132.0/22
+43.238.136.0/22
+43.238.140.0/22
+43.238.144.0/22
+43.238.148.0/22
+43.238.152.0/22
+43.238.156.0/22
+43.238.160.0/22
+43.238.164.0/22
+43.238.168.0/22
+43.238.172.0/22
+43.238.176.0/22
+43.238.180.0/22
+43.238.184.0/22
+43.238.188.0/22
+43.238.192.0/22
+43.238.196.0/22
+43.238.200.0/22
+43.238.204.0/22
+43.238.208.0/22
+43.238.212.0/22
+43.238.216.0/22
+43.238.220.0/22
+43.238.224.0/22
+43.238.228.0/22
+43.238.232.0/22
+43.238.236.0/22
+43.238.240.0/22
+43.238.244.0/22
+43.238.248.0/22
+43.238.252.0/22
+43.239.0.0/22
+43.239.4.0/22
+43.239.8.0/21
+43.239.16.0/22
+43.239.20.0/22
+43.239.24.0/22
+43.239.28.0/22
+43.239.32.0/22
+43.239.36.0/22
+43.239.40.0/22
+43.239.44.0/22
+43.239.48.0/22
+43.239.116.0/22
+43.239.176.0/22
+43.240.48.0/22
+43.240.56.0/22
+43.240.60.0/22
+43.240.68.0/22
+43.240.76.0/22
+43.240.84.0/22
+43.240.124.0/22
+43.240.128.0/22
+43.240.136.0/22
+43.240.156.0/22
+43.240.160.0/22
+43.240.164.0/22
+43.240.168.0/22
+43.240.172.0/22
+43.240.176.0/22
+43.240.180.0/22
+43.240.184.0/22
+43.240.188.0/22
+43.240.200.0/22
+43.240.208.0/22
+43.240.212.0/22
+43.240.216.0/22
+43.240.220.0/22
+43.240.236.0/22
+43.240.240.0/22
+43.240.244.0/22
+43.240.248.0/22
+43.240.252.0/22
+43.241.0.0/22
+43.241.4.0/22
+43.241.8.0/22
+43.241.12.0/22
+43.241.16.0/22
+43.241.20.0/22
+43.241.76.0/22
+43.241.80.0/22
+43.241.84.0/22
+43.241.88.0/22
+43.241.112.0/22
+43.241.168.0/22
+43.241.172.0/22
+43.241.176.0/22
+43.241.180.0/22
+43.241.196.0/22
+43.241.208.0/22
+43.241.212.0/22
+43.241.216.0/22
+43.241.220.0/22
+43.241.224.0/22
+43.241.228.0/22
+43.241.232.0/22
+43.241.236.0/22
+43.241.240.0/22
+43.241.248.0/22
+43.241.252.0/22
+43.242.8.0/22
+43.242.12.0/22
+43.242.16.0/22
+43.242.20.0/22
+43.242.24.0/22
+43.242.28.0/22
+43.242.44.0/22
+43.242.48.0/22
+43.242.52.0/22
+43.242.60.0/22
+43.242.72.0/22
+43.242.76.0/22
+43.242.80.0/22
+43.242.84.0/22
+43.242.88.0/22
+43.242.92.0/22
+43.242.96.0/22
+43.242.160.0/22
+43.242.168.0/22
+43.242.188.0/22
+43.242.196.0/22
+43.242.204.0/22
+43.242.216.0/22
+43.242.220.0/22
+43.242.252.0/22
+43.243.8.0/22
+43.243.12.0/22
+43.243.24.0/22
+43.243.136.0/22
+43.243.144.0/22
+43.243.168.0/22
+43.243.188.0/22
+43.243.244.0/22
+43.246.0.0/22
+43.246.4.0/22
+43.246.8.0/22
+43.246.12.0/22
+43.246.16.0/22
+43.246.20.0/22
+43.246.24.0/22
+43.246.28.0/22
+43.246.32.0/22
+43.246.36.0/22
+43.246.40.0/22
+43.246.44.0/22
+43.246.48.0/22
+43.246.52.0/22
+43.246.56.0/22
+43.246.60.0/22
+43.246.64.0/22
+43.246.68.0/22
+43.246.72.0/22
+43.246.76.0/22
+43.246.80.0/22
+43.246.84.0/22
+43.246.88.0/22
+43.246.92.0/22
+43.246.96.0/22
+43.246.212.0/22
+43.246.228.0/22
+43.247.4.0/22
+43.247.8.0/22
+43.247.44.0/22
+43.247.48.0/22
+43.247.68.0/22
+43.247.76.0/22
+43.247.84.0/22
+43.247.96.0/22
+43.247.100.0/22
+43.247.108.0/22
+43.247.112.0/22
+43.247.148.0/22
+43.247.152.0/22
+43.247.176.0/22
+43.247.180.0/22
+43.247.184.0/22
+43.247.188.0/22
+43.247.196.0/22
+43.247.200.0/22
+43.247.204.0/22
+43.247.220.0/22
+43.247.224.0/22
+43.247.228.0/22
+43.247.232.0/22
+43.247.236.0/22
+43.247.244.0/22
+43.247.248.0/22
+43.247.252.0/22
+43.248.0.0/22
+43.248.28.0/22
+43.248.76.0/22
+43.248.96.0/22
+43.248.100.0/22
+43.248.104.0/22
+43.248.112.0/22
+43.248.116.0/22
+43.248.120.0/22
+43.248.124.0/22
+43.248.128.0/22
+43.248.132.0/22
+43.248.136.0/22
+43.248.140.0/22
+43.248.144.0/22
+43.248.208.0/22
+43.248.228.0/22
+43.248.232.0/22
+43.249.8.0/22
+43.249.24.0/22
+43.249.120.0/22
+43.249.132.0/22
+43.249.148.0/22
+43.249.152.0/22
+43.249.156.0/22
+43.249.160.0/22
+43.249.164.0/22
+43.249.168.0/22
+43.250.12.0/22
+43.250.16.0/22
+43.250.20.0/22
+43.250.28.0/22
+43.250.36.0/22
+43.250.72.0/22
+43.250.100.0/22
+43.250.104.0/22
+43.250.108.0/22
+43.250.116.0/22
+43.250.148.0/22
+43.250.160.0/22
+43.250.172.0/22
+43.250.176.0/22
+43.250.216.0/22
+43.250.220.0/22
+43.250.236.0/22
+43.251.8.0/22
+43.251.12.0/22
+43.251.100.0/22
+43.251.116.0/22
+43.251.192.0/22
+43.251.236.0/22
+43.252.40.0/22
+43.252.48.0/22
+43.252.56.0/22
+43.252.224.0/22
+43.254.0.0/22
+43.254.4.0/22
+43.254.8.0/22
+43.254.24.0/22
+43.254.36.0/22
+43.254.64.0/22
+43.254.72.0/22
+43.254.84.0/22
+43.254.88.0/22
+43.254.92.0/22
+43.254.100.0/22
+43.254.112.0/22
+43.254.128.0/22
+43.254.140.0/22
+43.254.152.0/22
+43.254.156.0/22
+43.254.168.0/22
+43.254.172.0/22
+43.254.180.0/22
+43.254.184.0/22
+43.254.188.0/22
+43.254.192.0/22
+43.254.196.0/22
+43.254.200.0/22
+43.254.208.0/22
+43.254.224.0/22
+43.254.228.0/22
+43.254.232.0/22
+43.254.236.0/22
+43.254.240.0/22
+43.254.248.0/22
+43.254.252.0/22
+43.255.0.0/22
+43.255.4.0/22
+43.255.8.0/22
+43.255.16.0/22
+43.255.48.0/22
+43.255.64.0/22
+43.255.68.0/22
+43.255.72.0/22
+43.255.76.0/22
+43.255.96.0/22
+43.255.108.0/22
+43.255.144.0/22
+43.255.168.0/22
+43.255.176.0/22
+43.255.184.0/22
+43.255.192.0/22
+43.255.200.0/22
+43.255.208.0/22
+43.255.212.0/22
+43.255.224.0/22
+43.255.228.0/22
+43.255.232.0/22
+43.255.244.0/22
+45.40.192.0/18
+45.65.16.0/22
+45.65.20.0/22
+45.65.24.0/22
+45.65.28.0/22
+45.112.132.0/22
+45.112.188.0/22
+45.112.208.0/22
+45.112.212.0/22
+45.112.216.0/22
+45.112.220.0/22
+45.112.236.0/22
+45.113.12.0/22
+45.113.16.0/22
+45.113.20.0/22
+45.113.40.0/22
+45.113.52.0/22
+45.113.56.0/22
+45.113.72.0/22
+45.113.144.0/22
+45.113.148.0/22
+45.113.168.0/22
+45.113.176.0/22
+45.113.184.0/22
+45.113.204.0/22
+45.113.208.0/22
+45.113.212.0/22
+45.113.216.0/22
+45.113.220.0/22
+45.114.0.0/22
+45.114.12.0/22
+45.114.32.0/22
+45.114.40.0/22
+45.114.52.0/22
+45.114.96.0/22
+45.114.104.0/22
+45.114.108.0/22
+45.114.124.0/22
+45.114.136.0/22
+45.114.196.0/22
+45.114.200.0/22
+45.114.228.0/22
+45.115.44.0/22
+45.115.100.0/22
+45.115.120.0/22
+45.115.132.0/22
+45.115.212.0/22
+45.115.228.0/22
+45.115.236.0/22
+45.115.244.0/22
+45.116.12.0/22
+45.116.16.0/22
+45.116.24.0/22
+45.116.32.0/22
+45.116.36.0/22
+45.116.52.0/22
+45.116.96.0/22
+45.116.100.0/22
+45.116.140.0/22
+45.116.152.0/22
+45.116.208.0/22
+45.117.20.0/22
+45.117.68.0/22
+45.117.252.0/22
+45.119.52.0/22
+45.119.60.0/22
+45.119.72.0/22
+45.119.104.0/22
+45.119.116.0/22
+45.119.232.0/22
+45.120.140.0/22
+45.120.164.0/22
+45.120.220.0/22
+45.120.240.0/22
+45.121.20.0/22
+45.121.64.0/22
+45.121.92.0/22
+45.121.104.0/22
+45.121.212.0/22
+45.121.240.0/22
+45.121.244.0/22
+45.121.248.0/22
+45.121.252.0/22
+45.122.0.0/22
+45.122.4.0/22
+45.122.8.0/22
+45.122.12.0/22
+45.122.16.0/22
+45.122.20.0/22
+45.122.24.0/22
+45.122.28.0/22
+45.122.32.0/22
+45.122.36.0/22
+45.122.40.0/22
+45.122.60.0/22
+45.122.64.0/22
+45.122.68.0/22
+45.122.72.0/22
+45.122.76.0/22
+45.122.80.0/22
+45.122.84.0/22
+45.122.88.0/22
+45.122.92.0/22
+45.122.104.0/22
+45.122.108.0/22
+45.122.112.0/22
+45.122.116.0/22
+45.122.160.0/22
+45.122.164.0/22
+45.122.168.0/22
+45.122.172.0/22
+45.122.176.0/22
+45.122.180.0/22
+45.122.184.0/22
+45.122.188.0/22
+45.122.192.0/22
+45.122.196.0/22
+45.122.200.0/22
+45.122.204.0/22
+45.122.208.0/22
+45.122.212.0/22
+45.122.216.0/22
+45.123.28.0/22
+45.123.32.0/22
+45.123.36.0/22
+45.123.44.0/22
+45.123.48.0/22
+45.123.52.0/22
+45.123.56.0/22
+45.123.60.0/22
+45.123.64.0/22
+45.123.68.0/22
+45.123.72.0/22
+45.123.76.0/22
+45.123.80.0/22
+45.123.84.0/22
+45.123.88.0/22
+45.123.120.0/22
+45.123.128.0/22
+45.123.132.0/22
+45.123.136.0/22
+45.123.148.0/22
+45.123.164.0/22
+45.123.168.0/22
+45.123.172.0/22
+45.123.176.0/22
+45.123.180.0/22
+45.123.184.0/22
+45.123.204.0/22
+45.123.212.0/22
+45.123.224.0/22
+45.123.228.0/22
+45.123.232.0/22
+45.123.236.0/22
+45.123.240.0/22
+45.123.244.0/22
+45.123.248.0/22
+45.123.252.0/22
+45.124.0.0/22
+45.124.28.0/22
+45.124.32.0/22
+45.124.44.0/22
+45.124.76.0/22
+45.124.100.0/22
+45.124.124.0/22
+45.124.172.0/22
+45.124.176.0/22
+45.124.208.0/22
+45.124.248.0/22
+45.124.252.0/22
+45.125.12.0/22
+45.125.16.0/22
+45.125.28.0/22
+45.125.32.0/22
+45.125.52.0/22
+45.125.56.0/22
+45.125.76.0/22
+45.125.80.0/22
+45.125.84.0/22
+45.125.88.0/22
+45.125.92.0/22
+45.125.96.0/22
+45.125.100.0/22
+45.125.104.0/22
+45.125.136.0/22
+45.126.48.0/22
+45.126.52.0/22
+45.126.100.0/22
+45.126.112.0/22
+45.126.116.0/22
+45.126.212.0/22
+45.126.220.0/22
+45.127.8.0/22
+45.127.12.0/22
+45.127.96.0/22
+45.127.116.0/22
+45.127.124.0/22
+45.127.128.0/22
+45.127.144.0/22
+45.127.148.0/22
+45.127.156.0/22
+45.127.216.0/22
+45.248.80.0/22
+45.248.84.0/22
+45.248.88.0/22
+45.248.96.0/22
+45.248.100.0/22
+45.248.104.0/22
+45.248.108.0/22
+45.248.128.0/22
+45.248.132.0/22
+45.248.204.0/22
+45.248.208.0/22
+45.248.212.0/22
+45.248.216.0/22
+45.248.220.0/22
+45.248.224.0/22
+45.248.228.0/22
+45.248.232.0/22
+45.248.236.0/22
+45.248.240.0/22
+45.248.244.0/22
+45.248.248.0/22
+45.248.252.0/22
+45.249.0.0/22
+45.249.4.0/22
+45.249.12.0/22
+45.249.16.0/22
+45.249.20.0/22
+45.249.24.0/22
+45.249.28.0/22
+45.249.32.0/22
+45.249.36.0/22
+45.249.92.0/22
+45.249.180.0/22
+45.249.188.0/22
+45.249.192.0/22
+45.249.196.0/22
+45.249.200.0/22
+45.249.204.0/22
+45.249.208.0/22
+45.249.212.0/22
+45.250.28.0/22
+45.250.152.0/22
+45.250.164.0/22
+45.250.180.0/22
+45.250.184.0/22
+45.250.188.0/22
+45.250.192.0/22
+45.251.0.0/22
+45.251.8.0/22
+45.251.20.0/22
+45.251.52.0/22
+45.251.92.0/22
+45.251.96.0/22
+45.251.100.0/22
+45.251.120.0/22
+45.251.124.0/22
+45.251.136.0/22
+45.251.140.0/22
+45.251.144.0/22
+45.251.148.0/22
+45.251.152.0/22
+45.251.156.0/22
+45.251.160.0/22
+45.251.164.0/22
+45.251.168.0/22
+45.251.172.0/22
+45.251.176.0/22
+45.251.180.0/22
+45.251.184.0/22
+45.251.188.0/22
+45.251.192.0/22
+45.251.196.0/22
+45.251.200.0/22
+45.251.204.0/22
+45.251.208.0/22
+45.251.212.0/22
+45.251.216.0/22
+45.251.220.0/22
+45.251.224.0/22
+45.251.240.0/22
+45.252.0.0/22
+45.252.4.0/22
+45.252.8.0/22
+45.252.12.0/22
+45.252.16.0/22
+45.252.20.0/22
+45.252.24.0/22
+45.252.28.0/22
+45.252.32.0/22
+45.252.36.0/22
+45.252.40.0/22
+45.252.44.0/22
+45.252.48.0/22
+45.252.60.0/22
+45.252.84.0/22
+45.252.88.0/22
+45.252.92.0/22
+45.252.96.0/22
+45.252.100.0/22
+45.252.108.0/22
+45.252.112.0/22
+45.252.116.0/22
+45.252.120.0/22
+45.252.124.0/22
+45.252.128.0/22
+45.252.132.0/22
+45.252.136.0/22
+45.252.140.0/22
+45.252.144.0/22
+45.252.148.0/22
+45.252.156.0/22
+45.252.160.0/22
+45.252.164.0/22
+45.252.168.0/22
+45.252.172.0/22
+45.252.176.0/22
+45.252.192.0/22
+45.252.196.0/22
+45.252.228.0/22
+45.252.232.0/22
+45.253.44.0/22
+45.253.48.0/22
+45.253.52.0/22
+45.253.60.0/22
+45.253.68.0/22
+45.253.96.0/22
+45.253.100.0/22
+45.253.104.0/22
+45.253.108.0/22
+45.253.112.0/22
+45.253.116.0/22
+45.253.120.0/22
+45.253.132.0/22
+45.253.136.0/22
+45.253.140.0/22
+45.253.144.0/22
+45.253.148.0/22
+45.253.152.0/22
+45.253.156.0/22
+45.253.160.0/22
+45.253.164.0/22
+45.253.168.0/22
+45.253.172.0/22
+45.253.176.0/22
+45.253.180.0/22
+45.253.184.0/22
+45.253.188.0/22
+45.253.192.0/22
+45.253.196.0/22
+45.253.200.0/22
+45.253.204.0/22
+45.253.208.0/22
+45.253.212.0/22
+45.253.216.0/22
+45.253.220.0/22
+45.253.224.0/22
+45.253.228.0/22
+45.253.232.0/22
+45.253.236.0/22
+45.253.240.0/22
+45.253.244.0/22
+45.253.248.0/22
+45.253.252.0/22
+45.254.0.0/22
+45.254.4.0/22
+45.254.8.0/22
+45.254.12.0/22
+45.254.16.0/22
+45.254.20.0/22
+45.254.24.0/22
+45.254.40.0/22
+45.254.48.0/22
+45.254.52.0/22
+45.254.60.0/22
+45.254.64.0/22
+45.254.68.0/22
+45.254.72.0/22
+45.254.76.0/22
+45.254.80.0/22
+45.254.84.0/22
+45.254.88.0/22
+45.254.92.0/22
+45.254.100.0/22
+45.254.104.0/22
+45.254.108.0/22
+45.254.112.0/22
+45.254.116.0/22
+45.254.120.0/22
+45.254.124.0/22
+45.254.128.0/22
+45.254.136.0/22
+45.254.140.0/22
+45.254.144.0/22
+45.254.148.0/22
+45.254.156.0/22
+45.254.160.0/22
+45.254.164.0/22
+45.254.168.0/22
+45.254.172.0/22
+45.254.176.0/22
+45.254.184.0/22
+45.254.188.0/22
+45.254.192.0/22
+45.254.196.0/22
+45.254.236.0/22
+45.254.240.0/22
+45.254.248.0/22
+45.255.0.0/22
+45.255.4.0/22
+45.255.8.0/22
+45.255.12.0/22
+45.255.16.0/22
+45.255.20.0/22
+45.255.24.0/22
+45.255.28.0/22
+45.255.32.0/22
+45.255.36.0/22
+45.255.40.0/22
+45.255.44.0/22
+45.255.48.0/22
+45.255.52.0/22
+45.255.56.0/22
+45.255.60.0/22
+45.255.64.0/22
+45.255.68.0/22
+45.255.72.0/22
+45.255.76.0/22
+45.255.80.0/22
+45.255.84.0/22
+45.255.88.0/22
+45.255.92.0/22
+45.255.96.0/22
+45.255.100.0/22
+45.255.104.0/22
+45.255.108.0/22
+45.255.112.0/22
+45.255.116.0/22
+45.255.120.0/22
+45.255.124.0/22
+45.255.132.0/22
+45.255.136.0/22
+45.255.140.0/22
+45.255.144.0/22
+45.255.148.0/22
+45.255.152.0/22
+45.255.156.0/22
+45.255.160.0/22
+45.255.164.0/22
+45.255.168.0/22
+45.255.172.0/22
+45.255.176.0/22
+45.255.180.0/22
+45.255.184.0/22
+45.255.188.0/22
+45.255.192.0/22
+45.255.196.0/22
+45.255.200.0/22
+45.255.204.0/22
+45.255.208.0/22
+45.255.212.0/22
+45.255.216.0/22
+45.255.220.0/22
+45.255.224.0/22
+45.255.228.0/22
+45.255.232.0/22
+45.255.236.0/22
+45.255.240.0/22
+45.255.244.0/22
+45.255.248.0/22
+47.92.0.0/14
+47.96.0.0/11
+49.4.0.0/14
+49.51.0.0/16
+49.128.4.0/22
+49.152.0.0/14
+49.210.0.0/15
+49.232.0.0/14
+49.239.0.0/18
+49.239.192.0/18
+49.246.224.0/19
+52.80.0.0/15
+52.82.0.0/15
+52.130.0.0/15
+54.222.0.0/15
+58.14.0.0/15
+58.66.0.0/15
+58.68.128.0/17
+58.82.0.0/17
+58.83.0.0/17
+58.83.128.0/17
+58.87.64.0/18
+58.99.128.0/17
+58.100.0.0/15
+58.116.0.0/14
+58.128.0.0/13
+59.80.0.0/15
+59.82.0.0/15
+59.107.0.0/17
+59.107.128.0/17
+59.108.0.0/15
+59.110.0.0/15
+59.151.0.0/17
+59.152.16.0/22
+59.152.20.0/22
+59.152.24.0/22
+59.152.28.0/22
+59.152.32.0/22
+59.152.36.0/22
+59.152.64.0/22
+59.152.68.0/22
+59.152.72.0/22
+59.152.76.0/22
+59.152.112.0/22
+59.152.116.0/22
+59.153.4.0/22
+59.153.32.0/22
+59.153.60.0/22
+59.153.64.0/22
+59.153.68.0/22
+59.153.72.0/22
+59.153.92.0/22
+59.153.116.0/22
+59.153.136.0/22
+59.153.152.0/22
+59.153.156.0/22
+59.153.164.0/22
+59.153.168.0/22
+59.153.172.0/22
+59.153.176.0/22
+59.153.180.0/22
+59.153.184.0/22
+59.153.188.0/22
+59.153.192.0/22
+59.155.0.0/16
+59.191.0.0/17
+59.191.240.0/20
+59.192.0.0/10
+60.55.0.0/16
+60.194.0.0/15
+60.200.0.0/14
+60.205.0.0/16
+60.206.0.0/15
+60.232.0.0/15
+60.245.128.0/17
+60.252.0.0/16
+61.4.80.0/22
+61.4.176.0/20
+61.8.160.0/20
+61.14.212.0/22
+61.14.216.0/22
+61.14.220.0/22
+61.29.192.0/19
+61.29.224.0/20
+61.29.240.0/20
+61.47.128.0/18
+61.87.192.0/18
+62.234.0.0/16
+68.79.0.0/18
+69.230.192.0/18
+69.231.128.0/18
+69.234.192.0/18
+69.235.128.0/18
+71.131.192.0/18
+71.132.0.0/18
+71.136.64.0/18
+71.137.0.0/18
+81.68.0.0/14
+82.156.0.0/15
+94.191.0.0/17
+101.32.0.0/14
+101.36.0.0/17
+101.36.128.0/17
+101.37.0.0/16
+101.38.0.0/15
+101.40.0.0/13
+101.48.0.0/15
+101.50.8.0/22
+101.50.12.0/22
+101.52.0.0/16
+101.54.0.0/16
+101.78.32.0/19
+101.96.128.0/17
+101.104.0.0/14
+101.120.0.0/14
+101.124.0.0/15
+101.126.0.0/16
+101.129.0.0/16
+101.132.0.0/14
+101.192.0.0/14
+101.196.0.0/16
+101.197.0.0/16
+101.198.0.0/15
+101.200.0.0/15
+101.236.0.0/14
+101.240.0.0/14
+101.251.64.0/18
+101.251.128.0/17
+101.252.0.0/15
+101.254.0.0/16
+103.1.8.0/22
+103.1.20.0/22
+103.1.24.0/22
+103.1.72.0/22
+103.1.88.0/22
+103.1.168.0/22
+103.2.108.0/22
+103.2.156.0/22
+103.2.164.0/22
+103.2.200.0/22
+103.2.204.0/22
+103.2.208.0/22
+103.2.212.0/22
+103.3.84.0/22
+103.3.88.0/22
+103.3.92.0/22
+103.4.56.0/22
+103.4.168.0/22
+103.4.184.0/22
+103.4.224.0/22
+103.5.36.0/22
+103.5.52.0/22
+103.5.152.0/22
+103.6.76.0/22
+103.6.108.0/22
+103.6.220.0/22
+103.6.228.0/22
+103.7.4.0/22
+103.7.28.0/22
+103.7.140.0/22
+103.7.216.0/22
+103.7.220.0/22
+103.8.0.0/22
+103.8.4.0/22
+103.8.8.0/22
+103.8.32.0/22
+103.8.52.0/22
+103.8.68.0/22
+103.8.108.0/22
+103.8.156.0/22
+103.8.200.0/22
+103.8.204.0/22
+103.8.220.0/22
+103.9.8.0/22
+103.9.24.0/22
+103.9.108.0/22
+103.9.152.0/22
+103.9.192.0/22
+103.10.0.0/22
+103.10.16.0/22
+103.10.84.0/22
+103.10.140.0/22
+103.11.16.0/22
+103.11.168.0/22
+103.11.180.0/22
+103.12.32.0/22
+103.12.92.0/22
+103.12.136.0/22
+103.12.184.0/22
+103.12.232.0/22
+103.13.12.0/22
+103.13.72.0/23
+103.13.124.0/22
+103.13.144.0/22
+103.13.196.0/22
+103.13.220.0/22
+103.13.244.0/22
+103.14.32.0/22
+103.14.100.0/22
+103.14.136.0/22
+103.14.156.0/22
+103.14.240.0/22
+103.15.4.0/22
+103.15.8.0/22
+103.15.16.0/22
+103.15.96.0/22
+103.15.200.0/22
+103.16.52.0/22
+103.16.80.0/22
+103.16.84.0/22
+103.16.88.0/22
+103.16.108.0/22
+103.16.124.0/22
+103.17.40.0/22
+103.17.64.0/22
+103.17.120.0/22
+103.17.136.0/22
+103.17.160.0/22
+103.17.204.0/22
+103.17.228.0/22
+103.18.192.0/22
+103.18.208.0/22
+103.18.212.0/22
+103.19.0.0/22
+103.19.12.0/22
+103.19.40.0/22
+103.19.44.0/22
+103.19.64.0/22
+103.19.68.0/22
+103.19.72.0/22
+103.19.232.0/22
+103.20.12.0/22
+103.20.32.0/22
+103.20.44.0/22
+103.20.68.0/22
+103.20.128.0/22
+103.20.160.0/22
+103.20.248.0/22
+103.21.112.0/22
+103.21.136.0/22
+103.21.140.0/22
+103.21.208.0/22
+103.21.240.0/22
+103.22.228.0/22
+103.22.252.0/22
+103.23.8.0/22
+103.23.160.0/22
+103.23.164.0/22
+103.23.176.0/22
+103.23.228.0/22
+103.24.24.0/22
+103.24.116.0/22
+103.24.128.0/22
+103.24.144.0/22
+103.24.176.0/22
+103.24.220.0/22
+103.24.228.0/22
+103.24.248.0/22
+103.24.252.0/22
+103.25.8.0/23
+103.25.20.0/22
+103.25.24.0/22
+103.25.28.0/22
+103.25.32.0/22
+103.25.36.0/22
+103.25.48.0/22
+103.25.64.0/22
+103.25.68.0/22
+103.25.148.0/22
+103.25.156.0/22
+103.25.216.0/22
+103.26.0.0/22
+103.26.64.0/22
+103.26.76.0/22
+103.26.132.0/22
+103.26.156.0/22
+103.26.160.0/22
+103.26.228.0/22
+103.26.240.0/22
+103.27.12.0/22
+103.27.24.0/22
+103.27.56.0/22
+103.27.96.0/22
+103.27.184.0/22
+103.27.208.0/22
+103.27.212.0/22
+103.28.4.0/22
+103.28.8.0/22
+103.28.184.0/22
+103.28.212.0/22
+103.29.16.0/22
+103.29.132.0/22
+103.29.136.0/22
+103.30.20.0/22
+103.30.96.0/22
+103.30.148.0/22
+103.30.200.0/22
+103.30.228.0/22
+103.30.236.0/22
+103.31.0.0/22
+103.31.48.0/22
+103.31.52.0/22
+103.31.56.0/22
+103.31.60.0/22
+103.31.64.0/22
+103.31.68.0/22
+103.31.148.0/22
+103.31.160.0/22
+103.31.168.0/22
+103.31.236.0/22
+103.32.0.0/22
+103.32.4.0/22
+103.32.8.0/22
+103.32.12.0/22
+103.32.16.0/22
+103.32.20.0/22
+103.32.24.0/22
+103.32.28.0/22
+103.32.32.0/22
+103.32.36.0/22
+103.32.40.0/22
+103.32.44.0/22
+103.32.48.0/22
+103.32.52.0/22
+103.32.56.0/22
+103.32.60.0/22
+103.32.64.0/22
+103.32.68.0/22
+103.32.72.0/22
+103.32.76.0/22
+103.32.80.0/22
+103.32.84.0/22
+103.32.88.0/22
+103.32.92.0/22
+103.32.96.0/22
+103.32.100.0/22
+103.32.104.0/22
+103.32.108.0/22
+103.32.112.0/22
+103.32.116.0/22
+103.32.120.0/22
+103.32.124.0/22
+103.32.128.0/22
+103.32.132.0/22
+103.32.136.0/22
+103.32.140.0/22
+103.32.144.0/22
+103.32.148.0/22
+103.32.152.0/22
+103.32.156.0/22
+103.32.160.0/22
+103.32.164.0/22
+103.32.168.0/22
+103.32.172.0/22
+103.32.176.0/22
+103.32.180.0/22
+103.32.184.0/22
+103.32.188.0/22
+103.32.192.0/22
+103.32.196.0/22
+103.32.200.0/22
+103.32.204.0/22
+103.32.208.0/22
+103.32.212.0/22
+103.32.216.0/22
+103.32.220.0/22
+103.32.224.0/22
+103.32.228.0/22
+103.32.232.0/22
+103.32.236.0/22
+103.32.240.0/22
+103.32.244.0/22
+103.32.248.0/22
+103.32.252.0/22
+103.33.0.0/22
+103.33.4.0/22
+103.33.8.0/22
+103.33.12.0/22
+103.33.16.0/22
+103.33.20.0/22
+103.33.24.0/22
+103.33.28.0/22
+103.33.32.0/22
+103.33.36.0/22
+103.33.40.0/22
+103.33.44.0/22
+103.33.48.0/22
+103.33.52.0/22
+103.33.56.0/22
+103.33.60.0/22
+103.33.64.0/22
+103.33.68.0/22
+103.33.72.0/22
+103.33.76.0/22
+103.33.80.0/22
+103.33.84.0/22
+103.33.88.0/22
+103.33.92.0/22
+103.33.96.0/22
+103.33.100.0/22
+103.33.104.0/22
+103.33.108.0/22
+103.33.112.0/22
+103.33.116.0/22
+103.33.120.0/22
+103.33.124.0/22
+103.33.128.0/22
+103.33.132.0/22
+103.33.136.0/22
+103.33.140.0/22
+103.33.144.0/22
+103.33.148.0/22
+103.33.152.0/22
+103.33.156.0/22
+103.33.160.0/22
+103.33.164.0/22
+103.33.168.0/22
+103.33.172.0/22
+103.33.176.0/22
+103.33.180.0/22
+103.33.184.0/22
+103.33.188.0/22
+103.33.192.0/22
+103.33.196.0/22
+103.33.200.0/22
+103.33.204.0/22
+103.33.208.0/22
+103.33.212.0/22
+103.33.216.0/22
+103.33.220.0/22
+103.33.224.0/22
+103.33.228.0/22
+103.33.232.0/22
+103.33.236.0/22
+103.33.240.0/22
+103.33.244.0/22
+103.33.248.0/22
+103.33.252.0/22
+103.34.0.0/22
+103.34.4.0/22
+103.34.8.0/22
+103.34.12.0/22
+103.34.16.0/22
+103.34.20.0/22
+103.34.24.0/22
+103.34.28.0/22
+103.34.32.0/22
+103.34.36.0/22
+103.34.40.0/22
+103.34.44.0/22
+103.34.48.0/22
+103.34.52.0/22
+103.34.56.0/22
+103.34.60.0/22
+103.34.64.0/22
+103.34.68.0/22
+103.34.72.0/22
+103.34.76.0/22
+103.34.80.0/22
+103.34.84.0/22
+103.34.88.0/22
+103.34.92.0/22
+103.34.96.0/22
+103.34.100.0/22
+103.34.104.0/22
+103.34.108.0/22
+103.34.112.0/22
+103.34.116.0/22
+103.34.120.0/22
+103.34.124.0/22
+103.34.128.0/22
+103.34.132.0/22
+103.34.136.0/22
+103.34.140.0/22
+103.34.144.0/22
+103.34.148.0/22
+103.34.152.0/22
+103.34.156.0/22
+103.34.160.0/22
+103.34.164.0/22
+103.34.168.0/22
+103.34.172.0/22
+103.34.176.0/22
+103.34.180.0/22
+103.34.184.0/22
+103.34.188.0/22
+103.34.192.0/22
+103.34.196.0/22
+103.34.200.0/22
+103.34.204.0/22
+103.34.208.0/22
+103.34.212.0/22
+103.34.216.0/22
+103.34.220.0/22
+103.34.224.0/22
+103.34.228.0/22
+103.34.232.0/22
+103.34.236.0/22
+103.34.240.0/22
+103.34.244.0/22
+103.34.248.0/22
+103.34.252.0/22
+103.35.0.0/22
+103.35.4.0/22
+103.35.8.0/22
+103.35.12.0/22
+103.35.16.0/22
+103.35.20.0/22
+103.35.24.0/22
+103.35.28.0/22
+103.35.32.0/22
+103.35.36.0/22
+103.35.40.0/22
+103.35.44.0/22
+103.35.48.0/22
+103.35.116.0/22
+103.35.180.0/22
+103.35.200.0/22
+103.36.20.0/22
+103.36.28.0/22
+103.36.36.0/22
+103.36.56.0/22
+103.36.60.0/22
+103.36.64.0/22
+103.36.72.0/22
+103.36.96.0/22
+103.36.132.0/22
+103.36.136.0/22
+103.36.160.0/22
+103.36.164.0/22
+103.36.168.0/22
+103.36.172.0/22
+103.36.176.0/22
+103.36.180.0/22
+103.36.184.0/22
+103.36.188.0/22
+103.36.192.0/22
+103.36.196.0/22
+103.36.200.0/22
+103.36.204.0/22
+103.36.212.0/22
+103.36.216.0/22
+103.36.224.0/22
+103.36.228.0/22
+103.36.232.0/22
+103.36.236.0/22
+103.36.240.0/22
+103.36.244.0/22
+103.37.0.0/22
+103.37.12.0/22
+103.37.16.0/22
+103.37.24.0/22
+103.37.52.0/22
+103.37.56.0/22
+103.37.72.0/22
+103.37.104.0/22
+103.37.124.0/22
+103.37.136.0/22
+103.37.140.0/22
+103.37.144.0/22
+103.37.148.0/22
+103.37.152.0/22
+103.37.156.0/22
+103.37.160.0/22
+103.37.164.0/22
+103.37.172.0/22
+103.37.176.0/22
+103.37.188.0/22
+103.37.208.0/22
+103.37.212.0/22
+103.37.216.0/22
+103.37.220.0/22
+103.37.248.0/22
+103.37.252.0/22
+103.38.44.0/22
+103.38.56.0/22
+103.38.84.0/22
+103.38.92.0/22
+103.38.96.0/22
+103.38.116.0/22
+103.38.132.0/22
+103.38.140.0/22
+103.38.224.0/22
+103.38.228.0/22
+103.38.252.0/22
+103.39.16.0/22
+103.39.64.0/22
+103.39.100.0/22
+103.39.104.0/22
+103.39.108.0/22
+103.39.160.0/22
+103.39.164.0/22
+103.39.168.0/22
+103.39.172.0/22
+103.39.176.0/22
+103.39.180.0/22
+103.39.184.0/22
+103.39.188.0/22
+103.39.200.0/22
+103.39.204.0/22
+103.39.208.0/22
+103.39.212.0/22
+103.39.216.0/22
+103.39.220.0/22
+103.39.224.0/22
+103.39.228.0/22
+103.39.232.0/22
+103.40.16.0/22
+103.40.20.0/22
+103.40.24.0/22
+103.40.28.0/22
+103.40.32.0/22
+103.40.36.0/22
+103.40.40.0/22
+103.40.44.0/22
+103.40.88.0/22
+103.40.100.0/22
+103.40.112.0/22
+103.40.220.0/22
+103.40.228.0/22
+103.40.236.0/22
+103.40.240.0/22
+103.40.244.0/22
+103.40.248.0/22
+103.40.252.0/22
+103.41.0.0/22
+103.41.16.0/22
+103.41.52.0/22
+103.41.140.0/22
+103.41.148.0/22
+103.41.152.0/22
+103.41.160.0/22
+103.41.220.0/22
+103.41.224.0/22
+103.41.228.0/22
+103.41.232.0/22
+103.42.8.0/22
+103.42.24.0/22
+103.42.28.0/22
+103.42.32.0/22
+103.42.64.0/22
+103.42.68.0/22
+103.42.76.0/22
+103.42.104.0/22
+103.42.180.0/22
+103.42.232.0/22
+103.43.16.0/22
+103.43.84.0/22
+103.43.96.0/22
+103.43.104.0/22
+103.43.124.0/22
+103.43.184.0/22
+103.43.196.0/22
+103.43.208.0/22
+103.43.220.0/22
+103.43.224.0/22
+103.43.232.0/22
+103.43.240.0/22
+103.44.56.0/22
+103.44.88.0/22
+103.44.120.0/22
+103.44.124.0/22
+103.44.132.0/22
+103.44.144.0/22
+103.44.168.0/22
+103.44.176.0/22
+103.44.180.0/22
+103.44.184.0/22
+103.44.188.0/22
+103.44.192.0/22
+103.44.196.0/22
+103.44.200.0/22
+103.44.204.0/22
+103.44.224.0/22
+103.44.236.0/22
+103.44.240.0/22
+103.44.244.0/22
+103.44.248.0/22
+103.44.252.0/22
+103.45.0.0/22
+103.45.4.0/22
+103.45.8.0/22
+103.45.12.0/22
+103.45.16.0/22
+103.45.20.0/22
+103.45.24.0/22
+103.45.28.0/22
+103.45.32.0/22
+103.45.36.0/22
+103.45.40.0/22
+103.45.44.0/22
+103.45.48.0/22
+103.45.52.0/22
+103.45.56.0/22
+103.45.60.0/22
+103.45.80.0/22
+103.45.84.0/22
+103.45.88.0/22
+103.45.92.0/22
+103.45.96.0/22
+103.45.100.0/22
+103.45.104.0/22
+103.45.108.0/22
+103.45.112.0/22
+103.45.116.0/22
+103.45.120.0/22
+103.45.124.0/22
+103.45.128.0/22
+103.45.132.0/22
+103.45.136.0/22
+103.45.140.0/22
+103.45.144.0/22
+103.45.148.0/22
+103.45.152.0/22
+103.45.156.0/22
+103.45.164.0/22
+103.45.172.0/22
+103.45.176.0/22
+103.45.180.0/22
+103.45.184.0/22
+103.45.188.0/22
+103.45.192.0/22
+103.45.196.0/22
+103.45.200.0/22
+103.45.204.0/22
+103.45.208.0/22
+103.45.212.0/22
+103.45.216.0/22
+103.45.220.0/22
+103.45.248.0/22
+103.46.0.0/22
+103.46.12.0/22
+103.46.16.0/22
+103.46.20.0/22
+103.46.24.0/22
+103.46.28.0/22
+103.46.32.0/22
+103.46.36.0/22
+103.46.40.0/22
+103.46.44.0/22
+103.46.48.0/22
+103.46.52.0/22
+103.46.56.0/22
+103.46.60.0/22
+103.46.64.0/22
+103.46.68.0/22
+103.46.72.0/22
+103.46.76.0/22
+103.46.80.0/22
+103.46.84.0/22
+103.46.88.0/22
+103.46.92.0/22
+103.46.96.0/22
+103.46.100.0/22
+103.46.104.0/22
+103.46.108.0/22
+103.46.112.0/22
+103.46.116.0/22
+103.46.120.0/22
+103.46.124.0/22
+103.46.128.0/22
+103.46.132.0/22
+103.46.136.0/22
+103.46.152.0/22
+103.46.156.0/22
+103.46.160.0/22
+103.46.164.0/22
+103.46.168.0/22
+103.46.172.0/22
+103.46.176.0/22
+103.46.180.0/22
+103.46.244.0/22
+103.46.248.0/22
+103.47.4.0/22
+103.47.20.0/22
+103.47.36.0/22
+103.47.40.0/22
+103.47.48.0/22
+103.47.80.0/22
+103.47.96.0/22
+103.47.108.0/22
+103.47.116.0/22
+103.47.120.0/22
+103.47.136.0/22
+103.47.140.0/22
+103.47.212.0/22
+103.48.20.0/22
+103.48.52.0/22
+103.48.92.0/22
+103.48.144.0/22
+103.48.148.0/22
+103.48.152.0/22
+103.48.156.0/22
+103.48.202.0/23
+103.48.236.0/22
+103.48.240.0/22
+103.48.244.0/22
+103.49.12.0/22
+103.49.20.0/22
+103.49.72.0/22
+103.49.76.0/22
+103.49.92.0/22
+103.49.96.0/22
+103.49.108.0/22
+103.49.128.0/22
+103.49.176.0/22
+103.49.180.0/22
+103.49.196.0/22
+103.49.248.0/22
+103.50.36.0/22
+103.50.44.0/22
+103.50.48.0/22
+103.50.52.0/22
+103.50.56.0/22
+103.50.60.0/22
+103.50.64.0/22
+103.50.68.0/22
+103.50.72.0/22
+103.50.108.0/22
+103.50.112.0/22
+103.50.116.0/22
+103.50.120.0/22
+103.50.124.0/22
+103.50.132.0/22
+103.50.136.0/22
+103.50.140.0/22
+103.50.172.0/22
+103.50.176.0/22
+103.50.180.0/22
+103.50.184.0/22
+103.50.188.0/22
+103.50.192.0/22
+103.50.196.0/22
+103.50.200.0/22
+103.50.220.0/22
+103.50.224.0/22
+103.50.228.0/22
+103.50.232.0/22
+103.50.236.0/22
+103.50.240.0/22
+103.50.244.0/22
+103.50.248.0/22
+103.52.40.0/22
+103.52.72.0/22
+103.52.76.0/22
+103.52.80.0/22
+103.52.84.0/22
+103.52.104.0/22
+103.52.160.0/22
+103.52.164.0/22
+103.52.172.0/22
+103.52.196.0/22
+103.53.4.0/22
+103.53.64.0/22
+103.53.68.0/22
+103.53.92.0/22
+103.53.100.0/22
+103.53.128.0/22
+103.53.132.0/22
+103.53.136.0/22
+103.53.140.0/22
+103.53.144.0/22
+103.53.180.0/22
+103.53.208.0/22
+103.53.212.0/22
+103.53.216.0/22
+103.53.236.0/22
+103.53.248.0/22
+103.54.8.0/22
+103.54.48.0/22
+103.54.60.0/22
+103.54.160.0/22
+103.54.164.0/22
+103.54.212.0/22
+103.55.24.0/22
+103.55.80.0/22
+103.55.120.0/22
+103.55.152.0/22
+103.55.236.0/22
+103.56.8.0/22
+103.56.16.0/22
+103.56.20.0/22
+103.56.52.0/22
+103.56.56.0/22
+103.56.72.0/22
+103.56.76.0/22
+103.56.140.0/22
+103.56.152.0/22
+103.56.184.0/22
+103.56.200.0/22
+103.57.12.0/22
+103.57.52.0/22
+103.57.56.0/22
+103.57.76.0/22
+103.57.136.0/22
+103.58.24.0/22
+103.59.76.0/22
+103.59.100.0/22
+103.59.112.0/22
+103.59.120.0/22
+103.59.128.0/22
+103.59.148.0/22
+103.59.164.0/22
+103.60.32.0/22
+103.60.44.0/22
+103.60.228.0/22
+103.60.236.0/22
+103.61.60.0/22
+103.61.104.0/22
+103.61.140.0/22
+103.61.152.0/22
+103.61.172.0/22
+103.61.184.0/22
+103.61.188.0/22
+103.62.52.0/22
+103.62.72.0/22
+103.62.76.0/22
+103.62.80.0/22
+103.62.84.0/22
+103.62.88.0/22
+103.62.96.0/22
+103.62.100.0/22
+103.62.104.0/22
+103.62.108.0/22
+103.62.112.0/22
+103.62.116.0/22
+103.62.120.0/22
+103.62.124.0/22
+103.62.128.0/22
+103.62.132.0/22
+103.62.156.0/22
+103.62.160.0/22
+103.62.164.0/22
+103.62.168.0/22
+103.62.172.0/22
+103.62.176.0/22
+103.62.180.0/22
+103.62.184.0/22
+103.62.188.0/22
+103.62.192.0/22
+103.62.212.0/22
+103.62.216.0/22
+103.62.220.0/22
+103.62.224.0/22
+103.63.32.0/22
+103.63.36.0/22
+103.63.40.0/22
+103.63.44.0/22
+103.63.48.0/22
+103.63.52.0/22
+103.63.56.0/22
+103.63.60.0/22
+103.63.64.0/22
+103.63.68.0/22
+103.63.72.0/22
+103.63.76.0/22
+103.63.80.0/22
+103.63.84.0/22
+103.63.88.0/22
+103.63.140.0/22
+103.63.144.0/22
+103.63.152.0/22
+103.63.160.0/22
+103.63.164.0/22
+103.63.168.0/22
+103.63.172.0/22
+103.63.176.0/22
+103.63.180.0/22
+103.63.184.0/22
+103.63.192.0/22
+103.63.196.0/22
+103.63.200.0/22
+103.63.204.0/22
+103.63.208.0/22
+103.63.240.0/22
+103.63.244.0/22
+103.63.248.0/22
+103.63.252.0/22
+103.64.0.0/22
+103.64.4.0/22
+103.64.24.0/22
+103.64.28.0/22
+103.64.32.0/22
+103.64.36.0/22
+103.64.40.0/22
+103.64.44.0/22
+103.64.48.0/22
+103.64.52.0/22
+103.64.56.0/22
+103.64.60.0/22
+103.64.64.0/22
+103.64.68.0/22
+103.64.72.0/22
+103.64.76.0/22
+103.64.80.0/22
+103.64.84.0/22
+103.64.88.0/22
+103.64.92.0/22
+103.64.96.0/22
+103.64.100.0/22
+103.64.104.0/22
+103.64.108.0/22
+103.64.112.0/22
+103.64.116.0/22
+103.64.120.0/22
+103.64.124.0/22
+103.64.140.0/22
+103.64.144.0/22
+103.64.152.0/22
+103.64.156.0/22
+103.64.160.0/22
+103.64.164.0/22
+103.64.168.0/22
+103.64.172.0/22
+103.64.176.0/22
+103.64.180.0/22
+103.64.184.0/22
+103.64.188.0/22
+103.64.192.0/22
+103.64.196.0/22
+103.64.200.0/22
+103.64.204.0/22
+103.64.208.0/22
+103.64.212.0/22
+103.64.216.0/22
+103.64.220.0/22
+103.64.224.0/22
+103.64.228.0/22
+103.64.232.0/22
+103.64.236.0/22
+103.64.240.0/22
+103.64.244.0/22
+103.64.248.0/22
+103.64.252.0/22
+103.65.0.0/22
+103.65.4.0/22
+103.65.8.0/22
+103.65.12.0/22
+103.65.16.0/22
+103.65.36.0/22
+103.65.40.0/22
+103.65.48.0/22
+103.65.52.0/22
+103.65.56.0/22
+103.65.60.0/22
+103.65.64.0/22
+103.65.68.0/22
+103.65.72.0/22
+103.65.76.0/22
+103.65.80.0/22
+103.65.84.0/22
+103.65.88.0/22
+103.65.92.0/22
+103.65.100.0/22
+103.65.104.0/22
+103.65.108.0/22
+103.65.112.0/22
+103.65.144.0/22
+103.65.148.0/22
+103.65.152.0/22
+103.65.156.0/22
+103.65.160.0/22
+103.65.164.0/22
+103.65.168.0/22
+103.65.172.0/22
+103.66.32.0/22
+103.66.40.0/22
+103.66.92.0/22
+103.66.108.0/22
+103.66.200.0/22
+103.66.216.0/22
+103.66.240.0/22
+103.66.244.0/22
+103.66.248.0/22
+103.66.252.0/22
+103.67.0.0/22
+103.67.4.0/22
+103.67.8.0/22
+103.67.100.0/22
+103.67.104.0/22
+103.67.108.0/22
+103.67.112.0/22
+103.67.116.0/22
+103.67.120.0/22
+103.67.124.0/22
+103.67.128.0/22
+103.67.132.0/22
+103.67.136.0/22
+103.67.140.0/22
+103.67.144.0/22
+103.67.148.0/22
+103.67.172.0/22
+103.67.192.0/22
+103.67.212.0/22
+103.67.252.0/22
+103.68.64.0/22
+103.68.88.0/22
+103.68.100.0/22
+103.68.128.0/22
+103.68.192.0/22
+103.69.16.0/22
+103.69.116.0/22
+103.69.132.0/22
+103.69.152.0/22
+103.69.212.0/22
+103.70.8.0/22
+103.70.184.0/22
+103.70.220.0/22
+103.70.224.0/22
+103.70.236.0/22
+103.70.252.0/22
+103.71.0.0/22
+103.71.32.0/22
+103.71.48.0/22
+103.71.68.0/22
+103.71.72.0/22
+103.71.80.0/22
+103.71.84.0/22
+103.71.88.0/22
+103.71.120.0/22
+103.71.124.0/22
+103.71.128.0/22
+103.71.144.0/22
+103.71.196.0/22
+103.71.200.0/22
+103.71.232.0/22
+103.72.12.0/22
+103.72.16.0/22
+103.72.20.0/22
+103.72.24.0/22
+103.72.28.0/22
+103.72.32.0/22
+103.72.36.0/22
+103.72.40.0/22
+103.72.44.0/22
+103.72.48.0/22
+103.72.52.0/22
+103.72.116.0/22
+103.72.120.0/22
+103.72.124.0/22
+103.72.128.0/22
+103.72.132.0/22
+103.72.144.0/22
+103.72.148.0/22
+103.72.172.0/22
+103.72.180.0/22
+103.73.48.0/22
+103.73.88.0/22
+103.73.96.0/22
+103.73.116.0/22
+103.73.120.0/22
+103.73.128.0/22
+103.73.132.0/22
+103.73.136.0/22
+103.73.140.0/22
+103.73.168.0/22
+103.73.176.0/22
+103.73.204.0/22
+103.73.208.0/22
+103.73.240.0/22
+103.73.244.0/22
+103.73.248.0/22
+103.74.24.0/22
+103.74.28.0/22
+103.74.32.0/22
+103.74.36.0/22
+103.74.40.0/22
+103.74.44.0/22
+103.74.48.0/22
+103.74.56.0/22
+103.74.60.0/22
+103.74.80.0/22
+103.74.124.0/22
+103.74.148.0/22
+103.74.204.0/22
+103.74.232.0/22
+103.75.16.0/22
+103.75.88.0/22
+103.75.92.0/22
+103.75.104.0/22
+103.75.108.0/22
+103.75.112.0/22
+103.75.120.0/22
+103.75.128.0/22
+103.75.152.0/22
+103.75.236.0/24
+103.76.60.0/22
+103.76.64.0/22
+103.76.68.0/22
+103.76.72.0/22
+103.76.84.0/22
+103.76.92.0/22
+103.76.104.0/22
+103.76.216.0/22
+103.76.220.0/22
+103.76.224.0/22
+103.77.28.0/22
+103.77.52.0/22
+103.77.56.0/22
+103.77.72.0/22
+103.77.88.0/22
+103.77.92.0/22
+103.77.132.0/22
+103.77.148.0/22
+103.77.220.0/22
+103.78.56.0/22
+103.78.60.0/22
+103.78.68.0/22
+103.78.124.0/22
+103.78.172.0/22
+103.78.176.0/22
+103.78.196.0/22
+103.78.228.0/22
+103.79.24.0/22
+103.79.28.0/22
+103.79.36.0/22
+103.79.40.0/22
+103.79.44.0/22
+103.79.52.0/22
+103.79.56.0/22
+103.79.60.0/22
+103.79.64.0/22
+103.79.68.0/22
+103.79.80.0/22
+103.79.84.0/22
+103.79.120.0/22
+103.79.136.0/22
+103.79.188.0/22
+103.79.192.0/22
+103.79.196.0/22
+103.79.200.0/22
+103.79.204.0/22
+103.79.208.0/22
+103.79.212.0/22
+103.79.240.0/22
+103.80.24.0/22
+103.80.28.0/22
+103.80.44.0/22
+103.80.72.0/22
+103.80.176.0/22
+103.80.180.0/22
+103.80.184.0/22
+103.80.192.0/22
+103.80.200.0/22
+103.80.232.0/22
+103.81.4.0/22
+103.81.8.0/22
+103.81.16.0/22
+103.81.20.0/22
+103.81.44.0/22
+103.81.48.0/22
+103.81.96.0/22
+103.81.120.0/22
+103.81.148.0/22
+103.81.164.0/22
+103.81.168.0/22
+103.81.183.0/24
+103.81.184.0/22
+103.81.200.0/22
+103.81.232.0/22
+103.82.52.0/22
+103.82.60.0/22
+103.82.68.0/22
+103.82.84.0/22
+103.82.104.0/22
+103.82.224.0/22
+103.82.236.0/22
+103.83.44.0/22
+103.83.52.0/22
+103.83.60.0/22
+103.83.64.0/22
+103.83.72.0/22
+103.83.112.0/22
+103.83.120.0/22
+103.83.180.0/22
+103.84.0.0/22
+103.84.12.0/22
+103.84.16.0/22
+103.84.20.0/22
+103.84.24.0/22
+103.84.28.0/22
+103.84.48.0/22
+103.84.64.0/22
+103.84.72.0/22
+103.84.92.0/22
+103.84.108.0/22
+103.84.136.0/22
+103.85.20.0/22
+103.85.24.0/22
+103.85.44.0/22
+103.85.48.0/22
+103.85.136.0/22
+103.85.144.0/22
+103.85.164.0/22
+103.85.168.0/22
+103.85.172.0/22
+103.85.176.0/22
+103.85.224.0/22
+103.86.28.0/22
+103.86.32.0/22
+103.86.44.0/22
+103.86.60.0/22
+103.86.68.0/22
+103.86.80.0/22
+103.86.84.0/22
+103.86.88.0/22
+103.86.204.0/22
+103.86.208.0/22
+103.86.212.0/22
+103.86.216.0/22
+103.86.220.0/22
+103.86.224.0/22
+103.86.228.0/22
+103.86.232.0/22
+103.86.236.0/22
+103.86.240.0/22
+103.86.244.0/22
+103.86.248.0/22
+103.86.252.0/22
+103.87.0.0/22
+103.87.4.0/22
+103.87.20.0/22
+103.87.32.0/22
+103.87.72.0/22
+103.87.96.0/22
+103.87.132.0/22
+103.87.180.0/22
+103.87.224.0/22
+103.88.4.0/22
+103.88.8.0/22
+103.88.12.0/22
+103.88.16.0/22
+103.88.20.0/22
+103.88.32.0/22
+103.88.36.0/22
+103.88.60.0/22
+103.88.64.0/22
+103.88.72.0/22
+103.88.96.0/22
+103.88.100.0/22
+103.88.164.0/22
+103.88.176.0/22
+103.88.184.0/22
+103.88.188.0/22
+103.88.212.0/22
+103.89.28.0/22
+103.89.96.0/22
+103.89.100.0/22
+103.89.104.0/22
+103.89.108.0/22
+103.89.112.0/22
+103.89.116.0/22
+103.89.148.0/22
+103.89.172.0/22
+103.89.184.0/22
+103.89.188.0/22
+103.89.192.0/22
+103.89.196.0/22
+103.89.200.0/22
+103.89.204.0/22
+103.89.208.0/22
+103.89.212.0/22
+103.89.216.0/22
+103.89.220.0/22
+103.89.224.0/22
+103.89.228.0/22
+103.90.52.0/22
+103.90.100.0/22
+103.90.104.0/22
+103.90.108.0/22
+103.90.112.0/22
+103.90.116.0/22
+103.90.120.0/22
+103.90.124.0/22
+103.90.128.0/22
+103.90.132.0/22
+103.90.152.0/22
+103.90.168.0/22
+103.90.173.0/24
+103.90.176.0/22
+103.90.192.0/22
+103.91.36.0/22
+103.91.40.0/22
+103.91.152.0/22
+103.91.200.0/22
+103.91.212.0/22
+103.91.219.0/24
+103.91.236.0/22
+103.91.252.0/22
+103.92.0.0/22
+103.92.4.0/22
+103.92.8.0/22
+103.92.12.0/22
+103.92.48.0/22
+103.92.52.0/22
+103.92.56.0/22
+103.92.60.0/22
+103.92.64.0/22
+103.92.68.0/22
+103.92.72.0/22
+103.92.76.0/22
+103.92.80.0/22
+103.92.86.0/24
+103.92.88.0/22
+103.92.108.0/22
+103.92.124.0/22
+103.92.128.0/24
+103.92.156.0/22
+103.92.164.0/22
+103.92.168.0/22
+103.92.172.0/22
+103.92.176.0/22
+103.92.180.0/22
+103.92.184.0/22
+103.92.188.0/22
+103.92.192.0/22
+103.92.236.0/22
+103.92.240.0/22
+103.92.244.0/22
+103.92.248.0/22
+103.92.252.0/22
+103.93.0.0/22
+103.93.4.0/22
+103.93.28.0/22
+103.93.76.0/22
+103.93.84.0/22
+103.93.121.0/24
+103.93.152.0/22
+103.93.204.0/22
+103.94.12.0/22
+103.94.20.0/22
+103.94.28.0/22
+103.94.32.0/22
+103.94.36.0/22
+103.94.40.0/22
+103.94.44.0/22
+103.94.72.0/22
+103.94.88.0/22
+103.94.116.0/22
+103.94.160.0/22
+103.94.180.0/22
+103.94.200.0/22
+103.95.28.0/22
+103.95.52.0/22
+103.95.64.0/22
+103.95.68.0/22
+103.95.88.0/22
+103.95.92.0/22
+103.95.116.0/22
+103.95.128.0/22
+103.95.136.0/22
+103.95.140.0/22
+103.95.144.0/22
+103.95.152.0/22
+103.95.207.0/24
+103.95.216.0/22
+103.95.224.0/22
+103.95.236.0/22
+103.95.240.0/22
+103.95.244.0/22
+103.95.248.0/22
+103.95.252.0/22
+103.96.0.0/22
+103.96.8.0/22
+103.96.80.0/22
+103.96.124.0/22
+103.96.136.0/22
+103.96.140.0/24
+103.96.148.0/22
+103.96.152.0/22
+103.96.156.0/22
+103.96.160.0/22
+103.96.164.0/22
+103.96.168.0/22
+103.96.172.0/22
+103.96.176.0/22
+103.96.180.0/22
+103.96.184.0/22
+103.96.188.0/22
+103.96.192.0/22
+103.96.196.0/22
+103.96.200.0/22
+103.96.204.0/22
+103.96.208.0/22
+103.96.212.0/22
+103.96.216.0/22
+103.97.8.0/22
+103.97.12.0/22
+103.97.16.0/22
+103.97.20.0/22
+103.97.24.0/22
+103.97.28.0/22
+103.97.32.0/22
+103.97.36.0/22
+103.97.40.0/22
+103.97.56.0/22
+103.97.60.0/22
+103.97.64.0/22
+103.97.68.0/22
+103.97.72.0/22
+103.97.80.0/22
+103.97.112.0/22
+103.97.116.0/22
+103.97.128.0/22
+103.97.144.0/22
+103.97.148.0/22
+103.97.188.0/22
+103.97.192.0/22
+103.97.224.0/22
+103.97.228.0/23
+103.98.28.0/23
+103.98.40.0/22
+103.98.48.0/22
+103.98.56.0/22
+103.98.80.0/22
+103.98.88.0/22
+103.98.92.0/22
+103.98.96.0/22
+103.98.100.0/22
+103.98.124.0/22
+103.98.136.0/22
+103.98.140.0/22
+103.98.144.0/22
+103.98.164.0/22
+103.98.180.0/22
+103.98.196.0/22
+103.98.216.0/22
+103.98.224.0/22
+103.98.228.0/22
+103.98.232.0/22
+103.98.240.0/22
+103.98.244.0/22
+103.98.248.0/22
+103.98.252.0/22
+103.99.40.0/23
+103.99.52.0/22
+103.99.56.0/22
+103.99.60.0/22
+103.99.76.0/22
+103.99.104.0/22
+103.99.116.0/22
+103.99.120.0/22
+103.99.152.0/22
+103.99.220.0/22
+103.99.232.0/22
+103.99.236.0/22
+103.100.0.0/22
+103.100.32.0/22
+103.100.40.0/22
+103.100.48.0/22
+103.100.52.0/22
+103.100.56.0/22
+103.100.60.0/22
+103.100.64.0/22
+103.100.68.0/22
+103.100.88.0/22
+103.100.116.0/22
+103.100.140.0/22
+103.100.144.0/22
+103.100.236.0/22
+103.100.240.0/22
+103.100.248.0/22
+103.100.252.0/22
+103.101.4.0/22
+103.101.8.0/22
+103.101.12.0/22
+103.101.28.0/22
+103.101.60.0/22
+103.101.120.0/22
+103.101.124.0/22
+103.101.144.0/22
+103.101.148.0/22
+103.101.153.0/24
+103.101.180.0/22
+103.101.184.0/22
+103.102.76.0/22
+103.102.80.0/22
+103.102.168.0/22
+103.102.172.0/22
+103.102.180.0/22
+103.102.184.0/22
+103.102.188.0/22
+103.102.192.0/22
+103.102.196.0/22
+103.102.200.0/22
+103.102.208.0/22
+103.102.212.0/22
+103.103.12.0/22
+103.103.16.0/22
+103.103.36.0/22
+103.103.68.0/22
+103.103.72.0/22
+103.103.176.0/22
+103.103.188.0/22
+103.103.200.0/22
+103.103.204.0/22
+103.103.220.0/22
+103.103.224.0/22
+103.103.228.0/22
+103.103.232.0/22
+103.103.248.0/22
+103.103.252.0/22
+103.104.0.0/22
+103.104.4.0/22
+103.104.36.0/22
+103.104.40.0/22
+103.104.64.0/22
+103.104.104.0/22
+103.104.152.0/22
+103.104.168.0/22
+103.104.172.0/22
+103.104.188.0/22
+103.104.198.0/23
+103.104.252.0/22
+103.105.0.0/22
+103.105.4.0/22
+103.105.12.0/22
+103.105.16.0/22
+103.105.23.0/24
+103.105.56.0/22
+103.105.60.0/22
+103.105.116.0/22
+103.105.132.0/22
+103.105.180.0/22
+103.105.184.0/22
+103.105.200.0/22
+103.105.204.0/22
+103.105.220.0/22
+103.106.36.0/22
+103.106.40.0/22
+103.106.44.0/22
+103.106.60.0/22
+103.106.68.0/22
+103.106.96.0/22
+103.106.120.0/22
+103.106.128.0/22
+103.106.132.0/22
+103.106.160.0/22
+103.106.188.0/22
+103.106.196.0/22
+103.106.202.0/23
+103.106.212.0/22
+103.106.244.0/22
+103.106.252.0/22
+103.107.0.0/22
+103.107.8.0/24
+103.107.28.0/22
+103.107.32.0/22
+103.107.44.0/22
+103.107.72.0/22
+103.107.108.0/22
+103.107.164.0/22
+103.107.168.0/22
+103.107.188.0/22
+103.107.192.0/22
+103.107.208.0/22
+103.107.216.0/22
+103.107.220.0/22
+103.108.52.0/22
+103.108.64.0/22
+103.108.160.0/22
+103.108.164.0/22
+103.108.184.0/23
+103.108.188.0/23
+103.108.192.0/22
+103.108.196.0/22
+103.108.208.0/22
+103.108.212.0/22
+103.108.224.0/22
+103.108.251.0/24
+103.109.20.0/22
+103.109.48.0/22
+103.109.88.0/22
+103.109.106.0/23
+103.109.248.0/22
+103.110.32.0/22
+103.110.80.0/23
+103.110.92.0/22
+103.110.100.0/22
+103.110.116.0/22
+103.110.127.0/24
+103.110.128.0/23
+103.110.131.0/24
+103.110.132.0/22
+103.110.136.0/22
+103.110.152.0/22
+103.110.188.0/22
+103.110.204.0/22
+103.111.38.0/23
+103.111.64.0/22
+103.111.172.0/22
+103.111.252.0/22
+103.112.28.0/22
+103.112.68.0/22
+103.112.72.0/22
+103.112.88.0/22
+103.112.92.0/22
+103.112.96.0/22
+103.112.108.0/22
+103.112.112.0/22
+103.112.116.0/22
+103.112.140.0/22
+103.112.172.0/22
+103.112.184.0/22
+103.112.208.0/22
+103.113.4.0/22
+103.113.92.0/22
+103.113.144.0/22
+103.113.220.0/22
+103.113.232.0/22
+103.113.236.0/22
+103.114.4.0/22
+103.114.28.0/22
+103.114.68.0/22
+103.114.72.0/22
+103.114.100.0/22
+103.114.132.0/22
+103.114.148.0/22
+103.114.156.0/22
+103.114.176.0/22
+103.114.212.0/22
+103.114.236.0/22
+103.114.240.0/22
+103.115.16.0/22
+103.115.40.0/22
+103.115.44.0/22
+103.115.48.0/22
+103.115.52.0/22
+103.115.56.0/22
+103.115.60.0/22
+103.115.64.0/22
+103.115.68.0/22
+103.115.92.0/22
+103.115.120.0/22
+103.115.148.0/22
+103.115.204.0/23
+103.115.248.0/22
+103.116.20.0/22
+103.116.40.0/22
+103.116.64.0/22
+103.116.72.0/22
+103.116.76.0/22
+103.116.92.0/22
+103.116.120.0/22
+103.116.128.0/22
+103.116.132.0/23
+103.116.148.0/22
+103.116.184.0/22
+103.116.206.0/23
+103.116.220.0/22
+103.116.224.0/22
+103.116.228.0/22
+103.117.16.0/22
+103.117.72.0/22
+103.117.88.0/22
+103.117.132.0/22
+103.117.136.0/22
+103.117.220.0/22
+103.118.19.0/24
+103.118.36.0/22
+103.118.52.0/22
+103.118.56.0/22
+103.118.60.0/22
+103.118.64.0/22
+103.118.68.0/22
+103.118.72.0/22
+103.118.88.0/22
+103.118.173.0/24
+103.118.192.0/22
+103.118.196.0/22
+103.118.200.0/22
+103.118.204.0/22
+103.118.208.0/22
+103.118.212.0/22
+103.118.216.0/22
+103.118.220.0/22
+103.118.240.0/22
+103.118.244.0/22
+103.118.248.0/22
+103.118.252.0/22
+103.119.0.0/22
+103.119.12.0/22
+103.119.16.0/22
+103.119.28.0/22
+103.119.44.0/22
+103.119.104.0/22
+103.119.115.0/24
+103.119.156.0/22
+103.119.180.0/22
+103.119.200.0/22
+103.119.224.0/22
+103.120.52.0/22
+103.120.72.0/22
+103.120.76.0/24
+103.120.88.0/22
+103.120.96.0/22
+103.120.100.0/22
+103.120.140.0/22
+103.120.196.0/22
+103.120.224.0/22
+103.121.52.0/22
+103.121.92.0/22
+103.121.160.0/22
+103.121.164.0/22
+103.121.250.0/24
+103.121.252.0/22
+103.122.48.0/22
+103.122.176.0/22
+103.122.192.0/22
+103.122.240.0/22
+103.123.4.0/22
+103.123.88.0/22
+103.123.92.0/22
+103.123.116.0/22
+103.123.160.0/22
+103.123.176.0/22
+103.123.200.0/22
+103.123.204.0/22
+103.123.208.0/22
+103.123.212.0/22
+103.124.24.0/22
+103.124.48.0/22
+103.124.64.0/22
+103.124.212.0/22
+103.124.216.0/22
+103.125.20.0/22
+103.125.44.0/22
+103.125.132.0/22
+103.125.164.0/22
+103.125.196.0/22
+103.125.236.0/22
+103.125.248.0/22
+103.126.0.0/22
+103.126.16.0/22
+103.126.44.0/22
+103.129.52.0/22
+103.129.148.0/22
+103.130.132.0/22
+103.130.152.0/24
+103.130.160.0/22
+103.130.228.0/22
+103.131.20.0/22
+103.131.36.0/22
+103.131.152.0/22
+103.131.168.0/22
+103.131.176.0/22
+103.131.224.0/22
+103.131.228.0/22
+103.131.240.0/22
+103.192.8.0/22
+103.192.12.0/22
+103.192.16.0/22
+103.192.20.0/22
+103.192.24.0/22
+103.192.28.0/22
+103.192.48.0/22
+103.192.52.0/22
+103.192.56.0/22
+103.192.84.0/22
+103.192.88.0/22
+103.192.92.0/22
+103.192.96.0/22
+103.192.100.0/22
+103.192.104.0/22
+103.192.108.0/22
+103.192.112.0/22
+103.192.128.0/22
+103.192.136.0/22
+103.192.140.0/22
+103.192.164.0/22
+103.192.208.0/22
+103.192.216.0/22
+103.192.252.0/22
+103.193.40.0/22
+103.193.44.0/22
+103.193.120.0/22
+103.193.124.0/22
+103.193.144.0/22
+103.193.148.0/22
+103.193.160.0/22
+103.193.188.0/22
+103.193.192.0/22
+103.193.212.0/22
+103.193.216.0/22
+103.193.220.0/22
+103.193.224.0/22
+103.193.228.0/22
+103.193.232.0/22
+103.193.236.0/22
+103.193.240.0/22
+103.194.16.0/22
+103.195.104.0/22
+103.195.112.0/22
+103.195.136.0/22
+103.195.148.0/22
+103.195.152.0/22
+103.195.160.0/22
+103.195.192.0/22
+103.196.60.0/22
+103.196.64.0/22
+103.196.72.0/22
+103.196.88.0/22
+103.196.92.0/22
+103.196.96.0/22
+103.196.168.0/22
+103.196.204.0/22
+103.197.180.0/22
+103.197.228.0/22
+103.198.20.0/22
+103.198.60.0/22
+103.198.64.0/22
+103.198.72.0/22
+103.198.124.0/22
+103.198.156.0/22
+103.198.180.0/22
+103.198.196.0/22
+103.198.200.0/22
+103.198.216.0/22
+103.198.220.0/22
+103.198.224.0/22
+103.198.228.0/22
+103.198.232.0/22
+103.198.236.0/22
+103.198.240.0/22
+103.198.244.0/22
+103.199.164.0/22
+103.199.196.0/22
+103.199.228.0/22
+103.199.248.0/22
+103.199.252.0/22
+103.200.28.0/22
+103.200.32.0/22
+103.200.52.0/22
+103.200.64.0/22
+103.200.68.0/22
+103.200.136.0/22
+103.200.140.0/22
+103.200.144.0/22
+103.200.148.0/22
+103.200.152.0/22
+103.200.156.0/22
+103.200.160.0/22
+103.200.164.0/22
+103.200.168.0/22
+103.200.172.0/22
+103.200.176.0/22
+103.200.180.0/22
+103.200.184.0/22
+103.200.188.0/22
+103.200.192.0/22
+103.200.220.0/22
+103.200.224.0/22
+103.200.228.0/22
+103.200.232.0/22
+103.200.236.0/22
+103.200.240.0/22
+103.200.244.0/22
+103.200.248.0/22
+103.200.252.0/22
+103.201.0.0/22
+103.201.4.0/22
+103.201.8.0/22
+103.201.12.0/22
+103.201.16.0/22
+103.201.20.0/22
+103.201.28.0/22
+103.201.32.0/22
+103.201.36.0/22
+103.201.40.0/22
+103.201.44.0/22
+103.201.48.0/22
+103.201.52.0/22
+103.201.56.0/22
+103.201.60.0/22
+103.201.64.0/22
+103.201.76.0/22
+103.201.80.0/22
+103.201.84.0/22
+103.201.88.0/22
+103.201.92.0/22
+103.201.96.0/22
+103.201.100.0/22
+103.201.104.0/22
+103.201.108.0/22
+103.201.112.0/22
+103.201.116.0/22
+103.201.120.0/22
+103.201.152.0/22
+103.201.156.0/22
+103.201.160.0/22
+103.201.164.0/22
+103.201.168.0/22
+103.201.172.0/22
+103.201.176.0/22
+103.201.180.0/22
+103.201.184.0/22
+103.201.188.0/22
+103.201.192.0/22
+103.201.196.0/22
+103.201.200.0/22
+103.201.204.0/22
+103.201.208.0/22
+103.201.212.0/22
+103.201.216.0/22
+103.201.220.0/22
+103.201.224.0/22
+103.201.228.0/22
+103.201.232.0/22
+103.201.236.0/22
+103.201.240.0/22
+103.201.244.0/22
+103.201.248.0/22
+103.201.252.0/22
+103.202.0.0/22
+103.202.4.0/22
+103.202.8.0/22
+103.202.12.0/22
+103.202.16.0/22
+103.202.20.0/22
+103.202.24.0/22
+103.202.28.0/22
+103.202.32.0/22
+103.202.36.0/22
+103.202.40.0/22
+103.202.44.0/22
+103.202.56.0/22
+103.202.60.0/22
+103.202.64.0/22
+103.202.68.0/22
+103.202.72.0/22
+103.202.76.0/22
+103.202.80.0/22
+103.202.84.0/22
+103.202.88.0/22
+103.202.92.0/22
+103.202.96.0/22
+103.202.100.0/22
+103.202.104.0/22
+103.202.108.0/22
+103.202.112.0/22
+103.202.116.0/22
+103.202.120.0/22
+103.202.124.0/22
+103.202.128.0/22
+103.202.132.0/22
+103.202.136.0/22
+103.202.140.0/22
+103.202.144.0/22
+103.202.152.0/22
+103.202.156.0/22
+103.202.160.0/22
+103.202.164.0/22
+103.202.168.0/22
+103.202.172.0/22
+103.202.176.0/22
+103.202.180.0/22
+103.202.184.0/22
+103.202.188.0/22
+103.202.192.0/22
+103.202.196.0/22
+103.202.200.0/21
+103.202.212.0/22
+103.202.228.0/22
+103.202.236.0/22
+103.202.240.0/22
+103.202.244.0/22
+103.202.248.0/22
+103.202.252.0/22
+103.203.0.0/22
+103.203.4.0/22
+103.203.8.0/22
+103.203.12.0/22
+103.203.16.0/22
+103.203.20.0/22
+103.203.24.0/22
+103.203.28.0/22
+103.203.32.0/22
+103.203.52.0/22
+103.203.56.0/22
+103.203.96.0/22
+103.203.100.0/22
+103.203.104.0/22
+103.203.108.0/22
+103.203.112.0/22
+103.203.116.0/22
+103.203.120.0/22
+103.203.124.0/22
+103.203.128.0/22
+103.203.140.0/22
+103.203.192.0/22
+103.203.200.0/22
+103.203.212.0/22
+103.203.216.0/22
+103.204.24.0/22
+103.204.88.0/22
+103.204.112.0/22
+103.204.140.0/22
+103.204.144.0/22
+103.204.148.0/22
+103.204.152.0/22
+103.204.196.0/22
+103.204.232.0/22
+103.204.236.0/22
+103.205.8.0/22
+103.205.40.0/22
+103.205.44.0/22
+103.205.52.0/22
+103.205.120.0/22
+103.205.136.0/22
+103.205.162.0/24
+103.205.188.0/22
+103.205.196.0/22
+103.205.200.0/22
+103.205.236.0/22
+103.205.248.0/22
+103.206.0.0/22
+103.206.44.0/22
+103.206.108.0/22
+103.206.148.0/22
+103.207.48.0/22
+103.207.104.0/22
+103.207.164.0/22
+103.207.188.0/22
+103.207.192.0/22
+103.207.196.0/22
+103.207.200.0/22
+103.207.204.0/22
+103.207.208.0/22
+103.207.212.0/22
+103.207.220.0/22
+103.208.12.0/22
+103.208.16.0/22
+103.208.28.0/22
+103.208.40.0/22
+103.208.44.0/22
+103.208.48.0/22
+103.208.148.0/22
+103.209.112.0/22
+103.209.136.0/22
+103.209.200.0/22
+103.209.208.0/22
+103.209.216.0/22
+103.210.0.0/22
+103.210.20.0/22
+103.210.96.0/22
+103.210.156.0/22
+103.210.160.0/22
+103.210.164.0/22
+103.210.168.0/22
+103.210.172.0/22
+103.210.176.0/22
+103.210.180.0/22
+103.210.188.0/22
+103.210.216.0/22
+103.211.100.0/22
+103.211.156.0/22
+103.211.164.0/22
+103.211.192.0/22
+103.211.224.0/22
+103.211.248.0/22
+103.212.0.0/22
+103.212.4.0/22
+103.212.8.0/22
+103.212.12.0/22
+103.212.32.0/22
+103.212.44.0/22
+103.212.48.0/22
+103.212.84.0/22
+103.212.104.0/22
+103.212.148.0/22
+103.212.164.0/22
+103.212.196.0/22
+103.212.200.0/22
+103.212.228.0/22
+103.212.252.0/22
+103.213.40.0/22
+103.213.44.0/22
+103.213.48.0/22
+103.213.52.0/22
+103.213.56.0/22
+103.213.60.0/22
+103.213.64.0/22
+103.213.68.0/22
+103.213.72.0/22
+103.213.76.0/22
+103.213.80.0/22
+103.213.84.0/22
+103.213.88.0/22
+103.213.92.0/22
+103.213.136.0/22
+103.213.140.0/22
+103.213.144.0/22
+103.213.148.0/22
+103.213.152.0/22
+103.213.156.0/22
+103.213.160.0/22
+103.213.164.0/22
+103.213.168.0/22
+103.213.172.0/22
+103.213.176.0/22
+103.213.180.0/22
+103.213.184.0/22
+103.213.188.0/22
+103.213.248.0/22
+103.214.32.0/22
+103.214.84.0/22
+103.214.168.0/22
+103.214.212.0/22
+103.214.240.0/22
+103.214.244.0/22
+103.215.28.0/22
+103.215.32.0/22
+103.215.44.0/22
+103.215.48.0/22
+103.215.100.0/22
+103.215.104.0/22
+103.215.108.0/22
+103.215.116.0/22
+103.215.120.0/22
+103.215.140.0/22
+103.215.184.0/22
+103.215.228.0/22
+103.216.4.0/22
+103.216.8.0/22
+103.216.12.0/22
+103.216.16.0/22
+103.216.20.0/22
+103.216.24.0/22
+103.216.28.0/22
+103.216.32.0/22
+103.216.36.0/22
+103.216.40.0/22
+103.216.44.0/22
+103.216.64.0/22
+103.216.108.0/22
+103.216.136.0/22
+103.216.224.0/22
+103.216.228.0/22
+103.216.240.0/22
+103.216.244.0/22
+103.216.248.0/22
+103.217.0.0/22
+103.217.4.0/22
+103.217.8.0/22
+103.217.12.0/22
+103.217.16.0/22
+103.217.20.0/22
+103.217.24.0/22
+103.217.28.0/22
+103.217.32.0/22
+103.217.36.0/22
+103.217.40.0/22
+103.217.44.0/22
+103.217.48.0/22
+103.217.52.0/22
+103.217.56.0/22
+103.217.60.0/22
+103.217.168.0/22
+103.217.180.0/22
+103.217.184.0/22
+103.217.188.0/22
+103.217.192.0/22
+103.217.196.0/22
+103.217.200.0/22
+103.217.204.0/22
+103.218.0.0/22
+103.218.92.0/22
+103.218.184.0/22
+103.218.192.0/22
+103.218.196.0/22
+103.218.200.0/22
+103.218.204.0/22
+103.218.208.0/22
+103.218.212.0/22
+103.218.216.0/22
+103.219.24.0/22
+103.219.64.0/22
+103.219.84.0/22
+103.219.88.0/22
+103.219.92.0/22
+103.219.96.0/22
+103.219.100.0/22
+103.219.176.0/22
+103.219.184.0/22
+103.220.56.0/22
+103.220.60.0/22
+103.220.64.0/22
+103.220.92.0/22
+103.220.96.0/22
+103.220.100.0/22
+103.220.104.0/22
+103.220.108.0/22
+103.220.116.0/22
+103.220.120.0/22
+103.220.124.0/22
+103.220.128.0/22
+103.220.132.0/22
+103.220.136.0/22
+103.220.140.0/22
+103.220.144.0/22
+103.220.148.0/22
+103.220.152.0/22
+103.220.160.0/22
+103.220.164.0/22
+103.220.168.0/22
+103.220.172.0/22
+103.220.176.0/22
+103.220.180.0/22
+103.220.184.0/22
+103.220.188.0/22
+103.220.192.0/22
+103.220.196.0/22
+103.220.200.0/22
+103.220.240.0/22
+103.220.244.0/22
+103.220.248.0/22
+103.220.252.0/22
+103.221.0.0/22
+103.221.4.0/22
+103.221.8.0/22
+103.221.12.0/22
+103.221.16.0/22
+103.221.20.0/22
+103.221.24.0/22
+103.221.28.0/22
+103.221.32.0/22
+103.221.36.0/22
+103.221.40.0/22
+103.221.44.0/22
+103.221.48.0/22
+103.221.92.0/22
+103.221.96.0/22
+103.221.100.0/22
+103.221.104.0/22
+103.221.108.0/22
+103.221.112.0/22
+103.221.116.0/22
+103.221.120.0/22
+103.221.124.0/22
+103.221.128.0/22
+103.221.136.0/22
+103.221.144.0/22
+103.221.148.0/22
+103.221.152.0/22
+103.221.160.0/22
+103.221.164.0/22
+103.221.168.0/22
+103.221.172.0/22
+103.221.176.0/22
+103.221.200.0/22
+103.221.204.0/22
+103.222.52.0/22
+103.222.112.0/22
+103.222.116.0/22
+103.222.120.0/22
+103.222.124.0/22
+103.222.128.0/22
+103.222.132.0/22
+103.222.136.0/22
+103.222.140.0/22
+103.222.144.0/22
+103.222.148.0/22
+103.222.152.0/22
+103.222.156.0/22
+103.222.160.0/22
+103.222.168.0/22
+103.222.176.0/22
+103.222.180.0/22
+103.222.184.0/22
+103.222.188.0/22
+103.222.192.0/22
+103.222.216.0/22
+103.222.220.0/22
+103.222.224.0/22
+103.222.228.0/22
+103.222.232.0/22
+103.222.240.0/22
+103.222.244.0/22
+103.223.16.0/22
+103.223.20.0/22
+103.223.24.0/22
+103.223.28.0/22
+103.223.32.0/22
+103.223.36.0/22
+103.223.40.0/22
+103.223.44.0/22
+103.223.48.0/22
+103.223.52.0/22
+103.223.56.0/22
+103.223.60.0/22
+103.223.64.0/22
+103.223.68.0/22
+103.223.72.0/22
+103.223.76.0/22
+103.223.80.0/22
+103.223.84.0/22
+103.223.88.0/22
+103.223.92.0/22
+103.223.96.0/22
+103.223.100.0/22
+103.223.104.0/22
+103.223.108.0/22
+103.223.112.0/22
+103.223.116.0/22
+103.223.120.0/22
+103.223.124.0/22
+103.223.128.0/22
+103.223.132.0/22
+103.223.144.0/22
+103.223.148.0/22
+103.223.152.0/22
+103.223.156.0/22
+103.223.160.0/22
+103.223.164.0/22
+103.223.168.0/22
+103.223.172.0/22
+103.223.176.0/22
+103.223.180.0/22
+103.223.192.0/22
+103.223.196.0/22
+103.223.200.0/22
+103.223.204.0/22
+103.223.208.0/22
+103.223.212.0/22
+103.223.216.0/22
+103.223.220.0/22
+103.223.224.0/22
+103.223.228.0/22
+103.223.232.0/22
+103.223.236.0/22
+103.223.240.0/22
+103.223.244.0/22
+103.223.248.0/22
+103.223.252.0/22
+103.224.0.0/22
+103.224.40.0/22
+103.224.44.0/22
+103.224.60.0/22
+103.224.80.0/22
+103.224.220.0/22
+103.224.224.0/22
+103.224.232.0/22
+103.225.84.0/22
+103.226.16.0/22
+103.226.40.0/22
+103.226.56.0/22
+103.226.60.0/22
+103.226.80.0/22
+103.226.132.0/22
+103.226.156.0/22
+103.226.180.0/22
+103.226.196.0/22
+103.227.48.0/22
+103.227.72.0/22
+103.227.76.0/22
+103.227.80.0/22
+103.227.100.0/22
+103.227.132.0/22
+103.227.196.0/22
+103.227.204.0/22
+103.227.212.0/22
+103.227.228.0/22
+103.228.12.0/22
+103.228.28.0/22
+103.228.68.0/22
+103.228.88.0/22
+103.228.128.0/22
+103.228.136.0/22
+103.228.160.0/22
+103.228.176.0/22
+103.228.208.0/22
+103.228.228.0/22
+103.228.232.0/22
+103.229.20.0/22
+103.229.60.0/22
+103.229.136.0/22
+103.229.172.0/22
+103.229.216.0/22
+103.229.220.0/22
+103.229.228.0/22
+103.229.236.0/22
+103.229.240.0/22
+103.230.0.0/22
+103.230.28.0/22
+103.230.44.0/22
+103.230.96.0/22
+103.230.200.0/22
+103.230.204.0/22
+103.230.212.0/22
+103.231.16.0/22
+103.231.20.0/22
+103.231.64.0/22
+103.231.68.0/22
+103.231.144.0/22
+103.231.180.0/22
+103.231.184.0/22
+103.231.244.0/22
+103.232.4.0/22
+103.232.144.0/22
+103.232.188.0/22
+103.232.212.0/22
+103.233.4.0/22
+103.233.44.0/22
+103.233.52.0/22
+103.233.104.0/22
+103.233.128.0/22
+103.233.136.0/22
+103.234.0.0/22
+103.234.20.0/22
+103.234.128.0/22
+103.234.172.0/22
+103.234.180.0/22
+103.235.16.0/22
+103.235.48.0/22
+103.235.56.0/22
+103.235.60.0/22
+103.235.80.0/22
+103.235.84.0/22
+103.235.128.0/22
+103.235.132.0/22
+103.235.136.0/22
+103.235.140.0/22
+103.235.144.0/22
+103.235.148.0/22
+103.235.184.0/22
+103.235.192.0/22
+103.235.200.0/22
+103.235.220.0/22
+103.235.224.0/22
+103.235.228.0/22
+103.235.232.0/22
+103.235.236.0/22
+103.235.240.0/22
+103.235.244.0/22
+103.235.248.0/22
+103.235.252.0/22
+103.236.0.0/22
+103.236.4.0/22
+103.236.8.0/22
+103.236.12.0/22
+103.236.16.0/22
+103.236.20.0/22
+103.236.24.0/22
+103.236.28.0/22
+103.236.32.0/22
+103.236.36.0/22
+103.236.40.0/22
+103.236.44.0/22
+103.236.48.0/22
+103.236.52.0/22
+103.236.56.0/22
+103.236.60.0/22
+103.236.64.0/22
+103.236.68.0/22
+103.236.72.0/22
+103.236.76.0/22
+103.236.80.0/22
+103.236.84.0/22
+103.236.88.0/22
+103.236.92.0/22
+103.236.96.0/22
+103.236.184.0/22
+103.236.220.0/22
+103.236.232.0/22
+103.236.244.0/22
+103.236.248.0/22
+103.237.0.0/22
+103.237.4.0/22
+103.237.8.0/22
+103.237.12.0/22
+103.237.24.0/22
+103.237.28.0/22
+103.237.68.0/22
+103.237.88.0/22
+103.237.152.0/22
+103.237.176.0/22
+103.237.180.0/22
+103.237.184.0/22
+103.237.188.0/22
+103.237.192.0/22
+103.237.196.0/22
+103.237.200.0/22
+103.237.204.0/22
+103.237.208.0/22
+103.237.212.0/22
+103.237.216.0/22
+103.237.220.0/22
+103.237.224.0/22
+103.237.228.0/22
+103.237.232.0/22
+103.237.236.0/22
+103.237.240.0/22
+103.237.244.0/22
+103.237.248.0/22
+103.237.252.0/22
+103.238.0.0/22
+103.238.4.0/22
+103.238.16.0/22
+103.238.20.0/22
+103.238.24.0/22
+103.238.36.0/22
+103.238.40.0/22
+103.238.44.0/22
+103.238.48.0/22
+103.238.52.0/22
+103.238.56.0/22
+103.238.88.0/22
+103.238.92.0/22
+103.238.96.0/22
+103.238.132.0/22
+103.238.164.0/22
+103.238.168.0/22
+103.238.172.0/22
+103.238.176.0/22
+103.238.180.0/22
+103.238.188.0/22
+103.238.196.0/22
+103.238.204.0/22
+103.238.252.0/22
+103.239.0.0/22
+103.239.44.0/22
+103.239.68.0/22
+103.239.96.0/22
+103.239.156.0/22
+103.239.176.0/22
+103.239.180.0/22
+103.239.184.0/22
+103.239.192.0/22
+103.239.196.0/22
+103.239.204.0/22
+103.239.208.0/22
+103.239.224.0/22
+103.240.16.0/22
+103.240.156.0/22
+103.240.188.0/22
+103.240.244.0/22
+103.241.12.0/22
+103.241.72.0/22
+103.241.92.0/22
+103.241.96.0/22
+103.241.160.0/22
+103.241.184.0/22
+103.241.188.0/22
+103.241.220.0/22
+103.242.64.0/22
+103.242.128.0/22
+103.242.132.0/22
+103.242.160.0/22
+103.242.168.0/22
+103.242.172.0/22
+103.242.176.0/22
+103.242.200.0/22
+103.242.212.0/22
+103.242.220.0/22
+103.242.240.0/22
+103.243.136.0/22
+103.243.252.0/22
+103.244.16.0/22
+103.244.58.0/23
+103.244.60.0/22
+103.244.64.0/22
+103.244.68.0/22
+103.244.72.0/22
+103.244.76.0/22
+103.244.80.0/22
+103.244.84.0/22
+103.244.116.0/22
+103.244.164.0/22
+103.244.232.0/22
+103.244.252.0/22
+103.245.23.0/24
+103.245.52.0/22
+103.245.60.0/22
+103.245.80.0/22
+103.245.124.0/22
+103.245.128.0/22
+103.246.12.0/22
+103.246.120.0/22
+103.246.124.0/22
+103.246.132.0/22
+103.246.156.0/22
+103.247.168.0/22
+103.247.172.0/22
+103.247.176.0/22
+103.247.200.0/22
+103.247.212.0/22
+103.248.0.0/23
+103.248.64.0/22
+103.248.124.0/22
+103.248.168.0/22
+103.248.192.0/22
+103.248.212.0/22
+103.248.220.0/22
+103.248.224.0/22
+103.249.8.0/22
+103.249.12.0/22
+103.249.52.0/22
+103.249.104.0/22
+103.249.128.0/22
+103.249.136.0/22
+103.249.144.0/22
+103.249.164.0/22
+103.249.168.0/22
+103.249.172.0/22
+103.249.176.0/22
+103.249.188.0/22
+103.249.192.0/22
+103.249.244.0/22
+103.249.252.0/22
+103.250.224.0/22
+103.250.236.0/22
+103.250.252.0/22
+103.251.36.0/22
+103.251.84.0/22
+103.251.128.0/22
+103.251.192.0/22
+103.251.204.0/22
+103.251.236.0/22
+103.252.28.0/22
+103.252.36.0/22
+103.252.64.0/22
+103.252.96.0/22
+103.252.104.0/22
+103.252.172.0/22
+103.253.4.0/22
+103.253.204.0/22
+103.253.220.0/22
+103.253.224.0/22
+103.253.232.0/22
+103.254.8.0/22
+103.254.20.0/22
+103.254.64.0/22
+103.254.68.0/22
+103.254.72.0/22
+103.254.76.0/22
+103.254.112.0/22
+103.254.176.0/22
+103.254.196.0/24
+103.254.220.0/22
+103.255.56.0/22
+103.255.68.0/22
+103.255.92.0/22
+103.255.136.0/22
+103.255.140.0/22
+103.255.184.0/22
+103.255.212.0/22
+103.255.228.0/22
+106.0.44.0/22
+106.2.0.0/15
+106.11.0.0/16
+106.12.0.0/14
+106.50.0.0/16
+106.52.0.0/14
+106.74.0.0/16
+106.75.0.0/16
+109.244.0.0/16
+110.34.40.0/22
+110.34.44.0/22
+110.40.0.0/14
+110.44.144.0/20
+110.48.0.0/16
+110.51.0.0/16
+110.56.0.0/13
+110.75.0.0/17
+110.75.128.0/19
+110.75.160.0/19
+110.75.192.0/18
+110.76.0.0/19
+110.76.32.0/19
+110.76.132.0/22
+110.76.192.0/18
+110.77.0.0/17
+110.92.68.0/22
+110.165.32.0/19
+110.172.192.0/18
+110.173.0.0/19
+110.173.32.0/20
+110.173.96.0/19
+110.173.192.0/19
+110.232.32.0/19
+111.66.0.0/16
+111.67.192.0/20
+111.68.64.0/19
+111.91.192.0/19
+111.92.248.0/22
+111.92.252.0/22
+111.118.200.0/21
+111.119.64.0/18
+111.119.128.0/19
+111.128.0.0/11
+111.208.0.0/14
+111.212.0.0/14
+111.221.28.0/24
+111.221.128.0/17
+111.222.0.0/16
+111.223.4.0/22
+111.223.8.0/22
+111.223.16.0/22
+111.223.240.0/22
+111.223.248.0/22
+111.228.0.0/14
+111.235.96.0/19
+111.235.160.0/19
+112.73.0.0/16
+112.74.0.0/15
+112.124.0.0/14
+112.128.0.0/14
+113.11.192.0/19
+113.21.232.0/22
+113.21.236.0/22
+113.31.0.0/16
+113.44.0.0/14
+113.48.0.0/14
+113.52.160.0/19
+113.52.228.0/22
+113.130.96.0/20
+113.130.112.0/21
+113.197.100.0/22
+113.202.0.0/16
+113.208.96.0/19
+113.212.0.0/18
+113.212.100.0/22
+113.214.0.0/15
+114.28.0.0/16
+114.31.64.0/22
+114.31.68.0/22
+114.64.0.0/14
+114.68.0.0/16
+114.110.0.0/20
+114.110.64.0/18
+114.111.0.0/19
+114.111.160.0/19
+114.112.0.0/14
+114.116.0.0/16
+114.117.0.0/16
+114.118.0.0/16
+114.119.128.0/18
+114.119.192.0/21
+114.119.200.0/22
+114.119.204.0/22
+114.119.208.0/20
+114.132.0.0/16
+114.141.80.0/22
+114.141.84.0/22
+114.141.128.0/18
+114.196.0.0/15
+114.208.0.0/14
+114.215.0.0/16
+115.28.0.0/15
+115.31.64.0/22
+115.31.68.0/22
+115.31.72.0/22
+115.31.76.0/22
+115.32.0.0/14
+115.42.56.0/22
+115.44.0.0/15
+115.47.0.0/16
+115.84.0.0/18
+115.84.192.0/19
+115.100.0.0/14
+115.104.0.0/14
+115.120.0.0/14
+115.124.16.0/20
+115.159.0.0/16
+115.182.0.0/16
+115.183.0.0/16
+115.187.0.0/22
+115.187.4.0/22
+115.187.8.0/22
+115.187.12.0/22
+116.58.128.0/20
+116.58.208.0/20
+116.66.0.0/17
+116.66.176.0/22
+116.68.136.0/22
+116.68.140.0/22
+116.68.176.0/22
+116.68.180.0/22
+116.69.0.0/16
+116.76.0.0/15
+116.85.0.0/16
+116.89.144.0/20
+116.89.240.0/22
+116.90.80.0/20
+116.90.184.0/21
+116.193.16.0/20
+116.193.32.0/19
+116.193.152.0/22
+116.193.164.0/22
+116.196.0.0/16
+116.197.160.0/22
+116.197.164.0/22
+116.198.0.0/16
+116.199.128.0/19
+116.204.0.0/17
+116.204.132.0/22
+116.204.168.0/22
+116.204.216.0/22
+116.205.0.0/16
+116.206.92.0/22
+116.206.100.0/22
+116.206.176.0/22
+116.212.160.0/20
+116.213.44.0/22
+116.213.64.0/18
+116.213.128.0/17
+116.214.64.0/20
+116.214.128.0/17
+116.215.0.0/16
+116.251.64.0/18
+116.254.104.0/22
+116.254.108.0/22
+117.48.0.0/14
+117.53.48.0/20
+117.53.176.0/20
+117.58.0.0/17
+117.59.0.0/16
+117.72.0.0/15
+117.74.128.0/17
+117.103.16.0/20
+117.103.128.0/20
+117.106.0.0/15
+117.112.0.0/13
+117.120.64.0/18
+117.120.128.0/17
+117.121.0.0/17
+117.121.192.0/21
+117.124.0.0/14
+118.24.0.0/15
+118.26.0.0/19
+118.26.32.0/22
+118.26.36.0/22
+118.26.40.0/21
+118.26.56.0/21
+118.26.64.0/19
+118.26.96.0/21
+118.26.104.0/21
+118.26.112.0/21
+118.26.120.0/21
+118.26.128.0/17
+118.28.0.0/15
+118.30.0.0/16
+118.31.0.0/16
+118.64.0.0/15
+118.66.0.0/16
+118.67.112.0/20
+118.89.0.0/16
+118.91.240.0/20
+118.102.16.0/20
+118.103.164.0/22
+118.103.172.0/22
+118.103.176.0/22
+118.107.180.0/22
+118.126.0.0/16
+118.178.0.0/16
+118.184.0.0/17
+118.184.128.0/17
+118.186.0.0/15
+118.188.0.0/16
+118.190.0.0/16
+118.191.0.0/16
+118.192.0.0/16
+118.193.0.0/21
+118.193.8.0/21
+118.193.32.0/19
+118.193.64.0/20
+118.193.96.0/19
+118.193.128.0/17
+118.194.0.0/17
+118.194.128.0/17
+118.195.0.0/17
+118.195.128.0/17
+118.196.0.0/14
+118.242.0.0/16
+119.2.0.0/19
+119.2.128.0/17
+119.3.0.0/16
+119.8.0.0/16
+119.10.0.0/17
+119.15.136.0/21
+119.16.0.0/16
+119.18.192.0/20
+119.18.208.0/21
+119.18.224.0/20
+119.18.240.0/20
+119.19.0.0/16
+119.20.0.0/14
+119.27.160.0/19
+119.27.192.0/18
+119.28.0.0/15
+119.30.48.0/20
+119.31.192.0/19
+119.37.192.0/18
+119.38.128.0/18
+119.38.192.0/20
+119.38.208.0/20
+119.38.224.0/19
+119.40.0.0/18
+119.40.64.0/20
+119.42.52.0/22
+119.42.136.0/21
+119.57.0.0/16
+119.58.0.0/16
+119.61.0.0/16
+119.63.32.0/19
+119.75.208.0/20
+119.78.0.0/15
+119.80.0.0/16
+119.88.0.0/14
+119.161.120.0/22
+119.161.124.0/22
+119.161.128.0/17
+119.235.128.0/18
+119.253.0.0/16
+119.254.0.0/15
+120.24.0.0/14
+120.30.0.0/16
+120.31.0.0/16
+120.44.0.0/14
+120.48.0.0/15
+120.52.0.0/16
+120.53.0.0/16
+120.54.0.0/15
+120.64.0.0/14
+120.72.32.0/19
+120.72.128.0/17
+120.76.0.0/14
+120.92.0.0/16
+120.132.0.0/17
+120.132.128.0/17
+120.134.0.0/15
+120.136.16.0/22
+120.136.20.0/22
+120.136.128.0/18
+120.137.0.0/17
+121.0.16.0/20
+121.4.0.0/15
+121.36.0.0/16
+121.37.0.0/16
+121.38.0.0/15
+121.40.0.0/14
+121.46.0.0/18
+121.46.76.0/22
+121.46.128.0/17
+121.47.0.0/16
+121.51.0.0/16
+121.52.208.0/20
+121.54.188.0/22
+121.58.144.0/20
+121.79.128.0/18
+121.89.0.0/16
+121.101.208.0/20
+121.196.0.0/14
+121.255.0.0/16
+122.0.64.0/18
+122.0.128.0/17
+122.8.0.0/16
+122.9.0.0/16
+122.10.128.0/22
+122.10.132.0/23
+122.10.134.0/23
+122.10.136.0/23
+122.10.138.0/23
+122.10.140.0/22
+122.10.144.0/20
+122.10.160.0/19
+122.10.192.0/20
+122.10.208.0/21
+122.10.216.0/22
+122.10.220.0/22
+122.10.224.0/19
+122.11.0.0/17
+122.12.0.0/16
+122.14.0.0/17
+122.14.128.0/18
+122.14.192.0/18
+122.48.0.0/16
+122.49.0.0/18
+122.51.0.0/16
+122.102.0.0/20
+122.112.0.0/18
+122.112.64.0/18
+122.112.128.0/17
+122.113.0.0/16
+122.114.0.0/16
+122.115.0.0/17
+122.115.128.0/19
+122.115.160.0/19
+122.115.192.0/19
+122.115.224.0/19
+122.119.0.0/16
+122.128.100.0/22
+122.144.128.0/17
+122.152.192.0/18
+122.198.0.0/16
+122.200.40.0/22
+122.200.44.0/22
+122.200.64.0/18
+122.201.48.0/20
+122.248.48.0/20
+123.0.128.0/18
+123.49.128.0/17
+123.56.0.0/15
+123.58.0.0/20
+123.58.16.0/20
+123.58.32.0/19
+123.58.64.0/19
+123.58.96.0/19
+123.58.128.0/18
+123.58.192.0/19
+123.58.224.0/20
+123.58.240.0/20
+123.59.0.0/16
+123.60.0.0/16
+123.61.0.0/16
+123.62.0.0/16
+123.99.128.0/17
+123.108.128.0/20
+123.108.208.0/20
+123.136.80.0/20
+123.137.0.0/16
+123.176.60.0/22
+123.199.128.0/17
+123.206.0.0/15
+123.242.0.0/17
+123.242.192.0/22
+123.242.196.0/22
+123.249.0.0/16
+123.254.96.0/22
+123.254.100.0/22
+124.16.0.0/15
+124.22.0.0/15
+124.28.192.0/18
+124.29.0.0/17
+124.40.112.0/20
+124.40.128.0/18
+124.40.240.0/22
+124.42.0.0/17
+124.47.0.0/18
+124.108.8.0/21
+124.108.40.0/21
+124.126.0.0/15
+124.147.128.0/17
+124.150.137.0/24
+124.151.0.0/16
+124.174.0.0/15
+124.192.0.0/15
+124.196.0.0/16
+124.200.0.0/13
+124.220.0.0/14
+124.240.0.0/17
+124.242.0.0/16
+124.243.192.0/18
+124.248.0.0/17
+124.249.0.0/16
+124.250.0.0/15
+125.31.192.0/18
+125.58.128.0/17
+125.61.128.0/17
+125.62.0.0/18
+125.96.0.0/15
+125.98.0.0/16
+125.169.0.0/16
+125.210.0.0/16
+125.213.0.0/17
+125.215.0.0/18
+125.254.128.0/18
+125.254.192.0/18
+128.108.0.0/16
+129.28.0.0/16
+129.204.0.0/16
+129.211.0.0/16
+132.232.0.0/16
+134.175.0.0/16
+137.59.59.0/24
+137.59.88.0/22
+139.5.56.0/22
+139.5.60.0/22
+139.5.80.0/22
+139.5.92.0/22
+139.5.108.0/22
+139.5.128.0/22
+139.5.160.0/22
+139.5.192.0/22
+139.5.208.0/22
+139.5.244.0/22
+139.9.0.0/16
+139.129.0.0/16
+139.148.0.0/16
+139.155.0.0/16
+139.159.0.0/16
+139.176.0.0/16
+139.183.0.0/16
+139.186.0.0/16
+139.196.0.0/14
+139.217.0.0/16
+139.219.0.0/16
+139.220.0.0/15
+139.224.0.0/16
+140.143.0.0/16
+140.179.0.0/16
+140.205.0.0/16
+140.210.0.0/16
+144.48.8.0/22
+144.48.88.0/22
+144.48.156.0/22
+144.48.180.0/22
+144.48.184.0/22
+144.48.204.0/22
+144.48.208.0/22
+144.48.212.0/22
+144.48.220.0/22
+144.48.252.0/22
+146.56.192.0/18
+146.196.72.0/22
+146.196.92.0/22
+146.196.112.0/22
+146.196.116.0/22
+146.196.124.0/22
+148.70.0.0/16
+150.129.192.0/22
+150.129.216.0/22
+150.158.0.0/16
+150.242.0.0/22
+150.242.4.0/22
+150.242.8.0/22
+150.242.28.0/22
+150.242.44.0/22
+150.242.48.0/22
+150.242.52.0/22
+150.242.56.0/22
+150.242.76.0/22
+150.242.80.0/22
+150.242.92.0/22
+150.242.96.0/22
+150.242.112.0/22
+150.242.116.0/22
+150.242.120.0/22
+150.242.156.0/22
+150.242.160.0/22
+150.242.164.0/22
+150.242.168.0/22
+150.242.184.0/22
+150.242.188.0/22
+150.242.192.0/22
+150.242.212.0/22
+150.242.224.0/22
+150.242.228.0/22
+150.242.236.0/22
+150.242.240.0/22
+150.242.244.0/22
+150.242.248.0/22
+152.104.128.0/17
+152.136.0.0/16
+154.8.128.0/17
+157.119.0.0/22
+157.119.8.0/22
+157.119.12.0/22
+157.119.16.0/22
+157.119.28.0/22
+157.119.68.0/22
+157.119.112.0/22
+157.119.132.0/22
+157.119.136.0/22
+157.119.140.0/22
+157.119.144.0/22
+157.119.148.0/22
+157.119.152.0/22
+157.119.156.0/22
+157.119.160.0/22
+157.119.164.0/22
+157.119.172.0/22
+157.119.192.0/22
+157.119.196.0/22
+157.119.240.0/22
+157.119.252.0/22
+159.75.0.0/16
+159.226.0.0/16
+160.19.208.0/22
+160.19.216.0/22
+160.20.48.0/22
+160.202.168.0/22
+160.202.216.0/22
+160.202.220.0/22
+160.202.224.0/22
+160.202.228.0/22
+160.202.232.0/22
+160.202.236.0/22
+160.202.240.0/22
+160.202.244.0/22
+160.202.248.0/22
+160.202.252.0/22
+160.238.64.0/22
+161.189.0.0/16
+161.207.0.0/16
+162.14.0.0/16
+162.105.0.0/16
+163.47.4.0/22
+163.53.0.0/22
+163.53.4.0/22
+163.53.8.0/22
+163.53.12.0/22
+163.53.36.0/22
+163.53.40.0/22
+163.53.44.0/22
+163.53.56.0/22
+163.53.60.0/22
+163.53.64.0/22
+163.53.88.0/22
+163.53.92.0/22
+163.53.96.0/22
+163.53.100.0/22
+163.53.104.0/22
+163.53.108.0/22
+163.53.112.0/22
+163.53.116.0/22
+163.53.120.0/22
+163.53.124.0/22
+163.53.128.0/22
+163.53.132.0/22
+163.53.136.0/22
+163.53.160.0/22
+163.53.164.0/22
+163.53.172.0/22
+163.53.188.0/22
+163.53.240.0/22
+164.52.0.0/17
+166.111.0.0/16
+167.139.0.0/16
+167.189.0.0/16
+167.220.244.0/22
+168.160.0.0/16
+170.179.0.0/16
+171.84.0.0/14
+172.81.192.0/18
+175.24.0.0/14
+175.46.0.0/15
+175.48.0.0/12
+175.64.0.0/11
+175.102.0.0/16
+175.111.144.0/22
+175.111.148.0/22
+175.111.152.0/22
+175.111.156.0/22
+175.111.160.0/22
+175.111.164.0/22
+175.111.168.0/22
+175.111.172.0/22
+175.111.184.0/22
+175.158.96.0/22
+175.176.156.0/22
+175.176.176.0/22
+175.176.192.0/22
+175.178.0.0/16
+180.76.0.0/16
+180.78.0.0/15
+180.86.0.0/16
+180.94.56.0/21
+180.94.96.0/20
+180.94.120.0/22
+180.94.124.0/22
+180.148.224.0/19
+180.149.236.0/22
+180.150.160.0/19
+180.178.112.0/22
+180.178.116.0/22
+180.178.192.0/18
+180.184.0.0/14
+180.188.0.0/17
+180.200.252.0/22
+180.202.0.0/15
+180.210.212.0/22
+180.210.224.0/19
+180.222.224.0/19
+180.233.0.0/18
+180.233.64.0/19
+180.233.144.0/22
+180.235.64.0/19
+180.235.112.0/22
+180.235.136.0/22
+182.16.144.0/22
+182.16.148.0/22
+182.16.192.0/19
+182.18.0.0/17
+182.48.96.0/19
+182.49.0.0/16
+182.50.0.0/20
+182.50.112.0/20
+182.51.0.0/16
+182.54.244.0/22
+182.61.0.0/16
+182.80.0.0/14
+182.92.0.0/16
+182.157.0.0/16
+182.160.64.0/19
+182.174.0.0/15
+182.236.128.0/17
+182.237.24.0/22
+182.237.28.0/22
+182.238.0.0/16
+182.239.0.0/19
+182.254.0.0/16
+182.255.32.0/22
+182.255.36.0/22
+182.255.60.0/22
+183.78.160.0/22
+183.78.164.0/22
+183.81.172.0/22
+183.81.180.0/22
+183.84.0.0/15
+183.91.128.0/22
+183.91.144.0/20
+183.182.0.0/19
+188.131.128.0/17
+192.55.46.0/24
+192.55.68.0/22
+192.102.204.0/23
+192.140.128.0/22
+192.140.132.0/22
+192.140.136.0/22
+192.140.156.0/22
+192.140.160.0/22
+192.140.164.0/22
+192.140.168.0/22
+192.140.172.0/22
+192.140.176.0/22
+192.140.180.0/22
+192.140.184.0/22
+192.140.188.0/22
+192.140.192.0/22
+192.140.196.0/22
+192.140.200.0/22
+192.140.204.0/22
+192.144.128.0/17
+193.112.0.0/16
+198.175.100.0/22
+202.4.252.0/22
+202.5.208.0/22
+202.5.212.0/22
+202.5.216.0/22
+202.8.128.0/19
+202.10.64.0/20
+202.10.112.0/22
+202.10.116.0/22
+202.10.120.0/22
+202.10.124.0/22
+202.12.19.0/24
+202.14.172.0/22
+202.14.235.0/24
+202.14.236.0/23
+202.14.238.0/24
+202.20.126.0/24
+202.21.48.0/22
+202.21.52.0/22
+202.21.56.0/22
+202.21.60.0/22
+202.22.248.0/22
+202.22.252.0/22
+202.27.12.0/24
+202.27.14.0/24
+202.36.226.0/24
+202.38.8.0/21
+202.38.128.0/23
+202.38.132.0/23
+202.38.134.0/24
+202.38.135.0/24
+202.38.138.0/24
+202.38.146.0/23
+202.38.152.0/23
+202.38.160.0/23
+202.38.164.0/22
+202.38.171.0/24
+202.38.176.0/23
+202.41.152.0/21
+202.41.196.0/22
+202.41.200.0/22
+202.41.240.0/20
+202.43.76.0/22
+202.43.144.0/20
+202.44.48.0/22
+202.44.97.0/24
+202.46.32.0/19
+202.46.224.0/20
+202.47.96.0/22
+202.47.100.0/22
+202.47.104.0/22
+202.47.108.0/22
+202.52.33.0/24
+202.52.34.0/24
+202.52.47.0/24
+202.52.143.0/24
+202.53.140.0/24
+202.53.143.0/24
+202.57.192.0/22
+202.57.196.0/22
+202.57.200.0/22
+202.57.204.0/22
+202.57.212.0/22
+202.57.216.0/22
+202.58.104.0/22
+202.58.112.0/22
+202.59.1.0/24
+202.59.240.0/24
+202.60.112.0/20
+202.61.68.0/22
+202.61.76.0/22
+202.61.88.0/22
+202.61.123.0/24
+202.61.127.0/24
+202.63.80.0/24
+202.63.160.0/19
+202.63.248.0/22
+202.63.253.0/24
+202.65.96.0/22
+202.65.100.0/22
+202.65.104.0/22
+202.65.108.0/22
+202.66.168.0/22
+202.69.4.0/22
+202.69.16.0/20
+202.70.0.0/19
+202.71.32.0/22
+202.71.36.0/22
+202.71.40.0/22
+202.71.44.0/22
+202.72.112.0/22
+202.72.116.0/22
+202.72.120.0/22
+202.72.124.0/22
+202.73.240.0/22
+202.73.244.0/22
+202.73.248.0/22
+202.73.252.0/22
+202.74.36.0/24
+202.74.42.0/24
+202.74.52.0/24
+202.74.232.0/22
+202.81.176.0/22
+202.81.180.0/22
+202.81.184.0/22
+202.81.188.0/22
+202.84.0.0/22
+202.84.16.0/23
+202.84.22.0/24
+202.85.208.0/20
+202.88.32.0/22
+202.89.108.0/22
+202.89.119.0/24
+202.89.232.0/21
+202.90.0.0/22
+202.90.16.0/22
+202.90.20.0/22
+202.90.24.0/22
+202.90.28.0/22
+202.90.37.0/24
+202.90.96.0/22
+202.90.100.0/22
+202.90.104.0/22
+202.90.108.0/22
+202.90.193.0/24
+202.90.205.0/24
+202.90.224.0/20
+202.91.36.0/22
+202.91.128.0/22
+202.91.176.0/20
+202.91.224.0/19
+202.93.0.0/22
+202.93.252.0/22
+202.94.74.0/24
+202.94.81.0/24
+202.95.252.0/22
+202.101.192.0/19
+202.101.232.0/21
+202.101.240.0/20
+202.122.0.0/21
+202.122.32.0/21
+202.122.64.0/19
+202.122.112.0/21
+202.123.96.0/20
+202.123.116.0/22
+202.123.120.0/22
+202.125.109.0/24
+202.125.176.0/20
+202.127.0.0/23
+202.127.2.0/24
+202.127.3.0/24
+202.127.4.0/24
+202.127.5.0/24
+202.127.6.0/23
+202.127.12.0/22
+202.127.16.0/20
+202.127.40.0/21
+202.127.48.0/20
+202.127.144.0/20
+202.127.160.0/21
+202.127.192.0/23
+202.127.200.0/21
+202.127.208.0/24
+202.129.208.0/24
+202.130.0.0/19
+202.130.39.0/24
+202.131.16.0/21
+202.131.48.0/20
+202.131.208.0/20
+202.134.208.0/22
+202.134.212.0/22
+202.134.216.0/22
+202.134.220.0/22
+202.136.48.0/20
+202.136.208.0/20
+202.136.224.0/20
+202.136.248.0/22
+202.140.140.0/22
+202.140.144.0/22
+202.140.148.0/22
+202.140.152.0/22
+202.140.156.0/22
+202.141.160.0/19
+202.142.16.0/20
+202.143.16.0/20
+202.143.100.0/22
+202.143.104.0/22
+202.144.196.0/22
+202.146.186.0/24
+202.148.96.0/19
+202.149.160.0/19
+202.149.224.0/19
+202.150.16.0/20
+202.151.33.0/24
+202.152.176.0/20
+202.153.7.0/24
+202.153.48.0/20
+202.158.160.0/19
+202.158.242.0/24
+202.160.156.0/22
+202.160.176.0/20
+202.164.0.0/20
+202.165.176.0/20
+202.165.208.0/20
+202.168.80.0/22
+202.168.128.0/22
+202.168.132.0/22
+202.168.136.0/22
+202.168.140.0/22
+202.168.160.0/20
+202.168.176.0/20
+202.170.216.0/21
+202.171.232.0/24
+202.172.7.0/24
+202.173.6.0/24
+202.173.8.0/21
+202.173.112.0/22
+202.174.124.0/22
+202.179.160.0/22
+202.179.164.0/22
+202.179.168.0/22
+202.179.172.0/22
+202.180.128.0/19
+202.181.8.0/22
+202.181.28.0/22
+202.181.112.0/20
+202.189.0.0/18
+202.189.80.0/20
+203.16.63.0/24
+203.17.249.0/24
+203.19.1.0/24
+203.19.147.0/24
+203.20.44.0/24
+203.56.231.0/24
+203.76.208.0/22
+203.76.212.0/22
+203.76.216.0/22
+203.76.244.0/22
+203.78.156.0/22
+203.79.0.0/20
+203.79.32.0/20
+203.80.129.0/24
+203.80.144.0/20
+203.81.16.0/20
+203.81.244.0/22
+203.82.116.0/22
+203.82.120.0/22
+203.82.124.0/22
+203.82.224.0/22
+203.82.228.0/22
+203.82.232.0/22
+203.82.236.0/22
+203.83.8.0/22
+203.83.12.0/22
+203.83.56.0/21
+203.86.0.0/19
+203.86.32.0/19
+203.86.80.0/20
+203.86.250.0/24
+203.88.32.0/19
+203.88.192.0/19
+203.89.0.0/22
+203.89.100.0/22
+203.89.133.0/24
+203.89.144.0/24
+203.90.0.0/22
+203.90.12.0/22
+203.90.128.0/19
+203.90.160.0/19
+203.90.192.0/19
+203.91.32.0/19
+203.91.96.0/20
+203.92.6.0/24
+203.92.160.0/19
+203.94.0.0/22
+203.94.4.0/22
+203.94.8.0/21
+203.94.16.0/20
+203.95.0.0/21
+203.95.96.0/20
+203.95.112.0/20
+203.95.128.0/18
+203.95.200.0/22
+203.95.204.0/22
+203.95.208.0/22
+203.99.16.0/20
+203.99.80.0/20
+203.100.32.0/20
+203.100.58.0/24
+203.100.60.0/24
+203.100.80.0/20
+203.100.96.0/19
+203.100.192.0/20
+203.107.0.0/17
+203.110.160.0/19
+203.114.80.0/22
+203.114.84.0/22
+203.114.88.0/22
+203.114.92.0/22
+203.114.244.0/22
+203.118.192.0/19
+203.118.248.0/22
+203.119.24.0/21
+203.119.32.0/22
+203.119.80.0/22
+203.119.85.0/24
+203.119.128.0/17
+203.123.58.0/24
+203.128.32.0/19
+203.128.96.0/19
+203.132.32.0/19
+203.134.240.0/21
+203.135.96.0/20
+203.135.112.0/20
+203.142.219.0/24
+203.148.0.0/18
+203.148.64.0/20
+203.152.64.0/19
+203.156.192.0/18
+203.158.16.0/21
+203.160.52.0/22
+203.161.180.0/24
+203.161.183.0/24
+203.161.192.0/19
+203.167.28.0/22
+203.171.208.0/24
+203.171.224.0/20
+203.174.6.0/24
+203.174.7.0/24
+203.174.96.0/19
+203.175.128.0/19
+203.175.192.0/18
+203.176.0.0/18
+203.176.168.0/21
+203.184.80.0/20
+203.185.189.0/24
+203.187.160.0/19
+203.189.232.0/22
+203.189.240.0/22
+203.190.96.0/20
+203.191.2.0/24
+203.191.5.0/24
+203.191.7.0/24
+203.191.16.0/20
+203.191.64.0/18
+203.191.133.0/24
+203.191.144.0/21
+203.191.152.0/21
+203.192.0.0/19
+203.195.128.0/17
+203.196.0.0/21
+203.196.28.0/22
+203.201.181.0/24
+203.201.182.0/24
+203.205.128.0/17
+203.207.64.0/20
+203.207.80.0/21
+203.207.88.0/22
+203.207.92.0/22
+203.207.96.0/20
+203.207.112.0/20
+203.207.128.0/18
+203.207.192.0/21
+203.207.200.0/21
+203.207.208.0/20
+203.207.224.0/19
+203.208.0.0/20
+203.208.16.0/22
+203.208.32.0/19
+203.209.224.0/19
+203.212.80.0/20
+203.217.164.0/22
+203.222.192.0/20
+210.2.0.0/20
+210.2.16.0/20
+210.5.0.0/19
+210.5.144.0/20
+210.7.56.0/22
+210.7.60.0/22
+210.14.64.0/19
+210.14.112.0/20
+210.14.128.0/19
+210.15.64.0/19
+210.16.104.0/22
+210.16.128.0/18
+210.23.32.0/19
+210.72.0.0/17
+210.72.128.0/19
+210.72.192.0/18
+210.73.0.0/19
+210.73.32.0/19
+210.73.64.0/18
+210.73.128.0/17
+210.74.0.0/19
+210.74.32.0/19
+210.74.160.0/19
+210.74.192.0/18
+210.76.0.0/19
+210.76.32.0/19
+210.76.64.0/18
+210.76.128.0/17
+210.78.32.0/19
+210.78.64.0/18
+210.78.128.0/19
+210.79.224.0/19
+210.87.128.0/20
+210.87.144.0/20
+210.87.160.0/19
+211.88.0.0/16
+211.89.0.0/16
+211.99.0.0/18
+211.99.64.0/19
+211.99.96.0/19
+211.99.128.0/17
+211.101.0.0/18
+211.101.64.0/18
+211.101.128.0/17
+211.102.0.0/16
+211.103.128.0/17
+211.146.0.0/16
+211.147.0.0/16
+211.148.0.0/14
+211.152.0.0/15
+211.155.0.0/18
+211.155.64.0/19
+211.155.96.0/19
+211.155.128.0/17
+211.156.0.0/14
+211.160.0.0/14
+212.64.0.0/17
+212.129.128.0/17
+216.250.108.0/22
+218.96.0.0/15
+218.98.128.0/18
+218.98.192.0/19
+218.98.224.0/19
+218.99.0.0/16
+218.108.0.0/16
+218.109.0.0/16
+218.185.192.0/19
+218.240.0.0/14
+218.244.0.0/15
+218.246.0.0/15
+218.249.0.0/16
+219.72.0.0/16
+219.82.0.0/16
+219.83.128.0/17
+219.90.68.0/22
+219.90.72.0/22
+219.90.76.0/22
+219.238.0.0/15
+220.101.192.0/18
+220.154.0.0/15
+220.231.128.0/17
+220.232.64.0/18
+221.122.0.0/15
+221.128.128.0/17
+221.129.0.0/16
+221.133.224.0/19
+221.136.0.0/16
+222.125.0.0/16
+222.126.128.0/17
+222.248.0.0/16
+222.249.0.0/17
+222.249.128.0/19
+222.249.160.0/20
+222.249.176.0/20
+222.249.192.0/18
+223.0.0.0/15
+223.4.0.0/14
+223.20.0.0/15
+223.29.208.0/22
+223.29.252.0/22
+223.160.0.0/14
+223.192.0.0/15
+223.201.0.0/16
+223.202.0.0/15
+223.212.0.0/15
+223.223.176.0/20
+223.223.192.0/20
+223.248.0.0/14
+223.252.128.0/17
+223.254.0.0/16
+223.255.0.0/17
+223.255.236.0/22
diff --git a/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt
new file mode 100644
index 0000000000..b5c9a29bb3
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt
@@ -0,0 +1,895 @@
+1.24.0.0/13
+1.56.0.0/13
+1.116.0.0/15
+1.188.0.0/14
+14.204.0.0/15
+27.0.128.0/22
+27.0.132.0/22
+27.8.0.0/13
+27.36.0.0/14
+27.40.0.0/13
+27.50.128.0/17
+27.54.192.0/18
+27.98.224.0/19
+27.112.0.0/18
+27.115.0.0/17
+27.192.0.0/11
+36.32.0.0/14
+36.248.0.0/14
+39.64.0.0/11
+42.1.128.0/17
+42.4.0.0/14
+42.48.0.0/15
+42.51.0.0/16
+42.52.0.0/14
+42.56.0.0/14
+42.63.0.0/16
+42.84.0.0/14
+42.176.0.0/13
+42.224.0.0/12
+43.224.12.0/22
+43.224.24.0/22
+43.224.52.0/22
+43.224.56.0/22
+43.224.80.0/22
+43.224.160.0/22
+43.224.208.0/22
+43.225.120.0/22
+43.226.96.0/22
+43.226.100.0/22
+43.226.104.0/22
+43.226.108.0/22
+43.226.116.0/22
+43.226.120.0/22
+43.226.160.0/22
+43.226.164.0/22
+43.226.236.0/22
+43.227.104.0/22
+43.229.168.0/22
+43.229.172.0/22
+43.229.176.0/22
+43.229.180.0/22
+43.229.236.0/22
+43.239.120.0/22
+43.240.0.0/22
+43.240.72.0/22
+43.240.132.0/22
+43.240.192.0/22
+43.240.196.0/22
+43.240.204.0/22
+43.241.48.0/22
+43.241.92.0/22
+43.241.184.0/22
+43.242.64.0/22
+43.242.148.0/22
+43.242.152.0/22
+43.242.156.0/22
+43.242.164.0/22
+43.242.180.0/22
+43.243.4.0/22
+43.243.16.0/22
+43.243.88.0/22
+43.243.128.0/22
+43.243.156.0/22
+43.243.180.0/22
+43.243.228.0/22
+43.246.112.0/22
+43.247.88.0/22
+43.247.92.0/22
+43.247.212.0/22
+43.247.216.0/22
+43.248.4.0/22
+43.248.20.0/22
+43.248.48.0/22
+43.248.80.0/22
+43.248.84.0/22
+43.248.88.0/22
+43.248.92.0/22
+43.248.108.0/22
+43.248.148.0/22
+43.248.176.0/22
+43.248.180.0/22
+43.248.184.0/22
+43.248.188.0/22
+43.248.192.0/22
+43.248.196.0/22
+43.248.200.0/22
+43.248.204.0/22
+43.248.244.0/22
+43.249.4.0/22
+43.249.136.0/22
+43.249.144.0/22
+43.249.192.0/22
+43.249.236.0/22
+43.250.4.0/22
+43.250.32.0/22
+43.250.96.0/22
+43.250.112.0/22
+43.250.128.0/22
+43.250.144.0/22
+43.250.168.0/22
+43.250.200.0/22
+43.250.212.0/22
+43.250.244.0/22
+43.251.4.0/22
+43.251.36.0/22
+43.251.232.0/22
+43.254.44.0/22
+43.254.52.0/22
+43.254.104.0/22
+43.254.116.0/22
+43.254.136.0/22
+43.254.144.0/22
+43.254.148.0/22
+43.254.220.0/22
+43.255.84.0/22
+43.255.204.0/22
+45.112.228.0/22
+45.112.232.0/22
+45.113.24.0/22
+45.113.28.0/22
+45.113.200.0/22
+45.113.240.0/22
+45.113.252.0/22
+45.115.144.0/22
+45.115.156.0/22
+45.115.164.0/22
+45.115.200.0/22
+45.115.248.0/22
+45.117.8.0/22
+45.117.124.0/22
+45.119.64.0/22
+45.119.68.0/22
+45.120.100.0/22
+45.121.52.0/22
+45.121.96.0/22
+45.123.156.0/22
+45.124.20.0/22
+45.124.68.0/22
+45.124.80.0/22
+45.125.44.0/22
+45.126.108.0/22
+45.126.120.0/22
+45.248.8.0/22
+45.249.112.0/22
+45.250.32.0/22
+45.250.36.0/22
+45.250.40.0/22
+45.251.16.0/22
+45.251.84.0/22
+45.251.88.0/22
+45.252.104.0/22
+45.253.24.0/22
+45.253.32.0/22
+45.253.64.0/22
+58.16.0.0/16
+58.17.0.0/17
+58.17.128.0/17
+58.18.0.0/16
+58.19.0.0/16
+58.20.0.0/16
+58.21.0.0/16
+58.22.0.0/15
+58.24.0.0/15
+58.30.0.0/15
+58.144.0.0/16
+58.240.0.0/15
+58.242.0.0/15
+58.244.0.0/15
+58.246.0.0/15
+58.248.0.0/13
+60.0.0.0/13
+60.8.0.0/15
+60.10.0.0/16
+60.11.0.0/16
+60.12.0.0/16
+60.13.0.0/18
+60.13.64.0/18
+60.13.128.0/17
+60.14.0.0/15
+60.16.0.0/13
+60.24.0.0/14
+60.28.0.0/15
+60.30.0.0/16
+60.31.0.0/16
+60.63.0.0/16
+60.204.0.0/16
+60.208.0.0/13
+60.216.0.0/15
+60.218.0.0/15
+60.220.0.0/14
+60.253.128.0/17
+60.255.0.0/16
+61.28.0.0/20
+61.28.16.0/20
+61.28.32.0/19
+61.28.64.0/18
+61.45.128.0/18
+61.48.0.0/14
+61.52.0.0/15
+61.54.0.0/16
+61.55.0.0/16
+61.133.0.0/17
+61.134.96.0/19
+61.134.128.0/18
+61.134.192.0/18
+61.135.0.0/16
+61.136.0.0/18
+61.136.64.0/18
+61.137.128.0/17
+61.138.0.0/18
+61.138.64.0/18
+61.138.128.0/18
+61.139.128.0/18
+61.148.0.0/15
+61.156.0.0/16
+61.158.0.0/17
+61.158.128.0/17
+61.159.0.0/18
+61.161.0.0/18
+61.161.128.0/17
+61.162.0.0/16
+61.163.0.0/16
+61.167.0.0/16
+61.168.0.0/16
+61.176.0.0/16
+61.179.0.0/16
+61.180.128.0/17
+61.181.0.0/16
+61.182.0.0/16
+61.189.0.0/17
+61.240.0.0/14
+101.16.0.0/12
+101.64.0.0/13
+101.72.0.0/14
+101.204.0.0/14
+101.232.0.0/15
+103.3.96.0/22
+103.3.100.0/22
+103.3.104.0/22
+103.3.108.0/22
+103.3.112.0/22
+103.3.116.0/22
+103.3.120.0/22
+103.3.124.0/22
+103.3.132.0/22
+103.3.136.0/22
+103.3.140.0/22
+103.3.148.0/22
+103.3.152.0/22
+103.3.156.0/22
+103.5.56.0/22
+103.5.168.0/22
+103.5.192.0/22
+103.5.252.0/22
+103.7.212.0/22
+103.9.248.0/22
+103.9.252.0/22
+103.14.84.0/22
+103.14.132.0/22
+103.18.224.0/22
+103.21.116.0/22
+103.22.188.0/22
+103.25.40.0/22
+103.27.4.0/22
+103.27.240.0/22
+103.28.204.0/22
+103.31.200.0/22
+103.35.220.0/22
+103.36.208.0/22
+103.36.220.0/22
+103.37.44.0/22
+103.37.100.0/22
+103.38.32.0/22
+103.38.40.0/22
+103.39.88.0/22
+103.40.12.0/22
+103.40.192.0/22
+103.40.212.0/22
+103.40.232.0/22
+103.41.164.0/22
+103.43.100.0/22
+103.43.192.0/22
+103.44.80.0/22
+103.45.72.0/22
+103.45.76.0/22
+103.45.160.0/22
+103.45.168.0/22
+103.45.224.0/22
+103.48.216.0/22
+103.48.220.0/22
+103.48.224.0/22
+103.48.228.0/22
+103.48.232.0/22
+103.52.96.0/22
+103.52.100.0/22
+103.52.176.0/22
+103.52.184.0/22
+103.53.124.0/22
+103.53.204.0/22
+103.55.172.0/22
+103.55.204.0/22
+103.55.208.0/22
+103.55.228.0/22
+103.56.32.0/22
+103.56.60.0/22
+103.57.196.0/22
+103.59.116.0/22
+103.59.124.0/22
+103.60.164.0/22
+103.61.176.0/22
+103.70.148.0/22
+103.72.224.0/22
+103.72.228.0/22
+103.72.232.0/22
+103.72.236.0/22
+103.72.240.0/22
+103.72.244.0/22
+103.72.248.0/22
+103.72.252.0/22
+103.73.0.0/22
+103.73.4.0/22
+103.73.8.0/22
+103.73.12.0/22
+103.73.16.0/22
+103.73.20.0/22
+103.73.24.0/22
+103.73.28.0/22
+103.73.144.0/22
+103.78.64.0/22
+103.85.84.0/22
+103.90.92.0/22
+103.90.188.0/22
+103.91.108.0/22
+103.91.176.0/22
+103.91.208.0/22
+103.92.132.0/22
+103.93.180.0/22
+103.95.220.0/22
+103.98.44.0/22
+103.98.168.0/22
+103.98.220.0/22
+103.107.212.0/22
+103.108.244.0/22
+103.110.156.0/22
+103.117.188.0/22
+103.123.56.0/22
+103.192.4.0/22
+103.192.132.0/22
+103.192.188.0/22
+103.192.212.0/22
+103.203.168.0/22
+103.204.72.0/22
+103.204.136.0/22
+103.205.4.0/22
+103.205.192.0/22
+103.205.252.0/22
+103.207.184.0/22
+103.207.228.0/22
+103.207.232.0/22
+103.210.184.0/22
+103.211.44.0/22
+103.211.96.0/22
+103.211.220.0/22
+103.213.96.0/22
+103.213.132.0/22
+103.214.48.0/22
+103.215.36.0/22
+103.216.152.0/22
+103.216.252.0/22
+103.219.28.0/22
+103.219.32.0/22
+103.219.36.0/22
+103.220.48.0/22
+103.220.52.0/22
+103.221.140.0/22
+103.222.32.0/22
+103.222.40.0/22
+103.222.164.0/22
+103.222.172.0/22
+103.223.140.0/22
+103.223.188.0/22
+103.224.228.0/22
+103.227.120.0/22
+103.227.136.0/22
+103.228.204.0/22
+103.229.148.0/22
+103.229.212.0/22
+103.230.196.0/22
+103.230.236.0/22
+103.233.228.0/22
+103.234.56.0/22
+103.234.244.0/22
+103.236.120.0/22
+103.236.240.0/22
+103.236.252.0/22
+103.238.28.0/22
+103.238.32.0/22
+103.238.140.0/22
+103.238.144.0/22
+103.238.160.0/22
+103.238.184.0/22
+103.239.152.0/22
+103.239.244.0/22
+103.240.36.0/22
+103.240.72.0/22
+103.240.84.0/22
+103.240.124.0/22
+103.240.172.0/22
+103.246.8.0/22
+103.246.152.0/22
+103.248.100.0/22
+103.248.152.0/22
+103.250.32.0/22
+103.250.104.0/22
+103.250.124.0/22
+103.250.180.0/22
+103.250.192.0/22
+103.250.216.0/22
+103.250.248.0/22
+103.251.32.0/22
+103.251.96.0/22
+103.251.124.0/22
+103.251.160.0/22
+103.251.240.0/22
+103.252.204.0/22
+103.252.208.0/22
+103.252.232.0/22
+103.252.248.0/22
+103.253.60.0/22
+103.255.88.0/22
+103.255.200.0/22
+106.48.0.0/15
+110.6.0.0/15
+110.16.0.0/14
+110.44.12.0/22
+110.52.0.0/15
+110.72.0.0/15
+110.228.0.0/14
+110.236.0.0/15
+110.240.0.0/12
+111.85.0.0/16
+111.160.0.0/13
+111.192.0.0/12
+111.223.12.0/22
+112.64.0.0/15
+112.80.0.0/13
+112.88.0.0/13
+112.96.0.0/15
+112.109.128.0/17
+112.111.0.0/16
+112.122.0.0/15
+112.132.0.0/16
+112.192.0.0/14
+112.224.0.0/11
+113.0.0.0/13
+113.8.0.0/15
+113.18.0.0/16
+113.56.0.0/15
+113.58.0.0/16
+113.59.0.0/17
+113.194.0.0/15
+113.200.0.0/15
+113.204.0.0/14
+113.213.0.0/17
+113.224.0.0/12
+114.54.0.0/15
+114.60.0.0/14
+114.119.0.0/17
+114.119.224.0/19
+114.240.0.0/12
+115.46.0.0/16
+115.48.0.0/12
+115.85.192.0/18
+115.172.0.0/14
+116.2.0.0/15
+116.60.0.0/14
+116.70.0.0/17
+116.78.0.0/15
+116.95.0.0/16
+116.112.0.0/14
+116.116.0.0/15
+116.128.0.0/10
+116.194.0.0/15
+116.199.0.0/17
+116.214.32.0/19
+116.216.0.0/14
+116.242.0.0/15
+116.244.0.0/15
+116.254.128.0/17
+116.255.128.0/17
+117.8.0.0/13
+117.74.64.0/20
+117.75.0.0/16
+117.76.0.0/14
+117.100.0.0/15
+117.121.128.0/18
+118.26.48.0/21
+118.72.0.0/13
+118.80.0.0/15
+118.88.32.0/19
+118.88.64.0/18
+118.88.128.0/17
+118.103.168.0/22
+118.132.0.0/14
+118.144.0.0/14
+118.212.0.0/16
+118.224.0.0/14
+118.244.0.0/14
+119.4.0.0/14
+119.27.64.0/18
+119.32.0.0/14
+119.36.0.0/16
+119.37.0.0/17
+119.37.128.0/18
+119.38.0.0/17
+119.39.0.0/16
+119.40.128.0/17
+119.42.0.0/19
+119.42.224.0/19
+119.44.0.0/15
+119.48.0.0/13
+119.59.128.0/17
+119.62.0.0/16
+119.108.0.0/15
+119.112.0.0/13
+119.148.160.0/20
+119.162.0.0/15
+119.164.0.0/14
+119.176.0.0/12
+119.232.0.0/15
+119.248.0.0/14
+119.252.240.0/20
+120.0.0.0/12
+120.80.0.0/13
+120.128.0.0/14
+120.133.0.0/16
+121.16.0.0/13
+121.24.0.0/14
+121.28.0.0/15
+121.30.0.0/16
+121.31.0.0/16
+121.52.224.0/19
+121.55.0.0/18
+121.68.0.0/14
+121.76.0.0/15
+121.100.128.0/17
+121.201.0.0/16
+122.13.0.0/16
+122.96.0.0/15
+122.102.64.0/20
+122.136.0.0/13
+122.156.0.0/14
+122.188.0.0/14
+122.192.0.0/14
+123.4.0.0/14
+123.8.0.0/13
+123.98.0.0/17
+123.100.0.0/19
+123.112.0.0/12
+123.128.0.0/13
+123.138.0.0/15
+123.144.0.0/14
+123.148.0.0/16
+123.152.0.0/13
+123.176.80.0/20
+123.188.0.0/14
+123.196.0.0/15
+123.232.0.0/14
+124.6.64.0/18
+124.20.0.0/16
+124.21.0.0/20
+124.21.16.0/20
+124.21.32.0/19
+124.21.64.0/18
+124.21.128.0/17
+124.42.128.0/17
+124.64.0.0/15
+124.66.0.0/17
+124.67.0.0/16
+124.68.0.0/14
+124.88.0.0/16
+124.89.0.0/17
+124.89.128.0/17
+124.90.0.0/15
+124.92.0.0/14
+124.128.0.0/13
+124.152.0.0/16
+124.160.0.0/16
+124.161.0.0/16
+124.162.0.0/16
+124.163.0.0/16
+124.164.0.0/14
+124.172.0.0/15
+124.240.128.0/18
+125.32.0.0/16
+125.33.0.0/16
+125.34.0.0/16
+125.35.0.0/17
+125.35.128.0/17
+125.36.0.0/14
+125.40.0.0/13
+125.211.0.0/16
+125.214.96.0/19
+139.170.0.0/16
+139.208.0.0/13
+139.226.0.0/15
+140.206.0.0/15
+144.48.64.0/22
+146.196.56.0/22
+146.196.68.0/22
+150.129.136.0/22
+150.129.252.0/22
+150.242.152.0/22
+150.242.232.0/22
+150.255.0.0/16
+153.0.0.0/16
+153.3.0.0/16
+153.34.0.0/15
+153.36.0.0/15
+153.99.0.0/16
+153.101.0.0/16
+157.0.0.0/16
+157.18.0.0/16
+157.61.0.0/16
+157.122.0.0/16
+157.148.0.0/16
+157.156.0.0/16
+157.255.0.0/16
+160.19.212.0/22
+160.202.60.0/22
+160.202.148.0/22
+160.202.152.0/22
+160.202.212.0/22
+163.0.0.0/16
+163.53.48.0/22
+163.53.52.0/22
+163.53.168.0/22
+163.125.0.0/16
+163.142.0.0/16
+163.177.0.0/16
+163.179.0.0/16
+163.204.0.0/16
+171.34.0.0/15
+171.36.0.0/14
+171.116.0.0/14
+171.120.0.0/13
+175.16.0.0/13
+175.42.0.0/15
+175.44.0.0/16
+175.106.128.0/17
+175.146.0.0/15
+175.148.0.0/14
+175.152.0.0/14
+175.160.0.0/12
+175.184.128.0/18
+180.77.0.0/16
+180.95.128.0/17
+180.129.128.0/17
+180.130.0.0/16
+180.189.148.0/22
+180.223.0.0/16
+182.54.0.0/17
+182.88.0.0/14
+182.112.0.0/12
+183.92.0.0/14
+183.184.0.0/13
+192.140.208.0/22
+192.140.212.0/22
+202.74.8.0/21
+202.75.208.0/20
+202.89.96.0/22
+202.92.0.0/22
+202.96.0.0/18
+202.96.64.0/21
+202.96.72.0/21
+202.96.80.0/20
+202.97.128.0/18
+202.97.192.0/19
+202.97.224.0/21
+202.97.232.0/21
+202.97.240.0/20
+202.98.0.0/21
+202.98.8.0/21
+202.98.16.0/20
+202.99.0.0/18
+202.99.64.0/19
+202.99.96.0/21
+202.99.104.0/21
+202.99.112.0/20
+202.99.128.0/19
+202.99.160.0/21
+202.99.168.0/21
+202.99.176.0/20
+202.99.192.0/21
+202.99.200.0/21
+202.99.208.0/20
+202.99.224.0/21
+202.99.232.0/21
+202.99.240.0/20
+202.101.128.0/18
+202.102.128.0/21
+202.102.136.0/21
+202.102.144.0/20
+202.102.160.0/19
+202.102.224.0/21
+202.102.232.0/21
+202.102.240.0/20
+202.106.0.0/16
+202.107.0.0/17
+202.108.0.0/16
+202.110.0.0/18
+202.110.64.0/18
+202.110.192.0/18
+202.111.128.0/19
+202.111.160.0/19
+202.115.32.0/19
+202.115.64.0/18
+202.122.128.0/24
+202.127.112.0/20
+202.127.209.0/24
+202.130.224.0/19
+202.160.140.0/22
+202.170.128.0/19
+203.76.240.0/22
+203.93.0.0/22
+203.93.4.0/22
+203.93.8.0/24
+203.93.9.0/24
+203.93.10.0/23
+203.93.12.0/22
+203.93.16.0/20
+203.93.32.0/19
+203.93.64.0/18
+203.93.128.0/21
+203.93.136.0/22
+203.93.140.0/24
+203.93.141.0/24
+203.93.142.0/23
+203.93.144.0/20
+203.93.160.0/19
+203.93.192.0/18
+203.135.160.0/20
+203.166.160.0/19
+203.223.0.0/20
+210.5.128.0/20
+210.12.0.0/18
+210.12.64.0/18
+210.12.128.0/18
+210.12.192.0/18
+210.13.0.0/18
+210.13.64.0/18
+210.13.128.0/17
+210.14.160.0/19
+210.14.192.0/19
+210.14.224.0/19
+210.15.0.0/19
+210.15.32.0/19
+210.15.96.0/19
+210.15.128.0/18
+210.21.0.0/17
+210.21.128.0/17
+210.22.0.0/16
+210.51.0.0/16
+210.52.0.0/18
+210.52.64.0/18
+210.52.128.0/17
+210.53.0.0/17
+210.53.128.0/17
+210.56.192.0/19
+210.72.160.0/19
+210.74.64.0/19
+210.74.96.0/19
+210.74.128.0/19
+210.78.0.0/19
+210.78.160.0/19
+210.78.192.0/18
+210.79.64.0/18
+210.82.0.0/15
+211.90.0.0/15
+211.92.0.0/15
+211.94.0.0/15
+211.96.0.0/15
+211.144.0.0/15
+218.7.0.0/16
+218.8.0.0/15
+218.10.0.0/16
+218.11.0.0/16
+218.12.0.0/16
+218.21.128.0/17
+218.24.0.0/15
+218.26.0.0/16
+218.27.0.0/16
+218.28.0.0/15
+218.56.0.0/14
+218.60.0.0/15
+218.62.0.0/17
+218.67.128.0/17
+218.68.0.0/15
+218.98.0.0/17
+218.104.0.0/17
+218.104.128.0/19
+218.104.160.0/19
+218.104.192.0/21
+218.104.200.0/21
+218.104.208.0/20
+218.104.224.0/19
+218.105.0.0/16
+218.106.0.0/15
+219.154.0.0/15
+219.156.0.0/15
+219.158.0.0/17
+219.158.128.0/17
+219.159.0.0/18
+219.232.0.0/14
+220.152.128.0/17
+220.158.240.0/22
+220.192.0.0/15
+220.194.0.0/15
+220.196.0.0/14
+220.200.0.0/13
+220.231.0.0/18
+220.234.0.0/16
+220.248.0.0/14
+220.252.0.0/16
+221.0.0.0/15
+221.2.0.0/16
+221.3.0.0/17
+221.3.128.0/17
+221.4.0.0/16
+221.5.0.0/17
+221.5.128.0/17
+221.6.0.0/16
+221.7.0.0/19
+221.7.32.0/19
+221.7.64.0/19
+221.7.96.0/19
+221.7.128.0/17
+221.8.0.0/15
+221.10.0.0/16
+221.11.0.0/17
+221.11.128.0/18
+221.11.192.0/19
+221.11.224.0/19
+221.12.0.0/17
+221.12.128.0/18
+221.13.0.0/18
+221.13.64.0/19
+221.13.96.0/19
+221.13.128.0/17
+221.14.0.0/15
+221.137.0.0/16
+221.192.0.0/15
+221.194.0.0/16
+221.195.0.0/16
+221.196.0.0/15
+221.198.0.0/16
+221.199.0.0/19
+221.199.32.0/20
+221.199.48.0/20
+221.199.64.0/18
+221.199.128.0/18
+221.199.192.0/20
+221.199.224.0/19
+221.200.0.0/14
+221.204.0.0/15
+221.206.0.0/16
+221.207.0.0/18
+221.207.64.0/18
+221.207.128.0/17
+221.208.0.0/14
+221.212.0.0/16
+221.213.0.0/16
+221.214.0.0/15
+221.216.0.0/13
+222.128.0.0/14
+222.132.0.0/14
+222.136.0.0/13
+222.160.0.0/15
+222.162.0.0/16
+222.163.0.0/19
+222.163.32.0/19
+222.163.64.0/18
+222.163.128.0/17
+223.27.184.0/22
+223.166.0.0/15
diff --git a/package/lean/luci-app-mwan3helper/root/etc/uci-defaults/mwan3helper b/package/lean/luci-app-mwan3helper/root/etc/uci-defaults/mwan3helper
new file mode 100755
index 0000000000..426cd55ba9
--- /dev/null
+++ b/package/lean/luci-app-mwan3helper/root/etc/uci-defaults/mwan3helper
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@mwan3helper[-1]
+ add ucitrack mwan3helper
+ set ucitrack.@mwan3helper[-1].init=mwan3helper
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci*
+exit 0
diff --git a/package/lean/luci-app-n2n_v2/Makefile b/package/lean/luci-app-n2n_v2/Makefile
new file mode 100644
index 0000000000..55eecd40d1
--- /dev/null
+++ b/package/lean/luci-app-n2n_v2/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=n2n_v2 VPN Configuration module
+LUCI_DEPENDS:=+n2n_v2
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-n2n_v2/luasrc/controller/n2n_v2.lua b/package/lean/luci-app-n2n_v2/luasrc/controller/n2n_v2.lua
new file mode 100644
index 0000000000..a1d599886e
--- /dev/null
+++ b/package/lean/luci-app-n2n_v2/luasrc/controller/n2n_v2.lua
@@ -0,0 +1,18 @@
+--[[
+N2N V2 Luci configuration page.Made by 981213
+]]--
+
+module("luci.controller.n2n_v2", package.seeall)
+
+function index()
+
+ if not nixio.fs.access("/etc/config/n2n_v2") then
+ return
+ end
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+
+ local page
+ page = entry({"admin", "vpn", "n2n_v2"}, cbi("n2n_v2"), _("N2N v2 VPN"), 45)
+ page.dependent = true
+end
diff --git a/package/lean/luci-app-n2n_v2/luasrc/model/cbi/n2n_v2.lua b/package/lean/luci-app-n2n_v2/luasrc/model/cbi/n2n_v2.lua
new file mode 100644
index 0000000000..3c84c40ad9
--- /dev/null
+++ b/package/lean/luci-app-n2n_v2/luasrc/model/cbi/n2n_v2.lua
@@ -0,0 +1,59 @@
+--[[
+--N2N VPN(V2) configuration page. Made by 981213
+--
+]]--
+
+local fs = require "nixio.fs"
+
+m = Map("n2n_v2", translate("N2N v2 VPN"),
+ translatef("n2n is a layer-two peer-to-peer virtual private network (VPN) which allows users to exploit features typical of P2P applications at network instead of application level."))
+
+s = m:section(TypedSection, "edge", translate("N2N Edge Settings"))
+s.anonymous = true
+s.addremove = true
+
+switch = s:option(Flag, "enabled", translate("Enable"))
+switch.rmempty = false
+
+tunname = s:option(Value, "tunname", translate("TUN desvice name"))
+tunname.optional = false
+
+mode = s:option(ListValue, "mode", translate("Interface mode"))
+mode:value("dhcp")
+mode:value("static")
+
+ipaddr = s:option(Value, "ipaddr", translate("Interface IP address"))
+ipaddr.optional = false
+
+netmask = s:option(Value, "netmask", translate("Interface netmask"))
+netmask.optional = false
+
+supernode = s:option(Value, "supernode", translate("Supernode IP address"))
+supernode.optional = false
+
+port = s:option(Value, "port", translate("Supernode Port"))
+port.datatype = "range(0,65535)"
+port.optional = false
+
+community = s:option(Value, "community", translate("N2N Community name"))
+community.optional = false
+
+s:option(Value, "key", translate("Encryption key"))
+
+route = s:option(Flag, "route", translate("Enable packet forwarding"))
+route.rmempty = false
+
+s2 = m:section(TypedSection, "supernode", translate("N2N Supernode Settings"))
+s2.anonymous = true
+s2.addremove = true
+
+switch = s2:option(Flag, "enabled", translate("Enable"))
+switch.rmempty = false
+
+port = s2:option(Value, "port", translate("Port"))
+port.datatype = "range(0,65535)"
+port.optional = false
+
+return m
+
+
diff --git a/package/lean/luci-app-n2n_v2/po/zh-cn/n2n_v2.po b/package/lean/luci-app-n2n_v2/po/zh-cn/n2n_v2.po
new file mode 100644
index 0000000000..beedc04ae1
--- /dev/null
+++ b/package/lean/luci-app-n2n_v2/po/zh-cn/n2n_v2.po
@@ -0,0 +1,54 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-10-01\n"
+"PO-Revision-Date: 2014-10-01\n"
+"Last-Translator: 981213 \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgid "n2n is a layer-two peer-to-peer virtual private network (VPN) which allows users to exploit features typical of P2P applications at network instead of application level."
+msgstr "N2N是一个第二层点对点VPN程序,它可以让用户在网络层而不是应用层使用一些点对点服务。"
+
+msgid "N2N Edge Settings"
+msgstr "N2N Edge节点设置"
+
+msgid "TUN desvice name"
+msgstr "隧道设备名称"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Interface mode"
+msgstr "接口模式"
+
+msgid "Interface IP address"
+msgstr "接口IP地址"
+
+msgid "Interface netmask"
+msgstr "接口子网掩码"
+
+msgid "Supernode IP address"
+msgstr "Supernode节点IP地址"
+
+msgid "N2N Community name"
+msgstr "N2N网络组名称"
+
+msgid "Enable packet forwarding"
+msgstr "启用数据包转发"
+
+msgid "N2N Supernode Settings"
+msgstr "N2N Supernode节点设置"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "Supernode Port"
+msgstr "Supernode节点端口"
+
+msgid "Encryption key"
+msgstr "加密密钥"
diff --git a/package/lean/luci-app-n2n_v2/root/etc/uci-defaults/luci-n2n_v2 b/package/lean/luci-app-n2n_v2/root/etc/uci-defaults/luci-n2n_v2
new file mode 100755
index 0000000000..145579d557
--- /dev/null
+++ b/package/lean/luci-app-n2n_v2/root/etc/uci-defaults/luci-n2n_v2
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@n2n_v2[-1]
+ add ucitrack n2n_v2
+ set ucitrack.@n2n_v2[-1].init=n2n_v2
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-nft-qos/Makefile b/package/lean/luci-app-nft-qos/Makefile
new file mode 100644
index 0000000000..1452dac429
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Qos over Nftables
+LUCI_DEPENDS:=+nft-qos
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-nft-qos/luasrc/controller/nft-qos.lua b/package/lean/luci-app-nft-qos/luasrc/controller/nft-qos.lua
new file mode 100644
index 0000000000..637eb11736
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/luasrc/controller/nft-qos.lua
@@ -0,0 +1,55 @@
+-- Copyright 2018 Rosy Song
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.nft-qos", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/nft-qos") then
+ return
+ end
+
+ entry({"admin", "status", "realtime", "rate"},
+ template("nft-qos/rate"), _("Rate"), 5).leaf = true
+ entry({"admin", "status", "realtime", "rate_status"},
+ call("action_rate")).leaf = true
+ entry({"admin", "services", "nft-qos"}, cbi("nft-qos/nft-qos"),
+ _("Qos over Nftables"), 60)
+end
+
+function _action_rate(rv, n)
+ local c = nixio.fs.access("/proc/net/ipv6_route") and
+ io.popen("nft list chain inet nft-qos-monitor " .. n .. " 2>/dev/null") or
+ io.popen("nft list chain ip nft-qos-monitor " .. n .. " 2>/dev/null")
+
+ if c then
+ for l in c:lines() do
+ local _, i, p, b = l:match(
+ '^%s+ip ([^%s]+) ([^%s]+) counter packets (%d+) bytes (%d+)'
+ )
+ if i and p and b then
+ -- handle expression
+ rv[#rv + 1] = {
+ rule = {
+ family = "inet",
+ table = "nft-qos-monitor",
+ chain = n,
+ handle = 0,
+ expr = {
+ { match = { right = i } },
+ { counter = { packets = p, bytes = b } }
+ }
+ }
+ }
+ end
+ end
+ c:close()
+ end
+end
+
+function action_rate()
+ luci.http.prepare_content("application/json")
+ local data = { nftables = {} }
+ _action_rate(data.nftables, "upload")
+ _action_rate(data.nftables, "download")
+ luci.http.write_json(data)
+end
diff --git a/package/lean/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua b/package/lean/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua
new file mode 100644
index 0000000000..61a6d76a7d
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua
@@ -0,0 +1,229 @@
+-- Copyright 2018 Rosy Song
+-- Licensed to the public under the Apache License 2.0.
+
+local uci = require("luci.model.uci").cursor()
+local wa = require("luci.tools.webadmin")
+local fs = require("nixio.fs")
+local ipc = require("luci.ip")
+
+local def_rate_dl = uci:get("nft-qos", "default", "static_rate_dl")
+local def_rate_ul = uci:get("nft-qos", "default", "static_rate_ul")
+local def_unit_dl = uci:get("nft-qos", "default", "static_unit_dl")
+local def_unit_ul = uci:get("nft-qos", "default", "static_unit_ul")
+
+local def_up = uci:get("nft-qos", "default", "dynamic_bw_up")
+local def_down = uci:get("nft-qos", "default", "dynamic_bw_down")
+
+local limit_enable = uci:get("nft-qos", "default", "limit_enable")
+local limit_type = uci:get("nft-qos", "default", "limit_type")
+local enable_priority = uci:get("nft-qos", "default", "priority_enable")
+
+local has_ipv6 = fs.access("/proc/net/ipv6_route")
+
+m = Map("nft-qos", translate("Qos over Nftables"))
+
+--
+-- Taboptions
+--
+s = m:section(TypedSection, "default", translate("NFT-QoS Settings"))
+s.addremove = false
+s.anonymous = true
+
+s:tab("limit", "Limit Rate")
+s:tab("priority", "Traffic Priority")
+
+--
+-- Static
+--
+o = s:taboption("limit", Flag, "limit_enable", translate("Limit Enable"), translate("Enable Limit Rate Feature"))
+o.default = limit_enable or o.enabled
+o.rmempty = false
+
+o = s:taboption("limit", ListValue, "limit_type", translate("Limit Type"), translate("Type of Limit Rate"))
+o.default = limit_static or "static"
+o:depends("limit_enable","1")
+o:value("static", "Static")
+o:value("dynamic", "Dynamic")
+
+o = s:taboption("limit", Value, "static_rate_dl", translate("Default Download Rate"), translate("Default value for download rate"))
+o.datatype = "uinteger"
+o.default = def_rate_dl or '50'
+o:depends("limit_type","static")
+
+o = s:taboption("limit", ListValue, "static_unit_dl", translate("Default Download Unit"), translate("Default unit for download rate"))
+o.default = def_unit_dl or "kbytes"
+o:depends("limit_type","static")
+o:value("bytes", "Bytes/s")
+o:value("kbytes", "KBytes/s")
+o:value("mbytes", "MBytes/s")
+
+o = s:taboption("limit", Value, "static_rate_ul", translate("Default Upload Rate"), translate("Default value for upload rate"))
+o.datatype = "uinteger"
+o.default = def_rate_ul or '50'
+o:depends("limit_type","static")
+
+o = s:taboption("limit", ListValue, "static_unit_ul", translate("Default Upload Unit"), translate("Default unit for upload rate"))
+o.default = def_unit_ul or "kbytes"
+o:depends("limit_type","static")
+o:value("bytes", "Bytes/s")
+o:value("kbytes", "KBytes/s")
+o:value("mbytes", "MBytes/s")
+
+--
+-- Dynamic
+--
+o = s:taboption("limit", Value, "dynamic_bw_down", translate("Download Bandwidth (Mbps)"), translate("Default value for download bandwidth"))
+o.default = def_up or '100'
+o.datatype = "uinteger"
+o:depends("limit_type","dynamic")
+
+o = s:taboption("limit", Value, "dynamic_bw_up", translate("Upload Bandwidth (Mbps)"), translate("Default value for upload bandwidth"))
+o.default = def_down or '100'
+o.datatype = "uinteger"
+o:depends("limit_type","dynamic")
+
+o = s:taboption("limit", Value, "dynamic_cidr", translate("Target Network (IPv4/MASK)"), translate("Network to be apply, e.g. 192.168.1.0/24, 10.2.0.0/16, etc"))
+o.datatype = "cidr4"
+ipc.routes({ family = 4, type = 1 }, function(rt) o.default = rt.dest end)
+o:depends("limit_type","dynamic")
+
+if has_ipv6 then
+ o = s:taboption("limit", Value, "dynamic_cidr6", translate("Target Network6 (IPv6/MASK)"), translate("Network to be apply, e.g. AAAA::BBBB/64, CCCC::1/128, etc"))
+ o.datatype = "cidr6"
+ o:depends("limit_type","dynamic")
+end
+
+o = s:taboption("limit", DynamicList, "limit_whitelist", translate("White List for Limit Rate"))
+o.datatype = "ipaddr"
+o:depends("limit_enable","1")
+
+--
+-- Priority
+--
+o = s:taboption("priority", Flag, "priority_enable", translate("Enable Traffic Priority"), translate("Enable this feature"))
+o.default = enable_priority or o.enabled
+o.rmempty = false
+
+o = s:taboption("priority", ListValue, "priority_netdev", translate("Default Network Interface"), translate("Network Interface for Traffic Shaping, e.g. br-lan, eth0.1, eth0, etc"))
+o:depends("priority_enable", "1")
+wa.cbi_add_networks(o)
+
+--
+-- Static Limit Rate - Download Rate
+--
+if limit_enable == "1" and limit_type == "static" then
+
+x = m:section(TypedSection, "download", translate("Static QoS-Download Rate"))
+x.anonymous = true
+x.addremove = true
+x.template = "cbi/tblsection"
+
+o = x:option(Value, "hostname", translate("Hostname"))
+o.datatype = "hostname"
+o.default = 'undefined'
+
+if has_ipv6 then
+ o = x:option(Value, "ipaddr", translate("IP Address(V4 / V6)"))
+else
+ o = x:option(Value, "ipaddr", translate("IP Address(V4 Only)"))
+end
+o.datatype = "ipaddr"
+if nixio.fs.access("/tmp/dhcp.leases") or nixio.fs.access("/var/dhcp6.leases") then
+ o.titleref = luci.dispatcher.build_url("admin", "status", "overview")
+end
+
+o = x:option(Value, "macaddr", translate("MAC (optional)"))
+o.rmempty = true
+o.datatype = "macaddr"
+
+o = x:option(Value, "rate", translate("Rate"))
+o.default = def_rate_dl or '50'
+o.size = 4
+o.datatype = "uinteger"
+
+o = x:option(ListValue, "unit", translate("Unit"))
+o.default = def_unit_dl or "kbytes"
+o:value("bytes", "Bytes/s")
+o:value("kbytes", "KBytes/s")
+o:value("mbytes", "MBytes/s")
+
+--
+-- Static Limit Rate - Upload Rate
+--
+y = m:section(TypedSection, "upload", translate("Static QoS-Upload Rate"))
+y.anonymous = true
+y.addremove = true
+y.template = "cbi/tblsection"
+
+o = y:option(Value, "hostname", translate("Hostname"))
+o.datatype = "hostname"
+o.default = 'undefined'
+
+if has_ipv6 then
+ o = y:option(Value, "ipaddr", translate("IP Address(V4 / V6)"))
+else
+ o = y:option(Value, "ipaddr", translate("IP Address(V4 Only)"))
+end
+o.datatype = "ipaddr"
+if nixio.fs.access("/tmp/dhcp.leases") or nixio.fs.access("/var/dhcp6.leases") then
+ o.titleref = luci.dispatcher.build_url("admin", "status", "overview")
+end
+
+o = y:option(Value, "macaddr", translate("MAC (optional)"))
+o.rmempty = true
+o.datatype = "macaddr"
+
+o = y:option(Value, "rate", translate("Rate"))
+o.default = def_rate_ul or '50'
+o.size = 4
+o.datatype = "uinteger"
+
+o = y:option(ListValue, "unit", translate("Unit"))
+o.default = def_unit_ul or "kbytes"
+o:value("bytes", "Bytes/s")
+o:value("kbytes", "KBytes/s")
+o:value("mbytes", "MBytes/s")
+
+end
+
+--
+-- Traffic Priority Settings
+--
+if enable_priority == "1" then
+
+s = m:section(TypedSection, "priority", translate("Traffic Priority Settings"))
+s.anonymous = true
+s.addremove = true
+s.template = "cbi/tblsection"
+
+o = s:option(ListValue, "protocol", translate("Protocol"))
+o.default = "tcp"
+o:value("tcp", "TCP")
+o:value("udp", "UDP")
+o:value("udplite", "UDP-Lite")
+o:value("sctp", "SCTP")
+o:value("dccp", "DCCP")
+
+o = s:option(ListValue, "priority", translate("Priority"))
+o.default = "1"
+o:value("-400", "1")
+o:value("-300", "2")
+o:value("-225", "3")
+o:value("-200", "4")
+o:value("-150", "5")
+o:value("-100", "6")
+o:value("0", "7")
+o:value("50", "8")
+o:value("100", "9")
+o:value("225", "10")
+o:value("300", "11")
+
+o = s:option(Value, "service", translate("Service"), translate("e.g. https, 23, (separator is comma)"))
+o.default = '?'
+
+o = s:option(Value, "comment", translate("Comment"))
+o.default = '?'
+
+end
+
+return m
diff --git a/package/lean/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm b/package/lean/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm
new file mode 100644
index 0000000000..5f9cb57d20
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm
@@ -0,0 +1,167 @@
+<%#
+ Copyright 2018 Rosy Song
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+
+
+<%:Realtime Rate%>
+
+<%:This page gives an overview over currently download/upload rate.%>
+
+
+
+
+
+<%+footer%>
diff --git a/package/lean/luci-app-nft-qos/po/es/nft-qos.po b/package/lean/luci-app-nft-qos/po/es/nft-qos.po
new file mode 100644
index 0000000000..2d729e001e
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/po/es/nft-qos.po
@@ -0,0 +1,241 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+"Last-Translator: \n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: es\n"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:136
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:155
+msgid "Bytes Total"
+msgstr "Total de bytes"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:141
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:160
+msgid "Collecting data..."
+msgstr "Recolectando datos..."
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:224
+msgid "Comment"
+msgstr "Comentario"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default Download Rate"
+msgstr "Velocidad de descarga predeterminada"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default Download Unit"
+msgstr "Unidad de descarga predeterminada"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Default Network Interface"
+msgstr "Interfaz de red predeterminada"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default Upload Rate"
+msgstr "Velocidad de carga predeterminada"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default Upload Unit"
+msgstr "Unidad de carga predeterminada"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default unit for download rate"
+msgstr "Unidad predeterminada para la velocidad de descarga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default unit for upload rate"
+msgstr "Unidad predeterminada para la velocidad de carga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Default value for download bandwidth"
+msgstr "Valor predeterminado para el ancho de banda de descarga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default value for download rate"
+msgstr "Valor predeterminado para la velocidad de descarga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Default value for upload bandwidth"
+msgstr "Valor predeterminado para el ancho de banda de carga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default value for upload rate"
+msgstr "Valor predeterminado para la velocidad de carga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Download Bandwidth (Mbps)"
+msgstr "Ancho de banda de descarga (Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:135
+msgid "Download Rate"
+msgstr "Velocidad de descarga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Enable Limit Rate Feature"
+msgstr "Habilitar función de límite de velocidad"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable Traffic Priority"
+msgstr "Habilitar prioridad de tráfico"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable this feature"
+msgstr "Habilitar esta característica"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:121
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:158
+msgid "Hostname"
+msgstr "Nombre de host"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:134
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:153
+msgid "IP Address"
+msgstr "Dirección IP"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:126
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:163
+msgid "IP Address(V4 / V6)"
+msgstr "Dirección IP (v4/v6)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:128
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:165
+msgid "IP Address(V4 Only)"
+msgstr "Dirección IP (sólo v4)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Limit Enable"
+msgstr "Habilitar límite"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Limit Type"
+msgstr "Tipo de límite"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:135
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:172
+msgid "MAC (optional)"
+msgstr "MAC (opcional)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:48
+msgid "MB"
+msgstr "MB"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:28
+msgid "NFT-QoS Settings"
+msgstr "Configuración de NFT-QoS"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Network Interface for Traffic Shaping, e.g. br-lan, eth0.1, eth0, etc"
+msgstr "Interfaz de red para configuración de tráfico, por ejemplo, br-lan, eth0.1, eth0, etc."
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Network to be apply, e.g. 192.168.1.0/24, 10.2.0.0/16, etc"
+msgstr "Red a aplicar, por ejemplo. 192.168.1.0/24, 10.2.0.0/16, etc."
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Network to be apply, e.g. AAAA::BBBB/64, CCCC::1/128, etc"
+msgstr "Red a aplicar, por ejemplo. AAAA::BBBB/64, CCCC::1/128, etc."
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:65
+msgid "No information available"
+msgstr "No hay información disponible"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:137
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:156
+msgid "Packets Total"
+msgstr "Paquetes totales"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:207
+msgid "Priority"
+msgstr "Prioridad"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:199
+msgid "Protocol"
+msgstr "Protocolo"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:16
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:23
+msgid "Qos over Nftables"
+msgstr "Qos sobre Nftables"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:12
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:139
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:176
+msgid "Rate"
+msgstr "Velocidad"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:130
+msgid "Realtime Download Rate"
+msgstr "Velocidad de descarga en tiempo real"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:125
+msgid "Realtime Rate"
+msgstr "Velocidad en tiempo real"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:149
+msgid "Realtime Upload Rate"
+msgstr "Velocidad de carga en tiempo real"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "Service"
+msgstr "Servicio"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:116
+msgid "Static QoS-Download Rate"
+msgstr "Velocidad de descarga de QoS estática"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:153
+msgid "Static QoS-Upload Rate"
+msgstr "Velocidad de carga de QoS estática"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Target Network (IPv4/MASK)"
+msgstr "Red de destino (IPv4 / MASK)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Target Network6 (IPv6/MASK)"
+msgstr "Red de destino 6 (IPv6/MÁSCARA)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:127
+msgid "This page gives an overview over currently download/upload rate."
+msgstr "Esta página ofrece una vista general sobre la velocidad de descarga/carga actual."
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:194
+msgid "Traffic Priority Settings"
+msgstr "Ajustes de prioridad de tráfico"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Type of Limit Rate"
+msgstr "Tipo de límite de velocidad"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:144
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:181
+msgid "Unit"
+msgstr "Unidad"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Upload Bandwidth (Mbps)"
+msgstr "Ancho de banda de carga (Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:154
+msgid "Upload Rate"
+msgstr "Velocidad de carga"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:96
+msgid "White List for Limit Rate"
+msgstr "Lista blanca para el límite de velocidad"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "e.g. https, 23, (separator is comma)"
+msgstr "p.ej. https, 23, (el separador es una coma)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:44
+msgid "kB"
+msgstr "kB"
diff --git a/package/lean/luci-app-nft-qos/po/templates/nft-qos.pot b/package/lean/luci-app-nft-qos/po/templates/nft-qos.pot
new file mode 100755
index 0000000000..8fecfa073a
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/po/templates/nft-qos.pot
@@ -0,0 +1,230 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:136
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:155
+msgid "Bytes Total"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:141
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:160
+msgid "Collecting data..."
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:224
+msgid "Comment"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default Download Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default Download Unit"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Default Network Interface"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default Upload Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default Upload Unit"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default unit for download rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default unit for upload rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Default value for download bandwidth"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default value for download rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Default value for upload bandwidth"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default value for upload rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Download Bandwidth (Mbps)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:135
+msgid "Download Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Enable Limit Rate Feature"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable Traffic Priority"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable this feature"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:121
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:158
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:134
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:153
+msgid "IP Address"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:126
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:163
+msgid "IP Address(V4 / V6)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:128
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:165
+msgid "IP Address(V4 Only)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Limit Enable"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Limit Type"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:135
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:172
+msgid "MAC (optional)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:48
+msgid "MB"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:28
+msgid "NFT-QoS Settings"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Network Interface for Traffic Shaping, e.g. br-lan, eth0.1, eth0, etc"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Network to be apply, e.g. 192.168.1.0/24, 10.2.0.0/16, etc"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Network to be apply, e.g. AAAA::BBBB/64, CCCC::1/128, etc"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:65
+msgid "No information available"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:137
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:156
+msgid "Packets Total"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:207
+msgid "Priority"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:199
+msgid "Protocol"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:16
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:23
+msgid "Qos over Nftables"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:12
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:139
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:176
+msgid "Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:130
+msgid "Realtime Download Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:125
+msgid "Realtime Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:149
+msgid "Realtime Upload Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "Service"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:116
+msgid "Static QoS-Download Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:153
+msgid "Static QoS-Upload Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Target Network (IPv4/MASK)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Target Network6 (IPv6/MASK)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:127
+msgid "This page gives an overview over currently download/upload rate."
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:194
+msgid "Traffic Priority Settings"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Type of Limit Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:144
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:181
+msgid "Unit"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Upload Bandwidth (Mbps)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:154
+msgid "Upload Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:96
+msgid "White List for Limit Rate"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "e.g. https, 23, (separator is comma)"
+msgstr ""
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:44
+msgid "kB"
+msgstr ""
diff --git a/package/lean/luci-app-nft-qos/po/zh-cn/nft-qos.po b/package/lean/luci-app-nft-qos/po/zh-cn/nft-qos.po
new file mode 100644
index 0000000000..73ce11f5d0
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/po/zh-cn/nft-qos.po
@@ -0,0 +1,244 @@
+#
+# Yangfl , 2019.
+#
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2019-01-03 22:28+0800\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Gtranslator 3.30.1\n"
+"Last-Translator: Yangfl \n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Language: zh_CN\n"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:136
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:155
+msgid "Bytes Total"
+msgstr "字节总数"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:141
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:160
+msgid "Collecting data..."
+msgstr "正在收集数据…"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:224
+msgid "Comment"
+msgstr "注释"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default Download Rate"
+msgstr "默认下载速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default Download Unit"
+msgstr "默认下载速率单位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Default Network Interface"
+msgstr "默认网络接口"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default Upload Rate"
+msgstr "默认上传速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default Upload Unit"
+msgstr "默认上传速率单位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default unit for download rate"
+msgstr "默认的下载速率单位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default unit for upload rate"
+msgstr "默认的上传速率单位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Default value for download bandwidth"
+msgstr "下载带宽的默认值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default value for download rate"
+msgstr "下载速率的默认值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Default value for upload bandwidth"
+msgstr "上传带宽的默认值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default value for upload rate"
+msgstr "上传速率的默认值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Download Bandwidth (Mbps)"
+msgstr "下载带宽(Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:135
+msgid "Download Rate"
+msgstr "下载速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Enable Limit Rate Feature"
+msgstr "开启速率限制功能"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable Traffic Priority"
+msgstr "开启流量优先级"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable this feature"
+msgstr "开启这个功能"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:121
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:158
+msgid "Hostname"
+msgstr "主机名"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:134
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:153
+msgid "IP Address"
+msgstr "IP 地址"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:126
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:163
+msgid "IP Address(V4 / V6)"
+msgstr "IP 地址(V4 / V6)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:128
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:165
+msgid "IP Address(V4 Only)"
+msgstr "IP 地址(仅 V4)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Limit Enable"
+msgstr "限速开启"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Limit Type"
+msgstr "限速类型"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:135
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:172
+msgid "MAC (optional)"
+msgstr "物理地址(可选)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:48
+msgid "MB"
+msgstr "MB"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:28
+msgid "NFT-QoS Settings"
+msgstr "NFT-QoS 设置"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Network Interface for Traffic Shaping, e.g. br-lan, eth0.1, eth0, etc"
+msgstr "流量整形的目标网络接口,例如br-lan、eth0.1、eth0等"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Network to be apply, e.g. 192.168.1.0/24, 10.2.0.0/16, etc"
+msgstr "将要应用规则的网络,例如192.168.1.0/24、10.2.0.0/16等"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Network to be apply, e.g. AAAA::BBBB/64, CCCC::1/128, etc"
+msgstr "将要应用规则的网络,例如AAAA::BBBB/64、CCCC::1/128等"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:65
+msgid "No information available"
+msgstr "没有更多的信息"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:137
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:156
+msgid "Packets Total"
+msgstr "数据包总数"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:207
+msgid "Priority"
+msgstr "优先级"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:199
+msgid "Protocol"
+msgstr "协议"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:16
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:23
+msgid "Qos over Nftables"
+msgstr "QoS Nftables 版"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:12
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:139
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:176
+msgid "Rate"
+msgstr "速率"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:130
+msgid "Realtime Download Rate"
+msgstr "实时下载速率"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:125
+msgid "Realtime Rate"
+msgstr "实时速率显示"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:149
+msgid "Realtime Upload Rate"
+msgstr "实时上传速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "Service"
+msgstr "服务"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:116
+msgid "Static QoS-Download Rate"
+msgstr "静态 QoS-下载速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:153
+msgid "Static QoS-Upload Rate"
+msgstr "静态 QoS-上传速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Target Network (IPv4/MASK)"
+msgstr "目标网络(IPv4 地址/掩码)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Target Network6 (IPv6/MASK)"
+msgstr "目标网络 v6(IPv6 地址/掩码)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:127
+msgid "This page gives an overview over currently download/upload rate."
+msgstr "该页面提供了当前上传和下载速率的一个总览。"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:194
+msgid "Traffic Priority Settings"
+msgstr "流量优先级设置"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Type of Limit Rate"
+msgstr "限速的类型"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:144
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:181
+msgid "Unit"
+msgstr "单位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Upload Bandwidth (Mbps)"
+msgstr "上传带宽(Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:154
+msgid "Upload Rate"
+msgstr "上传速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:96
+msgid "White List for Limit Rate"
+msgstr "限速白名单"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "e.g. https, 23, (separator is comma)"
+msgstr "例如https, 23(用逗号分隔)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:44
+msgid "kB"
+msgstr "kB"
diff --git a/package/lean/luci-app-nft-qos/po/zh-tw/nft-qos.po b/package/lean/luci-app-nft-qos/po/zh-tw/nft-qos.po
new file mode 100644
index 0000000000..5be9d8abf6
--- /dev/null
+++ b/package/lean/luci-app-nft-qos/po/zh-tw/nft-qos.po
@@ -0,0 +1,244 @@
+#
+# Yangfl , 2019.
+#
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2019-01-03 22:28+0800\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Gtranslator 3.30.1\n"
+"Last-Translator: Yangfl \n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Language: zh_TW\n"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:136
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:155
+msgid "Bytes Total"
+msgstr "位元組總數"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:141
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:160
+msgid "Collecting data..."
+msgstr "正在收集資料…"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:224
+msgid "Comment"
+msgstr "註釋"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default Download Rate"
+msgstr "預設下載速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default Download Unit"
+msgstr "預設下載速率單位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Default Network Interface"
+msgstr "預設網路介面"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default Upload Rate"
+msgstr "預設上傳速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default Upload Unit"
+msgstr "預設上傳速率單位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:53
+msgid "Default unit for download rate"
+msgstr "預設的下載速率單位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:65
+msgid "Default unit for upload rate"
+msgstr "預設的上傳速率單位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Default value for download bandwidth"
+msgstr "下載頻寬的預設值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:48
+msgid "Default value for download rate"
+msgstr "下載速率的預設值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Default value for upload bandwidth"
+msgstr "上傳頻寬的預設值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:60
+msgid "Default value for upload rate"
+msgstr "上傳速率的預設值"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:75
+msgid "Download Bandwidth (Mbps)"
+msgstr "下載頻寬(Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:135
+msgid "Download Rate"
+msgstr "下載速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Enable Limit Rate Feature"
+msgstr "開啟速率限制功能"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable Traffic Priority"
+msgstr "開啟流量優先順序"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:103
+msgid "Enable this feature"
+msgstr "開啟這個功能"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:121
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:158
+msgid "Hostname"
+msgstr "主機名"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:134
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:153
+msgid "IP Address"
+msgstr "IP 位址"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:126
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:163
+msgid "IP Address(V4 / V6)"
+msgstr "IP 位址(V4 / V6)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:128
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:165
+msgid "IP Address(V4 Only)"
+msgstr "IP 位址(僅 V4)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:38
+msgid "Limit Enable"
+msgstr "限速開啟"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Limit Type"
+msgstr "限速型別"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:135
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:172
+msgid "MAC (optional)"
+msgstr "實體位址(可選)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:48
+msgid "MB"
+msgstr "MB"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:28
+msgid "NFT-QoS Settings"
+msgstr "NFT-QoS 設定"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:107
+msgid "Network Interface for Traffic Shaping, e.g. br-lan, eth0.1, eth0, etc"
+msgstr "流量整形的目標網路介面,例如br-lan、eth0.1、eth0等"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Network to be apply, e.g. 192.168.1.0/24, 10.2.0.0/16, etc"
+msgstr "將要應用規則的網路,例如192.168.1.0/24、10.2.0.0/16等"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Network to be apply, e.g. AAAA::BBBB/64, CCCC::1/128, etc"
+msgstr "將要應用規則的網路,例如AAAA::BBBB/64、CCCC::1/128等"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:65
+msgid "No information available"
+msgstr "沒有更多的資訊"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:137
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:156
+msgid "Packets Total"
+msgstr "資料包總數"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:207
+msgid "Priority"
+msgstr "優先順序"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:199
+msgid "Protocol"
+msgstr "協議"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:16
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:23
+msgid "Qos over Nftables"
+msgstr "QoS Nftables 版"
+
+#: applications/luci-app-nft-qos/luasrc/controller/nft-qos.lua:12
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:139
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:176
+msgid "Rate"
+msgstr "速率"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:130
+msgid "Realtime Download Rate"
+msgstr "實時下載速率"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:125
+msgid "Realtime Rate"
+msgstr "實時速率顯示"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:149
+msgid "Realtime Upload Rate"
+msgstr "實時上傳速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "Service"
+msgstr "服務"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:116
+msgid "Static QoS-Download Rate"
+msgstr "靜態 QoS-下載速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:153
+msgid "Static QoS-Upload Rate"
+msgstr "靜態 QoS-上傳速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:85
+msgid "Target Network (IPv4/MASK)"
+msgstr "目標網路(IPv4 位址/掩碼)"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:91
+msgid "Target Network6 (IPv6/MASK)"
+msgstr "目標網路 v6(IPv6 位址/掩碼)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:127
+msgid "This page gives an overview over currently download/upload rate."
+msgstr "該頁面提供了當前上傳和下載速率的一個總覽。"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:194
+msgid "Traffic Priority Settings"
+msgstr "流量優先順序設定"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:42
+msgid "Type of Limit Rate"
+msgstr "限速的型別"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:144
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:181
+msgid "Unit"
+msgstr "單位"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:80
+msgid "Upload Bandwidth (Mbps)"
+msgstr "上傳頻寬(Mbps)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:154
+msgid "Upload Rate"
+msgstr "上傳速率"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:96
+msgid "White List for Limit Rate"
+msgstr "限速白名單"
+
+#: applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua:221
+msgid "e.g. https, 23, (separator is comma)"
+msgstr "例如https, 23(用逗號分隔)"
+
+#: applications/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm:44
+msgid "kB"
+msgstr "kB"
diff --git a/package/lean/luci-app-nps/Makefile b/package/lean/luci-app-nps/Makefile
new file mode 100644
index 0000000000..11e25ed058
--- /dev/null
+++ b/package/lean/luci-app-nps/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI for Nps
+LUCI_DEPENDS:=+wget +npc
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.1
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-nps/luasrc/controller/nps.lua b/package/lean/luci-app-nps/luasrc/controller/nps.lua
new file mode 100755
index 0000000000..04fb7ced23
--- /dev/null
+++ b/package/lean/luci-app-nps/luasrc/controller/nps.lua
@@ -0,0 +1,17 @@
+module("luci.controller.nps",package.seeall)
+function index()
+if not nixio.fs.access("/etc/config/nps")then
+return
+end
+local e
+e=entry({"admin","services","nps"},cbi("nps"),_("Nps Setting"),100)
+e.i18n="nps"
+e.dependent=true
+entry({"admin","services","nps","status"},call("status")).leaf=true
+end
+function status()
+local e={}
+e.running=luci.sys.call("pgrep npc > /dev/null")==0
+luci.http.prepare_content("application/json")
+luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-nps/luasrc/i18n/nps.zh-cn.lmo b/package/lean/luci-app-nps/luasrc/i18n/nps.zh-cn.lmo
new file mode 100644
index 0000000000..940bd4e7dc
Binary files /dev/null and b/package/lean/luci-app-nps/luasrc/i18n/nps.zh-cn.lmo differ
diff --git a/package/lean/luci-app-nps/luasrc/i18n/nps.zh_CN.po b/package/lean/luci-app-nps/luasrc/i18n/nps.zh_CN.po
new file mode 100644
index 0000000000..8910c020c9
--- /dev/null
+++ b/package/lean/luci-app-nps/luasrc/i18n/nps.zh_CN.po
@@ -0,0 +1,28 @@
+# zh_CN translation for npc.
+# Copyright (C) 2015 Yoyodyne, Inc. (msgids)
+# This file is distributed under the same license as the gettext package.
+#
+msgid ""
+msgstr ""
+"PO-Revision-Date: 2015-09-01 09:53中国标准时间\n"
+"Last-Translator: Ardentwheel \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+
+msgid "Nps Setting"
+msgstr "Nps内网穿透"
+
+msgid "Enable Compression"
+msgstr "使用压缩传输"
+
+msgid "Enable Encryption"
+msgstr "使用加密传输"
+
+msgid "The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources"
+msgstr "压缩传输内容,加快流量转发速度,会额外消耗 cpu 资源"
+
+msgid "Encrypted the communication between Npc and Nps, will effectively prevent the traffic intercepted."
+msgstr "加密传输 npc 与 nps 之间的通信内容,会有效防止流量被拦截"
\ No newline at end of file
diff --git a/package/lean/luci-app-nps/luasrc/model/cbi/nps.lua b/package/lean/luci-app-nps/luasrc/model/cbi/nps.lua
new file mode 100755
index 0000000000..ea7a4e0ec2
--- /dev/null
+++ b/package/lean/luci-app-nps/luasrc/model/cbi/nps.lua
@@ -0,0 +1,47 @@
+m=Map("nps")
+m.title=translate("Nps Setting")
+m.description=translate("Nps is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.")
+
+m:section(SimpleSection).template="nps/nps_status"
+
+s=m:section(TypedSection,"nps")
+s.addremove=false
+s.anonymous=true
+
+s:tab("basic",translate("Basic Setting"))
+enable=s:taboption("basic",Flag,"enabled",translate("Enable"))
+enable.rmempty=false
+server=s:taboption("basic",Value,"server_addr",translate("Server"),translate("Must an IPv4 address"))
+server.datatype="ipaddr"
+server.optional=false
+server.rmempty=false
+port=s:taboption("basic",Value,"server_port",translate("Port"))
+port.datatype="port"
+port.default="8024"
+port.optional=false
+port.rmempty=false
+protocol=s:taboption("basic",ListValue,"protocol",translate("Protocol Type"))
+protocol.default="tcp"
+protocol:value("tcp",translate("TCP Protocol"))
+protocol:value("kcp",translate("KCP Protocol"))
+vkey=s:taboption("basic",Value,"vkey",translate("vkey"))
+vkey.optional=false
+vkey.password=true
+vkey.rmempty=false
+compress=s:taboption("basic",Flag,"compress",translate("Enable Compression"),translate("The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources."))
+compress.default="1"
+compress.rmempty=false
+crypt=s:taboption("basic",Flag,"crypt",translate("Enable Encryption"),translate("Encrypted the communication between Npc and Nps, will effectively prevent the traffic intercepted."))
+crypt.default="1"
+crypt.rmempty=false
+log_level=s:taboption("basic",ListValue,"log_level",translate("Log Level"))
+log_level:value(0,"Emergency")
+log_level:value(2,"Critical")
+log_level:value(3,"Error")
+log_level:value(4,"Warning")
+log_level:value(7,"Debug")
+log_level.default="3"
+
+return m
+
+
diff --git a/package/lean/luci-app-nps/luasrc/view/nps/nps_status.htm b/package/lean/luci-app-nps/luasrc/view/nps/nps_status.htm
new file mode 100755
index 0000000000..5952344bb7
--- /dev/null
+++ b/package/lean/luci-app-nps/luasrc/view/nps/nps_status.htm
@@ -0,0 +1,22 @@
+
+
+
diff --git a/package/lean/luci-app-nps/root/etc/config/nps b/package/lean/luci-app-nps/root/etc/config/nps
new file mode 100755
index 0000000000..5b166cd1d1
--- /dev/null
+++ b/package/lean/luci-app-nps/root/etc/config/nps
@@ -0,0 +1,11 @@
+
+config npc 'config'
+ option enabled '1'
+ option server_addr '1.1.1.1'
+ option server_port '8024'
+ option protocol 'tcp'
+ option compress '1'
+ option crypt '1'
+ option vkey 'aaa'
+ option log_level '4'
+
diff --git a/package/lean/luci-app-nps/root/etc/init.d/nps b/package/lean/luci-app-nps/root/etc/init.d/nps
new file mode 100755
index 0000000000..e9ab09f96d
--- /dev/null
+++ b/package/lean/luci-app-nps/root/etc/init.d/nps
@@ -0,0 +1,64 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2006-2011 OpenWrt.org
+
+START=50
+USE_PROCD=1
+
+LOGFILE="/var/etc/nps.log"
+tmpconf="/tmp/etc/nps.conf"
+
+nps_header() {
+ local enabled server_addr server_port protocol vkey compress crypt log_level
+
+ config_get enabled $1 enabled
+ config_get server_addr $1 server_addr
+ config_get server_port $1 server_port
+ config_get protocol $1 protocol
+ config_get vkey $1 vkey
+ config_get compress $1 compress
+ config_get crypt $1 crypt
+ config_get log_level $1 log_level
+
+ [ -f $tmpconf ] && rm $tmpconf
+ echo "[common]" >$tmpconf
+ echo "server_addr=${server_addr}:${server_port}" >>$tmpconf
+ echo "conn_type=${protocol}" >>$tmpconf
+ echo "vkey=${vkey}" >>$tmpconf
+
+ nps_write_bool compress $1
+ nps_write_bool crypt $1
+
+ Log_level=$log_level
+ Enabled=$enabled
+}
+
+nps_write_bool() {
+ local opt="$1"
+ local config="$2"
+ local val
+
+ config_get_bool val $config "$opt"
+ if [ "$val" -eq 0 ]; then
+ echo "${opt}=false" >> $tmpconf
+ else
+ echo "${opt}=true" >> $tmpconf
+ fi
+}
+
+start_service() {
+ config_load nps
+ config_foreach nps_header nps
+
+ if [ "$Enabled" = "1" ];then
+ logger -t FRPC 'Starting Nps service'
+ #service_start /usr/bin/npc -config=$tmpconf -log_level=$Log_level -log_path=$LOGFILE
+ procd_open_instance
+ procd_set_param command /usr/bin/npc -config=$tmpconf -log_level=$Log_level
+ procd_set_param file $tmpconf
+ procd_set_param respawn
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_close_instance
+ fi
+}
+
diff --git a/package/lean/luci-app-nps/root/etc/uci-defaults/luci-nps b/package/lean/luci-app-nps/root/etc/uci-defaults/luci-nps
new file mode 100755
index 0000000000..f4f0996aa2
--- /dev/null
+++ b/package/lean/luci-app-nps/root/etc/uci-defaults/luci-nps
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@nps[-1]
+ add ucitrack nps
+ set ucitrack.@nps[-1].init=nps
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-openvpn-server/Makefile b/package/lean/luci-app-openvpn-server/Makefile
new file mode 100644
index 0000000000..4e51de6440
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/Makefile
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for OpenVPN Server
+LUCI_DEPENDS:=+openvpn-openssl +openvpn-easy-rsa +kmod-tun
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-openvpn-server
+PKG_VERSION:=2.0
+PKG_RELEASE:=18
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
+
+
diff --git a/package/lean/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua b/package/lean/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua
new file mode 100644
index 0000000000..3d05765f3f
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua
@@ -0,0 +1,22 @@
+
+module("luci.controller.openvpn-server", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/openvpn") then
+ return
+ end
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+
+ local page
+
+ entry({"admin", "vpn", "openvpn-server"}, cbi("openvpn-server/openvpn-server"), _("OpenVPN Server"), 80).dependent=false
+ entry({"admin", "vpn", "openvpn-server","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep openvpn >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua b/package/lean/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua
new file mode 100644
index 0000000000..183a85ae42
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua
@@ -0,0 +1,103 @@
+
+--require("luci.tools.webadmin")
+
+mp = Map("openvpn", "OpenVPN Server",translate("An easy config OpenVPN Server Web-UI"))
+
+mp:section(SimpleSection).template = "openvpn/openvpn_status"
+
+s = mp:section(TypedSection, "openvpn")
+s.anonymous = true
+s.addremove = false
+
+s:tab("basic", translate("Base Setting"))
+
+o = s:taboption("basic", Flag, "enabled", translate("Enable"))
+
+proto = s:taboption("basic",Value,"proto", translate("Proto"))
+proto:value("tcp", translate("TCP Server"))
+proto:value("udp", translate("UDP Server"))
+
+port = s:taboption("basic", Value, "port", translate("Port"))
+port.datatype = "range(1,65535)"
+
+ddns = s:taboption("basic", Value, "ddns", translate("WAN DDNS or IP"))
+ddns.datatype = "string"
+ddns.default = "exmple.com"
+ddns.rmempty = false
+
+localnet = s:taboption("basic", Value, "server", translate("Client Network"))
+localnet.datatype = "string"
+localnet.description = translate("VPN Client Network IP with subnet")
+
+list = s:taboption("basic", DynamicList, "push")
+list.title = translate("Client Settings")
+list.datatype = "string"
+list.description = translate("Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router")
+
+
+local o
+o = s:taboption("basic", Button,"certificate",translate("OpenVPN Client config file"))
+o.inputtitle = translate("Download .ovpn file")
+o.description = translate("If you are using IOS client, please download this .ovpn file and send it via QQ or Email to your IOS device")
+o.inputstyle = "reload"
+o.write = function()
+ luci.sys.call("sh /etc/genovpn.sh 2>&1 >/dev/null")
+ Download()
+end
+
+s:tab("code", translate("Special Code"))
+
+local conf = "/etc/ovpnadd.conf"
+local NXFS = require "nixio.fs"
+o = s:taboption("code", TextValue, "conf")
+o.description = translate("(!)Special Code you know that add in to client .ovpn file")
+o.rows = 13
+o.wrap = "off"
+o.cfgvalue = function(self, section)
+ return NXFS.readfile(conf) or ""
+end
+o.write = function(self, section, value)
+ NXFS.writefile(conf, value:gsub("\r\n", "\n"))
+end
+
+
+local pid = luci.util.exec("/usr/bin/pgrep openvpn")
+
+function openvpn_process_status()
+ local status = "OpenVPN is not running now "
+
+ if pid ~= "" then
+ status = "OpenVPN is running with the PID " .. pid .. ""
+ end
+
+ local status = { status=status }
+ local table = { pid=status }
+ return table
+end
+
+
+
+function Download()
+ local t,e
+ t=nixio.open("/tmp/my.ovpn","r")
+ luci.http.header('Content-Disposition','attachment; filename="my.ovpn"')
+ luci.http.prepare_content("application/octet-stream")
+ while true do
+ e=t:read(nixio.const.buffersize)
+ if(not e)or(#e==0)then
+ break
+ else
+ luci.http.write(e)
+ end
+ end
+ t:close()
+ luci.http.close()
+end
+
+function mp.on_after_commit(self)
+ os.execute("uci set firewall.openvpn.dest_port=$(uci get openvpn.myvpn.port) && uci commit firewall && /etc/init.d/firewall restart")
+ os.execute("/etc/init.d/openvpn restart")
+end
+
+
+return mp
diff --git a/package/lean/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm b/package/lean/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm
new file mode 100644
index 0000000000..752abd4d0b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/Makefile b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/Makefile
new file mode 100644
index 0000000000..4e51de6440
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/Makefile
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for OpenVPN Server
+LUCI_DEPENDS:=+openvpn-openssl +openvpn-easy-rsa +kmod-tun
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-openvpn-server
+PKG_VERSION:=2.0
+PKG_RELEASE:=18
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
+
+
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua
new file mode 100644
index 0000000000..3d05765f3f
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/controller/openvpn-server.lua
@@ -0,0 +1,22 @@
+
+module("luci.controller.openvpn-server", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/openvpn") then
+ return
+ end
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+
+ local page
+
+ entry({"admin", "vpn", "openvpn-server"}, cbi("openvpn-server/openvpn-server"), _("OpenVPN Server"), 80).dependent=false
+ entry({"admin", "vpn", "openvpn-server","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep openvpn >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua
new file mode 100644
index 0000000000..183a85ae42
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/model/cbi/openvpn-server/openvpn-server.lua
@@ -0,0 +1,103 @@
+
+--require("luci.tools.webadmin")
+
+mp = Map("openvpn", "OpenVPN Server",translate("An easy config OpenVPN Server Web-UI"))
+
+mp:section(SimpleSection).template = "openvpn/openvpn_status"
+
+s = mp:section(TypedSection, "openvpn")
+s.anonymous = true
+s.addremove = false
+
+s:tab("basic", translate("Base Setting"))
+
+o = s:taboption("basic", Flag, "enabled", translate("Enable"))
+
+proto = s:taboption("basic",Value,"proto", translate("Proto"))
+proto:value("tcp", translate("TCP Server"))
+proto:value("udp", translate("UDP Server"))
+
+port = s:taboption("basic", Value, "port", translate("Port"))
+port.datatype = "range(1,65535)"
+
+ddns = s:taboption("basic", Value, "ddns", translate("WAN DDNS or IP"))
+ddns.datatype = "string"
+ddns.default = "exmple.com"
+ddns.rmempty = false
+
+localnet = s:taboption("basic", Value, "server", translate("Client Network"))
+localnet.datatype = "string"
+localnet.description = translate("VPN Client Network IP with subnet")
+
+list = s:taboption("basic", DynamicList, "push")
+list.title = translate("Client Settings")
+list.datatype = "string"
+list.description = translate("Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router")
+
+
+local o
+o = s:taboption("basic", Button,"certificate",translate("OpenVPN Client config file"))
+o.inputtitle = translate("Download .ovpn file")
+o.description = translate("If you are using IOS client, please download this .ovpn file and send it via QQ or Email to your IOS device")
+o.inputstyle = "reload"
+o.write = function()
+ luci.sys.call("sh /etc/genovpn.sh 2>&1 >/dev/null")
+ Download()
+end
+
+s:tab("code", translate("Special Code"))
+
+local conf = "/etc/ovpnadd.conf"
+local NXFS = require "nixio.fs"
+o = s:taboption("code", TextValue, "conf")
+o.description = translate("(!)Special Code you know that add in to client .ovpn file")
+o.rows = 13
+o.wrap = "off"
+o.cfgvalue = function(self, section)
+ return NXFS.readfile(conf) or ""
+end
+o.write = function(self, section, value)
+ NXFS.writefile(conf, value:gsub("\r\n", "\n"))
+end
+
+
+local pid = luci.util.exec("/usr/bin/pgrep openvpn")
+
+function openvpn_process_status()
+ local status = "OpenVPN is not running now "
+
+ if pid ~= "" then
+ status = "OpenVPN is running with the PID " .. pid .. ""
+ end
+
+ local status = { status=status }
+ local table = { pid=status }
+ return table
+end
+
+
+
+function Download()
+ local t,e
+ t=nixio.open("/tmp/my.ovpn","r")
+ luci.http.header('Content-Disposition','attachment; filename="my.ovpn"')
+ luci.http.prepare_content("application/octet-stream")
+ while true do
+ e=t:read(nixio.const.buffersize)
+ if(not e)or(#e==0)then
+ break
+ else
+ luci.http.write(e)
+ end
+ end
+ t:close()
+ luci.http.close()
+end
+
+function mp.on_after_commit(self)
+ os.execute("uci set firewall.openvpn.dest_port=$(uci get openvpn.myvpn.port) && uci commit firewall && /etc/init.d/firewall restart")
+ os.execute("/etc/init.d/openvpn restart")
+end
+
+
+return mp
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm
new file mode 100644
index 0000000000..752abd4d0b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/luasrc/view/openvpn/openvpn_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/po/zh-cn/openvpn-server.po b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/po/zh-cn/openvpn-server.po
new file mode 100644
index 0000000000..b13d6a8967
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/po/zh-cn/openvpn-server.po
@@ -0,0 +1,53 @@
+msgid "OpenVPN Server"
+msgstr "OpenVPN 服务器"
+
+msgid "An easy config OpenVPN Server Web-UI"
+msgstr "易于使用的 OpenVPN 服务器 Web-UI"
+
+msgid "Base Setting"
+msgstr "基本设置"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "WAN DDNS or IP"
+msgstr "WAN口的 DDNS域名 或者 IP"
+
+
+msgid "Client Network"
+msgstr "客户端网段"
+
+msgid "VPN Client Network IP with subnet"
+msgstr "客户端分配的网段地址(默认为 10.8.0.0 255.255.255.0)"
+
+
+msgid "Client Settings"
+msgstr "客户端推送配置"
+
+msgid "OpenVPN Client config file"
+msgstr "OpenVPN 客户端配置文件"
+
+
+msgid "Download .ovpn file"
+msgstr "一键下载 .ovpn 文件"
+
+msgid "If you are using IOS client, please download this .ovpn file and send it via QQ or Email to your IOS device"
+msgstr "如果你使用的是 iOS 设备,你可以使用 QQ 或者邮件发送到自己的设备上用 OpenVPN 客户端打开导入"
+
+msgid "Special Code"
+msgstr "特殊代码"
+
+msgid "(!)Special Code you know that add in to client .ovpn file"
+msgstr "(!)特殊代码将自动合并到客户端的 .ovpn 配置文件中"
+
+msgid "Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router"
+msgstr "根据路由的实际LAN IP 修改 route 192.168.0.0 255.255.255.0 和 dhcp-option DNS 192.168.0.1 这两行"
+
+msgid "OpenVPN status"
+msgstr "OpenVPN 服务器运行状态"
+
+msgid "Proto"
+msgstr "协议"
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/config/openvpn b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/config/openvpn
new file mode 100644
index 0000000000..6a829f4e60
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/config/openvpn
@@ -0,0 +1,28 @@
+
+config openvpn 'myvpn'
+ option enabled '0'
+ option proto 'tcp-server'
+ option port '1194'
+ option ddns example.com
+ option dev 'tun'
+ option topology 'subnet'
+ option server '10.8.0.0 255.255.255.0'
+ option comp_lzo 'adaptive'
+ option ca '/etc/openvpn/ca.crt'
+ option dh '/etc/openvpn/dh1024.pem'
+ option cert '/etc/openvpn/server.crt'
+ option key '/etc/openvpn/server.key'
+ option persist_key '1'
+ option persist_tun '1'
+ option user 'nobody'
+ option group 'nogroup'
+ option max_clients '10'
+ option keepalive '10 120'
+ option verb '3'
+ option status '/var/log/openvpn_status.log'
+ option log '/tmp/openvpn.log'
+ list push 'route 192.168.0.0 255.255.255.0'
+ list push 'comp-lzo adaptive'
+ list push 'redirect-gateway def1 bypass-dhcp'
+ list push 'dhcp-option DNS 192.168.0.1'
+
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt
new file mode 100755
index 0000000000..7b7ba5ef81
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgIJAK4YoeE9Uk3wMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNV
+BAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkoxCzAJBgNVBAoTAlpKMQsw
+CQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExGDAW
+BgkqhkiG9w0BCQEWCVpKQFpKLmNvbTAeFw0xNzA4MjMxNDE4MDVaFw0yNzA4MjEx
+NDE4MDVaMH0xCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkox
+CzAJBgNVBAoTAlpKMQswCQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNV
+BCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAsLBNLkjfNUK4Rb3xgJD0EEgzp6b+5zkiibT3wdS9
+SKZqkqJRcM/z7Ifc79LKXDDHrVnajkyc8QSaXOKVW8pTx75fYnoHjNbeU9JZHoTg
+9GgRWq5HHUJlxhsdbcGeTxOHrMRz2d+VzvNvs5KOLJaqGkRmm/KMb7nTRnwjhx4A
+pWsCAwEAAaOB4zCB4DAdBgNVHQ4EFgQUjdUEeRAFTxsSYxGsANT8zDEAuAkwgbAG
+A1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQswCQYDVQQG
+EwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkG
+A1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJ
+KoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4GBAAAN0aRmQGNsF23CxShEnj3ohgpYA20F2FwEWYmCCWXe
+CKxuPGtPZAeLmToIMgn75QlyuvVG+Uoe7I6ylbEK3XoeNStcS61wAXL8hIPfMcUX
+fDsImBvc+Bo+LxQMWMSz0r88+B8784KELyaQKOnvPlTrnTuyP9RftjUWpjy23Kjd
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key
new file mode 100755
index 0000000000..796fbf10ea
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCwTS5I3zVCuEW9
+8YCQ9BBIM6em/uc5Iom098HUvUimapKiUXDP8+yH3O/Sylwwx61Z2o5MnPEEmlzi
+lVvKU8e+X2J6B4zW3lPSWR6E4PRoEVquRx1CZcYbHW3Bnk8Th6zEc9nflc7zb7OS
+jiyWqhpEZpvyjG+500Z8I4ceAKVrAgMBAAECgYBfiTRyTlzCg6z3qNioV7hgBWpI
+wHcbtz0BkKRWXh1q5vDBEJtQkGCoCF5iHmvkpUuSY/9U6gACIicFCk8QLrpVZGzY
+1SgOQS796La2gYR0clCvtsf8Kg4BYCKkF40jGDGZTGKtWa9mPuOZXZFhSMZJiCQi
+UIfTN3D9Ngt/nLFMwQJBANwavdYXnPxGr/0rL1ct0RUjsZLyd9kmr1gD4Nq+/uwC
+FQrNHl/ieXOsSeD0eVNtuTFzmt8sTz963SXmbPv01dMCQQDNgPioq4M/1sluGUdI
+yK5bantPAD7A7wTb+uryP7lNMuDSGTXgLsh+RrhBuDlUdi+OPc0dIJV1fTjEiLbI
+fLsJAkEAoSSe6Zh+IaDrBfJRBYWQtuZcApasMfqFk227eMsuvcFEgJTt8QtRGeQA
+bsbt8Ku7Uz4tG8lH99TPBglurwRwawJAALVrJhnBUB0LgMexiTy37TTGzBTyInQw
+jhmlacRyfSOpxs+zcdx3cIgJ5qAeAn6N1227IViVa1xNL9BQ4QwdEQJBAJjLcCYS
+twmgoyOj4K5+8+aAuuXWj7lpxEakOZbghrBmy0GytiRKN9wb/x8QF6EtrJAmYltL
+wMMDUDE0zd+hc2Q=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr
new file mode 100755
index 0000000000..a3549ca15e
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBvzCCASgCAQAwfzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQH
+EwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMRAwDgYDVQQDEwdjbGllbnQx
+MRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb20wgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALdrQAvB76fD+lQywteH+qyFGK6vRLrd
+Vw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFshmH9TMrPWsD0j/B2t
+BrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUsCTlRw9RE74FequBj
+2BHGnOBv2GYRAgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQBfvn2fP2Tj8FTZH+Xk
+9u04rYaQdspSyL61F4QIEiP5UOUzbnSSU/B72KF5gm8b0irXGnTbHlXeMv6WXaAS
+VF1fEeM+gsGJIDOvomT5PKfudezr6DpGrUammQnRY3tho078Ao0Mkq8PAWpiVWGV
+Z7rsqHhI3mWYLNyne9n8K224pg==
+-----END CERTIFICATE REQUEST-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key
new file mode 100755
index 0000000000..076ce4e72b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALdrQAvB76fD+lQy
+wteH+qyFGK6vRLrdVw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFsh
+mH9TMrPWsD0j/B2tBrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUs
+CTlRw9RE74FequBj2BHGnOBv2GYRAgMBAAECgYBhWatEkkqA4KOczNRdUO7bYkkX
+bi8sfw4WK9b6+h6JF+dqLsZ6FkrJfd9QPsRBgTWcYtwb1dogi9PCirJF4gKmSsv1
+h/BISW4lrXJPf08aJAuBF0ym8XZUgVLLptn8KdXX3xc6YF6K336AnNNLZ80X4El8
+m7X4d8Y3F4k3Dj04AQJBAN9NkHYwevaZ8jfv5rZSTKECmdk1DZITd756+sObFAs1
+vX9VfunwVw6xWmaUyMt8oPFqb0wKES9zqrViaMhU9YkCQQDSRqV3ldHcaZJ6sTgm
+T8ZKm+UpbC4zat4rGSBYKaoeLRPh5nxP892rOfBAevkoIQzW7LfVfoMImM/i1J6T
+lNJJAkAHSOZ/lJFOXjNSs9bY99JcJlOSjHKG42+ynRx1KSf8PaKS9t0PELImXo7O
+begnC0fM2GYNGv74h1N4W1+DuZHRAkEAlNov3jSvh+EwMSxs/Cnyy/QJyEXteraH
+KWkzDVYJOC1e6sZXb93JKKHuIicrY63pwed2x2o0ZgyO9RrgWsa6CQJBAMogPcRO
+jUGjYs3IYXW6Suf1bRJN2aS81gx7lqyLQE3ignH6e9kMxcrzG4AzzePCLouY3waL
+HS1KW8V9I5c4qTs=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12 b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12
new file mode 100755
index 0000000000..1511e1d0af
Binary files /dev/null and b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12 differ
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem
new file mode 100755
index 0000000000..01eba3e5a1
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAMzJWsEtMQ76G81O8RzEweDaPrio4NihRBo0fmNuh3IjJtFJ++URPW4y
+HHtnGOkPoMwQjGQ3GQV+lfcR7QGWojET4NskXOk6mdFU+/nYB9s5YY7RR4qyln9b
+dNMp3KnMLtILNH1rJRoqYbSNoz3Z2cS59Ejp1dgXqN8kSP8wiqDjAgEC
+-----END DH PARAMETERS-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt
new file mode 100644
index 0000000000..4bb34b80fc
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt
@@ -0,0 +1,2 @@
+V 270821142642Z 01 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+V 270821142658Z 02 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr
new file mode 100755
index 0000000000..8f7e63a347
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr
@@ -0,0 +1 @@
+unique_subject = yes
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old
new file mode 100755
index 0000000000..8f7e63a347
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old
@@ -0,0 +1 @@
+unique_subject = yes
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old
new file mode 100755
index 0000000000..d74831ee49
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old
@@ -0,0 +1 @@
+V 270821142642Z 01 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial
new file mode 100755
index 0000000000..75016ea362
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial
@@ -0,0 +1 @@
+03
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old
new file mode 100755
index 0000000000..9e22bcb8e3
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old
@@ -0,0 +1 @@
+02
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr
new file mode 100755
index 0000000000..c83aadabec
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBvjCCAScCAQAwfjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQH
+EwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ8wDQYDVQQDEwZzZXJ2ZXIx
+EDAOBgNVBCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4GcuM6tPT6F4xjK60x1xczS6RUCIh3YD
++4fYTk58fZUifY5w3GgQJ/598HmThoPvqRZ4roZe6kJ0OE03Dck0poReZK3dkd3f
+Ar9T+PvAm8u9k3wmq70KxsNd2l2zwP9yoS8eC/amcXf5ADiKrqvAhhGrEt4eghPo
+w9W7by6/bsECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBAMrytQrG3MGVTD2vsZkw
+hJn9U4MYmikqFxEcSJ+JEeDJ0w3NTn95XJmFtmuT/CwsrnP9g+1neSpCXsZewozd
+QRisYBF9Rl9qw9fH2o1S/GIVpTDIiTtWjegZA6FPkhQQyY387LU9Lp4vG9hdNbuz
+QNDs6cI9T0U53PZiq7R65rGV
+-----END CERTIFICATE REQUEST-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key
new file mode 100755
index 0000000000..3aab5cd4bb
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOBnLjOrT0+heMYy
+utMdcXM0ukVAiId2A/uH2E5OfH2VIn2OcNxoECf+ffB5k4aD76kWeK6GXupCdDhN
+Nw3JNKaEXmSt3ZHd3wK/U/j7wJvLvZN8Jqu9CsbDXdpds8D/cqEvHgv2pnF3+QA4
+iq6rwIYRqxLeHoIT6MPVu28uv27BAgMBAAECgYEAqno4g1BVxG0rT4cin1fy5E7e
+Y9YO4ropdGFTVsoRkWZG+ZPI8eisvXV9P79c8AgzfgVwUUYLvXQWzt76QCLnN11x
+pBURloJbEReEEzIF6z3LiT9CKgVWY4vtC6f5OW4Kd+EZxXMftFz8kMzRuRQLvOzR
+WeU+3GD42mIwYjsXBrECQQD2P6azeZJ1ZIEf+9Ys19tSQ6uxbXyvghlTfhmZfBW3
+bshNRH4+L4lnnbm75pFkWqR0gYSAD3toRymccctN2K9jAkEA6UoRwURTjIER8yZO
+6gLTFtzb841jv6TPErOY7CzNZ/TYBUdUBmM/NB4+RgBpBNFXB3uqweNVPO6mhyQP
+zB88iwJBAOeH6YGOqTpfiVk9PJ9lRf4PSnvE6htLQ+zQ/9jkZrbWHtcns1tc7uDR
+2DToEYQ+BarVrHOMGwhtAJ7sD6/eMOkCQFjn8XxGHdEeH0kbAFgOW4QLB4f6Clmu
+5XurFkxrhpxnoyvf0xXcHOov3GuxHFTJsvEXICxkBsgB61T1WU/hV6ECQGzk5Eqo
+uik2OAYMOb7NPGYMWS68DpmP11QDuvoj5zm2vdzohXHyBM7mO12MLSrTpxfgjWhQ
+pqwEbErchuKkzN4=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf
new file mode 100755
index 0000000000..93ac6eaba8
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf
@@ -0,0 +1,286 @@
+# For use with easy-rsa version 2.0 and OpenSSL 1.0.0*
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha256 # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+default_md = sha256
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation after 2004).
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/vars b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/vars
new file mode 100755
index 0000000000..4873fbcf74
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/easy-rsa/vars
@@ -0,0 +1,19 @@
+export EASY_RSA="/etc/easy-rsa"
+export OPENSSL="openssl"
+export PKCS11TOOL="pkcs11-tool"
+export GREP="grep"
+export KEY_CONFIG=`/usr/sbin/whichopensslcnf $EASY_RSA`
+export KEY_DIR="$EASY_RSA/keys"
+echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
+export PKCS11_MODULE_PATH="dummy"
+export PKCS11_PIN="dummy"
+export KEY_SIZE=1024
+export CA_EXPIRE=3650
+export KEY_EXPIRE=3650
+export KEY_COUNTRY="CN"
+export KEY_PROVINCE="ZJ"
+export KEY_CITY="ZJ"
+export KEY_ORG="ZJ"
+export KEY_EMAIL="ZJ@ZJ.com"
+export KEY_OU="ZJ"
+export KEY_NAME="EasyRSA"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/genovpn.sh b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/genovpn.sh
new file mode 100755
index 0000000000..146d6b2369
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/genovpn.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+ddns=`uci get openvpn.myvpn.ddns`
+port=`uci get openvpn.myvpn.port`
+proto=`uci get openvpn.myvpn.proto`
+
+cat > /tmp/my.ovpn <' >> /tmp/my.ovpn
+cat /etc/openvpn/ca.crt >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+cat /etc/openvpn/client1.crt >> /tmp/my.ovpn
+echo ' ' >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+cat /etc/openvpn/client1.key >> /tmp/my.ovpn
+echo ' ' >> /tmp/my.ovpn
+[ -f /etc/ovpnadd.conf ] && cat /etc/ovpnadd.conf >> /tmp/my.ovpn
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/ca.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/ca.crt
new file mode 100755
index 0000000000..7b7ba5ef81
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgIJAK4YoeE9Uk3wMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNV
+BAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkoxCzAJBgNVBAoTAlpKMQsw
+CQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExGDAW
+BgkqhkiG9w0BCQEWCVpKQFpKLmNvbTAeFw0xNzA4MjMxNDE4MDVaFw0yNzA4MjEx
+NDE4MDVaMH0xCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkox
+CzAJBgNVBAoTAlpKMQswCQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNV
+BCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAsLBNLkjfNUK4Rb3xgJD0EEgzp6b+5zkiibT3wdS9
+SKZqkqJRcM/z7Ifc79LKXDDHrVnajkyc8QSaXOKVW8pTx75fYnoHjNbeU9JZHoTg
+9GgRWq5HHUJlxhsdbcGeTxOHrMRz2d+VzvNvs5KOLJaqGkRmm/KMb7nTRnwjhx4A
+pWsCAwEAAaOB4zCB4DAdBgNVHQ4EFgQUjdUEeRAFTxsSYxGsANT8zDEAuAkwgbAG
+A1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQswCQYDVQQG
+EwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkG
+A1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJ
+KoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4GBAAAN0aRmQGNsF23CxShEnj3ohgpYA20F2FwEWYmCCWXe
+CKxuPGtPZAeLmToIMgn75QlyuvVG+Uoe7I6ylbEK3XoeNStcS61wAXL8hIPfMcUX
+fDsImBvc+Bo+LxQMWMSz0r88+B8784KELyaQKOnvPlTrnTuyP9RftjUWpjy23Kjd
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.crt
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.crt
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.key b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.key
new file mode 100755
index 0000000000..076ce4e72b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/client1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALdrQAvB76fD+lQy
+wteH+qyFGK6vRLrdVw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFsh
+mH9TMrPWsD0j/B2tBrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUs
+CTlRw9RE74FequBj2BHGnOBv2GYRAgMBAAECgYBhWatEkkqA4KOczNRdUO7bYkkX
+bi8sfw4WK9b6+h6JF+dqLsZ6FkrJfd9QPsRBgTWcYtwb1dogi9PCirJF4gKmSsv1
+h/BISW4lrXJPf08aJAuBF0ym8XZUgVLLptn8KdXX3xc6YF6K336AnNNLZ80X4El8
+m7X4d8Y3F4k3Dj04AQJBAN9NkHYwevaZ8jfv5rZSTKECmdk1DZITd756+sObFAs1
+vX9VfunwVw6xWmaUyMt8oPFqb0wKES9zqrViaMhU9YkCQQDSRqV3ldHcaZJ6sTgm
+T8ZKm+UpbC4zat4rGSBYKaoeLRPh5nxP892rOfBAevkoIQzW7LfVfoMImM/i1J6T
+lNJJAkAHSOZ/lJFOXjNSs9bY99JcJlOSjHKG42+ynRx1KSf8PaKS9t0PELImXo7O
+begnC0fM2GYNGv74h1N4W1+DuZHRAkEAlNov3jSvh+EwMSxs/Cnyy/QJyEXteraH
+KWkzDVYJOC1e6sZXb93JKKHuIicrY63pwed2x2o0ZgyO9RrgWsa6CQJBAMogPcRO
+jUGjYs3IYXW6Suf1bRJN2aS81gx7lqyLQE3ignH6e9kMxcrzG4AzzePCLouY3waL
+HS1KW8V9I5c4qTs=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem
new file mode 100755
index 0000000000..01eba3e5a1
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAMzJWsEtMQ76G81O8RzEweDaPrio4NihRBo0fmNuh3IjJtFJ++URPW4y
+HHtnGOkPoMwQjGQ3GQV+lfcR7QGWojET4NskXOk6mdFU+/nYB9s5YY7RR4qyln9b
+dNMp3KnMLtILNH1rJRoqYbSNoz3Z2cS59Ejp1dgXqN8kSP8wiqDjAgEC
+-----END DH PARAMETERS-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.crt b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.crt
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.crt
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.key b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.key
new file mode 100755
index 0000000000..3aab5cd4bb
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpn/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOBnLjOrT0+heMYy
+utMdcXM0ukVAiId2A/uH2E5OfH2VIn2OcNxoECf+ffB5k4aD76kWeK6GXupCdDhN
+Nw3JNKaEXmSt3ZHd3wK/U/j7wJvLvZN8Jqu9CsbDXdpds8D/cqEvHgv2pnF3+QA4
+iq6rwIYRqxLeHoIT6MPVu28uv27BAgMBAAECgYEAqno4g1BVxG0rT4cin1fy5E7e
+Y9YO4ropdGFTVsoRkWZG+ZPI8eisvXV9P79c8AgzfgVwUUYLvXQWzt76QCLnN11x
+pBURloJbEReEEzIF6z3LiT9CKgVWY4vtC6f5OW4Kd+EZxXMftFz8kMzRuRQLvOzR
+WeU+3GD42mIwYjsXBrECQQD2P6azeZJ1ZIEf+9Ys19tSQ6uxbXyvghlTfhmZfBW3
+bshNRH4+L4lnnbm75pFkWqR0gYSAD3toRymccctN2K9jAkEA6UoRwURTjIER8yZO
+6gLTFtzb841jv6TPErOY7CzNZ/TYBUdUBmM/NB4+RgBpBNFXB3uqweNVPO6mhyQP
+zB88iwJBAOeH6YGOqTpfiVk9PJ9lRf4PSnvE6htLQ+zQ/9jkZrbWHtcns1tc7uDR
+2DToEYQ+BarVrHOMGwhtAJ7sD6/eMOkCQFjn8XxGHdEeH0kbAFgOW4QLB4f6Clmu
+5XurFkxrhpxnoyvf0xXcHOov3GuxHFTJsvEXICxkBsgB61T1WU/hV6ECQGzk5Eqo
+uik2OAYMOb7NPGYMWS68DpmP11QDuvoj5zm2vdzohXHyBM7mO12MLSrTpxfgjWhQ
+pqwEbErchuKkzN4=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpncert.sh b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpncert.sh
new file mode 100755
index 0000000000..61d6f0cb9c
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/openvpncert.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+clean-all
+echo -en "\n\n\n\n\n\n\n\n" | build-ca
+build-dh
+build-key-server server
+build-key-pkcs12 client1
+cp /etc/easy-rsa/keys/ca.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/server.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/server.key /etc/openvpn/
+cp /etc/easy-rsa/keys/dh1024.pem /etc/openvpn/
+cp /etc/easy-rsa/keys/client1.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/client1.key /etc/openvpn/
+/etc/init.d/openvpn restart
+echo "OpenVPN Cert renew successfully"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/ovpnadd.conf b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/ovpnadd.conf
new file mode 100644
index 0000000000..0d03f345b3
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/ovpnadd.conf
@@ -0,0 +1,9 @@
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "GET http://rd.go.10086.cn"
+http-proxy-option EXT1 "X-Online-Host: rd.go.10086.cn"
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "X-Online-Host: rd.go.10086.cn"
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "Host: rd.go.10086.cn"
+http-proxy-option EXT1 "GET http://rd.go.10086.cn"
+http-proxy-option EXT1 "Host: rd.go.10086.cn"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/uci-defaults/openvpn b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/uci-defaults/openvpn
new file mode 100755
index 0000000000..412fe1c8ab
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/luci-app-openvpn-server/root/etc/uci-defaults/openvpn
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+uci set network.vpn0="interface"
+uci set network.vpn0.ifname="tun0"
+uci set network.vpn0.proto="none"
+uci commit network;
+/etc/init.d/network reload
+
+uci delete firewall.openvpn
+uci add firewall rule
+uci rename firewall.@rule[-1]="openvpn"
+uci set firewall.@rule[-1].name="openvpn"
+uci set firewall.@rule[-1].target="ACCEPT"
+uci set firewall.@rule[-1].src="wan"
+uci set firewall.@rule[-1].proto="tcp udp"
+uci set firewall.@rule[-1].dest_port="1194"
+
+uci delete firewall.vpn
+uci delete firewall.vpnwan
+uci delete firewall.vpnlan
+uci commit firewall
+
+uci add firewall zone
+uci rename firewall.@zone[-1]="vpn"
+uci set firewall.@zone[-1].name="vpn"
+uci set firewall.@zone[-1].input="ACCEPT"
+uci set firewall.@zone[-1].forward="ACCEPT"
+uci set firewall.@zone[-1].output="ACCEPT"
+uci set firewall.@zone[-1].masq="1"
+uci set firewall.@zone[-1].network="vpn0"
+uci add firewall forwarding
+uci rename firewall.@forwarding[-1]="vpnwan"
+uci set firewall.@forwarding[-1].src="vpn"
+uci set firewall.@forwarding[-1].dest="wan"
+uci add firewall forwarding
+uci rename firewall.@forwarding[-1]="vpnlan"
+uci set firewall.@forwarding[-1].src="vpn"
+uci set firewall.@forwarding[-1].dest="lan"
+uci commit firewall;
+/etc/init.d/firewall restart
+
+/etc/init.d/openvpn enable
+/etc/init.d/openvpn stop
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-openvpn-server/po/zh-cn/openvpn-server.po b/package/lean/luci-app-openvpn-server/po/zh-cn/openvpn-server.po
new file mode 100644
index 0000000000..b13d6a8967
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/po/zh-cn/openvpn-server.po
@@ -0,0 +1,53 @@
+msgid "OpenVPN Server"
+msgstr "OpenVPN 服务器"
+
+msgid "An easy config OpenVPN Server Web-UI"
+msgstr "易于使用的 OpenVPN 服务器 Web-UI"
+
+msgid "Base Setting"
+msgstr "基本设置"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "WAN DDNS or IP"
+msgstr "WAN口的 DDNS域名 或者 IP"
+
+
+msgid "Client Network"
+msgstr "客户端网段"
+
+msgid "VPN Client Network IP with subnet"
+msgstr "客户端分配的网段地址(默认为 10.8.0.0 255.255.255.0)"
+
+
+msgid "Client Settings"
+msgstr "客户端推送配置"
+
+msgid "OpenVPN Client config file"
+msgstr "OpenVPN 客户端配置文件"
+
+
+msgid "Download .ovpn file"
+msgstr "一键下载 .ovpn 文件"
+
+msgid "If you are using IOS client, please download this .ovpn file and send it via QQ or Email to your IOS device"
+msgstr "如果你使用的是 iOS 设备,你可以使用 QQ 或者邮件发送到自己的设备上用 OpenVPN 客户端打开导入"
+
+msgid "Special Code"
+msgstr "特殊代码"
+
+msgid "(!)Special Code you know that add in to client .ovpn file"
+msgstr "(!)特殊代码将自动合并到客户端的 .ovpn 配置文件中"
+
+msgid "Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router"
+msgstr "根据路由的实际LAN IP 修改 route 192.168.0.0 255.255.255.0 和 dhcp-option DNS 192.168.0.1 这两行"
+
+msgid "OpenVPN status"
+msgstr "OpenVPN 服务器运行状态"
+
+msgid "Proto"
+msgstr "协议"
diff --git a/package/lean/luci-app-openvpn-server/root/etc/config/openvpn b/package/lean/luci-app-openvpn-server/root/etc/config/openvpn
new file mode 100644
index 0000000000..6a829f4e60
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/config/openvpn
@@ -0,0 +1,28 @@
+
+config openvpn 'myvpn'
+ option enabled '0'
+ option proto 'tcp-server'
+ option port '1194'
+ option ddns example.com
+ option dev 'tun'
+ option topology 'subnet'
+ option server '10.8.0.0 255.255.255.0'
+ option comp_lzo 'adaptive'
+ option ca '/etc/openvpn/ca.crt'
+ option dh '/etc/openvpn/dh1024.pem'
+ option cert '/etc/openvpn/server.crt'
+ option key '/etc/openvpn/server.key'
+ option persist_key '1'
+ option persist_tun '1'
+ option user 'nobody'
+ option group 'nogroup'
+ option max_clients '10'
+ option keepalive '10 120'
+ option verb '3'
+ option status '/var/log/openvpn_status.log'
+ option log '/tmp/openvpn.log'
+ list push 'route 192.168.0.0 255.255.255.0'
+ list push 'comp-lzo adaptive'
+ list push 'redirect-gateway def1 bypass-dhcp'
+ list push 'dhcp-option DNS 192.168.0.1'
+
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/01.pem
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/02.pem
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt
new file mode 100755
index 0000000000..7b7ba5ef81
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgIJAK4YoeE9Uk3wMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNV
+BAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkoxCzAJBgNVBAoTAlpKMQsw
+CQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExGDAW
+BgkqhkiG9w0BCQEWCVpKQFpKLmNvbTAeFw0xNzA4MjMxNDE4MDVaFw0yNzA4MjEx
+NDE4MDVaMH0xCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkox
+CzAJBgNVBAoTAlpKMQswCQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNV
+BCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAsLBNLkjfNUK4Rb3xgJD0EEgzp6b+5zkiibT3wdS9
+SKZqkqJRcM/z7Ifc79LKXDDHrVnajkyc8QSaXOKVW8pTx75fYnoHjNbeU9JZHoTg
+9GgRWq5HHUJlxhsdbcGeTxOHrMRz2d+VzvNvs5KOLJaqGkRmm/KMb7nTRnwjhx4A
+pWsCAwEAAaOB4zCB4DAdBgNVHQ4EFgQUjdUEeRAFTxsSYxGsANT8zDEAuAkwgbAG
+A1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQswCQYDVQQG
+EwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkG
+A1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJ
+KoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4GBAAAN0aRmQGNsF23CxShEnj3ohgpYA20F2FwEWYmCCWXe
+CKxuPGtPZAeLmToIMgn75QlyuvVG+Uoe7I6ylbEK3XoeNStcS61wAXL8hIPfMcUX
+fDsImBvc+Bo+LxQMWMSz0r88+B8784KELyaQKOnvPlTrnTuyP9RftjUWpjy23Kjd
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key
new file mode 100755
index 0000000000..796fbf10ea
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/ca.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCwTS5I3zVCuEW9
+8YCQ9BBIM6em/uc5Iom098HUvUimapKiUXDP8+yH3O/Sylwwx61Z2o5MnPEEmlzi
+lVvKU8e+X2J6B4zW3lPSWR6E4PRoEVquRx1CZcYbHW3Bnk8Th6zEc9nflc7zb7OS
+jiyWqhpEZpvyjG+500Z8I4ceAKVrAgMBAAECgYBfiTRyTlzCg6z3qNioV7hgBWpI
+wHcbtz0BkKRWXh1q5vDBEJtQkGCoCF5iHmvkpUuSY/9U6gACIicFCk8QLrpVZGzY
+1SgOQS796La2gYR0clCvtsf8Kg4BYCKkF40jGDGZTGKtWa9mPuOZXZFhSMZJiCQi
+UIfTN3D9Ngt/nLFMwQJBANwavdYXnPxGr/0rL1ct0RUjsZLyd9kmr1gD4Nq+/uwC
+FQrNHl/ieXOsSeD0eVNtuTFzmt8sTz963SXmbPv01dMCQQDNgPioq4M/1sluGUdI
+yK5bantPAD7A7wTb+uryP7lNMuDSGTXgLsh+RrhBuDlUdi+OPc0dIJV1fTjEiLbI
+fLsJAkEAoSSe6Zh+IaDrBfJRBYWQtuZcApasMfqFk227eMsuvcFEgJTt8QtRGeQA
+bsbt8Ku7Uz4tG8lH99TPBglurwRwawJAALVrJhnBUB0LgMexiTy37TTGzBTyInQw
+jhmlacRyfSOpxs+zcdx3cIgJ5qAeAn6N1227IViVa1xNL9BQ4QwdEQJBAJjLcCYS
+twmgoyOj4K5+8+aAuuXWj7lpxEakOZbghrBmy0GytiRKN9wb/x8QF6EtrJAmYltL
+wMMDUDE0zd+hc2Q=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.crt
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr
new file mode 100755
index 0000000000..a3549ca15e
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.csr
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBvzCCASgCAQAwfzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQH
+EwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMRAwDgYDVQQDEwdjbGllbnQx
+MRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb20wgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALdrQAvB76fD+lQywteH+qyFGK6vRLrd
+Vw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFshmH9TMrPWsD0j/B2t
+BrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUsCTlRw9RE74FequBj
+2BHGnOBv2GYRAgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQBfvn2fP2Tj8FTZH+Xk
+9u04rYaQdspSyL61F4QIEiP5UOUzbnSSU/B72KF5gm8b0irXGnTbHlXeMv6WXaAS
+VF1fEeM+gsGJIDOvomT5PKfudezr6DpGrUammQnRY3tho078Ao0Mkq8PAWpiVWGV
+Z7rsqHhI3mWYLNyne9n8K224pg==
+-----END CERTIFICATE REQUEST-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key
new file mode 100755
index 0000000000..076ce4e72b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALdrQAvB76fD+lQy
+wteH+qyFGK6vRLrdVw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFsh
+mH9TMrPWsD0j/B2tBrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUs
+CTlRw9RE74FequBj2BHGnOBv2GYRAgMBAAECgYBhWatEkkqA4KOczNRdUO7bYkkX
+bi8sfw4WK9b6+h6JF+dqLsZ6FkrJfd9QPsRBgTWcYtwb1dogi9PCirJF4gKmSsv1
+h/BISW4lrXJPf08aJAuBF0ym8XZUgVLLptn8KdXX3xc6YF6K336AnNNLZ80X4El8
+m7X4d8Y3F4k3Dj04AQJBAN9NkHYwevaZ8jfv5rZSTKECmdk1DZITd756+sObFAs1
+vX9VfunwVw6xWmaUyMt8oPFqb0wKES9zqrViaMhU9YkCQQDSRqV3ldHcaZJ6sTgm
+T8ZKm+UpbC4zat4rGSBYKaoeLRPh5nxP892rOfBAevkoIQzW7LfVfoMImM/i1J6T
+lNJJAkAHSOZ/lJFOXjNSs9bY99JcJlOSjHKG42+ynRx1KSf8PaKS9t0PELImXo7O
+begnC0fM2GYNGv74h1N4W1+DuZHRAkEAlNov3jSvh+EwMSxs/Cnyy/QJyEXteraH
+KWkzDVYJOC1e6sZXb93JKKHuIicrY63pwed2x2o0ZgyO9RrgWsa6CQJBAMogPcRO
+jUGjYs3IYXW6Suf1bRJN2aS81gx7lqyLQE3ignH6e9kMxcrzG4AzzePCLouY3waL
+HS1KW8V9I5c4qTs=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12 b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12
new file mode 100755
index 0000000000..1511e1d0af
Binary files /dev/null and b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/client1.p12 differ
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem
new file mode 100755
index 0000000000..01eba3e5a1
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAMzJWsEtMQ76G81O8RzEweDaPrio4NihRBo0fmNuh3IjJtFJ++URPW4y
+HHtnGOkPoMwQjGQ3GQV+lfcR7QGWojET4NskXOk6mdFU+/nYB9s5YY7RR4qyln9b
+dNMp3KnMLtILNH1rJRoqYbSNoz3Z2cS59Ejp1dgXqN8kSP8wiqDjAgEC
+-----END DH PARAMETERS-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt
new file mode 100644
index 0000000000..4bb34b80fc
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt
@@ -0,0 +1,2 @@
+V 270821142642Z 01 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+V 270821142658Z 02 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr
new file mode 100755
index 0000000000..8f7e63a347
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr
@@ -0,0 +1 @@
+unique_subject = yes
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old
new file mode 100755
index 0000000000..8f7e63a347
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.attr.old
@@ -0,0 +1 @@
+unique_subject = yes
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old
new file mode 100755
index 0000000000..d74831ee49
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/index.txt.old
@@ -0,0 +1 @@
+V 270821142642Z 01 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial
new file mode 100755
index 0000000000..75016ea362
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial
@@ -0,0 +1 @@
+03
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old
new file mode 100755
index 0000000000..9e22bcb8e3
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/serial.old
@@ -0,0 +1 @@
+02
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.crt
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr
new file mode 100755
index 0000000000..c83aadabec
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.csr
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBvjCCAScCAQAwfjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQH
+EwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ8wDQYDVQQDEwZzZXJ2ZXIx
+EDAOBgNVBCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4GcuM6tPT6F4xjK60x1xczS6RUCIh3YD
++4fYTk58fZUifY5w3GgQJ/598HmThoPvqRZ4roZe6kJ0OE03Dck0poReZK3dkd3f
+Ar9T+PvAm8u9k3wmq70KxsNd2l2zwP9yoS8eC/amcXf5ADiKrqvAhhGrEt4eghPo
+w9W7by6/bsECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBAMrytQrG3MGVTD2vsZkw
+hJn9U4MYmikqFxEcSJ+JEeDJ0w3NTn95XJmFtmuT/CwsrnP9g+1neSpCXsZewozd
+QRisYBF9Rl9qw9fH2o1S/GIVpTDIiTtWjegZA6FPkhQQyY387LU9Lp4vG9hdNbuz
+QNDs6cI9T0U53PZiq7R65rGV
+-----END CERTIFICATE REQUEST-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key
new file mode 100755
index 0000000000..3aab5cd4bb
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/keys/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOBnLjOrT0+heMYy
+utMdcXM0ukVAiId2A/uH2E5OfH2VIn2OcNxoECf+ffB5k4aD76kWeK6GXupCdDhN
+Nw3JNKaEXmSt3ZHd3wK/U/j7wJvLvZN8Jqu9CsbDXdpds8D/cqEvHgv2pnF3+QA4
+iq6rwIYRqxLeHoIT6MPVu28uv27BAgMBAAECgYEAqno4g1BVxG0rT4cin1fy5E7e
+Y9YO4ropdGFTVsoRkWZG+ZPI8eisvXV9P79c8AgzfgVwUUYLvXQWzt76QCLnN11x
+pBURloJbEReEEzIF6z3LiT9CKgVWY4vtC6f5OW4Kd+EZxXMftFz8kMzRuRQLvOzR
+WeU+3GD42mIwYjsXBrECQQD2P6azeZJ1ZIEf+9Ys19tSQ6uxbXyvghlTfhmZfBW3
+bshNRH4+L4lnnbm75pFkWqR0gYSAD3toRymccctN2K9jAkEA6UoRwURTjIER8yZO
+6gLTFtzb841jv6TPErOY7CzNZ/TYBUdUBmM/NB4+RgBpBNFXB3uqweNVPO6mhyQP
+zB88iwJBAOeH6YGOqTpfiVk9PJ9lRf4PSnvE6htLQ+zQ/9jkZrbWHtcns1tc7uDR
+2DToEYQ+BarVrHOMGwhtAJ7sD6/eMOkCQFjn8XxGHdEeH0kbAFgOW4QLB4f6Clmu
+5XurFkxrhpxnoyvf0xXcHOov3GuxHFTJsvEXICxkBsgB61T1WU/hV6ECQGzk5Eqo
+uik2OAYMOb7NPGYMWS68DpmP11QDuvoj5zm2vdzohXHyBM7mO12MLSrTpxfgjWhQ
+pqwEbErchuKkzN4=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf
new file mode 100755
index 0000000000..93ac6eaba8
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/openssl-1.0.0.cnf
@@ -0,0 +1,286 @@
+# For use with easy-rsa version 2.0 and OpenSSL 1.0.0*
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha256 # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+default_md = sha256
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation after 2004).
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/vars b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/vars
new file mode 100755
index 0000000000..4873fbcf74
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/easy-rsa/vars
@@ -0,0 +1,19 @@
+export EASY_RSA="/etc/easy-rsa"
+export OPENSSL="openssl"
+export PKCS11TOOL="pkcs11-tool"
+export GREP="grep"
+export KEY_CONFIG=`/usr/sbin/whichopensslcnf $EASY_RSA`
+export KEY_DIR="$EASY_RSA/keys"
+echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
+export PKCS11_MODULE_PATH="dummy"
+export PKCS11_PIN="dummy"
+export KEY_SIZE=1024
+export CA_EXPIRE=3650
+export KEY_EXPIRE=3650
+export KEY_COUNTRY="CN"
+export KEY_PROVINCE="ZJ"
+export KEY_CITY="ZJ"
+export KEY_ORG="ZJ"
+export KEY_EMAIL="ZJ@ZJ.com"
+export KEY_OU="ZJ"
+export KEY_NAME="EasyRSA"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/root/etc/genovpn.sh b/package/lean/luci-app-openvpn-server/root/etc/genovpn.sh
new file mode 100755
index 0000000000..146d6b2369
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/genovpn.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+ddns=`uci get openvpn.myvpn.ddns`
+port=`uci get openvpn.myvpn.port`
+proto=`uci get openvpn.myvpn.proto`
+
+cat > /tmp/my.ovpn <' >> /tmp/my.ovpn
+cat /etc/openvpn/ca.crt >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+cat /etc/openvpn/client1.crt >> /tmp/my.ovpn
+echo ' ' >> /tmp/my.ovpn
+echo '' >> /tmp/my.ovpn
+cat /etc/openvpn/client1.key >> /tmp/my.ovpn
+echo ' ' >> /tmp/my.ovpn
+[ -f /etc/ovpnadd.conf ] && cat /etc/ovpnadd.conf >> /tmp/my.ovpn
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/ca.crt b/package/lean/luci-app-openvpn-server/root/etc/openvpn/ca.crt
new file mode 100755
index 0000000000..7b7ba5ef81
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgIJAK4YoeE9Uk3wMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNV
+BAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkoxCzAJBgNVBAoTAlpKMQsw
+CQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExGDAW
+BgkqhkiG9w0BCQEWCVpKQFpKLmNvbTAeFw0xNzA4MjMxNDE4MDVaFw0yNzA4MjEx
+NDE4MDVaMH0xCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMCWkox
+CzAJBgNVBAoTAlpKMQswCQYDVQQLEwJaSjEOMAwGA1UEAxMFWkogQ0ExEDAOBgNV
+BCkTB0Vhc3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAsLBNLkjfNUK4Rb3xgJD0EEgzp6b+5zkiibT3wdS9
+SKZqkqJRcM/z7Ifc79LKXDDHrVnajkyc8QSaXOKVW8pTx75fYnoHjNbeU9JZHoTg
+9GgRWq5HHUJlxhsdbcGeTxOHrMRz2d+VzvNvs5KOLJaqGkRmm/KMb7nTRnwjhx4A
+pWsCAwEAAaOB4zCB4DAdBgNVHQ4EFgQUjdUEeRAFTxsSYxGsANT8zDEAuAkwgbAG
+A1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQswCQYDVQQG
+EwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkG
+A1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJ
+KoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4GBAAAN0aRmQGNsF23CxShEnj3ohgpYA20F2FwEWYmCCWXe
+CKxuPGtPZAeLmToIMgn75QlyuvVG+Uoe7I6ylbEK3XoeNStcS61wAXL8hIPfMcUX
+fDsImBvc+Bo+LxQMWMSz0r88+B8784KELyaQKOnvPlTrnTuyP9RftjUWpjy23Kjd
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.crt b/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.crt
new file mode 100755
index 0000000000..c757880eaa
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.crt
@@ -0,0 +1,71 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:58 2017 GMT
+ Not After : Aug 21 14:26:58 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b7:6b:40:0b:c1:ef:a7:c3:fa:54:32:c2:d7:87:
+ fa:ac:85:18:ae:af:44:ba:dd:57:0f:43:73:eb:df:
+ 37:5e:5e:8f:ad:43:7a:87:dd:a6:bd:6a:0b:68:8d:
+ 75:0c:fe:49:39:e1:54:11:53:0f:b5:63:10:5b:21:
+ 98:7f:53:32:b3:d6:b0:3d:23:fc:1d:ad:06:b0:f0:
+ fb:10:27:83:e1:5e:5a:b4:f6:7c:02:87:4c:73:86:
+ 7b:79:07:ca:a7:1c:18:2c:70:e3:9d:e6:f0:89:06:
+ 4b:25:2c:09:39:51:c3:d4:44:ef:81:5e:aa:e0:63:
+ d8:11:c6:9c:e0:6f:d8:66:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ Easy-RSA Generated Certificate
+ X509v3 Subject Key Identifier:
+ E8:15:F9:71:C4:A7:C2:41:A3:AF:F5:C5:93:51:8E:5D:67:9B:12:E2
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ Signature Algorithm: sha256WithRSAEncryption
+ 5b:63:c2:e5:e4:04:ad:f4:b6:76:24:df:94:a5:b0:a2:99:38:
+ f9:e7:b4:2f:79:91:51:1b:06:4b:33:fc:4c:74:ce:47:3e:54:
+ 1b:da:ea:43:e3:6d:6e:ec:b4:cd:77:86:ea:ea:48:a1:79:70:
+ 5c:ff:99:0e:fb:bd:fc:0d:89:a6:2e:13:fe:86:82:69:33:4c:
+ 28:21:0d:a8:ba:1a:3e:c7:2e:2d:97:0c:5a:ed:e3:af:73:fc:
+ bb:c9:58:05:c5:26:56:13:1c:3e:8f:90:c7:e8:d9:e5:0b:1f:
+ 40:9a:fa:15:49:b9:d8:8e:6a:fd:71:f4:3c:df:a1:11:af:fb:
+ ea:a8
+-----BEGIN CERTIFICATE-----
+MIIDpjCCAw+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjU4WhcNMjcwODIxMTQyNjU4WjB/
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxEDAOBgNVBAMTB2NsaWVudDExEDAOBgNVBCkTB0Vh
+c3lSU0ExGDAWBgkqhkiG9w0BCQEWCVpKQFpKLmNvbTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAt2tAC8Hvp8P6VDLC14f6rIUYrq9Eut1XD0Nz6983Xl6PrUN6
+h92mvWoLaI11DP5JOeFUEVMPtWMQWyGYf1Mys9awPSP8Ha0GsPD7ECeD4V5atPZ8
+AodMc4Z7eQfKpxwYLHDjnebwiQZLJSwJOVHD1ETvgV6q4GPYEcac4G/YZhECAwEA
+AaOCATIwggEuMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6BX5ccSnwkGjr/XFk1GOXWeb
+EuIwgbAGA1UdIwSBqDCBpYAUjdUEeRAFTxsSYxGsANT8zDEAuAmhgYGkfzB9MQsw
+CQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJa
+SjELMAkGA1UECxMCWkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNB
+MRgwFgYJKoZIhvcNAQkBFglaSkBaSi5jb22CCQCuGKHhPVJN8DATBgNVHSUEDDAK
+BggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADgYEAW2PC5eQE
+rfS2diTflKWwopk4+ee0L3mRURsGSzP8THTORz5UG9rqQ+Ntbuy0zXeG6upIoXlw
+XP+ZDvu9/A2Jpi4T/oaCaTNMKCENqLoaPscuLZcMWu3jr3P8u8lYBcUmVhMcPo+Q
+x+jZ5QsfQJr6FUm52I5q/XH0PN+hEa/76qg=
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.key b/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.key
new file mode 100755
index 0000000000..076ce4e72b
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/client1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALdrQAvB76fD+lQy
+wteH+qyFGK6vRLrdVw9Dc+vfN15ej61Deofdpr1qC2iNdQz+STnhVBFTD7VjEFsh
+mH9TMrPWsD0j/B2tBrDw+xAng+FeWrT2fAKHTHOGe3kHyqccGCxw453m8IkGSyUs
+CTlRw9RE74FequBj2BHGnOBv2GYRAgMBAAECgYBhWatEkkqA4KOczNRdUO7bYkkX
+bi8sfw4WK9b6+h6JF+dqLsZ6FkrJfd9QPsRBgTWcYtwb1dogi9PCirJF4gKmSsv1
+h/BISW4lrXJPf08aJAuBF0ym8XZUgVLLptn8KdXX3xc6YF6K336AnNNLZ80X4El8
+m7X4d8Y3F4k3Dj04AQJBAN9NkHYwevaZ8jfv5rZSTKECmdk1DZITd756+sObFAs1
+vX9VfunwVw6xWmaUyMt8oPFqb0wKES9zqrViaMhU9YkCQQDSRqV3ldHcaZJ6sTgm
+T8ZKm+UpbC4zat4rGSBYKaoeLRPh5nxP892rOfBAevkoIQzW7LfVfoMImM/i1J6T
+lNJJAkAHSOZ/lJFOXjNSs9bY99JcJlOSjHKG42+ynRx1KSf8PaKS9t0PELImXo7O
+begnC0fM2GYNGv74h1N4W1+DuZHRAkEAlNov3jSvh+EwMSxs/Cnyy/QJyEXteraH
+KWkzDVYJOC1e6sZXb93JKKHuIicrY63pwed2x2o0ZgyO9RrgWsa6CQJBAMogPcRO
+jUGjYs3IYXW6Suf1bRJN2aS81gx7lqyLQE3ignH6e9kMxcrzG4AzzePCLouY3waL
+HS1KW8V9I5c4qTs=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem b/package/lean/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem
new file mode 100755
index 0000000000..01eba3e5a1
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAMzJWsEtMQ76G81O8RzEweDaPrio4NihRBo0fmNuh3IjJtFJ++URPW4y
+HHtnGOkPoMwQjGQ3GQV+lfcR7QGWojET4NskXOk6mdFU+/nYB9s5YY7RR4qyln9b
+dNMp3KnMLtILNH1rJRoqYbSNoz3Z2cS59Ejp1dgXqN8kSP8wiqDjAgEC
+-----END DH PARAMETERS-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.crt b/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.crt
new file mode 100755
index 0000000000..a73a757e94
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.crt
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Validity
+ Not Before: Aug 23 14:26:42 2017 GMT
+ Not After : Aug 21 14:26:42 2027 GMT
+ Subject: C=CN, ST=ZJ, L=ZJ, O=ZJ, OU=ZJ, CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:e0:67:2e:33:ab:4f:4f:a1:78:c6:32:ba:d3:1d:
+ 71:73:34:ba:45:40:88:87:76:03:fb:87:d8:4e:4e:
+ 7c:7d:95:22:7d:8e:70:dc:68:10:27:fe:7d:f0:79:
+ 93:86:83:ef:a9:16:78:ae:86:5e:ea:42:74:38:4d:
+ 37:0d:c9:34:a6:84:5e:64:ad:dd:91:dd:df:02:bf:
+ 53:f8:fb:c0:9b:cb:bd:93:7c:26:ab:bd:0a:c6:c3:
+ 5d:da:5d:b3:c0:ff:72:a1:2f:1e:0b:f6:a6:71:77:
+ f9:00:38:8a:ae:ab:c0:86:11:ab:12:de:1e:82:13:
+ e8:c3:d5:bb:6f:2e:bf:6e:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ Easy-RSA Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 1C:06:47:33:61:0E:15:84:D5:08:5A:40:E6:C9:23:EF:87:F3:EE:F9
+ X509v3 Authority Key Identifier:
+ keyid:8D:D5:04:79:10:05:4F:1B:12:63:11:AC:00:D4:FC:CC:31:00:B8:09
+ DirName:/C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=ZJ CA/name=EasyRSA/emailAddress=ZJ@ZJ.com
+ serial:AE:18:A1:E1:3D:52:4D:F0
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:b5:0d:7a:b3:09:7a:d4:e6:df:46:0d:54:39:e2:34:da:4f:
+ 9b:92:d6:41:db:10:b9:ed:66:34:7f:9d:a9:fa:af:6a:80:b4:
+ 1f:13:d3:39:a6:72:93:5a:6b:e8:4f:ba:95:f9:83:10:58:b3:
+ fe:12:3f:a9:e8:31:04:e0:8e:d8:4c:c1:f7:7e:fc:7a:ba:17:
+ d8:2e:76:ab:7c:17:ca:a4:1d:3d:c5:bc:df:02:e5:2e:91:c4:
+ b2:6c:40:a1:e2:3d:64:15:da:a6:b0:e4:1d:66:11:54:c0:49:
+ 05:91:c4:69:68:2d:bc:d5:f6:32:43:3c:18:c4:97:54:45:52:
+ 28:36
+-----BEGIN CERTIFICATE-----
+MIIDvzCCAyigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDTjEL
+MAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQKEwJaSjELMAkGA1UECxMC
+WkoxDjAMBgNVBAMTBVpKIENBMRAwDgYDVQQpEwdFYXN5UlNBMRgwFgYJKoZIhvcN
+AQkBFglaSkBaSi5jb20wHhcNMTcwODIzMTQyNjQyWhcNMjcwODIxMTQyNjQyWjB+
+MQswCQYDVQQGEwJDTjELMAkGA1UECBMCWkoxCzAJBgNVBAcTAlpKMQswCQYDVQQK
+EwJaSjELMAkGA1UECxMCWkoxDzANBgNVBAMTBnNlcnZlcjEQMA4GA1UEKRMHRWFz
+eVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkouY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDgZy4zq09PoXjGMrrTHXFzNLpFQIiHdgP7h9hOTnx9lSJ9jnDc
+aBAn/n3weZOGg++pFniuhl7qQnQ4TTcNyTSmhF5krd2R3d8Cv1P4+8Cby72TfCar
+vQrGw13aXbPA/3KhLx4L9qZxd/kAOIquq8CGEasS3h6CE+jD1btvLr9uwQIDAQAB
+o4IBTDCCAUgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4
+QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFBwGRzNhDhWE1QhaQObJI++H8+75MIGwBgNVHSMEgagwgaWAFI3VBHkQ
+BU8bEmMRrADU/MwxALgJoYGBpH8wfTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpK
+MQswCQYDVQQHEwJaSjELMAkGA1UEChMCWkoxCzAJBgNVBAsTAlpKMQ4wDAYDVQQD
+EwVaSiBDQTEQMA4GA1UEKRMHRWFzeVJTQTEYMBYGCSqGSIb3DQEJARYJWkpAWkou
+Y29tggkArhih4T1STfAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWg
+MA0GCSqGSIb3DQEBCwUAA4GBAKq1DXqzCXrU5t9GDVQ54jTaT5uS1kHbELntZjR/
+nan6r2qAtB8T0zmmcpNaa+hPupX5gxBYs/4SP6noMQTgjthMwfd+/Hq6F9gudqt8
+F8qkHT3FvN8C5S6RxLJsQKHiPWQV2qaw5B1mEVTASQWRxGloLbzV9jJDPBjEl1RF
+Uig2
+-----END CERTIFICATE-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.key b/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.key
new file mode 100755
index 0000000000..3aab5cd4bb
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpn/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOBnLjOrT0+heMYy
+utMdcXM0ukVAiId2A/uH2E5OfH2VIn2OcNxoECf+ffB5k4aD76kWeK6GXupCdDhN
+Nw3JNKaEXmSt3ZHd3wK/U/j7wJvLvZN8Jqu9CsbDXdpds8D/cqEvHgv2pnF3+QA4
+iq6rwIYRqxLeHoIT6MPVu28uv27BAgMBAAECgYEAqno4g1BVxG0rT4cin1fy5E7e
+Y9YO4ropdGFTVsoRkWZG+ZPI8eisvXV9P79c8AgzfgVwUUYLvXQWzt76QCLnN11x
+pBURloJbEReEEzIF6z3LiT9CKgVWY4vtC6f5OW4Kd+EZxXMftFz8kMzRuRQLvOzR
+WeU+3GD42mIwYjsXBrECQQD2P6azeZJ1ZIEf+9Ys19tSQ6uxbXyvghlTfhmZfBW3
+bshNRH4+L4lnnbm75pFkWqR0gYSAD3toRymccctN2K9jAkEA6UoRwURTjIER8yZO
+6gLTFtzb841jv6TPErOY7CzNZ/TYBUdUBmM/NB4+RgBpBNFXB3uqweNVPO6mhyQP
+zB88iwJBAOeH6YGOqTpfiVk9PJ9lRf4PSnvE6htLQ+zQ/9jkZrbWHtcns1tc7uDR
+2DToEYQ+BarVrHOMGwhtAJ7sD6/eMOkCQFjn8XxGHdEeH0kbAFgOW4QLB4f6Clmu
+5XurFkxrhpxnoyvf0xXcHOov3GuxHFTJsvEXICxkBsgB61T1WU/hV6ECQGzk5Eqo
+uik2OAYMOb7NPGYMWS68DpmP11QDuvoj5zm2vdzohXHyBM7mO12MLSrTpxfgjWhQ
+pqwEbErchuKkzN4=
+-----END PRIVATE KEY-----
diff --git a/package/lean/luci-app-openvpn-server/root/etc/openvpncert.sh b/package/lean/luci-app-openvpn-server/root/etc/openvpncert.sh
new file mode 100755
index 0000000000..61d6f0cb9c
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/openvpncert.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+clean-all
+echo -en "\n\n\n\n\n\n\n\n" | build-ca
+build-dh
+build-key-server server
+build-key-pkcs12 client1
+cp /etc/easy-rsa/keys/ca.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/server.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/server.key /etc/openvpn/
+cp /etc/easy-rsa/keys/dh1024.pem /etc/openvpn/
+cp /etc/easy-rsa/keys/client1.crt /etc/openvpn/
+cp /etc/easy-rsa/keys/client1.key /etc/openvpn/
+/etc/init.d/openvpn restart
+echo "OpenVPN Cert renew successfully"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/root/etc/ovpnadd.conf b/package/lean/luci-app-openvpn-server/root/etc/ovpnadd.conf
new file mode 100644
index 0000000000..0d03f345b3
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/ovpnadd.conf
@@ -0,0 +1,9 @@
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "GET http://rd.go.10086.cn"
+http-proxy-option EXT1 "X-Online-Host: rd.go.10086.cn"
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "X-Online-Host: rd.go.10086.cn"
+http-proxy-option EXT1 "POST http://rd.go.10086.cn"
+http-proxy-option EXT1 "Host: rd.go.10086.cn"
+http-proxy-option EXT1 "GET http://rd.go.10086.cn"
+http-proxy-option EXT1 "Host: rd.go.10086.cn"
\ No newline at end of file
diff --git a/package/lean/luci-app-openvpn-server/root/etc/uci-defaults/openvpn b/package/lean/luci-app-openvpn-server/root/etc/uci-defaults/openvpn
new file mode 100755
index 0000000000..412fe1c8ab
--- /dev/null
+++ b/package/lean/luci-app-openvpn-server/root/etc/uci-defaults/openvpn
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+uci set network.vpn0="interface"
+uci set network.vpn0.ifname="tun0"
+uci set network.vpn0.proto="none"
+uci commit network;
+/etc/init.d/network reload
+
+uci delete firewall.openvpn
+uci add firewall rule
+uci rename firewall.@rule[-1]="openvpn"
+uci set firewall.@rule[-1].name="openvpn"
+uci set firewall.@rule[-1].target="ACCEPT"
+uci set firewall.@rule[-1].src="wan"
+uci set firewall.@rule[-1].proto="tcp udp"
+uci set firewall.@rule[-1].dest_port="1194"
+
+uci delete firewall.vpn
+uci delete firewall.vpnwan
+uci delete firewall.vpnlan
+uci commit firewall
+
+uci add firewall zone
+uci rename firewall.@zone[-1]="vpn"
+uci set firewall.@zone[-1].name="vpn"
+uci set firewall.@zone[-1].input="ACCEPT"
+uci set firewall.@zone[-1].forward="ACCEPT"
+uci set firewall.@zone[-1].output="ACCEPT"
+uci set firewall.@zone[-1].masq="1"
+uci set firewall.@zone[-1].network="vpn0"
+uci add firewall forwarding
+uci rename firewall.@forwarding[-1]="vpnwan"
+uci set firewall.@forwarding[-1].src="vpn"
+uci set firewall.@forwarding[-1].dest="wan"
+uci add firewall forwarding
+uci rename firewall.@forwarding[-1]="vpnlan"
+uci set firewall.@forwarding[-1].src="vpn"
+uci set firewall.@forwarding[-1].dest="lan"
+uci commit firewall;
+/etc/init.d/firewall restart
+
+/etc/init.d/openvpn enable
+/etc/init.d/openvpn stop
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-pppoe-relay/Makefile b/package/lean/luci-app-pppoe-relay/Makefile
new file mode 100644
index 0000000000..d65d987eba
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for PPPoE Relay
+LUCI_DEPENDS:=+rp-pppoe-common +rp-pppoe-relay
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-pppoe-relay/luasrc/controller/pppoe-relay.lua b/package/lean/luci-app-pppoe-relay/luasrc/controller/pppoe-relay.lua
new file mode 100644
index 0000000000..f3a2760e72
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/luasrc/controller/pppoe-relay.lua
@@ -0,0 +1,10 @@
+module("luci.controller.pppoe-relay",package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/pppoe-relay")then
+ return
+end
+
+entry({"admin","services","pppoe-relay"},cbi("pppoe-relay"),_("PPPoE Relay"),90).dependent=true
+
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-pppoe-relay/luasrc/model/cbi/pppoe-relay.lua b/package/lean/luci-app-pppoe-relay/luasrc/model/cbi/pppoe-relay.lua
new file mode 100644
index 0000000000..c43127ec9c
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/luasrc/model/cbi/pppoe-relay.lua
@@ -0,0 +1,56 @@
+local s=require"luci.sys"
+local net = require "luci.model.network".init()
+local ifaces = s.net:devices()
+local m,s,o
+
+m=Map("pppoe-relay",translate("PPPoE Relay"))
+m.description = translate("Opening the PPPoE relay allows devices in the Intranet to create a separate PPPoE connection that can cross NAT.")
+s=m:section(TypedSection,"service")
+s.addremove=true
+s.anonymous=true
+s.template="cbi/tblsection"
+
+o=s:option(Flag,"enabled",translate("Enabled"))
+o.rmempty=false
+
+o=s:option(ListValue,"server_interface",translate("Server Interface"))
+for _, iface in ipairs(ifaces) do
+ if not (iface == "lo" or iface:match("^ifb.*") or iface:match("gre*")) then
+ local nets = net:get_interface(iface)
+ nets = nets and nets:get_networks() or {}
+ for k, v in pairs(nets) do
+ nets[k] = nets[k].sid
+ end
+ nets = table.concat(nets, ",")
+ o:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
+ end
+end
+o.rmempty=true
+
+o=s:option(ListValue,"client_interface",translate("Client Interface"))
+for _, iface in ipairs(ifaces) do
+ if not (iface == "lo" or iface:match("^ifb.*") or iface:match("gre*")) then
+ local nets = net:get_interface(iface)
+ nets = nets and nets:get_networks() or {}
+ for k, v in pairs(nets) do
+ nets[k] = nets[k].sid
+ end
+ nets = table.concat(nets, ",")
+ o:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
+ end
+end
+o.rmempty=true
+
+o=s:option(Button,"is_run",translate("Check"))
+function o.write(self, section)
+ local server_interface = m.uci:get('pppoe-relay',section,"server_interface")
+ local client_interface = m.uci:get('pppoe-relay',section,"client_interface")
+ isrun=luci.sys.call("ps | grep '/usr/sbin/pppoe-relay -S "..server_interface.." -C "..client_interface.."' | grep -v 'grep' >/dev/null")
+ if isrun == 1 then
+ o.description = ""
+ else
+ o.description = ""
+ end
+end
+
+return m
diff --git a/package/lean/luci-app-pppoe-relay/po/zh-cn/pppoe-relay.po b/package/lean/luci-app-pppoe-relay/po/zh-cn/pppoe-relay.po
new file mode 100644
index 0000000000..e1a0c10340
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/po/zh-cn/pppoe-relay.po
@@ -0,0 +1,23 @@
+msgid "PPPoE Relay"
+msgstr "PPPoE 穿透"
+
+msgid "Opening the PPPoE relay allows devices in the Intranet to create a separate PPPoE connection that can cross NAT."
+msgstr "开启 PPPoE 中继允许内网中的设备创建一个可穿过 NAT 的独立 PPPoE 连接"
+
+msgid "Enable or disable the PPPoE Relay"
+msgstr "启用或禁用PPPoE穿透"
+
+msgid "Server Interface"
+msgstr "服务端接口"
+
+msgid "Specify the PPPoE server interface"
+msgstr "指定PPPoE服务器接口"
+
+msgid "Client Interface"
+msgstr "客户端接口"
+
+msgid "Specify the PPPoE client interface"
+msgstr "指定PPPoE客户端接口"
+
+msgid "Enabled"
+msgstr "启用"
\ No newline at end of file
diff --git a/package/lean/luci-app-pppoe-relay/root/etc/config/pppoe-relay b/package/lean/luci-app-pppoe-relay/root/etc/config/pppoe-relay
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/root/etc/config/pppoe-relay
@@ -0,0 +1 @@
+
diff --git a/package/lean/luci-app-pppoe-relay/root/etc/init.d/pppoe-relay b/package/lean/luci-app-pppoe-relay/root/etc/init.d/pppoe-relay
new file mode 100755
index 0000000000..ca069d155b
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/root/etc/init.d/pppoe-relay
@@ -0,0 +1,29 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2018 Lienol
+
+START=99
+STOP=16
+
+setup_service() {
+ local section="$1"
+ config_get enabled "$section" enabled
+ [ "$enabled" -eq 0 ] && return 0
+ config_get server_interface "$section" server_interface
+ config_get client_interface "$section" client_interface
+ [ -n "$server_interface" ] || return 0
+ [ -n "$client_interface" ] || return 0
+
+ is_run=$(ps | grep "/usr/sbin/pppoe-relay -S $server_interface -C $client_interface" | grep -v "grep")
+ [ -z "$is_run" ] && {
+ /usr/sbin/pppoe-relay -S $server_interface -C $client_interface
+ }
+}
+
+start() {
+ config_load pppoe-relay
+ config_foreach setup_service service
+}
+
+stop() {
+ killall -9 pppoe-relay
+}
diff --git a/package/lean/luci-app-pppoe-relay/root/etc/uci-defaults/luci-app-pppoe-relay b/package/lean/luci-app-pppoe-relay/root/etc/uci-defaults/luci-app-pppoe-relay
new file mode 100755
index 0000000000..287888b284
--- /dev/null
+++ b/package/lean/luci-app-pppoe-relay/root/etc/uci-defaults/luci-app-pppoe-relay
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@pppoe-relay[-1]
+ add ucitrack pppoe-relay
+ set ucitrack.@pppoe-relay[-1].init=pppoe-relay
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-pptp-server/Makefile b/package/lean/luci-app-pptp-server/Makefile
new file mode 100644
index 0000000000..2e13beb866
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/Makefile
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for PPTP VPN Server
+LUCI_DEPENDS:=+pptpd +kmod-mppe +ppp
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-pptp-server
+PKG_VERSION:=1.0
+PKG_RELEASE:=13
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
+
+
diff --git a/package/lean/luci-app-pptp-server/luasrc/controller/pptp-server.lua b/package/lean/luci-app-pptp-server/luasrc/controller/pptp-server.lua
new file mode 100644
index 0000000000..5bda830987
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/luasrc/controller/pptp-server.lua
@@ -0,0 +1,19 @@
+
+module("luci.controller.pptp-server", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/pptpd") then
+ return
+ end
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin", "vpn", "pptp-server"}, cbi("pptp-server/pptp-server"), _("PPTP VPN Server"), 80).dependent=false
+ entry({"admin", "vpn", "pptp-server","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep pptpd >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-pptp-server/luasrc/model/cbi/pptp-server/pptp-server.lua b/package/lean/luci-app-pptp-server/luasrc/model/cbi/pptp-server/pptp-server.lua
new file mode 100644
index 0000000000..7fa03cfdb9
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/luasrc/model/cbi/pptp-server/pptp-server.lua
@@ -0,0 +1,37 @@
+
+mp = Map("pptpd", translate("PPTP VPN Server"))
+mp.description = translate("PPTP VPN Server connectivity using the native built-in VPN Client on Windows/Linux or Andriod")
+
+mp:section(SimpleSection).template = "pptp/pptp_status"
+
+s = mp:section(NamedSection, "pptpd", "service")
+s.anonymouse = true
+
+enabled = s:option(Flag, "enabled", translate("Enable"))
+enabled.default = 0
+enabled.rmempty = false
+
+localip = s:option(Value, "localip", translate("Local IP"))
+localip.datatype = "ip4addr"
+
+clientip = s:option(Value, "remoteip", translate("Client IP"))
+clientip.datatype = "string"
+clientip.description = translate("LAN DHCP reserved start-to-end IP addresses with the same subnet mask")
+
+remotedns = s:option(Value, "remotedns", translate("Remote Client DNS"))
+remotedns.datatype = "ip4addr"
+
+logging = s:option(Flag, "logwtmp", translate("Debug Logging"))
+logging.default = 0
+logging.rmempty = false
+
+logins = mp:section(NamedSection, "login", "login", translate("PPTP Logins"))
+logins.anonymouse = true
+
+username = logins:option(Value, "username", translate("User name"))
+username.datatype = "string"
+
+password = logins:option(Value, "password", translate("Password"))
+password.password = true
+
+return mp
diff --git a/package/lean/luci-app-pptp-server/luasrc/view/pptp/pptp_status.htm b/package/lean/luci-app-pptp-server/luasrc/view/pptp/pptp_status.htm
new file mode 100644
index 0000000000..11a2852d9e
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/luasrc/view/pptp/pptp_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-pptp-server/po/zh-cn/pptp.po b/package/lean/luci-app-pptp-server/po/zh-cn/pptp.po
new file mode 100644
index 0000000000..0c2f78df3a
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/po/zh-cn/pptp.po
@@ -0,0 +1,56 @@
+msgid "Disable from startup"
+msgstr "禁止开机启动"
+
+msgid "Enable on startup"
+msgstr "允许开机启动"
+
+msgid "PPTP VPN Server"
+msgstr "PPTP VPN 服务器"
+
+msgid "PPTP Service"
+msgstr "PPTP 服务设置"
+
+msgid "Local IP"
+msgstr "PPTP 服务器IP"
+
+msgid "Client IP"
+msgstr "客户端分配的IP范围"
+
+msgid "LAN DHCP reserved start-to-end IP addresses with the same subnet mask"
+msgstr "使用和 LAN 同一网段。请使用 DHCP分配地址以外的空余IP地址范围,例如 192.168.0.20-30"
+
+msgid "Remote Client DNS"
+msgstr "客户端分配IP的DNS服务器"
+
+msgid "Debug Logging"
+msgstr "记录日志"
+
+msgid "PPTP Logins"
+msgstr "PPTP 登录设置"
+
+msgid "User name"
+msgstr "用户名"
+
+msgid "Password"
+msgstr "密码"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "PPTPD status"
+msgstr "PPTPD 服务状态"
+
+msgid "Start"
+msgstr "启动"
+
+msgid "Stop"
+msgstr "关闭"
+
+msgid "PPTP VPN Server"
+msgstr "PPTP VPN 服务器"
+
+msgid "PPTP VPN Server connectivity using the native built-in VPN Client on Windows/Linux or Andriod"
+msgstr "使用Windows/Linux 或者 Andriod 内置的 PPTP VPN 客户端进行连接 "
+
+
+
diff --git a/package/lean/luci-app-pptp-server/root/etc/config/pptpd b/package/lean/luci-app-pptp-server/root/etc/config/pptpd
new file mode 100644
index 0000000000..c55cfa60cc
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/root/etc/config/pptpd
@@ -0,0 +1,12 @@
+
+config service 'pptpd'
+ option enabled '0'
+ option remoteip '192.168.0.20-30'
+ option remotedns '192.168.0.1'
+ option logwtmp '0'
+ option localip '192.168.0.1'
+
+config login 'login'
+ option username 'lean'
+ option password 'I234567B'
+
diff --git a/package/lean/luci-app-pptp-server/root/etc/pptpd.include b/package/lean/luci-app-pptp-server/root/etc/pptpd.include
new file mode 100755
index 0000000000..e59a7b7eac
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/root/etc/pptpd.include
@@ -0,0 +1,6 @@
+iptables -D forwarding_rule -i ppp+ -j ACCEPT 2>/dev/null
+iptables -D forwarding_rule -o ppp+ -j ACCEPT 2>/dev/null
+
+iptables -A forwarding_rule -i ppp+ -j ACCEPT
+iptables -A forwarding_rule -o ppp+ -j ACCEPT
+echo 1 > /proc/sys/net/ipv4/conf/br-lan/proxy_arp
diff --git a/package/lean/luci-app-pptp-server/root/etc/uci-defaults/pptpd b/package/lean/luci-app-pptp-server/root/etc/uci-defaults/pptpd
new file mode 100755
index 0000000000..68c9f3e669
--- /dev/null
+++ b/package/lean/luci-app-pptp-server/root/etc/uci-defaults/pptpd
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.pptpd
+ set firewall.pptpd=include
+ set firewall.pptpd.type=script
+ set firewall.pptpd.path=/etc/pptpd.include
+ set firewall.pptpd.reload=1
+ delete firewall.pptp
+ add firewall rule
+ rename firewall.@rule[-1]="pptp"
+ set firewall.@rule[-1].name="pptp"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="tcp"
+ set firewall.@rule[-1].dest_port="1723"
+ delete firewall.gre
+ add firewall rule
+ rename firewall.@rule[-1]="gre"
+ set firewall.@rule[-1].name="gre"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="47"
+ commit firewall
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@pptpd[-1]
+ add ucitrack pptpd
+ set ucitrack.@pptpd[-1].init=pptpd
+ commit ucitrack
+EOF
+
+/etc/init.d/pptpd enable && /etc/init.d/pptpd restart
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-qbittorrent/Makefile b/package/lean/luci-app-qbittorrent/Makefile
new file mode 100644
index 0000000000..70f4703b62
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2019 Openwrt.org
+#
+# This is a free software, use it under Apache Licene 2.0 & GNU General Public License v3.0.
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=qbittorrent-nox for LuCI
+LUCI_DEPENDS:=+qBittorrent
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-qbittorrent
+PKG_VERSION=1.0
+PKG_RELEASE:=4
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-qbittorrent/luasrc/controller/qbittorrent.lua b/package/lean/luci-app-qbittorrent/luasrc/controller/qbittorrent.lua
new file mode 100644
index 0000000000..5363ac9786
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/luasrc/controller/qbittorrent.lua
@@ -0,0 +1,4 @@
+module("luci.controller.qbittorrent",package.seeall)
+function index()
+ entry({"admin","nas","qbittorrent"},cbi("qbittorrent"),_("qbittorrent"))
+end
diff --git a/package/lean/luci-app-qbittorrent/luasrc/model/cbi/qbittorrent.lua b/package/lean/luci-app-qbittorrent/luasrc/model/cbi/qbittorrent.lua
new file mode 100644
index 0000000000..af37d8fb3b
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/luasrc/model/cbi/qbittorrent.lua
@@ -0,0 +1,44 @@
+require("luci.sys")
+local e=require"luci.model.uci".cursor()
+local o=e:get_first("qbittorrent","Preferences","port")or 8080
+local a=(luci.sys.call("pidof qbittorrent-nox > /dev/null")==0)
+local t=""
+local e=""
+if a then
+t=" "
+end
+if a then
+e=""..translate("Running")..""
+else
+e=""..translate("Not running")..""
+end
+
+m = Map("qbittorrent", translate("qbittorrent"), translate("A BT/PT downloader base on Qt")..t
+.."
"..translate("qbittorrent Run Status").." : "..e.."
")
+
+s_basic = m:section(TypedSection, "basic", translate("Basic Settings"))
+s_basic.anonymous = true
+enable = s_basic:option(Flag, "enable", translate("Enable"))
+profile_dir = s_basic:option(Value,"profile_dir",translate("profile_dir"),translate("Store configuration files in the Path"))
+profile_dir.default = "/tmp"
+program_dir = s_basic:option(Value,"program_dir",translate("program_dir"),translate("Store Program files in the Path"))
+program_dir.default = "/usr/bin"
+library_dir = s_basic:option(Value,"library_dir",translate("library_dir"),translate("Store Library in the Path"))
+library_dir.default = "/usr/lib"
+
+s_download = m:section(TypedSection, "Preferences", translate("Download Settings"))
+s_download.anonymous = true
+download_dir = s_download:option(Value,"download_dir",translate("download_dir"),translate("Store download files in the Path"))
+download_dir.default = "/tmp/download"
+
+s_webui = m:section(TypedSection, "Preferences", translate("WEBUI Settings"))
+s_webui.anonymous = true
+port = s_webui:option(Value,"port",translate("port"),translate("WEBUI listening port"))
+port.default = "8080"
+
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ io.popen("/etc/init.d/qbittorrent restart")
+end
+
+return m
diff --git a/package/lean/luci-app-qbittorrent/po/zh-cn/qbittorrent.po b/package/lean/luci-app-qbittorrent/po/zh-cn/qbittorrent.po
new file mode 100644
index 0000000000..d7a834ede1
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/po/zh-cn/qbittorrent.po
@@ -0,0 +1,53 @@
+msgid "qbittorrent"
+msgstr "BT下载器-qb"
+
+msgid "Running"
+msgstr "运行中"
+
+msgid "Not running"
+msgstr "未运行"
+
+msgid "qbittorrent Run Status"
+msgstr "qbittorrent运行状态"
+
+msgid "Basic Settings"
+msgstr "基本设置"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "A BT/PT downloader base on Qt"
+msgstr "一个基于QT的BT/PT下载器"
+
+msgid "profile_dir"
+msgstr "配置文件目录"
+
+msgid "Store configuration files in the Path"
+msgstr "配置文件存放路径"
+
+msgid "program_dir"
+msgstr "程序目录"
+
+msgid "library_dir"
+msgstr "库目录"
+
+msgid "Store Program files in the Path"
+msgstr "程序文件存放路径"
+
+msgid "Store Library in the Path"
+msgstr "库文件存放路径"
+
+msgid "Download Settings"
+msgstr "下载设置"
+
+msgid "download_dir"
+msgstr "下载目录"
+
+msgid "Store download files in the Path"
+msgstr "下载文件存放目录"
+
+msgid "WEBUI Settings"
+msgstr "WEBUI设置"
+
+msgid "WEBUI listening port"
+msgstr "WEBUI监听端口"
diff --git a/package/lean/luci-app-qbittorrent/root/etc/config/qbittorrent b/package/lean/luci-app-qbittorrent/root/etc/config/qbittorrent
new file mode 100644
index 0000000000..b4fe940c3c
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/root/etc/config/qbittorrent
@@ -0,0 +1,3 @@
+config basic
+
+config Preferences
diff --git a/package/lean/luci-app-qbittorrent/root/etc/init.d/qbittorrent b/package/lean/luci-app-qbittorrent/root/etc/init.d/qbittorrent
new file mode 100755
index 0000000000..383317165d
--- /dev/null
+++ b/package/lean/luci-app-qbittorrent/root/etc/init.d/qbittorrent
@@ -0,0 +1,67 @@
+#!/bin/sh /etc/rc.common
+START=99
+
+config_qbittorrent()
+{
+ local download_dir
+ local port
+ config_get download_dir $1 download_dir
+ config_get port $1 port
+ if [ -f $2/qBittorrent/config/qBittorrent.conf ]; then
+ sed -i 's/WebUI\\Port=[0-9]*/WebUI\\Port='"$port"'/g' $2/qBittorrent/config/qBittorrent.conf
+ sed -i 's/Downloads\\SavePath=[a-z,/]*//g' $2/qBittorrent/config/qBittorrent.conf
+ echo "Downloads\SavePath=$down" >> $2/qBittorrent/config/qBittorrent.conf
+ else
+ cat>$2/qBittorrent/config/qBittorrent.conf< /proc/sys/vm/drop_caches")
+ luci.http.redirect(luci.dispatcher.build_url("admin/status"))
+end
diff --git a/package/lean/luci-app-ramfree/po/zh-cn/release_ram.po b/package/lean/luci-app-ramfree/po/zh-cn/release_ram.po
new file mode 100644
index 0000000000..cff6f0289d
--- /dev/null
+++ b/package/lean/luci-app-ramfree/po/zh-cn/release_ram.po
@@ -0,0 +1,2 @@
+msgid "Release Ram"
+msgstr "释放内存"
diff --git a/package/lean/luci-app-samba4/Makefile b/package/lean/luci-app-samba4/Makefile
new file mode 100644
index 0000000000..dae7f8071b
--- /dev/null
+++ b/package/lean/luci-app-samba4/Makefile
@@ -0,0 +1,10 @@
+# This is free software, licensed under the Apache License, Version 2.0 .
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Network Shares - Samba 4 SMB/CIFS module
+LUCI_DEPENDS:=+samba4-server
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-samba4/luasrc/controller/samba4.lua b/package/lean/luci-app-samba4/luasrc/controller/samba4.lua
new file mode 100644
index 0000000000..8ad0e52e36
--- /dev/null
+++ b/package/lean/luci-app-samba4/luasrc/controller/samba4.lua
@@ -0,0 +1,14 @@
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.samba4", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/samba4") then
+ return
+ end
+
+ local page
+
+ page = entry({"admin", "services", "samba4"}, cbi("samba4"), _("Network Shares"))
+ page.dependent = true
+end
diff --git a/package/lean/luci-app-samba4/luasrc/model/cbi/samba4.lua b/package/lean/luci-app-samba4/luasrc/model/cbi/samba4.lua
new file mode 100644
index 0000000000..2a1399b96b
--- /dev/null
+++ b/package/lean/luci-app-samba4/luasrc/model/cbi/samba4.lua
@@ -0,0 +1,112 @@
+-- Licensed to the public under the Apache License 2.0.
+
+m = Map("samba4", translate("Network Shares"))
+
+s = m:section(TypedSection, "samba", "Samba")
+s.anonymous = true
+
+s:tab("general", translate("General Settings"))
+s:tab("template", translate("Edit Template"))
+
+s:taboption("general", Value, "name", translate("Hostname"))
+s:taboption("general", Value, "description", translate("Description"))
+s:taboption("general", Value, "workgroup", translate("Workgroup"))
+h = s:taboption("general", Flag, "homes", translate("Share home-directories"),
+ translate("Allow system users to reach their home directories via " ..
+ "network shares"))
+h.rmempty = false
+
+macos = s:taboption("general", Flag, "macos", translate("Enable macOS compatible shares"),
+ translate("Enables Apple's AAPL extension globally and adds macOS compatibility options to all shares."))
+macos.rmempty = false
+
+if nixio.fs.access("/usr/sbin/nmbd") then
+ s:taboption("general", Flag, "disable_netbios", translate("Disable Netbios"))
+end
+if nixio.fs.access("/usr/sbin/samba") then
+ s:taboption("general", Flag, "disable_ad_dc", translate("Disable Active Directory Domain Controller"))
+end
+if nixio.fs.access("/usr/sbin/winbindd") then
+ s:taboption("general", Flag, "disable_winbind", translate("Disable Winbind"))
+end
+
+tmpl = s:taboption("template", Value, "_tmpl",
+ translate("Edit the template that is used for generating the samba configuration."),
+ translate("This is the content of the file '/etc/samba/smb.conf.template' from which your samba configuration will be generated. " ..
+ "Values enclosed by pipe symbols ('|') should not be changed. They get their values from the 'General Settings' tab."))
+
+tmpl.template = "cbi/tvalue"
+tmpl.rows = 20
+
+function tmpl.cfgvalue(self, section)
+ return nixio.fs.readfile("/etc/samba/smb.conf.template")
+end
+
+function tmpl.write(self, section, value)
+ value = value:gsub("\r\n?", "\n")
+ nixio.fs.writefile("/etc/samba/smb.conf.template", value)
+end
+
+
+s = m:section(TypedSection, "sambashare", translate("Shared Directories")
+ , translate("Please add directories to share. Each directory refers to a folder on a mounted device."))
+s.anonymous = true
+s.addremove = true
+s.template = "cbi/tblsection"
+
+s:option(Value, "name", translate("Name"))
+pth = s:option(Value, "path", translate("Path"))
+if nixio.fs.access("/etc/config/fstab") then
+ pth.titleref = luci.dispatcher.build_url("admin", "system", "fstab")
+end
+
+br = s:option(Flag, "browseable", translate("Browse-able"))
+br.enabled = "yes"
+br.disabled = "no"
+br.default = "yes"
+
+ro = s:option(Flag, "read_only", translate("Read-only"))
+ro.enabled = "yes"
+ro.disabled = "no"
+ro.default = "yes"
+
+s:option(Flag, "force_root", translate("Force Root"))
+
+au = s:option(Value, "users", translate("Allowed users"))
+au.rmempty = true
+
+go = s:option(Flag, "guest_ok", translate("Allow guests"))
+go.enabled = "yes"
+go.disabled = "no"
+go.default = "no"
+
+gon = s:option(Flag, "guest_only", translate("Guests only"))
+gon.enabled = "yes"
+gon.disabled = "no"
+gon.default = "no"
+
+iown = s:option(Flag, "inherit_owner", translate("Inherit owner"))
+iown.enabled = "yes"
+iown.disabled = "no"
+iown.default = "no"
+
+cm = s:option(Value, "create_mask", translate("Create mask"))
+cm.rmempty = true
+cm.maxlength = 4
+cm.placeholder = "0666"
+
+dm = s:option(Value, "dir_mask", translate("Directory mask"))
+dm.rmempty = true
+dm.maxlength = 4
+dm.placeholder = "0777"
+
+vfs = s:option(Value, "vfs_objects", translate("Vfs objects"))
+vfs.rmempty = true
+
+s:option(Flag, "timemachine", translate("Apple Time-machine share"))
+
+tms = s:option(Value, "timemachine_maxsize", translate("Time-machine size in GB"))
+tms.rmempty = true
+tms.maxlength = 5
+
+return m
diff --git a/package/lean/luci-app-samba4/po/ca/samba4.po b/package/lean/luci-app-samba4/po/ca/samba4.po
new file mode 100644
index 0000000000..b8f3301ace
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/ca/samba4.po
@@ -0,0 +1,170 @@
+# samba.pot
+# generated from ./applications/luci-samba/luasrc/i18n/samba.en.lua
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-06-10 03:40+0200\n"
+"PO-Revision-Date: 2014-07-01 05:47+0200\n"
+"Last-Translator: Alex \n"
+"Language-Team: LANGUAGE \n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.6\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permet convidats"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Permet que els usuaris del sistema pugin arribar als seus directoris d'inici "
+"via comparticions de xarxa"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Usuaris permesos"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Crea màscara"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descripció"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Màscara de directori"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Edita plantilla"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Edita la plantilla que s'usa per generar la configuració de samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Ajusts generals"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nom de màquina"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nom"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Comparticions de xarxa"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Ruta"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Només lectura"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Comparteix directoris d'inici"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Directoris compartits"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Això és el contingut del fitxer '/etc/samba/smb.conf.template' del qual la "
+"vostra configuració de samba es generarà. Valors encerclats per símbols de "
+"barra ('|') no es deuen canviar. Reben els seus valors de la pestanya "
+"'Ajusts generals'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Grup de treball"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Màscara per directoris nous"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Màscara per fitxers nous"
+
+#~ msgid "Shared Directory"
+#~ msgstr "Directori compartit"
+
+#~ msgid "Physical Path"
+#~ msgstr "Ruta física"
+
+#~ msgid "optional"
+#~ msgstr "opcional"
diff --git a/package/lean/luci-app-samba4/po/cs/samba4.po b/package/lean/luci-app-samba4/po/cs/samba4.po
new file mode 100644
index 0000000000..58913db49c
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/cs/samba4.po
@@ -0,0 +1,158 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2014-05-31 13:56+0200\n"
+"Last-Translator: koli \n"
+"Language-Team: none\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Pootle 2.0.6\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Povolení hosté"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Povoluje systémovým uživatelům přístup do jejich domácích adresářů skrze "
+"sdílení přes síť."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Povolení uživatelé"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Vytvořit masku"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Popis"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Maska adresáře"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Editovat šablonu"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Editovat šablonu, která je použita pro generování konfiguračního souboru pro "
+"sambu."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Obecné nastavení"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Název počítače."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Jméno"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Síťová sdílení"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Cesta"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Pouze pro čtení"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Sdílet domácí adresáře"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Sdílené adresáře"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Toto je obsah souboru \"/etc/samba/smb.conf.template\", ze kterého je "
+"konfigurace samby generována. Hodnoty uzavřené rourou (\"|\"), by se neměly "
+"měnit. Tyto hodnoty jsou brány ze záložky \"Obecná nastavení\"."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Skupina"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Maska pro nové adresáře"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Maska pro nové soubory"
diff --git a/package/lean/luci-app-samba4/po/de/samba4.po b/package/lean/luci-app-samba4/po/de/samba4.po
new file mode 100644
index 0000000000..4d52f1b40a
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/de/samba4.po
@@ -0,0 +1,165 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-26 17:57+0200\n"
+"PO-Revision-Date: 2011-10-18 13:13+0200\n"
+"Last-Translator: Manuel \n"
+"Language-Team: LANGUAGE \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Gastzugang"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Systembenutzer dürfen ihre Heimatverzeichnis über Netzwerkfreigaben "
+"erreichen."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Legitimierte Benutzer"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr "Durchsuchbar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Berechtigungs-maske für neue Dateien"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Beschreibung"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Verzeichnis-maske"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "Deaktiviere Active Directory Domain Controller"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "Deaktiviere Netbios"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "Deaktiviere Winbind"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Template bearbeiten"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Hier kann das Template bearbeitet werden, das zur Erstellung der Samba-"
+"Konfigurationsdateien verwendet wird."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Allgemeine Einstellungen"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "Nur Gaeste"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Hostname"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "Besitzer Erben"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Name"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Netzwerk-freigaben"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Pfad"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Nur Lesen"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Heimat-verzeichnisse freigeben"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Freigegebene Verzeichnisse"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Dieses Fenster zeigt den Inhalt der Datei '/etc/samba/smb.conf.template', "
+"die als Template zum Erstellen der Samba-Konfiguration verwendet wird. Werte "
+"die von Pipe Symbolen (|) eingeschlossen sind sollten nicht verändert "
+"werden, da diese beim Erstellen der Konfiguration mit den Werten aus dem Tab "
+"'Allgemeine Einstellungen' ersetzt werden."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Arbeitsgruppe"
+
+#~ msgid "Browseable"
+#~ msgstr "Suchbar"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Maske für neue Verzeichnisse"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Maske für neue Dateien"
diff --git a/package/lean/luci-app-samba4/po/el/samba4.po b/package/lean/luci-app-samba4/po/el/samba4.po
new file mode 100644
index 0000000000..f022250942
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/el/samba4.po
@@ -0,0 +1,147 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-28 02:08+0200\n"
+"PO-Revision-Date: 2012-03-18 15:31+0200\n"
+"Last-Translator: Vasilis \n"
+"Language-Team: LANGUAGE \n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Όνομα"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/en/samba4.po b/package/lean/luci-app-samba4/po/en/samba4.po
new file mode 100644
index 0000000000..0e515052fa
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/en/samba4.po
@@ -0,0 +1,157 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-03-22 15:23+0100\n"
+"PO-Revision-Date: 2011-10-25 21:26+0200\n"
+"Last-Translator: awm1 \n"
+"Language-Team: LANGUAGE \n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Allow guests"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "Allow system users to reach their home directories via network shares"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Allowed users"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Create mask"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Description"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Directory mask"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Edit template"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Edit the template that is used for generating the Samba configuration."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "General settings"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Hostname"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Name"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Network Shares"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Path"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Read-only"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Share home-directories"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Shared Directories"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your Samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"settings' tab."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Workgroup"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Mask for new directories"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Mask for new files"
diff --git a/package/lean/luci-app-samba4/po/es/samba4.po b/package/lean/luci-app-samba4/po/es/samba4.po
new file mode 100644
index 0000000000..0786d7a2fc
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/es/samba4.po
@@ -0,0 +1,171 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-06-10 03:41+0200\n"
+"PO-Revision-Date: 2019-03-01 18:06-0300\n"
+"Last-Translator: José Vicente \n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+"Language-Team: \n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permitir invitados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Permitir que los usuarios del sistema lleguen a sus carpetas personales a "
+"través de Samba"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Usuarios permitidos"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr "Compartir como Apple Time-Machine"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr "Navegable"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Crear máscara"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descripción"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Máscara de carpeta"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "Deshabilitar el controlador de dominio de carpeta activa"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "Deshabilitar Netbios"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "Deshabilitar Winbind"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Editar plantilla"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Editar la plantilla usada para generar la configuración de samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr "Habilitar compatibilidad de Samba con macOS"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+"Habilita la extensión AAPL de Apple globalmente y agrega opciones de "
+"compatibilidad de macOS a todos los recursos compartidos."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr "Forzar Root"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Ajustes generales"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "Sólo invitados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nombre del dispositivo"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "Heredar dueño"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nombre"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Samba"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Dirección"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+"Por favor agregue carpetas para compartir. Cada carpeta se refiere a una "
+"carpeta en un dispositivo montado."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Sólo lectura"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Compartir carpetas personales"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Carpetas compartidos"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Contenido del fichero '/etc/samba/smb.conf.template' desde el que se "
+"generará la configuración de samba. Los valores entre tuberías ('|') no "
+"deben cambiarse. Su valor se toma desde la pestaña 'Configuración General'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr "Tamaño del Time-Machine en GB"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr "Objetos vfs"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Grupo de trabajo"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Máscara para directorios nuevos"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Máscara para archivos nuevos"
+
+#~ msgid "Shared Directory"
+#~ msgstr "Directorio compatido"
+
+#~ msgid "Physical Path"
+#~ msgstr "Ruta Física"
+
+#~ msgid "optional"
+#~ msgstr "opcional"
diff --git a/package/lean/luci-app-samba4/po/fr/samba4.po b/package/lean/luci-app-samba4/po/fr/samba4.po
new file mode 100644
index 0000000000..3375bfb9c7
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/fr/samba4.po
@@ -0,0 +1,159 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-19 19:36+0200\n"
+"PO-Revision-Date: 2011-11-23 22:36+0200\n"
+"Last-Translator: fredb \n"
+"Language-Team: LANGUAGE \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Invités autorisés"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Autoriser les utilisateurs système à atteindre leurs dossiers personnels via "
+"les partages réseau"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Utilisateurs autorisés"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Maque de création"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Description"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Masque des dossiers"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Éditer le modèle"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Éditer le modèle utilisé pour générer la configuration Samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Paramètres généraux"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nom d'hôte"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nom"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Partages réseau"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Chemin"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Lecture seule"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Partager les dossiers personnels"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Dossiers partagés"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Voici le contenu du fichier '/etc/samba/smb.conf.template' d'où sera généré "
+"votre configuration Samba. Les valeurs entre les symboles barre-verticale "
+" (« | ») ne doivent pas être modifiées, elles proviennent de l'onglet "
+"« Paramètres généraux »."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Groupe de travail"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Masque pour les nouveaux dossiers"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Masque pour les nouveaux fichiers"
diff --git a/package/lean/luci-app-samba4/po/he/samba4.po b/package/lean/luci-app-samba4/po/he/samba4.po
new file mode 100644
index 0000000000..49f38a87d8
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/he/samba4.po
@@ -0,0 +1,142 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/hu/samba4.po b/package/lean/luci-app-samba4/po/hu/samba4.po
new file mode 100644
index 0000000000..ebd0f7dbaf
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/hu/samba4.po
@@ -0,0 +1,159 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2012-04-06 10:56+0200\n"
+"Last-Translator: juhosg \n"
+"Language-Team: none\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Vendég hozzáférés"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"A rendszerfelhasználók hálózati megosztáson keresztül hozzáférhetnek a home "
+"könyvtárukhoz."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Engedélyezett felhasználók"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Létrehozási maszk"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Leírás"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Könyvtár maszk"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Sablon szerkesztése"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Itt szerkesztheti a sablont, ami a végleges samba konfiguráció "
+"elkészítéséhez kerül felhasználásra."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Általános beállítások"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Gépnév"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Név"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Hálózati megosztások"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Elérési út"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Csak olvasható"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Home könyvtárak megosztása"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Megosztott könyvtárak"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Itt látható a /etc/samba/smb.conf.template file tartalma, ami a samba "
+"konfiguráció előállításához kerül felhasználásra. A pipe szimbólumok ('|') "
+"közé zárt értékek módosítása nem szükséges, az értéküket az általános "
+"beállítások fülről kapják."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Munkacsoport"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Új könyvtárak maszkja"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Új fájlok maszkja"
diff --git a/package/lean/luci-app-samba4/po/it/samba4.po b/package/lean/luci-app-samba4/po/it/samba4.po
new file mode 100644
index 0000000000..6a6fd03b68
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/it/samba4.po
@@ -0,0 +1,165 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-19 19:36+0200\n"
+"PO-Revision-Date: 2017-09-06 01:28+0200\n"
+"Last-Translator: bubu83 \n"
+"Language-Team: LANGUAGE \n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permetti ospiti"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Autorizza gli utenti del sistema a raggiungere la loro cartella home "
+"attraverso le condivisioni di rete"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Utenti ammessi"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Crea maschera"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descrizione"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Maschera della cartella"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Modifica Template"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Modifica il template utilizzato per generare la configurazione di samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Opzioni Generali"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Hostname"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nome"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Condivisioni di rete"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Percorso"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+"Per favore aggiungi le directory da condividere. Ogni directory si riferisce "
+"a una cartella su un dispositivo montato."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Solo lettura"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Condividi cartelle home"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Cartelle Condivise"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Questo è il contenuto del file '/etc/samba/smb.conf.template' dal quale sarà "
+"generata la tua configurazione di samba. I valori racchiusi tra il simbolo "
+"('|') non dovrebbero essere toccati. Essi vengono generati dalla schermata "
+"'Opzioni Generali'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Gruppo di lavoro"
+
+#~ msgid "Browseable"
+#~ msgstr "Sfogliabile"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Maschera per le nuove cartelle"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Maschera per i nuovi files"
diff --git a/package/lean/luci-app-samba4/po/ja/samba4.po b/package/lean/luci-app-samba4/po/ja/samba4.po
new file mode 100644
index 0000000000..95d7c01c74
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/ja/samba4.po
@@ -0,0 +1,158 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-19 19:36+0200\n"
+"PO-Revision-Date: 2018-08-06 05:17+0900\n"
+"Last-Translator: INAGAKI Hiroshi \n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 2.1.1\n"
+"Language-Team: \n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "ゲストアクセスを許可"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "sambaを介してユーザーのホームディレクトリへのアクセスを許可します"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "許可されたユーザー"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "マスクの作成"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "説明"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "ディレクトリのマスク"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "Active Directory ドメインコントローラを無効化"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "Netbios を無効化"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "Winbind を無効化"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "テンプレートの編集"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "samba設定を生成するテンプレートを編集します。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "一般設定"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "ゲストのみ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "ホスト名"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "オーナーの継承"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "名前"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "ネットワーク共有"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "パス"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+"共有するディレクトリを追加してください。マウントされたデバイス等のディレクト"
+"リを設定し、公開することができます。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "読み込みのみ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "ホームディレクトリの共有"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "共有ディレクトリ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"これは、samba設定を生成するための'/etc/samba/smb.conf.template' ファイルの内"
+"容です。パイプ('|')で閉じられた値は変更しないでください。これらの値は'一般設"
+"定'タブ内の値によって置き換えられます。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "ワークグループ"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "新規ディレクトリのマスク"
+
+#~ msgid "Mask for new files"
+#~ msgstr "新規ファイルのマスク"
diff --git a/package/lean/luci-app-samba4/po/ms/samba4.po b/package/lean/luci-app-samba4/po/ms/samba4.po
new file mode 100644
index 0000000000..ab8a5a8542
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/ms/samba4.po
@@ -0,0 +1,141 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/no/samba4.po b/package/lean/luci-app-samba4/po/no/samba4.po
new file mode 100644
index 0000000000..e8276b9835
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/no/samba4.po
@@ -0,0 +1,147 @@
+msgid ""
+msgstr ""
+"Last-Translator: Lars Hardy \n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Tillat gjester"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "Tillat systembrukere å nå sine hjemmekataloger via nettverks mapper."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Tillatte brukere"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Opprett Maske"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Katalog maske"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Rediger Mal"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Rediger malen som brukes til å generere samba konfigurasjonen."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Generelle Innstillinger"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Vertsnavn"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Navn"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Nettverks Mapper"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Fysisk bane"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Skrivebeskyttet"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Del Hjemmekataloger"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Delte Kataloger"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Dette er innholdet av filen '/etc/samba/smb.conf.template' som din samba "
+"konfigurasjon vil bli generert fra. Verdier omsluttet av ('|') bør ikke "
+"endres. De får sine verdier fra 'Generelle Innstillinger' fanen."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Arbeidsgruppe"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Maske for nye kataloger"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Maske for nye filer"
diff --git a/package/lean/luci-app-samba4/po/pl/samba4.po b/package/lean/luci-app-samba4/po/pl/samba4.po
new file mode 100644
index 0000000000..120ecd8570
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/pl/samba4.po
@@ -0,0 +1,158 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2011-08-26 09:51+0200\n"
+"Last-Translator: Staszek \n"
+"Language-Team: none\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Zezwalaj Gościom"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Użytkownicy systemu mogą dostać się do swoich katalogów domowych za "
+"pośrednictwem udziałów sieciowych."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Użytkownicy z prawem dostępu"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Utwórz maskę"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Opis"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Maska katalogu"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Edytuj szablon"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Edytuj szablon, który jest używany do generowania konfiguracji samby."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Ustawienia ogólne"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nazwa hosta"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nazwa"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Udziały sieciowe"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Ścieżka"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Tylko do odczytu"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Udostępniaj katalogi domowe"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Udostępniane katalogi"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"To jest zawartość pliku '/etc/samba/smb.conf.template\", na podstawie "
+"którego zostanie wygenerowana konfiguracja samby. Wartości otoczone symbolem "
+"kreski pionowej ('|') nie powinny być zmieniane. Wartości ich zostaną "
+"pobrane z zakładki \"Ustawienia ogólne\"."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Grupa robocza"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Maska dla nowych katalogów"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Maska dla nowych plików"
diff --git a/package/lean/luci-app-samba4/po/pt-br/samba4.po b/package/lean/luci-app-samba4/po/pt-br/samba4.po
new file mode 100644
index 0000000000..f6b6677dd2
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/pt-br/samba4.po
@@ -0,0 +1,172 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-06-10 03:41+0200\n"
+"PO-Revision-Date: 2018-09-20 19:48-0300\n"
+"Last-Translator: Luiz Angelo Daros de Luca \n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.1.1\n"
+"Language-Team: \n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permitir convidados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Usuários do sistema poderão acessar seu diretório home através dos "
+"compartilhamentos de rede"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Usuários permitidos"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr "Compartilhamento Time Machine da Apple"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr "Navegável"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Máscara de criação"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descrição"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Máscara do diretório"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "Desabilitar o Controlador de Domínios AD"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "Desabilitar o NetBIOS"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "Desabilitar o Winbind"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Editar modelo"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Edita o modelo que é usado para gerar a configuração do samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr "Habilitar compartilhamentos compatíveis com MacOS"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+"Habilitar globalmente a extensão AAPL da Apple e adicione a opção de "
+"compatibilidade com MacOS em todos os compartilhamentos."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr "Forçar Usuário Root"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Configurações Gerais"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "Somente convidados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nome do equipamento"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "Herdar o dono"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nome"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Compartilhamentos de Rede"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Caminho"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+"Por favor, adicione diretórios para compartilhar. Cada diretório refere-se a "
+"uma porta em um dispositivo montado."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Somente leitura"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Compartilhar diretórios home"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Diretórios Compartilhados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Este é o conteúdo do arquivo '/etc/samba/smb.conf.template' a partir do qual "
+"sua configuração do samba será gerada. Valores entre simbolos de pipe ('|') "
+"não devem ser alterados. Estes valores serão obtidos a partir da aba "
+"'Configurações Gerais'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr "Tamanho do Time Machine em GB"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr "Objetos VFS"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Grupo de trabalho"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Máscara para novos diretórios"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Máscara para novos arquivos"
+
+#~ msgid "Shared Directory"
+#~ msgstr "Diretório Compartilhado"
+
+#~ msgid "Physical Path"
+#~ msgstr "Caminho Físico"
+
+#~ msgid "optional"
+#~ msgstr "opcional"
diff --git a/package/lean/luci-app-samba4/po/pt/samba4.po b/package/lean/luci-app-samba4/po/pt/samba4.po
new file mode 100644
index 0000000000..d68ab66803
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/pt/samba4.po
@@ -0,0 +1,168 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-05-26 19:03+0200\n"
+"PO-Revision-Date: 2013-05-01 01:13+0200\n"
+"Last-Translator: pedromrgoncalves \n"
+"Language-Team: LANGUAGE \n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.0.6\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permitir Convidados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Utilizadores do sistema poderão aceder ao seu directório home através das "
+"partilhas de rede."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Utilizadores Permitidos"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Criar Máscara"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descrição"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Máscara do Directório"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Editar Template"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Editar a template que é utilizada para gerar a configuração samba"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Definições Gerais"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Nome do host"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nome"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Partilhas da Rede"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Caminho"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Apenas Leitura"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Partilha de directórios home"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Directórios Partilhados"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Isto é o conteúdo do ficheiro 'etc/samba/smb.conf.template' a partir do qual "
+"será gerado o ficheiro de configuração do samba. Os valores entre o símbolo "
+"| não devem ser alterados. Eles recebem os valores do separador 'Definições "
+"Gerais'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Grupo de trabalho"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Máscara para novos directórios"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Máscara para novos ficheiros"
+
+#~ msgid "Shared Directory"
+#~ msgstr "Diretório Compartilhado"
+
+#~ msgid "Physical Path"
+#~ msgstr "Caminho Físico"
+
+#~ msgid "optional"
+#~ msgstr "opcional"
diff --git a/package/lean/luci-app-samba4/po/ro/samba4.po b/package/lean/luci-app-samba4/po/ro/samba4.po
new file mode 100644
index 0000000000..2e1ecdaf6c
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/ro/samba4.po
@@ -0,0 +1,157 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2011-10-07 17:16+0200\n"
+"Last-Translator: Daniel \n"
+"Language-Team: none\n"
+"Language: ro\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
+"20)) ? 1 : 2);;\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Permite oaspeti"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Permite utilizatorii de sistem sa acceseze directoarele lor peste "
+"partajarile de retea"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Utilizatori acceptati"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Creaza masca"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Descriere"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Masca director"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Editeaza sablon"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Editeaza sablonul care e folosit pentru generarea configuratiei samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Setari generale"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Numele de host"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Nume"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Partajari pe retea"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Cale"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Doar citire"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Partajeaza directoarele proprii"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Directoare partajate"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Acesta este continutul fisierului '/etc/samba/smb.conf.template' din care se "
+"genereaza configuratia samba. Valorile dintre liniuta verticala ('|') n-ar "
+"trebui schimbate, ele iau valorile direct din tab-ul de \"Setari generale\"."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Workgroup"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Masca pentru directoarele noi"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Masca pentru fisierele noi"
diff --git a/package/lean/luci-app-samba4/po/ru/samba4.po b/package/lean/luci-app-samba4/po/ru/samba4.po
new file mode 100644
index 0000000000..9829dc50bb
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/ru/samba4.po
@@ -0,0 +1,166 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: LuCI: samba\n"
+"POT-Creation-Date: 2009-05-19 19:36+0200\n"
+"PO-Revision-Date: 2018-01-14 11:43+0300\n"
+"Language-Team: http://cyber-place.ru\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"Last-Translator: Vladimir aka sunny \n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Language: ru\n"
+"Project-Info: Это технический перевод, не дословный. Главное-удобный русский "
+"интерфейс, все проверялось в графическом режиме, совместим с другими apps\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Разрешить гостевой вход"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Разрешить пользователям получать доступ к их домашним папкам, через "
+"локальную сеть."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Разрешенные пользователи"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Создать маску"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Описание"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Маска папок"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Настройка config файла"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "Настройка config
файла samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Основные настройки"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Имя хоста"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Имя"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Сетевые ресурсы"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Путь"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+"Добавьте папки для совместного доступа. Каждая папка - соответствует разделу "
+"на подключенном устройстве."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Только для чтения"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Совместно использовать домашние папки"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Совместно используемые папки"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Это содержимое файла '/etc/samba/smb.conf.template', из которого "
+"генерируется config файл - samba.
Значения, заключенные в символы "
+"('|'), не должны быть изменены.
Они будут автоматически заменены на "
+"значения со страницы 'Основные настройки'."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Рабочая группа"
+
+#~ msgid "Browseable"
+#~ msgstr "Виден в списке доступных ресурсов"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Маска для новых папок"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Маска для новых файлов"
diff --git a/package/lean/luci-app-samba4/po/sk/samba4.po b/package/lean/luci-app-samba4/po/sk/samba4.po
new file mode 100644
index 0000000000..a1c1d38a3c
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/sk/samba4.po
@@ -0,0 +1,142 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/sv/samba4.po b/package/lean/luci-app-samba4/po/sv/samba4.po
new file mode 100644
index 0000000000..756fa6a273
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/sv/samba4.po
@@ -0,0 +1,150 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Tillåt gäster"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "Tillåt systemanvändare att nå deras hem-mappar via nätverksdelningar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Tillåtna användare"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Skapa mask"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Beskrivning"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Mask för mapp"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Redigera mall"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Redigera mallen som används för att generera konfigurationen för samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Generella inställningar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Värdnamn"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Namn"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Nätverksdelningar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Genväg"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Endast läsbar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Dela hem-mappar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Delade mappar"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Arbetsgrupp"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Mask för nya mappar"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Mask för nya filer"
diff --git a/package/lean/luci-app-samba4/po/templates/samba4.pot b/package/lean/luci-app-samba4/po/templates/samba4.pot
new file mode 100644
index 0000000000..d930c139ed
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/templates/samba4.pot
@@ -0,0 +1,135 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/tr/samba4.po b/package/lean/luci-app-samba4/po/tr/samba4.po
new file mode 100644
index 0000000000..570ad2f384
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/tr/samba4.po
@@ -0,0 +1,142 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr ""
diff --git a/package/lean/luci-app-samba4/po/uk/samba4.po b/package/lean/luci-app-samba4/po/uk/samba4.po
new file mode 100644
index 0000000000..5acd96cee7
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/uk/samba4.po
@@ -0,0 +1,159 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2012-03-18 20:35+0200\n"
+"Last-Translator: YuriPet \n"
+"Language-Team: none\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
+"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Pootle 2.0.4\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "Дозволити гостьовий вхід"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Дозволити користувачам системи досягати своїх домашніх каталогів через "
+"загальні мережеві ресурси"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "Дозволені користувачі"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "Створити маску"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Опис"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "Маска каталогу"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "Редагувати шаблон"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+"Редагувати шаблон, який використовується для створення конфігурації samba."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "Загальні настройки"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "Назва (ім'я) вузла"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "Ім'я"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Загальні мережеві ресурси"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "Шлях"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "Тільки читання"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Спільно використовувати домашні каталоги"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Загальні каталоги"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"Це вміст файлу '/etc/samba/smb.conf.template', з якого буде генеруватися "
+"ваша конфігурація samba. Значення, укладені в символи \"вертикальна риска"
+"\" (\"|\") не повинні змінюватися. Вони отримують свої значення з вкладки "
+"\"Загальні налаштування\"."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Робоча група"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Маска для нових каталогів"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Маска для нових файлів"
diff --git a/package/lean/luci-app-samba4/po/vi/samba4.po b/package/lean/luci-app-samba4/po/vi/samba4.po
new file mode 100644
index 0000000000..2920759f41
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/vi/samba4.po
@@ -0,0 +1,171 @@
+# samba.pot
+# generated from ./applications/luci-samba/luasrc/i18n/samba.en.lua
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-08-16 06:59+0200\n"
+"PO-Revision-Date: 2009-08-13 03:54+0200\n"
+"Last-Translator: Hong Phuc Dang \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Pootle 1.1.0\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+#, fuzzy
+msgid "Allow guests"
+msgstr "Cho phép khách"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+#, fuzzy
+msgid "Allow system users to reach their home directories via network shares"
+msgstr ""
+"Những người sử dụng hệ thống có thể tiếp cận những thư mục tại nhà thông qua "
+"mạng lưới chia sẻ trực tuyến."
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+#, fuzzy
+msgid "Allowed users"
+msgstr "Người sử dụng được cho phép"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+#, fuzzy
+msgid "Create mask"
+msgstr "Tạo Mask"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "Mô tả"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+#, fuzzy
+msgid "Directory mask"
+msgstr "Thư mục Mask"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "tên máy chủ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "Mạng chia sẻ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+#, fuzzy
+msgid "Read-only"
+msgstr "Chỉ đọc "
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "Chia sẻ danh bạ chính"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "Thư mục chia sẻ"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr ""
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "Nhóm làm việc "
+
+#~ msgid "Mask for new directories"
+#~ msgstr "Mask cho thư mục mới"
+
+#~ msgid "Mask for new files"
+#~ msgstr "Mask cho tập tin mới"
+
+#~ msgid "Shared Directory"
+#~ msgstr "Đã chia sẻ thư mục"
+
+#~ msgid "Physical Path"
+#~ msgstr "Đường dẫn vật lý"
+
+#~ msgid "optional"
+#~ msgstr "Tùy thích"
diff --git a/package/lean/luci-app-samba4/po/zh-cn/samba4.po b/package/lean/luci-app-samba4/po/zh-cn/samba4.po
new file mode 100644
index 0000000000..f1d681ecbf
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/zh-cn/samba4.po
@@ -0,0 +1,164 @@
+#
+# Yangfl , 2017, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-06-10 03:40+0200\n"
+"PO-Revision-Date: 2018-10-01 11:12+0800\n"
+"Last-Translator: Yangfl \n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "允许匿名用户"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "允许系统用户通过网络共享访问他们的家目录"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "允许用户"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr "Apple Time-machine 共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr "可浏览"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "创建权限掩码"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "描述"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "目录权限掩码"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "禁用 Active Directory 域控制器"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "禁用 Netbios"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "禁用 Winbind"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "编辑模板"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "编辑用来生成 samba 设置的模板"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr "启用 macOS 兼容共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr "全局启用 Apple 的 AAPL 扩展,并为所有共享添加 macOS 兼容性选项。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr "强制 Root"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "基本设置"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "仅来宾用户"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "主机名"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "继承所有者"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "共享名"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "网络共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "目录"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr "请添加要共享的目录。每个目录指到已挂载设备上的文件夹。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "只读"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "共享家目录"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "共享目录"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"这是将从其上生成 samba 配置的文件“/etc/samba/smb.conf.template”的内容。由管道"
+"符(“|”)包围的值不应更改。它们将从“常规设置”标签中获取其值。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr "Time-machine 大小(GB)"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr "VFS 对象"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "工作组"
+
+#~ msgid "Browseable"
+#~ msgstr "可浏览"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "新目录权限掩码"
+
+#~ msgid "Mask for new files"
+#~ msgstr "新文件权限掩码"
+
+#~ msgid "Physical Path"
+#~ msgstr "物理路径"
diff --git a/package/lean/luci-app-samba4/po/zh-tw/samba4.po b/package/lean/luci-app-samba4/po/zh-tw/samba4.po
new file mode 100644
index 0000000000..9e6fcfdff7
--- /dev/null
+++ b/package/lean/luci-app-samba4/po/zh-tw/samba4.po
@@ -0,0 +1,164 @@
+#
+# Yangfl , 2017, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-06-10 03:40+0200\n"
+"PO-Revision-Date: 2018-10-01 11:12+0800\n"
+"Last-Translator: Yangfl \n"
+"Language-Team: \n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:78
+msgid "Allow guests"
+msgstr "允許匿名使用者"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:15
+msgid "Allow system users to reach their home directories via network shares"
+msgstr "允許系統使用者通過網路共享訪問他們的家目錄"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:75
+msgid "Allowed users"
+msgstr "允許使用者"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:106
+msgid "Apple Time-machine share"
+msgstr "Apple Time-machine 共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:63
+msgid "Browse-able"
+msgstr "可瀏覽"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:93
+msgid "Create mask"
+msgstr "建立權限掩碼"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:12
+msgid "Description"
+msgstr "描述"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:98
+msgid "Directory mask"
+msgstr "目錄權限掩碼"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:27
+msgid "Disable Active Directory Domain Controller"
+msgstr "禁用 Active Directory 域控制器"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:24
+msgid "Disable Netbios"
+msgstr "禁用 Netbios"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:30
+msgid "Disable Winbind"
+msgstr "禁用 Winbind"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:9
+msgid "Edit Template"
+msgstr "編輯模板"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:34
+msgid "Edit the template that is used for generating the samba configuration."
+msgstr "編輯用來生成 samba 設定的模板"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:19
+msgid "Enable macOS compatible shares"
+msgstr "啟用 macOS 相容共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:20
+msgid ""
+"Enables Apple's AAPL extension globally and adds macOS compatibility options "
+"to all shares."
+msgstr "全域性啟用 Apple 的 AAPL 擴充套件,併為所有共享新增 macOS 相容性選項。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:73
+msgid "Force Root"
+msgstr "強制 Root"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:8
+msgid "General Settings"
+msgstr "基本設定"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:83
+msgid "Guests only"
+msgstr "僅來賓使用者"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:11
+msgid "Hostname"
+msgstr "主機名"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:88
+msgid "Inherit owner"
+msgstr "繼承所有者"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:57
+msgid "Name"
+msgstr "共享名"
+
+#: applications/luci-app-samba4/luasrc/controller/samba4.lua:12
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:3
+msgid "Network Shares"
+msgstr "網路共享"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:58
+msgid "Path"
+msgstr "目錄"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:52
+msgid ""
+"Please add directories to share. Each directory refers to a folder on a "
+"mounted device."
+msgstr "請新增要共享的目錄。每個目錄指到已掛載裝置上的資料夾。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:68
+msgid "Read-only"
+msgstr "只讀"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:14
+msgid "Share home-directories"
+msgstr "共享家目錄"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:51
+msgid "Shared Directories"
+msgstr "共享目錄"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:35
+msgid ""
+"This is the content of the file '/etc/samba/smb.conf.template' from which "
+"your samba configuration will be generated. Values enclosed by pipe symbols "
+"('|') should not be changed. They get their values from the 'General "
+"Settings' tab."
+msgstr ""
+"這是將從其上生成 samba 配置的檔案“/etc/samba/smb.conf.template”的內容。由管道"
+"符(“|”)包圍的值不應更改。它們將從“常規設定”標籤中獲取其值。"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:108
+msgid "Time-machine size in GB"
+msgstr "Time-machine 大小(GB)"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:103
+msgid "Vfs objects"
+msgstr "VFS 物件"
+
+#: applications/luci-app-samba4/luasrc/model/cbi/samba4.lua:13
+msgid "Workgroup"
+msgstr "工作組"
+
+#~ msgid "Browseable"
+#~ msgstr "可瀏覽"
+
+#~ msgid "Mask for new directories"
+#~ msgstr "新目錄權限掩碼"
+
+#~ msgid "Mask for new files"
+#~ msgstr "新檔案權限掩碼"
+
+#~ msgid "Physical Path"
+#~ msgstr "物理路徑"
diff --git a/package/lean/luci-app-sfe/Makefile b/package/lean/luci-app-sfe/Makefile
new file mode 100644
index 0000000000..25ed29e713
--- /dev/null
+++ b/package/lean/luci-app-sfe/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for Turbo ACC (SFE)
+LUCI_DEPENDS:=+kmod-fast-classifier +pdnsd-alt +kmod-tcp-bbr @LINUX_4_9
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=38
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-sfe/luasrc/controller/sfe.lua b/package/lean/luci-app-sfe/luasrc/controller/sfe.lua
new file mode 100644
index 0000000000..64ad85cd57
--- /dev/null
+++ b/package/lean/luci-app-sfe/luasrc/controller/sfe.lua
@@ -0,0 +1,44 @@
+module("luci.controller.sfe", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/sfe") then
+ return
+ end
+ local page
+ page = entry({"admin", "network", "sfe"}, cbi("sfe"), _("Turbo ACC Center"), 100)
+ page.i18n = "sfe"
+ page.dependent = true
+
+ entry({"admin", "network", "sfe", "status"}, call("action_status"))
+end
+
+local function is_running()
+ return luci.sys.call("lsmod | grep fast_classifier >/dev/null") == 0
+end
+
+local function is_bbr()
+ return luci.sys.call("sysctl net.ipv4.tcp_congestion_control | grep bbr >/dev/null") == 0
+end
+
+local function is_fullcone()
+ return luci.sys.call("iptables -t nat -L -n --line-numbers | grep FULLCONENAT >/dev/null") == 0
+end
+
+local function is_dns()
+ return luci.sys.call("[ `uci get sfe.@flow[0].dnscache_enable 2>/dev/null` -ne 3 ] && pgrep dnscache >/dev/null || pgrep AdGuardHome >/dev/null") == 0
+end
+
+local function is_ad()
+ return luci.sys.call("pgrep AdGuardHome >/dev/null") == 0
+end
+
+function action_status()
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({
+ run_state = is_running(),
+ down_state = is_bbr(),
+ up_state = is_fullcone(),
+ dns_state = is_dns(),
+ ad_state = is_ad()
+ })
+end
diff --git a/package/lean/luci-app-sfe/luasrc/model/cbi/sfe.lua b/package/lean/luci-app-sfe/luasrc/model/cbi/sfe.lua
new file mode 100644
index 0000000000..eae4f2145e
--- /dev/null
+++ b/package/lean/luci-app-sfe/luasrc/model/cbi/sfe.lua
@@ -0,0 +1,72 @@
+local trport = 3001
+local button = ""
+if luci.sys.call("pidof AdGuardHome >/dev/null") == 0 then
+ button = " "
+end
+
+m = Map("sfe")
+m.title = translate("Turbo ACC Acceleration Settings")
+m.description = translate("Opensource Qualcomm Shortcut FE driver (Fast Path)")
+
+m:append(Template("sfe/status"))
+
+s = m:section(TypedSection, "sfe", "")
+s.addremove = false
+s.anonymous = true
+
+
+enable = s:option(Flag, "enabled", translate("Enable SFE Fast Path"))
+enable.default = 0
+enable.rmempty = false
+enable.description = translate("Enable Fast Path offloading for connections. (decrease cpu load / increase routing throughput)")
+
+wifi = s:option(Flag, "wifi", translate("Bridge Acceleration"))
+wifi.default = 0
+wifi.rmempty = false
+wifi.description = translate("Enable Bridge Acceleration (may be functional conflict with bridge-mode VPN Server)")
+wifi:depends("enabled", 1)
+
+ipv6 = s:option(Flag, "ipv6", translate("IPv6 Acceleration"))
+ipv6.default = 0
+ipv6.rmempty = false
+ipv6.description = translate("Enable IPv6 Acceleration")
+ipv6:depends("enabled", 1)
+
+bbr = s:option(Flag, "bbr", translate("Enable BBR"))
+bbr.default = 0
+bbr.rmempty = false
+bbr.description = translate("Bottleneck Bandwidth and Round-trip propagation time (BBR)")
+
+aaaa = s:option(Flag, "filter_aaaa", translate("Filter AAAA"))
+aaaa.default = 1
+aaaa.rmempty = false
+aaaa.description = translate("Dnsmasq rejects IPv6 parsing and optimizes domestic complex dual-stack network")
+
+dns = s:option(Flag, "dns", translate("DNS Acceleration"))
+dns.default = 0
+dns.rmempty = false
+dns.description = translate("Enable DNS Cache Acceleration and anti ISP DNS pollution")
+
+o = s:option(ListValue, "dnscache_enable", translate("Resolve Dns Mode"), translate("AdGuardHome's login username/passwd: AdGuardHome, web console addr: IP:3001") .. button)
+o:value("1", translate("Use Pdnsd query and cache"))
+if nixio.fs.access("/usr/bin/dnsforwarder") then
+o:value("2", translate("Use dnsforwarder query and cache"))
+end
+if nixio.fs.access("/usr/bin/AdGuardHome") then
+o:value("3", translate("Use AdGuardHome query and cache"))
+end
+o.default = 1
+o:depends("dns", 1)
+
+o = s:option(Value, "dns_server", translate("Upsteam DNS Server"))
+o.default = "114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8"
+o.description = translate("Muitiple DNS server can saperate with ','")
+o:depends("dnscache_enable", 1)
+o:depends("dnscache_enable", 2)
+
+o = s:option(Value, "ipv6dns_server", translate("Upsteam IPV6 DNS Server"))
+o.default = "2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644"
+o.description = translate("Muitiple IPV6 DNS server can saperate with ','")
+o:depends("dnscache_enable", 2)
+
+return m
diff --git a/package/lean/luci-app-sfe/luasrc/view/sfe/status.htm b/package/lean/luci-app-sfe/luasrc/view/sfe/status.htm
new file mode 100644
index 0000000000..e38724c8e7
--- /dev/null
+++ b/package/lean/luci-app-sfe/luasrc/view/sfe/status.htm
@@ -0,0 +1,27 @@
+
+
+
diff --git a/package/lean/luci-app-sfe/po/zh-cn/sfe.po b/package/lean/luci-app-sfe/po/zh-cn/sfe.po
new file mode 100644
index 0000000000..7d991d9a43
--- /dev/null
+++ b/package/lean/luci-app-sfe/po/zh-cn/sfe.po
@@ -0,0 +1,86 @@
+msgid "SFE Acceleration"
+msgstr "SFE 转发加速"
+
+msgid "Turbo ACC Center"
+msgstr "Turbo ACC 网络加速"
+
+msgid "Turbo ACC Acceleration Settings"
+msgstr "Turbo ACC 网络加速设置"
+
+msgid "Shortcut Forwarding Engine Acceleration Settings"
+msgstr "SFE 转发加速引擎设置"
+
+msgid "Opensource Qualcomm Shortcut FE driver (Fast Path)"
+msgstr "来自高通开源的 Shortcut FE 转发加速引擎 (Fast Path)"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Enable SFE Fast Path"
+msgstr "启用 SFE Fast Path 加速"
+
+msgid "Enable Fast Path offloading for connections. (decrease cpu load / increase routing throughput)"
+msgstr "开启 Fast Path 转发加速. (降低CPU占用 / 增强路由转发性能,和多WAN或者QOS功能可能会冲突)"
+
+msgid "Bridge Acceleration"
+msgstr "桥接加速"
+
+msgid "Enable Bridge Acceleration (may be functional conflict with bridge-mode VPN Server)"
+msgstr "启用桥接加速 (可能会和路由器上桥接模式的VPN服务器冲突)"
+
+msgid "IPv6 Acceleration"
+msgstr "IPv6 加速"
+
+msgid "Enable IPv6 Acceleration"
+msgstr "开启IPv6加速"
+
+msgid "Enable BBR"
+msgstr "启用 BBR"
+
+msgid "Bottleneck Bandwidth and Round-trip propagation time (BBR)"
+msgstr "BBR拥塞控制算法可以加强TCP应用表现,但会降低路由器本机上的WIFI吞吐性能"
+
+msgid "DNS Acceleration"
+msgstr "DNS 加速"
+
+msgid "Enable DNS Cache Acceleration and anti ISP DNS pollution"
+msgstr "启用DNS多线程查询、缓存,并防止ISP的DNS广告和域名劫持"
+
+msgid "Resolve Dns Mode"
+msgstr "DNS解析方式"
+
+msgid "Enable dnsmasq killqqad"
+msgstr "启用dnsmasq去广告"
+
+msgid "dnsmasq killqqad"
+msgstr "dnsmasq去广告"
+
+msgid "Use Pdnsd query and cache"
+msgstr "使用pdnsd解析"
+
+msgid "Use dnsforwarder query and cache"
+msgstr "使用dnsforwarder解析"
+
+msgid "Use AdGuardHome query and cache"
+msgstr "使用AdGuardHome解析去广告"
+
+msgid "Upsteam DNS Server"
+msgstr "上游 DNS 服务器(国内)"
+
+msgid "Upsteam IPV6 DNS Server"
+msgstr "上游 IPV6 DNS 服务器"
+
+msgid "Muitiple DNS server can saperate with ','"
+msgstr "支持多个上游DNS服务器,用','分隔(注意用英文逗号).请填写您最快的DNS服务器"
+
+msgid "Muitiple IPV6 DNS server can saperate with ','"
+msgstr "支持dnsforwarder解析多个上游IPV6 DNS服务器,用','分隔(注意用英文逗号).请填写您最快的DNS服务器"
+
+msgid "AdGuardHome's login username/passwd: AdGuardHome, web console addr: IP:3001"
+msgstr "AdGuardHome 网页控制台地址:IP:3001 登录账号/密码:AdGuardHome"
+
+msgid "Filter AAAA"
+msgstr "忽略域名的IPv6地址"
+
+msgid "Dnsmasq rejects IPv6 parsing and optimizes domestic complex dual-stack network"
+msgstr "dnsmasq不返回ipv6解析结果,优化国内复杂双栈网络"
diff --git a/package/lean/luci-app-sfe/root/etc/config/sfe b/package/lean/luci-app-sfe/root/etc/config/sfe
new file mode 100644
index 0000000000..b48624ca65
--- /dev/null
+++ b/package/lean/luci-app-sfe/root/etc/config/sfe
@@ -0,0 +1,10 @@
+config sfe
+ option enabled '0'
+ option ipv6 '0'
+ option wifi '0'
+ option bbr '0'
+ option dns '0'
+ option dnscache_enable '1'
+ option dns_server '114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8'
+ option ipv6dns_server '2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644'
+
diff --git a/package/lean/luci-app-sfe/root/etc/init.d/sfe b/package/lean/luci-app-sfe/root/etc/init.d/sfe
new file mode 100755
index 0000000000..d80d20cba0
--- /dev/null
+++ b/package/lean/luci-app-sfe/root/etc/init.d/sfe
@@ -0,0 +1,299 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2007-2012 OpenWrt.org
+
+START=90
+
+USE_PROCD=1
+
+
+DNSMASQ_RESTART=N
+DNS_SERVER="114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8"
+IPV6DNS_SERVER="2001:4860:4860::8888,2001:4860:4860::8844,2001:2001::1111,2001:2001::1001,2400:da00::6666,240C::6666,240C::6644"
+
+start_pdnsd() {
+ DNS_SERVER=$(uci get sfe.config.dns_server 2>/dev/null)
+ cp -a /usr/sbin/pdnsd /usr/sbin/dnscache
+ sleep 1
+ [ -d /var/etc ] || mkdir -p /var/etc
+
+ if [ ! -f /var/dnscache/pdnsd.cache ]; then
+ mkdir -p /var/dnscache
+ echo -ne "pd13\000\000\000\000" > /var/dnscache/pdnsd.cache
+ chown -R nobody.nogroup /var/dnscache
+ fi
+
+ cat > /var/etc/dnscache.conf </dev/null)
+ IPV6DNS_SERVER=$(uci get sfe.config.ipv6dns_server 2>/dev/null)
+ dnscache_enable=$(uci get sfe.config.dnscache_enable 2>/dev/null)
+ if [ $dnscache_enable = "2" ]; then
+ localserver=0.0.0.0:5333
+ elif [ $dnscache_enable = "3" ]; then
+ localserver=0.0.0.0:5383
+ fi
+ cp -a /usr/bin/dnsforwarder /usr/sbin/dnscache
+ sleep 1
+ DNSCACHE_CONF=/var/run/dnscache/dnscache.conf
+ PID_PATH=/var/run/dnscache
+ PID_FILE=dns.pid
+ mkdir -p $PID_PATH
+ pid=`ps | awk '$5 ~ /\[dnscache\]/ {print $1}'`
+
+ cat > /var/run/dnscache/dnscache.conf </dev/null 2>&1 || kill -9 $(ps | grep AdGuardHome | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ kill $(pidof dnscache) >/dev/null 2>&1 || kill -9 $(ps | grep dnscache | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ rm -rf /var/run/dnscache
+ rm -rf /var/dnscache
+ echo "Stop DNS Cache"
+
+}
+
+change_dns() {
+ uci delete dhcp.@dnsmasq[0].server >/dev/null 2>&1
+ uci add_list dhcp.@dnsmasq[0].server=127.0.0.1#5333
+ uci set dhcp.@dnsmasq[0].noresolv=1
+ uci commit dhcp
+
+}
+
+revert_dns() {
+ uci del_list dhcp.@dnsmasq[0].server=127.0.0.1#5333 >/dev/null 2>&1
+ uci set dhcp.@dnsmasq[0].resolvfile=/tmp/resolv.conf.auto
+ uci set dhcp.@dnsmasq[0].noresolv=0
+ uci commit dhcp
+
+}
+
+start_service() {
+ enable=$(uci get sfe.config.enabled 2>/dev/null)
+ wifi=$(uci get sfe.config.wifi)
+ ipv6=$(uci get sfe.config.ipv6)
+ dns=$(uci get sfe.config.dns 2>/dev/null)
+ dnscache_enable=$(uci get sfe.config.dnscache_enable 2>/dev/null)
+ bbr=$(uci get sfe.config.bbr 2>/dev/null)
+ aaaa=$(uci get sfe.config.filter_aaaa 2>/dev/null)
+ if [ $aaaa -eq 1 ]; then
+ sed -i '/filter-aaaa/d' /etc/dnsmasq.conf
+ echo "filter-aaaa" >> /etc/dnsmasq.conf
+ else
+ sed -i '/filter-aaaa/d' /etc/dnsmasq.conf
+ fi
+
+ if [ $enable -eq 1 ]; then
+ ! (lsmod | grep fast_classifier >/dev/null) && (modprobe fast_classifier)
+ if [ $wifi -eq 1 ] ; then
+ echo 1 > /sys/fast_classifier/skip_to_bridge_ingress
+ else
+ echo 0 > /sys/fast_classifier/skip_to_bridge_ingress
+ fi
+
+ if [ $ipv6 -eq 1 ]; then
+ sfe_ipv6=$(cat /sys/sfe_ipv6/debug_dev)
+ [ ! -f /dev/sfe_ipv6 ] && mknod /dev/sfe_ipv6 c $sfe_ipv6 0
+ else
+ rm -f /dev/sfe_ipv6
+ fi
+ else
+ rmmod fast_classifier >/dev/null 2>&1
+ fi
+
+ if [ $dns -eq 1 ]; then
+ kill -9 $(ps | grep dnscache-while.sh | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ stop_dnscache
+ sleep 1
+ rm -f /var/log/dnscache.file
+ rm -f /etc/AdGuardHome/data/querylog.json*
+ if [ $dnscache_enable = "1" ]; then
+ start_pdnsd
+ elif [ $dnscache_enable = "2" ]; then
+ start_dnsforwarder
+ elif [ $dnscache_enable = "3" ]; then
+ AdGuardHome -c /etc/AdGuardHome/AdGuardHome.yaml -w /etc/AdGuardHome >/dev/null 2>&1 &
+ fi
+ change_dns
+ nohup /usr/share/dnscache/dnscache-while.sh > /var/log/dnscache.file 2>&1 &
+ else
+ stop_dnscache
+ revert_dns
+ kill -9 $(ps | grep dnscache-while.sh | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ fi
+
+ if [ $bbr -eq 1 ]; then
+ sysctl -w net.ipv4.tcp_congestion_control=bbr
+ else
+ sysctl -w net.ipv4.tcp_congestion_control=cubic
+ fi
+
+ if [ "$DNSMASQ_RESTART" = N ]; then
+ /etc/init.d/dnsmasq restart
+ fi
+}
+
+stop_service(){
+ enable=$(uci get sfe.config.enabled 2>/dev/null)
+ [ $enable -ne 1 ] && rmmod fast_classifier >/dev/null 2>&1
+ stop_dnscache
+ revert_dns
+ kill -9 $(ps | grep dnscache-while.sh | grep -v "grep" | awk '{print $1}') >/dev/null 2>&1
+ if [ "$DNSMASQ_RESTART" = N ]; then
+ /etc/init.d/dnsmasq restart
+ fi
+}
+
+restart(){
+ DNSMASQ_RESTART=Y
+ stop
+ start
+ /etc/init.d/dnsmasq restart && echo "DNSMASQ restart"
+}
diff --git a/package/lean/luci-app-sfe/root/etc/uci-defaults/sfe b/package/lean/luci-app-sfe/root/etc/uci-defaults/sfe
new file mode 100755
index 0000000000..50adb160fd
--- /dev/null
+++ b/package/lean/luci-app-sfe/root/etc/uci-defaults/sfe
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@sfe[-1]
+ add ucitrack sfe
+ set ucitrack.@sfe[-1].init=sfe
+ commit ucitrack
+EOF
+
+[ ! -f /usr/sbin/dnscache ] && ln /usr/sbin/pdnsd /usr/sbin/dnscache
+/etc/init.d/sfe enable
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-sfe/root/usr/share/dnscache/dnscache-while.sh b/package/lean/luci-app-sfe/root/usr/share/dnscache/dnscache-while.sh
new file mode 100755
index 0000000000..c477326ed6
--- /dev/null
+++ b/package/lean/luci-app-sfe/root/usr/share/dnscache/dnscache-while.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+sleeptime=30
+logfile="/var/log/dnscache.file"
+adg_logfile="/etc/AdGuardHome/data/querylog.json"
+dns_enable=$(uci get sfe.config.dns 2>/dev/null)
+dnscache_enable=$(uci get sfe.config.dnscache_enable 2>/dev/null)
+
+clean_log(){
+logrow=$(grep -c "" ${logfile})
+if [ $logrow -ge 500 ];then
+ cat /dev/null > ${logfile}
+ echo "$curtime Log条数超限,清空处理!" >> ${logfile}
+fi
+if [ $dnscache_enable = "3" ];then
+ adg_logrow=$(grep -c "" ${adg_logfile})
+ if [ $adg_logrow -ge 500 ];then
+ cat /dev/null > ${adg_logfile}
+ echo "$curtime Log条数超限,清空处理!" >> ${adg_logfile}
+ fi
+fi
+
+}
+
+while [ $dns_enable -eq 1 ];
+do
+curtime=`date "+%H:%M:%S"`
+echo "$curtime online! "
+if [ $dns_enable -eq 1 ]; then
+ if [ $dnscache_enable = "3" ]; then
+ if ! pidof AdGuardHome>/dev/null; then
+ AdGuardHome -c /etc/AdGuardHome/AdGuardHome.yaml -w /etc/AdGuardHome >/dev/null 2>&1 &
+ echo "$curtime 重启服务!" >> ${logfile}
+ fi
+ else
+ if ! pidof dnscache>/dev/null; then
+ if [ $dnscache_enable = "1" ]; then
+ /usr/sbin/dnscache -c /var/etc/dnscache.conf -d
+ elif [ $dnscache_enable = "2" ]; then
+ dnscache -f /var/run/dnscache/dnscache.conf -d
+ fi
+ echo "$curtime 重启服务!" >> ${logfile}
+ fi
+ fi
+fi
+
+sleep ${sleeptime}
+continue
+done
diff --git a/package/lean/luci-app-softethervpn/Makefile b/package/lean/luci-app-softethervpn/Makefile
new file mode 100644
index 0000000000..12f9c4a659
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2018-2019 Lienol
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for SoftEtherVPN
+LUCI_DEPENDS:=+zlib +libpthread +librt +libreadline +libncurses +libiconv-full +kmod-tun +libopenssl +softethervpn-bridge +softethervpn-client +softethervpn-server
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-softethervpn/luasrc/controller/softethervpn.lua b/package/lean/luci-app-softethervpn/luasrc/controller/softethervpn.lua
new file mode 100644
index 0000000000..d4ec9bc88c
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/luasrc/controller/softethervpn.lua
@@ -0,0 +1,18 @@
+module("luci.controller.softethervpn",package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/softethervpn")then
+ return
+ end
+
+ entry({"admin","vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin","vpn","softethervpn"},cbi("softethervpn"),_("SoftEther VPN"),50).dependent=true
+ entry({"admin","vpn","softethervpn","status"},call("status")).leaf=true
+end
+
+function status()
+ local e={}
+ e.status=luci.sys.call("pidof %s >/dev/null"%"vpnserver")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-softethervpn/luasrc/model/cbi/softethervpn.lua b/package/lean/luci-app-softethervpn/luasrc/model/cbi/softethervpn.lua
new file mode 100644
index 0000000000..1c0054c291
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/luasrc/model/cbi/softethervpn.lua
@@ -0,0 +1,14 @@
+local s=require"luci.sys"
+local m,s,o
+m=Map("softethervpn",translate("SoftEther VPN"))
+m.description = translate("SoftEther VPN is an open source, cross-platform, multi-protocol virtual private network solution developed by university of tsukuba graduate student Daiyuu Nobori for master's thesis.
can easily set up OpenVPN, IPsec, L2TP, ms-sstp, L2TPv3 and EtherIP servers on the router using the console.")
+m.template="softethervpn/index"
+s=m:section(TypedSection,"softether")
+s.anonymous=true
+o=s:option(DummyValue,"softethervpn_status",translate("Current Condition"))
+o.template="softethervpn/status"
+o.value=translate("Collecting data...")
+o=s:option(Flag,"enable",translate("Enabled"))
+o.rmempty=false
+o=s:option(DummyValue,"moreinfo",translate("控制台下载:
Windows-x86_x64-intel.exe
macos-x86-32bit.pkg"))
+return m
diff --git a/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/index.htm b/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/index.htm
new file mode 100644
index 0000000000..273f604eab
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/index.htm
@@ -0,0 +1,18 @@
+<%#
+ Copyright (C) 2018-2019 Lienol
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<% include("cbi/map") %>
+
diff --git a/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/status.htm b/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/status.htm
new file mode 100644
index 0000000000..055b112ec1
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/luasrc/view/softethervpn/status.htm
@@ -0,0 +1,3 @@
+<%+cbi/valueheader%>
+<%=pcdata(self:cfgvalue(section) or self.default or "")%>
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-softethervpn/po/zh-cn/softethervpn.po b/package/lean/luci-app-softethervpn/po/zh-cn/softethervpn.po
new file mode 100644
index 0000000000..8d17b15070
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/po/zh-cn/softethervpn.po
@@ -0,0 +1,23 @@
+msgid "SoftEther VPN"
+msgstr "SoftEther VPN 服务器"
+
+msgid "SoftEther VPN is an open source, cross-platform, multi-protocol virtual private network solution developed by university of tsukuba graduate student Daiyuu Nobori for master's thesis.
can easily set up OpenVPN, IPsec, L2TP, ms-sstp, L2TPv3 and EtherIP servers on the router using the console."
+msgstr "SoftEther VPN是由筑波大学研究生Daiyuu Nobori因硕士论文开发的开源,跨平台,多重协定的虚拟私人网路方案。
使用控制台可以轻松在路由器上搭建OpenVPN, IPsec, L2TP, MS-SSTP, L2TPv3 和 EtherIP服务器。"
+
+msgid "PPTP VPN Server status"
+msgstr "PPTP VPN 服务器运行状态"
+
+msgid "Current Condition"
+msgstr "当前状态"
+
+msgid "Enabled"
+msgstr "启用"
+
+msgid "Open L2TP/IPSec firewall"
+msgstr "开启L2TP/IPSec防火墙"
+
+msgid "Open the MS-SSTP firewall"
+msgstr "开启MS-SSTP防火墙"
+
+msgid "Open the OpenVPN firewall"
+msgstr "开启OpenVPN防火墙"
\ No newline at end of file
diff --git a/package/lean/luci-app-softethervpn/root/etc/config/softethervpn b/package/lean/luci-app-softethervpn/root/etc/config/softethervpn
new file mode 100644
index 0000000000..90732c5339
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/root/etc/config/softethervpn
@@ -0,0 +1,4 @@
+
+config softether
+ option enable '0'
+
diff --git a/package/lean/luci-app-softethervpn/root/etc/init.d/softethervpn b/package/lean/luci-app-softethervpn/root/etc/init.d/softethervpn
new file mode 100755
index 0000000000..d724d5b8c5
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/root/etc/init.d/softethervpn
@@ -0,0 +1,45 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2015 OpenWrt-dist
+# Copyright (C) 2016 Chen RuiWei
+# Copyright (C) 2018-2019 Lienol
+#
+# This is free software, licensed under the GNU General Public License v3.
+# See /LICENSE for more information.
+#
+
+START=99
+STOP=10
+
+CONFIG=softethervpn
+
+get_config()
+{
+ config_get_bool enable $1 enable 0
+}
+
+uci_get_by_type() {
+ local index=0
+ if [ -n $4 ]; then
+ index=$4
+ fi
+ local ret=$(uci get $CONFIG.@$1[$index].$2 2>/dev/null)
+ echo ${ret:=$3}
+}
+
+start()
+{
+ config_load softethervpn
+ config_foreach get_config softether
+ [ $enable -ne 1 ] && exit 0
+ logger -t 'softethervpn' "Starting softether vpnserver service."
+ /usr/bin/env LANG=en_US.UTF-8 /usr/libexec/softethervpn/vpnserver start > /dev/null 2>&1
+ fw3 reload
+}
+
+stop()
+{
+ logger -t 'softethervpn' "Stopping softether vpnserver service."
+ /usr/bin/env LANG=en_US.UTF-8 /usr/libexec/softethervpn/vpnserver stop > /dev/null 2>&1
+ fw3 reload
+}
diff --git a/package/lean/luci-app-softethervpn/root/etc/uci-defaults/luci-app-softethervpn b/package/lean/luci-app-softethervpn/root/etc/uci-defaults/luci-app-softethervpn
new file mode 100755
index 0000000000..1f6e2bdf4a
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/root/etc/uci-defaults/luci-app-softethervpn
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.softethervpn
+ set firewall.softethervpn=include
+ set firewall.softethervpn.type=script
+ set firewall.softethervpn.path=/usr/share/softethervpn/firewall.include
+ set firewall.softethervpn.reload=1
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@softethervpn[-1]
+ add ucitrack softethervpn
+ set ucitrack.@softethervpn[-1].init=softethervpn
+ commit ucitrack
+EOF
+
+/etc/init.d/softethervpnbridge disable && /etc/init.d/softethervpnbridge stop
+/etc/init.d/softethervpnclient disable && /etc/init.d/softethervpnclient stop
+/etc/init.d/softethervpnserver disable && /etc/init.d/softethervpnserver stop
+
+chmod a+x /usr/share/softethervpn/* >/dev/null 2>&1
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-softethervpn/root/usr/share/softethervpn/firewall.include b/package/lean/luci-app-softethervpn/root/usr/share/softethervpn/firewall.include
new file mode 100755
index 0000000000..3c2fd6dfe6
--- /dev/null
+++ b/package/lean/luci-app-softethervpn/root/usr/share/softethervpn/firewall.include
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+openvpnport=$(cat /usr/libexec/softethervpn/vpn_server.config 2>/dev/null|grep OpenVPN_UdpPortList | awk -F " " '{print $3}')
+[ -z "$openvpnport" ] && openvpnport=1194
+iptables -D INPUT -p udp -m multiport --dports 500,1701,4500 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+[ -n "$openvpnport" ] && iptables -D INPUT -p udp --dport $openvpnport -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+[ -n "$openvpnport" ] && iptables -D INPUT -p tcp --dport $openvpnport -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+iptables -D INPUT -p tcp --dport 443 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+iptables -D INPUT -p tcp --dport 5555 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+iptables -D INPUT -p tcp --dport 8888 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+iptables -D INPUT -p tcp --dport 992 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+
+enable=$(uci get softethervpn.@softether[0].enable)
+if [ $enable -eq 1 ]; then
+ iptables -I INPUT -p udp -m multiport --dports 500,1701,4500 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT 2>/dev/null
+ iptables -I INPUT -p udp --dport $openvpnport -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+ iptables -I INPUT -p tcp --dport $openvpnport -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+ iptables -I INPUT -p tcp --dport 443 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+ iptables -I INPUT -p tcp --dport 5555 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+ iptables -I INPUT -p tcp --dport 8888 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+ iptables -I INPUT -p tcp --dport 992 -m comment --comment "Rule For SoftEther VPN Server" -j ACCEPT
+fi
diff --git a/package/lean/luci-app-ssrserver-python/Makefile b/package/lean/luci-app-ssrserver-python/Makefile
new file mode 100644
index 0000000000..7d7a317550
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI for SSR Server Python
+LUCI_DEPENDS:=+python
+LUCI_PKGARCH:=all
+PKG_VERSION:=3.2.0
+PKG_RELEASE:=6
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-ssrserver-python/luasrc/controller/ssrs.lua b/package/lean/luci-app-ssrserver-python/luasrc/controller/ssrs.lua
new file mode 100644
index 0000000000..01095ec597
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/luasrc/controller/ssrs.lua
@@ -0,0 +1,21 @@
+module("luci.controller.ssrs",package.seeall)
+
+function index()
+if not nixio.fs.access("/etc/config/ssrs")then
+ return
+end
+
+entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+
+local page
+
+entry({"admin","vpn","ssrs"},cbi("ssrs"),_("SSR Python Server"),4).dependent=true
+entry({"admin","vpn","ssrs","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("ps | grep server.py |grep -v grep >/dev/null") == 0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua b/package/lean/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua
new file mode 100644
index 0000000000..889aca8a68
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua
@@ -0,0 +1,75 @@
+local a,t,e
+local m, s
+local o=require"nixio.fs"
+local n={
+"none",
+"aes-128-ctr",
+"aes-192-ctr",
+"aes-256-ctr",
+"aes-128-cfb",
+"aes-192-cfb",
+"aes-256-cfb",
+"rc4",
+"rc4-md5",
+"rc4-md5-6",
+}
+local s={
+"origin",
+"verify_deflate",
+"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 i={
+"plain",
+"http_simple",
+"http_post",
+"random_head",
+"tls1.2_ticket_auth",
+"tls1.2_ticket_fastauth",
+}
+local o={
+"false",
+"true",
+}
+
+a= Map("ssrs", translate("ShadowSocksR Server Config"))
+a.description = translate("ShadowsocksR Python Server is a fork of the Shadowsocks project, claimed to be superior in terms of security and stability")
+
+a:section(SimpleSection).template = "ssrs/ssrs_status"
+
+t=a:section(TypedSection,"server",translate(""))
+t.anonymous=true
+t.addremove=false
+
+e=t:option(Flag,"enable",translate("Enable"))
+e.rmempty=false
+
+e=t:option(Value,"server_port",translate("Server Port"))
+e.datatype="port"
+e.rmempty=false
+e.default=139
+
+e=t:option(Value,"password",translate("Password"))
+e.password=true
+e.rmempty=false
+
+e=t:option(ListValue,"encrypt_method",translate("Encrypt Method"))
+for a,t in ipairs(n)do e:value(t)end
+e.rmempty=false
+
+e=t:option(ListValue,"protocol",translate("Protocol"))
+for a,t in ipairs(s)do e:value(t)end
+e.rmempty=false
+
+e=t:option(ListValue,"obfs",translate("Obfs"))
+for a,t in ipairs(i)do e:value(t)end
+e.rmempty=false
+
+return a
diff --git a/package/lean/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm b/package/lean/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm
new file mode 100644
index 0000000000..043620d08a
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/po/zh-cn/ssrs.po b/package/lean/luci-app-ssrserver-python/po/zh-cn/ssrs.po
new file mode 100644
index 0000000000..e3b31875f2
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/po/zh-cn/ssrs.po
@@ -0,0 +1,35 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "SSR Python Server"
+msgstr "SSR Python 服务器"
+
+msgid "ShadowSocksR Server Config"
+msgstr "ShadowsocksR Python 服务器设置"
+
+msgid "Global Setting"
+msgstr "全局设置"
+
+msgid "Encrypt Method"
+msgstr "加密"
+
+msgid "Protocol_param"
+msgstr "协议参数"
+
+msgid "Obfs"
+msgstr "混淆"
+
+msgid "Obfs_param"
+msgstr "混淆参数"
+
+msgid "redirect"
+msgstr "重定向"
+
+msgid "Connection Timeout"
+msgstr "连接超时时间"
+
+msgid "ShadowsocksR Python Server is a fork of the Shadowsocks project, claimed to be superior in terms of security and stability"
+msgstr "ShadowsocksR Python 服务器是一个增加了协议参数和混淆的全功能版本"
+
+msgid "ShadowsocksR Python Server"
+msgstr "ShadowsocksR Python 服务器"
diff --git a/package/lean/luci-app-ssrserver-python/root/etc/config/ssrs b/package/lean/luci-app-ssrserver-python/root/etc/config/ssrs
new file mode 100644
index 0000000000..a85b5ca5e1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/etc/config/ssrs
@@ -0,0 +1,11 @@
+
+config server 'ssrs'
+ option password '12345678'
+ option timeout '300'
+ option fast_open 'true'
+ option encrypt_method 'rc4-md5'
+ option protocol 'auth_sha1_v4'
+ option obfs 'http_simple'
+ option server_port '10240'
+ option enable '0'
+
diff --git a/package/lean/luci-app-ssrserver-python/root/etc/init.d/ssrs b/package/lean/luci-app-ssrserver-python/root/etc/init.d/ssrs
new file mode 100755
index 0000000000..63a473b3f3
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/etc/init.d/ssrs
@@ -0,0 +1,95 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2015 OpenWrt-dist
+#
+# This is free software, licensed under the GNU General Public License v3.
+# See /LICENSE for more information.
+#
+
+START=91
+
+CONFIG=ssrs
+CONFIG_FILE=/var/etc/$CONFIG.json
+PID_FILE=/var/run/$CONFIG.pid
+LOG_FILE=/var/log/$CONFIG.log
+
+uci_get_by_type() {
+ local index=0
+ if [ -n $4 ]; then
+ index=$4
+ fi
+ local ret=$(uci get $CONFIG.@$1[$index].$2 2>/dev/null)
+ echo ${ret:=$3}
+}
+
+is_true() {
+ case $1 in
+ 1|on|true|yes|enabled) echo 0;;
+ *) echo 1;;
+ esac
+}
+
+load_config() {
+ ENABLED=$(uci_get_by_type server enable)
+ return $(is_true $ENABLED)
+}
+
+start_server() {
+ mkdir -p /var/etc
+ /usr/bin/python \
+ /usr/share/ssr/shadowsocks/server.py \
+ -c $CONFIG_FILE \
+ --pid-file $PID_FILE \
+ --log-file $LOG_FILE \
+ -d start \
+ >/dev/null 2>&1 &
+}
+
+gen_config_file() {
+ cat <<-EOF >$CONFIG_FILE
+ {
+ "server": "0.0.0.0",
+ "server_port": $(uci_get_by_type server server_port),
+ "local_address":"127.0.0.1",
+ "local_port":1086,
+ "password": "$(uci_get_by_type server password)",
+ "timeout": $(uci_get_by_type server timeout 60),
+ "method": "$(uci_get_by_type server encrypt_method)",
+ "protocol": "$(uci_get_by_type server protocol)",
+ "protocol_param": "$(uci_get_by_type server protocol_param)",
+ "obfs": "$(uci_get_by_type server obfs)",
+ "obfs_param": "$(uci_get_by_type server obfs_param)",
+ "redirect": "$(uci_get_by_type server redirect)",
+ "fast_open": "$(uci_get_by_type server fast_open)"
+ }
+EOF
+}
+
+
+add_rule() {
+ serverport=$(uci_get_by_type server server_port)
+ uci set firewall.ssrs.dest_port=$serverport && uci commit firewall && /etc/init.d/firewall restart
+}
+
+del_rule() {
+ serverport=$(uci_get_by_type server server_port)
+}
+
+start() {
+ ! load_config && exit 0
+ gen_config_file "server"
+ start_server
+ add_rule
+}
+
+stop() {
+ del_rule
+ /usr/bin/python \
+ /usr/share/ssr/shadowsocks/server.py \
+ -c $CONFIG_FILE \
+ --pid-file $PID_FILE \
+ --log-file $LOG_FILE \
+ -d stop \
+ >/dev/null 2>&1 &
+ rm -f $LOG_FILE
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/etc/uci-defaults/luci-app-ssrs b/package/lean/luci-app-ssrserver-python/root/etc/uci-defaults/luci-app-ssrs
new file mode 100755
index 0000000000..a387590987
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/etc/uci-defaults/luci-app-ssrs
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@ssrs[-1]
+ add ucitrack ssrs
+ set ucitrack.@ssrs[-1].init=ssrs
+ commit ucitrack
+ delete firewall.ssrs
+ add firewall rule
+ rename firewall.@rule[-1]="ssrs"
+ set firewall.@rule[-1].name="ssrs"
+ set firewall.@rule[-1].target="ACCEPT"
+ set firewall.@rule[-1].src="wan"
+ set firewall.@rule[-1].proto="tcp"
+ set firewall.@rule[-1].dest_port="10240"
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml
new file mode 100644
index 0000000000..014fa07145
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml
@@ -0,0 +1,21 @@
+language: python
+python:
+ - 2.6
+ - 2.7
+ - 3.3
+ - 3.4
+cache:
+ directories:
+ - dante-1.4.0
+before_install:
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq build-essential dnsutils iproute nginx bc
+ - sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10
+ - sudo sh -c "echo '127.0.0.1 localhost' > /etc/hosts"
+ - sudo service nginx restart
+ - pip install pep8 pyflakes nose coverage PySocks cymysql
+ - sudo tests/socksify/install.sh
+ - sudo tests/libsodium/install.sh
+ - sudo tests/setup_tc.sh
+script:
+ - tests/jenkins.sh
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES
new file mode 100644
index 0000000000..0cd91768ea
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES
@@ -0,0 +1,342 @@
+3.4.0 2017-07-27
+- add auth_chain_b
+- add initmudbjson.sh
+- allow set speed limit in runtime
+- fix bugs & mem leak
+
+3.3.3 2017-06-03
+- add DNS cache
+- add tls1.2_ticket_fastauth
+- fix bugs
+
+3.3.2 2017-05-20
+- revert http reply
+- refine tls1.2_ticket_auth error detector
+
+3.3.1 2017-05-18
+- fix stop script
+- Async DNS query under UDP
+- fix old version of OpenSSL
+- http reply
+
+3.3.0 2017-05-11
+- connect_log include local addr & port
+- fix auth_chain_a UDP bug
+- add "additional_ports_only"
+- add interface legendsockssr
+- run with newest python version
+- parse comment in hosts
+- update mujson_mgr
+- add cymysql setup script
+- new speed tester
+- fix leaks
+- bugs fixed
+
+3.2.0 2017-04-27
+- add auth_chain_a
+- remove auth_aes128, auth_sha1, auth_sha1_v2, verify_simple, auth_simple, verify_sha1
+
+3.1.2 2017-04-07
+- display UID
+- auto adjust TCP MSS
+
+3.1.1 2017-03-25
+- add "New session ticket"
+- ignore bind 10.0.0.0/8 and 192.168.0.0/16 by default
+- improve rand size under auth_aes128_*
+- fix bugs
+
+3.1.0 2017-03-16
+- add "glzjinmod" interface
+- rate limit
+- add additional_ports in config
+
+3.0.4 2017-01-08
+- multi-user in single port
+
+3.0.1 2017-01-03
+- remove auth_aes128_*_compatible
+
+3.0.0 2016-12-23
+- http_simple fix bugs
+- tls1.2_ticket_auth fix bug & defaule time diff set to 86400s
+
+2.9.7 2016-11-22
+- manage client with LRUCache
+- catch bind error
+- fix import error of resource on windows
+- print RLIMIT_NOFILE
+- always close cymysql objects
+- add init script
+
+2.9.6 2016-10-17
+- tls1.2_ticket_auth random packet size
+
+2.9.5.1 2016-10-16
+- UDP bind address
+
+2.9.5 2016-10-13
+- add auth_aes128_md5 and auth_aes128_sha1
+
+2.9.4 2016-10-11
+- sync client version
+
+2.6.13 2015-11-02
+- add protocol setting
+
+2.6.12 2015-10-27
+- IPv6 first
+- Fix mem leaks
+- auth_simple plugin
+- remove FORCE_NEW_PROTOCOL
+- optimize code
+
+2.6.11 2015-10-20
+- Obfs plugin
+- Obfs parameters
+- UDP over TCP
+- TCP over UDP (experimental)
+- Fix socket leaks
+- Catch abnormal UDP package
+
+2.6.10 2015-06-08
+- Optimize LRU cache
+- Refine logging
+
+2.6.9 2015-05-19
+- Fix a stability issue on Windows
+
+2.6.8 2015-02-10
+- Support multiple server ip on client side
+- Support --version
+- Minor fixes
+
+2.6.7 2015-02-02
+- Support --user
+- Support CIDR format in --forbidden-ip
+- Minor fixes
+
+2.6.6 2015-01-23
+- Fix a crash in forbidden list
+
+2.6.5 2015-01-18
+- Try both 32 bit and 64 bit dll on Windows
+
+2.6.4 2015-01-14
+- Also search lib* when searching libraries
+
+2.6.3 2015-01-12
+- Support --forbidden-ip to ban some IP, i.e. localhost
+- Search OpenSSL and libsodium harder
+- Now works on OpenWRT
+
+2.6.2 2015-01-03
+- Log client IP
+
+2.6.1 2014-12-26
+- Fix a problem with TCP Fast Open on local side
+- Fix sometimes daemon_start returns wrong exit status
+
+2.6 2014-12-21
+- Add daemon support
+
+2.5 2014-12-11
+- Add salsa20 and chacha20
+
+2.4.3 2014-11-10
+- Fix an issue on Python 3
+- Fix an issue with IPv6
+
+2.4.2 2014-11-06
+- Fix command line arguments on Python 3
+- Support table on Python 3
+- Fix TCP Fast Open on Python 3
+
+2.4.1 2014-11-01
+- Fix setup.py for non-utf8 locales on Python 3
+
+2.4 2014-11-01
+- Python 3 support
+- Performance improvement
+- Fix LRU cache behavior
+
+2.3.2 2014-10-11
+- Fix OpenSSL on Windows
+
+2.3.1 2014-10-09
+- Does not require M2Crypto any more
+
+2.3 2014-09-23
+- Support CFB1, CFB8 and CTR mode of AES
+- Do not require password config when using port_password
+- Use SIGTERM instead of SIGQUIT on Windows
+
+2.2.2 2014-09-14
+- Fix when multiple DNS set, IPv6 only sites are broken
+
+2.2.1 2014-09-10
+- Support graceful shutdown
+- Fix some bugs
+
+2.2.0 2014-09-09
+- Add RC4-MD5 encryption
+
+2.1.0 2014-08-10
+- Use only IPv4 DNS server
+- Does not ship config.json
+- Better error message
+
+2.0.12 2014-07-26
+- Support -q quiet mode
+- Exit 0 when showing help with -h
+
+2.0.11 2014-07-12
+- Prefers IP addresses over hostnames, more friendly with socksify and openvpn
+
+2.0.10 2014-07-11
+- Fix UDP on local
+
+2.0.9 2014-07-06
+- Fix EWOULDBLOCK on Windows
+- Fix Unicode config problem on some platforms
+
+2.0.8 2014-06-23
+- Use multiple DNS to query hostnames
+
+2.0.7 2014-06-21
+- Fix fastopen on local
+- Fallback when fastopen is not available
+- Add verbose logging mode -vv
+- Verify if hostname is valid
+
+2.0.6 2014-06-19
+- Fix CPU 100% on POLL_HUP
+- More friendly logging
+
+2.0.5 2014-06-18
+- Support a simple config format for multiple ports
+
+2.0.4 2014-06-12
+- Fix worker master
+
+2.0.3 2014-06-11
+- Fix table encryption with UDP
+
+2.0.2 2014-06-11
+- Add asynchronous DNS in TCP relay
+
+2.0.1 2014-06-05
+- Better logging
+- Maybe fix bad file descriptor
+
+2.0 2014-06-05
+- Use a new event model
+- Remove gevent
+- Refuse to use default password
+- Fix a problem when using multiple passwords with table encryption
+
+1.4.5 2014-05-24
+- Add timeout in TCP server
+- Close sockets in master process
+
+1.4.4 2014-05-17
+- Support multiple workers
+
+1.4.3 2014-05-13
+- Fix Windows
+
+1.4.2 2014-05-10
+- Add salsa20-ctr cipher
+
+1.4.1 2014-05-03
+- Fix error log
+- Fix EINPROGESS with some version of gevent
+
+1.4.0 2014-05-02
+- Adds UDP relay
+- TCP fast open support on Linux 3.7+
+
+1.3.7 2014-04-10
+- Fix a typo in help
+
+1.3.6 2014-04-10
+- Fix a typo in help
+
+1.3.5 2014-04-07
+- Add help
+- Change default local binding address into 127.0.0.1
+
+1.3.4 2014-02-17
+- Fix a bug when no config file exists
+- Client now support multiple server ports and multiple server/port pairs
+- Better error message with bad config.json format and wrong password
+
+1.3.3 2013-07-09
+- Fix default key length of rc2
+
+1.3.2 2013-07-04
+- Server will listen at server IP specified in config
+- Check config file and show some warning messages
+
+1.3.1 2013-06-29
+- Fix -c arg
+
+1.3.0 2013-06-22
+- Move to pypi
+
+1.2.3 2013-06-14
+- add bind address
+
+1.2.2 2013-05-31
+- local can listen at ::0 with -6 arg; bump 1.2.2
+
+1.2.1 2013-05-23
+- Fix an OpenSSL crash
+
+1.2 2013-05-22
+- Use random iv, we finally have strong encryption
+
+1.1.1 2013-05-21
+- Add encryption, AES, blowfish, etc.
+
+1.1 2013-05-16
+- Support IPv6 addresses (type 4)
+- Drop Python 2.5 support
+
+1.0 2013-04-03
+- Fix -6 IPv6
+
+0.9.4 2013-03-04
+- Support Python 2.5
+
+0.9.3 2013-01-14
+- Fix conn termination null data
+
+0.9.2 2013-01-05
+- Change default timeout
+
+0.9.1 2013-01-05
+- Add Travis-CI test
+
+0.9 2012-12-30
+- Replace send with sendall, fix FreeBSD
+
+0.6 2012-12-06
+- Support args
+
+0.5 2012-11-08
+- Fix encryption with negative md5sum
+
+0.4 2012-11-02
+- Move config into a JSON file
+- Auto-detect config path
+
+0.3 2012-06-06
+- Move socks5 negotiation to local
+
+0.2 2012-05-11
+- Add -6 arg for IPv6
+- Fix socket.error
+
+0.1 2012-04-20
+- Initial version
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md
new file mode 100644
index 0000000000..fbdb9c11b2
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+How to Contribute
+=================
+
+Pull Requests
+-------------
+
+1. Pull requests are welcome. If you would like to add a large feature
+or make a significant change, make sure to open an issue to discuss with
+people first.
+2. Follow PEP8.
+3. Make sure to pass the unit tests. Write unit tests for new modules if
+needed.
+
+Issues
+------
+
+1. Only bugs and feature requests are accepted here.
+2. We'll only work on important features. If the feature you're asking only
+benefits a few people, you'd better implement the feature yourself and send us
+a pull request, or ask some of your friends to do so.
+3. We don't answer questions of any other types here. Since very few people
+are watching the issue tracker here, you'll probably get no help from here.
+Read [Troubleshooting] and get help from forums or [mailing lists].
+4. Issues in languages other than English will be Google translated into English
+later.
+
+
+[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
+[mailing lists]: https://groups.google.com/forum/#!forum/shadowsocks
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile
new file mode 100644
index 0000000000..ec7b2c6e71
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile
@@ -0,0 +1,31 @@
+FROM alpine:3.6
+
+ENV SERVER_ADDR 0.0.0.0
+ENV SERVER_PORT 51348
+ENV PASSWORD psw
+ENV METHOD aes-128-ctr
+ENV PROTOCOL auth_aes128_md5
+ENV PROTOCOLPARAM 32
+ENV OBFS tls1.2_ticket_auth_compatible
+ENV TIMEOUT 300
+ENV DNS_ADDR 8.8.8.8
+ENV DNS_ADDR_2 8.8.4.4
+
+ARG BRANCH=manyuser
+ARG WORK=~
+
+
+RUN apk --no-cache add python \
+ libsodium \
+ wget
+
+
+RUN mkdir -p $WORK && \
+ wget -qO- --no-check-certificate https://github.com/shadowsocksr/shadowsocksr/archive/$BRANCH.tar.gz | tar -xzf - -C $WORK
+
+
+WORKDIR $WORK/shadowsocksr-$BRANCH/shadowsocks
+
+
+EXPOSE $SERVER_PORT
+CMD python server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS -G $PROTOCOLPARAM
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in
new file mode 100644
index 0000000000..1882dd7dce
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in
@@ -0,0 +1,3 @@
+recursive-include shadowsocks *.py
+include README.rst
+include LICENSE
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.md b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.md
new file mode 100644
index 0000000000..53eafeaa8e
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.md
@@ -0,0 +1,105 @@
+ShadowsocksR
+===========
+
+[![Build Status]][Travis CI]
+
+A fast tunnel proxy that helps you bypass firewalls.
+
+Server
+------
+
+### Install
+
+Debian / Ubuntu:
+
+ apt-get install git
+ git clone https://github.com/shadowsocksr/shadowsocksr.git
+
+CentOS:
+
+ yum install git
+ git clone https://github.com/shadowsocksr/shadowsocksr.git
+
+Windows:
+
+ git clone https://github.com/shadowsocksr/shadowsocksr.git
+
+### Usage for single user on linux platform
+
+If you clone it into "~/shadowsocksr"
+move to "~/shadowsocksr", then run:
+
+ bash initcfg.sh
+
+move to "~/shadowsocksr/shadowsocks", then run:
+
+ python server.py -p 443 -k password -m aes-128-cfb -O auth_aes128_md5 -o tls1.2_ticket_auth_compatible
+
+Check all the options via `-h`.
+
+You can also use a configuration file instead (recommend), move to "~/shadowsocksr" and edit the file "user-config.json", then move to "~/shadowsocksr/shadowsocks" again, just run:
+
+ python server.py
+
+To run in the background:
+
+ ./logrun.sh
+
+To stop:
+
+ ./stop.sh
+
+To monitor the log:
+
+ ./tail.sh
+
+
+Client
+------
+
+* [Windows] / [macOS]
+* [Android] / [iOS]
+* [OpenWRT]
+
+Use GUI clients on your local PC/phones. Check the README of your client
+for more information.
+
+Documentation
+-------------
+
+You can find all the documentation in the [Wiki].
+
+License
+-------
+
+Copyright 2015 clowwindy
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations
+under the License.
+
+Bugs and Issues
+----------------
+
+* [Issue Tracker]
+
+
+
+[Android]: https://github.com/shadowsocksr/shadowsocksr-android
+[Build Status]: https://travis-ci.org/shadowsocksr/shadowsocksr.svg?branch=manyuser
+[Debian sid]: https://packages.debian.org/unstable/python/shadowsocks
+[iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help
+[Issue Tracker]: https://github.com/shadowsocksr/shadowsocksr/issues?state=open
+[OpenWRT]: https://github.com/shadowsocks/openwrt-shadowsocks
+[macOS]: https://github.com/shadowsocksr/ShadowsocksX-NG
+[Travis CI]: https://travis-ci.org/shadowsocksr/shadowsocksr
+[Windows]: https://github.com/shadowsocksr/shadowsocksr-csharp
+[Wiki]: https://github.com/breakwa11/shadowsocks-rss/wiki
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.rst b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.rst
new file mode 100755
index 0000000000..bf2a3ec32d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/README.rst
@@ -0,0 +1,113 @@
+shadowsocks
+===========
+
+|PyPI version| |Build Status| |Coverage Status|
+
+A fast tunnel proxy that helps you bypass firewalls.
+
+Server
+------
+
+Install
+~~~~~~~
+
+Debian / Ubuntu:
+
+::
+
+ apt-get install python-pip
+ pip install shadowsocks
+
+CentOS:
+
+::
+
+ yum install python-setuptools && easy_install pip
+ pip install shadowsocks
+
+Windows:
+
+See `Install Server on
+Windows `__
+
+Usage
+~~~~~
+
+::
+
+ ssserver -p 443 -k password -m rc4-md5
+
+To run in the background:
+
+::
+
+ sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start
+
+To stop:
+
+::
+
+ sudo ssserver -d stop
+
+To check the log:
+
+::
+
+ sudo less /var/log/shadowsocks.log
+
+Check all the options via ``-h``. You can also use a
+`Configuration `__
+file instead.
+
+Client
+------
+
+- `Windows `__
+ / `OS
+ X `__
+- `Android `__
+ / `iOS `__
+- `OpenWRT `__
+
+Use GUI clients on your local PC/phones. Check the README of your client
+for more information.
+
+Documentation
+-------------
+
+You can find all the documentation in the
+`Wiki `__.
+
+License
+-------
+
+Copyright 2015 clowwindy
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+::
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Bugs and Issues
+---------------
+
+- `Troubleshooting `__
+- `Issue
+ Tracker `__
+- `Mailing list `__
+
+.. |PyPI version| image:: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat
+ :target: https://pypi.python.org/pypi/shadowsocks
+.. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat
+ :target: https://travis-ci.org/shadowsocks/shadowsocks
+.. |Coverage Status| image:: https://jenkins.shadowvpn.org/result/shadowsocks
+ :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux/htmlcov/index.html
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py
new file mode 100644
index 0000000000..5ee8be127f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py
@@ -0,0 +1,15 @@
+# Config
+API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, glzjinmod, legendsockssr, muapiv2(not support)
+UPDATE_TIME = 60
+SERVER_PUB_ADDR = '127.0.0.1' # mujson_mgr need this to generate ssr link
+
+#mudb
+MUDB_FILE = 'mudb.json'
+
+# Mysql
+MYSQL_CONFIG = 'usermysql.json'
+
+# API
+MUAPI_CONFIG = 'usermuapi.json'
+
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py
new file mode 100644
index 0000000000..9bf4d09360
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2014 clowwindy
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import time
+import os
+import socket
+import struct
+import re
+import logging
+from shadowsocks import common
+from shadowsocks import lru_cache
+from shadowsocks import eventloop
+import server_pool
+import Config
+
+class ServerMgr(object):
+
+ def __init__(self):
+ self._loop = None
+ self._request_id = 1
+ self._hosts = {}
+ self._hostname_status = {}
+ self._hostname_to_cb = {}
+ self._cb_to_hostname = {}
+ self._last_time = time.time()
+ self._sock = None
+ self._servers = None
+
+ def add_to_loop(self, loop):
+ if self._loop:
+ raise Exception('already add to loop')
+ self._loop = loop
+ # TODO when dns server is IPv6
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ self._sock.bind((Config.MANAGE_BIND_IP, Config.MANAGE_PORT))
+ self._sock.setblocking(False)
+ loop.add(self._sock, eventloop.POLL_IN, self)
+
+ def _handle_data(self, sock):
+ data, addr = sock.recvfrom(128)
+ #manage pwd:port:passwd:action
+ args = data.split(':')
+ if len(args) < 4:
+ return
+ if args[0] == Config.MANAGE_PASS:
+ if args[3] == '0':
+ server_pool.ServerPool.get_instance().cb_del_server(args[1])
+ elif args[3] == '1':
+ server_pool.ServerPool.get_instance().new_server(args[1], args[2])
+
+ def handle_event(self, sock, fd, event):
+ if sock != self._sock:
+ return
+ if event & eventloop.POLL_ERR:
+ logging.error('mgr socket err')
+ self._loop.remove(self._sock)
+ self._sock.close()
+ # TODO when dns server is IPv6
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ self._sock.setblocking(False)
+ self._loop.add(self._sock, eventloop.POLL_IN, self)
+ else:
+ self._handle_data(sock)
+
+ def close(self):
+ if self._sock:
+ if self._loop:
+ self._loop.remove(self._sock)
+ self._sock.close()
+ self._sock = None
+
+
+def test():
+ pass
+
+if __name__ == '__main__':
+ test()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/config.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/config.json
new file mode 100644
index 0000000000..55f12b5f4c
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/config.json
@@ -0,0 +1,25 @@
+{
+ "server": "0.0.0.0",
+ "server_ipv6": "::",
+ "server_port": 8388,
+ "local_address": "127.0.0.1",
+ "local_port": 1080,
+
+ "password": "m",
+ "method": "aes-128-ctr",
+ "protocol": "auth_aes128_md5",
+ "protocol_param": "",
+ "obfs": "tls1.2_ticket_auth_compatible",
+ "obfs_param": "",
+ "speed_limit_per_con": 0,
+ "speed_limit_per_user": 0,
+
+ "additional_ports" : {}, // only works under multi-user mode
+ "additional_ports_only" : false, // only works under multi-user mode
+ "timeout": 120,
+ "udp_timeout": 60,
+ "dns_ipv6": false,
+ "connect_verbose_info": 0,
+ "redirect": "",
+ "fast_open": false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py
new file mode 100644
index 0000000000..cf9d61961b
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+import importloader
+
+g_config = None
+
+def load_config():
+ global g_config
+ g_config = importloader.loads(['userapiconfig', 'apiconfig'])
+
+def get_config():
+ return g_config
+
+load_config()
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py
new file mode 100644
index 0000000000..67bda60834
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py
@@ -0,0 +1,631 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+
+import logging
+import time
+import sys
+from server_pool import ServerPool
+import traceback
+from shadowsocks import common, shell, lru_cache, obfs
+from configloader import load_config, get_config
+import importloader
+
+switchrule = None
+db_instance = None
+
+class TransferBase(object):
+ def __init__(self):
+ import threading
+ self.event = threading.Event()
+ self.key_list = ['port', 'u', 'd', 'transfer_enable', 'passwd', 'enable']
+ self.last_get_transfer = {} #上一次的实际流量
+ self.last_update_transfer = {} #上一次更新到的流量(小于等于实际流量)
+ self.force_update_transfer = set() #强制推入数据库的ID
+ self.port_uid_table = {} #端口到uid的映射(仅v3以上有用)
+ self.onlineuser_cache = lru_cache.LRUCache(timeout=60*30) #用户在线状态记录
+ self.pull_ok = False #记录是否已经拉出过数据
+ self.mu_ports = {}
+
+ def load_cfg(self):
+ pass
+
+ def push_db_all_user(self):
+ if self.pull_ok is False:
+ return
+ #更新用户流量到数据库
+ last_transfer = self.last_update_transfer
+ curr_transfer = ServerPool.get_instance().get_servers_transfer()
+ #上次和本次的增量
+ dt_transfer = {}
+ for id in self.force_update_transfer: #此表中的用户统计上次未计入的流量
+ if id in self.last_get_transfer and id in last_transfer:
+ dt_transfer[id] = [self.last_get_transfer[id][0] - last_transfer[id][0], self.last_get_transfer[id][1] - last_transfer[id][1]]
+
+ for id in curr_transfer.keys():
+ if id in self.force_update_transfer or id in self.mu_ports:
+ continue
+ #算出与上次记录的流量差值,保存于dt_transfer表
+ if id in last_transfer:
+ if curr_transfer[id][0] + curr_transfer[id][1] - last_transfer[id][0] - last_transfer[id][1] <= 0:
+ continue
+ dt_transfer[id] = [curr_transfer[id][0] - last_transfer[id][0],
+ curr_transfer[id][1] - last_transfer[id][1]]
+ else:
+ if curr_transfer[id][0] + curr_transfer[id][1] <= 0:
+ continue
+ dt_transfer[id] = [curr_transfer[id][0], curr_transfer[id][1]]
+
+ #有流量的,先记录在线状态
+ if id in self.last_get_transfer:
+ if curr_transfer[id][0] + curr_transfer[id][1] > self.last_get_transfer[id][0] + self.last_get_transfer[id][1]:
+ self.onlineuser_cache[id] = curr_transfer[id][0] + curr_transfer[id][1]
+ else:
+ self.onlineuser_cache[id] = curr_transfer[id][0] + curr_transfer[id][1]
+
+ self.onlineuser_cache.sweep()
+
+ update_transfer = self.update_all_user(dt_transfer) #返回有更新的表
+ for id in update_transfer.keys(): #其增量加在此表
+ if id not in self.force_update_transfer: #但排除在force_update_transfer内的
+ last = self.last_update_transfer.get(id, [0,0])
+ self.last_update_transfer[id] = [last[0] + update_transfer[id][0], last[1] + update_transfer[id][1]]
+ self.last_get_transfer = curr_transfer
+ for id in self.force_update_transfer:
+ if id in self.last_update_transfer:
+ del self.last_update_transfer[id]
+ if id in self.last_get_transfer:
+ del self.last_get_transfer[id]
+ self.force_update_transfer = set()
+
+ def del_server_out_of_bound_safe(self, last_rows, rows):
+ #停止超流量的服务
+ #启动没超流量的服务
+ try:
+ switchrule = importloader.load('switchrule')
+ except Exception as e:
+ logging.error('load switchrule.py fail')
+ cur_servers = {}
+ new_servers = {}
+ allow_users = {}
+ mu_servers = {}
+ config = shell.get_config(False)
+ for row in rows:
+ try:
+ allow = switchrule.isTurnOn(row) and row['enable'] == 1 and row['u'] + row['d'] < row['transfer_enable']
+ except Exception as e:
+ allow = False
+
+ port = row['port']
+ passwd = common.to_bytes(row['passwd'])
+ if hasattr(passwd, 'encode'):
+ passwd = passwd.encode('utf-8')
+ cfg = {'password': passwd}
+ if 'id' in row:
+ self.port_uid_table[row['port']] = row['id']
+
+ read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port', 'speed_limit_per_con', 'speed_limit_per_user']
+ for name in read_config_keys:
+ if name in row and row[name]:
+ cfg[name] = row[name]
+
+ merge_config_keys = ['password'] + read_config_keys
+ for name in cfg.keys():
+ if hasattr(cfg[name], 'encode'):
+ try:
+ cfg[name] = cfg[name].encode('utf-8')
+ except Exception as e:
+ logging.warning('encode cfg key "%s" fail, val "%s"' % (name, cfg[name]))
+
+ if port not in cur_servers:
+ cur_servers[port] = passwd
+ else:
+ logging.error('more than one user use the same port [%s]' % (port,))
+ continue
+
+ if 'protocol' in cfg and 'protocol_param' in cfg and common.to_str(cfg['protocol']) in obfs.mu_protocol():
+ if '#' in common.to_str(cfg['protocol_param']):
+ mu_servers[port] = passwd
+ allow = True
+
+ if allow:
+ if port not in mu_servers:
+ allow_users[port] = cfg
+
+ cfgchange = False
+ if port in ServerPool.get_instance().tcp_servers_pool:
+ relay = ServerPool.get_instance().tcp_servers_pool[port]
+ for name in merge_config_keys:
+ if name in cfg and not self.cmp(cfg[name], relay._config[name]):
+ cfgchange = True
+ break
+ if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
+ relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
+ for name in merge_config_keys:
+ if (name in cfg) and ((name not in relay._config) or not self.cmp(cfg[name], relay._config[name])):
+ cfgchange = True
+ break
+
+ if port in mu_servers:
+ if ServerPool.get_instance().server_is_run(port) > 0:
+ if cfgchange:
+ logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
+ ServerPool.get_instance().cb_del_server(port)
+ self.force_update_transfer.add(port)
+ new_servers[port] = (passwd, cfg)
+ else:
+ self.new_server(port, passwd, cfg)
+ else:
+ if ServerPool.get_instance().server_is_run(port) > 0:
+ if config['additional_ports_only'] or not allow:
+ logging.info('db stop server at port [%s]' % (port,))
+ ServerPool.get_instance().cb_del_server(port)
+ self.force_update_transfer.add(port)
+ else:
+ if cfgchange:
+ logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
+ ServerPool.get_instance().cb_del_server(port)
+ self.force_update_transfer.add(port)
+ new_servers[port] = (passwd, cfg)
+
+ elif not config['additional_ports_only'] and allow and port > 0 and port < 65536 and ServerPool.get_instance().server_run_status(port) is False:
+ self.new_server(port, passwd, cfg)
+
+ for row in last_rows:
+ if row['port'] in cur_servers:
+ pass
+ else:
+ logging.info('db stop server at port [%s] reason: port not exist' % (row['port']))
+ ServerPool.get_instance().cb_del_server(row['port'])
+ self.clear_cache(row['port'])
+ if row['port'] in self.port_uid_table:
+ del self.port_uid_table[row['port']]
+
+ if len(new_servers) > 0:
+ from shadowsocks import eventloop
+ self.event.wait(eventloop.TIMEOUT_PRECISION + eventloop.TIMEOUT_PRECISION / 2)
+ for port in new_servers.keys():
+ passwd, cfg = new_servers[port]
+ self.new_server(port, passwd, cfg)
+
+ logging.debug('db allow users %s \nmu_servers %s' % (allow_users, mu_servers))
+ for port in mu_servers:
+ ServerPool.get_instance().update_mu_users(port, allow_users)
+
+ self.mu_ports = mu_servers
+
+ def clear_cache(self, port):
+ if port in self.force_update_transfer: del self.force_update_transfer[port]
+ if port in self.last_get_transfer: del self.last_get_transfer[port]
+ if port in self.last_update_transfer: del self.last_update_transfer[port]
+
+ def new_server(self, port, passwd, cfg):
+ protocol = cfg.get('protocol', ServerPool.get_instance().config.get('protocol', 'origin'))
+ method = cfg.get('method', ServerPool.get_instance().config.get('method', 'None'))
+ obfs = cfg.get('obfs', ServerPool.get_instance().config.get('obfs', 'plain'))
+ logging.info('db start server at port [%s] pass [%s] protocol [%s] method [%s] obfs [%s]' % (port, passwd, protocol, method, obfs))
+ ServerPool.get_instance().new_server(port, cfg)
+
+ def cmp(self, val1, val2):
+ if type(val1) is bytes:
+ val1 = common.to_str(val1)
+ if type(val2) is bytes:
+ val2 = common.to_str(val2)
+ return val1 == val2
+
+ @staticmethod
+ def del_servers():
+ for port in [v for v in ServerPool.get_instance().tcp_servers_pool.keys()]:
+ if ServerPool.get_instance().server_is_run(port) > 0:
+ ServerPool.get_instance().cb_del_server(port)
+ for port in [v for v in ServerPool.get_instance().tcp_ipv6_servers_pool.keys()]:
+ if ServerPool.get_instance().server_is_run(port) > 0:
+ ServerPool.get_instance().cb_del_server(port)
+
+ @staticmethod
+ def thread_db(obj):
+ import socket
+ import time
+ global db_instance
+ timeout = 60
+ socket.setdefaulttimeout(timeout)
+ last_rows = []
+ db_instance = obj()
+ ServerPool.get_instance()
+ shell.log_shadowsocks_version()
+
+ try:
+ import resource
+ logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
+ except:
+ pass
+
+ try:
+ while True:
+ load_config()
+ db_instance.load_cfg()
+ try:
+ db_instance.push_db_all_user()
+ rows = db_instance.pull_db_all_user()
+ if rows:
+ db_instance.pull_ok = True
+ config = shell.get_config(False)
+ for port in config['additional_ports']:
+ val = config['additional_ports'][port]
+ val['port'] = int(port)
+ val['enable'] = 1
+ val['transfer_enable'] = 1024 ** 7
+ val['u'] = 0
+ val['d'] = 0
+ if "password" in val:
+ val["passwd"] = val["password"]
+ rows.append(val)
+ db_instance.del_server_out_of_bound_safe(last_rows, rows)
+ last_rows = rows
+ except Exception as e:
+ trace = traceback.format_exc()
+ logging.error(trace)
+ #logging.warn('db thread except:%s' % e)
+ if db_instance.event.wait(get_config().UPDATE_TIME) or not ServerPool.get_instance().thread.is_alive():
+ break
+ except KeyboardInterrupt as e:
+ pass
+ db_instance.del_servers()
+ ServerPool.get_instance().stop()
+ db_instance = None
+
+ @staticmethod
+ def thread_db_stop():
+ global db_instance
+ db_instance.event.set()
+
+class DbTransfer(TransferBase):
+ def __init__(self):
+ super(DbTransfer, self).__init__()
+ self.user_pass = {} #记录更新此用户流量时被跳过多少次
+ self.cfg = {
+ "host": "127.0.0.1",
+ "port": 3306,
+ "user": "ss",
+ "password": "pass",
+ "db": "shadowsocks",
+ "node_id": 0,
+ "transfer_mul": 1.0,
+ "ssl_enable": 0,
+ "ssl_ca": "",
+ "ssl_cert": "",
+ "ssl_key": ""}
+ self.load_cfg()
+
+ def load_cfg(self):
+ import json
+ config_path = get_config().MYSQL_CONFIG
+ cfg = None
+ with open(config_path, 'rb+') as f:
+ cfg = json.loads(f.read().decode('utf8'))
+
+ if cfg:
+ self.cfg.update(cfg)
+
+ def update_all_user(self, dt_transfer):
+ import cymysql
+ update_transfer = {}
+
+ query_head = 'UPDATE user'
+ query_sub_when = ''
+ query_sub_when2 = ''
+ query_sub_in = None
+ last_time = time.time()
+
+ for id in dt_transfer.keys():
+ transfer = dt_transfer[id]
+ #小于最低更新流量的先不更新
+ update_trs = 1024 * (2048 - self.user_pass.get(id, 0) * 64)
+ if transfer[0] + transfer[1] < update_trs and id not in self.force_update_transfer:
+ self.user_pass[id] = self.user_pass.get(id, 0) + 1
+ continue
+ if id in self.user_pass:
+ del self.user_pass[id]
+
+ query_sub_when += ' WHEN %s THEN u+%s' % (id, int(transfer[0] * self.cfg["transfer_mul"]))
+ query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
+ update_transfer[id] = transfer
+
+ if query_sub_in is not None:
+ query_sub_in += ',%s' % id
+ else:
+ query_sub_in = '%s' % id
+
+ if query_sub_when == '':
+ return update_transfer
+ query_sql = query_head + ' SET u = CASE port' + query_sub_when + \
+ ' END, d = CASE port' + query_sub_when2 + \
+ ' END, t = ' + str(int(last_time)) + \
+ ' WHERE port IN (%s)' % query_sub_in
+ if self.cfg["ssl_enable"] == 1:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8',
+ ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
+ else:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8')
+
+ try:
+ cur = conn.cursor()
+ try:
+ cur.execute(query_sql)
+ except Exception as e:
+ logging.error(e)
+ update_transfer = {}
+
+ cur.close()
+ conn.commit()
+ except Exception as e:
+ logging.error(e)
+ update_transfer = {}
+ finally:
+ conn.close()
+
+ return update_transfer
+
+ def pull_db_all_user(self):
+ import cymysql
+ #数据库所有用户信息
+ if self.cfg["ssl_enable"] == 1:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8',
+ ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
+ else:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8')
+
+ try:
+ rows = self.pull_db_users(conn)
+ finally:
+ conn.close()
+
+ if not rows:
+ logging.warn('no user in db')
+ return rows
+
+ def pull_db_users(self, conn):
+ try:
+ switchrule = importloader.load('switchrule')
+ keys = switchrule.getKeys(self.key_list)
+ except Exception as e:
+ keys = self.key_list
+
+ cur = conn.cursor()
+ cur.execute("SELECT " + ','.join(keys) + " FROM user")
+ rows = []
+ for r in cur.fetchall():
+ d = {}
+ for column in range(len(keys)):
+ d[keys[column]] = r[column]
+ rows.append(d)
+ cur.close()
+ return rows
+
+class Dbv3Transfer(DbTransfer):
+ def __init__(self):
+ super(Dbv3Transfer, self).__init__()
+ self.update_node_state = True if get_config().API_INTERFACE != 'legendsockssr' else False
+ if self.update_node_state:
+ self.key_list += ['id']
+ self.key_list += ['method']
+ if self.update_node_state:
+ self.ss_node_info_name = 'ss_node_info_log'
+ if get_config().API_INTERFACE == 'sspanelv3ssr':
+ self.key_list += ['obfs', 'protocol']
+ if get_config().API_INTERFACE == 'glzjinmod':
+ self.key_list += ['obfs', 'protocol']
+ self.ss_node_info_name = 'ss_node_info'
+ else:
+ self.key_list += ['obfs', 'protocol']
+ self.start_time = time.time()
+
+ def update_all_user(self, dt_transfer):
+ import cymysql
+ update_transfer = {}
+
+ query_head = 'UPDATE user'
+ query_sub_when = ''
+ query_sub_when2 = ''
+ query_sub_in = None
+ last_time = time.time()
+
+ alive_user_count = len(self.onlineuser_cache)
+ bandwidth_thistime = 0
+
+ if self.cfg["ssl_enable"] == 1:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8',
+ ssl={'ca':self.cfg["ssl_ca"],'cert':self.cfg["ssl_cert"],'key':self.cfg["ssl_key"]})
+ else:
+ conn = cymysql.connect(host=self.cfg["host"], port=self.cfg["port"],
+ user=self.cfg["user"], passwd=self.cfg["password"],
+ db=self.cfg["db"], charset='utf8')
+ conn.autocommit(True)
+
+ for id in dt_transfer.keys():
+ transfer = dt_transfer[id]
+ bandwidth_thistime = bandwidth_thistime + transfer[0] + transfer[1]
+
+ update_trs = 1024 * (2048 - self.user_pass.get(id, 0) * 64)
+ if transfer[0] + transfer[1] < update_trs:
+ self.user_pass[id] = self.user_pass.get(id, 0) + 1
+ continue
+ if id in self.user_pass:
+ del self.user_pass[id]
+
+ query_sub_when += ' WHEN %s THEN u+%s' % (id, int(transfer[0] * self.cfg["transfer_mul"]))
+ query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
+ update_transfer[id] = transfer
+
+ if self.update_node_state:
+ cur = conn.cursor()
+ try:
+ if id in self.port_uid_table:
+ cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
+ str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
+ str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
+ self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
+ except:
+ logging.warn('no `user_traffic_log` in db')
+ cur.close()
+
+ if query_sub_in is not None:
+ query_sub_in += ',%s' % id
+ else:
+ query_sub_in = '%s' % id
+
+ if query_sub_when != '':
+ query_sql = query_head + ' SET u = CASE port' + query_sub_when + \
+ ' END, d = CASE port' + query_sub_when2 + \
+ ' END, t = ' + str(int(last_time)) + \
+ ' WHERE port IN (%s)' % query_sub_in
+ cur = conn.cursor()
+ try:
+ cur.execute(query_sql)
+ except Exception as e:
+ logging.error(e)
+ cur.close()
+
+ if self.update_node_state:
+ try:
+ cur = conn.cursor()
+ try:
+ cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
+ str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
+ except Exception as e:
+ logging.error(e)
+ cur.close()
+
+ cur = conn.cursor()
+ try:
+ cur.execute("INSERT INTO `" + self.ss_node_info_name + "` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
+ str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
+ str(self.load()) + "', unix_timestamp()); ")
+ except Exception as e:
+ logging.error(e)
+ cur.close()
+ except:
+ logging.warn('no `ss_node_online_log` or `" + self.ss_node_info_name + "` in db')
+
+ conn.close()
+ return update_transfer
+
+ def pull_db_users(self, conn):
+ try:
+ switchrule = importloader.load('switchrule')
+ keys = switchrule.getKeys(self.key_list)
+ except Exception as e:
+ keys = self.key_list
+
+ cur = conn.cursor()
+
+ if self.update_node_state:
+ node_info_keys = ['traffic_rate']
+ try:
+ cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
+ nodeinfo = cur.fetchone()
+ except Exception as e:
+ logging.error(e)
+ nodeinfo = None
+
+ if nodeinfo == None:
+ rows = []
+ cur.close()
+ conn.commit()
+ logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
+ return rows
+ cur.close()
+
+ node_info_dict = {}
+ for column in range(len(nodeinfo)):
+ node_info_dict[node_info_keys[column]] = nodeinfo[column]
+ self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
+
+ cur = conn.cursor()
+ try:
+ rows = []
+ cur.execute("SELECT " + ','.join(keys) + " FROM user")
+ for r in cur.fetchall():
+ d = {}
+ for column in range(len(keys)):
+ d[keys[column]] = r[column]
+ rows.append(d)
+ except Exception as e:
+ logging.error(e)
+ cur.close()
+ return rows
+
+ def load(self):
+ import os
+ return os.popen("cat /proc/loadavg | awk '{ print $1\" \"$2\" \"$3 }'").readlines()[0]
+
+ def uptime(self):
+ return time.time() - self.start_time
+
+ def traffic_format(self, traffic):
+ if traffic < 1024 * 8:
+ return str(int(traffic)) + "B";
+
+ if traffic < 1024 * 1024 * 2:
+ return str(round((traffic / 1024.0), 2)) + "KB";
+
+ return str(round((traffic / 1048576.0), 2)) + "MB";
+
+class MuJsonTransfer(TransferBase):
+ def __init__(self):
+ super(MuJsonTransfer, self).__init__()
+
+ def update_all_user(self, dt_transfer):
+ import json
+ rows = None
+
+ config_path = get_config().MUDB_FILE
+ with open(config_path, 'rb+') as f:
+ rows = json.loads(f.read().decode('utf8'))
+ for row in rows:
+ if "port" in row:
+ port = row["port"]
+ if port in dt_transfer:
+ row["u"] += dt_transfer[port][0]
+ row["d"] += dt_transfer[port][1]
+
+ if rows:
+ output = json.dumps(rows, sort_keys=True, indent=4, separators=(',', ': '))
+ with open(config_path, 'r+') as f:
+ f.write(output)
+ f.truncate()
+
+ return dt_transfer
+
+ def pull_db_all_user(self):
+ import json
+ rows = None
+
+ config_path = get_config().MUDB_FILE
+ with open(config_path, 'rb+') as f:
+ rows = json.loads(f.read().decode('utf8'))
+ for row in rows:
+ try:
+ if 'forbidden_ip' in row:
+ row['forbidden_ip'] = common.IPNetwork(row['forbidden_ip'])
+ except Exception as e:
+ logging.error(e)
+ try:
+ if 'forbidden_port' in row:
+ row['forbidden_port'] = common.PortRange(row['forbidden_port'])
+ except Exception as e:
+ logging.error(e)
+
+ if not rows:
+ logging.warn('no user in json file')
+ return rows
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog
new file mode 100755
index 0000000000..4e7ad163f6
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog
@@ -0,0 +1,5 @@
+shadowsocks (2.1.0-1) unstable; urgency=low
+
+ * Initial release (Closes: #758900)
+
+ -- Shell.Xu Sat, 23 Aug 2014 00:56:04 +0800
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat
new file mode 100755
index 0000000000..45a4fb75db
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat
@@ -0,0 +1 @@
+8
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json
new file mode 100644
index 0000000000..35cb14a6cc
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json
@@ -0,0 +1,11 @@
+{
+ "server":"my_server_ip",
+ "server_port":8388,
+ "local_address": "127.0.0.1",
+ "local_port":1080,
+ "password":"mypassword",
+ "timeout":300,
+ "method":"aes-256-cfb",
+ "fast_open": false,
+ "workers": 1
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/control b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/control
new file mode 100755
index 0000000000..da0092004d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/control
@@ -0,0 +1,19 @@
+Source: shadowsocks
+Section: python
+Priority: extra
+Maintainer: Shell.Xu
+Build-Depends: debhelper (>= 8), python-all (>= 2.6.6-3~), python-setuptools
+Standards-Version: 3.9.5
+Homepage: https://github.com/clowwindy/shadowsocks
+Vcs-Git: git://github.com/shell909090/shadowsocks.git
+Vcs-Browser: http://github.com/shell909090/shadowsocks
+
+Package: shadowsocks
+Architecture: all
+Pre-Depends: dpkg (>= 1.15.6~)
+Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, python-m2crypto
+Description: Fast tunnel proxy that helps you bypass firewalls
+ A secure socks5 proxy, designed to protect your Internet traffic.
+ .
+ This package contain local and server part of shadowsocks, a fast,
+ powerful tunnel proxy to bypass firewalls.
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright
new file mode 100755
index 0000000000..7be81625ef
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright
@@ -0,0 +1,30 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: shadowsocks
+Source: https://github.com/clowwindy/shadowsocks
+
+Files: debian/*
+Copyright: 2014 Shell.Xu
+License: Expat
+
+Files: *
+Copyright: 2014 clowwindy
+License: Expat
+
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs
new file mode 100755
index 0000000000..0208fc1fed
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs
@@ -0,0 +1,2 @@
+README.md
+README.rst
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d
new file mode 100755
index 0000000000..2f4f3521e3
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d
@@ -0,0 +1,149 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: shadowsocks
+# Required-Start: $network $local_fs $remote_fs
+# Required-Stop: $network $local_fs $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Fast tunnel proxy that helps you bypass firewalls
+# Description: A secure socks5 proxy, designed to protect your Internet traffic.
+# This package contain local and server part of shadowsocks, a fast,
+# powerful tunnel proxy to bypass firewalls.
+### END INIT INFO
+
+# Author: Shell.Xu
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC=shadowsocks # Introduce a short description here
+NAME=shadowsocks # Introduce the short server's name here
+DAEMON=/usr/bin/ssserver # Introduce the server's location here
+DAEMON_ARGS="" # Arguments to run the daemon with
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+LOGFILE=/var/log/$NAME.log
+
+# Exit if the package is not installed
+[ -x $DAEMON ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ --background --make-pidfile --chdir / --chuid $USERID --no-close --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ --background --make-pidfile --chdir / --chuid $USERID --no-close -- \
+ $DAEMON_ARGS $DAEMON_OPTS >> $LOGFILE 2>&1 \
+ || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+ ;;
+ #reload|force-reload)
+ #
+ # If do_reload() is not implemented then leave this commented out
+ # and leave 'force-reload' as an alias for 'restart'.
+ #
+ #log_daemon_msg "Reloading $DESC" "$NAME"
+ #do_reload
+ #log_end_msg $?
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented then remove the
+ # 'force-reload' alias
+ #
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/install b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/install
new file mode 100755
index 0000000000..a61486462f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/install
@@ -0,0 +1 @@
+debian/config.json etc/shadowsocks/
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules
new file mode 100755
index 0000000000..62e2bb6f6f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules
@@ -0,0 +1,5 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+%:
+ dh $@ --with python2 --buildsystem=python_distutils
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default
new file mode 100755
index 0000000000..a520602797
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default
@@ -0,0 +1,12 @@
+# Defaults for shadowsocks initscript
+# sourced by /etc/init.d/shadowsocks
+# installed at /etc/default/shadowsocks by the maintainer scripts
+
+USERID="nobody"
+
+#
+# This is a POSIX shell fragment
+#
+
+# Additional options that are passed to the Daemon.
+DAEMON_OPTS="-q -c /etc/shadowsocks/config.json"
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages
new file mode 100755
index 0000000000..3df8a3341b
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages
@@ -0,0 +1,2 @@
+debian/sslocal.1
+debian/ssserver.1
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format
new file mode 100755
index 0000000000..163aaf8d82
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1 b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1
new file mode 100755
index 0000000000..0c2cf51659
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1
@@ -0,0 +1,59 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" (C) Copyright 2014 Shell.Xu ,
+.\"
+.TH SHADOWSOCKS 1 "August 23, 2014"
+.SH NAME
+shadowsocks \- Fast tunnel proxy that helps you bypass firewalls
+.SH SYNOPSIS
+.B ssserver
+.RI [ options ]
+.br
+.B sslocal
+.RI [ options ]
+.SH DESCRIPTION
+shadowsocks is a tunnel proxy helps you bypass firewall.
+.B ssserver
+is the server part, and
+.B sslocal
+is the local part.
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Show this help message and exit.
+.TP
+.B \-s SERVER_ADDR
+Server address, default: 0.0.0.0.
+.TP
+.B \-p SERVER_PORT
+Server port, default: 8388.
+.TP
+.B \-k PASSWORD
+Password.
+.TP
+.B \-m METHOD
+Encryption method, default: aes-256-cfb.
+.TP
+.B \-t TIMEOUT
+Timeout in seconds, default: 300.
+.TP
+.B \-c CONFIG
+Path to config file.
+.TP
+.B \-\-fast-open
+Use TCP_FASTOPEN, requires Linux 3.7+.
+.TP
+.B \-\-workers WORKERS
+Number of workers, available on Unix/Linux.
+.TP
+.B \-v, \-vv
+Verbose mode.
+.TP
+.B \-q, \-qq
+Quiet mode, only show warnings/errors.
+.SH SEE ALSO
+.br
+The programs are documented fully by
+.IR "Shell Xu "
+and
+.IR "Clowwindy ",
+available via the Info system.
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1 b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1
new file mode 100755
index 0000000000..0c2cf51659
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1
@@ -0,0 +1,59 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" (C) Copyright 2014 Shell.Xu ,
+.\"
+.TH SHADOWSOCKS 1 "August 23, 2014"
+.SH NAME
+shadowsocks \- Fast tunnel proxy that helps you bypass firewalls
+.SH SYNOPSIS
+.B ssserver
+.RI [ options ]
+.br
+.B sslocal
+.RI [ options ]
+.SH DESCRIPTION
+shadowsocks is a tunnel proxy helps you bypass firewall.
+.B ssserver
+is the server part, and
+.B sslocal
+is the local part.
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Show this help message and exit.
+.TP
+.B \-s SERVER_ADDR
+Server address, default: 0.0.0.0.
+.TP
+.B \-p SERVER_PORT
+Server port, default: 8388.
+.TP
+.B \-k PASSWORD
+Password.
+.TP
+.B \-m METHOD
+Encryption method, default: aes-256-cfb.
+.TP
+.B \-t TIMEOUT
+Timeout in seconds, default: 300.
+.TP
+.B \-c CONFIG
+Path to config file.
+.TP
+.B \-\-fast-open
+Use TCP_FASTOPEN, requires Linux 3.7+.
+.TP
+.B \-\-workers WORKERS
+Number of workers, available on Unix/Linux.
+.TP
+.B \-v, \-vv
+Verbose mode.
+.TP
+.B \-q, \-qq
+Quiet mode, only show warnings/errors.
+.SH SEE ALSO
+.br
+The programs are documented fully by
+.IR "Shell Xu "
+and
+.IR "Clowwindy ",
+available via the Info system.
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py
new file mode 100644
index 0000000000..c917cb7d95
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+
+def load(name):
+ try:
+ obj = __import__(name)
+ reload(obj)
+ return obj
+ except:
+ pass
+
+ try:
+ import importlib
+ obj = importlib.__import__(name)
+ importlib.reload(obj)
+ return obj
+ except:
+ pass
+
+def loads(namelist):
+ for name in namelist:
+ obj = load(name)
+ if obj is not None:
+ return obj
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat
new file mode 100755
index 0000000000..6d0bce695a
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat
@@ -0,0 +1,4 @@
+@echo off
+If Not Exist "userapiconfig.py" Copy "apiconfig.py" "userapiconfig.py"
+If Not Exist "user-config.json" Copy "config.json" "user-config.json"
+If Not Exist "usermysql.json" Copy "mysql.json" "usermysql.json"
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh
new file mode 100755
index 0000000000..862d1abed1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+chmod +x *.sh
+chmod +x shadowsocks/*.sh
+cp -n apiconfig.py userapiconfig.py
+cp -n config.json user-config.json
+cp -n mysql.json usermysql.json
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh
new file mode 100755
index 0000000000..09b07f3322
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+bash initcfg.sh
+sed -i "s/API_INTERFACE = .\+\?\#/API_INTERFACE = \'mudbjson\' \#/g" userapiconfig.py
+ip_addr=`ifconfig -a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'|tr -d "addr:"`
+ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
+
+if [[ $ip_count == 1 ]]; then
+ ip_addr=`ip a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'`
+ ip_addr=${ip_addr%/*}
+ ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
+fi
+if [[ $ip_count == 1 ]]; then
+ echo "server IP is "${ip_addr}
+ sed -i "s/SERVER_PUB_ADDR = .\+/SERVER_PUB_ADDR = \'"${ip_addr}"\'/g" userapiconfig.py
+ user_count=`python mujson_mgr.py -l|grep -c -e "[0-9]"`
+ if [[ $user_count == 0 ]]; then
+ port=`python -c 'import random;print(random.randint(10000, 65536))'`
+ python mujson_mgr.py -a -p ${port}
+ fi
+else
+ echo "unable to detect server IP"
+fi
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh
new file mode 100755
index 0000000000..94153fe9e2
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+cd `dirname $0`
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
+ulimit -n 512000
+nohup ${python_ver} server.py m>> ssserver.log 2>&1 &
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py
new file mode 100644
index 0000000000..2eb05d59d1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py
@@ -0,0 +1,358 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+
+import traceback
+from shadowsocks import shell, common
+from configloader import load_config, get_config
+import random
+import getopt
+import sys
+import json
+import base64
+
+
+class MuJsonLoader(object):
+ def __init__(self):
+ self.json = None
+
+ def load(self, path):
+ l = "[]"
+ try:
+ with open(path, 'rb+') as f:
+ l = f.read().decode('utf8')
+ except:
+ pass
+ self.json = json.loads(l)
+
+ def save(self, path):
+ if self.json is not None:
+ output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': '))
+ with open(path, 'a'):
+ pass
+ with open(path, 'rb+') as f:
+ f.write(output.encode('utf8'))
+ f.truncate()
+
+
+class MuMgr(object):
+ def __init__(self):
+ self.config_path = get_config().MUDB_FILE
+ try:
+ self.server_addr = get_config().SERVER_PUB_ADDR
+ except:
+ self.server_addr = '127.0.0.1'
+ self.data = MuJsonLoader()
+
+ if self.server_addr == '127.0.0.1':
+ self.server_addr = self.getipaddr()
+
+ def getipaddr(self, ifname='eth0'):
+ import socket
+ import struct
+ ret = '127.0.0.1'
+ try:
+ ret = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
+ except:
+ pass
+ if ret == '127.0.0.1':
+ try:
+ import fcntl
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
+ except:
+ pass
+ return ret
+
+ def ssrlink(self, user, encode, muid):
+ protocol = user.get('protocol', '')
+ obfs = user.get('obfs', '')
+ protocol = protocol.replace("_compatible", "")
+ obfs = obfs.replace("_compatible", "")
+ protocol_param = ''
+ if muid is not None:
+ protocol_param_ = user.get('protocol_param', '')
+ param = protocol_param_.split('#')
+ if len(param) == 2:
+ for row in self.data.json:
+ if int(row['port']) == muid:
+ param = str(muid) + ':' + row['passwd']
+ protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
+ break
+ link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
+ return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
+
+ def userinfo(self, user, muid = None):
+ ret = ""
+ key_list = ['user', 'port', 'method', 'passwd', 'protocol', 'protocol_param', 'obfs', 'obfs_param', 'transfer_enable', 'u', 'd']
+ for key in sorted(user):
+ if key not in key_list:
+ key_list.append(key)
+ for key in key_list:
+ if key in ['enable'] or key not in user:
+ continue
+ ret += '\n'
+ if (muid is not None) and (key in ['protocol_param']):
+ for row in self.data.json:
+ if int(row['port']) == muid:
+ ret += " %s : %s" % (key, str(muid) + ':' + row['passwd'])
+ break
+ elif key in ['transfer_enable', 'u', 'd']:
+ if muid is not None:
+ for row in self.data.json:
+ if int(row['port']) == muid:
+ val = row[key]
+ break
+ else:
+ val = user[key]
+ if val / 1024 < 4:
+ ret += " %s : %s" % (key, val)
+ elif val / 1024 ** 2 < 4:
+ val /= float(1024)
+ ret += " %s : %s K Bytes" % (key, val)
+ elif val / 1024 ** 3 < 4:
+ val /= float(1024 ** 2)
+ ret += " %s : %s M Bytes" % (key, val)
+ else:
+ val /= float(1024 ** 3)
+ ret += " %s : %s G Bytes" % (key, val)
+ else:
+ ret += " %s : %s" % (key, user[key])
+ ret += "\n " + self.ssrlink(user, False, muid)
+ ret += "\n " + self.ssrlink(user, True, muid)
+ return ret
+
+ def rand_pass(self):
+ return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)])
+
+ def add(self, user):
+ up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-ctr",
+ 'protocol': "auth_aes128_md5",
+ 'obfs': "tls1.2_ticket_auth_compatible",
+ 'transfer_enable': 9007199254740992}
+ up['passwd'] = self.rand_pass()
+ up.update(user)
+
+ self.data.load(self.config_path)
+ for row in self.data.json:
+ match = False
+ if 'user' in user and row['user'] == user['user']:
+ match = True
+ if 'port' in user and row['port'] == user['port']:
+ match = True
+ if match:
+ print("user [%s] port [%s] already exist" % (row['user'], row['port']))
+ return
+ self.data.json.append(up)
+ print("### add user info %s" % self.userinfo(up))
+ self.data.save(self.config_path)
+
+ def edit(self, user):
+ self.data.load(self.config_path)
+ for row in self.data.json:
+ match = True
+ if 'user' in user and row['user'] != user['user']:
+ match = False
+ if 'port' in user and row['port'] != user['port']:
+ match = False
+ if match:
+ print("edit user [%s]" % (row['user'],))
+ row.update(user)
+ print("### new user info %s" % self.userinfo(row))
+ break
+ self.data.save(self.config_path)
+
+ def delete(self, user):
+ self.data.load(self.config_path)
+ index = 0
+ for row in self.data.json:
+ match = True
+ if 'user' in user and row['user'] != user['user']:
+ match = False
+ if 'port' in user and row['port'] != user['port']:
+ match = False
+ if match:
+ print("delete user [%s]" % row['user'])
+ del self.data.json[index]
+ break
+ index += 1
+ self.data.save(self.config_path)
+
+ def clear_ud(self, user):
+ up = {'u': 0, 'd': 0}
+ self.data.load(self.config_path)
+ for row in self.data.json:
+ match = True
+ if 'user' in user and row['user'] != user['user']:
+ match = False
+ if 'port' in user and row['port'] != user['port']:
+ match = False
+ if match:
+ row.update(up)
+ print("clear user [%s]" % row['user'])
+ self.data.save(self.config_path)
+
+ def list_user(self, user):
+ self.data.load(self.config_path)
+ if not user:
+ for row in self.data.json:
+ print("user [%s] port %s" % (row['user'], row['port']))
+ return
+ for row in self.data.json:
+ match = True
+ if 'user' in user and row['user'] != user['user']:
+ match = False
+ if 'port' in user and row['port'] != user['port']:
+ match = False
+ if match:
+ muid = None
+ if 'muid' in user:
+ muid = user['muid']
+ print("### user [%s] info %s" % (row['user'], self.userinfo(row, muid)))
+
+
+def print_server_help():
+ print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
+
+Actions:
+ -a add/edit a user
+ -d delete a user
+ -e edit a user
+ -c set u&d to zero
+ -l display a user infomation or all users infomation
+
+Options:
+ -u USER the user name
+ -p PORT server port (only this option must be set if add a user)
+ -k PASSWORD password
+ -m METHOD encryption method, default: aes-128-ctr
+ -O PROTOCOL protocol plugin, default: auth_aes128_md5
+ -o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
+ -G PROTOCOL_PARAM protocol plugin param
+ -g OBFS_PARAM obfs plugin param
+ -t TRANSFER max transfer for G bytes, default: 8388608 (8 PB or 8192 TB)
+ -f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
+ -i MUID set sub id to display (only work with -l)
+ -s SPEED set speed_limit_per_con
+ -S SPEED set speed_limit_per_user
+
+General options:
+ -h, --help show this help message and exit
+''')
+
+
+def main():
+ shortopts = 'adeclu:i:p:k:O:o:G:g:m:t:f:hs:S:'
+ longopts = ['help']
+ action = None
+ user = {}
+ fast_set_obfs = {'0': 'plain',
+ '+1': 'http_simple_compatible',
+ '1': 'http_simple',
+ '+2': 'tls1.2_ticket_auth_compatible',
+ '2': 'tls1.2_ticket_auth'}
+ fast_set_protocol = {'0': 'origin',
+ 's4': 'auth_sha1_v4',
+ '+s4': 'auth_sha1_v4_compatible',
+ 'am': 'auth_aes128_md5',
+ 'as': 'auth_aes128_sha1',
+ 'ca': 'auth_chain_a',
+ }
+ fast_set_method = {'0': 'none',
+ 'a1c': 'aes-128-cfb',
+ 'a2c': 'aes-192-cfb',
+ 'a3c': 'aes-256-cfb',
+ 'r': 'rc4-md5',
+ 'r6': 'rc4-md5-6',
+ 'c': 'chacha20',
+ 'ci': 'chacha20-ietf',
+ 's': 'salsa20',
+ 'a1': 'aes-128-ctr',
+ 'a2': 'aes-192-ctr',
+ 'a3': 'aes-256-ctr'}
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
+ for key, value in optlist:
+ if key == '-a':
+ action = 1
+ elif key == '-d':
+ action = 2
+ elif key == '-e':
+ action = 3
+ elif key == '-l':
+ action = 4
+ elif key == '-c':
+ action = 0
+ elif key == '-u':
+ user['user'] = value
+ elif key == '-i':
+ user['muid'] = int(value)
+ elif key == '-p':
+ user['port'] = int(value)
+ elif key == '-k':
+ user['passwd'] = value
+ elif key == '-o':
+ if value in fast_set_obfs:
+ user['obfs'] = fast_set_obfs[value]
+ else:
+ user['obfs'] = value
+ elif key == '-O':
+ if value in fast_set_protocol:
+ user['protocol'] = fast_set_protocol[value]
+ else:
+ user['protocol'] = value
+ elif key == '-g':
+ user['obfs_param'] = value
+ elif key == '-G':
+ user['protocol_param'] = value
+ elif key == '-s':
+ user['speed_limit_per_con'] = int(value)
+ elif key == '-S':
+ user['speed_limit_per_user'] = int(value)
+ elif key == '-m':
+ if value in fast_set_method:
+ user['method'] = fast_set_method[value]
+ else:
+ user['method'] = value
+ elif key == '-f':
+ user['forbidden_port'] = value
+ elif key == '-t':
+ val = float(value)
+ try:
+ val = int(value)
+ except:
+ pass
+ user['transfer_enable'] = int(val * 1024) * (1024 ** 2)
+ elif key in ('-h', '--help'):
+ print_server_help()
+ sys.exit(0)
+ except getopt.GetoptError as e:
+ print(e)
+ sys.exit(2)
+
+ manage = MuMgr()
+ if action == 0:
+ manage.clear_ud(user)
+ elif action == 1:
+ if 'user' not in user and 'port' in user:
+ user['user'] = str(user['port'])
+ if 'user' in user and 'port' in user:
+ manage.add(user)
+ else:
+ print("You have to set the port with -p")
+ elif action == 2:
+ if 'user' in user or 'port' in user:
+ manage.delete(user)
+ else:
+ print("You have to set the user name or port with -u/-p")
+ elif action == 3:
+ if 'user' in user or 'port' in user:
+ manage.edit(user)
+ else:
+ print("You have to set the user name or port with -u/-p")
+ elif action == 4:
+ manage.list_user(user)
+ elif action is None:
+ print_server_help()
+
+if __name__ == '__main__':
+ main()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json
new file mode 100644
index 0000000000..1849e9e816
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json
@@ -0,0 +1,13 @@
+{
+ "host": "127.0.0.1",
+ "port": 3306,
+ "user": "ss",
+ "password": "pass",
+ "db": "sspanel",
+ "node_id": 0,
+ "transfer_mul": 1.0,
+ "ssl_enable": 0,
+ "ssl_ca": "",
+ "ssl_cert": "",
+ "ssl_key": ""
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/run.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/run.sh
new file mode 100755
index 0000000000..0de8d204ce
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/run.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+cd `dirname $0`
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
+ulimit -n 512000
+nohup ${python_ver} server.py m>> /dev/null 2>&1 &
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server.py
new file mode 100644
index 0000000000..ba863b6840
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 breakwall
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import time
+import sys
+import threading
+import os
+
+if __name__ == '__main__':
+ import inspect
+ os.chdir(os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))))
+
+import server_pool
+import db_transfer
+from shadowsocks import shell
+from configloader import load_config, get_config
+
+class MainThread(threading.Thread):
+ def __init__(self, obj):
+ super(MainThread, self).__init__()
+ self.daemon = True
+ self.obj = obj
+
+ def run(self):
+ self.obj.thread_db(self.obj)
+
+ def stop(self):
+ self.obj.thread_db_stop()
+
+def main():
+ shell.check_python()
+ if False:
+ db_transfer.DbTransfer.thread_db()
+ else:
+ if get_config().API_INTERFACE == 'mudbjson':
+ thread = MainThread(db_transfer.MuJsonTransfer)
+ elif get_config().API_INTERFACE == 'sspanelv2':
+ thread = MainThread(db_transfer.DbTransfer)
+ else:
+ thread = MainThread(db_transfer.Dbv3Transfer)
+ thread.start()
+ try:
+ while thread.is_alive():
+ thread.join(10.0)
+ except (KeyboardInterrupt, IOError, OSError) as e:
+ import traceback
+ traceback.print_exc()
+ thread.stop()
+
+if __name__ == '__main__':
+ main()
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py
new file mode 100644
index 0000000000..d159817a3f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2014 clowwindy
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import os
+import logging
+import struct
+import time
+from shadowsocks import shell, eventloop, tcprelay, udprelay, asyncdns, common
+import threading
+import sys
+import traceback
+from socket import *
+from configloader import load_config, get_config
+
+class MainThread(threading.Thread):
+ def __init__(self, params):
+ super(MainThread, self).__init__()
+ self.params = params
+
+ def run(self):
+ ServerPool._loop(*self.params)
+
+class ServerPool(object):
+
+ instance = None
+
+ def __init__(self):
+ shell.check_python()
+ self.config = shell.get_config(False)
+ self.dns_resolver = asyncdns.DNSResolver()
+ if not self.config.get('dns_ipv6', False):
+ asyncdns.IPV6_CONNECTION_SUPPORT = False
+
+ self.mgr = None #asyncmgr.ServerMgr()
+
+ self.tcp_servers_pool = {}
+ self.tcp_ipv6_servers_pool = {}
+ self.udp_servers_pool = {}
+ self.udp_ipv6_servers_pool = {}
+ self.stat_counter = {}
+
+ self.loop = eventloop.EventLoop()
+ self.thread = MainThread( (self.loop, self.dns_resolver, self.mgr) )
+ self.thread.start()
+
+ @staticmethod
+ def get_instance():
+ if ServerPool.instance is None:
+ ServerPool.instance = ServerPool()
+ return ServerPool.instance
+
+ def stop(self):
+ self.loop.stop()
+
+ @staticmethod
+ def _loop(loop, dns_resolver, mgr):
+ try:
+ if mgr is not None:
+ mgr.add_to_loop(loop)
+ dns_resolver.add_to_loop(loop)
+ loop.run()
+ except (KeyboardInterrupt, IOError, OSError) as e:
+ logging.error(e)
+ traceback.print_exc()
+ os.exit(0)
+ except Exception as e:
+ logging.error(e)
+ traceback.print_exc()
+
+ def server_is_run(self, port):
+ port = int(port)
+ ret = 0
+ if port in self.tcp_servers_pool:
+ ret = 1
+ if port in self.tcp_ipv6_servers_pool:
+ ret |= 2
+ return ret
+
+ def server_run_status(self, port):
+ if 'server' in self.config:
+ if port not in self.tcp_servers_pool:
+ return False
+ if 'server_ipv6' in self.config:
+ if port not in self.tcp_ipv6_servers_pool:
+ return False
+ return True
+
+ def new_server(self, port, user_config):
+ ret = True
+ port = int(port)
+ ipv6_ok = False
+
+ if 'server_ipv6' in self.config:
+ if port in self.tcp_ipv6_servers_pool:
+ logging.info("server already at %s:%d" % (self.config['server_ipv6'], port))
+ return 'this port server is already running'
+ else:
+ a_config = self.config.copy()
+ a_config.update(user_config)
+ if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]":
+ a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
+ a_config['server'] = a_config['server_ipv6']
+ a_config['server_port'] = port
+ a_config['max_connect'] = 128
+ a_config['method'] = common.to_str(a_config['method'])
+ try:
+ logging.info("starting server at [%s]:%d" % (common.to_str(a_config['server']), port))
+
+ tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
+ tcp_server.add_to_loop(self.loop)
+ self.tcp_ipv6_servers_pool.update({port: tcp_server})
+
+ udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
+ udp_server.add_to_loop(self.loop)
+ self.udp_ipv6_servers_pool.update({port: udp_server})
+
+ if common.to_str(a_config['server_ipv6']) == "::":
+ ipv6_ok = True
+ except Exception as e:
+ logging.warn("IPV6 %s " % (e,))
+
+ if 'server' in self.config:
+ if port in self.tcp_servers_pool:
+ logging.info("server already at %s:%d" % (common.to_str(self.config['server']), port))
+ return 'this port server is already running'
+ else:
+ a_config = self.config.copy()
+ a_config.update(user_config)
+ a_config['server_port'] = port
+ a_config['max_connect'] = 128
+ a_config['method'] = common.to_str(a_config['method'])
+ try:
+ logging.info("starting server at %s:%d" % (common.to_str(a_config['server']), port))
+
+ tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False)
+ tcp_server.add_to_loop(self.loop)
+ self.tcp_servers_pool.update({port: tcp_server})
+
+ udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False)
+ udp_server.add_to_loop(self.loop)
+ self.udp_servers_pool.update({port: udp_server})
+
+ except Exception as e:
+ if not ipv6_ok:
+ logging.warn("IPV4 %s " % (e,))
+
+ return True
+
+ def del_server(self, port):
+ port = int(port)
+ logging.info("del server at %d" % port)
+ try:
+ udpsock = socket(AF_INET, SOCK_DGRAM)
+ udpsock.sendto('%s:%s:0:0' % (get_config().MANAGE_PASS, port), (get_config().MANAGE_BIND_IP, get_config().MANAGE_PORT))
+ udpsock.close()
+ except Exception as e:
+ logging.warn(e)
+ return True
+
+ def cb_del_server(self, port):
+ port = int(port)
+
+ if port not in self.tcp_servers_pool:
+ logging.info("stopped server at %s:%d already stop" % (self.config['server'], port))
+ else:
+ logging.info("stopped server at %s:%d" % (self.config['server'], port))
+ try:
+ self.tcp_servers_pool[port].close(True)
+ del self.tcp_servers_pool[port]
+ except Exception as e:
+ logging.warn(e)
+ try:
+ self.udp_servers_pool[port].close(True)
+ del self.udp_servers_pool[port]
+ except Exception as e:
+ logging.warn(e)
+
+ if 'server_ipv6' in self.config:
+ if port not in self.tcp_ipv6_servers_pool:
+ logging.info("stopped server at [%s]:%d already stop" % (self.config['server_ipv6'], port))
+ else:
+ logging.info("stopped server at [%s]:%d" % (self.config['server_ipv6'], port))
+ try:
+ self.tcp_ipv6_servers_pool[port].close(True)
+ del self.tcp_ipv6_servers_pool[port]
+ except Exception as e:
+ logging.warn(e)
+ try:
+ self.udp_ipv6_servers_pool[port].close(True)
+ del self.udp_ipv6_servers_pool[port]
+ except Exception as e:
+ logging.warn(e)
+
+ return True
+
+ def update_mu_users(self, port, users):
+ port = int(port)
+ if port in self.tcp_servers_pool:
+ try:
+ self.tcp_servers_pool[port].update_users(users)
+ except Exception as e:
+ logging.warn(e)
+ try:
+ self.udp_servers_pool[port].update_users(users)
+ except Exception as e:
+ logging.warn(e)
+ if port in self.tcp_ipv6_servers_pool:
+ try:
+ self.tcp_ipv6_servers_pool[port].update_users(users)
+ except Exception as e:
+ logging.warn(e)
+ try:
+ self.udp_ipv6_servers_pool[port].update_users(users)
+ except Exception as e:
+ logging.warn(e)
+
+ def get_server_transfer(self, port):
+ port = int(port)
+ uid = struct.pack(' 63:
+ return None
+ results.append(common.chr(l))
+ results.append(label)
+ results.append(b'\0')
+ return b''.join(results)
+
+
+def build_request(address, qtype):
+ request_id = os.urandom(2)
+ header = struct.pack('!BBHHHH', 1, 0, 1, 0, 0, 0)
+ addr = build_address(address)
+ qtype_qclass = struct.pack('!HH', qtype, QCLASS_IN)
+ return request_id + header + addr + qtype_qclass
+
+
+def parse_ip(addrtype, data, length, offset):
+ if addrtype == QTYPE_A:
+ return socket.inet_ntop(socket.AF_INET, data[offset:offset + length])
+ elif addrtype == QTYPE_AAAA:
+ return socket.inet_ntop(socket.AF_INET6, data[offset:offset + length])
+ elif addrtype in [QTYPE_CNAME, QTYPE_NS]:
+ return parse_name(data, offset)[1]
+ else:
+ return data[offset:offset + length]
+
+
+def parse_name(data, offset):
+ p = offset
+ labels = []
+ l = common.ord(data[p])
+ while l > 0:
+ if (l & (128 + 64)) == (128 + 64):
+ # pointer
+ pointer = struct.unpack('!H', data[p:p + 2])[0]
+ pointer &= 0x3FFF
+ r = parse_name(data, pointer)
+ labels.append(r[1])
+ p += 2
+ # pointer is the end
+ return p - offset, b'.'.join(labels)
+ else:
+ labels.append(data[p + 1:p + 1 + l])
+ p += 1 + l
+ l = common.ord(data[p])
+ return p - offset + 1, b'.'.join(labels)
+
+
+# rfc1035
+# record
+# 1 1 1 1 1 1
+# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# | |
+# / /
+# / NAME /
+# | |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# | TYPE |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# | CLASS |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# | TTL |
+# | |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# | RDLENGTH |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+# / RDATA /
+# / /
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+def parse_record(data, offset, question=False):
+ nlen, name = parse_name(data, offset)
+ if not question:
+ record_type, record_class, record_ttl, record_rdlength = struct.unpack(
+ '!HHiH', data[offset + nlen:offset + nlen + 10]
+ )
+ ip = parse_ip(record_type, data, record_rdlength, offset + nlen + 10)
+ return nlen + 10 + record_rdlength, \
+ (name, ip, record_type, record_class, record_ttl)
+ else:
+ record_type, record_class = struct.unpack(
+ '!HH', data[offset + nlen:offset + nlen + 4]
+ )
+ return nlen + 4, (name, None, record_type, record_class, None, None)
+
+
+def parse_header(data):
+ if len(data) >= 12:
+ header = struct.unpack('!HBBHHHH', data[:12])
+ res_id = header[0]
+ res_qr = header[1] & 128
+ res_tc = header[1] & 2
+ res_ra = header[2] & 128
+ res_rcode = header[2] & 15
+ # assert res_tc == 0
+ # assert res_rcode in [0, 3]
+ res_qdcount = header[3]
+ res_ancount = header[4]
+ res_nscount = header[5]
+ res_arcount = header[6]
+ return (res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount,
+ res_ancount, res_nscount, res_arcount)
+ return None
+
+
+def parse_response(data):
+ try:
+ if len(data) >= 12:
+ header = parse_header(data)
+ if not header:
+ return None
+ res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount, \
+ res_ancount, res_nscount, res_arcount = header
+
+ qds = []
+ ans = []
+ offset = 12
+ for i in range(0, res_qdcount):
+ l, r = parse_record(data, offset, True)
+ offset += l
+ if r:
+ qds.append(r)
+ for i in range(0, res_ancount):
+ l, r = parse_record(data, offset)
+ offset += l
+ if r:
+ ans.append(r)
+ for i in range(0, res_nscount):
+ l, r = parse_record(data, offset)
+ offset += l
+ for i in range(0, res_arcount):
+ l, r = parse_record(data, offset)
+ offset += l
+ response = DNSResponse()
+ if qds:
+ response.hostname = qds[0][0]
+ for an in qds:
+ response.questions.append((an[1], an[2], an[3]))
+ for an in ans:
+ response.answers.append((an[1], an[2], an[3]))
+ return response
+ except Exception as e:
+ shell.print_exception(e)
+ return None
+
+
+def is_valid_hostname(hostname):
+ if len(hostname) > 255:
+ return False
+ if hostname[-1] == b'.':
+ hostname = hostname[:-1]
+ return all(VALID_HOSTNAME.match(x) for x in hostname.split(b'.'))
+
+
+class DNSResponse(object):
+ def __init__(self):
+ self.hostname = None
+ self.questions = [] # each: (addr, type, class)
+ self.answers = [] # each: (addr, type, class)
+
+ def __str__(self):
+ return '%s: %s' % (self.hostname, str(self.answers))
+
+
+STATUS_IPV4 = 0
+STATUS_IPV6 = 1
+
+
+class DNSResolver(object):
+ def __init__(self, black_hostname_list=None):
+ self._loop = None
+ self._hosts = {}
+ self._hostname_status = {}
+ self._hostname_to_cb = {}
+ self._cb_to_hostname = {}
+ self._cache = lru_cache.LRUCache(timeout=300)
+ # read black_hostname_list from config
+ if type(black_hostname_list) != list:
+ self._black_hostname_list = []
+ else:
+ self._black_hostname_list = list(map(
+ (lambda t: t if type(t) == bytes else t.encode('utf8')),
+ black_hostname_list
+ ))
+ logging.info('black_hostname_list init as : ' + str(self._black_hostname_list))
+ self._sock = None
+ self._servers = None
+ self._parse_resolv()
+ self._parse_hosts()
+ # TODO monitor hosts change and reload hosts
+ # TODO parse /etc/gai.conf and follow its rules
+
+ def _parse_resolv(self):
+ self._servers = []
+ try:
+ with open('dns.conf', 'rb') as f:
+ content = f.readlines()
+ for line in content:
+ line = line.strip()
+ if line:
+ parts = line.split(b' ', 1)
+ if len(parts) >= 2:
+ server = parts[0]
+ port = int(parts[1])
+ else:
+ server = parts[0]
+ port = 53
+ if common.is_ip(server) == socket.AF_INET:
+ if type(server) != str:
+ server = server.decode('utf8')
+ self._servers.append((server, port))
+ except IOError:
+ pass
+ if not self._servers:
+ try:
+ with open('/etc/resolv.conf', 'rb') as f:
+ content = f.readlines()
+ for line in content:
+ line = line.strip()
+ if line:
+ if line.startswith(b'nameserver'):
+ parts = line.split()
+ if len(parts) >= 2:
+ server = parts[1]
+ if common.is_ip(server) == socket.AF_INET:
+ if type(server) != str:
+ server = server.decode('utf8')
+ self._servers.append((server, 53))
+ except IOError:
+ pass
+ if not self._servers:
+ self._servers = [('8.8.4.4', 53), ('8.8.8.8', 53)]
+ logging.info('dns server: %s' % (self._servers,))
+
+ def _parse_hosts(self):
+ etc_path = '/etc/hosts'
+ if 'WINDIR' in os.environ:
+ etc_path = os.environ['WINDIR'] + '/system32/drivers/etc/hosts'
+ try:
+ with open(etc_path, 'rb') as f:
+ for line in f.readlines():
+ line = line.strip()
+ if b"#" in line:
+ line = line[:line.find(b'#')]
+ parts = line.split()
+ if len(parts) >= 2:
+ ip = parts[0]
+ if common.is_ip(ip):
+ for i in range(1, len(parts)):
+ hostname = parts[i]
+ if hostname:
+ self._hosts[hostname] = ip
+ except IOError:
+ self._hosts['localhost'] = '127.0.0.1'
+
+ def add_to_loop(self, loop):
+ if self._loop:
+ raise Exception('already add to loop')
+ self._loop = loop
+ # TODO when dns server is IPv6
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ self._sock.setblocking(False)
+ loop.add(self._sock, eventloop.POLL_IN, self)
+ loop.add_periodic(self.handle_periodic)
+
+ def _call_callback(self, hostname, ip, error=None):
+ callbacks = self._hostname_to_cb.get(hostname, [])
+ for callback in callbacks:
+ if callback in self._cb_to_hostname:
+ del self._cb_to_hostname[callback]
+ if ip or error:
+ callback((hostname, ip), error)
+ else:
+ callback((hostname, None),
+ Exception('unable to parse hostname %s' % hostname))
+ if hostname in self._hostname_to_cb:
+ del self._hostname_to_cb[hostname]
+ if hostname in self._hostname_status:
+ del self._hostname_status[hostname]
+
+ def _handle_data(self, data):
+ response = parse_response(data)
+ if response and response.hostname:
+ hostname = response.hostname
+ ip = None
+ for answer in response.answers:
+ if answer[1] in (QTYPE_A, QTYPE_AAAA) and \
+ answer[2] == QCLASS_IN:
+ ip = answer[0]
+ break
+ if IPV6_CONNECTION_SUPPORT:
+ if not ip and self._hostname_status.get(hostname, STATUS_IPV4) \
+ == STATUS_IPV6:
+ self._hostname_status[hostname] = STATUS_IPV4
+ self._send_req(hostname, QTYPE_A)
+ else:
+ if ip:
+ self._cache[hostname] = ip
+ self._call_callback(hostname, ip)
+ elif self._hostname_status.get(hostname, None) == STATUS_IPV4:
+ for question in response.questions:
+ if question[1] == QTYPE_A:
+ self._call_callback(hostname, None)
+ break
+ else:
+ if not ip and self._hostname_status.get(hostname, STATUS_IPV6) \
+ == STATUS_IPV4:
+ self._hostname_status[hostname] = STATUS_IPV6
+ self._send_req(hostname, QTYPE_AAAA)
+ else:
+ if ip:
+ self._cache[hostname] = ip
+ self._call_callback(hostname, ip)
+ elif self._hostname_status.get(hostname, None) == STATUS_IPV6:
+ for question in response.questions:
+ if question[1] == QTYPE_AAAA:
+ self._call_callback(hostname, None)
+ break
+
+ def handle_event(self, sock, fd, event):
+ if sock != self._sock:
+ return
+ if event & eventloop.POLL_ERR:
+ logging.error('dns socket err')
+ self._loop.remove(self._sock)
+ self._sock.close()
+ # TODO when dns server is IPv6
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ self._sock.setblocking(False)
+ self._loop.add(self._sock, eventloop.POLL_IN, self)
+ else:
+ data, addr = sock.recvfrom(1024)
+ if addr not in self._servers:
+ logging.warn('received a packet other than our dns')
+ return
+ self._handle_data(data)
+
+ def handle_periodic(self):
+ self._cache.sweep()
+
+ def remove_callback(self, callback):
+ hostname = self._cb_to_hostname.get(callback)
+ if hostname:
+ del self._cb_to_hostname[callback]
+ arr = self._hostname_to_cb.get(hostname, None)
+ if arr:
+ arr.remove(callback)
+ if not arr:
+ del self._hostname_to_cb[hostname]
+ if hostname in self._hostname_status:
+ del self._hostname_status[hostname]
+
+ def _send_req(self, hostname, qtype):
+ req = build_request(hostname, qtype)
+ for server in self._servers:
+ logging.debug('resolving %s with type %d using server %s',
+ hostname, qtype, server)
+ self._sock.sendto(req, server)
+
+ def resolve(self, hostname, callback):
+ if type(hostname) != bytes:
+ hostname = hostname.encode('utf8')
+ if not hostname:
+ callback(None, Exception('empty hostname'))
+ elif common.is_ip(hostname):
+ callback((hostname, hostname), None)
+ elif hostname in self._hosts:
+ logging.debug('hit hosts: %s', hostname)
+ ip = self._hosts[hostname]
+ callback((hostname, ip), None)
+ elif hostname in self._cache:
+ logging.debug('hit cache: %s ==>> %s', hostname, self._cache[hostname])
+ ip = self._cache[hostname]
+ callback((hostname, ip), None)
+ elif any(hostname.endswith(t) for t in self._black_hostname_list):
+ callback(None, Exception('hostname <%s> is block by the black hostname list' % hostname))
+ return
+ else:
+ if not is_valid_hostname(hostname):
+ callback(None, Exception('invalid hostname: %s' % hostname))
+ return
+ if False:
+ addrs = socket.getaddrinfo(hostname, 0, 0,
+ socket.SOCK_DGRAM, socket.SOL_UDP)
+ if addrs:
+ af, socktype, proto, canonname, sa = addrs[0]
+ logging.debug('DNS resolve %s %s' % (hostname, sa[0]))
+ self._cache[hostname] = sa[0]
+ callback((hostname, sa[0]), None)
+ return
+ arr = self._hostname_to_cb.get(hostname, None)
+ if not arr:
+ if IPV6_CONNECTION_SUPPORT:
+ self._hostname_status[hostname] = STATUS_IPV6
+ self._send_req(hostname, QTYPE_AAAA)
+ else:
+ self._hostname_status[hostname] = STATUS_IPV4
+ self._send_req(hostname, QTYPE_A)
+ self._hostname_to_cb[hostname] = [callback]
+ self._cb_to_hostname[callback] = hostname
+ else:
+ arr.append(callback)
+ # TODO send again only if waited too long
+ if IPV6_CONNECTION_SUPPORT:
+ self._send_req(hostname, QTYPE_AAAA)
+ else:
+ self._send_req(hostname, QTYPE_A)
+
+ def close(self):
+ if self._sock:
+ if self._loop:
+ self._loop.remove_periodic(self.handle_periodic)
+ self._loop.remove(self._sock)
+ self._sock.close()
+ self._sock = None
+
+
+def test():
+ black_hostname_list = [
+ 'baidu.com',
+ 'yahoo.com',
+ ]
+ dns_resolver = DNSResolver(black_hostname_list=black_hostname_list)
+ loop = eventloop.EventLoop()
+ dns_resolver.add_to_loop(loop)
+
+ global counter
+ counter = 0
+
+ def make_callback():
+ global counter
+
+ def callback(result, error):
+ global counter
+ # TODO: what can we assert?
+ print(result, error)
+ counter += 1
+ if counter == 12:
+ dns_resolver.close()
+ loop.stop()
+
+ a_callback = callback
+ return a_callback
+
+ assert (make_callback() != make_callback())
+
+ dns_resolver.resolve(b'google.com', make_callback())
+ dns_resolver.resolve('google.com', make_callback())
+ dns_resolver.resolve('baidu.com', make_callback())
+ dns_resolver.resolve('map.baidu.com', make_callback())
+ dns_resolver.resolve('yahoo.com', make_callback())
+ dns_resolver.resolve('example.com', make_callback())
+ dns_resolver.resolve('ipv6.google.com', make_callback())
+ dns_resolver.resolve('www.facebook.com', make_callback())
+ dns_resolver.resolve('ns2.google.com', make_callback())
+ dns_resolver.resolve('invalid.@!#$%^&$@.hostname', make_callback())
+ dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'long.hostname', make_callback())
+ dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'long.hostname', make_callback())
+ loop.run()
+ # test black_hostname_list
+ dns_resolver = DNSResolver(black_hostname_list=[])
+ assert type(dns_resolver._black_hostname_list) == list
+ assert len(dns_resolver._black_hostname_list) == 0
+ dns_resolver.close()
+ dns_resolver = DNSResolver(black_hostname_list=123)
+ assert type(dns_resolver._black_hostname_list) == list
+ assert len(dns_resolver._black_hostname_list) == 0
+ dns_resolver.close()
+ dns_resolver = DNSResolver(black_hostname_list=None)
+ assert type(dns_resolver._black_hostname_list) == list
+ assert len(dns_resolver._black_hostname_list) == 0
+ dns_resolver.close()
+ dns_resolver = DNSResolver()
+ assert type(dns_resolver._black_hostname_list) == list
+ assert dns_resolver._black_hostname_list.__len__() == 0
+ dns_resolver.close()
+
+
+if __name__ == '__main__':
+ test()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py
new file mode 100644
index 0000000000..c4484c0467
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py
@@ -0,0 +1,418 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2013-2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import socket
+import struct
+import logging
+import binascii
+import re
+
+from shadowsocks import lru_cache
+
+def compat_ord(s):
+ if type(s) == int:
+ return s
+ return _ord(s)
+
+
+def compat_chr(d):
+ if bytes == str:
+ return _chr(d)
+ return bytes([d])
+
+
+_ord = ord
+_chr = chr
+ord = compat_ord
+chr = compat_chr
+
+connect_log = logging.debug
+
+def to_bytes(s):
+ if bytes != str:
+ if type(s) == str:
+ return s.encode('utf-8')
+ return s
+
+
+def to_str(s):
+ if bytes != str:
+ if type(s) == bytes:
+ return s.decode('utf-8')
+ return s
+
+def int32(x):
+ if x > 0xFFFFFFFF or x < 0:
+ x &= 0xFFFFFFFF
+ if x > 0x7FFFFFFF:
+ x = int(0x100000000 - x)
+ if x < 0x80000000:
+ return -x
+ else:
+ return -2147483648
+ return x
+
+def inet_ntop(family, ipstr):
+ if family == socket.AF_INET:
+ return to_bytes(socket.inet_ntoa(ipstr))
+ elif family == socket.AF_INET6:
+ import re
+ v6addr = ':'.join(('%02X%02X' % (ord(i), ord(j))).lstrip('0')
+ for i, j in zip(ipstr[::2], ipstr[1::2]))
+ v6addr = re.sub('::+', '::', v6addr, count=1)
+ return to_bytes(v6addr)
+
+
+def inet_pton(family, addr):
+ addr = to_str(addr)
+ if family == socket.AF_INET:
+ return socket.inet_aton(addr)
+ elif family == socket.AF_INET6:
+ if '.' in addr: # a v4 addr
+ v4addr = addr[addr.rindex(':') + 1:]
+ v4addr = socket.inet_aton(v4addr)
+ v4addr = ['%02X' % ord(x) for x in v4addr]
+ v4addr.insert(2, ':')
+ newaddr = addr[:addr.rindex(':') + 1] + ''.join(v4addr)
+ return inet_pton(family, newaddr)
+ dbyts = [0] * 8 # 8 groups
+ grps = addr.split(':')
+ for i, v in enumerate(grps):
+ if v:
+ dbyts[i] = int(v, 16)
+ else:
+ for j, w in enumerate(grps[::-1]):
+ if w:
+ dbyts[7 - j] = int(w, 16)
+ else:
+ break
+ break
+ return b''.join((chr(i // 256) + chr(i % 256)) for i in dbyts)
+ else:
+ raise RuntimeError("What family?")
+
+
+def is_ip(address):
+ for family in (socket.AF_INET, socket.AF_INET6):
+ try:
+ if type(address) != str:
+ address = address.decode('utf8')
+ inet_pton(family, address)
+ return family
+ except (TypeError, ValueError, OSError, IOError):
+ pass
+ return False
+
+
+def match_regex(regex, text):
+ regex = re.compile(regex)
+ for item in regex.findall(text):
+ return True
+ return False
+
+
+def patch_socket():
+ if not hasattr(socket, 'inet_pton'):
+ socket.inet_pton = inet_pton
+
+ if not hasattr(socket, 'inet_ntop'):
+ socket.inet_ntop = inet_ntop
+
+
+patch_socket()
+
+
+ADDRTYPE_IPV4 = 1
+ADDRTYPE_IPV6 = 4
+ADDRTYPE_HOST = 3
+
+
+def pack_addr(address):
+ address_str = to_str(address)
+ for family in (socket.AF_INET, socket.AF_INET6):
+ try:
+ r = socket.inet_pton(family, address_str)
+ if family == socket.AF_INET6:
+ return b'\x04' + r
+ else:
+ return b'\x01' + r
+ except (TypeError, ValueError, OSError, IOError):
+ pass
+ if len(address) > 255:
+ address = address[:255] # TODO
+ return b'\x03' + chr(len(address)) + address
+
+def pre_parse_header(data):
+ if not data:
+ return None
+ datatype = ord(data[0])
+ if datatype == 0x80:
+ if len(data) <= 2:
+ return None
+ rand_data_size = ord(data[1])
+ if rand_data_size + 2 >= len(data):
+ logging.warn('header too short, maybe wrong password or '
+ 'encryption method')
+ return None
+ data = data[rand_data_size + 2:]
+ elif datatype == 0x81:
+ data = data[1:]
+ elif datatype == 0x82:
+ if len(data) <= 3:
+ return None
+ rand_data_size = struct.unpack('>H', data[1:3])[0]
+ if rand_data_size + 3 >= len(data):
+ logging.warn('header too short, maybe wrong password or '
+ 'encryption method')
+ return None
+ data = data[rand_data_size + 3:]
+ elif datatype == 0x88 or (~datatype & 0xff) == 0x88:
+ if len(data) <= 7 + 7:
+ return None
+ data_size = struct.unpack('>H', data[1:3])[0]
+ ogn_data = data
+ data = data[:data_size]
+ crc = binascii.crc32(data) & 0xffffffff
+ if crc != 0xffffffff:
+ logging.warn('uncorrect CRC32, maybe wrong password or '
+ 'encryption method')
+ return None
+ start_pos = 3 + ord(data[3])
+ data = data[start_pos:-4]
+ if data_size < len(ogn_data):
+ data += ogn_data[data_size:]
+ return data
+
+def parse_header(data):
+ addrtype = ord(data[0])
+ dest_addr = None
+ dest_port = None
+ header_length = 0
+ connecttype = (addrtype & 0x8) and 1 or 0
+ addrtype &= ~0x8
+ if addrtype == ADDRTYPE_IPV4:
+ if len(data) >= 7:
+ dest_addr = socket.inet_ntoa(data[1:5])
+ dest_port = struct.unpack('>H', data[5:7])[0]
+ header_length = 7
+ else:
+ logging.warn('header is too short')
+ elif addrtype == ADDRTYPE_HOST:
+ if len(data) > 2:
+ addrlen = ord(data[1])
+ if len(data) >= 4 + addrlen:
+ dest_addr = data[2:2 + addrlen]
+ dest_port = struct.unpack('>H', data[2 + addrlen:4 +
+ addrlen])[0]
+ header_length = 4 + addrlen
+ else:
+ logging.warn('header is too short')
+ else:
+ logging.warn('header is too short')
+ elif addrtype == ADDRTYPE_IPV6:
+ if len(data) >= 19:
+ dest_addr = socket.inet_ntop(socket.AF_INET6, data[1:17])
+ dest_port = struct.unpack('>H', data[17:19])[0]
+ header_length = 19
+ else:
+ logging.warn('header is too short')
+ else:
+ logging.warn('unsupported addrtype %d, maybe wrong password or '
+ 'encryption method' % addrtype)
+ if dest_addr is None:
+ return None
+ return connecttype, addrtype, to_bytes(dest_addr), dest_port, header_length
+
+
+class IPNetwork(object):
+ ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128, False: 0}
+
+ def __init__(self, addrs):
+ self.addrs_str = addrs
+ self._network_list_v4 = []
+ self._network_list_v6 = []
+ if type(addrs) == str:
+ addrs = addrs.split(',')
+ list(map(self.add_network, addrs))
+
+ def add_network(self, addr):
+ if addr is "":
+ return
+ block = addr.split('/')
+ addr_family = is_ip(block[0])
+ addr_len = IPNetwork.ADDRLENGTH[addr_family]
+ if addr_family is socket.AF_INET:
+ ip, = struct.unpack("!I", socket.inet_aton(block[0]))
+ elif addr_family is socket.AF_INET6:
+ hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0]))
+ ip = (hi << 64) | lo
+ else:
+ raise Exception("Not a valid CIDR notation: %s" % addr)
+ if len(block) is 1:
+ prefix_size = 0
+ while (ip & 1) == 0 and ip is not 0:
+ ip >>= 1
+ prefix_size += 1
+ logging.warn("You did't specify CIDR routing prefix size for %s, "
+ "implicit treated as %s/%d" % (addr, addr, addr_len))
+ elif block[1].isdigit() and int(block[1]) <= addr_len:
+ prefix_size = addr_len - int(block[1])
+ ip >>= prefix_size
+ else:
+ raise Exception("Not a valid CIDR notation: %s" % addr)
+ if addr_family is socket.AF_INET:
+ self._network_list_v4.append((ip, prefix_size))
+ else:
+ self._network_list_v6.append((ip, prefix_size))
+
+ def __contains__(self, addr):
+ addr_family = is_ip(addr)
+ if addr_family is socket.AF_INET:
+ ip, = struct.unpack("!I", socket.inet_aton(addr))
+ return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1],
+ self._network_list_v4))
+ elif addr_family is socket.AF_INET6:
+ hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr))
+ ip = (hi << 64) | lo
+ return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1],
+ self._network_list_v6))
+ else:
+ return False
+
+ def __cmp__(self, other):
+ return cmp(self.addrs_str, other.addrs_str)
+
+ def __eq__(self, other):
+ return self.addrs_str == other.addrs_str
+
+ def __ne__(self, other):
+ return self.addrs_str != other.addrs_str
+
+class PortRange(object):
+ def __init__(self, range_str):
+ self.range_str = to_str(range_str)
+ self.range = set()
+ range_str = to_str(range_str).split(',')
+ for item in range_str:
+ try:
+ int_range = item.split('-')
+ if len(int_range) == 1:
+ if item:
+ self.range.add(int(item))
+ elif len(int_range) == 2:
+ int_range[0] = int(int_range[0])
+ int_range[1] = int(int_range[1])
+ if int_range[0] < 0:
+ int_range[0] = 0
+ if int_range[1] > 65535:
+ int_range[1] = 65535
+ i = int_range[0]
+ while i <= int_range[1]:
+ self.range.add(i)
+ i += 1
+ except Exception as e:
+ logging.error(e)
+
+ def __contains__(self, val):
+ return val in self.range
+
+ def __cmp__(self, other):
+ return cmp(self.range_str, other.range_str)
+
+ def __eq__(self, other):
+ return self.range_str == other.range_str
+
+ def __ne__(self, other):
+ return self.range_str != other.range_str
+
+class UDPAsyncDNSHandler(object):
+ dns_cache = lru_cache.LRUCache(timeout=1800)
+ def __init__(self, params):
+ self.params = params
+ self.remote_addr = None
+ self.call_back = None
+
+ def resolve(self, dns_resolver, remote_addr, call_back):
+ if remote_addr in UDPAsyncDNSHandler.dns_cache:
+ if call_back:
+ call_back("", remote_addr, UDPAsyncDNSHandler.dns_cache[remote_addr], self.params)
+ else:
+ self.call_back = call_back
+ self.remote_addr = remote_addr
+ dns_resolver.resolve(remote_addr[0], self._handle_dns_resolved)
+ UDPAsyncDNSHandler.dns_cache.sweep()
+
+ def _handle_dns_resolved(self, result, error):
+ if error:
+ logging.error("%s when resolve DNS" % (error,)) #drop
+ return self.call_back(error, self.remote_addr, None, self.params)
+ if result:
+ ip = result[1]
+ if ip:
+ return self.call_back("", self.remote_addr, ip, self.params)
+ logging.warning("can't resolve %s" % (self.remote_addr,))
+ return self.call_back("fail to resolve", self.remote_addr, None, self.params)
+
+def test_inet_conv():
+ ipv4 = b'8.8.4.4'
+ b = inet_pton(socket.AF_INET, ipv4)
+ assert inet_ntop(socket.AF_INET, b) == ipv4
+ ipv6 = b'2404:6800:4005:805::1011'
+ b = inet_pton(socket.AF_INET6, ipv6)
+ assert inet_ntop(socket.AF_INET6, b) == ipv6
+
+
+def test_parse_header():
+ assert parse_header(b'\x03\x0ewww.google.com\x00\x50') == \
+ (0, b'www.google.com', 80, 18)
+ assert parse_header(b'\x01\x08\x08\x08\x08\x00\x35') == \
+ (0, b'8.8.8.8', 53, 7)
+ assert parse_header((b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00'
+ b'\x00\x10\x11\x00\x50')) == \
+ (0, b'2404:6800:4005:805::1011', 80, 19)
+
+
+def test_pack_header():
+ assert pack_addr(b'8.8.8.8') == b'\x01\x08\x08\x08\x08'
+ assert pack_addr(b'2404:6800:4005:805::1011') == \
+ b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00\x00\x10\x11'
+ assert pack_addr(b'www.google.com') == b'\x03\x0ewww.google.com'
+
+
+def test_ip_network():
+ ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.0.2.0')
+ assert '127.0.0.1' in ip_network
+ assert '127.0.1.1' not in ip_network
+ assert ':ff:ffff' in ip_network
+ assert '::ffff:1' not in ip_network
+ assert '::1' in ip_network
+ assert '::2' not in ip_network
+ assert '192.168.1.1' in ip_network
+ assert '192.168.1.2' not in ip_network
+ assert '192.0.2.1' in ip_network
+ assert '192.0.3.1' in ip_network # 192.0.2.0 is treated as 192.0.2.0/23
+ assert 'www.google.com' not in ip_network
+
+
+if __name__ == '__main__':
+ test_inet_conv()
+ test_parse_header()
+ test_pack_header()
+ test_ip_network()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py
new file mode 100644
index 0000000000..401c7b726e
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py
new file mode 100644
index 0000000000..efecfd415d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 clowwindy
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import logging
+from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \
+ create_string_buffer, c_void_p
+
+__all__ = ['ciphers']
+
+libsodium = None
+loaded = False
+
+buf_size = 2048
+
+# for salsa20 and chacha20
+BLOCK_SIZE = 64
+
+
+def load_libsodium():
+ global loaded, libsodium, buf
+
+ from ctypes.util import find_library
+ for p in ('sodium',):
+ libsodium_path = find_library(p)
+ if libsodium_path:
+ break
+ else:
+ raise Exception('libsodium not found')
+ logging.info('loading libsodium from %s', libsodium_path)
+ libsodium = CDLL(libsodium_path)
+ libsodium.sodium_init.restype = c_int
+ libsodium.crypto_stream_salsa20_xor_ic.restype = c_int
+ libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p,
+ c_ulonglong,
+ c_char_p, c_ulonglong,
+ c_char_p)
+ libsodium.crypto_stream_chacha20_xor_ic.restype = c_int
+ libsodium.crypto_stream_chacha20_xor_ic.argtypes = (c_void_p, c_char_p,
+ c_ulonglong,
+ c_char_p, c_ulonglong,
+ c_char_p)
+
+ libsodium.sodium_init()
+
+ buf = create_string_buffer(buf_size)
+ loaded = True
+
+
+class Salsa20Crypto(object):
+ def __init__(self, cipher_name, key, iv, op):
+ if not loaded:
+ load_libsodium()
+ self.key = key
+ self.iv = iv
+ self.key_ptr = c_char_p(key)
+ self.iv_ptr = c_char_p(iv)
+ if cipher_name == b'salsa20':
+ self.cipher = libsodium.crypto_stream_salsa20_xor_ic
+ elif cipher_name == b'chacha20':
+ self.cipher = libsodium.crypto_stream_chacha20_xor_ic
+ else:
+ raise Exception('Unknown cipher')
+ # byte counter, not block counter
+ self.counter = 0
+
+ def update(self, data):
+ global buf_size, buf
+ l = len(data)
+
+ # we can only prepend some padding to make the encryption align to
+ # blocks
+ padding = self.counter % BLOCK_SIZE
+ if buf_size < padding + l:
+ buf_size = (padding + l) * 2
+ buf = create_string_buffer(buf_size)
+
+ if padding:
+ data = (b'\0' * padding) + data
+ self.cipher(byref(buf), c_char_p(data), padding + l,
+ self.iv_ptr, int(self.counter / BLOCK_SIZE), self.key_ptr)
+ self.counter += l
+ # buf is copied to a str object when we access buf.raw
+ # strip off the padding
+ return buf.raw[padding:padding + l]
+
+
+ciphers = {
+ b'salsa20': (32, 8, Salsa20Crypto),
+ b'chacha20': (32, 8, Salsa20Crypto),
+}
+
+
+def test_salsa20():
+ from shadowsocks.crypto import util
+
+ cipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 1)
+ decipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+def test_chacha20():
+ from shadowsocks.crypto import util
+
+ cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1)
+ decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+if __name__ == '__main__':
+ test_chacha20()
+ test_salsa20()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py
new file mode 100644
index 0000000000..0ef8ce0f8c
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 clowwindy
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import logging
+from ctypes import CDLL, c_char_p, c_int, c_long, byref,\
+ create_string_buffer, c_void_p
+
+__all__ = ['ciphers']
+
+libcrypto = None
+loaded = False
+
+buf_size = 2048
+
+
+def load_openssl():
+ global loaded, libcrypto, buf
+
+ from ctypes.util import find_library
+ for p in ('crypto', 'eay32', 'libeay32'):
+ libcrypto_path = find_library(p)
+ if libcrypto_path:
+ break
+ else:
+ raise Exception('libcrypto(OpenSSL) not found')
+ logging.info('loading libcrypto from %s', libcrypto_path)
+ libcrypto = CDLL(libcrypto_path)
+ libcrypto.EVP_get_cipherbyname.restype = c_void_p
+ libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
+
+ libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_char_p,
+ c_char_p, c_char_p, c_int)
+
+ libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_void_p, c_void_p,
+ c_char_p, c_int)
+
+ libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
+ libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,)
+ if hasattr(libcrypto, 'OpenSSL_add_all_ciphers'):
+ libcrypto.OpenSSL_add_all_ciphers()
+
+ buf = create_string_buffer(buf_size)
+ loaded = True
+
+
+def load_cipher(cipher_name):
+ func_name = b'EVP_' + cipher_name.replace(b'-', b'_')
+ if bytes != str:
+ func_name = str(func_name, 'utf-8')
+ cipher = getattr(libcrypto, func_name, None)
+ if cipher:
+ cipher.restype = c_void_p
+ return cipher()
+ return None
+
+
+class CtypesCrypto(object):
+ def __init__(self, cipher_name, key, iv, op):
+ if not loaded:
+ load_openssl()
+ self._ctx = None
+ cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
+ if not cipher:
+ cipher = load_cipher(cipher_name)
+ if not cipher:
+ raise Exception('cipher %s not found in libcrypto' % cipher_name)
+ key_ptr = c_char_p(key)
+ iv_ptr = c_char_p(iv)
+ self._ctx = libcrypto.EVP_CIPHER_CTX_new()
+ if not self._ctx:
+ raise Exception('can not create cipher context')
+ r = libcrypto.EVP_CipherInit_ex(self._ctx, cipher, None,
+ key_ptr, iv_ptr, c_int(op))
+ if not r:
+ self.clean()
+ raise Exception('can not initialize cipher context')
+
+ def update(self, data):
+ global buf_size, buf
+ cipher_out_len = c_long(0)
+ l = len(data)
+ if buf_size < l:
+ buf_size = l * 2
+ buf = create_string_buffer(buf_size)
+ libcrypto.EVP_CipherUpdate(self._ctx, byref(buf),
+ byref(cipher_out_len), c_char_p(data), l)
+ # buf is copied to a str object when we access buf.raw
+ return buf.raw[:cipher_out_len.value]
+
+ def __del__(self):
+ self.clean()
+
+ def clean(self):
+ if self._ctx:
+ libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx)
+ libcrypto.EVP_CIPHER_CTX_free(self._ctx)
+
+
+ciphers = {
+ b'aes-128-cfb': (16, 16, CtypesCrypto),
+ b'aes-192-cfb': (24, 16, CtypesCrypto),
+ b'aes-256-cfb': (32, 16, CtypesCrypto),
+ b'aes-128-ofb': (16, 16, CtypesCrypto),
+ b'aes-192-ofb': (24, 16, CtypesCrypto),
+ b'aes-256-ofb': (32, 16, CtypesCrypto),
+ b'aes-128-ctr': (16, 16, CtypesCrypto),
+ b'aes-192-ctr': (24, 16, CtypesCrypto),
+ b'aes-256-ctr': (32, 16, CtypesCrypto),
+ b'aes-128-cfb8': (16, 16, CtypesCrypto),
+ b'aes-192-cfb8': (24, 16, CtypesCrypto),
+ b'aes-256-cfb8': (32, 16, CtypesCrypto),
+ b'aes-128-cfb1': (16, 16, CtypesCrypto),
+ b'aes-192-cfb1': (24, 16, CtypesCrypto),
+ b'aes-256-cfb1': (32, 16, CtypesCrypto),
+ b'bf-cfb': (16, 8, CtypesCrypto),
+ b'camellia-128-cfb': (16, 16, CtypesCrypto),
+ b'camellia-192-cfb': (24, 16, CtypesCrypto),
+ b'camellia-256-cfb': (32, 16, CtypesCrypto),
+ b'cast5-cfb': (16, 8, CtypesCrypto),
+ b'des-cfb': (8, 8, CtypesCrypto),
+ b'idea-cfb': (16, 8, CtypesCrypto),
+ b'rc2-cfb': (16, 8, CtypesCrypto),
+ b'rc4': (16, 0, CtypesCrypto),
+ b'seed-cfb': (16, 16, CtypesCrypto),
+}
+
+
+def run_method(method):
+ from shadowsocks.crypto import util
+
+ cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1)
+ decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+def test_aes_128_cfb():
+ run_method(b'aes-128-cfb')
+
+
+def test_aes_256_cfb():
+ run_method(b'aes-256-cfb')
+
+
+def test_aes_128_cfb8():
+ run_method(b'aes-128-cfb8')
+
+
+def test_aes_256_ofb():
+ run_method(b'aes-256-ofb')
+
+
+def test_aes_256_ctr():
+ run_method(b'aes-256-ctr')
+
+
+def test_bf_cfb():
+ run_method(b'bf-cfb')
+
+
+def test_rc4():
+ run_method(b'rc4')
+
+
+if __name__ == '__main__':
+ test_aes_128_cfb()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py
new file mode 100644
index 0000000000..0a8ca53fb8
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+from ctypes import c_char_p, c_int, c_long, byref,\
+ create_string_buffer, c_void_p
+
+from shadowsocks import common
+from shadowsocks.crypto import util
+
+__all__ = ['ciphers']
+
+libcrypto = None
+loaded = False
+
+buf_size = 2048
+
+
+def load_openssl():
+ global loaded, libcrypto, buf
+
+ libcrypto = util.find_library(('crypto', 'eay32'),
+ 'EVP_get_cipherbyname',
+ 'libcrypto')
+ if libcrypto is None:
+ raise Exception('libcrypto(OpenSSL) not found')
+
+ libcrypto.EVP_get_cipherbyname.restype = c_void_p
+ libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
+
+ libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_char_p,
+ c_char_p, c_char_p, c_int)
+
+ libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_void_p, c_void_p,
+ c_char_p, c_int)
+
+ if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"):
+ libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
+ else:
+ libcrypto.EVP_CIPHER_CTX_reset.argtypes = (c_void_p,)
+ libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,)
+
+ libcrypto.RAND_bytes.restype = c_int
+ libcrypto.RAND_bytes.argtypes = (c_void_p, c_int)
+
+ if hasattr(libcrypto, 'OpenSSL_add_all_ciphers'):
+ libcrypto.OpenSSL_add_all_ciphers()
+
+ buf = create_string_buffer(buf_size)
+ loaded = True
+
+
+def load_cipher(cipher_name):
+ func_name = 'EVP_' + cipher_name.replace('-', '_')
+ cipher = getattr(libcrypto, func_name, None)
+ if cipher:
+ cipher.restype = c_void_p
+ return cipher()
+ return None
+
+def rand_bytes(length):
+ if not loaded:
+ load_openssl()
+ buf = create_string_buffer(length)
+ r = libcrypto.RAND_bytes(buf, length)
+ if r <= 0:
+ raise Exception('RAND_bytes return error')
+ return buf.raw
+
+class OpenSSLCrypto(object):
+ def __init__(self, cipher_name, key, iv, op):
+ self._ctx = None
+ if not loaded:
+ load_openssl()
+ cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(cipher_name))
+ if not cipher:
+ cipher = load_cipher(cipher_name)
+ if not cipher:
+ raise Exception('cipher %s not found in libcrypto' % cipher_name)
+ key_ptr = c_char_p(key)
+ iv_ptr = c_char_p(iv)
+ self._ctx = libcrypto.EVP_CIPHER_CTX_new()
+ if not self._ctx:
+ raise Exception('can not create cipher context')
+ r = libcrypto.EVP_CipherInit_ex(self._ctx, cipher, None,
+ key_ptr, iv_ptr, c_int(op))
+ if not r:
+ self.clean()
+ raise Exception('can not initialize cipher context')
+
+ def update(self, data):
+ global buf_size, buf
+ cipher_out_len = c_long(0)
+ l = len(data)
+ if buf_size < l:
+ buf_size = l * 2
+ buf = create_string_buffer(buf_size)
+ libcrypto.EVP_CipherUpdate(self._ctx, byref(buf),
+ byref(cipher_out_len), c_char_p(data), l)
+ # buf is copied to a str object when we access buf.raw
+ return buf.raw[:cipher_out_len.value]
+
+ def __del__(self):
+ self.clean()
+
+ def clean(self):
+ if self._ctx:
+ if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"):
+ libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx)
+ else:
+ libcrypto.EVP_CIPHER_CTX_reset(self._ctx)
+ libcrypto.EVP_CIPHER_CTX_free(self._ctx)
+
+
+ciphers = {
+ 'aes-128-cbc': (16, 16, OpenSSLCrypto),
+ 'aes-192-cbc': (24, 16, OpenSSLCrypto),
+ 'aes-256-cbc': (32, 16, OpenSSLCrypto),
+ 'aes-128-cfb': (16, 16, OpenSSLCrypto),
+ 'aes-192-cfb': (24, 16, OpenSSLCrypto),
+ 'aes-256-cfb': (32, 16, OpenSSLCrypto),
+ 'aes-128-ofb': (16, 16, OpenSSLCrypto),
+ 'aes-192-ofb': (24, 16, OpenSSLCrypto),
+ 'aes-256-ofb': (32, 16, OpenSSLCrypto),
+ 'aes-128-ctr': (16, 16, OpenSSLCrypto),
+ 'aes-192-ctr': (24, 16, OpenSSLCrypto),
+ 'aes-256-ctr': (32, 16, OpenSSLCrypto),
+ 'aes-128-cfb8': (16, 16, OpenSSLCrypto),
+ 'aes-192-cfb8': (24, 16, OpenSSLCrypto),
+ 'aes-256-cfb8': (32, 16, OpenSSLCrypto),
+ 'aes-128-cfb1': (16, 16, OpenSSLCrypto),
+ 'aes-192-cfb1': (24, 16, OpenSSLCrypto),
+ 'aes-256-cfb1': (32, 16, OpenSSLCrypto),
+ 'bf-cfb': (16, 8, OpenSSLCrypto),
+ 'camellia-128-cfb': (16, 16, OpenSSLCrypto),
+ 'camellia-192-cfb': (24, 16, OpenSSLCrypto),
+ 'camellia-256-cfb': (32, 16, OpenSSLCrypto),
+ 'cast5-cfb': (16, 8, OpenSSLCrypto),
+ 'des-cfb': (8, 8, OpenSSLCrypto),
+ 'idea-cfb': (16, 8, OpenSSLCrypto),
+ 'rc2-cfb': (16, 8, OpenSSLCrypto),
+ 'rc4': (16, 0, OpenSSLCrypto),
+ 'seed-cfb': (16, 16, OpenSSLCrypto),
+}
+
+
+def run_method(method):
+
+ cipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 1)
+ decipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+def test_aes_128_cfb():
+ run_method('aes-128-cfb')
+
+
+def test_aes_256_cfb():
+ run_method('aes-256-cfb')
+
+
+def test_aes_128_cfb8():
+ run_method('aes-128-cfb8')
+
+
+def test_aes_256_ofb():
+ run_method('aes-256-ofb')
+
+
+def test_aes_256_ctr():
+ run_method('aes-256-ctr')
+
+
+def test_bf_cfb():
+ run_method('bf-cfb')
+
+
+def test_rc4():
+ run_method('rc4')
+
+
+if __name__ == '__main__':
+ test_aes_128_cfb()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py
new file mode 100644
index 0000000000..b0105ec29c
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import hashlib
+
+from shadowsocks.crypto import openssl
+
+__all__ = ['ciphers']
+
+
+def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None,
+ i=1, padding=1):
+ md5 = hashlib.md5()
+ md5.update(key)
+ md5.update(iv)
+ rc4_key = md5.digest()
+ return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op)
+
+
+ciphers = {
+ 'rc4-md5': (16, 16, create_cipher),
+ 'rc4-md5-6': (16, 6, create_cipher),
+}
+
+
+def test():
+ from shadowsocks.crypto import util
+
+ cipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 1)
+ decipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+if __name__ == '__main__':
+ test()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py
new file mode 100644
index 0000000000..51d476bedc
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+from ctypes import c_char_p, c_int, c_ulong, c_ulonglong, byref, \
+ create_string_buffer, c_void_p
+
+from shadowsocks.crypto import util
+
+__all__ = ['ciphers']
+
+libsodium = None
+loaded = False
+
+buf_size = 2048
+
+# for salsa20 and chacha20 and chacha20-ietf
+BLOCK_SIZE = 64
+
+
+def load_libsodium():
+ global loaded, libsodium, buf
+
+ libsodium = util.find_library('sodium', 'crypto_stream_salsa20_xor_ic',
+ 'libsodium')
+ if libsodium is None:
+ raise Exception('libsodium not found')
+
+ libsodium.crypto_stream_salsa20_xor_ic.restype = c_int
+ libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p,
+ c_ulonglong,
+ c_char_p, c_ulonglong,
+ c_char_p)
+ libsodium.crypto_stream_chacha20_xor_ic.restype = c_int
+ libsodium.crypto_stream_chacha20_xor_ic.argtypes = (c_void_p, c_char_p,
+ c_ulonglong,
+ c_char_p, c_ulonglong,
+ c_char_p)
+
+ try:
+ libsodium.crypto_stream_chacha20_ietf_xor_ic.restype = c_int
+ libsodium.crypto_stream_chacha20_ietf_xor_ic.argtypes = (c_void_p, c_char_p,
+ c_ulonglong,
+ c_char_p, c_ulong,
+ c_char_p)
+ except:
+ pass
+
+ buf = create_string_buffer(buf_size)
+ loaded = True
+
+
+class SodiumCrypto(object):
+ def __init__(self, cipher_name, key, iv, op):
+ if not loaded:
+ load_libsodium()
+ self.key = key
+ self.iv = iv
+ self.key_ptr = c_char_p(key)
+ self.iv_ptr = c_char_p(iv)
+ if cipher_name == 'salsa20':
+ self.cipher = libsodium.crypto_stream_salsa20_xor_ic
+ elif cipher_name == 'chacha20':
+ self.cipher = libsodium.crypto_stream_chacha20_xor_ic
+ elif cipher_name == 'chacha20-ietf':
+ self.cipher = libsodium.crypto_stream_chacha20_ietf_xor_ic
+ else:
+ raise Exception('Unknown cipher')
+ # byte counter, not block counter
+ self.counter = 0
+
+ def update(self, data):
+ global buf_size, buf
+ l = len(data)
+
+ # we can only prepend some padding to make the encryption align to
+ # blocks
+ padding = self.counter % BLOCK_SIZE
+ if buf_size < padding + l:
+ buf_size = (padding + l) * 2
+ buf = create_string_buffer(buf_size)
+
+ if padding:
+ data = (b'\0' * padding) + data
+ self.cipher(byref(buf), c_char_p(data), padding + l,
+ self.iv_ptr, int(self.counter / BLOCK_SIZE), self.key_ptr)
+ self.counter += l
+ # buf is copied to a str object when we access buf.raw
+ # strip off the padding
+ return buf.raw[padding:padding + l]
+
+
+ciphers = {
+ 'salsa20': (32, 8, SodiumCrypto),
+ 'chacha20': (32, 8, SodiumCrypto),
+ 'chacha20-ietf': (32, 12, SodiumCrypto),
+}
+
+
+def test_salsa20():
+ cipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 1)
+ decipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+def test_chacha20():
+
+ cipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 1)
+ decipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+
+def test_chacha20_ietf():
+
+ cipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 1)
+ decipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 0)
+
+ util.run_cipher(cipher, decipher)
+
+if __name__ == '__main__':
+ test_chacha20_ietf()
+ test_chacha20()
+ test_salsa20()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py
new file mode 100644
index 0000000000..60c2f24519
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py
@@ -0,0 +1,181 @@
+# !/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import string
+import struct
+import hashlib
+
+
+__all__ = ['ciphers']
+
+cached_tables = {}
+
+if hasattr(string, 'maketrans'):
+ maketrans = string.maketrans
+ translate = string.translate
+else:
+ maketrans = bytes.maketrans
+ translate = bytes.translate
+
+
+def get_table(key):
+ m = hashlib.md5()
+ m.update(key)
+ s = m.digest()
+ a, b = struct.unpack(' 0:
+ # parent waits for its child
+ time.sleep(5)
+ sys.exit(0)
+
+ # child signals its parent to exit
+ ppid = os.getppid()
+ pid = os.getpid()
+ if write_pid_file(pid_file, pid) != 0:
+ os.kill(ppid, signal.SIGINT)
+ sys.exit(1)
+
+ os.setsid()
+ signal.signal(signal.SIG_IGN, signal.SIGHUP)
+
+ print('started')
+ os.kill(ppid, signal.SIGTERM)
+
+ sys.stdin.close()
+ try:
+ freopen(log_file, 'a', sys.stdout)
+ freopen(log_file, 'a', sys.stderr)
+ except IOError as e:
+ shell.print_exception(e)
+ sys.exit(1)
+
+
+def daemon_stop(pid_file):
+ import errno
+ try:
+ with open(pid_file) as f:
+ buf = f.read()
+ pid = common.to_str(buf)
+ if not buf:
+ logging.error('not running')
+ except IOError as e:
+ shell.print_exception(e)
+ if e.errno == errno.ENOENT:
+ # always exit 0 if we are sure daemon is not running
+ logging.error('not running')
+ return
+ sys.exit(1)
+ pid = int(pid)
+ if pid > 0:
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except OSError as e:
+ if e.errno == errno.ESRCH:
+ logging.error('not running')
+ # always exit 0 if we are sure daemon is not running
+ return
+ shell.print_exception(e)
+ sys.exit(1)
+ else:
+ logging.error('pid is not positive: %d', pid)
+
+ # sleep for maximum 10s
+ for i in range(0, 200):
+ try:
+ # query for the pid
+ os.kill(pid, 0)
+ except OSError as e:
+ if e.errno == errno.ESRCH:
+ break
+ time.sleep(0.05)
+ else:
+ logging.error('timed out when stopping pid %d', pid)
+ sys.exit(1)
+ print('stopped')
+ os.unlink(pid_file)
+
+
+def set_user(username):
+ if username is None:
+ return
+
+ import pwd
+ import grp
+
+ try:
+ pwrec = pwd.getpwnam(username)
+ except KeyError:
+ logging.error('user not found: %s' % username)
+ raise
+ user = pwrec[0]
+ uid = pwrec[2]
+ gid = pwrec[3]
+
+ cur_uid = os.getuid()
+ if uid == cur_uid:
+ return
+ if cur_uid != 0:
+ logging.error('can not set user as nonroot user')
+ # will raise later
+
+ # inspired by supervisor
+ if hasattr(os, 'setgroups'):
+ groups = [grprec[2] for grprec in grp.getgrall() if user in grprec[3]]
+ groups.insert(0, gid)
+ os.setgroups(groups)
+ os.setgid(gid)
+ os.setuid(uid)
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py
new file mode 100644
index 0000000000..44f9052506
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+#
+# Copyright 2012-2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+
+from shadowsocks import common
+from shadowsocks.crypto import rc4_md5, openssl, sodium, table
+
+
+method_supported = {}
+method_supported.update(rc4_md5.ciphers)
+method_supported.update(openssl.ciphers)
+method_supported.update(sodium.ciphers)
+method_supported.update(table.ciphers)
+
+
+def random_string(length):
+ try:
+ return os.urandom(length)
+ except NotImplementedError as e:
+ return openssl.rand_bytes(length)
+
+cached_keys = {}
+
+
+def try_cipher(key, method=None):
+ Encryptor(key, method)
+
+
+def EVP_BytesToKey(password, key_len, iv_len):
+ # equivalent to OpenSSL's EVP_BytesToKey() with count 1
+ # so that we make the same key and iv as nodejs version
+ if hasattr(password, 'encode'):
+ password = password.encode('utf-8')
+ cached_key = '%s-%d-%d' % (password, key_len, iv_len)
+ r = cached_keys.get(cached_key, None)
+ if r:
+ return r
+ m = []
+ i = 0
+ while len(b''.join(m)) < (key_len + iv_len):
+ md5 = hashlib.md5()
+ data = password
+ if i > 0:
+ data = m[i - 1] + password
+ md5.update(data)
+ m.append(md5.digest())
+ i += 1
+ ms = b''.join(m)
+ key = ms[:key_len]
+ iv = ms[key_len:key_len + iv_len]
+ cached_keys[cached_key] = (key, iv)
+ return key, iv
+
+
+class Encryptor(object):
+ def __init__(self, key, method, iv = None):
+ self.key = key
+ self.method = method
+ self.iv = None
+ self.iv_sent = False
+ self.cipher_iv = b''
+ self.iv_buf = b''
+ self.cipher_key = b''
+ self.decipher = None
+ method = method.lower()
+ self._method_info = self.get_method_info(method)
+ if self._method_info:
+ if iv is None or len(iv) != self._method_info[1]:
+ self.cipher = self.get_cipher(key, method, 1,
+ random_string(self._method_info[1]))
+ else:
+ self.cipher = self.get_cipher(key, method, 1, iv)
+ else:
+ logging.error('method %s not supported' % method)
+ sys.exit(1)
+
+ def get_method_info(self, method):
+ method = method.lower()
+ m = method_supported.get(method)
+ return m
+
+ def iv_len(self):
+ return len(self.cipher_iv)
+
+ def get_cipher(self, password, method, op, iv):
+ password = common.to_bytes(password)
+ m = self._method_info
+ if m[0] > 0:
+ key, iv_ = EVP_BytesToKey(password, m[0], m[1])
+ else:
+ # key_length == 0 indicates we should use the key directly
+ key, iv = password, b''
+
+ iv = iv[:m[1]]
+ if op == 1:
+ # this iv is for cipher not decipher
+ self.cipher_iv = iv[:m[1]]
+ self.cipher_key = key
+ return m[2](method, key, iv, op)
+
+ def encrypt(self, buf):
+ if len(buf) == 0:
+ return buf
+ if self.iv_sent:
+ return self.cipher.update(buf)
+ else:
+ self.iv_sent = True
+ return self.cipher_iv + self.cipher.update(buf)
+
+ def decrypt(self, buf):
+ if len(buf) == 0:
+ return buf
+ if self.decipher is not None: #optimize
+ return self.decipher.update(buf)
+
+ decipher_iv_len = self._method_info[1]
+ if len(self.iv_buf) <= decipher_iv_len:
+ self.iv_buf += buf
+ if len(self.iv_buf) > decipher_iv_len:
+ decipher_iv = self.iv_buf[:decipher_iv_len]
+ self.decipher = self.get_cipher(self.key, self.method, 0,
+ iv=decipher_iv)
+ buf = self.iv_buf[decipher_iv_len:]
+ del self.iv_buf
+ return self.decipher.update(buf)
+ else:
+ return b''
+
+def encrypt_all(password, method, op, data):
+ result = []
+ method = method.lower()
+ (key_len, iv_len, m) = method_supported[method]
+ if key_len > 0:
+ key, _ = EVP_BytesToKey(password, key_len, iv_len)
+ else:
+ key = password
+ if op:
+ iv = random_string(iv_len)
+ result.append(iv)
+ else:
+ iv = data[:iv_len]
+ data = data[iv_len:]
+ cipher = m(method, key, iv, op)
+ result.append(cipher.update(data))
+ return b''.join(result)
+
+def encrypt_key(password, method):
+ method = method.lower()
+ (key_len, iv_len, m) = method_supported[method]
+ if key_len > 0:
+ key, _ = EVP_BytesToKey(password, key_len, iv_len)
+ else:
+ key = password
+ return key
+
+def encrypt_iv_len(method):
+ method = method.lower()
+ (key_len, iv_len, m) = method_supported[method]
+ return iv_len
+
+def encrypt_new_iv(method):
+ method = method.lower()
+ (key_len, iv_len, m) = method_supported[method]
+ return random_string(iv_len)
+
+def encrypt_all_iv(key, method, op, data, ref_iv):
+ result = []
+ method = method.lower()
+ (key_len, iv_len, m) = method_supported[method]
+ if op:
+ iv = ref_iv[0]
+ result.append(iv)
+ else:
+ iv = data[:iv_len]
+ data = data[iv_len:]
+ ref_iv[0] = iv
+ cipher = m(method, key, iv, op)
+ result.append(cipher.update(data))
+ return b''.join(result)
+
+
+CIPHERS_TO_TEST = [
+ 'aes-128-cfb',
+ 'aes-256-cfb',
+ 'rc4-md5',
+ 'salsa20',
+ 'chacha20',
+ 'table',
+]
+
+
+def test_encryptor():
+ from os import urandom
+ plain = urandom(10240)
+ for method in CIPHERS_TO_TEST:
+ logging.warn(method)
+ encryptor = Encryptor(b'key', method)
+ decryptor = Encryptor(b'key', method)
+ cipher = encryptor.encrypt(plain)
+ plain2 = decryptor.decrypt(cipher)
+ assert plain == plain2
+
+
+def test_encrypt_all():
+ from os import urandom
+ plain = urandom(10240)
+ for method in CIPHERS_TO_TEST:
+ logging.warn(method)
+ cipher = encrypt_all(b'key', method, 1, plain)
+ plain2 = encrypt_all(b'key', method, 0, cipher)
+ assert plain == plain2
+
+
+if __name__ == '__main__':
+ test_encrypt_all()
+ test_encryptor()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py
new file mode 100644
index 0000000000..d5e5077899
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py
@@ -0,0 +1,51 @@
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import sys
+import os
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../'))
+
+
+from shadowsocks.crypto import rc4_md5
+from shadowsocks.crypto import openssl
+from shadowsocks.crypto import sodium
+from shadowsocks.crypto import table
+
+def run(func):
+ try:
+ func()
+ except:
+ pass
+
+def run_n(func, name):
+ try:
+ func(name)
+ except:
+ pass
+
+def main():
+ print("\n""rc4_md5")
+ rc4_md5.test()
+ print("\n""aes-256-cfb")
+ openssl.test_aes_256_cfb()
+ print("\n""aes-128-cfb")
+ openssl.test_aes_128_cfb()
+ print("\n""bf-cfb")
+ run(openssl.test_bf_cfb)
+ print("\n""camellia-128-cfb")
+ run_n(openssl.run_method, "camellia-128-cfb")
+ print("\n""cast5-cfb")
+ run_n(openssl.run_method, "cast5-cfb")
+ print("\n""idea-cfb")
+ run_n(openssl.run_method, "idea-cfb")
+ print("\n""seed-cfb")
+ run_n(openssl.run_method, "seed-cfb")
+ print("\n""salsa20")
+ run(sodium.test_salsa20)
+ print("\n""chacha20")
+ run(sodium.test_chacha20)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py
new file mode 100644
index 0000000000..341620efd1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py
@@ -0,0 +1,258 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2013-2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# from ssloop
+# https://github.com/clowwindy/ssloop
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import time
+import socket
+import select
+import errno
+import logging
+from collections import defaultdict
+
+from shadowsocks import shell
+
+
+__all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR',
+ 'POLL_HUP', 'POLL_NVAL', 'EVENT_NAMES']
+
+POLL_NULL = 0x00
+POLL_IN = 0x01
+POLL_OUT = 0x04
+POLL_ERR = 0x08
+POLL_HUP = 0x10
+POLL_NVAL = 0x20
+
+
+EVENT_NAMES = {
+ POLL_NULL: 'POLL_NULL',
+ POLL_IN: 'POLL_IN',
+ POLL_OUT: 'POLL_OUT',
+ POLL_ERR: 'POLL_ERR',
+ POLL_HUP: 'POLL_HUP',
+ POLL_NVAL: 'POLL_NVAL',
+}
+
+# we check timeouts every TIMEOUT_PRECISION seconds
+TIMEOUT_PRECISION = 2
+
+
+class KqueueLoop(object):
+
+ MAX_EVENTS = 1024
+
+ def __init__(self):
+ self._kqueue = select.kqueue()
+ self._fds = {}
+
+ def _control(self, fd, mode, flags):
+ events = []
+ if mode & POLL_IN:
+ events.append(select.kevent(fd, select.KQ_FILTER_READ, flags))
+ if mode & POLL_OUT:
+ events.append(select.kevent(fd, select.KQ_FILTER_WRITE, flags))
+ for e in events:
+ self._kqueue.control([e], 0)
+
+ def poll(self, timeout):
+ if timeout < 0:
+ timeout = None # kqueue behaviour
+ events = self._kqueue.control(None, KqueueLoop.MAX_EVENTS, timeout)
+ results = defaultdict(lambda: POLL_NULL)
+ for e in events:
+ fd = e.ident
+ if e.filter == select.KQ_FILTER_READ:
+ results[fd] |= POLL_IN
+ elif e.filter == select.KQ_FILTER_WRITE:
+ results[fd] |= POLL_OUT
+ return results.items()
+
+ def register(self, fd, mode):
+ self._fds[fd] = mode
+ self._control(fd, mode, select.KQ_EV_ADD)
+
+ def unregister(self, fd):
+ self._control(fd, self._fds[fd], select.KQ_EV_DELETE)
+ del self._fds[fd]
+
+ def modify(self, fd, mode):
+ self.unregister(fd)
+ self.register(fd, mode)
+
+ def close(self):
+ self._kqueue.close()
+
+
+class SelectLoop(object):
+
+ def __init__(self):
+ self._r_list = set()
+ self._w_list = set()
+ self._x_list = set()
+
+ def poll(self, timeout):
+ r, w, x = select.select(self._r_list, self._w_list, self._x_list,
+ timeout)
+ results = defaultdict(lambda: POLL_NULL)
+ for p in [(r, POLL_IN), (w, POLL_OUT), (x, POLL_ERR)]:
+ for fd in p[0]:
+ results[fd] |= p[1]
+ return results.items()
+
+ def register(self, fd, mode):
+ if mode & POLL_IN:
+ self._r_list.add(fd)
+ if mode & POLL_OUT:
+ self._w_list.add(fd)
+ if mode & POLL_ERR:
+ self._x_list.add(fd)
+
+ def unregister(self, fd):
+ if fd in self._r_list:
+ self._r_list.remove(fd)
+ if fd in self._w_list:
+ self._w_list.remove(fd)
+ if fd in self._x_list:
+ self._x_list.remove(fd)
+
+ def modify(self, fd, mode):
+ self.unregister(fd)
+ self.register(fd, mode)
+
+ def close(self):
+ pass
+
+
+class EventLoop(object):
+ def __init__(self):
+ if hasattr(select, 'epoll'):
+ self._impl = select.epoll()
+ model = 'epoll'
+ elif hasattr(select, 'kqueue'):
+ self._impl = KqueueLoop()
+ model = 'kqueue'
+ elif hasattr(select, 'select'):
+ self._impl = SelectLoop()
+ model = 'select'
+ else:
+ raise Exception('can not find any available functions in select '
+ 'package')
+ self._fdmap = {} # (f, handler)
+ self._last_time = time.time()
+ self._periodic_callbacks = []
+ self._stopping = False
+ logging.debug('using event model: %s', model)
+
+ def poll(self, timeout=None):
+ events = self._impl.poll(timeout)
+ return [(self._fdmap[fd][0], fd, event) for fd, event in events]
+
+ def add(self, f, mode, handler):
+ fd = f.fileno()
+ self._fdmap[fd] = (f, handler)
+ self._impl.register(fd, mode)
+
+ def remove(self, f):
+ fd = f.fileno()
+ del self._fdmap[fd]
+ self._impl.unregister(fd)
+
+ def removefd(self, fd):
+ del self._fdmap[fd]
+ self._impl.unregister(fd)
+
+ def add_periodic(self, callback):
+ self._periodic_callbacks.append(callback)
+
+ def remove_periodic(self, callback):
+ self._periodic_callbacks.remove(callback)
+
+ def modify(self, f, mode):
+ fd = f.fileno()
+ self._impl.modify(fd, mode)
+
+ def stop(self):
+ self._stopping = True
+
+ def run(self):
+ events = []
+ while not self._stopping:
+ asap = False
+ try:
+ events = self.poll(TIMEOUT_PRECISION)
+ except (OSError, IOError) as e:
+ if errno_from_exception(e) in (errno.EPIPE, errno.EINTR):
+ # EPIPE: Happens when the client closes the connection
+ # EINTR: Happens when received a signal
+ # handles them as soon as possible
+ asap = True
+ logging.debug('poll:%s', e)
+ else:
+ logging.error('poll:%s', e)
+ import traceback
+ traceback.print_exc()
+ continue
+
+ handle = False
+ for sock, fd, event in events:
+ handler = self._fdmap.get(fd, None)
+ if handler is not None:
+ handler = handler[1]
+ try:
+ handle = handler.handle_event(sock, fd, event) or handle
+ except (OSError, IOError) as e:
+ shell.print_exception(e)
+ now = time.time()
+ if asap or now - self._last_time >= TIMEOUT_PRECISION:
+ for callback in self._periodic_callbacks:
+ callback()
+ self._last_time = now
+ if events and not handle:
+ time.sleep(0.001)
+
+ def __del__(self):
+ self._impl.close()
+
+
+# from tornado
+def errno_from_exception(e):
+ """Provides the errno from an Exception object.
+
+ There are cases that the errno attribute was not set so we pull
+ the errno out of the args but if someone instatiates an Exception
+ without any args you will get a tuple error. So this function
+ abstracts all that behavior to give you a safe way to get the
+ errno.
+ """
+
+ if hasattr(e, 'errno'):
+ return e.errno
+ elif e.args:
+ return e.args[0]
+ else:
+ return None
+
+
+# from tornado
+def get_sock_error(sock):
+ error_number = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ return socket.error(error_number, os.strerror(error_number))
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py
new file mode 100644
index 0000000000..9f54d93066
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2012-2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import sys
+import os
+import logging
+import signal
+
+if __name__ == '__main__':
+ import inspect
+ file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))
+ sys.path.insert(0, os.path.join(file_path, '../'))
+
+from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns
+
+
+def main():
+ shell.check_python()
+
+ # fix py2exe
+ if hasattr(sys, "frozen") and sys.frozen in \
+ ("windows_exe", "console_exe"):
+ p = os.path.dirname(os.path.abspath(sys.executable))
+ os.chdir(p)
+
+ config = shell.get_config(True)
+
+ if not config.get('dns_ipv6', False):
+ asyncdns.IPV6_CONNECTION_SUPPORT = False
+
+ daemon.daemon_exec(config)
+ logging.info("local start with protocol[%s] password [%s] method [%s] obfs [%s] obfs_param [%s]" %
+ (config['protocol'], config['password'], config['method'], config['obfs'], config['obfs_param']))
+
+ try:
+ logging.info("starting local at %s:%d" %
+ (config['local_address'], config['local_port']))
+
+ dns_resolver = asyncdns.DNSResolver()
+ tcp_server = tcprelay.TCPRelay(config, dns_resolver, True)
+ udp_server = udprelay.UDPRelay(config, dns_resolver, True)
+ loop = eventloop.EventLoop()
+ dns_resolver.add_to_loop(loop)
+ tcp_server.add_to_loop(loop)
+ udp_server.add_to_loop(loop)
+
+ def handler(signum, _):
+ logging.warn('received SIGQUIT, doing graceful shutting down..')
+ tcp_server.close(next_tick=True)
+ udp_server.close(next_tick=True)
+ signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), handler)
+
+ def int_handler(signum, _):
+ sys.exit(1)
+ signal.signal(signal.SIGINT, int_handler)
+
+ daemon.set_user(config.get('user', None))
+ loop.run()
+ except Exception as e:
+ shell.print_exception(e)
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh
new file mode 100755
index 0000000000..fc081e1d2b
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+cd `dirname $0`
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
+ulimit -n 512000
+nohup ${python_ver} server.py a>> ssserver.log 2>&1 &
+
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py
new file mode 100644
index 0000000000..ab0d210861
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py
@@ -0,0 +1,179 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import collections
+import logging
+import time
+
+if __name__ == '__main__':
+ import os, sys, inspect
+ file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))
+ sys.path.insert(0, os.path.join(file_path, '../'))
+
+try:
+ from collections import OrderedDict
+except:
+ from shadowsocks.ordereddict import OrderedDict
+
+# this LRUCache is optimized for concurrency, not QPS
+# n: concurrency, keys stored in the cache
+# m: visits not timed out, proportional to QPS * timeout
+# get & set is O(1), not O(n). thus we can support very large n
+# sweep is O((n - m)) or O(1024) at most,
+# no metter how large the cache or timeout value is
+
+SWEEP_MAX_ITEMS = 1024
+
+class LRUCache(collections.MutableMapping):
+ """This class is not thread safe"""
+
+ def __init__(self, timeout=60, close_callback=None, *args, **kwargs):
+ self.timeout = timeout
+ self.close_callback = close_callback
+ self._store = {}
+ self._keys_to_last_time = OrderedDict()
+ self.update(dict(*args, **kwargs)) # use the free update to set keys
+
+ def __getitem__(self, key):
+ # O(1)
+ t = time.time()
+ last_t = self._keys_to_last_time[key]
+ del self._keys_to_last_time[key]
+ self._keys_to_last_time[key] = t
+ return self._store[key]
+
+ def __setitem__(self, key, value):
+ # O(1)
+ t = time.time()
+ if key in self._keys_to_last_time:
+ del self._keys_to_last_time[key]
+ self._keys_to_last_time[key] = t
+ self._store[key] = value
+
+ def __delitem__(self, key):
+ # O(1)
+ last_t = self._keys_to_last_time[key]
+ del self._store[key]
+ del self._keys_to_last_time[key]
+
+ def __contains__(self, key):
+ return key in self._store
+
+ def __iter__(self):
+ return iter(self._store)
+
+ def __len__(self):
+ return len(self._store)
+
+ def first(self):
+ if len(self._keys_to_last_time) > 0:
+ for key in self._keys_to_last_time:
+ return key
+
+ def sweep(self, sweep_item_cnt = SWEEP_MAX_ITEMS):
+ # O(n - m)
+ now = time.time()
+ c = 0
+ while c < sweep_item_cnt:
+ if len(self._keys_to_last_time) == 0:
+ break
+ for key in self._keys_to_last_time:
+ break
+ last_t = self._keys_to_last_time[key]
+ if now - last_t <= self.timeout:
+ break
+ value = self._store[key]
+ del self._store[key]
+ del self._keys_to_last_time[key]
+ if self.close_callback is not None:
+ self.close_callback(value)
+ c += 1
+ if c:
+ logging.debug('%d keys swept' % c)
+ return c < SWEEP_MAX_ITEMS
+
+ def clear(self, keep):
+ now = time.time()
+ c = 0
+ while len(self._keys_to_last_time) > keep:
+ if len(self._keys_to_last_time) == 0:
+ break
+ for key in self._keys_to_last_time:
+ break
+ last_t = self._keys_to_last_time[key]
+ value = self._store[key]
+ if self.close_callback is not None:
+ self.close_callback(value)
+ del self._store[key]
+ del self._keys_to_last_time[key]
+ c += 1
+ if c:
+ logging.debug('%d keys swept' % c)
+ return c < SWEEP_MAX_ITEMS
+
+def test():
+ c = LRUCache(timeout=0.3)
+
+ c['a'] = 1
+ assert c['a'] == 1
+ c['a'] = 1
+
+ time.sleep(0.5)
+ c.sweep()
+ assert 'a' not in c
+
+ c['a'] = 2
+ c['b'] = 3
+ time.sleep(0.2)
+ c.sweep()
+ assert c['a'] == 2
+ assert c['b'] == 3
+
+ time.sleep(0.2)
+ c.sweep()
+ c['b']
+ time.sleep(0.2)
+ c.sweep()
+ assert 'a' not in c
+ assert c['b'] == 3
+
+ time.sleep(0.5)
+ c.sweep()
+ assert 'a' not in c
+ assert 'b' not in c
+
+ global close_cb_called
+ close_cb_called = False
+
+ def close_cb(t):
+ global close_cb_called
+ assert not close_cb_called
+ close_cb_called = True
+
+ c = LRUCache(timeout=0.1, close_callback=close_cb)
+ c['s'] = 1
+ c['s']
+ time.sleep(0.1)
+ c['s']
+ time.sleep(0.3)
+ c.sweep()
+
+if __name__ == '__main__':
+ test()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py
new file mode 100644
index 0000000000..80d0a320bb
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py
@@ -0,0 +1,291 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import errno
+import traceback
+import socket
+import logging
+import json
+import collections
+
+from shadowsocks import common, eventloop, tcprelay, udprelay, asyncdns, shell
+
+
+BUF_SIZE = 1506
+STAT_SEND_LIMIT = 50
+
+
+class Manager(object):
+
+ def __init__(self, config):
+ self._config = config
+ self._relays = {} # (tcprelay, udprelay)
+ self._loop = eventloop.EventLoop()
+ self._dns_resolver = asyncdns.DNSResolver()
+ self._dns_resolver.add_to_loop(self._loop)
+
+ self._statistics = collections.defaultdict(int)
+ self._control_client_addr = None
+ try:
+ manager_address = common.to_str(config['manager_address'])
+ if ':' in manager_address:
+ addr = manager_address.rsplit(':', 1)
+ addr = addr[0], int(addr[1])
+ addrs = socket.getaddrinfo(addr[0], addr[1])
+ if addrs:
+ family = addrs[0][0]
+ else:
+ logging.error('invalid address: %s', manager_address)
+ exit(1)
+ else:
+ addr = manager_address
+ family = socket.AF_UNIX
+ self._control_socket = socket.socket(family,
+ socket.SOCK_DGRAM)
+ self._control_socket.bind(addr)
+ self._control_socket.setblocking(False)
+ except (OSError, IOError) as e:
+ logging.error(e)
+ logging.error('can not bind to manager address')
+ exit(1)
+ self._loop.add(self._control_socket,
+ eventloop.POLL_IN, self)
+ self._loop.add_periodic(self.handle_periodic)
+
+ port_password = config['port_password']
+ del config['port_password']
+ for port, password in port_password.items():
+ a_config = config.copy()
+ a_config['server_port'] = int(port)
+ a_config['password'] = password
+ self.add_port(a_config)
+
+ def add_port(self, config):
+ port = int(config['server_port'])
+ servers = self._relays.get(port, None)
+ if servers:
+ logging.error("server already exists at %s:%d" % (config['server'],
+ port))
+ return
+ logging.info("adding server at %s:%d" % (config['server'], port))
+ t = tcprelay.TCPRelay(config, self._dns_resolver, False,
+ stat_callback=self.stat_callback)
+ u = udprelay.UDPRelay(config, self._dns_resolver, False,
+ stat_callback=self.stat_callback)
+ t.add_to_loop(self._loop)
+ u.add_to_loop(self._loop)
+ self._relays[port] = (t, u)
+
+ def remove_port(self, config):
+ port = int(config['server_port'])
+ servers = self._relays.get(port, None)
+ if servers:
+ logging.info("removing server at %s:%d" % (config['server'], port))
+ t, u = servers
+ t.close(next_tick=False)
+ u.close(next_tick=False)
+ del self._relays[port]
+ else:
+ logging.error("server not exist at %s:%d" % (config['server'],
+ port))
+
+ def handle_event(self, sock, fd, event):
+ if sock == self._control_socket and event == eventloop.POLL_IN:
+ data, self._control_client_addr = sock.recvfrom(BUF_SIZE)
+ parsed = self._parse_command(data)
+ if parsed:
+ command, config = parsed
+ a_config = self._config.copy()
+ if config:
+ # let the command override the configuration file
+ a_config.update(config)
+ if 'server_port' not in a_config:
+ logging.error('can not find server_port in config')
+ else:
+ if command == 'add':
+ self.add_port(a_config)
+ self._send_control_data(b'ok')
+ elif command == 'remove':
+ self.remove_port(a_config)
+ self._send_control_data(b'ok')
+ elif command == 'ping':
+ self._send_control_data(b'pong')
+ else:
+ logging.error('unknown command %s', command)
+
+ def _parse_command(self, data):
+ # commands:
+ # add: {"server_port": 8000, "password": "foobar"}
+ # remove: {"server_port": 8000"}
+ data = common.to_str(data)
+ parts = data.split(':', 1)
+ if len(parts) < 2:
+ return data, None
+ command, config_json = parts
+ try:
+ config = shell.parse_json_in_str(config_json)
+ return command, config
+ except Exception as e:
+ logging.error(e)
+ return None
+
+ def stat_callback(self, port, data_len):
+ self._statistics[port] += data_len
+
+ def handle_periodic(self):
+ r = {}
+ i = 0
+
+ def send_data(data_dict):
+ if data_dict:
+ # use compact JSON format (without space)
+ data = common.to_bytes(json.dumps(data_dict,
+ separators=(',', ':')))
+ self._send_control_data(b'stat: ' + data)
+
+ for k, v in self._statistics.items():
+ r[k] = v
+ i += 1
+ # split the data into segments that fit in UDP packets
+ if i >= STAT_SEND_LIMIT:
+ send_data(r)
+ r.clear()
+ i = 0
+ if len(r) > 0 :
+ send_data(r)
+ self._statistics.clear()
+
+ def _send_control_data(self, data):
+ if self._control_client_addr:
+ try:
+ self._control_socket.sendto(data, self._control_client_addr)
+ except (socket.error, OSError, IOError) as e:
+ error_no = eventloop.errno_from_exception(e)
+ if error_no in (errno.EAGAIN, errno.EINPROGRESS,
+ errno.EWOULDBLOCK):
+ return
+ else:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+
+ def run(self):
+ self._loop.run()
+
+
+def run(config):
+ Manager(config).run()
+
+
+def test():
+ import time
+ import threading
+ import struct
+ from shadowsocks import encrypt
+
+ logging.basicConfig(level=5,
+ format='%(asctime)s %(levelname)-8s %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+ enc = []
+ eventloop.TIMEOUT_PRECISION = 1
+
+ def run_server():
+ config = shell.get_config(True)
+ config = config.copy()
+ a_config = {
+ 'server': '127.0.0.1',
+ 'local_port': 1081,
+ 'port_password': {
+ '8381': 'foobar1',
+ '8382': 'foobar2'
+ },
+ 'method': 'aes-256-cfb',
+ 'manager_address': '127.0.0.1:6001',
+ 'timeout': 60,
+ 'fast_open': False,
+ 'verbose': 2
+ }
+ config.update(a_config)
+ manager = Manager(config)
+ enc.append(manager)
+ manager.run()
+
+ t = threading.Thread(target=run_server)
+ t.start()
+ time.sleep(1)
+ manager = enc[0]
+ cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ cli.connect(('127.0.0.1', 6001))
+
+ # test add and remove
+ time.sleep(1)
+ cli.send(b'add: {"server_port":7001, "password":"asdfadsfasdf"}')
+ time.sleep(1)
+ assert 7001 in manager._relays
+ data, addr = cli.recvfrom(1506)
+ assert b'ok' in data
+
+ cli.send(b'remove: {"server_port":8381}')
+ time.sleep(1)
+ assert 8381 not in manager._relays
+ data, addr = cli.recvfrom(1506)
+ assert b'ok' in data
+ logging.info('add and remove test passed')
+
+ # test statistics for TCP
+ header = common.pack_addr(b'google.com') + struct.pack('>H', 80)
+ data = encrypt.encrypt_all(b'asdfadsfasdf', 'aes-256-cfb', 1,
+ header + b'GET /\r\n\r\n')
+ tcp_cli = socket.socket()
+ tcp_cli.connect(('127.0.0.1', 7001))
+ tcp_cli.send(data)
+ tcp_cli.recv(4096)
+ tcp_cli.close()
+
+ data, addr = cli.recvfrom(1506)
+ data = common.to_str(data)
+ assert data.startswith('stat: ')
+ data = data.split('stat:')[1]
+ stats = shell.parse_json_in_str(data)
+ assert '7001' in stats
+ logging.info('TCP statistics test passed')
+
+ # test statistics for UDP
+ header = common.pack_addr(b'127.0.0.1') + struct.pack('>H', 80)
+ data = encrypt.encrypt_all(b'foobar2', 'aes-256-cfb', 1,
+ header + b'test')
+ udp_cli = socket.socket(type=socket.SOCK_DGRAM)
+ udp_cli.sendto(data, ('127.0.0.1', 8382))
+ tcp_cli.close()
+
+ data, addr = cli.recvfrom(1506)
+ data = common.to_str(data)
+ assert data.startswith('stat: ')
+ data = data.split('stat:')[1]
+ stats = json.loads(data)
+ assert '8382' in stats
+ logging.info('UDP statistics test passed')
+
+ manager._loop.stop()
+ t.join()
+
+
+if __name__ == '__main__':
+ test()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py
new file mode 100644
index 0000000000..3dfdb141f1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2015 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+
+from shadowsocks import common
+from shadowsocks.obfsplugin import plain, http_simple, obfs_tls, verify, auth, auth_chain
+
+
+method_supported = {}
+method_supported.update(plain.obfs_map)
+method_supported.update(http_simple.obfs_map)
+method_supported.update(obfs_tls.obfs_map)
+method_supported.update(verify.obfs_map)
+method_supported.update(auth.obfs_map)
+method_supported.update(auth_chain.obfs_map)
+
+def mu_protocol():
+ return ["auth_aes128_md5", "auth_aes128_sha1", "auth_chain_a"]
+
+class server_info(object):
+ def __init__(self, data):
+ self.data = data
+
+class obfs(object):
+ def __init__(self, method):
+ method = common.to_str(method)
+ self.method = method
+ self._method_info = self.get_method_info(method)
+ if self._method_info:
+ self.obfs = self.get_obfs(method)
+ else:
+ raise Exception('obfs plugin [%s] not supported' % method)
+
+ def init_data(self):
+ return self.obfs.init_data()
+
+ def set_server_info(self, server_info):
+ return self.obfs.set_server_info(server_info)
+
+ def get_server_info(self):
+ return self.obfs.get_server_info()
+
+ def get_method_info(self, method):
+ method = method.lower()
+ m = method_supported.get(method)
+ return m
+
+ def get_obfs(self, method):
+ m = self._method_info
+ return m[0](method)
+
+ def get_overhead(self, direction):
+ return self.obfs.get_overhead(direction)
+
+ def client_pre_encrypt(self, buf):
+ return self.obfs.client_pre_encrypt(buf)
+
+ def client_encode(self, buf):
+ return self.obfs.client_encode(buf)
+
+ def client_decode(self, buf):
+ return self.obfs.client_decode(buf)
+
+ def client_post_decrypt(self, buf):
+ return self.obfs.client_post_decrypt(buf)
+
+ def server_pre_encrypt(self, buf):
+ return self.obfs.server_pre_encrypt(buf)
+
+ def server_encode(self, buf):
+ return self.obfs.server_encode(buf)
+
+ def server_decode(self, buf):
+ return self.obfs.server_decode(buf)
+
+ def server_post_decrypt(self, buf):
+ return self.obfs.server_post_decrypt(buf)
+
+ def client_udp_pre_encrypt(self, buf):
+ return self.obfs.client_udp_pre_encrypt(buf)
+
+ def client_udp_post_decrypt(self, buf):
+ return self.obfs.client_udp_post_decrypt(buf)
+
+ def server_udp_pre_encrypt(self, buf, uid):
+ return self.obfs.server_udp_pre_encrypt(buf, uid)
+
+ def server_udp_post_decrypt(self, buf):
+ return self.obfs.server_udp_post_decrypt(buf)
+
+ def dispose(self):
+ self.obfs.dispose()
+ del self.obfs
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py
new file mode 100644
index 0000000000..401c7b726e
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py
new file mode 100644
index 0000000000..a745e098fc
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py
@@ -0,0 +1,787 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2015 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+import binascii
+import base64
+import time
+import datetime
+import random
+import math
+import struct
+import zlib
+import hmac
+import hashlib
+
+import shadowsocks
+from shadowsocks import common, lru_cache, encrypt
+from shadowsocks.obfsplugin import plain
+from shadowsocks.common import to_bytes, to_str, ord, chr
+
+def create_auth_sha1_v4(method):
+ return auth_sha1_v4(method)
+
+def create_auth_aes128_md5(method):
+ return auth_aes128_sha1(method, hashlib.md5)
+
+def create_auth_aes128_sha1(method):
+ return auth_aes128_sha1(method, hashlib.sha1)
+
+obfs_map = {
+ 'auth_sha1_v4': (create_auth_sha1_v4,),
+ 'auth_sha1_v4_compatible': (create_auth_sha1_v4,),
+ 'auth_aes128_md5': (create_auth_aes128_md5,),
+ 'auth_aes128_sha1': (create_auth_aes128_sha1,),
+}
+
+def match_begin(str1, str2):
+ if len(str1) >= len(str2):
+ if str1[:len(str2)] == str2:
+ return True
+ return False
+
+class auth_base(plain.plain):
+ def __init__(self, method):
+ super(auth_base, self).__init__(method)
+ self.method = method
+ self.no_compatible_method = ''
+ self.overhead = 7
+
+ def init_data(self):
+ return ''
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return self.overhead
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+
+ def client_encode(self, buf):
+ return buf
+
+ def client_decode(self, buf):
+ return (buf, False)
+
+ def server_encode(self, buf):
+ return buf
+
+ def server_decode(self, buf):
+ return (buf, True, False)
+
+ def not_match_return(self, buf):
+ self.raw_trans = True
+ self.overhead = 0
+ if self.method == self.no_compatible_method:
+ return (b'E'*2048, False)
+ return (buf, False)
+
+class client_queue(object):
+ def __init__(self, begin_id):
+ self.front = begin_id - 64
+ self.back = begin_id + 1
+ self.alloc = {}
+ self.enable = True
+ self.last_update = time.time()
+
+ def update(self):
+ self.last_update = time.time()
+
+ def is_active(self):
+ return time.time() - self.last_update < 60 * 3
+
+ def re_enable(self, connection_id):
+ self.enable = True
+ self.front = connection_id - 64
+ self.back = connection_id + 1
+ self.alloc = {}
+
+ def insert(self, connection_id):
+ if not self.enable:
+ logging.warn('obfs auth: not enable')
+ return False
+ if not self.is_active():
+ self.re_enable(connection_id)
+ self.update()
+ if connection_id < self.front:
+ logging.warn('obfs auth: deprecated id, someone replay attack')
+ return False
+ if connection_id > self.front + 0x4000:
+ logging.warn('obfs auth: wrong id')
+ return False
+ if connection_id in self.alloc:
+ logging.warn('obfs auth: duplicate id, someone replay attack')
+ return False
+ if self.back <= connection_id:
+ self.back = connection_id + 1
+ self.alloc[connection_id] = 1
+ while (self.front in self.alloc) or self.front + 0x1000 < self.back:
+ if self.front in self.alloc:
+ del self.alloc[self.front]
+ self.front += 1
+ return True
+
+class obfs_auth_v2_data(object):
+ def __init__(self):
+ self.client_id = lru_cache.LRUCache()
+ self.local_client_id = b''
+ self.connection_id = 0
+ self.set_max_client(64) # max active client count
+
+ def update(self, client_id, connection_id):
+ if client_id in self.client_id:
+ self.client_id[client_id].update()
+
+ def set_max_client(self, max_client):
+ self.max_client = max_client
+ self.max_buffer = max(self.max_client * 2, 1024)
+
+ def insert(self, client_id, connection_id):
+ if self.client_id.get(client_id, None) is None or not self.client_id[client_id].enable:
+ if self.client_id.first() is None or len(self.client_id) < self.max_client:
+ if client_id not in self.client_id:
+ #TODO: check
+ self.client_id[client_id] = client_queue(connection_id)
+ else:
+ self.client_id[client_id].re_enable(connection_id)
+ return self.client_id[client_id].insert(connection_id)
+
+ if not self.client_id[self.client_id.first()].is_active():
+ del self.client_id[self.client_id.first()]
+ if client_id not in self.client_id:
+ #TODO: check
+ self.client_id[client_id] = client_queue(connection_id)
+ else:
+ self.client_id[client_id].re_enable(connection_id)
+ return self.client_id[client_id].insert(connection_id)
+
+ logging.warn('auth_sha1_v2: no inactive client')
+ return False
+ else:
+ return self.client_id[client_id].insert(connection_id)
+
+class auth_sha1_v4(auth_base):
+ def __init__(self, method):
+ super(auth_sha1_v4, self).__init__(method)
+ self.recv_buf = b''
+ self.unit_len = 8100
+ self.decrypt_packet_num = 0
+ self.raw_trans = False
+ self.has_sent_header = False
+ self.has_recv_header = False
+ self.client_id = 0
+ self.connection_id = 0
+ self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
+ self.salt = b"auth_sha1_v4"
+ self.no_compatible_method = 'auth_sha1_v4'
+
+ def init_data(self):
+ return obfs_auth_v2_data()
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+ try:
+ max_client = int(server_info.protocol_param)
+ except:
+ max_client = 64
+ self.server_info.data.set_max_client(max_client)
+
+ def rnd_data(self, buf_size):
+ if buf_size > 1200:
+ return b'\x01'
+
+ if buf_size > 400:
+ rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 256)
+ else:
+ rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 512)
+
+ if len(rnd_data) < 128:
+ return common.chr(len(rnd_data) + 1) + rnd_data
+ else:
+ return common.chr(255) + struct.pack('>H', len(rnd_data) + 3) + rnd_data
+
+ def pack_data(self, buf):
+ data = self.rnd_data(len(buf)) + buf
+ data_len = len(data) + 8
+ crc = binascii.crc32(struct.pack('>H', data_len)) & 0xFFFF
+ data = struct.pack('H', data_len) + data
+ adler32 = zlib.adler32(data) & 0xFFFFFFFF
+ data += struct.pack('H', data_len) + self.salt + self.server_info.key) & 0xFFFFFFFF
+ data = struct.pack('H', data_len) + data
+ data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10]
+ return data
+
+ def auth_data(self):
+ utc_time = int(time.time()) & 0xFFFFFFFF
+ if self.server_info.data.connection_id > 0xFF000000:
+ self.server_info.data.local_client_id = b''
+ if not self.server_info.data.local_client_id:
+ self.server_info.data.local_client_id = os.urandom(4)
+ logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),))
+ self.server_info.data.connection_id = struct.unpack(' self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf)
+ return ret
+
+ def client_post_decrypt(self, buf):
+ if self.raw_trans:
+ return buf
+ self.recv_buf += buf
+ out_buf = b''
+ while len(self.recv_buf) > 4:
+ crc = struct.pack('H', self.recv_buf[:2])[0]
+ if length >= 8192 or length < 7:
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data error')
+ if length > len(self.recv_buf):
+ break
+
+ if struct.pack('H', self.recv_buf[5:7])[0] + 4
+ out_buf += self.recv_buf[pos:length - 4]
+ self.recv_buf = self.recv_buf[length:]
+
+ if out_buf:
+ self.decrypt_packet_num += 1
+ return out_buf
+
+ def server_pre_encrypt(self, buf):
+ if self.raw_trans:
+ return buf
+ ret = b''
+ while len(buf) > self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf)
+ return ret
+
+ def server_post_decrypt(self, buf):
+ if self.raw_trans:
+ return (buf, False)
+ self.recv_buf += buf
+ out_buf = b''
+ sendback = False
+
+ if not self.has_recv_header:
+ if len(self.recv_buf) <= 6:
+ return (b'', False)
+ crc = struct.pack('H', self.recv_buf[:2])[0]
+ if length > len(self.recv_buf):
+ return (b'', False)
+ sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:length - 10], hashlib.sha1).digest()[:10]
+ if sha1data != self.recv_buf[length - 10:length]:
+ logging.error('auth_sha1_v4 data uncorrect auth HMAC-SHA1')
+ return self.not_match_return(self.recv_buf)
+ pos = common.ord(self.recv_buf[6])
+ if pos < 255:
+ pos += 6
+ else:
+ pos = struct.unpack('>H', self.recv_buf[7:9])[0] + 6
+ out_buf = self.recv_buf[pos:length - 10]
+ if len(out_buf) < 12:
+ logging.info('auth_sha1_v4: too short, data %s' % (binascii.hexlify(self.recv_buf),))
+ return self.not_match_return(self.recv_buf)
+ utc_time = struct.unpack(' self.max_time_dif:
+ logging.info('auth_sha1_v4: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(out_buf),))
+ return self.not_match_return(self.recv_buf)
+ elif self.server_info.data.insert(client_id, connection_id):
+ self.has_recv_header = True
+ out_buf = out_buf[12:]
+ self.client_id = client_id
+ self.connection_id = connection_id
+ else:
+ logging.info('auth_sha1_v4: auth fail, data %s' % (binascii.hexlify(out_buf),))
+ return self.not_match_return(self.recv_buf)
+ self.recv_buf = self.recv_buf[length:]
+ self.has_recv_header = True
+ sendback = True
+
+ while len(self.recv_buf) > 4:
+ crc = struct.pack('H', self.recv_buf[:2])[0]
+ if length >= 8192 or length < 7:
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.decrypt_packet_num == 0:
+ logging.info('auth_sha1_v4: over size')
+ return (b'E'*2048, False)
+ else:
+ raise Exception('server_post_decrype data error')
+ if length > len(self.recv_buf):
+ break
+
+ if struct.pack('H', self.recv_buf[5:7])[0] + 4
+ out_buf += self.recv_buf[pos:length - 4]
+ self.recv_buf = self.recv_buf[length:]
+ if pos == length - 4:
+ sendback = True
+
+ if out_buf:
+ self.server_info.data.update(self.client_id, self.connection_id)
+ self.decrypt_packet_num += 1
+ return (out_buf, sendback)
+
+class obfs_auth_mu_data(object):
+ def __init__(self):
+ self.user_id = {}
+ self.local_client_id = b''
+ self.connection_id = 0
+ self.set_max_client(64) # max active client count
+
+ def update(self, user_id, client_id, connection_id):
+ if user_id not in self.user_id:
+ self.user_id[user_id] = lru_cache.LRUCache()
+ local_client_id = self.user_id[user_id]
+
+ if client_id in local_client_id:
+ local_client_id[client_id].update()
+
+ def set_max_client(self, max_client):
+ self.max_client = max_client
+ self.max_buffer = max(self.max_client * 2, 1024)
+
+ def insert(self, user_id, client_id, connection_id):
+ if user_id not in self.user_id:
+ self.user_id[user_id] = lru_cache.LRUCache()
+ local_client_id = self.user_id[user_id]
+
+ if local_client_id.get(client_id, None) is None or not local_client_id[client_id].enable:
+ if local_client_id.first() is None or len(local_client_id) < self.max_client:
+ if client_id not in local_client_id:
+ #TODO: check
+ local_client_id[client_id] = client_queue(connection_id)
+ else:
+ local_client_id[client_id].re_enable(connection_id)
+ return local_client_id[client_id].insert(connection_id)
+
+ if not local_client_id[local_client_id.first()].is_active():
+ del local_client_id[local_client_id.first()]
+ if client_id not in local_client_id:
+ #TODO: check
+ local_client_id[client_id] = client_queue(connection_id)
+ else:
+ local_client_id[client_id].re_enable(connection_id)
+ return local_client_id[client_id].insert(connection_id)
+
+ logging.warn('auth_aes128: no inactive client')
+ return False
+ else:
+ return local_client_id[client_id].insert(connection_id)
+
+class auth_aes128_sha1(auth_base):
+ def __init__(self, method, hashfunc):
+ super(auth_aes128_sha1, self).__init__(method)
+ self.hashfunc = hashfunc
+ self.recv_buf = b''
+ self.unit_len = 8100
+ self.raw_trans = False
+ self.has_sent_header = False
+ self.has_recv_header = False
+ self.client_id = 0
+ self.connection_id = 0
+ self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
+ self.salt = hashfunc == hashlib.md5 and b"auth_aes128_md5" or b"auth_aes128_sha1"
+ self.no_compatible_method = hashfunc == hashlib.md5 and "auth_aes128_md5" or 'auth_aes128_sha1'
+ self.extra_wait_size = struct.unpack('>H', os.urandom(2))[0] % 1024
+ self.pack_id = 1
+ self.recv_id = 1
+ self.user_id = None
+ self.user_key = None
+ self.last_rnd_len = 0
+ self.overhead = 9
+
+ def init_data(self):
+ return obfs_auth_mu_data()
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return self.overhead
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+ try:
+ max_client = int(server_info.protocol_param.split('#')[0])
+ except:
+ max_client = 64
+ self.server_info.data.set_max_client(max_client)
+
+ def trapezoid_random_float(self, d):
+ if d == 0:
+ return random.random()
+ s = random.random()
+ a = 1 - d
+ return (math.sqrt(a * a + 4 * d * s) - a) / (2 * d)
+
+ def trapezoid_random_int(self, max_val, d):
+ v = self.trapezoid_random_float(d)
+ return int(v * max_val)
+
+ def rnd_data_len(self, buf_size, full_buf_size):
+ if full_buf_size >= self.server_info.buffer_size:
+ return 0
+ tcp_mss = self.server_info.tcp_mss
+ rev_len = tcp_mss - buf_size - 9
+ if rev_len == 0:
+ return 0
+ if rev_len < 0:
+ if rev_len > -tcp_mss:
+ return self.trapezoid_random_int(rev_len + tcp_mss, -0.3)
+ return common.ord(os.urandom(1)[0]) % 32
+ if buf_size > 900:
+ return struct.unpack('>H', os.urandom(2))[0] % rev_len
+ return self.trapezoid_random_int(rev_len, -0.3)
+
+ def rnd_data(self, buf_size, full_buf_size):
+ data_len = self.rnd_data_len(buf_size, full_buf_size)
+
+ if data_len < 128:
+ return common.chr(data_len + 1) + os.urandom(data_len)
+
+ return common.chr(255) + struct.pack(' 400:
+ rnd_len = struct.unpack(' 0xFF000000:
+ self.server_info.data.local_client_id = b''
+ if not self.server_info.data.local_client_id:
+ self.server_info.data.local_client_id = os.urandom(4)
+ logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),))
+ self.server_info.data.connection_id = struct.unpack(' self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len], ogn_data_len)
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf, ogn_data_len)
+ self.last_rnd_len = ogn_data_len
+ return ret
+
+ def client_post_decrypt(self, buf):
+ if self.raw_trans:
+ return buf
+ self.recv_buf += buf
+ out_buf = b''
+ while len(self.recv_buf) > 4:
+ mac_key = self.user_key + struct.pack('= 8192 or length < 7:
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data error')
+ if length > len(self.recv_buf):
+ break
+
+ if hmac.new(mac_key, self.recv_buf[:length - 4], self.hashfunc).digest()[:4] != self.recv_buf[length - 4:length]:
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data uncorrect checksum')
+
+ self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
+ pos = common.ord(self.recv_buf[4])
+ if pos < 255:
+ pos += 4
+ else:
+ pos = struct.unpack(' self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len], ogn_data_len)
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf, ogn_data_len)
+ self.last_rnd_len = ogn_data_len
+ return ret
+
+ def server_post_decrypt(self, buf):
+ if self.raw_trans:
+ return (buf, False)
+ self.recv_buf += buf
+ out_buf = b''
+ sendback = False
+
+ if not self.has_recv_header:
+ if len(self.recv_buf) >= 7 or len(self.recv_buf) in [2, 3]:
+ recv_len = min(len(self.recv_buf), 7)
+ mac_key = self.server_info.recv_iv + self.server_info.key
+ sha1data = hmac.new(mac_key, self.recv_buf[:1], self.hashfunc).digest()[:recv_len - 1]
+ if sha1data != self.recv_buf[1:recv_len]:
+ return self.not_match_return(self.recv_buf)
+
+ if len(self.recv_buf) < 31:
+ return (b'', False)
+ sha1data = hmac.new(mac_key, self.recv_buf[7:27], self.hashfunc).digest()[:4]
+ if sha1data != self.recv_buf[27:31]:
+ logging.error('%s data uncorrect auth HMAC-SHA1 from %s:%d, data %s' % (self.no_compatible_method, self.server_info.client, self.server_info.client_port, binascii.hexlify(self.recv_buf)))
+ if len(self.recv_buf) < 31 + self.extra_wait_size:
+ return (b'', False)
+ return self.not_match_return(self.recv_buf)
+
+ uid = self.recv_buf[7:11]
+ if uid in self.server_info.users:
+ self.user_id = uid
+ self.user_key = self.hashfunc(self.server_info.users[uid]).digest()
+ self.server_info.update_user_func(uid)
+ else:
+ if not self.server_info.users:
+ self.user_key = self.server_info.key
+ else:
+ self.user_key = self.server_info.recv_iv
+ encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc')
+ head = encryptor.decrypt(b'\x00' * 16 + self.recv_buf[11:27] + b'\x00') # need an extra byte or recv empty
+ length = struct.unpack(' self.max_time_dif:
+ logging.info('%s: wrong timestamp, time_dif %d, data %s' % (self.no_compatible_method, time_dif, binascii.hexlify(head)))
+ return self.not_match_return(self.recv_buf)
+ elif self.server_info.data.insert(self.user_id, client_id, connection_id):
+ self.has_recv_header = True
+ out_buf = self.recv_buf[31 + rnd_len:length - 4]
+ self.client_id = client_id
+ self.connection_id = connection_id
+ else:
+ logging.info('%s: auth fail, data %s' % (self.no_compatible_method, binascii.hexlify(out_buf)))
+ return self.not_match_return(self.recv_buf)
+ self.recv_buf = self.recv_buf[length:]
+ self.has_recv_header = True
+ sendback = True
+
+ while len(self.recv_buf) > 4:
+ mac_key = self.user_key + struct.pack('= 8192 or length < 7:
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.recv_id == 0:
+ logging.info(self.no_compatible_method + ': over size')
+ return (b'E'*2048, False)
+ else:
+ raise Exception('server_post_decrype data error')
+ if length > len(self.recv_buf):
+ break
+
+ if hmac.new(mac_key, self.recv_buf[:length - 4], self.hashfunc).digest()[:4] != self.recv_buf[length - 4:length]:
+ logging.info('%s: checksum error, data %s' % (self.no_compatible_method, binascii.hexlify(self.recv_buf[:length])))
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.recv_id == 0:
+ return (b'E'*2048, False)
+ else:
+ raise Exception('server_post_decrype data uncorrect checksum')
+
+ self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
+ pos = common.ord(self.recv_buf[4])
+ if pos < 255:
+ pos += 4
+ else:
+ pos = struct.unpack('> 17) ^ (y >> 26)) & xorshift128plus.max_int
+ self.v1 = x
+ return (x + y) & xorshift128plus.max_int
+
+ def init_from_bin(self, bin):
+ bin += b'\0' * 16
+ self.v0 = struct.unpack('= len(str2):
+ if str1[:len(str2)] == str2:
+ return True
+ return False
+
+
+class auth_base(plain.plain):
+ def __init__(self, method):
+ super(auth_base, self).__init__(method)
+ self.method = method
+ self.no_compatible_method = ''
+ self.overhead = 4
+
+ def init_data(self):
+ return ''
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return self.overhead
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+
+ def client_encode(self, buf):
+ return buf
+
+ def client_decode(self, buf):
+ return (buf, False)
+
+ def server_encode(self, buf):
+ return buf
+
+ def server_decode(self, buf):
+ return (buf, True, False)
+
+ def not_match_return(self, buf):
+ self.raw_trans = True
+ self.overhead = 0
+ if self.method == self.no_compatible_method:
+ return (b'E' * 2048, False)
+ return (buf, False)
+
+
+class client_queue(object):
+ def __init__(self, begin_id):
+ self.front = begin_id - 64
+ self.back = begin_id + 1
+ self.alloc = {}
+ self.enable = True
+ self.last_update = time.time()
+ self.ref = 0
+
+ def update(self):
+ self.last_update = time.time()
+
+ def addref(self):
+ self.ref += 1
+
+ def delref(self):
+ if self.ref > 0:
+ self.ref -= 1
+
+ def is_active(self):
+ return (self.ref > 0) and (time.time() - self.last_update < 60 * 10)
+
+ def re_enable(self, connection_id):
+ self.enable = True
+ self.front = connection_id - 64
+ self.back = connection_id + 1
+ self.alloc = {}
+
+ def insert(self, connection_id):
+ if not self.enable:
+ logging.warn('obfs auth: not enable')
+ return False
+ if not self.is_active():
+ self.re_enable(connection_id)
+ self.update()
+ if connection_id < self.front:
+ logging.warn('obfs auth: deprecated id, someone replay attack')
+ return False
+ if connection_id > self.front + 0x4000:
+ logging.warn('obfs auth: wrong id')
+ return False
+ if connection_id in self.alloc:
+ logging.warn('obfs auth: duplicate id, someone replay attack')
+ return False
+ if self.back <= connection_id:
+ self.back = connection_id + 1
+ self.alloc[connection_id] = 1
+ while (self.front in self.alloc) or self.front + 0x1000 < self.back:
+ if self.front in self.alloc:
+ del self.alloc[self.front]
+ self.front += 1
+ self.addref()
+ return True
+
+
+class obfs_auth_chain_data(object):
+ def __init__(self, name):
+ self.name = name
+ self.user_id = {}
+ self.local_client_id = b''
+ self.connection_id = 0
+ self.set_max_client(64) # max active client count
+
+ def update(self, user_id, client_id, connection_id):
+ if user_id not in self.user_id:
+ self.user_id[user_id] = lru_cache.LRUCache()
+ local_client_id = self.user_id[user_id]
+
+ if client_id in local_client_id:
+ local_client_id[client_id].update()
+
+ def set_max_client(self, max_client):
+ self.max_client = max_client
+ self.max_buffer = max(self.max_client * 2, 1024)
+
+ def insert(self, user_id, client_id, connection_id):
+ if user_id not in self.user_id:
+ self.user_id[user_id] = lru_cache.LRUCache()
+ local_client_id = self.user_id[user_id]
+
+ if local_client_id.get(client_id, None) is None or not local_client_id[client_id].enable:
+ if local_client_id.first() is None or len(local_client_id) < self.max_client:
+ if client_id not in local_client_id:
+ # TODO: check
+ local_client_id[client_id] = client_queue(connection_id)
+ else:
+ local_client_id[client_id].re_enable(connection_id)
+ return local_client_id[client_id].insert(connection_id)
+
+ if not local_client_id[local_client_id.first()].is_active():
+ del local_client_id[local_client_id.first()]
+ if client_id not in local_client_id:
+ # TODO: check
+ local_client_id[client_id] = client_queue(connection_id)
+ else:
+ local_client_id[client_id].re_enable(connection_id)
+ return local_client_id[client_id].insert(connection_id)
+
+ logging.warn(self.name + ': no inactive client')
+ return False
+ else:
+ return local_client_id[client_id].insert(connection_id)
+
+ def remove(self, user_id, client_id):
+ if user_id in self.user_id:
+ local_client_id = self.user_id[user_id]
+ if client_id in local_client_id:
+ local_client_id[client_id].delref()
+
+
+class auth_chain_a(auth_base):
+ def __init__(self, method):
+ super(auth_chain_a, self).__init__(method)
+ self.hashfunc = hashlib.md5
+ self.recv_buf = b''
+ self.unit_len = 2800
+ self.raw_trans = False
+ self.has_sent_header = False
+ self.has_recv_header = False
+ self.client_id = 0
+ self.connection_id = 0
+ self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
+ self.salt = b"auth_chain_a"
+ self.no_compatible_method = 'auth_chain_a'
+ self.pack_id = 1
+ self.recv_id = 1
+ self.user_id = None
+ self.user_id_num = 0
+ self.user_key = None
+ self.overhead = 4
+ self.client_over_head = 4
+ self.last_client_hash = b''
+ self.last_server_hash = b''
+ self.random_client = xorshift128plus()
+ self.random_server = xorshift128plus()
+ self.encryptor = None
+
+ def init_data(self):
+ return obfs_auth_chain_data(self.method)
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return self.overhead
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+ try:
+ max_client = int(server_info.protocol_param.split('#')[0])
+ except:
+ max_client = 64
+ self.server_info.data.set_max_client(max_client)
+
+ def trapezoid_random_float(self, d):
+ if d == 0:
+ return random.random()
+ s = random.random()
+ a = 1 - d
+ return (math.sqrt(a * a + 4 * d * s) - a) / (2 * d)
+
+ def trapezoid_random_int(self, max_val, d):
+ v = self.trapezoid_random_float(d)
+ return int(v * max_val)
+
+ def rnd_data_len(self, buf_size, last_hash, random):
+ if buf_size > 1440:
+ return 0
+ random.init_from_bin_len(last_hash, buf_size)
+ if buf_size > 1300:
+ return random.next() % 31
+ if buf_size > 900:
+ return random.next() % 127
+ if buf_size > 400:
+ return random.next() % 521
+ return random.next() % 1021
+
+ def udp_rnd_data_len(self, last_hash, random):
+ random.init_from_bin(last_hash)
+ return random.next() % 127
+
+ def rnd_start_pos(self, rand_len, random):
+ if rand_len > 0:
+ return random.next() % 8589934609 % rand_len
+ return 0
+
+ def rnd_data(self, buf_size, buf, last_hash, random):
+ rand_len = self.rnd_data_len(buf_size, last_hash, random)
+
+ rnd_data_buf = os.urandom(rand_len)
+
+ if buf_size == 0:
+ return rnd_data_buf
+ else:
+ if rand_len > 0:
+ start_pos = self.rnd_start_pos(rand_len, random)
+ return rnd_data_buf[:start_pos] + buf + rnd_data_buf[start_pos:]
+ else:
+ return buf
+
+ def pack_client_data(self, buf):
+ buf = self.encryptor.encrypt(buf)
+ data = self.rnd_data(len(buf), buf, self.last_client_hash, self.random_client)
+ data_len = len(data) + 8
+ mac_key = self.user_key + struct.pack(' 0xFF000000:
+ self.server_info.data.local_client_id = b''
+ if not self.server_info.data.local_client_id:
+ self.server_info.data.local_client_id = os.urandom(4)
+ logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),))
+ self.server_info.data.connection_id = struct.unpack(' self.unit_len:
+ ret += self.pack_client_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_client_data(buf)
+ return ret
+
+ def client_post_decrypt(self, buf):
+ if self.raw_trans:
+ return buf
+ self.recv_buf += buf
+ out_buf = b''
+ while len(self.recv_buf) > 4:
+ mac_key = self.user_key + struct.pack('= 4096:
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data error')
+
+ if length + 4 > len(self.recv_buf):
+ break
+
+ server_hash = hmac.new(mac_key, self.recv_buf[:length + 2], self.hashfunc).digest()
+ if server_hash[:2] != self.recv_buf[length + 2: length + 4]:
+ logging.info('%s: checksum error, data %s'
+ % (self.no_compatible_method, binascii.hexlify(self.recv_buf[:length])))
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data uncorrect checksum')
+
+ pos = 2
+ if data_len > 0 and rand_len > 0:
+ pos = 2 + self.rnd_start_pos(rand_len, self.random_server)
+ out_buf += self.encryptor.decrypt(self.recv_buf[pos: data_len + pos])
+ self.last_server_hash = server_hash
+ if self.recv_id == 1:
+ self.server_info.tcp_mss = struct.unpack(' self.unit_len:
+ ret += self.pack_server_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_server_data(buf)
+ return ret
+
+ def server_post_decrypt(self, buf):
+ if self.raw_trans:
+ return (buf, False)
+ self.recv_buf += buf
+ out_buf = b''
+ sendback = False
+
+ if not self.has_recv_header:
+ if len(self.recv_buf) >= 12 or len(self.recv_buf) in [7, 8]:
+ recv_len = min(len(self.recv_buf), 12)
+ mac_key = self.server_info.recv_iv + self.server_info.key
+ md5data = hmac.new(mac_key, self.recv_buf[:4], self.hashfunc).digest()
+ if md5data[:recv_len - 4] != self.recv_buf[4:recv_len]:
+ return self.not_match_return(self.recv_buf)
+
+ if len(self.recv_buf) < 12 + 24:
+ return (b'', False)
+
+ self.last_client_hash = md5data
+ uid = struct.unpack(' self.max_time_dif:
+ logging.info('%s: wrong timestamp, time_dif %d, data %s' % (
+ self.no_compatible_method, time_dif, binascii.hexlify(head)
+ ))
+ return self.not_match_return(self.recv_buf)
+ elif self.server_info.data.insert(self.user_id, client_id, connection_id):
+ self.has_recv_header = True
+ self.client_id = client_id
+ self.connection_id = connection_id
+ else:
+ logging.info('%s: auth fail, data %s' % (self.no_compatible_method, binascii.hexlify(out_buf)))
+ return self.not_match_return(self.recv_buf)
+
+ self.encryptor = encrypt.Encryptor(
+ to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'rc4')
+ self.recv_buf = self.recv_buf[36:]
+ self.has_recv_header = True
+ sendback = True
+
+ while len(self.recv_buf) > 4:
+ mac_key = self.user_key + struct.pack('= 4096:
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.recv_id == 0:
+ logging.info(self.no_compatible_method + ': over size')
+ return (b'E' * 2048, False)
+ else:
+ raise Exception('server_post_decrype data error')
+
+ if length + 4 > len(self.recv_buf):
+ break
+
+ client_hash = hmac.new(mac_key, self.recv_buf[:length + 2], self.hashfunc).digest()
+ if client_hash[:2] != self.recv_buf[length + 2: length + 4]:
+ logging.info('%s: checksum error, data %s' % (
+ self.no_compatible_method, binascii.hexlify(self.recv_buf[:length])
+ ))
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.recv_id == 0:
+ return (b'E' * 2048, False)
+ else:
+ raise Exception('server_post_decrype data uncorrect checksum')
+
+ self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
+ pos = 2
+ if data_len > 0 and rand_len > 0:
+ pos = 2 + self.rnd_start_pos(rand_len, self.random_client)
+ out_buf += self.encryptor.decrypt(self.recv_buf[pos: data_len + pos])
+ self.last_client_hash = client_hash
+ self.recv_buf = self.recv_buf[length + 4:]
+ if data_len == 0:
+ sendback = True
+
+ if out_buf:
+ self.server_info.data.update(self.user_id, self.client_id, self.connection_id)
+ return (out_buf, sendback)
+
+ def client_udp_pre_encrypt(self, buf):
+ if self.user_key is None:
+ if b':' in to_bytes(self.server_info.protocol_param):
+ try:
+ items = to_bytes(self.server_info.protocol_param).split(':')
+ self.user_key = self.hashfunc(items[1]).digest()
+ self.user_id = struct.pack('= 1440:
+ return 0
+ random.init_from_bin_len(last_hash, buf_size)
+ pos = bisect.bisect_left(self.data_size_list, buf_size + self.server_info.overhead)
+ final_pos = pos + random.next() % (len(self.data_size_list))
+ # 假设random均匀分布,则越长的原始数据长度越容易if false
+ if final_pos < len(self.data_size_list):
+ return self.data_size_list[final_pos] - buf_size - self.server_info.overhead
+
+ # 上面if false后选择2号补全数组,此处有更精细的长度分段
+ pos = bisect.bisect_left(self.data_size_list2, buf_size + self.server_info.overhead)
+ final_pos = pos + random.next() % (len(self.data_size_list2))
+ if final_pos < len(self.data_size_list2):
+ return self.data_size_list2[final_pos] - buf_size - self.server_info.overhead
+ # final_pos 总是分布在pos~(data_size_list2.len-1)之间
+ if final_pos < pos + len(self.data_size_list2) - 1:
+ return 0
+ # 有1/len(self.data_size_list2)的概率不满足上一个if ?
+ # 理论上不会运行到此处,因此可以插入运行断言 ?
+ # assert False
+
+ if buf_size > 1300:
+ return random.next() % 31
+ if buf_size > 900:
+ return random.next() % 127
+ if buf_size > 400:
+ return random.next() % 521
+ return random.next() % 1021
+
+
+class auth_chain_c(auth_chain_b):
+ def __init__(self, method):
+ super(auth_chain_c, self).__init__(method)
+ self.salt = b"auth_chain_c"
+ self.no_compatible_method = 'auth_chain_c'
+ self.data_size_list0 = []
+
+ def init_data_size(self, key):
+ if self.data_size_list0:
+ self.data_size_list0 = []
+ random = xorshift128plus()
+ random.init_from_bin(key)
+ # 补全数组长为12~24-1
+ list_len = random.next() % (8 + 16) + (4 + 8)
+ for i in range(0, list_len):
+ self.data_size_list0.append((int)(random.next() % 2340 % 2040 % 1440))
+ self.data_size_list0.sort()
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+ try:
+ max_client = int(server_info.protocol_param.split('#')[0])
+ except:
+ max_client = 64
+ self.server_info.data.set_max_client(max_client)
+ self.init_data_size(self.server_info.key)
+
+ def rnd_data_len(self, buf_size, last_hash, random):
+ other_data_size = buf_size + self.server_info.overhead
+ # 一定要在random使用前初始化,以保证服务器与客户端同步,保证包大小验证结果正确
+ random.init_from_bin_len(last_hash, buf_size)
+ # final_pos 总是分布在pos~(data_size_list0.len-1)之间
+ # 除非data_size_list0中的任何值均过小使其全部都无法容纳buf
+ if other_data_size >= self.data_size_list0[-1]:
+ if other_data_size >= 1440:
+ return 0
+ if other_data_size > 1300:
+ return random.next() % 31
+ if other_data_size > 900:
+ return random.next() % 127
+ if other_data_size > 400:
+ return random.next() % 521
+ return random.next() % 1021
+
+ pos = bisect.bisect_left(self.data_size_list0, other_data_size)
+ # random select a size in the leftover data_size_list0
+ final_pos = pos + random.next() % (len(self.data_size_list0) - pos)
+ return self.data_size_list0[final_pos] - other_data_size
+
+
+class auth_chain_d(auth_chain_b):
+ def __init__(self, method):
+ super(auth_chain_d, self).__init__(method)
+ self.salt = b"auth_chain_d"
+ self.no_compatible_method = 'auth_chain_d'
+ self.data_size_list0 = []
+
+ def check_and_patch_data_size(self, random):
+ # append new item
+ # when the biggest item(first time) or the last append item(other time) are not big enough.
+ # but set a limit size (64) to avoid stack overflow.
+ if self.data_size_list0[-1] < 1300 and len(self.data_size_list0) < 64:
+ self.data_size_list0.append((int)(random.next() % 2340 % 2040 % 1440))
+ self.check_and_patch_data_size(random)
+
+ def init_data_size(self, key):
+ if self.data_size_list0:
+ self.data_size_list0 = []
+ random = xorshift128plus()
+ random.init_from_bin(key)
+ # 补全数组长为12~24-1
+ list_len = random.next() % (8 + 16) + (4 + 8)
+ for i in range(0, list_len):
+ self.data_size_list0.append((int)(random.next() % 2340 % 2040 % 1440))
+ self.data_size_list0.sort()
+ old_len = len(self.data_size_list0)
+ self.check_and_patch_data_size(random)
+ # if check_and_patch_data_size are work, re-sort again.
+ if old_len != len(self.data_size_list0):
+ self.data_size_list0.sort()
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+ try:
+ max_client = int(server_info.protocol_param.split('#')[0])
+ except:
+ max_client = 64
+ self.server_info.data.set_max_client(max_client)
+ self.init_data_size(self.server_info.key)
+
+ def rnd_data_len(self, buf_size, last_hash, random):
+ other_data_size = buf_size + self.server_info.overhead
+ # if other_data_size > the bigest item in data_size_list0, not padding any data
+ if other_data_size >= self.data_size_list0[-1]:
+ return 0
+
+ random.init_from_bin_len(last_hash, buf_size)
+ pos = bisect.bisect_left(self.data_size_list0, other_data_size)
+ # random select a size in the leftover data_size_list0
+ final_pos = pos + random.next() % (len(self.data_size_list0) - pos)
+ return self.data_size_list0[final_pos] - other_data_size
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py
new file mode 100644
index 0000000000..ff3c5fdfbc
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2015 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+import binascii
+import struct
+import base64
+import datetime
+import random
+
+from shadowsocks import common
+from shadowsocks.obfsplugin import plain
+from shadowsocks.common import to_bytes, to_str, ord, chr
+
+def create_http_simple_obfs(method):
+ return http_simple(method)
+
+def create_http_post_obfs(method):
+ return http_post(method)
+
+def create_random_head_obfs(method):
+ return random_head(method)
+
+obfs_map = {
+ 'http_simple': (create_http_simple_obfs,),
+ 'http_simple_compatible': (create_http_simple_obfs,),
+ 'http_post': (create_http_post_obfs,),
+ 'http_post_compatible': (create_http_post_obfs,),
+ 'random_head': (create_random_head_obfs,),
+ 'random_head_compatible': (create_random_head_obfs,),
+}
+
+def match_begin(str1, str2):
+ if len(str1) >= len(str2):
+ if str1[:len(str2)] == str2:
+ return True
+ return False
+
+class http_simple(plain.plain):
+ def __init__(self, method):
+ self.method = method
+ self.has_sent_header = False
+ self.has_recv_header = False
+ self.host = None
+ self.port = 0
+ self.recv_buffer = b''
+ # TODO user config user_agent
+ self.user_agent = [b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
+ b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0",
+ b"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
+ b"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36",
+ b"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
+ b"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
+ b"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
+ b"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
+ b"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
+ b"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
+ b"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
+ b"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"]
+
+ def encode_head(self, buf):
+ hexstr = binascii.hexlify(buf)
+ chs = []
+ for i in range(0, len(hexstr), 2):
+ chs.append(b"%" + hexstr[i:i+2])
+ return b''.join(chs)
+
+ def client_encode(self, buf):
+ if self.has_sent_header:
+ return buf
+ head_size = len(self.server_info.iv) + self.server_info.head_len
+ if len(buf) - head_size > 64:
+ headlen = head_size + random.randint(0, 64)
+ else:
+ headlen = len(buf)
+ headdata = buf[:headlen]
+ buf = buf[headlen:]
+ port = b''
+ if self.server_info.port != 80:
+ port = b':' + to_bytes(str(self.server_info.port))
+ body = None
+ hosts = (self.server_info.obfs_param or self.server_info.host)
+ pos = hosts.find("#")
+ if pos >= 0:
+ body = hosts[pos + 1:].replace("\n", "\r\n")
+ body = body.replace("\\n", "\r\n")
+ hosts = hosts[:pos]
+ hosts = hosts.split(',')
+ host = random.choice(hosts)
+ http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n"
+ http_head += b"Host: " + to_bytes(host) + port + b"\r\n"
+ if body:
+ http_head += body + "\r\n\r\n"
+ else:
+ http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n"
+ http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n"
+ self.has_sent_header = True
+ return http_head + buf
+
+ def client_decode(self, buf):
+ if self.has_recv_header:
+ return (buf, False)
+ pos = buf.find(b'\r\n\r\n')
+ if pos >= 0:
+ self.has_recv_header = True
+ return (buf[pos + 4:], False)
+ else:
+ return (b'', False)
+
+ def server_encode(self, buf):
+ if self.has_sent_header:
+ return buf
+
+ header = b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: '
+ header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT'))
+ header += b'\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n'
+ self.has_sent_header = True
+ return header + buf
+
+ def get_data_from_http_header(self, buf):
+ ret_buf = b''
+ lines = buf.split(b'\r\n')
+ if lines and len(lines) > 1:
+ hex_items = lines[0].split(b'%')
+ if hex_items and len(hex_items) > 1:
+ for index in range(1, len(hex_items)):
+ if len(hex_items[index]) < 2:
+ ret_buf += binascii.unhexlify('0' + hex_items[index])
+ break
+ elif len(hex_items[index]) > 2:
+ ret_buf += binascii.unhexlify(hex_items[index][:2])
+ break
+ else:
+ ret_buf += binascii.unhexlify(hex_items[index])
+ return ret_buf
+ return b''
+
+ def get_host_from_http_header(self, buf):
+ ret_buf = b''
+ lines = buf.split(b'\r\n')
+ if lines and len(lines) > 1:
+ for line in lines:
+ if match_begin(line, b"Host: "):
+ return common.to_str(line[6:])
+
+ def not_match_return(self, buf):
+ self.has_sent_header = True
+ self.has_recv_header = True
+ if self.method == 'http_simple':
+ return (b'E'*2048, False, False)
+ return (buf, True, False)
+
+ def error_return(self, buf):
+ self.has_sent_header = True
+ self.has_recv_header = True
+ return (b'E'*2048, False, False)
+
+ def server_decode(self, buf):
+ if self.has_recv_header:
+ return (buf, True, False)
+
+ self.recv_buffer += buf
+ buf = self.recv_buffer
+ if len(buf) > 10:
+ if match_begin(buf, b'GET ') or match_begin(buf, b'POST '):
+ if len(buf) > 65536:
+ self.recv_buffer = None
+ logging.warn('http_simple: over size')
+ return self.not_match_return(buf)
+ else: #not http header, run on original protocol
+ self.recv_buffer = None
+ logging.debug('http_simple: not match begin')
+ return self.not_match_return(buf)
+ else:
+ return (b'', True, False)
+
+ if b'\r\n\r\n' in buf:
+ datas = buf.split(b'\r\n\r\n', 1)
+ ret_buf = self.get_data_from_http_header(buf)
+ host = self.get_host_from_http_header(buf)
+ if host and self.server_info.obfs_param:
+ pos = host.find(":")
+ if pos >= 0:
+ host = host[:pos]
+ hosts = self.server_info.obfs_param.split(',')
+ if host not in hosts:
+ return self.not_match_return(buf)
+ if len(ret_buf) < 4:
+ return self.error_return(buf)
+ if len(datas) > 1:
+ ret_buf += datas[1]
+ if len(ret_buf) >= 13:
+ self.has_recv_header = True
+ return (ret_buf, True, False)
+ return self.not_match_return(buf)
+ else:
+ return (b'', True, False)
+
+class http_post(http_simple):
+ def __init__(self, method):
+ super(http_post, self).__init__(method)
+
+ def boundary(self):
+ return to_bytes(''.join([random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)]))
+
+ def client_encode(self, buf):
+ if self.has_sent_header:
+ return buf
+ head_size = len(self.server_info.iv) + self.server_info.head_len
+ if len(buf) - head_size > 64:
+ headlen = head_size + random.randint(0, 64)
+ else:
+ headlen = len(buf)
+ headdata = buf[:headlen]
+ buf = buf[headlen:]
+ port = b''
+ if self.server_info.port != 80:
+ port = b':' + to_bytes(str(self.server_info.port))
+ body = None
+ hosts = (self.server_info.obfs_param or self.server_info.host)
+ pos = hosts.find("#")
+ if pos >= 0:
+ body = hosts[pos + 1:].replace("\\n", "\r\n")
+ hosts = hosts[:pos]
+ hosts = hosts.split(',')
+ host = random.choice(hosts)
+ http_head = b"POST /" + self.encode_head(headdata) + b" HTTP/1.1\r\n"
+ http_head += b"Host: " + to_bytes(host) + port + b"\r\n"
+ if body:
+ http_head += body + "\r\n\r\n"
+ else:
+ http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n"
+ http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n"
+ http_head += b"Content-Type: multipart/form-data; boundary=" + self.boundary() + b"\r\nDNT: 1\r\n"
+ http_head += b"Connection: keep-alive\r\n\r\n"
+ self.has_sent_header = True
+ return http_head + buf
+
+ def not_match_return(self, buf):
+ self.has_sent_header = True
+ self.has_recv_header = True
+ if self.method == 'http_post':
+ return (b'E'*2048, False, False)
+ return (buf, True, False)
+
+class random_head(plain.plain):
+ def __init__(self, method):
+ self.method = method
+ self.has_sent_header = False
+ self.has_recv_header = False
+ self.raw_trans_sent = False
+ self.raw_trans_recv = False
+ self.send_buffer = b''
+
+ def client_encode(self, buf):
+ if self.raw_trans_sent:
+ return buf
+ self.send_buffer += buf
+ if not self.has_sent_header:
+ self.has_sent_header = True
+ data = os.urandom(common.ord(os.urandom(1)[0]) % 96 + 4)
+ crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff
+ return data + struct.pack('= len(str2):
+ if str1[:len(str2)] == str2:
+ return True
+ return False
+
+class obfs_auth_data(object):
+ def __init__(self):
+ self.client_data = lru_cache.LRUCache(60 * 5)
+ self.client_id = os.urandom(32)
+ self.startup_time = int(time.time() - 60 * 30) & 0xFFFFFFFF
+ self.ticket_buf = {}
+
+class tls_ticket_auth(plain.plain):
+ def __init__(self, method):
+ self.method = method
+ self.handshake_status = 0
+ self.send_buffer = b''
+ self.recv_buffer = b''
+ self.client_id = b''
+ self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
+ self.tls_version = b'\x03\x03'
+ self.overhead = 5
+
+ def init_data(self):
+ return obfs_auth_data()
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return self.overhead
+
+ def sni(self, url):
+ url = common.to_bytes(url)
+ data = b"\x00" + struct.pack('>H', len(url)) + url
+ data = b"\x00\x00" + struct.pack('>H', len(data) + 2) + struct.pack('>H', len(data)) + data
+ return data
+
+ def pack_auth_data(self, client_id):
+ utc_time = int(time.time()) & 0xFFFFFFFF
+ data = struct.pack('>I', utc_time) + os.urandom(18)
+ data += hmac.new(self.server_info.key + client_id, data, hashlib.sha1).digest()[:10]
+ return data
+
+ def client_encode(self, buf):
+ if self.handshake_status == -1:
+ return buf
+ if self.handshake_status == 8:
+ ret = b''
+ while len(buf) > 2048:
+ size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf))
+ ret += b"\x17" + self.tls_version + struct.pack('>H', size) + buf[:size]
+ buf = buf[size:]
+ if len(buf) > 0:
+ ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
+ return ret
+ if len(buf) > 0:
+ self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
+ if self.handshake_status == 0:
+ self.handshake_status = 1
+ data = self.tls_version + self.pack_auth_data(self.server_info.data.client_id) + b"\x20" + self.server_info.data.client_id + binascii.unhexlify(b"001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a" + b"0100")
+ ext = binascii.unhexlify(b"ff01000100")
+ host = self.server_info.obfs_param or self.server_info.host
+ if host and host[-1] in string.digits:
+ host = ''
+ hosts = host.split(',')
+ host = random.choice(hosts)
+ ext += self.sni(host)
+ ext += b"\x00\x17\x00\x00"
+ if host not in self.server_info.data.ticket_buf:
+ self.server_info.data.ticket_buf[host] = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 17 + 8) * 16)
+ ext += b"\x00\x23" + struct.pack('>H', len(self.server_info.data.ticket_buf[host])) + self.server_info.data.ticket_buf[host]
+ ext += binascii.unhexlify(b"000d001600140601060305010503040104030301030302010203")
+ ext += binascii.unhexlify(b"000500050100000000")
+ ext += binascii.unhexlify(b"00120000")
+ ext += binascii.unhexlify(b"75500000")
+ ext += binascii.unhexlify(b"000b00020100")
+ ext += binascii.unhexlify(b"000a0006000400170018")
+ data += struct.pack('>H', len(ext)) + ext
+ data = b"\x01\x00" + struct.pack('>H', len(data)) + data
+ data = b"\x16\x03\x01" + struct.pack('>H', len(data)) + data
+ return data
+ elif self.handshake_status == 1 and len(buf) == 0:
+ data = b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec
+ data += b"\x16" + self.tls_version + b"\x00\x20" + os.urandom(22) #Finished
+ data += hmac.new(self.server_info.key + self.server_info.data.client_id, data, hashlib.sha1).digest()[:10]
+ ret = data + self.send_buffer
+ self.send_buffer = b''
+ self.handshake_status = 8
+ return ret
+ return b''
+
+ def client_decode(self, buf):
+ if self.handshake_status == -1:
+ return (buf, False)
+
+ if self.handshake_status == 8:
+ ret = b''
+ self.recv_buffer += buf
+ while len(self.recv_buffer) > 5:
+ if ord(self.recv_buffer[0]) != 0x17:
+ logging.info("data = %s" % (binascii.hexlify(self.recv_buffer)))
+ raise Exception('server_decode appdata error')
+ size = struct.unpack('>H', self.recv_buffer[3:5])[0]
+ if len(self.recv_buffer) < size + 5:
+ break
+ buf = self.recv_buffer[5:size+5]
+ ret += buf
+ self.recv_buffer = self.recv_buffer[size+5:]
+ return (ret, False)
+
+ if len(buf) < 11 + 32 + 1 + 32:
+ raise Exception('client_decode data error')
+ verify = buf[11:33]
+ if hmac.new(self.server_info.key + self.server_info.data.client_id, verify, hashlib.sha1).digest()[:10] != buf[33:43]:
+ raise Exception('client_decode data error')
+ if hmac.new(self.server_info.key + self.server_info.data.client_id, buf[:-10], hashlib.sha1).digest()[:10] != buf[-10:]:
+ raise Exception('client_decode data error')
+ return (b'', True)
+
+ def server_encode(self, buf):
+ if self.handshake_status == -1:
+ return buf
+ if (self.handshake_status & 8) == 8:
+ ret = b''
+ while len(buf) > 2048:
+ size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf))
+ ret += b"\x17" + self.tls_version + struct.pack('>H', size) + buf[:size]
+ buf = buf[size:]
+ if len(buf) > 0:
+ ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
+ return ret
+ self.handshake_status |= 8
+ data = self.tls_version + self.pack_auth_data(self.client_id) + b"\x20" + self.client_id + binascii.unhexlify(b"c02f000005ff01000100")
+ data = b"\x02\x00" + struct.pack('>H', len(data)) + data #server hello
+ data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data
+ if random.randint(0, 8) < 1:
+ ticket = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 164) * 2 + 64)
+ ticket = struct.pack('>H', len(ticket) + 4) + b"\x04\x00" + struct.pack('>H', len(ticket)) + ticket
+ data += b"\x16" + self.tls_version + ticket #New session ticket
+ data += b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec
+ finish_len = random.choice([32, 40])
+ data += b"\x16" + self.tls_version + struct.pack('>H', finish_len) + os.urandom(finish_len - 10) #Finished
+ data += hmac.new(self.server_info.key + self.client_id, data, hashlib.sha1).digest()[:10]
+ if buf:
+ data += self.server_encode(buf)
+ return data
+
+ def decode_error_return(self, buf):
+ self.handshake_status = -1
+ if self.overhead > 0:
+ self.server_info.overhead -= self.overhead
+ self.overhead = 0
+ if self.method in ['tls1.2_ticket_auth', 'tls1.2_ticket_fastauth']:
+ return (b'E'*2048, False, False)
+ return (buf, True, False)
+
+ def server_decode(self, buf):
+ if self.handshake_status == -1:
+ return (buf, True, False)
+
+ if (self.handshake_status & 4) == 4:
+ ret = b''
+ self.recv_buffer += buf
+ while len(self.recv_buffer) > 5:
+ if ord(self.recv_buffer[0]) != 0x17 or ord(self.recv_buffer[1]) != 0x3 or ord(self.recv_buffer[2]) != 0x3:
+ logging.info("data = %s" % (binascii.hexlify(self.recv_buffer)))
+ raise Exception('server_decode appdata error')
+ size = struct.unpack('>H', self.recv_buffer[3:5])[0]
+ if len(self.recv_buffer) < size + 5:
+ break
+ ret += self.recv_buffer[5:size+5]
+ self.recv_buffer = self.recv_buffer[size+5:]
+ return (ret, True, False)
+
+ if (self.handshake_status & 1) == 1:
+ self.recv_buffer += buf
+ buf = self.recv_buffer
+ verify = buf
+ if len(buf) < 11:
+ raise Exception('server_decode data error')
+ if not match_begin(buf, b"\x14" + self.tls_version + b"\x00\x01\x01"): #ChangeCipherSpec
+ raise Exception('server_decode data error')
+ buf = buf[6:]
+ if not match_begin(buf, b"\x16" + self.tls_version + b"\x00"): #Finished
+ raise Exception('server_decode data error')
+ verify_len = struct.unpack('>H', buf[3:5])[0] + 1 # 11 - 10
+ if len(verify) < verify_len + 10:
+ return (b'', False, False)
+ if hmac.new(self.server_info.key + self.client_id, verify[:verify_len], hashlib.sha1).digest()[:10] != verify[verify_len:verify_len+10]:
+ raise Exception('server_decode data error')
+ self.recv_buffer = verify[verify_len + 10:]
+ status = self.handshake_status
+ self.handshake_status |= 4
+ ret = self.server_decode(b'')
+ return ret;
+
+ #raise Exception("handshake data = %s" % (binascii.hexlify(buf)))
+ self.recv_buffer += buf
+ buf = self.recv_buffer
+ ogn_buf = buf
+ if len(buf) < 3:
+ return (b'', False, False)
+ if not match_begin(buf, b'\x16\x03\x01'):
+ return self.decode_error_return(ogn_buf)
+ buf = buf[3:]
+ header_len = struct.unpack('>H', buf[:2])[0]
+ if header_len > len(buf) - 2:
+ return (b'', False, False)
+
+ self.recv_buffer = self.recv_buffer[header_len + 5:]
+ self.handshake_status = 1
+ buf = buf[2:header_len + 2]
+ if not match_begin(buf, b'\x01\x00'): #client hello
+ logging.info("tls_auth not client hello message")
+ return self.decode_error_return(ogn_buf)
+ buf = buf[2:]
+ if struct.unpack('>H', buf[:2])[0] != len(buf) - 2:
+ logging.info("tls_auth wrong message size")
+ return self.decode_error_return(ogn_buf)
+ buf = buf[2:]
+ if not match_begin(buf, self.tls_version):
+ logging.info("tls_auth wrong tls version")
+ return self.decode_error_return(ogn_buf)
+ buf = buf[2:]
+ verifyid = buf[:32]
+ buf = buf[32:]
+ sessionid_len = ord(buf[0])
+ if sessionid_len < 32:
+ logging.info("tls_auth wrong sessionid_len")
+ return self.decode_error_return(ogn_buf)
+ sessionid = buf[1:sessionid_len + 1]
+ buf = buf[sessionid_len+1:]
+ self.client_id = sessionid
+ sha1 = hmac.new(self.server_info.key + sessionid, verifyid[:22], hashlib.sha1).digest()[:10]
+ utc_time = struct.unpack('>I', verifyid[:4])[0]
+ time_dif = common.int32((int(time.time()) & 0xffffffff) - utc_time)
+ if self.server_info.obfs_param:
+ try:
+ self.max_time_dif = int(self.server_info.obfs_param)
+ except:
+ pass
+ if self.max_time_dif > 0 and (time_dif < -self.max_time_dif or time_dif > self.max_time_dif \
+ or common.int32(utc_time - self.server_info.data.startup_time) < -self.max_time_dif / 2):
+ logging.info("tls_auth wrong time")
+ return self.decode_error_return(ogn_buf)
+ if sha1 != verifyid[22:]:
+ logging.info("tls_auth wrong sha1")
+ return self.decode_error_return(ogn_buf)
+ if self.server_info.data.client_data.get(verifyid[:22]):
+ logging.info("replay attack detect, id = %s" % (binascii.hexlify(verifyid)))
+ return self.decode_error_return(ogn_buf)
+ self.server_info.data.client_data.sweep()
+ self.server_info.data.client_data[verifyid[:22]] = sessionid
+ if len(self.recv_buffer) >= 11:
+ ret = self.server_decode(b'')
+ return (ret[0], True, True)
+ # (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
+ return (b'', False, True)
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py
new file mode 100644
index 0000000000..8c6355c38f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2015 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+
+from shadowsocks.common import ord
+
+def create_obfs(method):
+ return plain(method)
+
+obfs_map = {
+ 'plain': (create_obfs,),
+ 'origin': (create_obfs,),
+}
+
+class plain(object):
+ def __init__(self, method):
+ self.method = method
+ self.server_info = None
+
+ def init_data(self):
+ return b''
+
+ def get_overhead(self, direction): # direction: true for c->s false for s->c
+ return 0
+
+ def get_server_info(self):
+ return self.server_info
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+
+ def client_pre_encrypt(self, buf):
+ return buf
+
+ def client_encode(self, buf):
+ return buf
+
+ def client_decode(self, buf):
+ # (buffer_to_recv, is_need_to_encode_and_send_back)
+ return (buf, False)
+
+ def client_post_decrypt(self, buf):
+ return buf
+
+ def server_pre_encrypt(self, buf):
+ return buf
+
+ def server_encode(self, buf):
+ return buf
+
+ def server_decode(self, buf):
+ # (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
+ return (buf, True, False)
+
+ def server_post_decrypt(self, buf):
+ return (buf, False)
+
+ def client_udp_pre_encrypt(self, buf):
+ return buf
+
+ def client_udp_post_decrypt(self, buf):
+ return buf
+
+ def server_udp_pre_encrypt(self, buf, uid):
+ return buf
+
+ def server_udp_post_decrypt(self, buf):
+ return (buf, None)
+
+ def dispose(self):
+ pass
+
+ def get_head_size(self, buf, def_value):
+ if len(buf) < 2:
+ return def_value
+ head_type = ord(buf[0]) & 0x7
+ if head_type == 1:
+ return 7
+ if head_type == 4:
+ return 19
+ if head_type == 3:
+ return 4 + ord(buf[1])
+ return def_value
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py
new file mode 100644
index 0000000000..0dc0ca6d10
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2015 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import hashlib
+import logging
+import binascii
+import base64
+import time
+import datetime
+import random
+import struct
+import zlib
+import hmac
+import hashlib
+
+import shadowsocks
+from shadowsocks import common
+from shadowsocks.obfsplugin import plain
+from shadowsocks.common import to_bytes, to_str, ord, chr
+
+def create_verify_deflate(method):
+ return verify_deflate(method)
+
+obfs_map = {
+ 'verify_deflate': (create_verify_deflate,),
+}
+
+def match_begin(str1, str2):
+ if len(str1) >= len(str2):
+ if str1[:len(str2)] == str2:
+ return True
+ return False
+
+class obfs_verify_data(object):
+ def __init__(self):
+ pass
+
+class verify_base(plain.plain):
+ def __init__(self, method):
+ super(verify_base, self).__init__(method)
+ self.method = method
+
+ def init_data(self):
+ return obfs_verify_data()
+
+ def set_server_info(self, server_info):
+ self.server_info = server_info
+
+ def client_encode(self, buf):
+ return buf
+
+ def client_decode(self, buf):
+ return (buf, False)
+
+ def server_encode(self, buf):
+ return buf
+
+ def server_decode(self, buf):
+ return (buf, True, False)
+
+class verify_deflate(verify_base):
+ def __init__(self, method):
+ super(verify_deflate, self).__init__(method)
+ self.recv_buf = b''
+ self.unit_len = 32700
+ self.decrypt_packet_num = 0
+ self.raw_trans = False
+
+ def pack_data(self, buf):
+ if len(buf) == 0:
+ return b''
+ data = zlib.compress(buf)
+ data = struct.pack('>H', len(data)) + data[2:]
+ return data
+
+ def client_pre_encrypt(self, buf):
+ ret = b''
+ while len(buf) > self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf)
+ return ret
+
+ def client_post_decrypt(self, buf):
+ if self.raw_trans:
+ return buf
+ self.recv_buf += buf
+ out_buf = b''
+ while len(self.recv_buf) > 2:
+ length = struct.unpack('>H', self.recv_buf[:2])[0]
+ if length >= 32768 or length < 6:
+ self.raw_trans = True
+ self.recv_buf = b''
+ raise Exception('client_post_decrypt data error')
+ if length > len(self.recv_buf):
+ break
+
+ out_buf += zlib.decompress(b'x\x9c' + self.recv_buf[2:length])
+ self.recv_buf = self.recv_buf[length:]
+
+ if out_buf:
+ self.decrypt_packet_num += 1
+ return out_buf
+
+ def server_pre_encrypt(self, buf):
+ ret = b''
+ while len(buf) > self.unit_len:
+ ret += self.pack_data(buf[:self.unit_len])
+ buf = buf[self.unit_len:]
+ ret += self.pack_data(buf)
+ return ret
+
+ def server_post_decrypt(self, buf):
+ if self.raw_trans:
+ return (buf, False)
+ self.recv_buf += buf
+ out_buf = b''
+ while len(self.recv_buf) > 2:
+ length = struct.unpack('>H', self.recv_buf[:2])[0]
+ if length >= 32768 or length < 6:
+ self.raw_trans = True
+ self.recv_buf = b''
+ if self.decrypt_packet_num == 0:
+ return (b'E'*2048, False)
+ else:
+ raise Exception('server_post_decrype data error')
+ if length > len(self.recv_buf):
+ break
+
+ out_buf += zlib.decompress(b'\x78\x9c' + self.recv_buf[2:length])
+ self.recv_buf = self.recv_buf[length:]
+
+ if out_buf:
+ self.decrypt_packet_num += 1
+ return (out_buf, False)
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py
new file mode 100644
index 0000000000..e1918f5e02
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py
@@ -0,0 +1,214 @@
+import collections
+
+################################################################################
+### OrderedDict
+################################################################################
+
+class OrderedDict(dict):
+ 'Dictionary that remembers insertion order'
+ # An inherited dict maps keys to values.
+ # The inherited dict provides __getitem__, __len__, __contains__, and get.
+ # The remaining methods are order-aware.
+ # Big-O running times for all methods are the same as regular dictionaries.
+
+ # The internal self.__map dict maps keys to links in a doubly linked list.
+ # The circular doubly linked list starts and ends with a sentinel element.
+ # The sentinel element never gets deleted (this simplifies the algorithm).
+ # Each link is stored as a list of length three: [PREV, NEXT, KEY].
+
+ def __init__(*args, **kwds):
+ '''Initialize an ordered dictionary. The signature is the same as
+ regular dictionaries, but keyword arguments are not recommended because
+ their insertion order is arbitrary.
+
+ '''
+ if not args:
+ raise TypeError("descriptor '__init__' of 'OrderedDict' object "
+ "needs an argument")
+ self = args[0]
+ args = args[1:]
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__root
+ except AttributeError:
+ self.__root = root = [] # sentinel node
+ root[:] = [root, root, None]
+ self.__map = {}
+ self.__update(*args, **kwds)
+
+ def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
+ 'od.__setitem__(i, y) <==> od[i]=y'
+ # Setting a new item creates a new link at the end of the linked list,
+ # and the inherited dictionary is updated with the new key/value pair.
+ if key not in self:
+ root = self.__root
+ last = root[0]
+ last[1] = root[0] = self.__map[key] = [last, root, key]
+ return dict_setitem(self, key, value)
+
+ def __delitem__(self, key, dict_delitem=dict.__delitem__):
+ 'od.__delitem__(y) <==> del od[y]'
+ # Deleting an existing item uses self.__map to find the link which gets
+ # removed by updating the links in the predecessor and successor nodes.
+ dict_delitem(self, key)
+ link_prev, link_next, _ = self.__map.pop(key)
+ link_prev[1] = link_next # update link_prev[NEXT]
+ link_next[0] = link_prev # update link_next[PREV]
+
+ def __iter__(self):
+ 'od.__iter__() <==> iter(od)'
+ # Traverse the linked list in order.
+ root = self.__root
+ curr = root[1] # start at the first node
+ while curr is not root:
+ yield curr[2] # yield the curr[KEY]
+ curr = curr[1] # move to next node
+
+ def __reversed__(self):
+ 'od.__reversed__() <==> reversed(od)'
+ # Traverse the linked list in reverse order.
+ root = self.__root
+ curr = root[0] # start at the last node
+ while curr is not root:
+ yield curr[2] # yield the curr[KEY]
+ curr = curr[0] # move to previous node
+
+ def clear(self):
+ 'od.clear() -> None. Remove all items from od.'
+ root = self.__root
+ root[:] = [root, root, None]
+ self.__map.clear()
+ dict.clear(self)
+
+ # -- the following methods do not depend on the internal structure --
+
+ def keys(self):
+ 'od.keys() -> list of keys in od'
+ return list(self)
+
+ def values(self):
+ 'od.values() -> list of values in od'
+ return [self[key] for key in self]
+
+ def items(self):
+ 'od.items() -> list of (key, value) pairs in od'
+ return [(key, self[key]) for key in self]
+
+ def iterkeys(self):
+ 'od.iterkeys() -> an iterator over the keys in od'
+ return iter(self)
+
+ def itervalues(self):
+ 'od.itervalues -> an iterator over the values in od'
+ for k in self:
+ yield self[k]
+
+ def iteritems(self):
+ 'od.iteritems -> an iterator over the (key, value) pairs in od'
+ for k in self:
+ yield (k, self[k])
+
+ update = collections.MutableMapping.update
+
+ __update = update # let subclasses override update without breaking __init__
+
+ __marker = object()
+
+ def pop(self, key, default=__marker):
+ '''od.pop(k[,d]) -> v, remove specified key and return the corresponding
+ value. If key is not found, d is returned if given, otherwise KeyError
+ is raised.
+
+ '''
+ if key in self:
+ result = self[key]
+ del self[key]
+ return result
+ if default is self.__marker:
+ raise KeyError(key)
+ return default
+
+ def setdefault(self, key, default=None):
+ 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
+ if key in self:
+ return self[key]
+ self[key] = default
+ return default
+
+ def popitem(self, last=True):
+ '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+ Pairs are returned in LIFO order if last is true or FIFO order if false.
+
+ '''
+ if not self:
+ raise KeyError('dictionary is empty')
+ key = next(reversed(self) if last else iter(self))
+ value = self.pop(key)
+ return key, value
+
+ def __repr__(self, _repr_running={}):
+ 'od.__repr__() <==> repr(od)'
+ call_key = id(self), _get_ident()
+ if call_key in _repr_running:
+ return '...'
+ _repr_running[call_key] = 1
+ try:
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+ finally:
+ del _repr_running[call_key]
+
+ def __reduce__(self):
+ 'Return state information for pickling'
+ items = [[k, self[k]] for k in self]
+ inst_dict = vars(self).copy()
+ for k in vars(OrderedDict()):
+ inst_dict.pop(k, None)
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def copy(self):
+ 'od.copy() -> a shallow copy of od'
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
+ If not specified, the value defaults to None.
+
+ '''
+ self = cls()
+ for key in iterable:
+ self[key] = value
+ return self
+
+ def __eq__(self, other):
+ '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
+ while comparison to a regular mapping is order-insensitive.
+
+ '''
+ if isinstance(other, OrderedDict):
+ return dict.__eq__(self, other) and all(_imap(_eq, self, other))
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ 'od.__ne__(y) <==> od!=y'
+ return not self == other
+
+ # -- the following methods support python 3.x style dictionary views --
+
+ def viewkeys(self):
+ "od.viewkeys() -> a set-like object providing a view on od's keys"
+ return KeysView(self)
+
+ def viewvalues(self):
+ "od.viewvalues() -> an object providing a view on od's values"
+ return ValuesView(self)
+
+ def viewitems(self):
+ "od.viewitems() -> a set-like object providing a view on od's items"
+ return ItemsView(self)
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh
new file mode 100755
index 0000000000..43720308dc
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+cd `dirname $0`
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
+ulimit -n 512000
+nohup ${python_ver} server.py a>> /dev/null 2>&1 &
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py
new file mode 100644
index 0000000000..081538933b
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import sys
+import os
+import logging
+import signal
+
+if __name__ == '__main__':
+ import inspect
+
+ file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))
+ sys.path.insert(0, os.path.join(file_path, '../'))
+
+from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, \
+ asyncdns, manager, common
+
+
+def main():
+ shell.check_python()
+
+ config = shell.get_config(False)
+
+ shell.log_shadowsocks_version()
+
+ daemon.daemon_exec(config)
+
+ try:
+ import resource
+ logging.info(
+ 'current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
+ except ImportError:
+ pass
+
+ if config['port_password']:
+ pass
+ else:
+ config['port_password'] = {}
+ server_port = config['server_port']
+ if type(server_port) == list:
+ for a_server_port in server_port:
+ config['port_password'][a_server_port] = config['password']
+ else:
+ config['port_password'][str(server_port)] = config['password']
+
+ if not config.get('dns_ipv6', False):
+ asyncdns.IPV6_CONNECTION_SUPPORT = False
+
+ if config.get('manager_address', 0):
+ logging.info('entering manager mode')
+ manager.run(config)
+ return
+
+ tcp_servers = []
+ udp_servers = []
+ dns_resolver = asyncdns.DNSResolver(config['black_hostname_list'])
+ if int(config['workers']) > 1:
+ stat_counter_dict = None
+ else:
+ stat_counter_dict = {}
+ port_password = config['port_password']
+ config_password = config.get('password', 'm')
+ del config['port_password']
+ for port, password_obfs in port_password.items():
+ method = config["method"]
+ protocol = config.get("protocol", 'origin')
+ protocol_param = config.get("protocol_param", '')
+ obfs = config.get("obfs", 'plain')
+ obfs_param = config.get("obfs_param", '')
+ bind = config.get("out_bind", '')
+ bindv6 = config.get("out_bindv6", '')
+ if type(password_obfs) == list:
+ password = password_obfs[0]
+ obfs = common.to_str(password_obfs[1])
+ if len(password_obfs) > 2:
+ protocol = common.to_str(password_obfs[2])
+ elif type(password_obfs) == dict:
+ password = password_obfs.get('password', config_password)
+ method = common.to_str(password_obfs.get('method', method))
+ protocol = common.to_str(password_obfs.get('protocol', protocol))
+ protocol_param = common.to_str(password_obfs.get('protocol_param', protocol_param))
+ obfs = common.to_str(password_obfs.get('obfs', obfs))
+ obfs_param = common.to_str(password_obfs.get('obfs_param', obfs_param))
+ bind = password_obfs.get('out_bind', bind)
+ bindv6 = password_obfs.get('out_bindv6', bindv6)
+ else:
+ password = password_obfs
+ a_config = config.copy()
+ ipv6_ok = False
+ logging.info("server start with protocol[%s] password [%s] method [%s] obfs [%s] obfs_param [%s]" %
+ (protocol, password, method, obfs, obfs_param))
+ if 'server_ipv6' in a_config:
+ try:
+ if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][
+ -1] == "]":
+ a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
+ a_config['server_port'] = int(port)
+ a_config['password'] = password
+ a_config['method'] = method
+ a_config['protocol'] = protocol
+ a_config['protocol_param'] = protocol_param
+ a_config['obfs'] = obfs
+ a_config['obfs_param'] = obfs_param
+ a_config['out_bind'] = bind
+ a_config['out_bindv6'] = bindv6
+ a_config['server'] = a_config['server_ipv6']
+ logging.info("starting server at [%s]:%d" %
+ (a_config['server'], int(port)))
+ tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict))
+ udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict))
+ if a_config['server_ipv6'] == b"::":
+ ipv6_ok = True
+ except Exception as e:
+ shell.print_exception(e)
+
+ try:
+ a_config = config.copy()
+ a_config['server_port'] = int(port)
+ a_config['password'] = password
+ a_config['method'] = method
+ a_config['protocol'] = protocol
+ a_config['protocol_param'] = protocol_param
+ a_config['obfs'] = obfs
+ a_config['obfs_param'] = obfs_param
+ a_config['out_bind'] = bind
+ a_config['out_bindv6'] = bindv6
+ logging.info("starting server at %s:%d" %
+ (a_config['server'], int(port)))
+ tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict))
+ udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict))
+ except Exception as e:
+ if not ipv6_ok:
+ shell.print_exception(e)
+
+ def run_server():
+ def child_handler(signum, _):
+ logging.warn('received SIGQUIT, doing graceful shutting down..')
+ list(map(lambda s: s.close(next_tick=True),
+ tcp_servers + udp_servers))
+
+ signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM),
+ child_handler)
+
+ def int_handler(signum, _):
+ sys.exit(1)
+
+ signal.signal(signal.SIGINT, int_handler)
+
+ try:
+ loop = eventloop.EventLoop()
+ dns_resolver.add_to_loop(loop)
+ list(map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers))
+
+ daemon.set_user(config.get('user', None))
+ loop.run()
+ except Exception as e:
+ shell.print_exception(e)
+ sys.exit(1)
+
+ if int(config['workers']) > 1:
+ if os.name == 'posix':
+ children = []
+ is_child = False
+ for i in range(0, int(config['workers'])):
+ r = os.fork()
+ if r == 0:
+ logging.info('worker started')
+ is_child = True
+ run_server()
+ break
+ else:
+ children.append(r)
+ if not is_child:
+ def handler(signum, _):
+ for pid in children:
+ try:
+ os.kill(pid, signum)
+ os.waitpid(pid, 0)
+ except OSError: # child may already exited
+ pass
+ sys.exit()
+
+ signal.signal(signal.SIGTERM, handler)
+ signal.signal(signal.SIGQUIT, handler)
+ signal.signal(signal.SIGINT, handler)
+
+ # master
+ for a_tcp_server in tcp_servers:
+ a_tcp_server.close()
+ for a_udp_server in udp_servers:
+ a_udp_server.close()
+ dns_resolver.close()
+
+ for child in children:
+ os.waitpid(child, 0)
+ else:
+ logging.warn('worker is only available on Unix/Linux')
+ run_server()
+ else:
+ run_server()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py
new file mode 100644
index 0000000000..a1547d0821
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py
@@ -0,0 +1,451 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import json
+import sys
+import getopt
+import logging
+from shadowsocks.common import to_bytes, to_str, IPNetwork, PortRange
+from shadowsocks import encrypt
+
+VERBOSE_LEVEL = 5
+
+verbose = 0
+
+
+def check_python():
+ info = sys.version_info
+ if info[0] == 2 and not info[1] >= 6:
+ print('Python 2.6+ required')
+ sys.exit(1)
+ elif info[0] == 3 and not info[1] >= 3:
+ print('Python 3.3+ required')
+ sys.exit(1)
+ elif info[0] not in [2, 3]:
+ print('Python version not supported')
+ sys.exit(1)
+
+
+def print_exception(e):
+ global verbose
+ logging.error(e)
+ if verbose > 0:
+ import traceback
+ traceback.print_exc()
+
+
+def __version():
+ version_str = ''
+ try:
+ import pkg_resources
+ version_str = pkg_resources.get_distribution('shadowsocks').version
+ except Exception:
+ try:
+ from shadowsocks import version
+ version_str = version.version()
+ except Exception:
+ pass
+ return version_str
+
+
+def print_shadowsocks():
+ print('ShadowsocksR %s' % __version())
+
+
+def log_shadowsocks_version():
+ logging.info('ShadowsocksR %s' % __version())
+
+
+def find_config():
+ user_config_path = 'user-config.json'
+ config_path = 'config.json'
+
+ def sub_find(file_name):
+ if os.path.exists(file_name):
+ return file_name
+ file_name = os.path.join(os.path.abspath('..'), file_name)
+ return file_name if os.path.exists(file_name) else None
+
+ return sub_find(user_config_path) or sub_find(config_path)
+
+
+def check_config(config, is_local):
+ if config.get('daemon', None) == 'stop':
+ # no need to specify configuration for daemon stop
+ return
+
+ if is_local and not config.get('password', None):
+ logging.error('password not specified')
+ print_help(is_local)
+ sys.exit(2)
+
+ if not is_local and not config.get('password', None) \
+ and not config.get('port_password', None):
+ logging.error('password or port_password not specified')
+ print_help(is_local)
+ sys.exit(2)
+
+ if 'local_port' in config:
+ config['local_port'] = int(config['local_port'])
+
+ if 'server_port' in config and type(config['server_port']) != list:
+ config['server_port'] = int(config['server_port'])
+
+ if config.get('local_address', '') in [b'0.0.0.0']:
+ logging.warning('warning: local set to listen on 0.0.0.0, it\'s not safe')
+ if config.get('server', '') in ['127.0.0.1', 'localhost']:
+ logging.warning('warning: server set to listen on %s:%s, are you sure?' %
+ (to_str(config['server']), config['server_port']))
+ if config.get('timeout', 300) < 100:
+ logging.warning('warning: your timeout %d seems too short' %
+ int(config.get('timeout')))
+ if config.get('timeout', 300) > 600:
+ logging.warning('warning: your timeout %d seems too long' %
+ int(config.get('timeout')))
+ if config.get('password') in [b'mypassword']:
+ logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your '
+ 'config.json!')
+ sys.exit(1)
+ if config.get('user', None) is not None:
+ if os.name != 'posix':
+ logging.error('user can be used only on Unix')
+ sys.exit(1)
+
+ encrypt.try_cipher(config['password'], config['method'])
+
+
+def get_config(is_local):
+ global verbose
+ config = {}
+ config_path = None
+ logging.basicConfig(level=logging.INFO,
+ format='%(levelname)-s: %(message)s')
+ if is_local:
+ shortopts = 'hd:s:b:p:k:l:m:O:o:G:g:c:t:vq'
+ longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user=',
+ 'version']
+ else:
+ shortopts = 'hd:s:p:k:m:O:o:G:g:c:t:vq'
+ longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=',
+ 'forbidden-ip=', 'user=', 'manager-address=', 'version']
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
+ for key, value in optlist:
+ if key == '-c':
+ config_path = value
+ elif key in ('-h', '--help'):
+ print_help(is_local)
+ sys.exit(0)
+ elif key == '--version':
+ print_shadowsocks()
+ sys.exit(0)
+ else:
+ continue
+
+ if config_path is None:
+ config_path = find_config()
+
+ if config_path:
+ logging.debug('loading config from %s' % config_path)
+ with open(config_path, 'rb') as f:
+ try:
+ config = parse_json_in_str(remove_comment(f.read().decode('utf8')))
+ except ValueError as e:
+ logging.error('found an error in config.json: %s', str(e))
+ sys.exit(1)
+
+ v_count = 0
+ for key, value in optlist:
+ if key == '-p':
+ config['server_port'] = int(value)
+ elif key == '-k':
+ config['password'] = to_bytes(value)
+ elif key == '-l':
+ config['local_port'] = int(value)
+ elif key == '-s':
+ config['server'] = to_str(value)
+ elif key == '-m':
+ config['method'] = to_str(value)
+ elif key == '-O':
+ config['protocol'] = to_str(value)
+ elif key == '-o':
+ config['obfs'] = to_str(value)
+ elif key == '-G':
+ config['protocol_param'] = to_str(value)
+ elif key == '-g':
+ config['obfs_param'] = to_str(value)
+ elif key == '-b':
+ config['local_address'] = to_str(value)
+ elif key == '-v':
+ v_count += 1
+ # '-vv' turns on more verbose mode
+ config['verbose'] = v_count
+ elif key == '-t':
+ config['timeout'] = int(value)
+ elif key == '--fast-open':
+ config['fast_open'] = True
+ elif key == '--workers':
+ config['workers'] = int(value)
+ elif key == '--manager-address':
+ config['manager_address'] = value
+ elif key == '--user':
+ config['user'] = to_str(value)
+ elif key == '--forbidden-ip':
+ config['forbidden_ip'] = to_str(value)
+
+ elif key == '-d':
+ config['daemon'] = to_str(value)
+ elif key == '--pid-file':
+ config['pid-file'] = to_str(value)
+ elif key == '--log-file':
+ config['log-file'] = to_str(value)
+ elif key == '-q':
+ v_count -= 1
+ config['verbose'] = v_count
+ else:
+ continue
+ except getopt.GetoptError as e:
+ print(e, file=sys.stderr)
+ print_help(is_local)
+ sys.exit(2)
+
+ if not config:
+ logging.error('config not specified')
+ print_help(is_local)
+ sys.exit(2)
+
+ config['password'] = to_bytes(config.get('password', b''))
+ config['method'] = to_str(config.get('method', 'aes-256-cfb'))
+ config['protocol'] = to_str(config.get('protocol', 'origin'))
+ config['protocol_param'] = to_str(config.get('protocol_param', ''))
+ config['obfs'] = to_str(config.get('obfs', 'plain'))
+ config['obfs_param'] = to_str(config.get('obfs_param', ''))
+ config['port_password'] = config.get('port_password', None)
+ config['additional_ports'] = config.get('additional_ports', {})
+ config['additional_ports_only'] = config.get('additional_ports_only', False)
+ config['timeout'] = int(config.get('timeout', 300))
+ config['udp_timeout'] = int(config.get('udp_timeout', 120))
+ config['udp_cache'] = int(config.get('udp_cache', 64))
+ config['fast_open'] = config.get('fast_open', False)
+ config['workers'] = config.get('workers', 1)
+ config['pid-file'] = config.get('pid-file', '/var/run/shadowsocksr.pid')
+ config['log-file'] = config.get('log-file', '/var/log/shadowsocksr.log')
+ config['verbose'] = config.get('verbose', False)
+ config['connect_verbose_info'] = config.get('connect_verbose_info', 0)
+ config['local_address'] = to_str(config.get('local_address', '127.0.0.1'))
+ config['local_port'] = config.get('local_port', 1080)
+ if is_local:
+ if config.get('server', None) is None:
+ logging.error('server addr not specified')
+ print_local_help()
+ sys.exit(2)
+ else:
+ config['server'] = to_str(config['server'])
+ else:
+ config['server'] = to_str(config.get('server', '0.0.0.0'))
+ config['black_hostname_list'] = to_str(config.get('black_hostname_list', '')).split(',')
+ if len(config['black_hostname_list']) == 1 and config['black_hostname_list'][0] == '':
+ config['black_hostname_list'] = []
+ try:
+ config['forbidden_ip'] = \
+ IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128'))
+ except Exception as e:
+ logging.error(e)
+ sys.exit(2)
+ try:
+ config['forbidden_port'] = PortRange(config.get('forbidden_port', ''))
+ except Exception as e:
+ logging.error(e)
+ sys.exit(2)
+ try:
+ config['ignore_bind'] = \
+ IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128,10.0.0.0/8,192.168.0.0/16'))
+ except Exception as e:
+ logging.error(e)
+ sys.exit(2)
+ config['server_port'] = config.get('server_port', 8388)
+
+ logging.getLogger('').handlers = []
+ logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE')
+ if config['verbose'] >= 2:
+ level = VERBOSE_LEVEL
+ elif config['verbose'] == 1:
+ level = logging.DEBUG
+ elif config['verbose'] == -1:
+ level = logging.WARN
+ elif config['verbose'] <= -2:
+ level = logging.ERROR
+ else:
+ level = logging.INFO
+ verbose = config['verbose']
+ logging.basicConfig(level=level,
+ format='%(asctime)s %(levelname)-8s %(filename)s:%(lineno)s %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+
+ check_config(config, is_local)
+
+ return config
+
+
+def print_help(is_local):
+ if is_local:
+ print_local_help()
+ else:
+ print_server_help()
+
+
+def print_local_help():
+ print('''usage: sslocal [OPTION]...
+A fast tunnel proxy that helps you bypass firewalls.
+
+You can supply configurations via either config file or command line arguments.
+
+Proxy options:
+ -c CONFIG path to config file
+ -s SERVER_ADDR server address
+ -p SERVER_PORT server port, default: 8388
+ -b LOCAL_ADDR local binding address, default: 127.0.0.1
+ -l LOCAL_PORT local port, default: 1080
+ -k PASSWORD password
+ -m METHOD encryption method, default: aes-256-cfb
+ -o OBFS obfsplugin, default: http_simple
+ -t TIMEOUT timeout in seconds, default: 300
+ --fast-open use TCP_FASTOPEN, requires Linux 3.7+
+
+General options:
+ -h, --help show this help message and exit
+ -d start/stop/restart daemon mode
+ --pid-file PID_FILE pid file for daemon mode
+ --log-file LOG_FILE log file for daemon mode
+ --user USER username to run as
+ -v, -vv verbose mode
+ -q, -qq quiet mode, only show warnings/errors
+ --version show version information
+
+Online help:
+''')
+
+
+def print_server_help():
+ print('''usage: ssserver [OPTION]...
+A fast tunnel proxy that helps you bypass firewalls.
+
+You can supply configurations via either config file or command line arguments.
+
+Proxy options:
+ -c CONFIG path to config file
+ -s SERVER_ADDR server address, default: 0.0.0.0
+ -p SERVER_PORT server port, default: 8388
+ -k PASSWORD password
+ -m METHOD encryption method, default: aes-256-cfb
+ -o OBFS obfsplugin, default: http_simple
+ -t TIMEOUT timeout in seconds, default: 300
+ --fast-open use TCP_FASTOPEN, requires Linux 3.7+
+ --workers WORKERS number of workers, available on Unix/Linux
+ --forbidden-ip IPLIST comma seperated IP list forbidden to connect
+ --manager-address ADDR optional server manager UDP address, see wiki
+
+General options:
+ -h, --help show this help message and exit
+ -d start/stop/restart daemon mode
+ --pid-file PID_FILE pid file for daemon mode
+ --log-file LOG_FILE log file for daemon mode
+ --user USER username to run as
+ -v, -vv verbose mode
+ -q, -qq quiet mode, only show warnings/errors
+ --version show version information
+
+Online help:
+''')
+
+
+def _decode_list(data):
+ rv = []
+ for item in data:
+ if hasattr(item, 'encode'):
+ item = item.encode('utf-8')
+ elif isinstance(item, list):
+ item = _decode_list(item)
+ elif isinstance(item, dict):
+ item = _decode_dict(item)
+ rv.append(item)
+ return rv
+
+
+def _decode_dict(data):
+ rv = {}
+ for key, value in data.items():
+ if hasattr(value, 'encode'):
+ value = value.encode('utf-8')
+ elif isinstance(value, list):
+ value = _decode_list(value)
+ elif isinstance(value, dict):
+ value = _decode_dict(value)
+ rv[key] = value
+ return rv
+
+
+class JSFormat:
+ def __init__(self):
+ self.state = 0
+
+ def push(self, ch):
+ ch = ord(ch)
+ if self.state == 0:
+ if ch == ord('"'):
+ self.state = 1
+ return to_str(chr(ch))
+ elif ch == ord('/'):
+ self.state = 3
+ else:
+ return to_str(chr(ch))
+ elif self.state == 1:
+ if ch == ord('"'):
+ self.state = 0
+ return to_str(chr(ch))
+ elif ch == ord('\\'):
+ self.state = 2
+ return to_str(chr(ch))
+ elif self.state == 2:
+ self.state = 1
+ if ch == ord('"'):
+ return to_str(chr(ch))
+ return "\\" + to_str(chr(ch))
+ elif self.state == 3:
+ if ch == ord('/'):
+ self.state = 4
+ else:
+ return "/" + to_str(chr(ch))
+ elif self.state == 4:
+ if ch == ord('\n'):
+ self.state = 0
+ return "\n"
+ return ""
+
+
+def remove_comment(json):
+ fmt = JSFormat()
+ return "".join([fmt.push(c) for c in json])
+
+
+def parse_json_in_str(data):
+ # parse json and convert everything from unicode to str
+ return json.loads(data, object_hook=_decode_dict)
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh
new file mode 100755
index 0000000000..d7d29589ec
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh
new file mode 100755
index 0000000000..aa37139396
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+tail -f ssserver.log
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py
new file mode 100644
index 0000000000..595e2be73f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py
@@ -0,0 +1,1476 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import time
+import socket
+import errno
+import struct
+import logging
+import binascii
+import traceback
+import random
+import platform
+import threading
+
+from shadowsocks import encrypt, obfs, eventloop, shell, common, lru_cache, version
+from shadowsocks.common import pre_parse_header, parse_header
+
+# we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time
+TIMEOUTS_CLEAN_SIZE = 512
+
+MSG_FASTOPEN = 0x20000000
+
+# SOCKS command definition
+CMD_CONNECT = 1
+CMD_BIND = 2
+CMD_UDP_ASSOCIATE = 3
+
+# for each opening port, we have a TCP Relay
+
+# for each connection, we have a TCP Relay Handler to handle the connection
+
+# for each handler, we have 2 sockets:
+# local: connected to the client
+# remote: connected to remote server
+
+# for each handler, it could be at one of several stages:
+
+# as sslocal:
+# stage 0 SOCKS hello received from local, send hello to local
+# stage 1 addr received from local, query DNS for remote
+# stage 2 UDP assoc
+# stage 3 DNS resolved, connect to remote
+# stage 4 still connecting, more data from local received
+# stage 5 remote connected, piping local and remote
+
+# as ssserver:
+# stage 0 just jump to stage 1
+# stage 1 addr received from local, query DNS for remote
+# stage 3 DNS resolved, connect to remote
+# stage 4 still connecting, more data from local received
+# stage 5 remote connected, piping local and remote
+
+STAGE_INIT = 0
+STAGE_ADDR = 1
+STAGE_UDP_ASSOC = 2
+STAGE_DNS = 3
+STAGE_CONNECTING = 4
+STAGE_STREAM = 5
+STAGE_DESTROYED = -1
+
+# for each handler, we have 2 stream directions:
+# upstream: from client to server direction
+# read local and write to remote
+# downstream: from server to client direction
+# read remote and write to local
+
+STREAM_UP = 0
+STREAM_DOWN = 1
+
+# for each stream, it's waiting for reading, or writing, or both
+WAIT_STATUS_INIT = 0
+WAIT_STATUS_READING = 1
+WAIT_STATUS_WRITING = 2
+WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
+
+NETWORK_MTU = 1500
+TCP_MSS = NETWORK_MTU - 40
+BUF_SIZE = 32 * 1024
+UDP_MAX_BUF_SIZE = 65536
+
+class SpeedTester(object):
+ def __init__(self, max_speed = 0):
+ self.max_speed = max_speed * 1024
+ self.last_time = time.time()
+ self.sum_len = 0
+
+ def update_limit(self, max_speed):
+ self.max_speed = max_speed * 1024
+
+ def add(self, data_len):
+ if self.max_speed > 0:
+ cut_t = time.time()
+ self.sum_len -= (cut_t - self.last_time) * self.max_speed
+ if self.sum_len < 0:
+ self.sum_len = 0
+ self.last_time = cut_t
+ self.sum_len += data_len
+
+ def isExceed(self):
+ if self.max_speed > 0:
+ cut_t = time.time()
+ self.sum_len -= (cut_t - self.last_time) * self.max_speed
+ if self.sum_len < 0:
+ self.sum_len = 0
+ self.last_time = cut_t
+ return self.sum_len >= self.max_speed
+ return False
+
+class TCPRelayHandler(object):
+ def __init__(self, server, fd_to_handlers, loop, local_sock, config,
+ dns_resolver, is_local):
+ self._server = server
+ self._fd_to_handlers = fd_to_handlers
+ self._loop = loop
+ self._local_sock = local_sock
+ self._remote_sock = None
+ self._remote_sock_v6 = None
+ self._local_sock_fd = None
+ self._remote_sock_fd = None
+ self._remotev6_sock_fd = None
+ self._remote_udp = False
+ self._config = config
+ self._dns_resolver = dns_resolver
+ self._add_ref = 0
+ if not self._create_encryptor(config):
+ return
+
+ self._client_address = local_sock.getpeername()[:2]
+ self._accept_address = local_sock.getsockname()[:2]
+ self._user = None
+ self._user_id = server._listen_port
+ self._update_tcp_mss(local_sock)
+
+ # TCP Relay works as either sslocal or ssserver
+ # if is_local, this is sslocal
+ self._is_local = is_local
+ self._encrypt_correct = True
+ self._obfs = obfs.obfs(config['obfs'])
+ self._protocol = obfs.obfs(config['protocol'])
+ self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
+ self._recv_buffer_size = BUF_SIZE - self._overhead
+
+ server_info = obfs.server_info(server.obfs_data)
+ server_info.host = config['server']
+ server_info.port = server._listen_port
+ #server_info.users = server.server_users
+ #server_info.update_user_func = self._update_user
+ server_info.client = self._client_address[0]
+ server_info.client_port = self._client_address[1]
+ server_info.protocol_param = ''
+ server_info.obfs_param = config['obfs_param']
+ server_info.iv = self._encryptor.cipher_iv
+ server_info.recv_iv = b''
+ server_info.key_str = common.to_bytes(config['password'])
+ server_info.key = self._encryptor.cipher_key
+ server_info.head_len = 30
+ server_info.tcp_mss = self._tcp_mss
+ server_info.buffer_size = self._recv_buffer_size
+ server_info.overhead = self._overhead
+ self._obfs.set_server_info(server_info)
+
+ server_info = obfs.server_info(server.protocol_data)
+ server_info.host = config['server']
+ server_info.port = server._listen_port
+ server_info.users = server.server_users
+ server_info.update_user_func = self._update_user
+ server_info.client = self._client_address[0]
+ server_info.client_port = self._client_address[1]
+ server_info.protocol_param = config['protocol_param']
+ server_info.obfs_param = ''
+ server_info.iv = self._encryptor.cipher_iv
+ server_info.recv_iv = b''
+ server_info.key_str = common.to_bytes(config['password'])
+ server_info.key = self._encryptor.cipher_key
+ server_info.head_len = 30
+ server_info.tcp_mss = self._tcp_mss
+ server_info.buffer_size = self._recv_buffer_size
+ server_info.overhead = self._overhead
+ self._protocol.set_server_info(server_info)
+
+ self._redir_list = config.get('redirect', ["*#0.0.0.0:0"])
+ self._is_redirect = False
+ self._bind = config.get('out_bind', '')
+ self._bindv6 = config.get('out_bindv6', '')
+ self._ignore_bind_list = config.get('ignore_bind', [])
+
+ self._fastopen_connected = False
+ self._data_to_write_to_local = []
+ self._data_to_write_to_remote = []
+ self._udp_data_send_buffer = b''
+ self._upstream_status = WAIT_STATUS_READING
+ self._downstream_status = WAIT_STATUS_INIT
+ self._remote_address = None
+
+ self._forbidden_iplist = config.get('forbidden_ip', None)
+ self._forbidden_portset = config.get('forbidden_port', None)
+ if is_local:
+ self._chosen_server = self._get_a_server()
+
+ self.last_activity = 0
+ self._update_activity()
+ self._server.add_connection(1)
+ self._server.stat_add(self._client_address[0], 1)
+ self._add_ref = 1
+ self.speed_tester_u = SpeedTester(config.get("speed_limit_per_con", 0))
+ self.speed_tester_d = SpeedTester(config.get("speed_limit_per_con", 0))
+ self._recv_u_max_size = BUF_SIZE
+ self._recv_d_max_size = BUF_SIZE
+ self._recv_pack_id = 0
+ self._udp_send_pack_id = 0
+ self._udpv6_send_pack_id = 0
+
+ local_sock.setblocking(False)
+ local_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ self._local_sock_fd = local_sock.fileno()
+ fd_to_handlers[self._local_sock_fd] = self
+ loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR, self._server)
+ self._stage = STAGE_INIT
+
+ def __hash__(self):
+ # default __hash__ is id / 16
+ # we want to eliminate collisions
+ return id(self)
+
+ @property
+ def remote_address(self):
+ return self._remote_address
+
+ def _get_a_server(self):
+ server = self._config['server']
+ server_port = self._config['server_port']
+ if type(server_port) == list:
+ server_port = random.choice(server_port)
+ if type(server) == list:
+ server = random.choice(server)
+ logging.debug('chosen server: %s:%d', server, server_port)
+ return server, server_port
+
+ def _update_tcp_mss(self, local_sock):
+ self._tcp_mss = TCP_MSS
+ try:
+ tcp_mss = local_sock.getsockopt(socket.SOL_TCP, socket.TCP_MAXSEG)
+ if tcp_mss > 500 and tcp_mss <= 1500:
+ self._tcp_mss = tcp_mss
+ logging.debug("TCP MSS = %d" % (self._tcp_mss,))
+ except:
+ pass
+
+ def _create_encryptor(self, config):
+ try:
+ self._encryptor = encrypt.Encryptor(config['password'],
+ config['method'])
+ return True
+ except Exception:
+ self._stage = STAGE_DESTROYED
+ logging.error('create encryptor fail at port %d', self._server._listen_port)
+
+ def _update_user(self, user):
+ self._user = user
+ self._user_id = struct.unpack(' 6:
+ length = struct.unpack('>H', self._udp_data_send_buffer[:2])[0]
+
+ if length > len(self._udp_data_send_buffer):
+ break
+
+ data = self._udp_data_send_buffer[:length]
+ self._udp_data_send_buffer = self._udp_data_send_buffer[length:]
+
+ frag = common.ord(data[2])
+ if frag != 0:
+ logging.warn('drop a message since frag is %d' % (frag,))
+ continue
+ else:
+ data = data[3:]
+ header_result = parse_header(data)
+ if header_result is None:
+ continue
+ connecttype, addrtype, dest_addr, dest_port, header_length = header_result
+ if (addrtype & 7) == 3:
+ af = common.is_ip(dest_addr)
+ if af == False:
+ handler = common.UDPAsyncDNSHandler(data[header_length:])
+ handler.resolve(self._dns_resolver, (dest_addr, dest_port), self._handle_server_dns_resolved)
+ else:
+ return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
+ else:
+ return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
+
+ except Exception as e:
+ #trace = traceback.format_exc()
+ #logging.error(trace)
+ error_no = eventloop.errno_from_exception(e)
+ if error_no in (errno.EAGAIN, errno.EINPROGRESS,
+ errno.EWOULDBLOCK):
+ uncomplete = True
+ else:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return False
+ return True
+ else:
+ try:
+ if self._encrypt_correct:
+ if sock == self._remote_sock:
+ self._server.add_transfer_u(self._user, len(data))
+ self._update_activity(len(data))
+ if data:
+ l = len(data)
+ s = sock.send(data)
+ if s < l:
+ data = data[s:]
+ uncomplete = True
+ else:
+ return
+ except (OSError, IOError) as e:
+ error_no = eventloop.errno_from_exception(e)
+ if error_no in (errno.EAGAIN, errno.EINPROGRESS,
+ errno.EWOULDBLOCK):
+ uncomplete = True
+ else:
+ #traceback.print_exc()
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return False
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return False
+ if uncomplete:
+ if sock == self._local_sock:
+ self._data_to_write_to_local.append(data)
+ self._update_stream(STREAM_DOWN, WAIT_STATUS_WRITING)
+ elif sock == self._remote_sock:
+ self._data_to_write_to_remote.append(data)
+ self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
+ else:
+ logging.error('write_all_to_sock:unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
+ else:
+ if sock == self._local_sock:
+ self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)
+ elif sock == self._remote_sock:
+ self._update_stream(STREAM_UP, WAIT_STATUS_READING)
+ else:
+ logging.error('write_all_to_sock:unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
+ return True
+
+ def _handle_server_dns_resolved(self, error, remote_addr, server_addr, data):
+ if error:
+ return
+ try:
+ addrs = socket.getaddrinfo(server_addr, remote_addr[1], 0, socket.SOCK_DGRAM, socket.SOL_UDP)
+ if not addrs: # drop
+ return
+ af, socktype, proto, canonname, sa = addrs[0]
+ if af == socket.AF_INET6:
+ self._remote_sock_v6.sendto(data, (server_addr, remote_addr[1]))
+ if self._udpv6_send_pack_id == 0:
+ addr, port = self._remote_sock_v6.getsockname()[:2]
+ common.connect_log('UDPv6 sendto %s(%s):%d from %s:%d by user %d' %
+ (common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
+ self._udpv6_send_pack_id += 1
+ else:
+ self._remote_sock.sendto(data, (server_addr, remote_addr[1]))
+ if self._udp_send_pack_id == 0:
+ addr, port = self._remote_sock.getsockname()[:2]
+ common.connect_log('UDP sendto %s(%s):%d from %s:%d by user %d' %
+ (common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
+ self._udp_send_pack_id += 1
+ return True
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+
+ def _get_redirect_host(self, client_address, ogn_data):
+ host_list = self._redir_list or ["*#0.0.0.0:0"]
+
+ if type(host_list) != list:
+ host_list = [host_list]
+
+ items_sum = common.to_str(host_list[0]).rsplit('#', 1)
+ if len(items_sum) < 2:
+ hash_code = binascii.crc32(ogn_data)
+ addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP)
+ af, socktype, proto, canonname, sa = addrs[0]
+ address_bytes = common.inet_pton(af, sa[0])
+ if af == socket.AF_INET6:
+ addr = struct.unpack('>Q', address_bytes[8:])[0]
+ elif af == socket.AF_INET:
+ addr = struct.unpack('>I', address_bytes)[0]
+ else:
+ addr = 0
+
+ host_port = []
+ match_port = False
+ for host in host_list:
+ items = common.to_str(host).rsplit(':', 1)
+ if len(items) > 1:
+ try:
+ port = int(items[1])
+ if port == self._server._listen_port:
+ match_port = True
+ host_port.append((items[0], port))
+ except:
+ pass
+ else:
+ host_port.append((host, 80))
+
+ if match_port:
+ last_host_port = host_port
+ host_port = []
+ for host in last_host_port:
+ if host[1] == self._server._listen_port:
+ host_port.append(host)
+
+ return host_port[((hash_code & 0xffffffff) + addr) % len(host_port)]
+
+ else:
+ host_port = []
+ for host in host_list:
+ items_sum = common.to_str(host).rsplit('#', 1)
+ items_match = common.to_str(items_sum[0]).rsplit(':', 1)
+ items = common.to_str(items_sum[1]).rsplit(':', 1)
+ if len(items_match) > 1:
+ if items_match[1] != "*":
+ try:
+ if self._server._listen_port != int(items_match[1]) and int(items_match[1]) != 0:
+ continue
+ except:
+ pass
+
+ if items_match[0] != "*" and common.match_regex(
+ items_match[0], ogn_data) == False:
+ continue
+ if len(items) > 1:
+ try:
+ port = int(items[1])
+ return (items[0], port)
+ except:
+ pass
+ else:
+ return (items[0], 80)
+
+ return ("0.0.0.0", 0)
+
+ def _handel_protocol_error(self, client_address, ogn_data):
+ logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d by UID %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port, self._user_id))
+ self._encrypt_correct = False
+ #create redirect or disconnect by hash code
+ host, port = self._get_redirect_host(client_address, ogn_data)
+ if port == 0:
+ raise Exception('can not parse header')
+ data = b"\x03" + common.to_bytes(common.chr(len(host))) + common.to_bytes(host) + struct.pack('>H', port)
+ self._is_redirect = True
+ logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data)))
+ return data + ogn_data
+
+ def _handle_stage_connecting(self, data):
+ if self._is_local:
+ if self._encryptor is not None:
+ data = self._protocol.client_pre_encrypt(data)
+ data = self._encryptor.encrypt(data)
+ data = self._obfs.client_encode(data)
+ if data:
+ self._data_to_write_to_remote.append(data)
+ if self._is_local and not self._fastopen_connected and \
+ self._config['fast_open']:
+ # for sslocal and fastopen, we basically wait for data and use
+ # sendto to connect
+ try:
+ # only connect once
+ self._fastopen_connected = True
+ remote_sock = \
+ self._create_remote_socket(self._chosen_server[0],
+ self._chosen_server[1])
+ self._loop.add(remote_sock, eventloop.POLL_ERR, self._server)
+ data = b''.join(self._data_to_write_to_remote)
+ l = len(data)
+ s = remote_sock.sendto(data, MSG_FASTOPEN, self._chosen_server)
+ if s < l:
+ data = data[s:]
+ self._data_to_write_to_remote = [data]
+ else:
+ self._data_to_write_to_remote = []
+ self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING)
+ except (OSError, IOError) as e:
+ if eventloop.errno_from_exception(e) == errno.EINPROGRESS:
+ # in this case data is not sent at all
+ self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING)
+ elif eventloop.errno_from_exception(e) == errno.ENOTCONN:
+ logging.error('fast open not supported on this OS')
+ self._config['fast_open'] = False
+ self.destroy()
+ else:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+
+ def _get_head_size(self, buf, def_value):
+ if len(buf) < 2:
+ return def_value
+ head_type = common.ord(buf[0]) & 0xF
+ if head_type == 1:
+ return 7
+ if head_type == 4:
+ return 19
+ if head_type == 3:
+ return 4 + common.ord(buf[1])
+ return def_value
+
+ def _handle_stage_addr(self, ogn_data, data):
+ try:
+ if self._is_local:
+ cmd = common.ord(data[1])
+ if cmd == CMD_UDP_ASSOCIATE:
+ logging.debug('UDP associate')
+ if self._local_sock.family == socket.AF_INET6:
+ header = b'\x05\x00\x00\x04'
+ else:
+ header = b'\x05\x00\x00\x01'
+ addr, port = self._local_sock.getsockname()[:2]
+ addr_to_send = socket.inet_pton(self._local_sock.family,
+ addr)
+ port_to_send = struct.pack('>H', port)
+ self._write_to_sock(header + addr_to_send + port_to_send,
+ self._local_sock)
+ self._stage = STAGE_UDP_ASSOC
+ # just wait for the client to disconnect
+ return
+ elif cmd == CMD_CONNECT:
+ # just trim VER CMD RSV
+ data = data[3:]
+ else:
+ logging.error('invalid command %d', cmd)
+ self.destroy()
+ return
+
+ before_parse_data = data
+ if self._is_local:
+ header_result = parse_header(data)
+ else:
+ data = pre_parse_header(data)
+ if data is None:
+ data = self._handel_protocol_error(self._client_address, ogn_data)
+ header_result = parse_header(data)
+ if header_result is not None:
+ try:
+ common.to_str(header_result[2])
+ except Exception as e:
+ header_result = None
+ if header_result is None:
+ data = self._handel_protocol_error(self._client_address, ogn_data)
+ header_result = parse_header(data)
+ self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
+ self._recv_buffer_size = BUF_SIZE - self._overhead
+ server_info = self._obfs.get_server_info()
+ server_info.buffer_size = self._recv_buffer_size
+ server_info = self._protocol.get_server_info()
+ server_info.buffer_size = self._recv_buffer_size
+ connecttype, addrtype, remote_addr, remote_port, header_length = header_result
+ if connecttype != 0:
+ pass
+ #common.connect_log('UDP over TCP by user %d' %
+ # (self._user_id, ))
+ else:
+ common.connect_log('TCP request %s:%d by user %d' %
+ (common.to_str(remote_addr), remote_port, self._user_id))
+ self._remote_address = (common.to_str(remote_addr), remote_port)
+ self._remote_udp = (connecttype != 0)
+ # pause reading
+ self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
+ self._stage = STAGE_DNS
+ if self._is_local:
+ # forward address to remote
+ self._write_to_sock((b'\x05\x00\x00\x01'
+ b'\x00\x00\x00\x00\x10\x10'),
+ self._local_sock)
+ head_len = self._get_head_size(data, 30)
+ self._obfs.obfs.server_info.head_len = head_len
+ self._protocol.obfs.server_info.head_len = head_len
+ if self._encryptor is not None:
+ data = self._protocol.client_pre_encrypt(data)
+ data_to_send = self._encryptor.encrypt(data)
+ data_to_send = self._obfs.client_encode(data_to_send)
+ if data_to_send:
+ self._data_to_write_to_remote.append(data_to_send)
+ # notice here may go into _handle_dns_resolved directly
+ self._dns_resolver.resolve(self._chosen_server[0],
+ self._handle_dns_resolved)
+ else:
+ if len(data) > header_length:
+ self._data_to_write_to_remote.append(data[header_length:])
+ # notice here may go into _handle_dns_resolved directly
+ self._dns_resolver.resolve(remote_addr,
+ self._handle_dns_resolved)
+ except Exception as e:
+ self._log_error(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ self.destroy()
+
+ def _socket_bind_addr(self, sock, af):
+ bind_addr = ''
+ if self._bind and af == socket.AF_INET:
+ bind_addr = self._bind
+ elif self._bindv6 and af == socket.AF_INET6:
+ bind_addr = self._bindv6
+ else:
+ bind_addr = self._accept_address[0]
+
+ bind_addr = bind_addr.replace("::ffff:", "")
+ if bind_addr in self._ignore_bind_list:
+ bind_addr = None
+ if bind_addr:
+ local_addrs = socket.getaddrinfo(bind_addr, 0, 0, socket.SOCK_STREAM, socket.SOL_TCP)
+ if local_addrs[0][0] == af:
+ logging.debug("bind %s" % (bind_addr,))
+ try:
+ sock.bind((bind_addr, 0))
+ except Exception as e:
+ logging.warn("bind %s fail" % (bind_addr,))
+
+ def _create_remote_socket(self, ip, port):
+ if self._remote_udp:
+ addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
+ addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
+ else:
+ addrs = socket.getaddrinfo(ip, port, 0, socket.SOCK_STREAM, socket.SOL_TCP)
+ if len(addrs) == 0:
+ raise Exception("getaddrinfo failed for %s:%d" % (ip, port))
+ af, socktype, proto, canonname, sa = addrs[0]
+ if not self._remote_udp and not self._is_redirect:
+ if self._forbidden_iplist:
+ if common.to_str(sa[0]) in self._forbidden_iplist:
+ if self._remote_address:
+ raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d by UID %d' %
+ (common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
+ raise Exception('IP %s is in forbidden list, reject' %
+ common.to_str(sa[0]))
+ if self._forbidden_portset:
+ if sa[1] in self._forbidden_portset:
+ if self._remote_address:
+ raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d by UID %d' %
+ (sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
+ raise Exception('Port %d is in forbidden list, reject' % sa[1])
+ remote_sock = socket.socket(af, socktype, proto)
+ self._remote_sock = remote_sock
+ self._remote_sock_fd = remote_sock.fileno()
+ self._fd_to_handlers[self._remote_sock_fd] = self
+
+ if self._remote_udp:
+ af, socktype, proto, canonname, sa = addrs_v6[0]
+ remote_sock_v6 = socket.socket(af, socktype, proto)
+ self._remote_sock_v6 = remote_sock_v6
+ self._remotev6_sock_fd = remote_sock_v6.fileno()
+ self._fd_to_handlers[self._remotev6_sock_fd] = self
+
+ remote_sock.setblocking(False)
+ if self._remote_udp:
+ remote_sock_v6.setblocking(False)
+
+ if not self._is_local:
+ self._socket_bind_addr(remote_sock, af)
+ self._socket_bind_addr(remote_sock_v6, af)
+ else:
+ remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ if not self._is_local:
+ self._socket_bind_addr(remote_sock, af)
+ return remote_sock
+
+ def _handle_dns_resolved(self, result, error):
+ if error:
+ self._log_error(error)
+ self.destroy()
+ return
+ if result:
+ ip = result[1]
+ if ip:
+ try:
+ self._stage = STAGE_CONNECTING
+ remote_addr = ip
+ if self._is_local:
+ remote_port = self._chosen_server[1]
+ else:
+ remote_port = self._remote_address[1]
+
+ if self._is_local and self._config['fast_open']:
+ # for fastopen:
+ # wait for more data to arrive and send them in one SYN
+ self._stage = STAGE_CONNECTING
+ # we don't have to wait for remote since it's not
+ # created
+ self._update_stream(STREAM_UP, WAIT_STATUS_READING)
+ # TODO when there is already data in this packet
+ else:
+ # else do connect
+ remote_sock = self._create_remote_socket(remote_addr,
+ remote_port)
+ if self._remote_udp:
+ self._loop.add(remote_sock,
+ eventloop.POLL_IN,
+ self._server)
+ if self._remote_sock_v6:
+ self._loop.add(self._remote_sock_v6,
+ eventloop.POLL_IN,
+ self._server)
+ else:
+ try:
+ remote_sock.connect((remote_addr, remote_port))
+ except (OSError, IOError) as e:
+ if eventloop.errno_from_exception(e) in (errno.EINPROGRESS,
+ errno.EWOULDBLOCK):
+ pass # always goto here
+ else:
+ raise e
+ addr, port = self._remote_sock.getsockname()[:2]
+ common.connect_log('TCP connecting %s(%s):%d from %s:%d by user %d' %
+ (common.to_str(self._remote_address[0]), common.to_str(remote_addr), remote_port, addr, port, self._user_id))
+
+ self._loop.add(remote_sock,
+ eventloop.POLL_ERR | eventloop.POLL_OUT,
+ self._server)
+ self._stage = STAGE_CONNECTING
+ self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING)
+ self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)
+ if self._remote_udp:
+ while self._data_to_write_to_remote:
+ data = self._data_to_write_to_remote[0]
+ del self._data_to_write_to_remote[0]
+ self._write_to_sock(data, self._remote_sock)
+ return
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+
+ def _get_read_size(self, sock, recv_buffer_size, up):
+ if self._overhead == 0:
+ return recv_buffer_size
+ buffer_size = len(sock.recv(recv_buffer_size, socket.MSG_PEEK))
+ frame_size = self._tcp_mss - self._overhead
+ if up:
+ buffer_size = min(buffer_size, self._recv_u_max_size)
+ self._recv_u_max_size = min(self._recv_u_max_size + frame_size, BUF_SIZE)
+ else:
+ buffer_size = min(buffer_size, self._recv_d_max_size)
+ self._recv_d_max_size = min(self._recv_d_max_size + frame_size, BUF_SIZE)
+ if buffer_size == recv_buffer_size:
+ return buffer_size
+ if buffer_size > frame_size:
+ buffer_size = int(buffer_size / frame_size) * frame_size
+ return buffer_size
+
+ def _on_local_read(self):
+ # handle all local read events and dispatch them to methods for
+ # each stage
+ if not self._local_sock:
+ return
+ is_local = self._is_local
+ if is_local:
+ recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size, True)
+ else:
+ recv_buffer_size = BUF_SIZE
+ data = None
+ try:
+ data = self._local_sock.recv(recv_buffer_size)
+ except (OSError, IOError) as e:
+ if eventloop.errno_from_exception(e) in \
+ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
+ return
+ if not data:
+ self.destroy()
+ return
+
+ self.speed_tester_u.add(len(data))
+ self._server.speed_tester_u(self._user_id).add(len(data))
+ ogn_data = data
+ if not is_local:
+ if self._encryptor is not None:
+ if self._encrypt_correct:
+ try:
+ obfs_decode = self._obfs.server_decode(data)
+ if self._stage == STAGE_INIT:
+ self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
+ server_info = self._protocol.get_server_info()
+ server_info.overhead = self._overhead
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ if obfs_decode[2]:
+ data = self._obfs.server_encode(b'')
+ try:
+ self._write_to_sock(data, self._local_sock)
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ if obfs_decode[1]:
+ if not self._protocol.obfs.server_info.recv_iv:
+ iv_len = len(self._protocol.obfs.server_info.iv)
+ self._protocol.obfs.server_info.recv_iv = obfs_decode[0][:iv_len]
+ data = self._encryptor.decrypt(obfs_decode[0])
+ else:
+ data = obfs_decode[0]
+ try:
+ data, sendback = self._protocol.server_post_decrypt(data)
+ if sendback:
+ backdata = self._protocol.server_pre_encrypt(b'')
+ backdata = self._encryptor.encrypt(backdata)
+ backdata = self._obfs.server_encode(backdata)
+ try:
+ self._write_to_sock(backdata, self._local_sock)
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ else:
+ return
+ if not data:
+ return
+ if self._stage == STAGE_STREAM:
+ if self._is_local:
+ if self._encryptor is not None:
+ data = self._protocol.client_pre_encrypt(data)
+ data = self._encryptor.encrypt(data)
+ data = self._obfs.client_encode(data)
+ self._write_to_sock(data, self._remote_sock)
+ elif is_local and self._stage == STAGE_INIT:
+ # TODO check auth method
+ self._write_to_sock(b'\x05\00', self._local_sock)
+ self._stage = STAGE_ADDR
+ elif self._stage == STAGE_CONNECTING:
+ self._handle_stage_connecting(data)
+ elif (is_local and self._stage == STAGE_ADDR) or \
+ (not is_local and self._stage == STAGE_INIT):
+ self._handle_stage_addr(ogn_data, data)
+
+ def _on_remote_read(self, is_remote_sock):
+ # handle all remote read events
+ data = None
+ try:
+ if self._remote_udp:
+ if is_remote_sock:
+ data, addr = self._remote_sock.recvfrom(UDP_MAX_BUF_SIZE)
+ else:
+ data, addr = self._remote_sock_v6.recvfrom(UDP_MAX_BUF_SIZE)
+ port = struct.pack('>H', addr[1])
+ try:
+ ip = socket.inet_aton(addr[0])
+ data = b'\x00\x01' + ip + port + data
+ except Exception as e:
+ ip = socket.inet_pton(socket.AF_INET6, addr[0])
+ data = b'\x00\x04' + ip + port + data
+ size = len(data) + 2
+ data = struct.pack('>H', size) + data
+ #logging.info('UDP over TCP recvfrom %s:%d %d bytes to %s:%d' % (addr[0], addr[1], len(data), self._client_address[0], self._client_address[1]))
+ else:
+ if self._is_local:
+ recv_buffer_size = BUF_SIZE
+ else:
+ recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size, False)
+ data = self._remote_sock.recv(recv_buffer_size)
+ self._recv_pack_id += 1
+ except (OSError, IOError) as e:
+ if eventloop.errno_from_exception(e) in \
+ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK
+ return
+ if not data:
+ self.destroy()
+ return
+
+ self.speed_tester_d.add(len(data))
+ self._server.speed_tester_d(self._user_id).add(len(data))
+ if self._encryptor is not None:
+ if self._is_local:
+ try:
+ obfs_decode = self._obfs.client_decode(data)
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ if obfs_decode[1]:
+ send_back = self._obfs.client_encode(b'')
+ self._write_to_sock(send_back, self._remote_sock)
+ if not self._protocol.obfs.server_info.recv_iv:
+ iv_len = len(self._protocol.obfs.server_info.iv)
+ self._protocol.obfs.server_info.recv_iv = obfs_decode[0][:iv_len]
+ data = self._encryptor.decrypt(obfs_decode[0])
+ try:
+ data = self._protocol.client_post_decrypt(data)
+ if self._recv_pack_id == 1:
+ self._tcp_mss = self._protocol.get_server_info().tcp_mss
+ except Exception as e:
+ shell.print_exception(e)
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+ return
+ else:
+ if self._encrypt_correct:
+ data = self._protocol.server_pre_encrypt(data)
+ data = self._encryptor.encrypt(data)
+ data = self._obfs.server_encode(data)
+ self._server.add_transfer_d(self._user, len(data))
+ self._update_activity(len(data))
+ else:
+ return
+ try:
+ self._write_to_sock(data, self._local_sock)
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+
+ def _on_local_write(self):
+ # handle local writable event
+ if self._data_to_write_to_local:
+ data = b''.join(self._data_to_write_to_local)
+ self._data_to_write_to_local = []
+ self._write_to_sock(data, self._local_sock)
+ else:
+ self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)
+
+ def _on_remote_write(self):
+ # handle remote writable event
+ self._stage = STAGE_STREAM
+ if self._data_to_write_to_remote:
+ data = b''.join(self._data_to_write_to_remote)
+ self._data_to_write_to_remote = []
+ self._write_to_sock(data, self._remote_sock)
+ else:
+ self._update_stream(STREAM_UP, WAIT_STATUS_READING)
+
+ def _on_local_error(self):
+ if self._local_sock:
+ err = eventloop.get_sock_error(self._local_sock)
+ if err.errno not in [errno.ECONNRESET, errno.EPIPE]:
+ logging.error(err)
+ logging.error("local error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+
+ def _on_remote_error(self):
+ if self._remote_sock:
+ err = eventloop.get_sock_error(self._remote_sock)
+ if err.errno not in [errno.ECONNRESET]:
+ logging.error(err)
+ if self._remote_address:
+ logging.error("remote error, when connect to %s:%d" % (self._remote_address[0], self._remote_address[1]))
+ else:
+ logging.error("remote error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
+ self.destroy()
+
+ def handle_event(self, sock, fd, event):
+ # handle all events in this handler and dispatch them to methods
+ handle = False
+ if self._stage == STAGE_DESTROYED:
+ logging.debug('ignore handle_event: destroyed')
+ return True
+ if self._user is not None and self._user not in self._server.server_users:
+ self.destroy()
+ return True
+ if fd == self._remote_sock_fd or fd == self._remotev6_sock_fd:
+ if event & eventloop.POLL_ERR:
+ handle = True
+ self._on_remote_error()
+ elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
+ if not self.speed_tester_d.isExceed() and not self._server.speed_tester_d(self._user_id).isExceed():
+ handle = True
+ self._on_remote_read(sock == self._remote_sock)
+ else:
+ self._recv_d_max_size = self._tcp_mss - self._overhead
+ elif event & eventloop.POLL_OUT:
+ handle = True
+ self._on_remote_write()
+ elif fd == self._local_sock_fd:
+ if event & eventloop.POLL_ERR:
+ handle = True
+ self._on_local_error()
+ elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
+ if not self.speed_tester_u.isExceed() and not self._server.speed_tester_u(self._user_id).isExceed():
+ handle = True
+ self._on_local_read()
+ else:
+ self._recv_u_max_size = self._tcp_mss - self._overhead
+ elif event & eventloop.POLL_OUT:
+ handle = True
+ self._on_local_write()
+ else:
+ logging.warn('unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
+ try:
+ self._loop.removefd(fd)
+ except Exception as e:
+ shell.print_exception(e)
+ try:
+ del self._fd_to_handlers[fd]
+ except Exception as e:
+ shell.print_exception(e)
+ sock.close()
+
+ return handle
+
+ def _log_error(self, e):
+ logging.error('%s when handling connection from %s:%d' %
+ (e, self._client_address[0], self._client_address[1]))
+
+ def stage(self):
+ return self._stage
+
+ def destroy(self):
+ # destroy the handler and release any resources
+ # promises:
+ # 1. destroy won't make another destroy() call inside
+ # 2. destroy releases resources so it prevents future call to destroy
+ # 3. destroy won't raise any exceptions
+ # if any of the promises are broken, it indicates a bug has been
+ # introduced! mostly likely memory leaks, etc
+ if self._stage == STAGE_DESTROYED:
+ # this couldn't happen
+ logging.debug('already destroyed')
+ return
+ self._stage = STAGE_DESTROYED
+ if self._remote_address:
+ logging.debug('destroy: %s:%d' %
+ self._remote_address)
+ else:
+ logging.debug('destroy')
+ if self._remote_sock:
+ logging.debug('destroying remote')
+ try:
+ self._loop.removefd(self._remote_sock_fd)
+ except Exception as e:
+ shell.print_exception(e)
+ try:
+ if self._remote_sock_fd is not None:
+ del self._fd_to_handlers[self._remote_sock_fd]
+ except Exception as e:
+ shell.print_exception(e)
+ self._remote_sock.close()
+ self._remote_sock = None
+ if self._remote_sock_v6:
+ logging.debug('destroying remote_v6')
+ try:
+ self._loop.removefd(self._remotev6_sock_fd)
+ except Exception as e:
+ shell.print_exception(e)
+ try:
+ if self._remotev6_sock_fd is not None:
+ del self._fd_to_handlers[self._remotev6_sock_fd]
+ except Exception as e:
+ shell.print_exception(e)
+ self._remote_sock_v6.close()
+ self._remote_sock_v6 = None
+ if self._local_sock:
+ logging.debug('destroying local')
+ try:
+ self._loop.removefd(self._local_sock_fd)
+ except Exception as e:
+ shell.print_exception(e)
+ try:
+ if self._local_sock_fd is not None:
+ del self._fd_to_handlers[self._local_sock_fd]
+ except Exception as e:
+ shell.print_exception(e)
+ self._local_sock.close()
+ self._local_sock = None
+ if self._obfs:
+ self._obfs.dispose()
+ self._obfs = None
+ if self._protocol:
+ self._protocol.dispose()
+ self._protocol = None
+ self._encryptor = None
+ self._dns_resolver.remove_callback(self._handle_dns_resolved)
+ self._server.remove_handler(self)
+ if self._add_ref > 0:
+ self._server.add_connection(-1)
+ self._server.stat_add(self._client_address[0], -1)
+
+class TCPRelay(object):
+ def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None):
+ self._config = config
+ self._is_local = is_local
+ self._dns_resolver = dns_resolver
+ self._closed = False
+ self._eventloop = None
+ self._fd_to_handlers = {}
+ self.server_transfer_ul = 0
+ self.server_transfer_dl = 0
+ self.server_users = {}
+ self.server_users_cfg = {}
+ self.server_user_transfer_ul = {}
+ self.server_user_transfer_dl = {}
+ self.mu = False
+ self._speed_tester_u = {}
+ self._speed_tester_d = {}
+ self.server_connections = 0
+ self.protocol_data = obfs.obfs(config['protocol']).init_data()
+ self.obfs_data = obfs.obfs(config['obfs']).init_data()
+
+ if config.get('connect_verbose_info', 0) > 0:
+ common.connect_log = logging.info
+
+ self._timeout = config['timeout']
+ self._timeout_cache = lru_cache.LRUCache(timeout=self._timeout,
+ close_callback=self._close_tcp_client)
+
+ if is_local:
+ listen_addr = config['local_address']
+ listen_port = config['local_port']
+ else:
+ listen_addr = config['server']
+ listen_port = config['server_port']
+ self._listen_port = listen_port
+
+ if common.to_str(config['protocol']) in obfs.mu_protocol():
+ self._update_users(None, None)
+
+ addrs = socket.getaddrinfo(listen_addr, listen_port, 0,
+ socket.SOCK_STREAM, socket.SOL_TCP)
+ if len(addrs) == 0:
+ raise Exception("can't get addrinfo for %s:%d" %
+ (listen_addr, listen_port))
+ af, socktype, proto, canonname, sa = addrs[0]
+ server_socket = socket.socket(af, socktype, proto)
+ server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ server_socket.bind(sa)
+ server_socket.setblocking(False)
+ if config['fast_open']:
+ try:
+ server_socket.setsockopt(socket.SOL_TCP, 23, 5)
+ except socket.error:
+ logging.error('warning: fast open is not available')
+ self._config['fast_open'] = False
+ server_socket.listen(config.get('max_connect', 1024))
+ self._server_socket = server_socket
+ self._server_socket_fd = server_socket.fileno()
+ self._stat_counter = stat_counter
+ self._stat_callback = stat_callback
+
+ def add_to_loop(self, loop):
+ if self._eventloop:
+ raise Exception('already add to loop')
+ if self._closed:
+ raise Exception('already closed')
+ self._eventloop = loop
+ self._eventloop.add(self._server_socket,
+ eventloop.POLL_IN | eventloop.POLL_ERR, self)
+ self._eventloop.add_periodic(self.handle_periodic)
+
+ def remove_handler(self, client):
+ if hash(client) in self._timeout_cache:
+ del self._timeout_cache[hash(client)]
+
+ def add_connection(self, val):
+ self.server_connections += val
+ logging.debug('server port %5d connections = %d' % (self._listen_port, self.server_connections,))
+
+ def get_ud(self):
+ return (self.server_transfer_ul, self.server_transfer_dl)
+
+ def get_users_ud(self):
+ return (self.server_user_transfer_ul.copy(), self.server_user_transfer_dl.copy())
+
+ def _update_users(self, protocol_param, acl):
+ if protocol_param is None:
+ protocol_param = self._config['protocol_param']
+ param = common.to_bytes(protocol_param).split(b'#')
+ if len(param) == 2:
+ self.mu = True
+ user_list = param[1].split(b',')
+ if user_list:
+ for user in user_list:
+ items = user.split(b':')
+ if len(items) == 2:
+ user_int_id = int(items[0])
+ uid = struct.pack('= stat_dict.get(-1, 0) + connections_step:
+ logging.info('port %d connections up to %d' % (port, newval))
+ stat_dict[-1] = stat_dict.get(-1, 0) + connections_step
+ elif newval <= stat_dict.get(-1, 0) - connections_step:
+ logging.info('port %d connections down to %d' % (port, newval))
+ stat_dict[-1] = stat_dict.get(-1, 0) - connections_step
+
+ def stat_add(self, local_addr, val):
+ if self._stat_counter is not None:
+ if self._listen_port not in self._stat_counter:
+ self._stat_counter[self._listen_port] = {}
+ newval = self._stat_counter[self._listen_port].get(local_addr, 0) + val
+ logging.debug('port %d addr %s connections %d' % (self._listen_port, local_addr, newval))
+ self._stat_counter[self._listen_port][local_addr] = newval
+ self.update_stat(self._listen_port, self._stat_counter[self._listen_port], val)
+ if newval <= 0:
+ if local_addr in self._stat_counter[self._listen_port]:
+ del self._stat_counter[self._listen_port][local_addr]
+
+ newval = self._stat_counter.get(0, 0) + val
+ self._stat_counter[0] = newval
+ logging.debug('Total connections %d' % newval)
+
+ connections_step = 50
+ if newval >= self._stat_counter.get(-1, 0) + connections_step:
+ logging.info('Total connections up to %d' % newval)
+ self._stat_counter[-1] = self._stat_counter.get(-1, 0) + connections_step
+ elif newval <= self._stat_counter.get(-1, 0) - connections_step:
+ logging.info('Total connections down to %d' % newval)
+ self._stat_counter[-1] = self._stat_counter.get(-1, 0) - connections_step
+
+ def update_activity(self, client, data_len):
+ if data_len and self._stat_callback:
+ self._stat_callback(self._listen_port, data_len)
+
+ self._timeout_cache[hash(client)] = client
+
+ def _sweep_timeout(self):
+ self._timeout_cache.sweep()
+
+ def _close_tcp_client(self, client):
+ if client.remote_address:
+ logging.debug('timed out: %s:%d' %
+ client.remote_address)
+ else:
+ logging.debug('timed out')
+ client.destroy()
+
+ def handle_event(self, sock, fd, event):
+ # handle events and dispatch to handlers
+ handle = False
+ if sock:
+ logging.log(shell.VERBOSE_LEVEL, 'fd %d %s', fd,
+ eventloop.EVENT_NAMES.get(event, event))
+ if sock == self._server_socket:
+ if event & eventloop.POLL_ERR:
+ # TODO
+ raise Exception('server_socket error')
+ handler = None
+ handle = True
+ try:
+ logging.debug('accept')
+ conn = self._server_socket.accept()
+ handler = TCPRelayHandler(self, self._fd_to_handlers,
+ self._eventloop, conn[0], self._config,
+ self._dns_resolver, self._is_local)
+ if handler.stage() == STAGE_DESTROYED:
+ conn[0].close()
+ except (OSError, IOError) as e:
+ error_no = eventloop.errno_from_exception(e)
+ if error_no in (errno.EAGAIN, errno.EINPROGRESS,
+ errno.EWOULDBLOCK):
+ return
+ else:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ if handler:
+ handler.destroy()
+ else:
+ if sock:
+ handler = self._fd_to_handlers.get(fd, None)
+ if handler:
+ handle = handler.handle_event(sock, fd, event)
+ else:
+ logging.warn('unknown fd')
+ handle = True
+ try:
+ self._eventloop.removefd(fd)
+ except Exception as e:
+ shell.print_exception(e)
+ sock.close()
+ else:
+ logging.warn('poll removed fd')
+ handle = True
+ if fd in self._fd_to_handlers:
+ try:
+ del self._fd_to_handlers[fd]
+ except Exception as e:
+ shell.print_exception(e)
+ return handle
+
+ def handle_periodic(self):
+ if self._closed:
+ if self._server_socket:
+ self._eventloop.removefd(self._server_socket_fd)
+ self._server_socket.close()
+ self._server_socket = None
+ logging.info('closed TCP port %d', self._listen_port)
+ for handler in list(self._fd_to_handlers.values()):
+ handler.destroy()
+ self._sweep_timeout()
+
+ def close(self, next_tick=False):
+ logging.debug('TCP close')
+ self._closed = True
+ if not next_tick:
+ if self._eventloop:
+ self._eventloop.remove_periodic(self.handle_periodic)
+ self._eventloop.removefd(self._server_socket_fd)
+ self._server_socket.close()
+ for handler in list(self._fd_to_handlers.values()):
+ handler.destroy()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py
new file mode 100644
index 0000000000..b9606cd818
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py
@@ -0,0 +1,656 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# SOCKS5 UDP Request
+# +----+------+------+----------+----------+----------+
+# |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+# +----+------+------+----------+----------+----------+
+# | 2 | 1 | 1 | Variable | 2 | Variable |
+# +----+------+------+----------+----------+----------+
+
+# SOCKS5 UDP Response
+# +----+------+------+----------+----------+----------+
+# |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+# +----+------+------+----------+----------+----------+
+# | 2 | 1 | 1 | Variable | 2 | Variable |
+# +----+------+------+----------+----------+----------+
+
+# shadowsocks UDP Request (before encrypted)
+# +------+----------+----------+----------+
+# | ATYP | DST.ADDR | DST.PORT | DATA |
+# +------+----------+----------+----------+
+# | 1 | Variable | 2 | Variable |
+# +------+----------+----------+----------+
+
+# shadowsocks UDP Response (before encrypted)
+# +------+----------+----------+----------+
+# | ATYP | DST.ADDR | DST.PORT | DATA |
+# +------+----------+----------+----------+
+# | 1 | Variable | 2 | Variable |
+# +------+----------+----------+----------+
+
+# shadowsocks UDP Request and Response (after encrypted)
+# +-------+--------------+
+# | IV | PAYLOAD |
+# +-------+--------------+
+# | Fixed | Variable |
+# +-------+--------------+
+
+# HOW TO NAME THINGS
+# ------------------
+# `dest` means destination server, which is from DST fields in the SOCKS5
+# request
+# `local` means local server of shadowsocks
+# `remote` means remote server of shadowsocks
+# `client` means UDP clients that connects to other servers
+# `server` means the UDP server that handles user requests
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import time
+import socket
+import logging
+import struct
+import errno
+import random
+import binascii
+import traceback
+import threading
+
+from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell
+from shadowsocks.common import pre_parse_header, parse_header, pack_addr
+
+# for each handler, we have 2 stream directions:
+# upstream: from client to server direction
+# read local and write to remote
+# downstream: from server to client direction
+# read remote and write to local
+
+STREAM_UP = 0
+STREAM_DOWN = 1
+
+# for each stream, it's waiting for reading, or writing, or both
+WAIT_STATUS_INIT = 0
+WAIT_STATUS_READING = 1
+WAIT_STATUS_WRITING = 2
+WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
+
+BUF_SIZE = 65536
+DOUBLE_SEND_BEG_IDS = 16
+POST_MTU_MIN = 500
+POST_MTU_MAX = 1400
+SENDING_WINDOW_SIZE = 8192
+
+STAGE_INIT = 0
+STAGE_RSP_ID = 1
+STAGE_DNS = 2
+STAGE_CONNECTING = 3
+STAGE_STREAM = 4
+STAGE_DESTROYED = -1
+
+CMD_CONNECT = 0
+CMD_RSP_CONNECT = 1
+CMD_CONNECT_REMOTE = 2
+CMD_RSP_CONNECT_REMOTE = 3
+CMD_POST = 4
+CMD_SYN_STATUS = 5
+CMD_POST_64 = 6
+CMD_SYN_STATUS_64 = 7
+CMD_DISCONNECT = 8
+
+CMD_VER_STR = b"\x08"
+
+RSP_STATE_EMPTY = b""
+RSP_STATE_REJECT = b"\x00"
+RSP_STATE_CONNECTED = b"\x01"
+RSP_STATE_CONNECTEDREMOTE = b"\x02"
+RSP_STATE_ERROR = b"\x03"
+RSP_STATE_DISCONNECT = b"\x04"
+RSP_STATE_REDIRECT = b"\x05"
+
+def client_key(source_addr, server_af):
+ # notice this is server af, not dest af
+ return '%s:%s:%d' % (source_addr[0], source_addr[1], server_af)
+
+class UDPRelay(object):
+ def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None):
+ self._config = config
+ if config.get('connect_verbose_info', 0) > 0:
+ common.connect_log = logging.info
+ if is_local:
+ self._listen_addr = config['local_address']
+ self._listen_port = config['local_port']
+ self._remote_addr = config['server']
+ self._remote_port = config['server_port']
+ else:
+ self._listen_addr = config['server']
+ self._listen_port = config['server_port']
+ self._remote_addr = None
+ self._remote_port = None
+ self._dns_resolver = dns_resolver
+ self._password = common.to_bytes(config['password'])
+ self._method = config['method']
+ self._timeout = config['timeout']
+ self._is_local = is_local
+ self._udp_cache_size = config['udp_cache']
+ self._cache = lru_cache.LRUCache(timeout=config['udp_timeout'],
+ close_callback=self._close_client_pair)
+ self._cache_dns_client = lru_cache.LRUCache(timeout=10,
+ close_callback=self._close_client_pair)
+ self._client_fd_to_server_addr = {}
+ #self._dns_cache = lru_cache.LRUCache(timeout=1800)
+ self._eventloop = None
+ self._closed = False
+ self.server_transfer_ul = 0
+ self.server_transfer_dl = 0
+ self.server_users = {}
+ self.server_user_transfer_ul = {}
+ self.server_user_transfer_dl = {}
+
+ if common.to_bytes(config['protocol']) in obfs.mu_protocol():
+ self._update_users(None, None)
+
+ self.protocol_data = obfs.obfs(config['protocol']).init_data()
+ self._protocol = obfs.obfs(config['protocol'])
+ server_info = obfs.server_info(self.protocol_data)
+ server_info.host = self._listen_addr
+ server_info.port = self._listen_port
+ server_info.users = self.server_users
+ server_info.protocol_param = config['protocol_param']
+ server_info.obfs_param = ''
+ server_info.iv = b''
+ server_info.recv_iv = b''
+ server_info.key_str = common.to_bytes(config['password'])
+ server_info.key = encrypt.encrypt_key(self._password, self._method)
+ server_info.head_len = 30
+ server_info.tcp_mss = 1452
+ server_info.buffer_size = BUF_SIZE
+ server_info.overhead = 0
+ self._protocol.set_server_info(server_info)
+
+ self._sockets = set()
+ self._fd_to_handlers = {}
+ self._reqid_to_hd = {}
+ self._data_to_write_to_server_socket = []
+
+ self._timeout_cache = lru_cache.LRUCache(timeout=self._timeout,
+ close_callback=self._close_tcp_client)
+
+ self._bind = config.get('out_bind', '')
+ self._bindv6 = config.get('out_bindv6', '')
+ self._ignore_bind_list = config.get('ignore_bind', [])
+
+ if 'forbidden_ip' in config:
+ self._forbidden_iplist = config['forbidden_ip']
+ else:
+ self._forbidden_iplist = None
+ if 'forbidden_port' in config:
+ self._forbidden_portset = config['forbidden_port']
+ else:
+ self._forbidden_portset = None
+
+ addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0,
+ socket.SOCK_DGRAM, socket.SOL_UDP)
+ if len(addrs) == 0:
+ raise Exception("can't get addrinfo for %s:%d" %
+ (self._listen_addr, self._listen_port))
+ af, socktype, proto, canonname, sa = addrs[0]
+ server_socket = socket.socket(af, socktype, proto)
+ server_socket.bind((self._listen_addr, self._listen_port))
+ server_socket.setblocking(False)
+ self._server_socket = server_socket
+ self._stat_callback = stat_callback
+
+ def _get_a_server(self):
+ server = self._config['server']
+ server_port = self._config['server_port']
+ if type(server_port) == list:
+ server_port = random.choice(server_port)
+ if type(server) == list:
+ server = random.choice(server)
+ logging.debug('chosen server: %s:%d', server, server_port)
+ return server, server_port
+
+ def get_ud(self):
+ return (self.server_transfer_ul, self.server_transfer_dl)
+
+ def get_users_ud(self):
+ ret = (self.server_user_transfer_ul.copy(), self.server_user_transfer_dl.copy())
+ return ret
+
+ def _update_users(self, protocol_param, acl):
+ if protocol_param is None:
+ protocol_param = self._config['protocol_param']
+ param = common.to_bytes(protocol_param).split(b'#')
+ if len(param) == 2:
+ user_list = param[1].split(b',')
+ if user_list:
+ for user in user_list:
+ items = user.split(b':')
+ if len(items) == 2:
+ user_int_id = int(items[0])
+ uid = struct.pack(' header_length + 13 and data[header_length + 4 : header_length + 12] == b"\x00\x01\x00\x00\x00\x00\x00\x00":
+ is_dns = True
+ else:
+ pass
+ if sa[1] == 53 and is_dns: #DNS
+ logging.debug("DNS query %s from %s:%d" % (common.to_str(sa[0]), r_addr[0], r_addr[1]))
+ self._cache_dns_client[key] = (client, uid)
+ else:
+ self._cache[key] = (client, uid)
+ self._client_fd_to_server_addr[client.fileno()] = (r_addr, af)
+
+ self._sockets.add(client.fileno())
+ self._eventloop.add(client, eventloop.POLL_IN, self)
+
+ logging.debug('UDP port %5d sockets %d' % (self._listen_port, len(self._sockets)))
+
+ if uid is not None:
+ user_id = struct.unpack(' 255:
+ # drop
+ return
+ data = pack_addr(r_addr[0]) + struct.pack('>H', r_addr[1]) + data
+ ref_iv = [encrypt.encrypt_new_iv(self._method)]
+ self._protocol.obfs.server_info.iv = ref_iv[0]
+ data = self._protocol.server_udp_pre_encrypt(data, client_uid)
+ response = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1,
+ data, ref_iv)
+ if not response:
+ return
+ else:
+ ref_iv = [0]
+ data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 0,
+ data, ref_iv)
+ if not data:
+ return
+ self._protocol.obfs.server_info.recv_iv = ref_iv[0]
+ data = self._protocol.client_udp_post_decrypt(data)
+ header_result = parse_header(data)
+ if header_result is None:
+ return
+ #connecttype, dest_addr, dest_port, header_length = header_result
+ #logging.debug('UDP handle_client %s:%d to %s:%d' % (common.to_str(r_addr[0]), r_addr[1], dest_addr, dest_port))
+
+ response = b'\x00\x00\x00' + data
+
+ if client_addr:
+ if client_uid:
+ self.add_transfer_d(client_uid, len(response))
+ else:
+ self.server_transfer_dl += len(response)
+ self.write_to_server_socket(response, client_addr[0])
+ if client_dns_pair:
+ logging.debug("remove dns client %s:%d" % (client_addr[0][0], client_addr[0][1]))
+ del self._cache_dns_client[key]
+ self._close_client(client_dns_pair[0])
+ else:
+ # this packet is from somewhere else we know
+ # simply drop that packet
+ pass
+
+ def write_to_server_socket(self, data, addr):
+ uncomplete = False
+ retry = 0
+ try:
+ self._server_socket.sendto(data, addr)
+ data = None
+ while self._data_to_write_to_server_socket:
+ data_buf = self._data_to_write_to_server_socket[0]
+ retry = data_buf[1] + 1
+ del self._data_to_write_to_server_socket[0]
+ data, addr = data_buf[0]
+ self._server_socket.sendto(data, addr)
+ except (OSError, IOError) as e:
+ error_no = eventloop.errno_from_exception(e)
+ uncomplete = True
+ if error_no in (errno.EWOULDBLOCK,):
+ pass
+ else:
+ shell.print_exception(e)
+ return False
+ #if uncomplete and data is not None and retry < 3:
+ # self._data_to_write_to_server_socket.append([(data, addr), retry])
+ #'''
+
+ def add_to_loop(self, loop):
+ if self._eventloop:
+ raise Exception('already add to loop')
+ if self._closed:
+ raise Exception('already closed')
+ self._eventloop = loop
+
+ server_socket = self._server_socket
+ self._eventloop.add(server_socket,
+ eventloop.POLL_IN | eventloop.POLL_ERR, self)
+ loop.add_periodic(self.handle_periodic)
+
+ def remove_handler(self, client):
+ if hash(client) in self._timeout_cache:
+ del self._timeout_cache[hash(client)]
+
+ def update_activity(self, client):
+ self._timeout_cache[hash(client)] = client
+
+ def _sweep_timeout(self):
+ self._timeout_cache.sweep()
+
+ def _close_tcp_client(self, client):
+ if client.remote_address:
+ logging.debug('timed out: %s:%d' %
+ client.remote_address)
+ else:
+ logging.debug('timed out')
+ client.destroy()
+ client.destroy_local()
+
+ def handle_event(self, sock, fd, event):
+ if sock == self._server_socket:
+ if event & eventloop.POLL_ERR:
+ logging.error('UDP server_socket err')
+ try:
+ self._handle_server()
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ elif sock and (fd in self._sockets):
+ if event & eventloop.POLL_ERR:
+ logging.error('UDP client_socket err')
+ try:
+ self._handle_client(sock)
+ except Exception as e:
+ shell.print_exception(e)
+ if self._config['verbose']:
+ traceback.print_exc()
+ else:
+ if sock:
+ handler = self._fd_to_handlers.get(fd, None)
+ if handler:
+ handler.handle_event(sock, event)
+ else:
+ logging.warn('poll removed fd')
+
+ def handle_periodic(self):
+ if self._closed:
+ self._cache.clear(0)
+ self._cache_dns_client.clear(0)
+ if self._eventloop:
+ self._eventloop.remove_periodic(self.handle_periodic)
+ self._eventloop.remove(self._server_socket)
+ if self._server_socket:
+ self._server_socket.close()
+ self._server_socket = None
+ logging.info('closed UDP port %d', self._listen_port)
+ else:
+ before_sweep_size = len(self._sockets)
+ self._cache.sweep()
+ self._cache_dns_client.sweep()
+ if before_sweep_size != len(self._sockets):
+ logging.debug('UDP port %5d sockets %d' % (self._listen_port, len(self._sockets)))
+ self._sweep_timeout()
+
+ def close(self, next_tick=False):
+ logging.debug('UDP close')
+ self._closed = True
+ if not next_tick:
+ if self._eventloop:
+ self._eventloop.remove_periodic(self.handle_periodic)
+ self._eventloop.remove(self._server_socket)
+ self._server_socket.close()
+ self._cache.clear(0)
+ self._cache_dns_client.clear(0)
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py
new file mode 100644
index 0000000000..f3e1ef796d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2017 breakwa11
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+def version():
+ return '3.4.0 2017-07-27'
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh
new file mode 100755
index 0000000000..56567daa0c
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
+eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py
new file mode 100644
index 0000000000..6687e12cfb
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py
@@ -0,0 +1,8 @@
+def getKeys(key_list):
+ return key_list
+ #return key_list + ['plan'] # append the column name 'plan'
+
+def isTurnOn(row):
+ return True
+ #return row['plan'] == 'B' # then judge here
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh
new file mode 100755
index 0000000000..f36f605eed
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+cd `dirname $0`
+tail -f ssserver.log
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json
new file mode 100644
index 0000000000..40d0b2107e
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb1",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json
new file mode 100644
index 0000000000..fb7014b1ee
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb8",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json
new file mode 100644
index 0000000000..1fed8a8c77
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-ctr",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json
new file mode 100644
index 0000000000..a3d95b9b5f
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh
new file mode 100755
index 0000000000..b0c679cbcd
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh
@@ -0,0 +1,148 @@
+#!/bin/bash
+# assert.sh 1.0 - bash unit testing framework
+# Copyright (C) 2009, 2010, 2011, 2012 Robert Lehmann
+#
+# http://github.com/lehmannro/assert.sh
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+export DISCOVERONLY=${DISCOVERONLY:-}
+export DEBUG=${DEBUG:-}
+export STOP=${STOP:-}
+export INVARIANT=${INVARIANT:-}
+export CONTINUE=${CONTINUE:-}
+
+args="$(getopt -n "$0" -l \
+ verbose,help,stop,discover,invariant,continue vhxdic $*)" \
+|| exit -1
+for arg in $args; do
+ case "$arg" in
+ -h)
+ echo "$0 [-vxidc]" \
+ "[--verbose] [--stop] [--invariant] [--discover] [--continue]"
+ echo "`sed 's/./ /g' <<< "$0"` [-h] [--help]"
+ exit 0;;
+ --help)
+ cat < [stdin]
+ (( tests_ran++ )) || :
+ [[ -n "$DISCOVERONLY" ]] && return || true
+ # printf required for formatting
+ printf -v expected "x${2:-}" # x required to overwrite older results
+ result="$(eval 2>/dev/null $1 <<< ${3:-})" || true
+ # Note: $expected is already decorated
+ if [[ "x$result" == "$expected" ]]; then
+ [[ -n "$DEBUG" ]] && echo -n . || true
+ return
+ fi
+ result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<< "$result")"
+ [[ -z "$result" ]] && result="nothing" || result="\"$result\""
+ [[ -z "$2" ]] && expected="nothing" || expected="\"$2\""
+ _assert_fail "expected $expected${_indent}got $result" "$1" "$3"
+}
+
+assert_raises() {
+ # assert_raises [stdin]
+ (( tests_ran++ )) || :
+ [[ -n "$DISCOVERONLY" ]] && return || true
+ status=0
+ (eval $1 <<< ${3:-}) > /dev/null 2>&1 || status=$?
+ expected=${2:-0}
+ if [[ "$status" -eq "$expected" ]]; then
+ [[ -n "$DEBUG" ]] && echo -n . || true
+ return
+ fi
+ _assert_fail "program terminated with code $status instead of $expected" "$1" "$3"
+}
+
+_assert_fail() {
+ # _assert_fail
+ [[ -n "$DEBUG" ]] && echo -n X
+ report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1"
+ if [[ -n "$STOP" ]]; then
+ [[ -n "$DEBUG" ]] && echo
+ echo "$report"
+ exit 1
+ fi
+ tests_errors[$tests_failed]="$report"
+ (( tests_failed++ )) || :
+}
+
+_assert_reset
+: ${tests_suite_status:=0} # remember if any of the tests failed so far
+_assert_cleanup() {
+ local status=$?
+ # modify exit code if it's not already non-zero
+ [[ $status -eq 0 && -z $CONTINUE ]] && exit $tests_suite_status
+}
+trap _assert_cleanup EXIT
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json
new file mode 100644
index 0000000000..541a9beb51
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"salsa20_password",
+ "timeout":60,
+ "method":"chacha20",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json
new file mode 100644
index 0000000000..1823c2a758
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json
@@ -0,0 +1,10 @@
+{
+ "server":["127.0.0.1", "127.0.0.1"],
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py
new file mode 100644
index 0000000000..23cc8cd71d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+if __name__ == '__main__':
+ import tornado.ioloop
+ import tornado.web
+ import urllib
+
+ class MainHandler(tornado.web.RequestHandler):
+ def get(self, project):
+ try:
+ with open('/tmp/%s-coverage' % project, 'rb') as f:
+ coverage = f.read().strip()
+ n = int(coverage.strip('%'))
+ if n >= 80:
+ color = 'brightgreen'
+ else:
+ color = 'yellow'
+ self.redirect(('https://img.shields.io/badge/'
+ 'coverage-%s-%s.svg'
+ '?style=flat') %
+ (urllib.quote(coverage), color))
+ except IOError:
+ raise tornado.web.HTTPError(404)
+
+ application = tornado.web.Application([
+ (r"/([a-zA-Z0-9\-_]+)", MainHandler),
+ ])
+
+ if __name__ == "__main__":
+ application.listen(8888, address='127.0.0.1')
+ tornado.ioloop.IOLoop.instance().start()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json
new file mode 100644
index 0000000000..f3980b65e5
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"fastopen_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "fast_open":true
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json
new file mode 100644
index 0000000000..6c3cfaf8d5
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json
@@ -0,0 +1,10 @@
+{
+ "server":"::1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json
new file mode 100644
index 0000000000..d855f9c47b
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json
@@ -0,0 +1,10 @@
+{
+ "server":"::",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh
new file mode 100755
index 0000000000..ea5c1630b8
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+result=0
+
+function run_test {
+ printf '\e[0;36m'
+ echo "running test: $command $@"
+ printf '\e[0m'
+
+ $command "$@"
+ status=$?
+ if [ $status -ne 0 ]; then
+ printf '\e[0;31m'
+ echo "test failed: $command $@"
+ printf '\e[0m'
+ echo
+ result=1
+ else
+ printf '\e[0;32m'
+ echo OK
+ printf '\e[0m'
+ echo
+ fi
+ return 0
+}
+
+python --version
+coverage erase
+mkdir tmp
+run_test pep8 --ignore=E402 .
+run_test pyflakes .
+run_test coverage run tests/nose_plugin.py -v
+run_test python setup.py sdist
+run_test tests/test_daemon.sh
+run_test python tests/test.py --with-coverage -c tests/aes.json
+run_test python tests/test.py --with-coverage -c tests/aes-ctr.json
+run_test python tests/test.py --with-coverage -c tests/aes-cfb1.json
+run_test python tests/test.py --with-coverage -c tests/aes-cfb8.json
+run_test python tests/test.py --with-coverage -c tests/rc4-md5.json
+run_test python tests/test.py --with-coverage -c tests/salsa20.json
+run_test python tests/test.py --with-coverage -c tests/chacha20.json
+run_test python tests/test.py --with-coverage -c tests/table.json
+run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json
+run_test python tests/test.py --with-coverage -s tests/aes.json -c tests/client-multi-server-ip.json
+run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json
+run_test python tests/test.py --with-coverage -c tests/workers.json
+run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json
+run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv"
+run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1"
+run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1"
+
+# test if DNS works
+run_test python tests/test.py --with-coverage -c tests/aes.json --url="https://clients1.google.com/generate_204"
+
+# test localhost is in the forbidden list by default
+run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1"
+
+# test localhost is available when forbidden list is empty
+run_test python tests/test.py --with-coverage --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1"
+
+if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then
+ if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then
+ # we have to run it twice:
+ # the first time there's no syn cookie
+ # the second time there is syn cookie
+ run_test python tests/test.py --with-coverage -c tests/fastopen.json
+ run_test python tests/test.py --with-coverage -c tests/fastopen.json
+ fi
+fi
+
+run_test tests/test_large_file.sh
+run_test tests/test_udp_src.sh
+run_test tests/test_command.sh
+
+coverage combine && coverage report --include=shadowsocks/*
+rm -rf htmlcov
+rm -rf tmp
+coverage html --include=shadowsocks/*
+
+coverage report --include=shadowsocks/* | tail -n1 | rev | cut -d' ' -f 1 | rev > /tmp/shadowsocks-coverage
+
+exit $result
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh
new file mode 100755
index 0000000000..b0e35fa131
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [ ! -d libsodium-1.0.1 ]; then
+ wget https://github.com/jedisct1/libsodium/releases/download/1.0.1/libsodium-1.0.1.tar.gz || exit 1
+ tar xf libsodium-1.0.1.tar.gz || exit 1
+fi
+pushd libsodium-1.0.1
+./configure && make -j2 && make install || exit 1
+sudo ldconfig
+popd
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py
new file mode 100644
index 0000000000..86b1a86537
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import nose
+from nose.plugins.base import Plugin
+
+
+class ExtensionPlugin(Plugin):
+
+ name = "ExtensionPlugin"
+
+ def options(self, parser, env):
+ Plugin.options(self, parser, env)
+
+ def configure(self, options, config):
+ Plugin.configure(self, options, config)
+ self.enabled = True
+
+ def wantFile(self, file):
+ return file.endswith('.py')
+
+ def wantDirectory(self, directory):
+ return True
+
+ def wantModule(self, file):
+ return True
+
+
+if __name__ == '__main__':
+ nose.main(addplugins=[ExtensionPlugin()])
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json
new file mode 100644
index 0000000000..26ba0dfa76
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"aes_password",
+ "timeout":60,
+ "method":"rc4-md5",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json
new file mode 100644
index 0000000000..5ca6c45f52
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"salsa20_password",
+ "timeout":60,
+ "method":"salsa20-ctr",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json
new file mode 100644
index 0000000000..7e303800d8
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"salsa20_password",
+ "timeout":60,
+ "method":"salsa20",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json
new file mode 100644
index 0000000000..c822c98b9d
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json
@@ -0,0 +1,8 @@
+{
+ "server": "127.0.0.1",
+ "server_port": "8385",
+ "local_port": 1081,
+ "password": "foobar5",
+ "timeout": 60,
+ "method": "aes-256-cfb"
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json
new file mode 100644
index 0000000000..a2c0a80896
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json
@@ -0,0 +1,19 @@
+{
+ "server": "127.0.0.1",
+ "server_port": 8384,
+ "local_port": 1081,
+ "password": "foobar4",
+ "port_password": {
+ "8381": "foobar1",
+ "8382": "foobar2",
+ "8383": "foobar3",
+ "8384": "foobar4",
+ "8385": "foobar5",
+ "8386": "foobar6",
+ "8387": "foobar7",
+ "8388": "foobar8",
+ "8389": "foobar9"
+ },
+ "timeout": 60,
+ "method": "table"
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json
new file mode 100644
index 0000000000..b1407f0a17
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json
@@ -0,0 +1,17 @@
+{
+ "server": "127.0.0.1",
+ "local_port": 1081,
+ "port_password": {
+ "8381": "foobar1",
+ "8382": "foobar2",
+ "8383": "foobar3",
+ "8384": "foobar4",
+ "8385": "foobar5",
+ "8386": "foobar6",
+ "8387": "foobar7",
+ "8388": "foobar8",
+ "8389": "foobar9"
+ },
+ "timeout": 60,
+ "method": "aes-256-cfb"
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json
new file mode 100644
index 0000000000..5bdbcab641
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json
@@ -0,0 +1,8 @@
+{
+ "server": "127.0.0.1",
+ "server_port": [8384, 8345, 8346, 8347],
+ "local_port": 1081,
+ "password": "foobar4",
+ "timeout": 60,
+ "method": "aes-256-cfb"
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh
new file mode 100755
index 0000000000..1a5fa208bf
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+DEV=lo
+PORT=8388
+DELAY=100ms
+
+type tc 2> /dev/null && (
+ tc qdisc add dev $DEV root handle 1: htb
+ tc class add dev $DEV parent 1: classid 1:1 htb rate 2mbps
+ tc class add dev $DEV parent 1:1 classid 1:6 htb rate 2mbps ceil 1mbps prio 0
+ tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6
+
+ tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:6
+ tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:6
+
+ tc qdisc show dev lo
+)
+
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh
new file mode 100755
index 0000000000..8eff72df08
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [ ! -d dante-1.4.0 ]; then
+ wget http://www.inet.no/dante/files/dante-1.4.0.tar.gz || exit 1
+ tar xf dante-1.4.0.tar.gz || exit 1
+fi
+pushd dante-1.4.0
+./configure && make -j4 && make install || exit 1
+popd
+cp tests/socksify/socks.conf /etc/ || exit 1
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf
new file mode 100644
index 0000000000..13db772fd1
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf
@@ -0,0 +1,5 @@
+route {
+ from: 0.0.0.0/0 to: 0.0.0.0/0 via: 127.0.0.1 port = 1081
+ proxyprotocol: socks_v5
+ method: none
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json
new file mode 100644
index 0000000000..cca6ac27e4
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"table_password",
+ "timeout":60,
+ "method":"table",
+ "local_address":"127.0.0.1",
+ "fast_open":false
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py
new file mode 100644
index 0000000000..408340134e
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 clowwindy
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import sys
+import os
+import signal
+import select
+import time
+import argparse
+from subprocess import Popen, PIPE
+
+python = ['python']
+
+default_url = 'http://localhost/'
+
+parser = argparse.ArgumentParser(description='test Shadowsocks')
+parser.add_argument('-c', '--client-conf', type=str, default=None)
+parser.add_argument('-s', '--server-conf', type=str, default=None)
+parser.add_argument('-a', '--client-args', type=str, default=None)
+parser.add_argument('-b', '--server-args', type=str, default=None)
+parser.add_argument('--with-coverage', action='store_true', default=None)
+parser.add_argument('--should-fail', action='store_true', default=None)
+parser.add_argument('--tcp-only', action='store_true', default=None)
+parser.add_argument('--url', type=str, default=default_url)
+parser.add_argument('--dns', type=str, default='8.8.8.8')
+
+config = parser.parse_args()
+
+if config.with_coverage:
+ python = ['coverage', 'run', '-p']
+
+client_args = python + ['shadowsocks/local.py', '-v']
+server_args = python + ['shadowsocks/server.py', '-v']
+
+if config.client_conf:
+ client_args.extend(['-c', config.client_conf])
+ if config.server_conf:
+ server_args.extend(['-c', config.server_conf])
+ else:
+ server_args.extend(['-c', config.client_conf])
+if config.client_args:
+ client_args.extend(config.client_args.split())
+ if config.server_args:
+ server_args.extend(config.server_args.split())
+ else:
+ server_args.extend(config.client_args.split())
+if config.url == default_url:
+ server_args.extend(['--forbidden-ip', ''])
+
+p1 = Popen(server_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+p2 = Popen(client_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+p3 = None
+p4 = None
+p3_fin = False
+p4_fin = False
+
+# 1 shadowsocks started
+# 2 curl started
+# 3 curl finished
+# 4 dig started
+# 5 dig finished
+stage = 1
+
+try:
+ local_ready = False
+ server_ready = False
+ fdset = [p1.stdout, p2.stdout, p1.stderr, p2.stderr]
+ while True:
+ r, w, e = select.select(fdset, [], fdset)
+ if e:
+ break
+
+ for fd in r:
+ line = fd.readline()
+ if not line:
+ if stage == 2 and fd == p3.stdout:
+ stage = 3
+ if stage == 4 and fd == p4.stdout:
+ stage = 5
+ if bytes != str:
+ line = str(line, 'utf8')
+ sys.stderr.write(line)
+ if line.find('starting local') >= 0:
+ local_ready = True
+ if line.find('starting server') >= 0:
+ server_ready = True
+
+ if stage == 1:
+ time.sleep(2)
+
+ p3 = Popen(['curl', config.url, '-v', '-L',
+ '--socks5-hostname', '127.0.0.1:1081',
+ '-m', '15', '--connect-timeout', '10'],
+ stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+ if p3 is not None:
+ fdset.append(p3.stdout)
+ fdset.append(p3.stderr)
+ stage = 2
+ else:
+ sys.exit(1)
+
+ if stage == 3 and p3 is not None:
+ fdset.remove(p3.stdout)
+ fdset.remove(p3.stderr)
+ r = p3.wait()
+ if config.should_fail:
+ if r == 0:
+ sys.exit(1)
+ else:
+ if r != 0:
+ sys.exit(1)
+ if config.tcp_only:
+ break
+ p4 = Popen(['socksify', 'dig', '@%s' % config.dns,
+ 'www.google.com'],
+ stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+ if p4 is not None:
+ fdset.append(p4.stdout)
+ fdset.append(p4.stderr)
+ stage = 4
+ else:
+ sys.exit(1)
+
+ if stage == 5:
+ r = p4.wait()
+ if config.should_fail:
+ if r == 0:
+ sys.exit(1)
+ print('test passed (expecting failure)')
+ else:
+ if r != 0:
+ sys.exit(1)
+ print('test passed')
+ break
+finally:
+ for p in [p1, p2]:
+ try:
+ os.kill(p.pid, signal.SIGINT)
+ os.waitpid(p.pid, 0)
+ except OSError:
+ pass
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh
new file mode 100755
index 0000000000..a1a777b0d9
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+. tests/assert.sh
+
+PYTHON="coverage run -p"
+LOCAL="$PYTHON shadowsocks/local.py"
+SERVER="$PYTHON shadowsocks/server.py"
+
+assert "$LOCAL --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks"
+assert "$SERVER --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks"
+
+
+assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: server set to listen on 127.0.0.1:8388, are you sure?"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t10 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 10 seems too short"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t1000 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 1000 seems too long"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+assert "$LOCAL 2>&1 -m rc4 -k testrc4 -s 0.0.0.0 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: RC4 is not safe; please use a safer cipher, like AES-256-CFB"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+assert "$LOCAL 2>&1 -m rc4-md5 -k mypassword -s 0.0.0.0 -p 8388 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " DON'T USE DEFAULT PASSWORD! Please change it in your config.json!"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+
+assert "$SERVER 2>&1 --forbidden-ip 127.0.0.1/4a -m rc4-md5 -k 12345 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": Not a valid CIDR notation: 127.0.0.1/4a"
+$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
+
+assert_end command
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh
new file mode 100755
index 0000000000..7a192bdb11
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+function run_test {
+ expected=$1
+ shift
+ echo "running test: $command $@"
+ $command $@
+ status=$?
+ if [ $status -ne $expected ]; then
+ echo "exit $status != $expected"
+ exit 1
+ fi
+ echo "exit status $status == $expected"
+ echo OK
+ return
+}
+
+for module in local server
+do
+
+command="coverage run -p shadowsocks/$module.py"
+
+mkdir -p tmp
+
+run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 1 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 0 -c tests/aes.json -d restart --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+run_test 0 -c tests/aes.json -d restart --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+run_test 1 -c tests/aes.json -d start --pid-file tmp/not_exist/shadowsocks.pid --log-file tmp/shadowsocks.log
+
+done
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh
new file mode 100755
index 0000000000..7a61caff14
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+PYTHON="coverage run -p"
+URL=http://127.0.0.1/file
+
+mkdir -p tmp
+
+$PYTHON shadowsocks/local.py -c tests/aes.json &
+LOCAL=$!
+
+$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" &
+SERVER=$!
+
+sleep 3
+
+time curl -o tmp/expected $URL
+time curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL
+
+kill -s SIGINT $LOCAL
+kill -s SIGINT $SERVER
+
+sleep 2
+
+diff tmp/expected tmp/result || exit 1
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py
new file mode 100644
index 0000000000..e8fa5057eb
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+
+import socket
+import socks
+
+
+SERVER_IP = '127.0.0.1'
+SERVER_PORT = 1081
+
+
+if __name__ == '__main__':
+ # Test 1: same source port IPv4
+ sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_out.set_proxy(socks.SOCKS5, SERVER_IP, SERVER_PORT)
+ sock_out.bind(('127.0.0.1', 9000))
+
+ sock_in1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_in2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+
+ sock_in1.bind(('127.0.0.1', 9001))
+ sock_in2.bind(('127.0.0.1', 9002))
+
+ sock_out.sendto(b'data', ('127.0.0.1', 9001))
+ result1 = sock_in1.recvfrom(8)
+
+ sock_out.sendto(b'data', ('127.0.0.1', 9002))
+ result2 = sock_in2.recvfrom(8)
+
+ sock_out.close()
+ sock_in1.close()
+ sock_in2.close()
+
+ # make sure they're from the same source port
+ assert result1 == result2
+
+ # Test 2: same source port IPv6
+ # try again from the same port but IPv6
+ sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_out.set_proxy(socks.SOCKS5, SERVER_IP, SERVER_PORT)
+ sock_out.bind(('127.0.0.1', 9000))
+
+ sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_in2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+
+ sock_in1.bind(('::1', 9001))
+ sock_in2.bind(('::1', 9002))
+
+ sock_out.sendto(b'data', ('::1', 9001))
+ result1 = sock_in1.recvfrom(8)
+
+ sock_out.sendto(b'data', ('::1', 9002))
+ result2 = sock_in2.recvfrom(8)
+
+ sock_out.close()
+ sock_in1.close()
+ sock_in2.close()
+
+ # make sure they're from the same source port
+ assert result1 == result2
+
+ # Test 3: different source ports IPv6
+ sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_out.set_proxy(socks.SOCKS5, SERVER_IP, SERVER_PORT)
+ sock_out.bind(('127.0.0.1', 9003))
+
+ sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
+ socket.SOL_UDP)
+ sock_in1.bind(('::1', 9001))
+ sock_out.sendto(b'data', ('::1', 9001))
+ result3 = sock_in1.recvfrom(8)
+
+ # make sure they're from different source ports
+ assert result1 != result3
+
+ sock_out.close()
+ sock_in1.close()
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh
new file mode 100755
index 0000000000..6a778abc10
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+PYTHON="coverage run -p"
+
+mkdir -p tmp
+
+$PYTHON shadowsocks/local.py -c tests/aes.json -v &
+LOCAL=$!
+
+$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" -v &
+SERVER=$!
+
+sleep 3
+
+python tests/test_udp_src.py
+r=$?
+
+kill -s SIGINT $LOCAL
+kill -s SIGINT $SERVER
+
+sleep 2
+
+exit $r
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json
new file mode 100644
index 0000000000..2015ff6c6a
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json
@@ -0,0 +1,10 @@
+{
+ "server":"127.0.0.1",
+ "server_port":8388,
+ "local_port":1081,
+ "password":"workers_password",
+ "timeout":60,
+ "method":"aes-256-cfb",
+ "local_address":"127.0.0.1",
+ "workers": 4
+}
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md
new file mode 100644
index 0000000000..f624309c72
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md
@@ -0,0 +1,9 @@
+Useful Tools
+===========
+
+autoban.py
+----------
+
+Automatically ban IPs that try to brute force crack the server.
+
+See https://github.com/shadowsocks/shadowsocks/wiki/Ban-Brute-Force-Crackers
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py
new file mode 100644
index 0000000000..1bbb65c9b7
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 clowwindy
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import absolute_import, division, print_function, \
+ with_statement
+
+import os
+import sys
+import argparse
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='See README')
+ parser.add_argument('-c', '--count', default=3, type=int,
+ help='with how many failure times it should be '
+ 'considered as an attack')
+ config = parser.parse_args()
+ ips = {}
+ banned = set()
+ for line in sys.stdin:
+ if 'can not parse header when' in line:
+ ip = line.split()[-1].split(':')[0]
+ if ip not in ips:
+ ips[ip] = 1
+ print(ip)
+ sys.stdout.flush()
+ else:
+ ips[ip] += 1
+ if ip not in banned and ips[ip] >= config.count:
+ banned.add(ip)
+ cmd = 'iptables -A INPUT -s %s -j DROP' % ip
+ print(cmd, file=sys.stderr)
+ sys.stderr.flush()
+ os.system(cmd)
diff --git a/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf
new file mode 100644
index 0000000000..9b1c7ec7aa
--- /dev/null
+++ b/package/lean/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf
@@ -0,0 +1,5 @@
+[Definition]
+
+_daemon = shadowsocks
+
+failregex = ^\s+ERROR\s+can not parse header when handling connection from :\d+$
diff --git a/package/lean/luci-app-syncdial/Makefile b/package/lean/luci-app-syncdial/Makefile
new file mode 100644
index 0000000000..70da338617
--- /dev/null
+++ b/package/lean/luci-app-syncdial/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Virtual WAN config generator
+LUCI_DEPENDS:=+kmod-macvlan +luci-app-mwan3
+PKG_VERSION:=2.0
+PKG_RELEASE:=25
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+#Makefile for syncdial
diff --git a/package/lean/luci-app-syncdial/luasrc/controller/syncdial.lua b/package/lean/luci-app-syncdial/luasrc/controller/syncdial.lua
new file mode 100644
index 0000000000..9e6c707688
--- /dev/null
+++ b/package/lean/luci-app-syncdial/luasrc/controller/syncdial.lua
@@ -0,0 +1,14 @@
+module("luci.controller.syncdial",package.seeall)
+function index()
+if not nixio.fs.access("/etc/config/syncdial")then
+return
+end
+local e
+e=entry({"admin","network","syncdial"},cbi("syncdial"),_("多线多拨"),103)
+e.dependent=true
+e=entry({"admin","network","macvlan_redial"},call("redial"),nil)
+e.leaf=true
+end
+function redial()
+os.execute("killall -9 pppd")
+end
diff --git a/package/lean/luci-app-syncdial/luasrc/model/cbi/syncdial.lua b/package/lean/luci-app-syncdial/luasrc/model/cbi/syncdial.lua
new file mode 100644
index 0000000000..30c7f172c9
--- /dev/null
+++ b/package/lean/luci-app-syncdial/luasrc/model/cbi/syncdial.lua
@@ -0,0 +1,62 @@
+local e=require"nixio.fs"
+require("luci.tools.webadmin")
+local e="mwan3 status | grep -c \"is online and tracking is active\""
+local e=io.popen(e,"r")
+local t=e:read("*a")
+e:close()
+m=Map("syncdial",translate("多线多拨"),
+translate("使用macvlan驱动创建多个虚拟WAN口,支持并发多拨
当前在线接口数量:")..t)
+s=m:section(TypedSection,"syncdial",translate(" "))
+s.anonymous=true
+o=s:option(Flag,"enabled","启用")
+o.rmempty=false
+o=s:option(Flag,"syncon","启用并发多拨")
+o.rmempty=false
+o=s:option(ListValue,"dial_type",translate("多拨类型"))
+o:value("1",translate("单线多拨"))
+o:value("2",translate("双线多拨"))
+o.rmempty=false
+o=s:option(Value,"wanselect",translate("选择外网接口"),translate("指定要多拨的外网接口,如wan"))
+luci.tools.webadmin.cbi_add_networks(o)
+o.optional=false
+o.rmempty=false
+o=s:option(Value,"wannum","虚拟WAN接口数量")
+o.datatype="range(0,249)"
+o.optional=false
+o.default=1
+o=s:option(Flag,"bindwan","绑定物理接口")
+o.rmempty=false
+o=s:option(Value,"wanselect2",translate("选择第二个外网接口"),translate("指定要多拨的第二个外网接口,如wan2"))
+luci.tools.webadmin.cbi_add_networks(o)
+o.optional=false
+o:depends("dial_type","2")
+o=s:option(Value,"wannum2",translate("第二条线虚拟WAN接口数量"),translate("设置第二条线的拨号数"))
+o.datatype="range(0,249)"
+o.optional=false
+o.default=1
+o:depends("dial_type","2")
+o=s:option(Flag,"bindwan2","绑定物理接口","第二条线生成的虚拟接口绑定当前物理接口")
+o.rmempty=false
+o:depends("dial_type","2")
+o=s:option(Flag,"dialchk","启用掉线检测")
+o.rmempty=false
+o=s:option(Value,"dialnum","最低在线接口数量","如果在线接口数量小于这个值则重拨。")
+o.datatype="range(0,248)"
+o.optional=false
+o.default=2
+o=s:option(Value,"dialnum2","第二条线最低在线接口数量","如果第二条线在线接口数量小于这个值则重拨。")
+o.datatype="range(0,248)"
+o.optional=false
+o.default=2
+o:depends("dial_type","2")
+o=s:option(Value,"dialwait","重拨等待时间","重拨时,接口全部下线后下一次拨号前的等待时间。单位:秒 最小值:5秒")
+o.datatype="and(uinteger,min(5))"
+o.optional=false
+o=s:option(Flag,"old_frame","使用旧的macvlan创建方式")
+o.rmempty=false
+o=s:option(Flag,"nomwan","不自动配置MWAN3负载均衡","需要自定义负载均衡设置或者要使用策略路由的用户选择")
+o.rmempty=false
+o=s:option(DummyValue,"_redial","重新并发拨号")
+o.template="syncdial/redial_button"
+o.width="10%"
+return m
diff --git a/package/lean/luci-app-syncdial/luasrc/view/syncdial/redial_button.htm b/package/lean/luci-app-syncdial/luasrc/view/syncdial/redial_button.htm
new file mode 100644
index 0000000000..d1ba683d17
--- /dev/null
+++ b/package/lean/luci-app-syncdial/luasrc/view/syncdial/redial_button.htm
@@ -0,0 +1,17 @@
+<%+cbi/valueheader%>
+
+
+
+
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-syncdial/root/bin/genwancfg b/package/lean/luci-app-syncdial/root/bin/genwancfg
new file mode 100755
index 0000000000..6a058b3bca
--- /dev/null
+++ b/package/lean/luci-app-syncdial/root/bin/genwancfg
@@ -0,0 +1,278 @@
+#!/bin/sh
+#macvlan及PPPoE拨号接口配置批量自动生成脚本
+#Copyright (C) 2016
+. /lib/functions.sh
+
+#检测IP列表
+chk_ip_list="www.baidu.com 114.114.114.114 119.29.29.29"
+origfirewall=$(uci get firewall.@zone[1].network)
+backupdev=$(uci get syncdial.config.devbackup)
+norun=$(echo $origfirewall|grep vwan)
+nomwan=$(uci get syncdial.config.nomwan)
+wanselect=$(uci get syncdial.config.wanselect)
+wannum=$(uci get syncdial.config.wannum)
+oldframe=$(uci get syncdial.config.old_frame)
+bindwan=$(uci -q get syncdial.config.bindwan)
+
+dial_type=$(uci get syncdial.config.dial_type)
+[ "$dial_type" -eq 2 ] && {
+ wanselect2=$(uci get syncdial.config.wanselect2)
+ [ $? -ne 0 ] && {
+ logger -t Syncppp "You must select another pppoe interface ! 启用双线多拨必须同时选择第二个外网接口!"
+ return 0
+ }
+
+ wannum2=$(uci get syncdial.config.wannum2)
+ [ $? -ne 0 ] && {
+ logger -t Syncppp "When dualdial is enabled, the number of the second virtual WAN cannot be blank! 启用双线多拨时第二个虚拟WAN接口数不能为空!"
+ return 0
+ }
+ bindwan2=$(uci -q get syncdial.config.bindwan2)
+}
+
+#添加MWAN负载均衡相关配置
+#$1:接口名称
+mwan_cfg_add() {
+ #gen mwan3_interface
+ uci set mwan3.${1}=interface
+ uci set mwan3.${1}.enabled=1
+ uci set mwan3.${1}.count=2
+ uci set mwan3.${1}.timeout=2
+ uci set mwan3.${1}.interval=5
+ uci set mwan3.${1}.down=4
+ uci set mwan3.${1}.up=1
+ for i in $chk_ip_list
+ do
+ uci add_list mwan3.${1}.track_ip="$i"
+ done
+ uci set mwan3.${1}.reliability=1
+ uci set mwan3.${1}.initial_state=online
+ uci set mwan3.${1}.family=ipv4
+ uci set mwan3.${1}.track_method=ping
+ uci set mwan3.${1}.size=56
+ uci set mwan3.${1}.failure_interval=5
+ uci set mwan3.${1}.recovery_interval=5
+ uci set mwan3.${1}.flush_conntrack=never
+ #gen mwan3_member
+ uci set mwan3.${1}_m1_w1=member
+ uci set mwan3.${1}_m1_w1.interface=${1}
+ uci set mwan3.${1}_m1_w1.metric=1
+ uci set mwan3.${1}_m1_w1.weight=1
+ #gen mwan3_policy
+ uci add_list mwan3.balanced.use_member=${1}_m1_w1
+}
+
+#删除MWAN负载均衡相关配置
+#$1:接口名称
+mwan_cfg_del() {
+ uci del mwan3.${1}
+ uci del mwan3.${1}_m1_w1
+ uci del_list mwan3.balanced.use_member=${1}_m1_w1
+}
+
+#添加macvlan设备
+#$1:设虚拟备名称 $2:原始设备名称
+macvlan_dev_add() {
+ uci set network.macvlandev_${1}=device
+ uci set network.macvlandev_${1}.name=${1}
+ uci set network.macvlandev_${1}.ifname=${2}
+ uci set network.macvlandev_${1}.type=macvlan
+}
+
+#添加PPPoE接口
+#$1:接口名称 $2:设备名称 $3:账户 $4:密码 $5:网关跃点
+pppoe_if_add() {
+ #gen vwan macaddr
+ NEW_MACADDR=$(openssl rand -hex 6 | sed 's/\(..\)/\1:/g; s/.$//')
+ #gen wan if
+ uci set network.${1}=interface
+ uci set network.${1}.ifname=${2}
+ uci set network.${1}.proto=pppoe
+ uci set network.${1}.username=${3}
+ uci set network.${1}.password=${4}
+ uci set network.${1}.metric=${5}
+ uci set network.${1}.macaddr=$NEW_MACADDR
+ #gen firewall
+ uci add_list firewall.@zone[1].network=${1}
+}
+
+
+orig_firewall_add() {
+ need_del_rule=`uci -q get firewall.@zone[1].network | awk -F"'" '{print $2}'`
+ uci del_list firewall.@zone[1].network="$need_del_rule"
+ for k in $( seq 1 250 )
+ do
+ origdev=$(echo $origfirewall | cut -d " " -f$k)
+ if [ -z "$origdev" ]; then
+ break
+ fi
+ [ -z "$(uci get firewall.@zone[1].network | grep -w $origdev)" ] && uci add_list firewall.@zone[1].network=$origdev
+ done
+}
+
+apply_cfg() {
+ uci commit
+ #/etc/init.d/network restart &
+ logger -t Syncppp "Apply syncdial configuaration."
+ ifup wan &
+ killall pppconnectcheck
+}
+
+general_config_load() {
+ config_load 'syncdial'
+ config_get_bool enabled 'config' 'enabled'
+ config_get_bool old_frame 'config' 'old_frame'
+ congig_get_bool dial_type 'config' 'dial_type'
+
+ if [ "$enabled" -eq 0 ]; then
+ if [ "$old_frame" -eq 1 ]; then
+ mwan_cfg_add $wanselect
+ if [ "$dial_type" -eq 2 ]; then
+ mwan_cfg_add $wanselect2
+ fi
+ fi
+ echo "Disabled.Exit now."
+ apply_cfg
+ exit 1
+ fi
+
+ config_load 'network'
+ config_get pppoe_user $wanselect 'username'
+ config_get pppoe_password $wanselect 'password'
+ pppoe_ifname=$(uci get network.$wanselect.ifname)
+ [ "$dial_type" -eq 2 ] && {
+ config_get pppoe_user2 $wanselect2 'username'
+ config_get pppoe_password2 $wanselect2 'password'
+ pppoe_ifname2=$(uci get network.$wanselect2.ifname)
+ }
+
+}
+
+check_remove_device() {
+ local devcfg=${1}
+ [ ${devcfg::11} == 'macvlandev_' ] && uci del network.${devcfg}
+}
+
+check_remove_interface() {
+ local ifcfg=${1}
+ [ ${ifcfg::4} == 'vwan' ] && {
+ uci del network.${ifcfg}
+ uci del_list firewall.@zone[1].network=${ifcfg}
+ [ "$nomwan" -ne 1 ] && mwan_cfg_del ${ifcfg}
+ }
+ uci set firewall.@zone[1].network="$backupdev"
+}
+
+general_config_remove() {
+ config_load network
+ config_foreach check_remove_device 'device'
+ config_foreach check_remove_interface 'interface'
+ all_macvlans=`ip link show |grep macvlan | awk -F":" '{print $2}' | awk -F"@" '{print $1}'`
+ [ -n "$all_macvlans" ] && {
+ for macvlan in $all_macvlans
+ do
+ ip link delete $macvlan
+ done
+ }
+ [ "$(uci get network.$wanselect.proto)" == "none" ] && {
+ uci set network.$wanselect.proto=pppoe
+ }
+
+ if [ "$oldframe" -eq 0 ]; then
+ [ "$wanselect" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f1)" ] && \
+ [ "$wanselect" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f2)" ] && \
+ [ "$nomwan" -ne 1 ] && mwan_cfg_del $wanselect
+ else
+ [ "$nomwan" -ne 1 ] && mwan_cfg_del $wanselect
+ fi
+
+ [ "$dial_type" -eq 2 ] && {
+ [ $(uci get network.$wanselect2.proto) == "none" ] && {
+ uci set network.$wanselect2.proto=pppoe
+ }
+
+ if [ "$oldframe" -eq 0 ]; then
+ [ "$wanselect2" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f1)" ] && \
+ [ "$wanselect2" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f2)" ] && \
+ [ "$nomwan" -ne 1 ] && mwan_cfg_del $wanselect2
+ else
+ [ "$nomwan" -ne 1 ] && mwan_cfg_del $wanselect2
+ fi
+ }
+}
+
+
+[ -z "$norun" ] && uci set syncdial.config.devbackup="$origfirewall" && uci commit syncdial
+general_config_remove
+general_config_load
+
+uci set network.$wanselect.metric=40
+if [ "$wannum" -gt 0 ]; then
+ [ "$old_frame" -eq 1 ] && {
+ uci set network.$wanselect.proto=none
+ ifname=$(uci get network.$wanselect.ifname)
+ for i in $(seq 1 $wannum)
+ do
+ ip link add link $ifname name macvlan$i type macvlan
+ ifconfig macvlan$i hw ether $(echo $(cat /sys/class/net/$ifname/address|awk -F ":" '{print $1":"$2":"$3":"$4":"$5":" }')$(echo "" | awk -F ":" '{printf("%X\n", 16+i);}' i=$i))
+ ifconfig macvlan$i up
+ done
+ }
+ [ "$wanselect" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f1)" ] && \
+ [ "$wanselect" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f2)" ] && \
+ [ "$old_frame" -eq 0 -a "$nomwan" -ne 1 ] && mwan_cfg_add $wanselect
+
+
+ for i in $(seq 1 $wannum)
+ do
+ [ "$old_frame" -eq 0 ] && macvlan_dev_add macvlan$i $pppoe_ifname
+ if [ "$bindwan" != "" -a "$bindwan" == "1" ]; then
+ pppoe_if_add vwan$i $pppoe_ifname $pppoe_user $pppoe_password $((40+$i))
+ else
+ pppoe_if_add vwan$i macvlan$i $pppoe_user $pppoe_password $((40+$i))
+ fi
+ [ "$nomwan" -ne 1 ] && mwan_cfg_add vwan$i
+ done
+else
+ [ "$nomwan" -ne 1 ] && mwan_cfg_add $wanselect
+fi
+
+###dualdial configuration
+[ "$(uci -q get syncdial.config.dial_type)" = "2" ] && {
+
+ uci set network.$wanselect2.metric=60
+ if [ "$wannum2" -gt 0 ]; then
+ [ "$old_frame" -eq 1 ] && {
+ uci set network.$wanselect2.proto=none
+ ifname2=$(uci get network.$wanselect2.ifname)
+ for i in $(seq 1 $wannum2)
+ do
+ ip link add link $ifname2 name macvlan$(($wannum+$i)) type macvlan
+ ifconfig macvlan$(($wannum+$i)) hw ether $(echo $(cat /sys/class/net/$ifname2/address|awk -F ":" '{print $1":"$2":"$3":"$4":"$5":" }')$(echo "" | awk -F ":" '{printf("%X\n", 16+i);}' i=$i))
+ ifconfig macvlan$(($wannum+$i)) up
+ done
+ }
+ [ "$wanselect2" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f1)" ] && \
+ [ "$wanselect2" != "$(echo $(uci get syncdial.config.devbackup)| cut -d " " -f2)" ] && \
+ [ "$old_frame" -eq 0 -a "$nomwan" -ne 1 ] && mwan_cfg_add $wanselect2
+
+
+ for i in $(seq 1 $wannum2)
+ do
+ [ "$old_frame" -eq 0 ] && macvlan_dev_add macvlan$(($wannum+$i)) $pppoe_ifname2
+ if [ "$bindwan2" != "" -a "$bindwan2" == "1" ]; then
+ pppoe_if_add vwan$(($wannum+$i)) $pppoe_ifname2 $pppoe_user2 $pppoe_password2 $((60+$i))
+ else
+ pppoe_if_add vwan$(($wannum+$i)) macvlan$(($wannum+$i)) $pppoe_user2 $pppoe_password2 $((60+$i))
+ fi
+ [ "$nomwan" -ne 1 ] && mwan_cfg_add vwan$(($wannum+$i))
+ done
+ else
+ [ "$nomwan" -ne 1 ] && mwan_cfg_add $wanselect2
+ fi
+}
+
+orig_firewall_add
+apply_cfg
+
+return 0
diff --git a/package/lean/luci-app-syncdial/root/etc/config/syncdial b/package/lean/luci-app-syncdial/root/etc/config/syncdial
new file mode 100644
index 0000000000..5a39e586f8
--- /dev/null
+++ b/package/lean/luci-app-syncdial/root/etc/config/syncdial
@@ -0,0 +1,19 @@
+
+config syncdial 'config'
+ option syncon '1'
+ option dialwait '25'
+ option dialchk '1'
+ option nomwan '0'
+ option wanselect 'wan'
+ option dial_type '2'
+ option wannum '3'
+ option wanselect2 'wan2'
+ option wannum2 '2'
+ option dialnum '3'
+ option dialnum2 '2'
+ option old_frame '1'
+ option devbackup 'wan wan6 wan2'
+ option bindwan '0'
+ option bindwan2 '0'
+ option enabled '0'
+
diff --git a/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-dialcheck b/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-dialcheck
new file mode 100755
index 0000000000..716db654c5
--- /dev/null
+++ b/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-dialcheck
@@ -0,0 +1,36 @@
+#!/bin/sh
+[ "$ACTION" = "ifdown" ] && pppconnectcheck &
+
+wanselect=$(uci get syncdial.config.wanselect)
+[ "$(uci get syncdial.config.dial_type)" = "2" ] && {
+ wanselect2=$(uci get syncdial.config.wanselect2)
+}
+
+[ "$(uci get syncdial.config.enabled)" = "1" ] && \
+ [ "$(uci get syncdial.config.old_frame)" = "1" ] && \
+ [ "$DEVICE" = "$(uci get network.$wanselect.ifname)" ] && \
+ [ "$ACTION" = "ifup" ] && {
+ ifname=$(uci get network.$wanselect.ifname)
+ wannum=$(uci get syncdial.config.wannum)
+ for i in $(seq 1 $wannum)
+ do
+ [ -d /sys/class/net/macvlan$i ] || {
+ ip link add link $ifname name macvlan$i type macvlan
+ ifconfig macvlan$i hw ether $(echo $(cat /sys/class/net/$ifname/address|awk -F ":" '{print $1":"$2":"$3":"$4":"$5":" }')$(echo "" | awk -F ":" '{printf("%X\n", 16+i);}' i=$i))
+ ifconfig macvlan$i up
+ }
+ done
+
+ [ "$(uci get syncdial.config.dial_type)" = "2" ] && {
+ ifname2=$(uci get network.$wanselect2.ifname)
+ wannum2=$(uci get syncdial.config.wannum2)
+ for i in $(seq 1 $wannum2)
+ do
+ [ -d /sys/class/net/macvlan$(($wannum+$i)) ] || {
+ ip link add link $ifname2 name macvlan$(($wannum+$i)) type macvlan
+ ifconfig macvlan$(($wannum+$i)) hw ether $(echo $(cat /sys/class/net/$ifname2/address|awk -F ":" '{print $1":"$2":"$3":"$4":"$5":" }')$(echo "" | awk -F ":" '{printf("%X\n", 16+i);}' i=$i))
+ ifconfig macvlan$(($wannum+$i)) up
+ }
+ done
+ }
+}
diff --git a/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-mvifcreate b/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-mvifcreate
new file mode 100755
index 0000000000..426341fed9
--- /dev/null
+++ b/package/lean/luci-app-syncdial/root/etc/hotplug.d/iface/01-mvifcreate
@@ -0,0 +1,16 @@
+#!/bin/sh
+[ "$(uci get syncdial.config.enabled)" = "1" ] && \
+ [ "$(uci get syncdial.config.old_frame)" = "1" ] && \
+ [ "$DEVICE" = "$(uci get network.wan.ifname)" ] && \
+ [ "$ACTION" = "ifup" ] && {
+ ifname=$(uci get network.wan.ifname)
+ wannum=$(uci get syncdial.config.wannum)
+ for i in $(seq 1 $wannum)
+ do
+ [ -d /sys/class/net/macvlan$i ] || {
+ ip link add link $ifname name macvlan$i type macvlan
+ ifconfig macvlan$i hw ether $(echo $(cat /sys/class/net/$ifname/address|awk -F ":" '{print $1":"$2":"$3":"$4":"$5":" }')$(echo "" | awk -F ":" '{printf("%X\n", 16+i);}' i=$i))
+ ifconfig macvlan$i up
+ }
+ done
+}
diff --git a/package/lean/luci-app-syncdial/root/etc/uci-defaults/luci-syncdial b/package/lean/luci-app-syncdial/root/etc/uci-defaults/luci-syncdial
new file mode 100755
index 0000000000..5394164cca
--- /dev/null
+++ b/package/lean/luci-app-syncdial/root/etc/uci-defaults/luci-syncdial
@@ -0,0 +1,12 @@
+#!/bin/sh
+touch /etc/config/syncdial
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@syncdial[-1]
+ add ucitrack syncdial
+ set ucitrack.@syncdial[-1].exec='/bin/genwancfg'
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-ttyd/Makefile b/package/lean/luci-app-ttyd/Makefile
new file mode 100644
index 0000000000..cd54f30d29
--- /dev/null
+++ b/package/lean/luci-app-ttyd/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for ttyd
+LUCI_DEPENDS:=+ttyd
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-ttyd/luasrc/controller/terminal.lua b/package/lean/luci-app-ttyd/luasrc/controller/terminal.lua
new file mode 100644
index 0000000000..f4618f2da7
--- /dev/null
+++ b/package/lean/luci-app-ttyd/luasrc/controller/terminal.lua
@@ -0,0 +1,9 @@
+module("luci.controller.terminal", package.seeall)
+
+function index()
+ if not (luci.sys.call("pidof ttyd > /dev/null") == 0) then
+ return
+ end
+
+ entry({"admin", "system", "terminal"}, template("terminal"), _("TTYD Terminal"), 10).leaf = true
+end
diff --git a/package/lean/luci-app-ttyd/luasrc/view/terminal.htm b/package/lean/luci-app-ttyd/luasrc/view/terminal.htm
new file mode 100644
index 0000000000..784f86f4ff
--- /dev/null
+++ b/package/lean/luci-app-ttyd/luasrc/view/terminal.htm
@@ -0,0 +1,9 @@
+<%+header%>
+
+ <%=translate("TTYD Terminal")%>
+
+
+
+<%+footer%>
\ No newline at end of file
diff --git a/package/lean/luci-app-ttyd/po/zh-cn/terminal.po b/package/lean/luci-app-ttyd/po/zh-cn/terminal.po
new file mode 100644
index 0000000000..ed7b7523b3
--- /dev/null
+++ b/package/lean/luci-app-ttyd/po/zh-cn/terminal.po
@@ -0,0 +1,5 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "TTYD Terminal"
+msgstr "TTYD 终端"
diff --git a/package/lean/luci-app-ttyd/root/etc/init.d/ttyd b/package/lean/luci-app-ttyd/root/etc/init.d/ttyd
new file mode 100755
index 0000000000..87216650bf
--- /dev/null
+++ b/package/lean/luci-app-ttyd/root/etc/init.d/ttyd
@@ -0,0 +1,26 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+SERVICE_WRITE_PID=1
+SERVICE_DAEMONIZE=1
+
+start() {
+ logger -t TTYD 'Starting ttyd service'
+ service_start /usr/bin/ttyd -i br-lan /bin/login
+ return 0
+}
+
+stop() {
+ if [ -n "`pidof ttyd`" ]; then
+ logger -t TTYD 'Shutting down ttyd service'
+ service_stop /usr/bin/ttyd
+ fi
+ return 0
+}
+
+restart() {
+ logger -t TTYD 'Restarting ttyd service'
+ stop
+ sleep 2
+ start
+}
diff --git a/package/lean/luci-app-unblockmusic/Makefile b/package/lean/luci-app-unblockmusic/Makefile
new file mode 100644
index 0000000000..a50de73a3b
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/Makefile
@@ -0,0 +1,27 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+# licheng
+# www.maxlicheng.com
+# 2019-06-08
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-unblockmusic
+PKG_VERSION:=2.2.0
+PKG_RELEASE:=9
+
+PKG_LICENSE:=Apache-2.0
+
+LUCI_TITLE:=LuCI support for Unblock NeteaseCloudMusic
+LUCI_DEPENDS:=+UnblockNeteaseMusic +bash +dnsmasq-full +ipset
+LUCI_PKGARCH:=all
+
+PKG_MAINTAINER:=lean
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua b/package/lean/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua
new file mode 100644
index 0000000000..231f0bf212
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua
@@ -0,0 +1,22 @@
+
+module("luci.controller.unblockmusic", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/unblockmusic") then
+ return
+ end
+
+ entry({"admin", "services", "unblockmusic"},firstchild(), _("解锁网易云灰色歌曲"), 50).dependent = false
+
+ entry({"admin", "services", "unblockmusic", "general"},cbi("unblockmusic"), _("Base Setting"), 1)
+ entry({"admin", "services", "unblockmusic", "log"},form("unblockmusiclog"), _("Log"), 2)
+
+ entry({"admin", "services", "unblockmusic", "status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("busybox ps -w | grep app.js | grep -v grep >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic.lua b/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic.lua
new file mode 100644
index 0000000000..436b19a85e
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic.lua
@@ -0,0 +1,74 @@
+
+mp = Map("unblockmusic", translate("解锁网易云灰色歌曲"))
+mp.description = translate("采用 [QQ/虾米/百度/酷狗/酷我/咕咪/JOOX]等音源,替换网易云变灰歌曲链接")
+
+mp:section(SimpleSection).template = "unblockmusic/unblockmusic_status"
+
+s = mp:section(TypedSection, "unblockmusic")
+s.anonymous=true
+s.addremove=false
+
+enabled = s:option(Flag, "enabled", translate("启用解锁"))
+enabled.default = 0
+enabled.rmempty = false
+enabled.description = translate("启用后,路由器自动分流解锁,大部分设备无需设置代理。
苹果系列设备需要设置 WIFI/有线代理方式为 自动 ,并安装 CA根证书并信任。")
+
+speedtype = s:option(ListValue, "musicapptype", translate("音源选择"))
+speedtype:value("default", translate("默认"))
+speedtype:value("netease", translate("网易云音乐"))
+speedtype:value("qq", translate("QQ音乐"))
+speedtype:value("xiami", translate("虾米音乐"))
+speedtype:value("baidu", translate("百度音乐"))
+speedtype:value("kugou", translate("酷狗音乐"))
+speedtype:value("kuwo", translate("酷我音乐(高音质推荐)"))
+speedtype:value("migu", translate("咕咪音乐"))
+speedtype:value("joox", translate("JOOX音乐"))
+
+endpoint = s:option(Value, "endpoint", translate("转发HTTPS音源地址"))
+endpoint.default = "https://music.163.com"
+endpoint.rmempty = true
+endpoint.description = translate("默认为 https://music.163.com")
+
+o = s:option(Button,"certificate",translate("HTTPS 证书"))
+o.inputtitle = translate("下载 CA 根证书")
+o.description = translate("iOS 13 系统需要在“设置 -> 通用 -> 关于本机 -> 证书信任设置” 中,信任 UnblockNeteaseMusic Root CA )")
+o.inputstyle = "apply"
+o.write = function()
+ Download()
+end
+
+function Download()
+ local t,e
+ t=nixio.open("/usr/share/UnblockNeteaseMusic/ca.crt","r")
+ luci.http.header('Content-Disposition','attachment; filename="ca.crt"')
+ luci.http.prepare_content("application/octet-stream")
+ while true do
+ e=t:read(nixio.const.buffersize)
+ if(not e)or(#e==0)then
+ break
+ else
+ luci.http.write(e)
+ end
+ end
+ t:close()
+ luci.http.close()
+end
+
+o = s:option(Flag, "autoupdate")
+o.title = translate("自动检查更新主程序")
+o.default = 0
+o.rmempty = false
+o.description = translate("每天自动检测并更新到最新版本")
+
+local ver = luci.sys.exec("cat /usr/share/UnblockNeteaseMusic/core_ver")
+
+o = s:option(Button, "restart",translate("手动更新"))
+o.inputtitle = translate("更新核心版本")
+o.description = string.format(translate("目前运行主程序版本") .. ": %s ", ver)
+o.inputstyle = "reload"
+o.write = function()
+ luci.sys.exec("bash /usr/share/UnblockNeteaseMusic/update_core.sh 2>&1")
+ luci.http.redirect(luci.dispatcher.build_url("admin", "services", "unblockmusic"))
+end
+
+return mp
diff --git a/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusiclog.lua b/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusiclog.lua
new file mode 100644
index 0000000000..1be151743c
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/luasrc/model/cbi/unblockmusiclog.lua
@@ -0,0 +1,16 @@
+local fs = require "nixio.fs"
+local conffile = "/tmp/unblockmusic.log"
+local conffile = "/tmp/music.log"
+
+f = SimpleForm("logview")
+
+t = f:field(TextValue, "conf")
+t.rmempty = true
+t.rows = 20
+function t.cfgvalue()
+ luci.sys.exec("grep -B 1 'http' /tmp/unblockmusic.log > /tmp/music.log")
+ return fs.readfile(conffile) or ""
+end
+t.readonly="readonly"
+
+return f
diff --git a/package/lean/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm b/package/lean/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm
new file mode 100644
index 0000000000..74dd928129
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-unblockmusic/root/etc/config/unblockmusic b/package/lean/luci-app-unblockmusic/root/etc/config/unblockmusic
new file mode 100644
index 0000000000..1102183f6f
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/etc/config/unblockmusic
@@ -0,0 +1,10 @@
+
+config unblockmusic
+ option enabled '0'
+ option musicapptype 'default'
+ option port '5200'
+ option enable_ipset '1'
+ option endpoint 'https://music.163.com'
+ option autoupdate '1'
+
+
diff --git a/package/lean/luci-app-unblockmusic/root/etc/init.d/unblockmusic b/package/lean/luci-app-unblockmusic/root/etc/init.d/unblockmusic
new file mode 100755
index 0000000000..5be5239562
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/etc/init.d/unblockmusic
@@ -0,0 +1,110 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+STOP=10
+
+enable=$(uci get unblockmusic.@unblockmusic[0].enabled)
+TYPE=$(uci get unblockmusic.@unblockmusic[0].musicapptype)
+ROUTE_IP=$(uci get network.lan.ipaddr)
+ENDPOINT=$(uci get unblockmusic.@unblockmusic[0].endpoint)
+AUTOUPDATE=$(uci get unblockmusic.@unblockmusic[0].autoupdate)
+
+CRON_FILE=/etc/crontabs/root
+
+ipt_n="iptables -t nat"
+
+add_rule()
+{
+ ipset -! -N music hash:ip
+ $ipt_n -N cloud_music
+ $ipt_n -A cloud_music -d 0.0.0.0/8 -j RETURN
+ $ipt_n -A cloud_music -d 10.0.0.0/8 -j RETURN
+ $ipt_n -A cloud_music -d 127.0.0.0/8 -j RETURN
+ $ipt_n -A cloud_music -d 169.254.0.0/16 -j RETURN
+ $ipt_n -A cloud_music -d 172.16.0.0/12 -j RETURN
+ $ipt_n -A cloud_music -d 192.168.0.0/16 -j RETURN
+ $ipt_n -A cloud_music -d 224.0.0.0/4 -j RETURN
+ $ipt_n -A cloud_music -d 240.0.0.0/4 -j RETURN
+ $ipt_n -A cloud_music -p tcp -j REDIRECT --to-ports 5202
+ $ipt_n -I PREROUTING -p tcp --dport 80 -m set --match-set music dst -j cloud_music
+}
+
+del_rule(){
+ $ipt_n -D PREROUTING -p tcp --dport 80 -m set --match-set music dst -j cloud_music 2>/dev/null
+ $ipt_n -F cloud_music 2>/dev/null
+ $ipt_n -X cloud_music 2>/dev/null
+
+ rm -f /tmp/dnsmasq.d/dnsmasq-163.conf
+ /etc/init.d/dnsmasq restart >/dev/null 2>&1
+}
+
+set_firewall(){
+ rm -f /tmp/dnsmasq.d/dnsmasq-163.conf
+ mkdir -p /tmp/dnsmasq.d
+ echo "dhcp-option=252,http://$ROUTE_IP:5200/proxy.pac" > /tmp/dnsmasq.d/dnsmasq-163.conf
+ echo "ipset=/music.163.com/music" >> /tmp/dnsmasq.d/dnsmasq-163.conf
+ echo "ipset=/interface.music.163.com/music" >> /tmp/dnsmasq.d/dnsmasq-163.conf
+ /etc/init.d/dnsmasq restart >/dev/null 2>&1
+
+ add_rule
+
+ mkdir -p /var/etc
+ cat > "/var/etc/unblockmusic.include" <<-EOF
+/etc/init.d/unblockmusic restart
+ EOF
+}
+
+add_cron()
+{
+ if [ $AUTOUPDATE -eq 1 ]; then
+ sed -i '/update_core.sh/d' $CRON_FILE
+ echo '0 2 * * * /usr/share/UnblockNeteaseMusic/update_core.sh 2>&1' >> $CRON_FILE
+ crontab $CRON_FILE
+ fi
+}
+
+del_cron()
+{
+ sed -i '/update_core.sh/d' $CRON_FILE
+ /etc/init.d/cron restart
+}
+
+start()
+{
+ stop
+
+ [ $enable -eq "0" ] && exit 0
+
+ endponintset="";
+
+ if [ -n "$ENDPOINT" ]; then
+ endponintset="-e ${ENDPOINT}"
+ fi
+
+ if [ $TYPE = "default" ]; then
+ node /usr/share/UnblockNeteaseMusic/app.js $endponintset -p 5200:5201 >/tmp/unblockmusic.log 2>&1 &
+ node /usr/share/UnblockNeteaseMusic/app.js -p 5202 >>/tmp/unblockmusic.log 2>&1 &
+ else
+ node /usr/share/UnblockNeteaseMusic/app.js $endponintset -p 5200:5201 -o $TYPE >/tmp/unblockmusic.log 2>&1 &
+ node /usr/share/UnblockNeteaseMusic/app.js -p 5202 -o $TYPE >>/tmp/unblockmusic.log 2>&1 &
+ fi
+
+ set_firewall
+ add_cron
+
+ /usr/share/UnblockNeteaseMusic/logcheck.sh >/dev/null 2>&1 &
+}
+
+stop()
+{
+ kill -9 $(busybox ps -w | grep monitor | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+ kill -9 $(busybox ps -w | grep app.js | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+ kill -9 $(busybox ps -w | grep logcheck.sh | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+ rm -f /tmp/unblockmusic.log
+
+ del_rule
+ del_cron
+}
+
+
+
diff --git a/package/lean/luci-app-unblockmusic/root/etc/ppp/ip-up.d/unblockmusic.sh b/package/lean/luci-app-unblockmusic/root/etc/ppp/ip-up.d/unblockmusic.sh
new file mode 100755
index 0000000000..e8844f63ad
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/etc/ppp/ip-up.d/unblockmusic.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+sleep 10
+wget http://httpdns.n.netease.com/httpdns/v2/d?domain=music.163.com,interface.music.163.com,interface3.music.163.com,apm.music.163.com,apm3.music.163.com,clientlog.music.163.com,clientlog3.music.163.com -O- | grep -Eo '[0-9]+?\.[0-9]+?\.[0-9]+?\.[0-9]+?' | sort | uniq | awk '{print "ipset -! add music "$1}' | sh
diff --git a/package/lean/luci-app-unblockmusic/root/etc/uci-defaults/unblockmusic b/package/lean/luci-app-unblockmusic/root/etc/uci-defaults/unblockmusic
new file mode 100755
index 0000000000..38a16d6958
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/etc/uci-defaults/unblockmusic
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@unblockmusic[-1]
+ add ucitrack unblockmusic
+ set ucitrack.@unblockmusic[-1].init=unblockmusic
+ commit ucitrack
+ delete firewall.unblockmusic
+ set firewall.unblockmusic=include
+ set firewall.unblockmusic.type=script
+ set firewall.unblockmusic.path=/var/etc/unblockmusic.include
+ set firewall.unblockmusic.reload=1
+ commit firewall
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh b/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh
new file mode 100755
index 0000000000..168020537d
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+log_max_size="10" #使用KB计算
+log_file="/tmp/unblockmusic.log"
+
+while true
+do
+ sleep 10s
+ icount=`busybox ps -w | grep app.js |grep -v grep| wc -l`
+ if [ $icount -ne 2 ] ;then
+ /etc/init.d/unblockmusic restart
+ fi
+ (( log_size = "$(ls -l "${log_file}" | awk -F ' ' '{print $5}')" / "1024" ))
+ (( "${log_size}" >= "${log_max_size}" )) && echo "" > /tmp/unblockmusic.log
+ sleep 10m
+done
+
diff --git a/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh b/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh
new file mode 100755
index 0000000000..51705698b8
--- /dev/null
+++ b/package/lean/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh
@@ -0,0 +1,64 @@
+
+
+function check_if_already_running(){
+ running_tasks="$(ps |grep "unblockneteasemusic" |grep "update_core" |grep -v "grep" |awk '{print $1}' |wc -l)"
+ [ "${running_tasks}" -gt "2" ] && echo -e "\nA task is already running." >>/tmp/unblockmusic_update.log && exit 2
+}
+
+function clean_log(){
+ echo "" > /tmp/unblockmusic_update.log
+}
+
+function check_latest_version(){
+ latest_ver="$(wget -O- https://github.com/nondanee/UnblockNeteaseMusic/commits/master |tr -d '\n' |grep -Eo 'commit\/[0-9a-z]+' |sed -n 1p |sed 's#commit/##g')"
+ [ -z "${latest_ver}" ] && echo -e "\nFailed to check latest version, please try again later." >>/tmp/unblockmusic_update.log && exit 1
+ if [ ! -e "/usr/share/UnblockNeteaseMusic/local_ver" ]; then
+ clean_log
+ echo -e "Local version: NOT FOUND, cloud version: ${latest_ver}." >>/tmp/unblockmusic_update.log
+ update_core
+ else
+ if [ "$(cat /usr/share/UnblockNeteaseMusic/local_ver)" != "${latest_ver}" ]; then
+ clean_log
+ echo -e "Local version: $(cat /usr/share/UnblockNeteaseMusic/local_ver 2>/dev/null), cloud version: ${latest_ver}." >>/tmp/unblockmusic_update.log
+ update_core
+ else
+ echo -e "\nLocal version: $(cat /usr/share/UnblockNeteaseMusic/local_ver 2>/dev/null), cloud version: ${latest_ver}." >>/tmp/unblockmusic_update.log
+ echo -e "You're already using the latest version." >>/tmp/unblockmusic_update.log
+ exit 3
+ fi
+ fi
+}
+
+function update_core(){
+ echo -e "Updating core..." >>/tmp/unblockmusic_update.log
+
+ mkdir -p "/tmp/unblockneteasemusic/core" >/dev/null 2>&1
+ rm -rf /tmp/unblockneteasemusic/core/* >/dev/null 2>&1
+
+ wget-ssl --no-check-certificate -t 1 -T 10 -O /tmp/unblockneteasemusic/core/core.tar.gz "https://github.com/nondanee/UnblockNeteaseMusic/archive/master.tar.gz" >/dev/null 2>&1
+ tar -zxf "/tmp/unblockneteasemusic/core/core.tar.gz" -C "/tmp/unblockneteasemusic/core/" >/dev/null 2>&1
+ rm -f /tmp/unblockneteasemusic/core/UnblockNeteaseMusic-master/ca.crt /tmp/unblockneteasemusic/core/UnblockNeteaseMusic-master/server.crt /tmp/unblockneteasemusic/core/UnblockNeteaseMusic-master/server.key
+ cp -a /tmp/unblockneteasemusic/core/UnblockNeteaseMusic-master/* "/usr/share/UnblockNeteaseMusic/"
+ rm -rf "/tmp/unblockneteasemusic" >/dev/null 2>&1
+
+ if [ ! -e "/usr/share/UnblockNeteaseMusic/app.js" ]; then
+ echo -e "Failed to download core." >>/tmp/unblockmusic_update.log
+ exit 1
+ else
+ [ "${luci_update}" == "y" ] && touch "/usr/share/unblockneteasemusic/update_successfully"
+ echo -e "${latest_ver}" > /usr/share/UnblockNeteaseMusic/local_ver
+ /etc/init.d/unblockmusic restart
+ fi
+
+ echo -e "Succeeded in updating core." >/tmp/unblockmusic_update.log
+ echo -e "Local version: $(cat /usr/share/UnblockNeteaseMusic/local_ver 2>/dev/null), cloud version: ${latest_ver}.\n" >>/tmp/unblockmusic_update.log
+ node /usr/share/UnblockNeteaseMusic/app.js -v > /usr/share/UnblockNeteaseMusic/core_ver
+}
+
+function main(){
+ check_if_already_running
+ check_latest_version
+}
+
+ [ "$1" == "luci_update" ] && luci_update="y"
+ main
diff --git a/package/lean/luci-app-usb-printer/Makefile b/package/lean/luci-app-usb-printer/Makefile
new file mode 100644
index 0000000000..b213376810
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=USB Printer Share via TCP/IP
+LUCI_DEPENDS:=+p910nd +kmod-usb-printer
+PKG_VERSION:=1.0
+PKG_RELEASE:=2
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+#applications/luci-app-usb-printer/
+#applications/luci-app-usb-printer/
diff --git a/package/lean/luci-app-usb-printer/ipkg/postinst b/package/lean/luci-app-usb-printer/ipkg/postinst
new file mode 100755
index 0000000000..97a343c7b1
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/ipkg/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+[ -n "${IPKG_INSTROOT}" ] || {
+ ( . /etc/uci-defaults/luci-usb-printer ) && rm -f /etc/uci-defaults/luci-usb-printer
+ exit 0
+}
+
diff --git a/package/lean/luci-app-usb-printer/luasrc/controller/usb_printer.lua b/package/lean/luci-app-usb-printer/luasrc/controller/usb_printer.lua
new file mode 100644
index 0000000000..ebcee4f79a
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/luasrc/controller/usb_printer.lua
@@ -0,0 +1,29 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+require("luci.sys")
+
+module("luci.controller.usb_printer", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/usb_printer") then
+ return
+ end
+
+ entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
+
+ local page
+
+ page = entry({"admin", "nas", "usb_printer"}, cbi("usb_printer"), _("USB Printer Server"), 50)
+end
diff --git a/package/lean/luci-app-usb-printer/luasrc/model/cbi/usb_printer.lua b/package/lean/luci-app-usb-printer/luasrc/model/cbi/usb_printer.lua
new file mode 100644
index 0000000000..3cc7526ef9
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/luasrc/model/cbi/usb_printer.lua
@@ -0,0 +1,130 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth
+Copyright 2005-2013 hackpascal
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+require "luci.util"
+local uci = luci.model.uci.cursor_state()
+local net = require "luci.model.network"
+
+m = Map("usb_printer", translate("USB Printer Server"),
+ translate("Shares multiple USB printers via TCP/IP.
When modified bingings, re-plug usb connectors to take effect.
This module requires kmod-usb-printer."))
+
+function hex_align(hex, num)
+ local len = num - string.len(hex)
+
+ return string.rep("0", len) .. hex
+end
+
+function detect_usb_printers()
+ local data = {}
+
+ local lps = luci.util.execi("/usr/bin/detectlp")
+
+ for value in lps do
+ local row = {}
+
+ --[[
+ detectlp 的输出格式:
+ 设备名,VID/PID/?,描述,型号
+ ]]--
+
+ local pos = string.find(value, ",")
+
+ local devname = string.sub(value, 1, pos - 1)
+
+ local value = string.sub(value, pos + 1, string.len(value))
+
+ pos = string.find(value, ",")
+ local product = string.sub(value, 1, pos - 1)
+
+ value = string.sub(value, pos + 1, string.len(value))
+
+ pos = string.find(value, ",")
+ local model = string.sub(value, 1, pos - 1)
+
+ local name = string.sub(value, pos + 1, string.len(value))
+
+ pos = string.find(product, "/");
+
+ local vid = string.sub(product, 1, pos - 1)
+
+ local pid = string.sub(product, pos + 1, string.len(product))
+
+ pos = string.find(pid, "/")
+ pid = string.sub(pid, 1, pos - 1)
+
+ row["description"] = name
+ row["model"] = model
+ row["id"] = hex_align(vid, 4) .. ":" .. hex_align(pid, 4)
+ row["name"] = devname
+ row["product"] = product
+
+ table.insert(data, row)
+ end
+
+ return data
+end
+
+local printers = detect_usb_printers()
+
+v = m:section(Table, printers, translate("Detected printers"))
+
+v:option(DummyValue, "description", translate("Description"))
+v:option(DummyValue, "model", translate("Printer Model"))
+v:option(DummyValue, "id", translate("VID/PID"))
+v:option(DummyValue, "name", translate("Device Name"))
+
+net = net.init(m.uci)
+
+s = m:section(TypedSection, "printer", translate("Bindings"))
+s.addremove = true
+s.anonymous = true
+
+s:option(Flag, "enabled", translate("enable"))
+
+d = s:option(Value, "device", translate("Device"))
+d.rmempty = true
+
+for key, item in ipairs(printers) do
+ d:value(item["product"], item["description"] .. " [" .. item["id"] .. "]")
+end
+
+b = s:option(Value, "bind", translate("Interface"), translate("Specifies the interface to listen on."))
+b.template = "cbi/network_netlist"
+b.nocreate = true
+b.unspecified = true
+
+function b.cfgvalue(...)
+ local v = Value.cfgvalue(...)
+ if v then
+ return (net:get_status_by_address(v))
+ end
+end
+
+function b.write(self, section, value)
+ local n = net:get_network(value)
+ if n and n:ipaddr() then
+ Value.write(self, section, n:ipaddr())
+ end
+end
+
+p = s:option(ListValue, "port", translate("Port"), translate("TCP listener port."))
+p.rmempty = true
+for i = 0, 9 do
+ p:value(i, 9100 + i)
+end
+
+s:option(Flag, "bidirectional", translate("Bidirectional mode"))
+
+return m
diff --git a/package/lean/luci-app-usb-printer/po/zh-cn/usb-printer.po b/package/lean/luci-app-usb-printer/po/zh-cn/usb-printer.po
new file mode 100644
index 0000000000..05ce0d1eea
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/po/zh-cn/usb-printer.po
@@ -0,0 +1,65 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-05-18 01:34+0800\n"
+"PO-Revision-Date: 2014-05-18 01:34+0800\n"
+"Last-Translator: hackpascal \n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Pootle 2.0.6\n"
+
+msgid "Bidirectional mode"
+msgstr "双向模式"
+
+msgid "Bindings"
+msgstr "绑定"
+
+msgid "Device"
+msgstr "设备"
+
+msgid "Device Name"
+msgstr "设备名"
+
+msgid "Detected printers"
+msgstr "检测到的打印机"
+
+msgid ""
+"Shares multiple USB printers via TCP/IP.
"
+"When modified bingings, re-plug usb connectors to take effect.
"
+"This module requires kmod-usb-printer."
+msgstr ""
+"通过 TCP/IP 共享 USB 打印机。
修改设置后,请重新连接打印机以使设置生效。
"
+"此模块需要 kmod-usb-printer 支持。"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "Printer Model"
+msgstr "打印机型号"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "TCP listener port."
+msgstr "TCP 监听端口。"
+
+msgid "enable"
+msgstr "启用"
+
+msgid "USB Printer Server"
+msgstr "USB 打印服务器"
+
+msgid "Specifies the interface to listen on."
+msgstr "指定要监听的接口。"
+
+msgid "NAS"
+msgstr "网络存储"
+
+msgid "Architecture"
+msgstr "架构"
+
diff --git a/package/lean/luci-app-usb-printer/root/etc/config/usb_printer b/package/lean/luci-app-usb-printer/root/etc/config/usb_printer
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/package/lean/luci-app-usb-printer/root/etc/hotplug.d/usb/10-usb_printer b/package/lean/luci-app-usb-printer/root/etc/hotplug.d/usb/10-usb_printer
new file mode 100755
index 0000000000..c250fe78fe
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/root/etc/hotplug.d/usb/10-usb_printer
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Copyright (C) 2005-2014 NowRush Studio
+# Author: hackpascal
+
+if [ x"$INTERFACE" = x"7/1/1" ] || [ x"$INTERFACE" = x"7/1/2" ]; then
+ /usr/bin/usb_printer_hotplug "$PRODUCT" "$ACTION"
+fi
diff --git a/package/lean/luci-app-usb-printer/root/etc/init.d/usb_printer b/package/lean/luci-app-usb-printer/root/etc/init.d/usb_printer
new file mode 100755
index 0000000000..4da5e34456
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/root/etc/init.d/usb_printer
@@ -0,0 +1,22 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2005-2013 NowRush Studio
+# Author: hackpascal
+
+START=70
+
+stop() {
+ killall p910nd 2>/dev/null
+}
+
+start() {
+ for lps in `/usr/bin/detectlp`; do
+ product=`echo $lps | cut -d , -f 2`
+
+ /usr/bin/usb_printer_hotplug "$product" add
+ done
+}
+
+restart() {
+ stop
+ start
+}
diff --git a/package/lean/luci-app-usb-printer/root/etc/uci-defaults/luci-usb-printer b/package/lean/luci-app-usb-printer/root/etc/uci-defaults/luci-usb-printer
new file mode 100755
index 0000000000..38c214f9c1
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/root/etc/uci-defaults/luci-usb-printer
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@usb_printer[-1]
+ add ucitrack usb_printer
+ set ucitrack.@usb_printer[-1].init=usb_printer
+ commit ucitrack
+EOF
+
+[ -f /etc/init.d/p910nd ] && /etc/init.d/p910nd disable
+
+exit 0
diff --git a/package/lean/luci-app-usb-printer/root/usr/bin/detectlp b/package/lean/luci-app-usb-printer/root/usr/bin/detectlp
new file mode 100755
index 0000000000..b69385bb90
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/root/usr/bin/detectlp
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+lp_path=/sys/class/usbmisc
+
+if ! [ -d "$lp_path" ]; then
+ exit
+fi
+
+cd $lp_path
+
+for lps in `ls`; do
+ desc_file=$lp_path/$lps/device/ieee1284_id
+ uevent_file=$lp_path/$lps/device/uevent
+
+ name=`cat $desc_file | sed 's/.*DES:\(.*\);.*/\1/' | cut -d ';' -f 1`
+ model=`cat $desc_file | sed 's/.*MDL:\(.*\);.*/\1/' | cut -d ';' -f 1`
+ product=`cat $uevent_file | grep PRODUCT= | sed 's/PRODUCT=\(.*\)/\1/'`
+
+ echo $lps,$product,$model,$name;
+done
diff --git a/package/lean/luci-app-usb-printer/root/usr/bin/usb_printer_hotplug b/package/lean/luci-app-usb-printer/root/usr/bin/usb_printer_hotplug
new file mode 100755
index 0000000000..e0a60b0033
--- /dev/null
+++ b/package/lean/luci-app-usb-printer/root/usr/bin/usb_printer_hotplug
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Copyright (C) 2005-2014 NowRush Studio
+# Author: hackpascal
+
+. $IPKG_INSTROOT/lib/functions.sh
+
+PRODUCT=$1
+ACTION=$2
+
+DEVICES=
+
+check_printer() {
+ local cfg=$1
+ local enabled
+ local device_id
+ local bind_ip
+ local port
+ local bidirect
+ local device_file
+ local args=""
+ local pid_file
+
+ config_get_bool enabled "$cfg" enabled 0
+ [ "$enabled" -eq 0 ] && return 0
+
+ config_get device_id "$cfg" device ""
+ config_get bind_ip "$cfg" bind "0.0.0.0"
+ config_get port "$cfg" port ""
+ config_get_bool bidirect "$cfg" bidirectional "0"
+
+ if [ -z "$device_id" ] || [ -z "$port" ]; then
+ return
+ fi
+
+ if [ x"$PRODUCT" != x"$device_id" ]; then
+ return
+ fi
+
+ device_file=`echo $DEVICES | grep $device_id | cut -d , -f 1`
+
+ if [ "$ACTION" = "add" ] && [ -z "$device_file" ]; then
+ return
+ fi
+
+ pid_file=/var/run/p910${port}d.pid
+ [ -f $pid_file ] && kill `cat $pid_file` 2>/dev/null
+
+ if [ "$ACTION" = "add" ]; then
+ if [ "$bidirect" != 0 ]; then
+ args='-b'
+ fi
+
+ logger "usb_printer: start p910nd on $bind_ip:$port for /dev/usb/$device_file"
+ /usr/sbin/p910nd $args -f /dev/usb/$device_file -i $bind_ip $port
+ fi
+}
+
+if [ -z "$PRODUCT" ] || [ -z "$ACTION" ]; then
+ echo "Arguements required"
+ exit 1
+fi
+
+if [ "$ACTION" != "add" ] && [ "$ACTION" != "remove" ]; then
+ echo "Invalid action arguement"
+ exit 1
+fi
+
+DEVICES=`/usr/bin/detectlp`
+
+config_load usb_printer
+
+config_foreach check_printer printer
diff --git a/package/lean/luci-app-v2ray-server/Makefile b/package/lean/luci-app-v2ray-server/Makefile
new file mode 100644
index 0000000000..ec8cd0bf40
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2018-2019 Lienol
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for V2ray Server
+LUCI_DEPENDS:=+v2ray
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=6
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua b/package/lean/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua
new file mode 100644
index 0000000000..5ca8c3d279
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua
@@ -0,0 +1,47 @@
+module("luci.controller.v2ray_server",package.seeall)
+local http = require "luci.http"
+local v2ray = require "luci.model.cbi.v2ray_server.api.v2ray"
+
+function index()
+ if not nixio.fs.access("/etc/config/v2ray_server")then
+ return
+ end
+ entry({"admin","vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin","vpn","v2ray_server"},cbi("v2ray_server/index"),_("V2ray Server"),3).dependent=true
+ entry({"admin","vpn","v2ray_server","config"},cbi("v2ray_server/config")).leaf=true
+
+ entry({"admin","vpn","v2ray_server","users_status"},call("v2ray_users_status")).leaf=true
+ entry({"admin", "vpn", "v2ray_server", "check"}, call("v2ray_check")).leaf = true
+ entry({"admin", "vpn", "v2ray_server", "update"}, call("v2ray_update")).leaf = true
+end
+
+local function http_write_json(content)
+ http.prepare_content("application/json")
+ http.write_json(content or { code = 1 })
+end
+
+function v2ray_users_status()
+ local e={}
+ e.index=luci.http.formvalue("index")
+ e.status=luci.sys.call("ps -w| grep -v grep | grep '/var/etc/v2ray_server/" .. luci.http.formvalue("id") .. "' >/dev/null")==0
+ http_write_json(e)
+end
+
+function v2ray_check()
+ local json = v2ray.to_check("")
+ http_write_json(json)
+end
+
+function v2ray_update()
+ local json = nil
+ local task = http.formvalue("task")
+ if task == "extract" then
+ json = v2ray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
+ elseif task == "move" then
+ json = v2ray.to_move(http.formvalue("file"))
+ else
+ json = v2ray.to_download(http.formvalue("url"))
+ end
+
+ http_write_json(json)
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua
new file mode 100644
index 0000000000..31a99999c3
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua
@@ -0,0 +1,82 @@
+local ucursor = require "luci.model.uci".cursor()
+local json = require "luci.jsonc"
+local server_section = arg[1]
+local server = ucursor:get_all("v2ray_server", server_section)
+
+local proset
+
+if server.protocol == "vmess" then
+ proset = {
+ clients = {
+ {
+ id = server.VMess_id,
+ alterId = tonumber(server.VMess_alterId),
+ level = tonumber(server.VMess_level)
+ }
+ }
+ }
+else
+ proset = {
+ auth = "password",
+ accounts = {
+ {
+ user = server.Socks_user,
+ pass = server.Socks_pass
+ }
+ }
+ }
+end
+
+
+local v2ray = {
+ log = {
+ --error = "/var/log/v2ray.log",
+ loglevel = "warning"
+ },
+ --
+ inbound = {
+ port = tonumber(server.port),
+ protocol = server.protocol,
+ settings = proset,
+ -- ײ㴫
+ streamSettings = {
+ network = server.transport,
+ security = (server.tls == '1') and "tls" or "none",
+ kcpSettings = (server.transport == "mkcp") and {
+ mtu = tonumber(server.mkcp_mtu),
+ tti = tonumber(server.mkcp_tti),
+ uplinkCapacity = tonumber(server.mkcp_uplinkCapacity),
+ downlinkCapacity = tonumber(server.mkcp_downlinkCapacity),
+ congestion = (server.mkcp_congestion == "1") and true or false,
+ readBufferSize = tonumber(server.mkcp_readBufferSize),
+ writeBufferSize = tonumber(server.mkcp_writeBufferSize),
+ header = {
+ type = server.mkcp_guise
+ }
+ } or nil,
+ httpSettings = (server.transport == "h2") and {
+ path = server.h2_path,
+ host = server.h2_host,
+ } or nil,
+ quicSettings = (server.transport == "quic") and {
+ security = server.quic_security,
+ key = server.quic_key,
+ header = {
+ type = server.quic_guise
+ }
+ } or nil
+ }
+ },
+ --
+ outbound = {
+ protocol = "freedom"
+ },
+ --
+ outboundDetour = {
+ {
+ protocol = "blackhole",
+ tag = "blocked"
+ }
+ }
+}
+print(json.stringify(v2ray,1))
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua
new file mode 100644
index 0000000000..d7500b4584
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua
@@ -0,0 +1,321 @@
+-- Copyright 2018 Lienol
+-- Licensed to the public under the Apache License 2.0.
+
+local fs = require "nixio.fs"
+local sys = require "luci.sys"
+local uci = require "luci.model.uci".cursor()
+local util = require "luci.util"
+local i18n = require "luci.i18n"
+
+module("luci.model.cbi.v2ray_server.api.v2ray", package.seeall)
+
+local v2ray_api = "https://api.github.com/repos/v2ray/v2ray-core/releases/latest"
+
+local wget = "/usr/bin/wget"
+local wget_args = { "--no-check-certificate", "--quiet", "--timeout=10", "--tries=2" }
+local curl = "/usr/bin/curl"
+local command_timeout = 40
+
+local function _unpack(t, i)
+ i = i or 1
+ if t[i] ~= nil then
+ return t[i], _unpack(t, i + 1)
+ end
+end
+
+local function exec(cmd, args, writer, timeout)
+ local os = require "os"
+ local nixio = require "nixio"
+
+ local fdi, fdo = nixio.pipe()
+ local pid = nixio.fork()
+
+ if pid > 0 then
+ fdo:close()
+
+ if writer or timeout then
+ local starttime = os.time()
+ while true do
+ if timeout and os.difftime(os.time(), starttime) >= timeout then
+ nixio.kill(pid, nixio.const.SIGTERM)
+ return 1
+ end
+
+ if writer then
+ local buffer = fdi:read(2048)
+ if buffer and #buffer > 0 then
+ writer(buffer)
+ end
+ end
+
+ local wpid, stat, code = nixio.waitpid(pid, "nohang")
+
+ if wpid and stat == "exited" then
+ return code
+ end
+
+ if not writer and timeout then
+ nixio.nanosleep(1)
+ end
+ end
+ else
+ local wpid, stat, code = nixio.waitpid(pid)
+ return wpid and stat == "exited" and code
+ end
+ elseif pid == 0 then
+ nixio.dup(fdo, nixio.stdout)
+ fdi:close()
+ fdo:close()
+ nixio.exece(cmd, args, nil)
+ nixio.stdout:close()
+ os.exit(1)
+ end
+end
+
+local function compare_versions(ver1, comp, ver2)
+ local table = table
+
+ local av1 = util.split(ver1, "[%.%-]", nil, true)
+ local av2 = util.split(ver2, "[%.%-]", nil, true)
+
+ local max = table.getn(av1)
+ local n2 = table.getn(av2)
+ if (max < n2) then
+ max = n2
+ end
+
+ for i = 1, max, 1 do
+ local s1 = av1[i] or ""
+ local s2 = av2[i] or ""
+
+ if comp == "~=" and (s1 ~= s2) then return true end
+ if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
+ if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
+ if (s1 ~= s2) then return false end
+ end
+
+ return not (comp == "<" or comp == ">")
+end
+
+local function auto_get_arch()
+ local arch = nixio.uname().machine or ""
+
+ if arch == "mips" then
+ if fs.access("/usr/lib/os-release") then
+ arch = sys.exec("grep 'LEDE_BOARD' /usr/lib/os-release | grep -oE 'ramips|ar71xx'")
+ elseif fs.access("/etc/openwrt_release") then
+ arch = sys.exec("grep 'DISTRIB_TARGET' /etc/openwrt_release | grep -oE 'ramips|ar71xx'")
+ end
+ end
+
+ return util.trim(arch)
+end
+
+local function get_file_info(arch)
+ local file_tree = ""
+ local sub_version = ""
+
+ if arch == "x86_64" then
+ file_tree = "64"
+ elseif arch == "ramips" then
+ file_tree = "mipsle"
+ elseif arch == "ar71xx" then
+ file_tree = "mips"
+ elseif arch:match("^i[%d]86$") then
+ file_tree = "32"
+ elseif arch:match("^armv[5-8]") then
+ file_tree = "arm"
+ sub_version = arch:match("[5-8]")
+ end
+
+ return file_tree, sub_version
+end
+
+local function get_api_json(url)
+ local jsonc = require "luci.jsonc"
+
+ local output = { }
+ --exec(wget, { "-O-", url, _unpack(wget_args) },
+ -- function(chunk) output[#output + 1] = chunk end)
+ --local json_content = util.trim(table.concat(output))
+
+ local json_content = luci.sys.exec(curl.." -sL "..url)
+
+ if json_content == "" then
+ return { }
+ end
+
+ return jsonc.parse(json_content) or { }
+end
+
+function get_config_option(option, default)
+ return uci:get("v2ray", "general", option) or default
+end
+
+function get_current_log_file(type)
+ local log_folder = get_config_option("log_folder", "/var/log/v2ray")
+ return "%s/%s.%s.log" % { log_folder, type, "general" }
+end
+
+function is_running(client)
+ if client and client ~= "" then
+ local file_name = client:match(".*/([^/]+)$") or ""
+ if file_name ~= "" then
+ return sys.call("pidof %s >/dev/null" % file_name) == 0
+ end
+ end
+
+ return false
+end
+
+function get_v2ray_version()
+ return luci.sys.exec("/usr/bin/v2ray/v2ray -version | awk '{print $2}' | sed -n 1P")
+end
+
+function to_check(arch)
+ if not arch or arch == "" then
+ arch = auto_get_arch()
+ end
+
+ local file_tree, sub_version = get_file_info(arch)
+
+ if file_tree == "" then
+ return {
+ code = 1,
+ error = i18n.translate("Can't determine ARCH, or ARCH not supported. Please select manually.")
+ }
+ end
+
+ local json = get_api_json(v2ray_api)
+
+ if json.tag_name == nil then
+ return {
+ code = 1,
+ error = i18n.translate("Get remote version info failed.")
+ }
+ end
+
+ local remote_version = json.tag_name:match("[^v]+")
+ local needs_update = compare_versions(get_v2ray_version(), "<", remote_version)
+ local html_url, download_url
+
+ if needs_update then
+ html_url = json.html_url
+ for _, v in ipairs(json.assets) do
+ if v.name and v.name:match("linux%-" .. file_tree) then
+ download_url = v.browser_download_url
+ break
+ end
+ end
+ end
+
+ if needs_update and not download_url then
+ return {
+ code = 1,
+ version = remote_version,
+ html_url = html_url,
+ type = file_tree .. sub_version,
+ error = i18n.translate("New version found, but failed to get new version download url.")
+ }
+ end
+
+ return {
+ code = 0,
+ update = needs_update,
+ version = remote_version,
+ url = {
+ html = html_url,
+ download = download_url
+ },
+ type = file_tree .. sub_version
+ }
+end
+
+function to_download(url)
+ if not url or url == "" then
+ return {
+ code = 1,
+ error = i18n.translate("Download url is required.")
+ }
+ end
+
+ sys.call("/bin/rm -f /tmp/v2ray_download.*")
+
+ local tmp_file = util.trim(util.exec("mktemp -u -t v2ray_download.XXXXXX"))
+
+ local result = exec(wget, {
+ "-O", tmp_file, url, _unpack(wget_args) }, nil, command_timeout) == 0
+
+ if not result then
+ exec("/bin/rm", { "-f", tmp_file })
+ return {
+ code = 1,
+ error = i18n.translatef("File download failed or timed out: %s", url)
+ }
+ end
+
+ return {
+ code = 0,
+ file = tmp_file
+ }
+end
+
+function to_extract(file, subfix)
+ local isinstall_unzip=sys.call("opkg list-installed | grep unzip > /dev/null")==0
+ if not isinstall_unzip then
+ sys.call("opkg update && opkg install unzip > /dev/null")
+ end
+
+ if not file or file == "" or not fs.access(file) then
+ return {
+ code = 1,
+ error = i18n.translate("File path required.")
+ }
+ end
+
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX"))
+
+ local output = { }
+ exec("/usr/bin/unzip", { "-o", file , "-d", tmp_dir },
+ function(chunk) output[#output + 1] = chunk end)
+
+ local files = util.split(table.concat(output))
+
+ exec("/bin/rm", { "-f", file })
+
+ return {
+ code = 0,
+ file = tmp_dir
+ }
+end
+
+function to_move(file)
+ if not file or file == "" then
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ return {
+ code = 1,
+ error = i18n.translate("Client file is required.")
+ }
+ end
+
+ local client_file = "/usr/bin/v2ray"
+
+ sys.call("mkdir -p "..client_file)
+
+ local result = exec("/bin/mv", { "-f", file.."/v2ray", file.."/v2ctl", client_file }, nil, command_timeout) == 0
+
+ if not result or not fs.access(client_file) then
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ return {
+ code = 1,
+ error = i18n.translatef("Can't move new file to path: %s", client_file)
+ }
+ end
+
+ exec("/bin/chmod", { "-R", "755", client_file })
+
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+
+ return { code = 0 }
+end
diff --git a/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua
new file mode 100644
index 0000000000..ddfaa4bb9f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua
@@ -0,0 +1,105 @@
+local i="v2ray_server"
+local n=require"luci.dispatcher"
+local a,t,e
+local o={
+"none",
+"srtp",
+"utp",
+"wechat-video",
+"dtls",
+"wireguard",
+}
+a=Map(i,"V2ray "..translate("Server Config"))
+a.redirect=n.build_url("admin","vpn","v2ray_server")
+
+t=a:section(NamedSection,arg[1],"user","")
+t.addremove=false
+t.dynamic=false
+
+e=t:option(Flag,"enable",translate("Enable"))
+e.default="1"
+e.rmempty=false
+
+e=t:option(Value,"remarks",translate("Remarks"))
+e.default=translate("Remarks")
+
+e.rmempty=false
+e=t:option(Value,"port",translate("Port"))
+e.datatype="port"
+e.rmempty=false
+e.default=10086
+
+e=t:option(ListValue,"protocol",translate("Protocol"))
+e:value("vmess",translate("Vmess"))
+e:value("socks",translate("Socks"))
+
+e=t:option(Value,"VMess_id",translate("ID"))
+e.default=luci.sys.exec("cat /proc/sys/kernel/random/uuid")
+e.rmempty=true
+e:depends("protocol","vmess")
+
+e=t:option(Value,"VMess_alterId",translate("Alter ID"))
+e.default=16
+e.rmempty=true
+e:depends("protocol","vmess")
+
+e=t:option(Value,"Socks_user",translate("User name"))
+e.default="lean"
+e.rmempty=true
+e:depends("protocol","socks")
+
+e=t:option(Value,"Socks_pass",translate("Password"))
+e.default="password"
+e.rmempty=true
+e.password=true
+e:depends("protocol","socks")
+
+e=t:option(Value,"VMess_level",translate("User Level"))
+e.default=1
+
+e=t:option(ListValue,"transport",translate("Transport"))
+e.default=tcp
+e:value("tcp","TCP")
+e:value("mkcp","mKCP")
+e:value("quic","QUIC")
+e:depends("protocol","vmess")
+
+e=t:option(ListValue,"tcp_guise",translate("Camouflage Type"))
+e:depends("transport","tcp")
+e:value("none","none")
+e:value("http","http")
+e.default=none
+
+e=t:option(DynamicList,"tcp_guise_http_host",translate("HTTP Host"))
+e:depends("tcp_guise","http")
+e=t:option(DynamicList,"tcp_guise_http_path",translate("HTTP Path"))
+e:depends("tcp_guise","http")
+e=t:option(ListValue,"mkcp_guise",translate("Camouflage Type"))
+for a,t in ipairs(o)do e:value(t)end
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_mtu",translate("KCP MTU"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_tti",translate("KCP TTI"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_uplinkCapacity",translate("KCP uplinkCapacity"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_downlinkCapacity",translate("KCP downlinkCapacity"))
+e:depends("transport","mkcp")
+e=t:option(Flag,"mkcp_congestion",translate("KCP Congestion"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_readBufferSize",translate("KCP readBufferSize"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_writeBufferSize",translate("KCP writeBufferSize"))
+e:depends("transport","mkcp")
+e=t:option(ListValue,"quic_security",translate("Encrypt Method"))
+e:value("none")
+e:value("aes-128-gcm")
+e:value("chacha20-poly1305")
+e:depends("transport","quic")
+e=t:option(Value,"quic_key",translate("Encrypt Method")..translate("Key"))
+e:depends("transport","quic")
+e=t:option(ListValue,"quic_guise",translate("Camouflage Type"))
+for a,t in ipairs(o)do e:value(t)end
+e:depends("transport","quic")
+
+return a
diff --git a/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua
new file mode 100644
index 0000000000..b6fc41e06e
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua
@@ -0,0 +1,45 @@
+local i=require"luci.dispatcher"
+local e=require"nixio.fs"
+local e=require"luci.sys"
+local e=luci.model.uci.cursor()
+local o="v2ray_server"
+local a,e,t
+
+a=Map(o,translate("V2ray Server"))
+e=a:section(TypedSection,"global",translate("Global Setting"))
+e.anonymous=true
+e.addremove=false
+t=e:option(Flag,"enable",translate("Enable"))
+t.rmempty=false
+
+e=a:section(TypedSection,"user",translate("Server Setting"))
+e.anonymous=true
+e.addremove=true
+e.template="cbi/tblsection"
+e.extedit=i.build_url("admin","vpn",o,"config","%s")
+
+function e.create(t,e)
+local e=TypedSection.create(t,e)
+luci.http.redirect(i.build_url("admin","vpn",o,"config",e))
+end
+
+function e.remove(e,a)
+e.map.proceed=true
+e.map:del(a)
+luci.http.redirect(i.build_url("admin","vpn",o))
+end
+
+t=e:option(Flag,"enable",translate("Enable"))
+t.width="5%"
+t.rmempty=false
+t=e:option(DummyValue,"status",translate("Status"))
+t.template="v2ray_server/users_status"
+t.value=translate("Collecting data...")
+t=e:option(DummyValue,"remarks",translate("Remarks"))
+t.width="15%"
+t=e:option(DummyValue,"port",translate("Port"))
+t=e:option(DummyValue,"protocol",translate("Protocol"))
+
+a:append(Template("v2ray_server/users_list_status"))
+
+return a
diff --git a/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm
new file mode 100644
index 0000000000..d02ca6157d
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm
@@ -0,0 +1,28 @@
+<%#
+ Copyright 2018 Lienol
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%
+local dsp = require "luci.dispatcher"
+-%>
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm
new file mode 100644
index 0000000000..40c2e3c58c
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm
@@ -0,0 +1,3 @@
+<%+cbi/valueheader%>
+--
+<%+cbi/valuefooter%>
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm
new file mode 100644
index 0000000000..7208bf7f5f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm
@@ -0,0 +1,188 @@
+<%#
+ Copyright 2018
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%
+local v2ray_version=luci.sys.exec("/usr/bin/v2ray/v2ray -version | awk '{print $2}' | sed -n 1P")
+local dsp = require "luci.dispatcher"
+-%>
+
+
+
+
+
+
+
+
+ 【 <%=v2ray_version%>】
+
+
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/Makefile b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/Makefile
new file mode 100644
index 0000000000..ec8cd0bf40
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2018-2019 Lienol
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for V2ray Server
+LUCI_DEPENDS:=+v2ray
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=6
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua
new file mode 100644
index 0000000000..5ca8c3d279
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua
@@ -0,0 +1,47 @@
+module("luci.controller.v2ray_server",package.seeall)
+local http = require "luci.http"
+local v2ray = require "luci.model.cbi.v2ray_server.api.v2ray"
+
+function index()
+ if not nixio.fs.access("/etc/config/v2ray_server")then
+ return
+ end
+ entry({"admin","vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin","vpn","v2ray_server"},cbi("v2ray_server/index"),_("V2ray Server"),3).dependent=true
+ entry({"admin","vpn","v2ray_server","config"},cbi("v2ray_server/config")).leaf=true
+
+ entry({"admin","vpn","v2ray_server","users_status"},call("v2ray_users_status")).leaf=true
+ entry({"admin", "vpn", "v2ray_server", "check"}, call("v2ray_check")).leaf = true
+ entry({"admin", "vpn", "v2ray_server", "update"}, call("v2ray_update")).leaf = true
+end
+
+local function http_write_json(content)
+ http.prepare_content("application/json")
+ http.write_json(content or { code = 1 })
+end
+
+function v2ray_users_status()
+ local e={}
+ e.index=luci.http.formvalue("index")
+ e.status=luci.sys.call("ps -w| grep -v grep | grep '/var/etc/v2ray_server/" .. luci.http.formvalue("id") .. "' >/dev/null")==0
+ http_write_json(e)
+end
+
+function v2ray_check()
+ local json = v2ray.to_check("")
+ http_write_json(json)
+end
+
+function v2ray_update()
+ local json = nil
+ local task = http.formvalue("task")
+ if task == "extract" then
+ json = v2ray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
+ elseif task == "move" then
+ json = v2ray.to_move(http.formvalue("file"))
+ else
+ json = v2ray.to_download(http.formvalue("url"))
+ end
+
+ http_write_json(json)
+end
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua
new file mode 100644
index 0000000000..31a99999c3
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/genv2rayconfig.lua
@@ -0,0 +1,82 @@
+local ucursor = require "luci.model.uci".cursor()
+local json = require "luci.jsonc"
+local server_section = arg[1]
+local server = ucursor:get_all("v2ray_server", server_section)
+
+local proset
+
+if server.protocol == "vmess" then
+ proset = {
+ clients = {
+ {
+ id = server.VMess_id,
+ alterId = tonumber(server.VMess_alterId),
+ level = tonumber(server.VMess_level)
+ }
+ }
+ }
+else
+ proset = {
+ auth = "password",
+ accounts = {
+ {
+ user = server.Socks_user,
+ pass = server.Socks_pass
+ }
+ }
+ }
+end
+
+
+local v2ray = {
+ log = {
+ --error = "/var/log/v2ray.log",
+ loglevel = "warning"
+ },
+ --
+ inbound = {
+ port = tonumber(server.port),
+ protocol = server.protocol,
+ settings = proset,
+ -- ײ㴫
+ streamSettings = {
+ network = server.transport,
+ security = (server.tls == '1') and "tls" or "none",
+ kcpSettings = (server.transport == "mkcp") and {
+ mtu = tonumber(server.mkcp_mtu),
+ tti = tonumber(server.mkcp_tti),
+ uplinkCapacity = tonumber(server.mkcp_uplinkCapacity),
+ downlinkCapacity = tonumber(server.mkcp_downlinkCapacity),
+ congestion = (server.mkcp_congestion == "1") and true or false,
+ readBufferSize = tonumber(server.mkcp_readBufferSize),
+ writeBufferSize = tonumber(server.mkcp_writeBufferSize),
+ header = {
+ type = server.mkcp_guise
+ }
+ } or nil,
+ httpSettings = (server.transport == "h2") and {
+ path = server.h2_path,
+ host = server.h2_host,
+ } or nil,
+ quicSettings = (server.transport == "quic") and {
+ security = server.quic_security,
+ key = server.quic_key,
+ header = {
+ type = server.quic_guise
+ }
+ } or nil
+ }
+ },
+ --
+ outbound = {
+ protocol = "freedom"
+ },
+ --
+ outboundDetour = {
+ {
+ protocol = "blackhole",
+ tag = "blocked"
+ }
+ }
+}
+print(json.stringify(v2ray,1))
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua
new file mode 100644
index 0000000000..d7500b4584
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua
@@ -0,0 +1,321 @@
+-- Copyright 2018 Lienol
+-- Licensed to the public under the Apache License 2.0.
+
+local fs = require "nixio.fs"
+local sys = require "luci.sys"
+local uci = require "luci.model.uci".cursor()
+local util = require "luci.util"
+local i18n = require "luci.i18n"
+
+module("luci.model.cbi.v2ray_server.api.v2ray", package.seeall)
+
+local v2ray_api = "https://api.github.com/repos/v2ray/v2ray-core/releases/latest"
+
+local wget = "/usr/bin/wget"
+local wget_args = { "--no-check-certificate", "--quiet", "--timeout=10", "--tries=2" }
+local curl = "/usr/bin/curl"
+local command_timeout = 40
+
+local function _unpack(t, i)
+ i = i or 1
+ if t[i] ~= nil then
+ return t[i], _unpack(t, i + 1)
+ end
+end
+
+local function exec(cmd, args, writer, timeout)
+ local os = require "os"
+ local nixio = require "nixio"
+
+ local fdi, fdo = nixio.pipe()
+ local pid = nixio.fork()
+
+ if pid > 0 then
+ fdo:close()
+
+ if writer or timeout then
+ local starttime = os.time()
+ while true do
+ if timeout and os.difftime(os.time(), starttime) >= timeout then
+ nixio.kill(pid, nixio.const.SIGTERM)
+ return 1
+ end
+
+ if writer then
+ local buffer = fdi:read(2048)
+ if buffer and #buffer > 0 then
+ writer(buffer)
+ end
+ end
+
+ local wpid, stat, code = nixio.waitpid(pid, "nohang")
+
+ if wpid and stat == "exited" then
+ return code
+ end
+
+ if not writer and timeout then
+ nixio.nanosleep(1)
+ end
+ end
+ else
+ local wpid, stat, code = nixio.waitpid(pid)
+ return wpid and stat == "exited" and code
+ end
+ elseif pid == 0 then
+ nixio.dup(fdo, nixio.stdout)
+ fdi:close()
+ fdo:close()
+ nixio.exece(cmd, args, nil)
+ nixio.stdout:close()
+ os.exit(1)
+ end
+end
+
+local function compare_versions(ver1, comp, ver2)
+ local table = table
+
+ local av1 = util.split(ver1, "[%.%-]", nil, true)
+ local av2 = util.split(ver2, "[%.%-]", nil, true)
+
+ local max = table.getn(av1)
+ local n2 = table.getn(av2)
+ if (max < n2) then
+ max = n2
+ end
+
+ for i = 1, max, 1 do
+ local s1 = av1[i] or ""
+ local s2 = av2[i] or ""
+
+ if comp == "~=" and (s1 ~= s2) then return true end
+ if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
+ if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
+ if (s1 ~= s2) then return false end
+ end
+
+ return not (comp == "<" or comp == ">")
+end
+
+local function auto_get_arch()
+ local arch = nixio.uname().machine or ""
+
+ if arch == "mips" then
+ if fs.access("/usr/lib/os-release") then
+ arch = sys.exec("grep 'LEDE_BOARD' /usr/lib/os-release | grep -oE 'ramips|ar71xx'")
+ elseif fs.access("/etc/openwrt_release") then
+ arch = sys.exec("grep 'DISTRIB_TARGET' /etc/openwrt_release | grep -oE 'ramips|ar71xx'")
+ end
+ end
+
+ return util.trim(arch)
+end
+
+local function get_file_info(arch)
+ local file_tree = ""
+ local sub_version = ""
+
+ if arch == "x86_64" then
+ file_tree = "64"
+ elseif arch == "ramips" then
+ file_tree = "mipsle"
+ elseif arch == "ar71xx" then
+ file_tree = "mips"
+ elseif arch:match("^i[%d]86$") then
+ file_tree = "32"
+ elseif arch:match("^armv[5-8]") then
+ file_tree = "arm"
+ sub_version = arch:match("[5-8]")
+ end
+
+ return file_tree, sub_version
+end
+
+local function get_api_json(url)
+ local jsonc = require "luci.jsonc"
+
+ local output = { }
+ --exec(wget, { "-O-", url, _unpack(wget_args) },
+ -- function(chunk) output[#output + 1] = chunk end)
+ --local json_content = util.trim(table.concat(output))
+
+ local json_content = luci.sys.exec(curl.." -sL "..url)
+
+ if json_content == "" then
+ return { }
+ end
+
+ return jsonc.parse(json_content) or { }
+end
+
+function get_config_option(option, default)
+ return uci:get("v2ray", "general", option) or default
+end
+
+function get_current_log_file(type)
+ local log_folder = get_config_option("log_folder", "/var/log/v2ray")
+ return "%s/%s.%s.log" % { log_folder, type, "general" }
+end
+
+function is_running(client)
+ if client and client ~= "" then
+ local file_name = client:match(".*/([^/]+)$") or ""
+ if file_name ~= "" then
+ return sys.call("pidof %s >/dev/null" % file_name) == 0
+ end
+ end
+
+ return false
+end
+
+function get_v2ray_version()
+ return luci.sys.exec("/usr/bin/v2ray/v2ray -version | awk '{print $2}' | sed -n 1P")
+end
+
+function to_check(arch)
+ if not arch or arch == "" then
+ arch = auto_get_arch()
+ end
+
+ local file_tree, sub_version = get_file_info(arch)
+
+ if file_tree == "" then
+ return {
+ code = 1,
+ error = i18n.translate("Can't determine ARCH, or ARCH not supported. Please select manually.")
+ }
+ end
+
+ local json = get_api_json(v2ray_api)
+
+ if json.tag_name == nil then
+ return {
+ code = 1,
+ error = i18n.translate("Get remote version info failed.")
+ }
+ end
+
+ local remote_version = json.tag_name:match("[^v]+")
+ local needs_update = compare_versions(get_v2ray_version(), "<", remote_version)
+ local html_url, download_url
+
+ if needs_update then
+ html_url = json.html_url
+ for _, v in ipairs(json.assets) do
+ if v.name and v.name:match("linux%-" .. file_tree) then
+ download_url = v.browser_download_url
+ break
+ end
+ end
+ end
+
+ if needs_update and not download_url then
+ return {
+ code = 1,
+ version = remote_version,
+ html_url = html_url,
+ type = file_tree .. sub_version,
+ error = i18n.translate("New version found, but failed to get new version download url.")
+ }
+ end
+
+ return {
+ code = 0,
+ update = needs_update,
+ version = remote_version,
+ url = {
+ html = html_url,
+ download = download_url
+ },
+ type = file_tree .. sub_version
+ }
+end
+
+function to_download(url)
+ if not url or url == "" then
+ return {
+ code = 1,
+ error = i18n.translate("Download url is required.")
+ }
+ end
+
+ sys.call("/bin/rm -f /tmp/v2ray_download.*")
+
+ local tmp_file = util.trim(util.exec("mktemp -u -t v2ray_download.XXXXXX"))
+
+ local result = exec(wget, {
+ "-O", tmp_file, url, _unpack(wget_args) }, nil, command_timeout) == 0
+
+ if not result then
+ exec("/bin/rm", { "-f", tmp_file })
+ return {
+ code = 1,
+ error = i18n.translatef("File download failed or timed out: %s", url)
+ }
+ end
+
+ return {
+ code = 0,
+ file = tmp_file
+ }
+end
+
+function to_extract(file, subfix)
+ local isinstall_unzip=sys.call("opkg list-installed | grep unzip > /dev/null")==0
+ if not isinstall_unzip then
+ sys.call("opkg update && opkg install unzip > /dev/null")
+ end
+
+ if not file or file == "" or not fs.access(file) then
+ return {
+ code = 1,
+ error = i18n.translate("File path required.")
+ }
+ end
+
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX"))
+
+ local output = { }
+ exec("/usr/bin/unzip", { "-o", file , "-d", tmp_dir },
+ function(chunk) output[#output + 1] = chunk end)
+
+ local files = util.split(table.concat(output))
+
+ exec("/bin/rm", { "-f", file })
+
+ return {
+ code = 0,
+ file = tmp_dir
+ }
+end
+
+function to_move(file)
+ if not file or file == "" then
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ return {
+ code = 1,
+ error = i18n.translate("Client file is required.")
+ }
+ end
+
+ local client_file = "/usr/bin/v2ray"
+
+ sys.call("mkdir -p "..client_file)
+
+ local result = exec("/bin/mv", { "-f", file.."/v2ray", file.."/v2ctl", client_file }, nil, command_timeout) == 0
+
+ if not result or not fs.access(client_file) then
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+ return {
+ code = 1,
+ error = i18n.translatef("Can't move new file to path: %s", client_file)
+ }
+ end
+
+ exec("/bin/chmod", { "-R", "755", client_file })
+
+ sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
+
+ return { code = 0 }
+end
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua
new file mode 100644
index 0000000000..ddfaa4bb9f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/config.lua
@@ -0,0 +1,105 @@
+local i="v2ray_server"
+local n=require"luci.dispatcher"
+local a,t,e
+local o={
+"none",
+"srtp",
+"utp",
+"wechat-video",
+"dtls",
+"wireguard",
+}
+a=Map(i,"V2ray "..translate("Server Config"))
+a.redirect=n.build_url("admin","vpn","v2ray_server")
+
+t=a:section(NamedSection,arg[1],"user","")
+t.addremove=false
+t.dynamic=false
+
+e=t:option(Flag,"enable",translate("Enable"))
+e.default="1"
+e.rmempty=false
+
+e=t:option(Value,"remarks",translate("Remarks"))
+e.default=translate("Remarks")
+
+e.rmempty=false
+e=t:option(Value,"port",translate("Port"))
+e.datatype="port"
+e.rmempty=false
+e.default=10086
+
+e=t:option(ListValue,"protocol",translate("Protocol"))
+e:value("vmess",translate("Vmess"))
+e:value("socks",translate("Socks"))
+
+e=t:option(Value,"VMess_id",translate("ID"))
+e.default=luci.sys.exec("cat /proc/sys/kernel/random/uuid")
+e.rmempty=true
+e:depends("protocol","vmess")
+
+e=t:option(Value,"VMess_alterId",translate("Alter ID"))
+e.default=16
+e.rmempty=true
+e:depends("protocol","vmess")
+
+e=t:option(Value,"Socks_user",translate("User name"))
+e.default="lean"
+e.rmempty=true
+e:depends("protocol","socks")
+
+e=t:option(Value,"Socks_pass",translate("Password"))
+e.default="password"
+e.rmempty=true
+e.password=true
+e:depends("protocol","socks")
+
+e=t:option(Value,"VMess_level",translate("User Level"))
+e.default=1
+
+e=t:option(ListValue,"transport",translate("Transport"))
+e.default=tcp
+e:value("tcp","TCP")
+e:value("mkcp","mKCP")
+e:value("quic","QUIC")
+e:depends("protocol","vmess")
+
+e=t:option(ListValue,"tcp_guise",translate("Camouflage Type"))
+e:depends("transport","tcp")
+e:value("none","none")
+e:value("http","http")
+e.default=none
+
+e=t:option(DynamicList,"tcp_guise_http_host",translate("HTTP Host"))
+e:depends("tcp_guise","http")
+e=t:option(DynamicList,"tcp_guise_http_path",translate("HTTP Path"))
+e:depends("tcp_guise","http")
+e=t:option(ListValue,"mkcp_guise",translate("Camouflage Type"))
+for a,t in ipairs(o)do e:value(t)end
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_mtu",translate("KCP MTU"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_tti",translate("KCP TTI"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_uplinkCapacity",translate("KCP uplinkCapacity"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_downlinkCapacity",translate("KCP downlinkCapacity"))
+e:depends("transport","mkcp")
+e=t:option(Flag,"mkcp_congestion",translate("KCP Congestion"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_readBufferSize",translate("KCP readBufferSize"))
+e:depends("transport","mkcp")
+e=t:option(Value,"mkcp_writeBufferSize",translate("KCP writeBufferSize"))
+e:depends("transport","mkcp")
+e=t:option(ListValue,"quic_security",translate("Encrypt Method"))
+e:value("none")
+e:value("aes-128-gcm")
+e:value("chacha20-poly1305")
+e:depends("transport","quic")
+e=t:option(Value,"quic_key",translate("Encrypt Method")..translate("Key"))
+e:depends("transport","quic")
+e=t:option(ListValue,"quic_guise",translate("Camouflage Type"))
+for a,t in ipairs(o)do e:value(t)end
+e:depends("transport","quic")
+
+return a
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua
new file mode 100644
index 0000000000..b6fc41e06e
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua
@@ -0,0 +1,45 @@
+local i=require"luci.dispatcher"
+local e=require"nixio.fs"
+local e=require"luci.sys"
+local e=luci.model.uci.cursor()
+local o="v2ray_server"
+local a,e,t
+
+a=Map(o,translate("V2ray Server"))
+e=a:section(TypedSection,"global",translate("Global Setting"))
+e.anonymous=true
+e.addremove=false
+t=e:option(Flag,"enable",translate("Enable"))
+t.rmempty=false
+
+e=a:section(TypedSection,"user",translate("Server Setting"))
+e.anonymous=true
+e.addremove=true
+e.template="cbi/tblsection"
+e.extedit=i.build_url("admin","vpn",o,"config","%s")
+
+function e.create(t,e)
+local e=TypedSection.create(t,e)
+luci.http.redirect(i.build_url("admin","vpn",o,"config",e))
+end
+
+function e.remove(e,a)
+e.map.proceed=true
+e.map:del(a)
+luci.http.redirect(i.build_url("admin","vpn",o))
+end
+
+t=e:option(Flag,"enable",translate("Enable"))
+t.width="5%"
+t.rmempty=false
+t=e:option(DummyValue,"status",translate("Status"))
+t.template="v2ray_server/users_status"
+t.value=translate("Collecting data...")
+t=e:option(DummyValue,"remarks",translate("Remarks"))
+t.width="15%"
+t=e:option(DummyValue,"port",translate("Port"))
+t=e:option(DummyValue,"protocol",translate("Protocol"))
+
+a:append(Template("v2ray_server/users_list_status"))
+
+return a
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm
new file mode 100644
index 0000000000..d02ca6157d
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm
@@ -0,0 +1,28 @@
+<%#
+ Copyright 2018 Lienol
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%
+local dsp = require "luci.dispatcher"
+-%>
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm
new file mode 100644
index 0000000000..40c2e3c58c
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/users_status.htm
@@ -0,0 +1,3 @@
+<%+cbi/valueheader%>
+--
+<%+cbi/valuefooter%>
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm
new file mode 100644
index 0000000000..7208bf7f5f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm
@@ -0,0 +1,188 @@
+<%#
+ Copyright 2018
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%
+local v2ray_version=luci.sys.exec("/usr/bin/v2ray/v2ray -version | awk '{print $2}' | sed -n 1P")
+local dsp = require "luci.dispatcher"
+-%>
+
+
+
+
+
+
+
+
+ 【 <%=v2ray_version%>】
+
+
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/po/zh-cn/v2ray_server.po b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/po/zh-cn/v2ray_server.po
new file mode 100644
index 0000000000..c5db9b349f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/po/zh-cn/v2ray_server.po
@@ -0,0 +1,53 @@
+msgid "V2ray Server"
+msgstr "V2ray 服务器"
+
+msgid "Global Settings"
+msgstr "全局设置"
+
+msgid "Server Config"
+msgstr "服务器配置"
+
+msgid "Users Manager"
+msgstr "用户管理"
+
+msgid "Remarks"
+msgstr "备注"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "Password"
+msgstr "密码"
+
+msgid "Protocol"
+msgstr "协议"
+
+msgid "Null"
+msgstr "无"
+
+msgid "Alter ID"
+msgstr "额外ID(AlterID)"
+
+msgid "User Level"
+msgstr "用户等级(Level)"
+
+msgid "Transport"
+msgstr "传输方式"
+
+msgid "Camouflage Type"
+msgstr "伪装类型"
+
+msgid "Enabled"
+msgstr "启用"
+
+msgid "Status"
+msgstr "状态"
+
+msgid "Current Condition"
+msgstr "当前状态"
+
+msgid "NOT RUNNING"
+msgstr "未运行"
+
+msgid "RUNNING"
+msgstr "运行中"
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/config/v2ray_server b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/config/v2ray_server
new file mode 100644
index 0000000000..cab9eeb88e
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/config/v2ray_server
@@ -0,0 +1,15 @@
+
+config global
+ option enable '0'
+
+config user
+ option enable '1'
+ option remarks '备注222'
+ option protocol 'vmess'
+ option VMess_id 'fd00927a-b0c2-4629-aef7-d9ff15a9d722'
+ option VMess_alterId '16'
+ option VMess_level '1'
+ option transport 'tcp'
+ option tcp_guise 'none'
+ option port '12366'
+
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/init.d/v2ray_server b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/init.d/v2ray_server
new file mode 100755
index 0000000000..ba4f90653f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/init.d/v2ray_server
@@ -0,0 +1,49 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2018-2019 Lienol
+
+START=99
+
+CONFIG=v2ray_server
+CONFIG_PATH=/var/etc/$CONFIG
+
+gen_v2ray_config_file() {
+ config_get enable $1 enable
+ [ "$enable" = "0" ] && return 0
+ config_get remarks $1 remarks
+ config_get port $1 port
+ lua /usr/lib/lua/luci/model/cbi/v2ray_server/api/genv2rayconfig.lua $1 > $CONFIG_PATH/$1.json
+ [ -f /var/v2server ] || cp -a /usr/bin/v2ray/v2ray /var/v2server
+ /var/v2server -config $CONFIG_PATH/$1.json >/dev/null 2>&1 &
+}
+
+start_v2ray_server() {
+ config_foreach gen_v2ray_config_file "user"
+ fw3 reload
+}
+
+stop_v2ray_server() {
+ fw3 reload
+ ps -w | grep "$CONFIG_PATH/" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
+}
+
+start() {
+ config_load $CONFIG
+ enable=$(uci get $CONFIG.@global[0].enable)
+ if [ "$enable" = "0" ];then
+ stop_v2ray_server
+ else
+ mkdir -p $CONFIG_PATH
+ start_v2ray_server
+ fi
+}
+
+stop() {
+ stop_v2ray_server
+ rm -rf $CONFIG_PATH
+}
+
+restart() {
+ stop
+ sleep 1
+ start
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server
new file mode 100755
index 0000000000..364f7db225
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.v2ray_server
+ set firewall.v2ray_server=include
+ set firewall.v2ray_server.type=script
+ set firewall.v2ray_server.path=/usr/share/v2ray_server/firewall.include
+ set firewall.v2ray_server.reload=1
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@v2ray_server[-1]
+ add ucitrack v2ray_server
+ set ucitrack.@v2ray_server[-1].init=v2ray_server
+ commit ucitrack
+EOF
+
+chmod a+x /usr/share/v2ray_server/* >/dev/null 2>&1
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include
new file mode 100755
index 0000000000..1f06a00b6f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+. $IPKG_INSTROOT/lib/functions.sh
+. $IPKG_INSTROOT/lib/functions/service.sh
+
+gen_user_iptables() {
+ config_get enable $1 enable
+ [ "$enable" = "0" ] && return 0
+ config_get remarks $1 remarks
+ config_get port $1 port
+ iptables -A V2RAY-SERVER -p tcp --dport $port -m comment --comment "$remarks" -j ACCEPT
+ iptables -A V2RAY-SERVER -p udp --dport $port -m comment --comment "$remarks" -j ACCEPT
+}
+
+iptables -F V2RAY-SERVER 2>/dev/null
+iptables -D INPUT -j V2RAY-SERVER 2>/dev/null
+iptables -X V2RAY-SERVER 2>/dev/null
+
+enable=$(uci get v2ray_server.@global[0].enable)
+if [ $enable -eq 1 ]; then
+ iptables -N V2RAY-SERVER
+ iptables -I INPUT -j V2RAY-SERVER
+ config_load v2ray_server
+ config_foreach gen_user_iptables "user"
+fi
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/po/zh-cn/v2ray_server.po b/package/lean/luci-app-v2ray-server/po/zh-cn/v2ray_server.po
new file mode 100644
index 0000000000..c5db9b349f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/po/zh-cn/v2ray_server.po
@@ -0,0 +1,53 @@
+msgid "V2ray Server"
+msgstr "V2ray 服务器"
+
+msgid "Global Settings"
+msgstr "全局设置"
+
+msgid "Server Config"
+msgstr "服务器配置"
+
+msgid "Users Manager"
+msgstr "用户管理"
+
+msgid "Remarks"
+msgstr "备注"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "Password"
+msgstr "密码"
+
+msgid "Protocol"
+msgstr "协议"
+
+msgid "Null"
+msgstr "无"
+
+msgid "Alter ID"
+msgstr "额外ID(AlterID)"
+
+msgid "User Level"
+msgstr "用户等级(Level)"
+
+msgid "Transport"
+msgstr "传输方式"
+
+msgid "Camouflage Type"
+msgstr "伪装类型"
+
+msgid "Enabled"
+msgstr "启用"
+
+msgid "Status"
+msgstr "状态"
+
+msgid "Current Condition"
+msgstr "当前状态"
+
+msgid "NOT RUNNING"
+msgstr "未运行"
+
+msgid "RUNNING"
+msgstr "运行中"
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/root/etc/config/v2ray_server b/package/lean/luci-app-v2ray-server/root/etc/config/v2ray_server
new file mode 100644
index 0000000000..cab9eeb88e
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/root/etc/config/v2ray_server
@@ -0,0 +1,15 @@
+
+config global
+ option enable '0'
+
+config user
+ option enable '1'
+ option remarks '备注222'
+ option protocol 'vmess'
+ option VMess_id 'fd00927a-b0c2-4629-aef7-d9ff15a9d722'
+ option VMess_alterId '16'
+ option VMess_level '1'
+ option transport 'tcp'
+ option tcp_guise 'none'
+ option port '12366'
+
diff --git a/package/lean/luci-app-v2ray-server/root/etc/init.d/v2ray_server b/package/lean/luci-app-v2ray-server/root/etc/init.d/v2ray_server
new file mode 100755
index 0000000000..ba4f90653f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/root/etc/init.d/v2ray_server
@@ -0,0 +1,49 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2018-2019 Lienol
+
+START=99
+
+CONFIG=v2ray_server
+CONFIG_PATH=/var/etc/$CONFIG
+
+gen_v2ray_config_file() {
+ config_get enable $1 enable
+ [ "$enable" = "0" ] && return 0
+ config_get remarks $1 remarks
+ config_get port $1 port
+ lua /usr/lib/lua/luci/model/cbi/v2ray_server/api/genv2rayconfig.lua $1 > $CONFIG_PATH/$1.json
+ [ -f /var/v2server ] || cp -a /usr/bin/v2ray/v2ray /var/v2server
+ /var/v2server -config $CONFIG_PATH/$1.json >/dev/null 2>&1 &
+}
+
+start_v2ray_server() {
+ config_foreach gen_v2ray_config_file "user"
+ fw3 reload
+}
+
+stop_v2ray_server() {
+ fw3 reload
+ ps -w | grep "$CONFIG_PATH/" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
+}
+
+start() {
+ config_load $CONFIG
+ enable=$(uci get $CONFIG.@global[0].enable)
+ if [ "$enable" = "0" ];then
+ stop_v2ray_server
+ else
+ mkdir -p $CONFIG_PATH
+ start_v2ray_server
+ fi
+}
+
+stop() {
+ stop_v2ray_server
+ rm -rf $CONFIG_PATH
+}
+
+restart() {
+ stop
+ sleep 1
+ start
+}
\ No newline at end of file
diff --git a/package/lean/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server b/package/lean/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server
new file mode 100755
index 0000000000..364f7db225
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/root/etc/uci-defaults/luci-app-v2ray-server
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.v2ray_server
+ set firewall.v2ray_server=include
+ set firewall.v2ray_server.type=script
+ set firewall.v2ray_server.path=/usr/share/v2ray_server/firewall.include
+ set firewall.v2ray_server.reload=1
+EOF
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@v2ray_server[-1]
+ add ucitrack v2ray_server
+ set ucitrack.@v2ray_server[-1].init=v2ray_server
+ commit ucitrack
+EOF
+
+chmod a+x /usr/share/v2ray_server/* >/dev/null 2>&1
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include b/package/lean/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include
new file mode 100755
index 0000000000..1f06a00b6f
--- /dev/null
+++ b/package/lean/luci-app-v2ray-server/root/usr/share/v2ray_server/firewall.include
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+. $IPKG_INSTROOT/lib/functions.sh
+. $IPKG_INSTROOT/lib/functions/service.sh
+
+gen_user_iptables() {
+ config_get enable $1 enable
+ [ "$enable" = "0" ] && return 0
+ config_get remarks $1 remarks
+ config_get port $1 port
+ iptables -A V2RAY-SERVER -p tcp --dport $port -m comment --comment "$remarks" -j ACCEPT
+ iptables -A V2RAY-SERVER -p udp --dport $port -m comment --comment "$remarks" -j ACCEPT
+}
+
+iptables -F V2RAY-SERVER 2>/dev/null
+iptables -D INPUT -j V2RAY-SERVER 2>/dev/null
+iptables -X V2RAY-SERVER 2>/dev/null
+
+enable=$(uci get v2ray_server.@global[0].enable)
+if [ $enable -eq 1 ]; then
+ iptables -N V2RAY-SERVER
+ iptables -I INPUT -j V2RAY-SERVER
+ config_load v2ray_server
+ config_foreach gen_user_iptables "user"
+fi
\ No newline at end of file
diff --git a/package/lean/luci-app-vlmcsd/Makefile b/package/lean/luci-app-vlmcsd/Makefile
new file mode 100644
index 0000000000..2bb1feb9c0
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI page for KMS
+LUCI_DEPENDS:=+vlmcsd
+PKG_VERSION:=1.0
+PKG_RELEASE:=5
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-vlmcsd/luasrc/controller/vlmcsd.lua b/package/lean/luci-app-vlmcsd/luasrc/controller/vlmcsd.lua
new file mode 100644
index 0000000000..f540847cbc
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/luasrc/controller/vlmcsd.lua
@@ -0,0 +1,19 @@
+module("luci.controller.vlmcsd", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/vlmcsd") then
+ return
+ end
+ local page
+ page = entry({"admin", "services", "vlmcsd"}, cbi("vlmcsd"), _("KMS Server"), 100)
+ page.i18n = "vlmcsd"
+ page.dependent = true
+ entry({"admin","services","vlmcsd","status"},call("act_status")).leaf=true
+end
+
+function act_status()
+ local e={}
+ e.running=luci.sys.call("pgrep vlmcsd >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-vlmcsd/luasrc/model/cbi/vlmcsd.lua b/package/lean/luci-app-vlmcsd/luasrc/model/cbi/vlmcsd.lua
new file mode 100644
index 0000000000..98c3e16fb1
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/luasrc/model/cbi/vlmcsd.lua
@@ -0,0 +1,35 @@
+
+
+m = Map("vlmcsd")
+m.title = translate("vlmcsd config")
+m.description = translate("A KMS Serever Emulator to active your Windows or Office")
+
+m:section(SimpleSection).template = "vlmcsd/vlmcsd_status"
+
+s = m:section(TypedSection, "vlmcsd")
+s.addremove = false
+s.anonymous = true
+
+s:tab("basic", translate("Basic Setting"))
+enable = s:taboption("basic",Flag, "enabled", translate("Enable"))
+enable.rmempty = false
+
+autoactivate = s:taboption("basic", Flag, "autoactivate", translate("Auto activate"))
+autoactivate.rmempty = false
+
+s:tab("config", translate("Config File"))
+config = s:taboption("config", Value, "config", translate("configfile"), translate("This file is /etc/vlmcsd.ini."), "")
+config.template = "cbi/tvalue"
+config.rows = 13
+config.wrap = "off"
+
+function config.cfgvalue(self, section)
+ return nixio.fs.readfile("/etc/vlmcsd.ini")
+end
+
+function config.write(self, section, value)
+ value = value:gsub("\r\n?", "\n")
+ nixio.fs.writefile("/etc/vlmcsd.ini", value)
+end
+
+return m
diff --git a/package/lean/luci-app-vlmcsd/luasrc/view/vlmcsd/vlmcsd_status.htm b/package/lean/luci-app-vlmcsd/luasrc/view/vlmcsd/vlmcsd_status.htm
new file mode 100644
index 0000000000..d711300e26
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/luasrc/view/vlmcsd/vlmcsd_status.htm
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/package/lean/luci-app-vlmcsd/po/zh-cn/vlmcsd.zh-cn.po b/package/lean/luci-app-vlmcsd/po/zh-cn/vlmcsd.zh-cn.po
new file mode 100644
index 0000000000..eba783c298
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/po/zh-cn/vlmcsd.zh-cn.po
@@ -0,0 +1,38 @@
+msgid ""
+msgstr ""
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgid "vlmcsd config"
+msgstr "KMS 服务器设置"
+
+msgid "KMS Server"
+msgstr "KMS 服务器"
+
+msgid "Basic Setting"
+msgstr "基本设置"
+
+msgid "Config File"
+msgstr "配置文件"
+
+
+msgid "Vlmcsd is running."
+msgstr "KMS 服务器运行中"
+
+msgid "Vlmcsd is not running."
+msgstr "KMS 服务器未运行"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Auto activate"
+msgstr "自动激活局域网客户端"
+
+msgid "configfile"
+msgstr "配置文件"
+
+msgid "This file is /etc/vlmcsd.ini."
+msgstr "这个文件在 /etc/vlmcsd.ini 下,可以增加新的产品主密钥。"
diff --git a/package/lean/luci-app-vlmcsd/root/etc/config/vlmcsd b/package/lean/luci-app-vlmcsd/root/etc/config/vlmcsd
new file mode 100644
index 0000000000..8940d0c0d3
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/root/etc/config/vlmcsd
@@ -0,0 +1,5 @@
+
+config vlmcsd 'config'
+ option autoactivate '1'
+ option enabled '1'
+
diff --git a/package/lean/luci-app-vlmcsd/root/etc/init.d/kms b/package/lean/luci-app-vlmcsd/root/etc/init.d/kms
new file mode 100755
index 0000000000..7878239e14
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/root/etc/init.d/kms
@@ -0,0 +1,56 @@
+#!/bin/sh /etc/rc.common
+# Copyright (c) 2011-2015 OpenWrt.org
+
+START=90
+
+get_config() {
+ config_get_bool enabled $1 enabled 0
+ config_get autoactivate $1 autoactivate 1
+}
+
+add_vlmcs_entry() {
+ local new_hostname="$1"
+
+ uci -q batch <<-EOF >/dev/null
+ add dhcp srvhost
+ set dhcp.@srvhost[-1].srv=_vlmcs._tcp
+ set dhcp.@srvhost[-1].target=$new_hostname
+ set dhcp.@srvhost[-1].port=1688
+ set dhcp.@srvhost[-1].class=0
+ set dhcp.@srvhost[-1].weight=100
+ commit dhcp
+ EOF
+
+ /etc/init.d/dnsmasq restart
+ exit 0
+}
+
+start() {
+ config_load vlmcsd
+ config_foreach get_config vlmcsd
+ [ $enabled -eq 0 ] && exit 0
+ /usr/bin/vlmcsd -i /etc/vlmcsd.ini -L 0.0.0.0:1688
+ echo "KMS Server has started."
+
+ if [ $autoactivate -eq 1 ]; then
+ local HOSTNAME=`uci get system.@system[0].hostname`
+
+ local index=$(uci -q show dhcp |grep "].srv='_vlmcs._tcp'") \
+ || add_vlmcs_entry $HOSTNAME
+ index=${index#*[}
+ index=${index%]*}
+
+ local host_name=$(uci -q get dhcp.@srvhost[$index].target)
+
+ if [ "$HOSTNAME" != "$host_name" ]; then
+ uci delete dhcp.@srvhost[$index]
+ add_vlmcs_entry $HOSTNAME
+ fi
+ fi
+}
+
+stop() {
+ killall -q -9 vlmcsd
+ echo "KMS Server has stopped."
+}
+
diff --git a/package/lean/luci-app-vlmcsd/root/etc/uci-defaults/luci-app-vlmcsd b/package/lean/luci-app-vlmcsd/root/etc/uci-defaults/luci-app-vlmcsd
new file mode 100755
index 0000000000..d28b2ec9f8
--- /dev/null
+++ b/package/lean/luci-app-vlmcsd/root/etc/uci-defaults/luci-app-vlmcsd
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@vlmcsd[-1]
+ add ucitrack vlmcsd
+ set ucitrack.@vlmcsd[-1].init=kms
+ commit ucitrack
+EOF
+
+uci delete firewall.kms
+uci add firewall rule
+uci rename firewall.@rule[-1]="kms"
+uci set firewall.@rule[-1].name="kms"
+uci set firewall.@rule[-1].target="ACCEPT"
+uci set firewall.@rule[-1].src="wan"
+uci set firewall.@rule[-1].proto="tcp"
+uci set firewall.@rule[-1].dest_port="1688"
+uci commit firewall
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-vsftpd/Makefile b/package/lean/luci-app-vsftpd/Makefile
new file mode 100644
index 0000000000..49b71c9e9f
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for VSFTPD
+LUCI_DEPENDS:=+vsftpd-alt
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=3
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-vsftpd/luasrc/controller/vsftpd.lua b/package/lean/luci-app-vsftpd/luasrc/controller/vsftpd.lua
new file mode 100644
index 0000000000..de44711dc5
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/controller/vsftpd.lua
@@ -0,0 +1,47 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+require("luci.sys")
+
+module("luci.controller.vsftpd", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/vsftpd") then
+ return
+ end
+
+ entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
+ entry({"admin", "nas", "vsftpd"},
+ alias("admin", "nas", "vsftpd", "general"),
+ _("FTP Server"))
+
+ entry({"admin", "nas", "vsftpd", "general"},
+ cbi("vsftpd/general"),
+ _("General Settings"), 10).leaf = true
+
+ entry({"admin", "nas", "vsftpd", "users"},
+ cbi("vsftpd/users"),
+ _("Virtual Users"), 20).leaf = true
+
+ entry({"admin", "nas", "vsftpd", "anonymous"},
+ cbi("vsftpd/anonymous"),
+ _("Anonymous User"), 30).leaf = true
+
+ entry({"admin", "nas", "vsftpd", "log"},
+ cbi("vsftpd/log"),
+ _("Log Settings"), 40).leaf = true
+
+ entry({"admin", "nas", "vsftpd", "item"},
+ cbi("vsftpd/item"), nil).leaf = true
+end
diff --git a/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/anonymous.lua b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/anonymous.lua
new file mode 100644
index 0000000000..3ebbb4ba9c
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/anonymous.lua
@@ -0,0 +1,44 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+m = Map("vsftpd", translate("FTP Server - Anonymous Settings"))
+
+sa = m:section(NamedSection, "anonymous", "anonymous", translate("Anonymous Settings"))
+
+o = sa:option(Flag, "enabled", translate("Enabled"))
+o.default = false
+
+o = sa:option(Value, "username", translate("Username"), translate("An actual local user to handle anonymous user"))
+o.default = "ftp"
+
+o = sa:option(Value, "root", translate("Root directory"))
+o.default = "/home/ftp"
+
+o = sa:option(Value, "umask", translate("File mode umask"))
+o.default = "022"
+
+o = sa:option(Value, "maxrate", translate("Max transmit rate"), translate("0 means no limitation"))
+o.default = "0"
+
+o = sa:option(Flag, "writemkdir", translate("Enable write/mkdir"))
+o.default = false
+
+o = sa:option(Flag, "upload", translate("Enable upload"))
+o.default = false
+
+o = sa:option(Flag, "others", translate("Enable other rights"), translate("Include rename, deletion ..."))
+o.default = false
+
+
+return m
diff --git a/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/general.lua b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/general.lua
new file mode 100644
index 0000000000..2de653c798
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/general.lua
@@ -0,0 +1,122 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+m = Map("vsftpd", translate("FTP Server - General Settings"))
+
+sl = m:section(NamedSection, "listen", "listen", translate("Listening Settings"))
+
+o = sl:option(Flag, "enable4", translate("Enable IPv4"))
+o.rmempty = false
+o.default = true
+
+o = sl:option(Value, "ipv4", translate("IPv4 Address"))
+o.datatype = "ip4addr"
+o.default = "0.0.0.0"
+
+o = sl:option(Flag, "enable6", translate("Enable IPv6"))
+o.rmempty = false
+
+o = sl:option(Value, "ipv6", translate("IPv6 Address"))
+o.datatype = "ip6addr"
+o.default = "::"
+
+o = sl:option(Value, "port", translate("Listen Port"))
+o.datatype = "uinteger"
+o.default = "21"
+
+o = sl:option(Value, "dataport", translate("Data Port"))
+o.datatype = "uinteger"
+o.default = "20"
+
+o = sl:option(Value, "pasv_min_port", translate("Pasv Min Port"))
+o.datatype = "uinteger"
+o.default = "50000"
+
+o = sl:option(Value, "pasv_max_port", translate("Pasv Max Port"))
+o.datatype = "uinteger"
+o.default = "51000"
+
+
+sg = m:section(NamedSection, "global", "global", translate("Global Settings"))
+
+o = sg:option(Flag, "write", translate("Enable write"), translate("When disabled, all write request will give permission denied."));
+o.default = true
+
+o = sg:option(Flag, "download", translate("Enable download"), translate("When disabled, all download request will give permission denied."));
+o.default = true
+
+o = sg:option(Flag, "dirlist", translate("Enable directory list"), translate("When disabled, list commands will give permission denied."))
+o.default = true
+
+o = sg:option(Flag, "lsrecurse", translate("Allow directory recursely list"))
+
+o = sg:option(Flag, "dotfile", translate("Show dot files"), translate(". and .. are excluded."));
+o.default = true
+
+o = sg:option(Value, "umask", translate("File mode umask"), translate("Uploaded file mode will be 666 - <umask>; directory mode will be 777 - <umask>."))
+o.default = "022"
+
+o = sg:option(Value, "banner", translate("FTP Banner"))
+
+o = sg:option(Flag, "dirmessage", translate("Enable directory message"), translate("A message will be displayed when entering a directory."))
+
+o = sg:option(Value, "dirmsgfile", translate("Directory message filename"))
+o.default = ".message"
+
+
+sl = m:section(NamedSection, "local", "local", translate("Local Users"))
+
+o = sl:option(Flag, "enabled", translate("Enable local user"))
+o.rmempty = false
+
+o = sl:option(Value, "root", translate("Root directory"), translate("Leave empty will use user's home directory"))
+o.default = ""
+
+
+sc = m:section(NamedSection, "connection", "connection", translate("Connection Settings"))
+
+o = sc:option(Flag, "portmode", translate("Enable PORT mode"))
+o = sc:option(Flag, "pasvmode", translate("Enable PASV mode"))
+
+o = sc:option(ListValue, "ascii", translate("ASCII mode"))
+o:value("disabled", translate("Disabled"))
+o:value("download", translate("Download only"))
+o:value("upload", translate("Upload only"))
+o:value("both", translate("Both download and upload"))
+o.default = "both"
+
+o = sc:option(Value, "idletimeout", translate("Idle session timeout"), translate("in seconds"))
+o.datatype = "uinteger"
+o.default = "1800"
+o = sc:option(Value, "conntimeout", translate("Connection timeout"), translate("in seconds"))
+o.datatype = "uinteger"
+o.default = "120"
+o = sc:option(Value, "dataconntimeout", translate("Data connection timeout"), translate("in seconds"))
+o.datatype = "uinteger"
+o.default = "120"
+o = sc:option(Value, "maxclient", translate("Max clients"), translate("0 means no limitation"))
+o.datatype = "uinteger"
+o.default = "0"
+o = sc:option(Value, "maxperip", translate("Max clients per IP"), translate("0 means no limitation"))
+o.datatype = "uinteger"
+o.default = "0"
+o = sc:option(Value, "maxrate", translate("Max transmit rate"), translate("in KB/s, 0 means no limitation"))
+o.datatype = "uinteger"
+o.default = "0"
+o = sc:option(Value, "maxretry", translate("Max login fail count"), translate("Can not be zero, default is 3"))
+o.datatype = "uinteger"
+o.default = "3"
+
+
+return m
diff --git a/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/item.lua b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/item.lua
new file mode 100644
index 0000000000..2a0003c8bd
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/item.lua
@@ -0,0 +1,70 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+local sid = arg[1]
+local utl = require "luci.util"
+
+m = Map("vsftpd", translate("FTP Server - Virtual User <new>"))
+
+m.redirect = luci.dispatcher.build_url("admin/nas/vsftpd/users")
+
+if m.uci:get("vsftpd", sid) ~= "user" then
+ luci.http.redirect(m.redirect)
+ return
+end
+
+m.uci:foreach("vsftpd", "user",
+ function(s)
+ if s['.name'] == sid and s.username then
+ m.title = translatef("FTP Server - Virtual User %q", s.username)
+ return false
+ end
+ end)
+
+s = m:section(NamedSection, sid, "settings", translate("User Settings"))
+s.addremove = false
+
+o = s:option(Value, "username", translate("Username"))
+o.rmempty = false
+
+function o.validate(self, value)
+ if value == "" then
+ return nil, translate("Username cannot be empty")
+ end
+ return value
+end
+
+o = s:option(Value, "password", translate("Password"))
+o.password = true
+
+o = s:option(Value, "home", translate("Home directory"))
+o.default = "/home/ftp"
+
+o = s:option(Value, "umask", translate("File mode umask"))
+o.default = "022"
+
+o = s:option(Value, "maxrate", translate("Max transmit rate"), translate("0 means no limitation"))
+o.default = "0"
+
+o = s:option(Flag, "writemkdir", translate("Enable write/mkdir"))
+o.default = false
+
+o = s:option(Flag, "upload", translate("Enable upload"))
+o.default = false
+
+o = s:option(Flag, "others", translate("Enable other rights"), translate("Include rename, deletion ..."))
+o.default = false
+
+
+return m
diff --git a/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/log.lua b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/log.lua
new file mode 100644
index 0000000000..969ce817c4
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/log.lua
@@ -0,0 +1,29 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+m = Map("vsftpd", translate("FTP Server - Log Settings"))
+
+sl = m:section(NamedSection, "log", "log", translate("Log Settings"))
+
+o = sl:option(Flag, "syslog", translate("Enable syslog"))
+o.default = false
+
+o = sl:option(Flag, "xreflog", translate("Enable file log"))
+o.default = true
+
+o = sl:option(Value, "file", translate("Log file"))
+o.default = "/var/log/vsftpd.log"
+
+
+return m
diff --git a/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/users.lua b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/users.lua
new file mode 100644
index 0000000000..2ffaf759d5
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/luasrc/model/cbi/vsftpd/users.lua
@@ -0,0 +1,54 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2016 Weijie Gao
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+m = Map("vsftpd", translate("FTP Server - Virtual User Settings"))
+
+sv = m:section(NamedSection, "vuser", "vuser", translate("Settings"))
+
+o = sv:option(Flag, "enabled", translate("Enabled"))
+o.default = false
+
+o = sv:option(Value, "username", translate("Username"), translate("An actual local user to handle virtual users"))
+o.default = "ftp"
+
+s = m:section(TypedSection, "user", translate("User lists"))
+s.template = "cbi/tblsection"
+s.extedit = luci.dispatcher.build_url("admin/nas/vsftpd/item/%s")
+s.addremove = true
+s.anonymous = true
+
+function s.create(...)
+ local id = TypedSection.create(...)
+ luci.http.redirect(s.extedit % id)
+end
+
+function s.remove(self, section)
+ return TypedSection.remove(self, section)
+end
+
+o = s:option(DummyValue, "username", translate("Username"))
+function o.cfgvalue(...)
+ local v = Value.cfgvalue(...) or ("<%s>" % translate("Unknown"))
+ return v
+end
+o.rmempty = false
+
+o = s:option(DummyValue, "home", translate("Home directory"))
+function o.cfgvalue(...)
+ local v = Value.cfgvalue(...) or ("/home/ftp")
+ return v
+end
+o.rmempty = false
+
+return m
diff --git a/package/lean/luci-app-vsftpd/po/zh-cn/vsftpd.po b/package/lean/luci-app-vsftpd/po/zh-cn/vsftpd.po
new file mode 100644
index 0000000000..d77cb43adc
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/po/zh-cn/vsftpd.po
@@ -0,0 +1,236 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-02-17 18:00+0800\n"\
+"Last-Translator: Weijie Gao \n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Pootle 2.0.6\n"
+
+msgid "NAS"
+msgstr "网络存储"
+
+msgid ". and .. are excluded."
+msgstr ". 和 .. 将被忽略。"
+
+msgid "0 means no limitation"
+msgstr "0 表明不限制"
+
+msgid "A message will be displayed when entering a directory."
+msgstr "在进入一个新目录时将显示的消息"
+
+msgid "ASCII mode"
+msgstr "ASCII 模式"
+
+msgid "Allow directory recursely list"
+msgstr "允许递归列目录"
+
+msgid "An actual local user to handle anonymous user"
+msgstr "承载匿名用户的本地用户"
+
+msgid "An actual local user to handle virtual users"
+msgstr "承载虚拟用户的本地用户"
+
+msgid "Anonymous Settings"
+msgstr "匿名用户设置"
+
+msgid "Anonymous User"
+msgstr "匿名用户"
+
+msgid "Both download and upload"
+msgstr "下载和上传"
+
+msgid "Can not be zero, default is 3"
+msgstr "不能为 0,默认为 3"
+
+msgid "Connection Settings"
+msgstr "连接设置"
+
+msgid "Connection timeout"
+msgstr "连接超时"
+
+msgid "Data Port"
+msgstr "数据端口"
+
+msgid "Data connection timeout"
+msgstr "数据连接超时"
+
+msgid "Directory message filename"
+msgstr "目录消息文件名"
+
+msgid "Download only"
+msgstr "仅下载"
+
+msgid "Enable IPv4"
+msgstr "启用 IPv4"
+
+msgid "Enable IPv6"
+msgstr "启用 IPv6"
+
+msgid "Enable PASV mode"
+msgstr "启用 PASV 模式"
+
+msgid "Enable PORT mode"
+msgstr "启用 PORT 模式"
+
+msgid "Enable directory list"
+msgstr "允许列目录"
+
+msgid "Enable directory message"
+msgstr "启用目录消息"
+
+msgid "Enable download"
+msgstr "允许下载"
+
+msgid "Enable file log"
+msgstr "启用文件日志"
+
+msgid "Enable local user"
+msgstr "启用本地用户"
+
+msgid "Enable other rights"
+msgstr "允许其它权限"
+
+msgid "Enable syslog"
+msgstr "启用系统日志"
+
+msgid "Enable upload"
+msgstr "允许上传"
+
+msgid "Enable write"
+msgstr "允许写"
+
+msgid "Enable write/mkdir"
+msgstr "允许写/创建目录"
+
+msgid "FTP Banner"
+msgstr "FTP 欢迎提示"
+
+msgid "FTP Server"
+msgstr "FTP 服务器"
+
+msgid "FTP Server - Anonymous Settings"
+msgstr "FTP 服务器 - 匿名用户设置"
+
+msgid "FTP Server - General Settings"
+msgstr "FTP 服务器 - 常规设置"
+
+msgid "FTP Server - Log Settings"
+msgstr "FTP 服务器 - 日志设置"
+
+msgid "FTP Server - Virtual User %q"
+msgstr "FTP 服务器 - 虚拟用户 %q"
+
+msgid "FTP Server - Virtual User <new>"
+msgstr "FTP 服务器 - 虚拟用户 <新用户>"
+
+msgid "FTP Server - Virtual User Settings"
+msgstr "FTP 服务器 - 虚拟用户设置"
+
+msgid "File mode umask"
+msgstr "文件权限掩码"
+
+msgid "General Settings"
+msgstr "常规设置"
+
+msgid "Global Settings"
+msgstr "全局设置"
+
+msgid "Home directory"
+msgstr "主目录"
+
+msgid "IPv4 Address"
+msgstr "IPv4 地址"
+
+msgid "IPv6 Address"
+msgstr "IPv6 地址"
+
+msgid "Idle session timeout"
+msgstr "空闲回话超时"
+
+msgid "Include rename, deletion ..."
+msgstr "包括重命名、删除 ..."
+
+msgid "Leave empty will use user's home directory"
+msgstr "留空将使用用户主目录"
+
+msgid "Listen Port"
+msgstr "监听端口"
+
+msgid "Listening Settings"
+msgstr "监听设置"
+
+msgid "Local Users"
+msgstr "本地用户"
+
+msgid "Log Settings"
+msgstr "日志设置"
+
+msgid "Log file"
+msgstr "日志文件"
+
+msgid "Max clients"
+msgstr "最大连接数"
+
+msgid "Max clients per IP"
+msgstr "同一 IP 的最大连接数"
+
+msgid "Max login fail count"
+msgstr "最大登录尝试数"
+
+msgid "Max transmit rate"
+msgstr "最大传输速率"
+
+msgid "Root directory"
+msgstr "根目录"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "Show dot files"
+msgstr "显示以点开头的文件 (隐藏文件)"
+
+msgid "Upload only"
+msgstr "仅上传"
+
+msgid "Uploaded file mode will be 666 - <umask>; directory mode will be 777 - <umask>."
+msgstr "上传的文件权限将被设置为 666 - <掩码>;目录权限将被设置为 777 - <掩码>。"
+
+msgid "User Settings"
+msgstr "用户设置"
+
+msgid "User lists"
+msgstr "用户列表"
+
+msgid "Username cannot be empty"
+msgstr "用户名不能为空"
+
+msgid "When disabled, all download request will give permission denied."
+msgstr "如果禁止,所有的下载请求都将被拒绝。"
+
+msgid "When disabled, all write request will give permission denied."
+msgstr "如果禁止,所有的写类型请求都将被拒绝。"
+
+msgid "When disabled, list commands will give permission denied."
+msgstr "如果禁止,列目录命令将被拒绝。"
+
+msgid "Virtual Users"
+msgstr "虚拟用户"
+
+msgid "in seconds"
+msgstr "单位为秒"
+
+msgid "in KB/s, 0 means no limitation"
+msgstr "单位为 KB/s,0 表明不限制"
+
+msgid "Pasv Min Port"
+msgstr "Pasv 模式最小端口"
+
+msgid "Pasv Max Port"
+msgstr "Pasv 模式最大端口"
+
diff --git a/package/lean/luci-app-vsftpd/root/etc/uci-defaults/luci-vsftpd b/package/lean/luci-app-vsftpd/root/etc/uci-defaults/luci-vsftpd
new file mode 100755
index 0000000000..aa2f71a36b
--- /dev/null
+++ b/package/lean/luci-app-vsftpd/root/etc/uci-defaults/luci-vsftpd
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@vsftpd[-1]
+ add ucitrack vsftpd
+ set ucitrack.@vsftpd[-1].init=vsftpd
+ commit ucitrack
+EOF
+
+rm -f /tmp/luci-vsftpd
+exit 0
diff --git a/package/lean/luci-app-webadmin/Makefile b/package/lean/luci-app-webadmin/Makefile
new file mode 100644
index 0000000000..711673fab5
--- /dev/null
+++ b/package/lean/luci-app-webadmin/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI page for Web Admin
+LUCI_DEPENDS:=+uhttpd
+PKG_VERSION:=1.0
+PKG_RELEASE:=3
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-webadmin/luasrc/controller/webadmin.lua b/package/lean/luci-app-webadmin/luasrc/controller/webadmin.lua
new file mode 100644
index 0000000000..5e3d17d4d3
--- /dev/null
+++ b/package/lean/luci-app-webadmin/luasrc/controller/webadmin.lua
@@ -0,0 +1,17 @@
+-- Copyright 2018 lean
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.webadmin", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/uhttpd") then
+ return
+ end
+
+ local page
+
+ page = entry({"admin", "system", "webadmin"}, cbi("webadmin"), _("Web Admin"), 1)
+ page.leaf = true
+
+end
+
diff --git a/package/lean/luci-app-webadmin/luasrc/model/cbi/webadmin.lua b/package/lean/luci-app-webadmin/luasrc/model/cbi/webadmin.lua
new file mode 100644
index 0000000000..e1872d20d7
--- /dev/null
+++ b/package/lean/luci-app-webadmin/luasrc/model/cbi/webadmin.lua
@@ -0,0 +1,50 @@
+-- Copyright 2015 Daniel Dickinson
+-- Licensed to the public under the Apache License 2.0.
+
+local fs = require("nixio.fs")
+
+local m = Map("uhttpd", translate("Web Admin Settings"),
+ translate("Web Admin Settings Page"))
+
+local ucs = m:section(TypedSection, "uhttpd")
+ucs.addremove = false
+ucs.anonymous = true
+
+lhttp = ucs:option(DynamicList, "listen_http", translate("HTTP listeners (address:port)"), translate("Bind to specific interface:port (by specifying interface address"))
+lhttp.datatype = "list(ipaddrport(1))"
+
+function lhttp.validate(self, value, section)
+ local have_https_listener = false
+ local have_http_listener = false
+ if lhttp and lhttp:formvalue(section) and (#(lhttp:formvalue(section)) > 0) then
+ for k, v in pairs(lhttp:formvalue(section)) do
+ if v and (v ~= "") then
+ have_http_listener = true
+ break
+ end
+ end
+ end
+ if lhttps and lhttps:formvalue(section) and (#(lhttps:formvalue(section)) > 0) then
+ for k, v in pairs(lhttps:formvalue(section)) do
+ if v and (v ~= "") then
+ have_https_listener = true
+ break
+ end
+ end
+ end
+ if not (have_http_listener or have_https_listener) then
+ return nil, "must listen on at list one address:port"
+ end
+ return DynamicList.validate(self, value, section)
+end
+
+o = ucs:option(Flag, "redirect_https", translate("Redirect all HTTP to HTTPS"))
+o.default = o.enabled
+o.rmempty = false
+o.description = translate("Redirect all HTTP to HTTPS when SSl cert was installed")
+
+o = ucs:option(Flag, "rfc1918_filter", translate("Ignore private IPs on public interface"), translate("Prevent access from private (RFC1918) IPs on an interface if it has an public IP address"))
+o.default = o.enabled
+o.rmempty = false
+
+return m
diff --git a/package/lean/luci-app-webadmin/po/zh-cn/webadmin.po b/package/lean/luci-app-webadmin/po/zh-cn/webadmin.po
new file mode 100644
index 0000000000..8370b7174b
--- /dev/null
+++ b/package/lean/luci-app-webadmin/po/zh-cn/webadmin.po
@@ -0,0 +1,41 @@
+msgid ""
+msgstr ""
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgid "Web Admin"
+msgstr "Web 管理"
+
+msgid "Web Admin Settings"
+msgstr "Web 管理页面设置"
+
+msgid "Web Admin Settings Page"
+msgstr "设置更改Web管理页面端口,绑定地址,是否允许从互联网访问等"
+
+msgid "HTTP listeners (address:port)""
+msgstr "HTTP 监听 (IP地址:端口)""
+
+msgid "Bind to specific interface:port (by specifying interface address"
+msgstr "绑定到指定的IP地址:端口"
+
+msgid "Redirect all HTTP to HTTPS"
+msgstr "重定向HTTP到HTTPS"
+
+msgid "Redirect all HTTP to HTTPS when SSl cert was installed"
+msgstr "当安装了SSL证书后,重定向HTTP到HTTPS管理页面"
+
+msgid "Auto activate"
+msgstr "自动激活局域网客户端"
+
+msgid "configfile"
+msgstr "配置文件"
+
+msgid "Ignore private IPs on public interface"
+msgstr "只允许内网访问"
+
+msgid "Prevent access from private (RFC1918) IPs on an interface if it has an public IP address"
+msgstr "拒绝从互联网访问Web管理页面"
+
diff --git a/package/lean/luci-app-wrtbwmon/Makefile b/package/lean/luci-app-wrtbwmon/Makefile
new file mode 100644
index 0000000000..412b35fe52
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/Makefile
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Openwrt.org
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for Wrtbwmon
+LUCI_DEPENDS:=+luci-app-nlbwmon
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=7
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js b/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js
new file mode 100644
index 0000000000..36812e723b
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js
@@ -0,0 +1,562 @@
+var wrt = {
+ // variables for auto-update, interval is in seconds
+ scheduleTimeout: undefined,
+ updateTimeout: undefined,
+ isScheduled: true,
+ interval: 5,
+ // option on whether to show per host sub-totals
+ perHostTotals: false,
+ // variables for sorting
+ sortData: {
+ column: 7,
+ elId: 'thTotal',
+ dir: 'desc',
+ cache: {}
+ }
+};
+
+(function () {
+ var oldDate, oldValues = [];
+
+ // find base path
+ var re = /(.*?admin\/nlbw\/[^/]+)/;
+ var basePath = window.location.pathname.match(re)[1];
+
+ //----------------------
+ // HELPER FUNCTIONS
+ //----------------------
+
+ /**
+ * Human readable text for size
+ * @param size
+ * @returns {string}
+ */
+ function getSize(size) {
+ var prefix = [' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z'];
+ var precision, base = 1000, pos = 0;
+ while (size > base) {
+ size /= base;
+ pos++;
+ }
+ if (pos > 2) precision = 1000; else precision = 1;
+ return (Math.round(size * precision) / precision) + ' ' + prefix[pos] + 'B';
+ }
+
+ /**
+ * Human readable text for date
+ * @param date
+ * @returns {string}
+ */
+ function dateToString(date) {
+ return date.toString().substring(0, 24);
+ }
+
+ /**
+ * Gets the string representation of the date received from BE
+ * @param value
+ * @returns {*}
+ */
+ function getDateString(value) {
+ var tmp = value.split('_'),
+ str = tmp[0].split('-').reverse().join('-') + 'T' + tmp[1];
+ return dateToString(new Date(str));
+ }
+
+ /**
+ * Create a `tr` element with content
+ * @param content
+ * @returns {string}
+ */
+ function createTR(content) {
+ var res = '';
+ res += content;
+ res += ' ';
+
+ return res;
+ }
+
+ /**
+ * Create a `th` element with content and options
+ * @param content
+ * @param opts
+ * @returns {string}
+ */
+ function createTH(content, opts) {
+ opts = opts || {};
+ var res = '';
+ res += content;
+ res += ' ';
+ return res;
+ }
+
+ /**
+ * Create a `td` element with content and options
+ * @param content
+ * @param opts
+ * @returns {string}
+ */
+ function createTD(content, opts) {
+ opts = opts || {};
+ var res = '';
+ res += content;
+ res += ' ';
+ return res;
+ }
+
+ /**
+ * Returns true if obj is instance of Array
+ * @param obj
+ * @returns {boolean}
+ */
+ function isArray(obj) {
+ return obj instanceof Array;
+ }
+
+ //----------------------
+ // END HELPER FUNCTIONS
+ //----------------------
+
+ /**
+ * Handle the error that happened during the call to the BE
+ */
+ function handleError() {
+ // TODO handle errors
+ // var message = 'Something went wrong...';
+ }
+
+ /**
+ * Handle the new `values` that were received from the BE
+ * @param values
+ * @returns {string}
+ */
+ function handleValues(values) {
+ if (!isArray(values)) return '';
+
+ // find data and totals
+ var res = parseValues(values);
+ var data = res[0];
+ var totals = res[1];
+
+ // aggregate (sub-total) by hostname (or MAC address) after the global totals are computed, before sort and display
+ aggregateHostTotals(data);
+
+ // store them in cache for quicker re-rendering
+ wrt.sortData.cache.data = data;
+ wrt.sortData.cache.totals = totals;
+
+ renderTableData(data, totals);
+ }
+
+ /**
+ * Renders the table body
+ * @param data
+ * @param totals
+ */
+ function renderTableData(data, totals) {
+ // sort data
+ data.sort(sortingFunction);
+
+ // display data
+ document.getElementById('tableBody').innerHTML = getDisplayData(data, totals);
+
+ // set sorting arrows
+ var el = document.getElementById(wrt.sortData.elId);
+ if (el) {
+ el.innerHTML = el.innerHTML + (wrt.sortData.dir === 'desc' ? '▼' : '▲');
+ }
+
+ // register table events
+ registerTableEventHandlers();
+ }
+
+ /**
+ * Parses the values and returns a data array, where each element in the data array is an array with two elements,
+ * and a totals array, that holds aggregated values for each column.
+ * The first element of each row in the data array, is the HTML output of the row as a `tr` element
+ * and the second is the actual data:
+ * [ result, data ]
+ * @param values The `values` array
+ * @returns {Array}
+ */
+ function parseValues(values) {
+ var data = [], totals = [0, 0, 0, 0, 0];
+ for (var i = 0; i < values.length; i++) {
+ var d = parseValueRow(values[i]);
+ if (d[1]) {
+ data.push(d);
+ // get totals
+ for (var j = 0; j < totals.length; j++) {
+ totals[j] += d[1][3 + j];
+ }
+ }
+ }
+
+ return [data, totals];
+ }
+
+ /**
+ * Parse each row in the `values` array and return an array with two elements.
+ * The first element is the HTML output of the row as a `tr` element and the second is the actual data
+ * [ result, data ]
+ * @param data A row from the `values` array
+ * @returns {[ string, [] ]}
+ */
+ function parseValueRow(data) {
+ // check if data is array
+ if (!isArray(data)) return [''];
+
+ // find old data
+ var oldData;
+ for (var i = 0; i < oldValues.length; i++) {
+ var cur = oldValues[i];
+ // compare mac addresses and ip addresses
+ if (oldValues[i][1] === data[1] && oldValues[i][2] === data[2]) {
+ oldData = cur;
+ break;
+ }
+ }
+
+ // find download and upload speeds
+ var dlSpeed = 0, upSpeed = 0;
+ if (oldData) {
+ var now = new Date(),
+ seconds = (now - oldDate) / 1000;
+ dlSpeed = (data[3] - oldData[3]) / seconds;
+ upSpeed = (data[4] - oldData[4]) / seconds;
+ }
+
+ // create rowData
+ var rowData = [];
+ for (var j = 0; j < data.length; j++) {
+ rowData.push(data[j]);
+ if (j === 2) {
+ rowData.push(dlSpeed, upSpeed);
+ }
+ }
+
+ // create displayData
+ var displayData = [
+ createTD(data[0] + '
' + data[2], {title: data[1]}),
+ createTD(getSize(dlSpeed) + '/s', {right: true}),
+ createTD(getSize(upSpeed) + '/s', {right: true}),
+ createTD(getSize(data[3]), {right: true}),
+ createTD(getSize(data[4]), {right: true}),
+ createTD(getSize(data[5]), {right: true}),
+ createTD(getDateString(data[6])),
+ createTD(getDateString(data[7]))
+ ];
+
+ // display row data
+ var result = '';
+ for (var k = 0; k < displayData.length; k++) {
+ result += displayData[k];
+ }
+ result = createTR(result);
+ return [result, rowData];
+ }
+
+ /**
+ * Creates the HTML output based on the `data` and `totals` inputs
+ * @param data
+ * @param totals
+ * @returns {string} HTML output
+ */
+ function getDisplayData(data, totals) {
+ var result =
+ createTH('客户端', {id: 'thClient'}) +
+ createTH('下载带宽', {id: 'thDownload'}) +
+ createTH('上传带宽', {id: 'thUpload'}) +
+ createTH('总下载流量', {id: 'thTotalDown'}) +
+ createTH('总上传流量', {id: 'thTotalUp'}) +
+ createTH('流量合计', {id: 'thTotal'}) +
+ createTH('首次上线时间', {id: 'thFirstSeen'}) +
+ createTH('最后上线时间', {id: 'thLastSeen'});
+ result = createTR(result);
+ for (var k = 0; k < data.length; k++) {
+ result += data[k][0];
+ }
+ var totalsRow = createTH('总计');
+ for (var m = 0; m < totals.length; m++) {
+ var t = totals[m];
+ totalsRow += createTD(getSize(t) + (m < 2 ? '/s' : ''), {right: true});
+ }
+ result += createTR(totalsRow);
+ return result;
+ }
+
+ /**
+ * Calculates per host sub-totals and adds them in the data input
+ * @param data The data input
+ */
+ function aggregateHostTotals(data) {
+ if (!wrt.perHostTotals) return;
+
+ var curHost = 0, insertAt = 1;
+ while (curHost < data.length && insertAt < data.length) {
+ // grab the current hostname/mac, and walk the data looking for rows with the same host/mac
+ var hostName = data[curHost][1][0].toLowerCase();
+ for (var k = curHost + 1; k < data.length; k++) {
+ if (data[k][1][0].toLowerCase() === hostName) {
+ // this is another row for the same host, group it with any other rows for this host
+ data.splice(insertAt, 0, data.splice(k, 1)[0]);
+ insertAt++;
+ }
+ }
+
+ // if we found more than one row for the host, add a subtotal row
+ if (insertAt > curHost + 1) {
+ var hostTotals = [data[curHost][1][0], '', '', 0, 0, 0, 0, 0];
+ for (var i = curHost; i < insertAt && i < data.length; i++) {
+ for (var j = 3; j < hostTotals.length; j++) {
+ hostTotals[j] += data[i][1][j];
+ }
+ }
+ var hostTotalRow = createTH(data[curHost][1][0] + '
(host total)', {title: data[curHost][1][1]});
+ for (var m = 3; m < hostTotals.length; m++) {
+ var t = hostTotals[m];
+ hostTotalRow += createTD(getSize(t) + (m < 5 ? '/s' : ''), {right: true});
+ }
+ hostTotalRow = createTR(hostTotalRow);
+ data.splice(insertAt, 0, [hostTotalRow, hostTotals]);
+ }
+ curHost = insertAt;
+ insertAt = curHost + 1;
+ }
+ }
+
+ /**
+ * Sorting function used to sort the `data`. Uses the global sort settings
+ * @param x first item to compare
+ * @param y second item to compare
+ * @returns {number} 1 for desc, -1 for asc, 0 for equal
+ */
+ function sortingFunction(x, y) {
+ // get data from global variable
+ var sortColumn = wrt.sortData.column, sortDirection = wrt.sortData.dir;
+ var a = x[1][sortColumn];
+ var b = y[1][sortColumn];
+ if (a === b) {
+ return 0;
+ } else if (sortDirection === 'desc') {
+ return a < b ? 1 : -1;
+ } else {
+ return a > b ? 1 : -1;
+ }
+ }
+
+ /**
+ * Sets the relevant global sort variables and re-renders the table to apply the new sorting
+ * @param elId
+ * @param column
+ */
+ function setSortColumn(elId, column) {
+ if (column === wrt.sortData.column) {
+ // same column clicked, switch direction
+ wrt.sortData.dir = wrt.sortData.dir === 'desc' ? 'asc' : 'desc';
+ } else {
+ // change sort column
+ wrt.sortData.column = column;
+ // reset sort direction
+ wrt.sortData.dir = 'desc';
+ }
+ wrt.sortData.elId = elId;
+
+ // render table data from cache
+ renderTableData(wrt.sortData.cache.data, wrt.sortData.cache.totals);
+ }
+
+ /**
+ * Registers the table events handlers for sorting when clicking the column headers
+ */
+ function registerTableEventHandlers() {
+ // note these ordinals are into the data array, not the table output
+ document.getElementById('thClient').addEventListener('click', function () {
+ setSortColumn(this.id, 0); // hostname
+ });
+ document.getElementById('thDownload').addEventListener('click', function () {
+ setSortColumn(this.id, 3); // dl speed
+ });
+ document.getElementById('thUpload').addEventListener('click', function () {
+ setSortColumn(this.id, 4); // ul speed
+ });
+ document.getElementById('thTotalDown').addEventListener('click', function () {
+ setSortColumn(this.id, 5); // total down
+ });
+ document.getElementById('thTotalUp').addEventListener('click', function () {
+ setSortColumn(this.id, 6); // total up
+ });
+ document.getElementById('thTotal').addEventListener('click', function () {
+ setSortColumn(this.id, 7); // total
+ });
+ }
+
+ /**
+ * Fetches and handles the updated `values` from the BE
+ * @param once If set to true, it re-schedules itself for execution based on selected interval
+ */
+ function receiveData(once) {
+ var ajax = new XMLHttpRequest();
+ ajax.onreadystatechange = function () {
+ // noinspection EqualityComparisonWithCoercionJS
+ if (this.readyState == 4 && this.status == 200) {
+ var re = /(var values = new Array[^;]*;)/,
+ match = ajax.responseText.match(re);
+ if (!match) {
+ handleError();
+ } else {
+ // evaluate values
+ eval(match[1]);
+ //noinspection JSUnresolvedVariable
+ var v = values;
+ if (!v) {
+ handleError();
+ } else {
+ handleValues(v);
+ // set old values
+ oldValues = v;
+ // set old date
+ oldDate = new Date();
+ document.getElementById('updated').innerHTML = '数据更新时间 ' + dateToString(oldDate);
+ }
+ }
+ var int = wrt.interval;
+ if (!once && int > 0) reschedule(int);
+ }
+ };
+ ajax.open('GET', basePath + '/usage_data', true);
+ ajax.send();
+ }
+
+ /**
+ * Registers DOM event listeners for user interaction
+ */
+ function addEventListeners() {
+ document.getElementById('intervalSelect').addEventListener('change', function () {
+ var int = wrt.interval = this.value;
+ if (int > 0) {
+ // it is not scheduled, schedule it
+ if (!wrt.isScheduled) {
+ reschedule(int);
+ }
+ } else {
+ // stop the scheduling
+ stopSchedule();
+ }
+ });
+
+ document.getElementById('resetDatabase').addEventListener('click', function () {
+ if (confirm('This will delete the database file. Are you sure?')) {
+ var ajax = new XMLHttpRequest();
+ ajax.onreadystatechange = function () {
+ // noinspection EqualityComparisonWithCoercionJS
+ if (this.readyState == 4 && this.status == 204) {
+ location.reload();
+ }
+ };
+ ajax.open('GET', basePath + '/usage_reset', true);
+ ajax.send();
+ }
+ });
+
+ document.getElementById('perHostTotals').addEventListener('change', function () {
+ wrt.perHostTotals = !wrt.perHostTotals;
+ });
+ }
+
+ //----------------------
+ // AUTO-UPDATE
+ //----------------------
+
+ /**
+ * Stop auto-update schedule
+ */
+ function stopSchedule() {
+ window.clearTimeout(wrt.scheduleTimeout);
+ window.clearTimeout(wrt.updateTimeout);
+ setUpdateMessage('');
+ wrt.isScheduled = false;
+ }
+
+ /**
+ * Start auto-update schedule
+ * @param seconds
+ */
+ function reschedule(seconds) {
+ wrt.isScheduled = true;
+ seconds = seconds || 60;
+ updateSeconds(seconds);
+ wrt.scheduleTimeout = window.setTimeout(receiveData, seconds * 1000);
+ }
+
+ /**
+ * Sets the text of the `#updating` element
+ * @param msg
+ */
+ function setUpdateMessage(msg) {
+ document.getElementById('updating').innerHTML = msg;
+ }
+
+ /**
+ * Updates the 'Updating in X seconds' message
+ * @param start
+ */
+ function updateSeconds(start) {
+ setUpdateMessage('倒数 ' + start + ' 秒后刷新.');
+ if (start > 0) {
+ wrt.updateTimeout = window.setTimeout(function () {
+ updateSeconds(start - 1);
+ }, 1000);
+ }
+ }
+
+ //----------------------
+ // END AUTO-UPDATE
+ //----------------------
+
+ /**
+ * Check for dependency, and if all is well, run callback
+ * @param cb Callback function
+ */
+ function checkForDependency(cb) {
+ var ajax = new XMLHttpRequest();
+ ajax.onreadystatechange = function () {
+ // noinspection EqualityComparisonWithCoercionJS
+ if (this.readyState == 4 && this.status == 200) {
+ // noinspection EqualityComparisonWithCoercionJS
+ if (ajax.responseText == "1") {
+ cb();
+ } else {
+ alert("wrtbwmon is not installed!");
+ }
+ }
+ };
+ ajax.open('GET', basePath + '/check_dependency', true);
+ ajax.send();
+ }
+
+ checkForDependency(function () {
+ // register events
+ addEventListeners();
+ // Main entry point
+ receiveData();
+ });
+
+})();
diff --git a/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua b/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua
new file mode 100644
index 0000000000..25909c846d
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua
@@ -0,0 +1,43 @@
+module("luci.controller.wrtbwmon", package.seeall)
+
+function index()
+ entry({"admin", "nlbw", "usage"}, alias("admin", "nlbw", "usage", "details"), _("Usage"), 60)
+ entry({"admin", "nlbw", "usage", "details"}, template("wrtbwmon"), _("Details"), 10).leaf=true
+ entry({"admin", "nlbw", "usage", "config"}, cbi("wrtbwmon/config"), _("Configuration"), 20).leaf=true
+ entry({"admin", "nlbw", "usage", "custom"}, form("wrtbwmon/custom"), _("User file"), 30).leaf=true
+ entry({"admin", "nlbw", "usage", "check_dependency"}, call("check_dependency")).dependent=true
+ entry({"admin", "nlbw", "usage", "usage_data"}, call("usage_data")).dependent=true
+ entry({"admin", "nlbw", "usage", "usage_reset"}, call("usage_reset")).dependent=true
+end
+
+function usage_database_path()
+ local cursor = luci.model.uci.cursor()
+ if cursor:get("wrtbwmon", "general", "persist") == "1" then
+ return "/etc/config/usage.db"
+ else
+ return "/tmp/usage.db"
+ end
+end
+
+function check_dependency()
+ local ret = "0"
+ if require("luci.model.ipkg").installed('iptables') then
+ ret = "1"
+ end
+ luci.http.prepare_content("text/plain")
+ luci.http.write(ret)
+end
+
+function usage_data()
+ local db = usage_database_path()
+ local publish_cmd = "wrtbwmon publish " .. db .. " /tmp/usage.htm /etc/config/wrtbwmon.user"
+ local cmd = "wrtbwmon update " .. db .. " && " .. publish_cmd .. " && cat /tmp/usage.htm"
+ luci.http.prepare_content("text/html")
+ luci.http.write(luci.sys.exec(cmd))
+end
+
+function usage_reset()
+ local db = usage_database_path()
+ local ret = luci.sys.call("wrtbwmon update " .. db .. " && rm " .. db)
+ luci.http.status(204)
+end
diff --git a/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua
new file mode 100644
index 0000000000..469d4f8f2c
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua
@@ -0,0 +1,18 @@
+local m = Map("wrtbwmon", translate("Details"))
+
+local s = m:section(NamedSection, "general", "wrtbwmon", translate("General settings"))
+
+local o = s:option(Flag, "persist", translate("Persist database"),
+ translate("Check this to persist the database file"))
+o.rmempty = false
+
+function o.write(self, section, value)
+ if value == '1' then
+ luci.sys.call("mv /tmp/usage.db /etc/config/usage.db")
+ elseif value == '0' then
+ luci.sys.call("mv /etc/config/usage.db /tmp/usage.db")
+ end
+ return Flag.write(self, section ,value)
+end
+
+return m
diff --git a/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua
new file mode 100644
index 0000000000..671879b4cc
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua
@@ -0,0 +1,23 @@
+local USER_FILE_PATH = "/etc/config/wrtbwmon.user"
+
+local fs = require "nixio.fs"
+
+local f = SimpleForm("wrtbwmon",
+ translate("Usage - Custom User File"),
+ translate("This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username"))
+
+local o = f:field(Value, "_custom")
+
+o.template = "cbi/tvalue"
+o.rows = 20
+
+function o.cfgvalue(self, section)
+ return fs.readfile(USER_FILE_PATH)
+end
+
+function o.write(self, section, value)
+ value = value:gsub("\r\n?", "\n")
+ fs.writefile(USER_FILE_PATH, value)
+end
+
+return f
diff --git a/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm b/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
new file mode 100644
index 0000000000..68713b1238
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
@@ -0,0 +1,46 @@
+<%+header%>
+<%=translate("Usage")%>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%=translate("Loading...")%>
+
+
+
+<%+footer%>
diff --git a/package/lean/luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po b/package/lean/luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
new file mode 100644
index 0000000000..3eb050e3f4
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
@@ -0,0 +1,56 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Usage"
+msgstr "实时流量监测"
+
+msgid "Details"
+msgstr "详细信息"
+
+msgid "Configuration"
+msgstr "配置"
+
+msgid "User file"
+msgstr "自定义主机信息"
+
+msgid "Usage - Configuration"
+msgstr "详细设置"
+
+msgid "General settings"
+msgstr "通用设置"
+
+msgid "Persist database"
+msgstr "写入数据库到硬盘"
+
+msgid "Check this to persist the database file"
+msgstr "把统计数据写入 /etc/config 中避免重启或者升级后丢失"
+
+msgid "Usage - Custom User File"
+msgstr "自定义MAC地址对应的主机名"
+
+msgid "This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username"
+msgstr "每一行的格式为 00:aa:bb:cc:ee:ff,username"
+
+msgid "Reset Database"
+msgstr "重置数据库"
+
+msgid "Auto Refresh Interval"
+msgstr "自动刷新间隔"
+
+msgid "Disabled"
+msgstr "禁用"
+
+msgid "Second"
+msgstr "秒"
+
+msgid "Seconds"
+msgstr "秒"
+
+msgid "Minutes"
+msgstr "分钟"
+
+msgid "Per-host Totals"
+msgstr "合并每一客户端数据"
+
+msgid "Loading..."
+msgstr "加载中..."
diff --git a/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon
new file mode 100644
index 0000000000..419270dbab
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon
@@ -0,0 +1,4 @@
+
+config wrtbwmon 'general'
+ option persist '0'
+
diff --git a/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon
new file mode 100755
index 0000000000..d57c09ae1d
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon
@@ -0,0 +1,22 @@
+#!/bin/sh /etc/rc.common
+#
+# start/stop wrtbwmon bandwidth monitor
+
+### BEGIN INIT INFO
+# Provides: wrtbwmon
+# Required-Start: $network $local_fs $remote_fs
+# Required-Stop: $local_fs $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: iptables-based bandwidth monitor
+### END INIT INFO
+
+START=91
+
+start(){
+ /usr/sbin/wrtbwmon setup /tmp/usage.db
+}
+
+stop(){
+ /usr/sbin/wrtbwmon remove
+}
diff --git a/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
new file mode 100755
index 0000000000..bcb5b337f3
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete firewall.wrtbwmon
+ set firewall.wrtbwmon=include
+ set firewall.wrtbwmon.type=script
+ set firewall.wrtbwmon.path='/etc/wrtbwmon.include'
+ set firewall.wrtbwmon.reload=1
+ commit firewall
+EOF
+
+/etc/init.d/wrtbwmon enable
+/etc/init.d/wrtbwmon start
+exit 0
diff --git a/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include b/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include
new file mode 100755
index 0000000000..39bcf43a10
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include
@@ -0,0 +1 @@
+/etc/init.d/wrtbwmon restart >/dev/null 2>&1
diff --git a/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk b/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk
new file mode 100755
index 0000000000..fe67e4ae8b
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk
@@ -0,0 +1,157 @@
+#!/usr/bin/awk
+
+function inInterfaces(host){
+ return(interfaces ~ "(^| )"host"($| )")
+}
+
+function newRule(arp_ip,
+ ipt_cmd){
+ # checking for existing rules shouldn't be necessary if newRule is
+ # always called after db is read, arp table is read, and existing
+ # iptables rules are read.
+ ipt_cmd="iptables -t mangle -j RETURN -s " arp_ip
+ system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
+ ipt_cmd="iptables -t mangle -j RETURN -d " arp_ip
+ system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
+}
+
+function total(i){
+ return(bw[i "/in"] + bw[i "/out"])
+}
+
+function date( cmd, d){
+ cmd="date +%d-%m-%Y_%H:%M:%S"
+ cmd | getline d
+ close(cmd)
+ #!@todo could start a process with "while true; do date ...; done"
+ return(d)
+}
+
+BEGIN {
+ od=""
+ fid=1
+ debug=0
+ rrd=0
+}
+
+/^#/ { # get DB filename
+ FS=","
+ dbFile=FILENAME
+ next
+}
+
+# data from database; first file
+FNR==NR { #!@todo this doesn't help if the DB file is empty.
+ if($2 == "NA")
+ #!@todo could get interface IP here
+ n=$1
+ else
+ n=$2
+
+ hosts[n] = "" # add this host/interface to hosts
+ mac[n] = $1
+ ip[n] = $2
+ inter[n] = $3
+ bw[n "/in"] = $4
+ bw[n "/out"] = $5
+ firstDate[n] = $7
+ lastDate[n] = $8
+ next
+}
+
+# not triggered on the first file
+FNR==1 {
+ FS=" "
+ fid++ #!@todo use fid for all files; may be problematic for empty files
+ next
+}
+
+# arp: ip hw flags hw_addr mask device
+fid==2 {
+ #!@todo regex match IPs and MACs for sanity
+ arp_ip = $1
+ arp_flags = $3
+ arp_mac = $4
+ arp_dev = $6
+ if(arp_flags != "0x0" && !(arp_ip in ip)){
+ if(debug)
+ print "new host:", arp_ip, arp_flags > "/dev/stderr"
+ hosts[arp_ip] = ""
+ mac[arp_ip] = arp_mac
+ ip[arp_ip] = arp_ip
+ inter[arp_ip] = arp_dev
+ bw[arp_ip "/in"] = bw[arp_ip "/out"] = 0
+ firstDate[arp_ip] = lastDate[arp_ip] = date()
+ }
+ next
+}
+
+#!@todo could use mangle chain totals or tailing "unnact" rules to
+# account for data for new hosts from their first presence on the
+# network to rule creation. The "unnact" rules would have to be
+# maintained at the end of the list, and new rules would be inserted
+# at the top.
+
+# skip line
+# read the chain name and deal with the data accordingly
+fid==3 && $1 == "Chain"{
+ rrd=$2 ~ /RRDIPT_.*/
+ next
+}
+
+fid==3 && rrd && (NF < 9 || $1=="pkts"){ next }
+
+fid==3 && rrd { # iptables input
+ if($6 != "*"){
+ m=$6
+ n=m "/out"
+ } else if($7 != "*"){
+ m=$7
+ n=m "/in"
+ } else if($8 != "0.0.0.0/0"){
+ m=$8
+ n=m "/out"
+ } else { # $9 != "0.0.0.0/0"
+ m=$9
+ n=m "/in"
+ }
+
+ # remove host from array; any hosts left in array at END get new
+ # iptables rules
+
+ #!@todo this deletes a host if any rule exists; if only one
+ # directional rule is removed, this will not remedy the situation
+ delete hosts[m]
+
+ if($2 > 0){ # counted some bytes
+ if(mode == "diff" || mode == "noUpdate")
+ print n, $2
+ if(mode!="noUpdate"){
+ if(inInterfaces(m)){ # if label is an interface
+ if(!(m in mac)){ # if label was not in db (also not in
+ # arp table, but interfaces won't be
+ # there anyway)
+ firstDate[m] = date()
+ mac[m] = inter[m] = m
+ ip[m] = "NA"
+ bw[m "/in"]=bw[m "/out"]= 0
+ }
+ }
+ bw[n]+=$2
+ lastDate[m] = date()
+ }
+ }
+}
+
+END {
+ if(mode=="noUpdate") exit
+ close(dbFile)
+ system("rm -f " dbFile)
+ print "#mac,ip,iface,in,out,total,first_date,last_date" > dbFile
+ OFS=","
+ for(i in mac)
+ print mac[i], ip[i], inter[i], bw[i "/in"], bw[i "/out"], total(i), firstDate[i], lastDate[i] > dbFile
+ close(dbFile)
+ # for hosts without rules
+ for(host in hosts) if(!inInterfaces(host)) newRule(host)
+}
diff --git a/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon
new file mode 100755
index 0000000000..b2c0b9a07a
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon
@@ -0,0 +1,301 @@
+#!/bin/sh
+#
+# wrtbwmon: traffic logging tool for routers
+#
+# Peter Bailey (peter.eldridge.bailey+wrtbwmon AT gmail.com)
+#
+# Based on work by:
+# Emmanuel Brucy (e.brucy AT qut.edu.au)
+# Fredrik Erlandsson (erlis AT linux.nu)
+# twist - http://wiki.openwrt.org/RrdTrafficWatch
+
+trap "rm -f /tmp/*_$$.tmp; kill $$" INT
+binDir=/usr/sbin
+dataDir=/usr/share/wrtbwmon
+lockDir=/tmp/wrtbwmon.lock
+pidFile=$lockDir/pid
+networkFuncs=/lib/functions/network.sh
+uci=`which uci 2>/dev/null`
+nslookup=`which nslookup 2>/dev/null`
+nvram=`which nvram 2>/dev/null`
+
+chains='INPUT OUTPUT FORWARD'
+DEBUG=
+interfaces='eth0 tun0' # in addition to detected WAN
+DB=$2
+mode=
+
+# DNS server for reverse lookups provided in "DNS".
+# don't perform reverse DNS lookups by default
+DO_RDNS=${DNS-}
+
+header="#mac,ip,iface,in,out,total,first_date,last_date"
+
+createDbIfMissing()
+{
+ [ ! -f "$DB" ] && echo $header > "$DB"
+}
+
+checkDbArg()
+{
+ [ -z "$DB" ] && echo "ERROR: Missing argument 2 (database file)" && exit 1
+}
+
+checkDB()
+{
+ [ ! -f "$DB" ] && echo "ERROR: $DB does not exist" && exit 1
+ [ ! -w "$DB" ] && echo "ERROR: $DB is not writable" && exit 1
+}
+
+checkWAN()
+{
+ [ -z "$wan" ] && echo "Warning: failed to detect WAN interface."
+}
+
+lookup()
+{
+ MAC=$1
+ IP=$2
+ userDB=$3
+ for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do
+ [ -e "$USERSFILE" ] || continue
+ case $USERSFILE in
+ /tmp/dhcp.leases )
+ USER=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ')
+ ;;
+ /etc/hosts )
+ USER=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ')
+ ;;
+ * )
+ USER=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,)
+ ;;
+ esac
+ [ "$USER" = "*" ] && USER=
+ [ -n "$USER" ] && break
+ done
+ if [ -n "$DO_RDNS" -a -z "$USER" -a "$IP" != "NA" -a -n "$nslookup" ]; then
+ USER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'`
+ fi
+ [ -z "$USER" ] && USER=${MAC}
+ echo $USER
+}
+
+detectIF()
+{
+ if [ -f "$networkFuncs" ]; then
+ IF=`. $networkFuncs; network_get_device netdev $1; echo $netdev`
+ [ -n "$IF" ] && echo $IF && return
+ fi
+
+ if [ -n "$uci" -a -x "$uci" ]; then
+ IF=`$uci get network.${1}.ifname 2>/dev/null`
+ [ $? -eq 0 -a -n "$IF" ] && echo $IF && return
+ fi
+
+ if [ -n "$nvram" -a -x "$nvram" ]; then
+ IF=`$nvram get ${1}_ifname 2>/dev/null`
+ [ $? -eq 0 -a -n "$IF" ] && echo $IF && return
+ fi
+}
+
+detectLAN()
+{
+ [ -e /sys/class/net/br-lan ] && echo br-lan && return
+ lan=$(detectIF lan)
+ [ -n "$lan" ] && echo $lan && return
+}
+
+detectWAN()
+{
+ [ -n "$WAN_IF" ] && echo $WAN_IF && return
+ wan=$(detectIF wan)
+ [ -n "$wan" ] && echo $wan && return
+ wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\1/')
+ [ -n "$wan" ] && echo $wan && return
+ [ -f "$networkFuncs" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan)
+ [ -n "$wan" ] && echo $wan && return
+}
+
+lock()
+{
+ attempts=0
+ while [ $attempts -lt 10 ]; do
+ mkdir $lockDir 2>/dev/null && break
+ attempts=$((attempts+1))
+ pid=`cat $pidFile 2>/dev/null`
+ if [ -n "$pid" ]; then
+ if [ -d "/proc/$pid" ]; then
+ [ -n "$DEBUG" ] && echo "WARNING: Lockfile detected but process $(cat $pidFile) does not exist !"
+ rm -rf $lockDir
+ else
+ sleep 1
+ fi
+ fi
+ done
+ mkdir $lockDir 2>/dev/null
+ echo $$ > $pidFile
+ [ -n "$DEBUG" ] && echo $$ "got lock after $attempts attempts"
+ trap '' INT
+}
+
+unlock()
+{
+ rm -rf $lockDir
+ [ -n "$DEBUG" ] && echo $$ "released lock"
+ trap "rm -f /tmp/*_$$.tmp; kill $$" INT
+}
+
+# chain
+newChain()
+{
+ chain=$1
+ # Create the RRDIPT_$chain chain (it doesn't matter if it already exists).
+ iptables -t mangle -N RRDIPT_$chain 2> /dev/null
+
+ # Add the RRDIPT_$chain CHAIN to the $chain chain if not present
+ iptables -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null
+ if [ $? -ne 0 ]; then
+ [ -n "$DEBUG" ] && echo "DEBUG: iptables chain misplaced, recreating it..."
+ iptables -t mangle -I $chain -j RRDIPT_$chain
+ fi
+}
+
+# chain tun
+newRuleIF()
+{
+ chain=$1
+ IF=$2
+
+ #!@todo test
+ if [ "$chain" = "OUTPUT" ]; then
+ cmd="iptables -t mangle -o $IF -j RETURN"
+ eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain"
+ elif [ "$chain" = "INPUT" ]; then
+ cmd="iptables -t mangle -i $IF -j RETURN"
+ eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain"
+ fi
+}
+
+update()
+{
+ #!@todo could let readDB.awk handle this; that would place header
+ #!info in fewer places
+ createDbIfMissing
+
+ checkDB
+ checkWAN
+
+ > /tmp/iptables_$$.tmp
+ lock
+ # only zero our own chains
+ for chain in $chains; do
+ iptables -nvxL RRDIPT_$chain -t mangle -Z >> /tmp/iptables_$$.tmp
+ done
+ # the iptables and readDB commands have to be separate. Otherwise,
+ # they will fight over iptables locks
+ awk -v mode="$mode" -v interfaces=\""$interfaces"\" -f $binDir/readDB.awk \
+ $DB \
+ /proc/net/arp \
+ /tmp/iptables_$$.tmp
+ unlock
+}
+
+############################################################
+
+case $1 in
+ "dump" )
+ checkDbArg
+ lock
+ tr ',' '\t' < "$DB"
+ unlock
+ ;;
+
+ "update" )
+ checkDbArg
+ wan=$(detectWAN)
+ interfaces="$interfaces $wan"
+ update
+ rm -f /tmp/*_$$.tmp
+ exit
+ ;;
+
+ "publish" )
+ checkDbArg
+ [ -z "$3" ] && echo "ERROR: Missing argument 3 (output html file)" && exit 1
+
+ # sort DB
+ lock
+
+ # busybox sort truncates numbers to 32 bits
+ grep -v '^#' $DB | awk -F, '{OFS=","; a=sprintf("%f",$4/1e6); $4=""; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=",";$1=sprintf("%f",$1*1e6);print}' > /tmp/sorted_$$.tmp
+
+ # create HTML page
+ rm -f $3.tmp
+ cp $dataDir/usage.htm1 $3.tmp
+
+ #!@todo fix publishing
+ while IFS=, read PEAKUSAGE_IN MAC IP IFACE PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN
+ do
+ echo "
+new Array(\"$(lookup $MAC $IP $4)\",\"$MAC\",\"$IP\",
+$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\"$FIRSTSEEN\",\"$LASTSEEN\")," >> $3.tmp
+ done < /tmp/sorted_$$.tmp
+ echo "0);" >> $3.tmp
+
+ sed "s/(date)/`date`/" < $dataDir/usage.htm2 >> $3.tmp
+ mv $3.tmp $3
+
+ unlock
+
+ #Free some memory
+ rm -f /tmp/*_$$.tmp
+ ;;
+
+ "setup" )
+ checkDbArg
+ [ -w "$DB" ] && echo "Warning: using existing $DB"
+ createDbIfMissing
+
+ for chain in $chains; do
+ newChain $chain
+ done
+
+ #lan=$(detectLAN)
+ wan=$(detectWAN)
+ checkWAN
+ interfaces="$interfaces $wan"
+
+ # track local data
+ for chain in INPUT OUTPUT; do
+ for interface in $interfaces; do
+ [ -n "$interface" ] && [ -e "/sys/class/net/$interface" ] && newRuleIF $chain $interface
+ done
+ done
+
+ # this will add rules for hosts in arp table
+ update
+
+ rm -f /tmp/*_$$.tmp
+ ;;
+
+ "remove" )
+ iptables-save | grep -v RRDIPT | iptables-restore
+ rm -rf "$lockDir"
+ ;;
+
+ *)
+ echo \
+"Usage: $0 {setup|update|publish|remove} [options...]
+Options:
+ $0 setup database_file
+ $0 update database_file
+ $0 publish database_file path_of_html_report [user_file]
+Examples:
+ $0 setup /tmp/usage.db
+ $0 update /tmp/usage.db
+ $0 publish /tmp/usage.db /www/user/usage.htm /jffs/users.txt
+ $0 remove
+Note: [user_file] is an optional file to match users with MAC addresses.
+ Its format is \"00:MA:CA:DD:RE:SS,username\", with one entry per line."
+ ;;
+esac
diff --git a/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1 b/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1
new file mode 100644
index 0000000000..1f0c342a7c
--- /dev/null
+++ b/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1
@@ -0,0 +1,23 @@
+Traffic
+
+Total Usage:
+
+
+User
+Download
+Upload
+Total
+First seen
+Last seen
+
+
+
This page was generated on (date)
+
diff --git a/package/lean/luci-app-xlnetacc/Makefile b/package/lean/luci-app-xlnetacc/Makefile
new file mode 100644
index 0000000000..f60c4e4868
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/Makefile
@@ -0,0 +1,69 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-xlnetacc
+PKG_VERSION:=1.0.3
+PKG_RELEASE:=9
+
+PKG_LICENSE:=GPLv2
+PKG_MAINTAINER:=Sense
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/$(PKG_NAME)
+ SECTION:=luci
+ CATEGORY:=LuCI
+ SUBMENU:=3. Applications
+ TITLE:=LuCI Support for XLNetAcc
+ PKGARCH:=all
+ DEPENDS:=+jshn +wget +openssl-util
+endef
+
+define Package/$(PKG_NAME)/description
+ LuCI Support for XLNetAcc.
+endef
+
+define Build/Prepare
+ $(foreach po,$(wildcard ${CURDIR}/files/luci/i18n/*.po), \
+ po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/$(PKG_NAME)/postinst
+#!/bin/sh
+if [ -z "$${IPKG_INSTROOT}" ]; then
+ ( . /etc/uci-defaults/luci-xlnetacc ) && rm -f /etc/uci-defaults/luci-xlnetacc
+fi
+exit 0
+endef
+
+define Package/$(PKG_NAME)/conffiles
+ /etc/config/xlnetacc
+endef
+
+define Package/$(PKG_NAME)/install
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
+ $(INSTALL_DATA) $(PKG_BUILD_DIR)/xlnetacc.*.lmo $(1)/usr/lib/lua/luci/i18n/
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
+ $(INSTALL_DATA) ./files/luci/controller/*.lua $(1)/usr/lib/lua/luci/controller/
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
+ $(INSTALL_DATA) ./files/luci/model/cbi/*.lua $(1)/usr/lib/lua/luci/model/cbi/
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/xlnetacc
+ $(INSTALL_DATA) ./files/luci/view/xlnetacc/*.htm $(1)/usr/lib/lua/luci/view/xlnetacc/
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_CONF) ./files/root/etc/config/xlnetacc $(1)/etc/config/xlnetacc
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/root/etc/init.d/xlnetacc $(1)/etc/init.d/xlnetacc
+ $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
+ $(INSTALL_BIN) ./files/root/etc/hotplug.d/iface/95-xlnetacc $(1)/etc/hotplug.d/iface/95-xlnetacc
+ $(INSTALL_DIR) $(1)/etc/uci-defaults
+ $(INSTALL_BIN) ./files/root/etc/uci-defaults/luci-xlnetacc $(1)/etc/uci-defaults/luci-xlnetacc
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) ./files/root/usr/bin/xlnetacc.sh $(1)/usr/bin/xlnetacc.sh
+endef
+
+$(eval $(call BuildPackage,$(PKG_NAME)))
diff --git a/package/lean/luci-app-xlnetacc/files/luci/controller/xlnetacc.lua b/package/lean/luci-app-xlnetacc/files/luci/controller/xlnetacc.lua
new file mode 100644
index 0000000000..ab9d929473
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/luci/controller/xlnetacc.lua
@@ -0,0 +1,47 @@
+module("luci.controller.xlnetacc", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/xlnetacc") then
+ return
+ end
+
+ entry({"admin", "services", "xlnetacc"},
+ firstchild(), _("XLNetAcc")).dependent = false
+
+ entry({"admin", "services", "xlnetacc", "general"},
+ cbi("xlnetacc"), _("Settings"), 1)
+
+ entry({"admin", "services", "xlnetacc", "log"},
+ template("xlnetacc/logview"), _("Log"), 2)
+
+ entry({"admin", "services", "xlnetacc", "status"}, call("action_status"))
+ entry({"admin", "services", "xlnetacc", "logdata"}, call("action_log"))
+end
+
+local function is_running()
+ return luci.sys.call("(ps | grep xlnetacc.sh | grep -v 'grep') >/dev/null" ) == 0
+end
+
+function action_status()
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({
+ run_state = is_running(),
+ down_state = nixio.fs.readfile("/var/state/xlnetacc_down_state") or "",
+ up_state = nixio.fs.readfile("/var/state/xlnetacc_up_state") or ""
+ })
+end
+
+function action_log()
+ local uci = require "luci.model.uci".cursor()
+ local util = require "luci.util"
+ local log_data = { }
+
+ log_data.syslog = util.trim(util.exec("logread | grep xlnetacc"))
+ if uci:get("xlnetacc", "general", "logging") ~= "0" then
+ log_data.client = nixio.fs.readfile("/var/log/xlnetacc.log") or ""
+ end
+ uci:unload("xlnetacc")
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(log_data)
+end
diff --git a/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.lmo b/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.lmo
new file mode 100644
index 0000000000..3c07641419
Binary files /dev/null and b/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.lmo differ
diff --git a/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.po b/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.po
new file mode 100644
index 0000000000..80c6b9d23b
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/luci/i18n/xlnetacc.zh-cn.po
@@ -0,0 +1,92 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "XLNetAcc"
+msgstr "迅雷快鸟"
+
+msgid "XLNetAcc is a Thunder joint broadband operators launched a commitment to help users solve the low broadband, slow Internet access, poor Internet experience of professional-grade broadband upgrade software."
+msgstr "迅雷快鸟是迅雷联合宽带运营商推出的一款致力于帮助用户解决宽带低、网速慢、上网体验差的专业级宽带加速软件。"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "Log"
+msgstr "日志"
+
+msgid "Running Status"
+msgstr "运行状态"
+
+msgid "XLNetAcc Running Status"
+msgstr "快鸟运行状态"
+
+msgid "DownLink Upgrade Status"
+msgstr "下行提速状态"
+
+msgid "UpLink Upgrade Status"
+msgstr "上行提速状态"
+
+msgid "RUNNING"
+msgstr "运行中"
+
+msgid "NOT RUNNING"
+msgstr "未运行"
+
+msgid "No upgrade information"
+msgstr "暂无提速信息"
+
+msgid "General Settings"
+msgstr "基本设置"
+
+msgid "Enabled"
+msgstr "启用"
+
+msgid "Enable DownLink Upgrade"
+msgstr "开启下行提速"
+
+msgid "Enable UpLink Upgrade"
+msgstr "开启上行提速"
+
+msgid "Enable Logging"
+msgstr "启用日志记录"
+
+msgid "Enable verbose logging"
+msgstr "启用详细日志"
+
+msgid "Upgrade interface"
+msgstr "指定提速接口"
+
+msgid "XLNetAcc account"
+msgstr "迅雷快鸟帐号"
+
+msgid "XLNetAcc password"
+msgstr "迅雷快鸟密码"
+
+msgid "Does not store the plaintext password, automatically emptied after start."
+msgstr "不存储明文密码,启动后自动清空。"
+
+msgid "Encrypted password"
+msgstr "加密后的密码"
+
+msgid "Auto-generate in accordance with the plaintext password, do not modify it!"
+msgstr "根据明文密码自动生成,请勿修改!"
+
+msgid "Log Data"
+msgstr "日志数据"
+
+msgid "Loading..."
+msgstr "正在加载..."
+
+msgid "Refresh every 5 seconds."
+msgstr "每 5 秒刷新。"
+
+msgid "syslog:"
+msgstr "系统日志:"
+
+msgid "log file:"
+msgstr "日志文件:"
+
+msgid "No log data."
+msgstr "无日志数据。"
+
+msgid "Error get log data."
+msgstr "获取日志数据失败。"
diff --git a/package/lean/luci-app-xlnetacc/files/luci/model/cbi/xlnetacc.lua b/package/lean/luci-app-xlnetacc/files/luci/model/cbi/xlnetacc.lua
new file mode 100644
index 0000000000..9bef249ce9
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/luci/model/cbi/xlnetacc.lua
@@ -0,0 +1,36 @@
+local m, s, o
+local uci = luci.model.uci.cursor()
+
+m = Map("xlnetacc", "%s - %s" %{translate("XLNetAcc"), translate("Settings")}, translate("XLNetAcc is a Thunder joint broadband operators launched a commitment to help users solve the low broadband, slow Internet access, poor Internet experience of professional-grade broadband upgrade software."))
+m:append(Template("xlnetacc/status"))
+
+s = m:section(NamedSection, "general", "general", translate("General Settings"))
+s.anonymous = true
+s.addremove = false
+
+o = s:option(Flag, "enabled", translate("Enabled"))
+o.rmempty = false
+
+o = s:option(Flag, "down_acc", translate("Enable DownLink Upgrade"))
+
+o = s:option(Flag, "up_acc", translate("Enable UpLink Upgrade"))
+
+o = s:option(Flag, "logging", translate("Enable Logging"))
+o.default = "1"
+
+o = s:option(Flag, "verbose", translate("Enable verbose logging"))
+o:depends("logging", "1")
+
+o = s:option(ListValue, "network", translate("Upgrade interface"))
+uci:foreach("network", "interface", function(section)
+ if section[".name"] ~= "loopback" then
+ o:value(section[".name"])
+ end
+end)
+
+o = s:option(Value, "account", translate("XLNetAcc account"))
+
+o = s:option(Value, "password", translate("XLNetAcc password"))
+o.password = true
+
+return m
diff --git a/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/logview.htm b/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/logview.htm
new file mode 100644
index 0000000000..a07a27112f
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/logview.htm
@@ -0,0 +1,49 @@
+<% css = [[
+ #log_text {
+ padding: 10px;
+ text-align: left;
+ height: 500px;
+ overflow: auto;
+ }
+ #log_text pre {
+ word-break: break-all;
+ margin: 0;
+ }
+ .description {
+ color: #ffffff;
+ background-color: #0099ff;
+ }
+]]
+%>
+
+<%+header%>
+
+
+ <%:XLNetAcc%> - <%:Log Data%>
+
+
+
+
+
+
+<%+footer%>
diff --git a/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/status.htm b/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/status.htm
new file mode 100644
index 0000000000..3da9c4422a
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/luci/view/xlnetacc/status.htm
@@ -0,0 +1,21 @@
+
+
+
+ <%:XLNetAcc Running Status%> <%:Collecting data...%>
+ <%:DownLink Upgrade Status%> <%:Collecting data...%>
+ <%:UpLink Upgrade Status%> <%:Collecting data...%>
+
+
+
+
diff --git a/package/lean/luci-app-xlnetacc/files/root/etc/config/xlnetacc b/package/lean/luci-app-xlnetacc/files/root/etc/config/xlnetacc
new file mode 100644
index 0000000000..4345070c4c
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/root/etc/config/xlnetacc
@@ -0,0 +1,5 @@
+
+config general 'general'
+ option enabled '0'
+ option network 'wan'
+
diff --git a/package/lean/luci-app-xlnetacc/files/root/etc/hotplug.d/iface/95-xlnetacc b/package/lean/luci-app-xlnetacc/files/root/etc/hotplug.d/iface/95-xlnetacc
new file mode 100755
index 0000000000..be0271ad8f
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/root/etc/hotplug.d/iface/95-xlnetacc
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+/etc/init.d/xlnetacc enabled || exit 0
+[ "$INTERFACE" != "$(uci get xlnetacc.general.network)" ] && exit 0
+
+case "$ACTION" in
+ ifup)
+ /etc/init.d/xlnetacc start
+ ;;
+ ifdown)
+ /etc/init.d/xlnetacc stop
+ ;;
+esac
diff --git a/package/lean/luci-app-xlnetacc/files/root/etc/init.d/xlnetacc b/package/lean/luci-app-xlnetacc/files/root/etc/init.d/xlnetacc
new file mode 100755
index 0000000000..d156281136
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/root/etc/init.d/xlnetacc
@@ -0,0 +1,49 @@
+#!/bin/sh /etc/rc.common
+
+START=95
+STOP=10
+SERVICE_DAEMONIZE=1
+
+NAME=xlnetacc
+
+start() {
+ local retry=1
+ while pidof "${NAME}.sh" >/dev/null 2>&1; do
+ [ $retry -ge 10 ] && return 1 || let retry++
+ sleep 1
+ done
+
+ config_load "$NAME"
+ config_get_bool enabled "general" "enabled" 0
+ config_get_bool down_acc "general" "down_acc" 0
+ config_get_bool up_acc "general" "up_acc" 0
+ config_get network "general" "network"
+ config_get username "general" "account"
+ config_get password "general" "password"
+ ( [ $enabled -eq 0 ] || [ $down_acc -eq 0 -a $up_acc -eq 0 ] || [ -z "$username" -o -z "$password" -o -z "$network" ] ) && return 2
+
+ logger -p "daemon.notice" -t "$NAME" "XLNetAcc is starting ..."
+ service_start /usr/bin/${NAME}.sh --start
+}
+
+stop() {
+ ps | grep xlnetacc.sh | grep -v 'grep' >/dev/null 2>&1 || return 1
+
+ local pid spid
+ for pid in $(ps | grep xlnetacc.sh | grep -v 'grep' | awk '{print $1}'); do
+ echo "Stop XLNetAcc process PID: $pid"
+ kill -9 $pid >/dev/null 2>&1
+ for spid in $(pgrep -P $pid "sleep"); do
+ echo "Stop XLNetAcc process SPID: $spid"
+ kill -9 $spid >/dev/null 2>&1
+ done
+ done
+ logger -p "daemon.notice" -t "$NAME" "XLNetAcc has stoped."
+ return 0
+}
+
+restart() {
+ rm -rf /tmp/state/xlnetacc*
+ stop && sleep 1
+ start
+}
diff --git a/package/lean/luci-app-xlnetacc/files/root/etc/uci-defaults/luci-xlnetacc b/package/lean/luci-app-xlnetacc/files/root/etc/uci-defaults/luci-xlnetacc
new file mode 100755
index 0000000000..632c026789
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/root/etc/uci-defaults/luci-xlnetacc
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@xlnetacc[-1]
+ add ucitrack xlnetacc
+ set ucitrack.@xlnetacc[-1].init=xlnetacc
+ commit ucitrack
+EOF
+
+general=$(uci -q get xlnetacc.@general[-1])
+if [ -z "$general" ]; then
+ uci -q add xlnetacc general
+fi
+if [ "$general"x != "general"x ]; then
+ uci -q batch <<-EOF >/dev/null
+ rename xlnetacc.@general[-1]="general"
+ commit xlnetacc
+ EOF
+fi
+
+rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
+exit 0
diff --git a/package/lean/luci-app-xlnetacc/files/root/usr/bin/xlnetacc.sh b/package/lean/luci-app-xlnetacc/files/root/usr/bin/xlnetacc.sh
new file mode 100755
index 0000000000..57fac717aa
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/files/root/usr/bin/xlnetacc.sh
@@ -0,0 +1,670 @@
+#!/bin/sh
+
+# 声明常量
+readonly packageName='com.xunlei.vip.swjsq'
+readonly protocolVersion=200
+readonly businessType=68
+readonly sdkVersion='2.1.1.177662'
+readonly clientVersion='2.4.1.3'
+readonly agent_xl="android-async-http/xl-acc-sdk/version-$sdkVersion"
+readonly agent_down='okhttp/3.4.1'
+readonly agent_up='android-async-http/xl-acc-sdk/version-1.0.0.1'
+readonly client_type_down='android-swjsq'
+readonly client_type_up='android-uplink'
+
+# 声明全局变量
+_bind_ip=
+_http_cmd=
+_peerid=
+_devicesign=
+_userid=
+_loginkey=
+_sessionid=
+_portal_down=
+_portal_up=
+_dial_account=
+access_url=
+http_args=
+user_agent=
+link_cn=
+lasterr=
+sequence_xl=1000000
+sequence_down=$(( $(date +%s) / 6 ))
+sequence_up=$sequence_down
+
+# 包含用于解析 JSON 格式返回值的函数
+. /usr/share/libubox/jshn.sh
+
+# 读取 UCI 设置相关函数
+uci_get_by_name() {
+ local ret=$(uci get $NAME.$1.$2 2> /dev/null)
+ echo -n ${ret:=$3}
+}
+uci_get_by_type() {
+ local ret=$(uci get $NAME.@$1[-1].$2 2> /dev/null)
+ echo -n ${ret:=$3}
+}
+uci_get_by_bool() {
+ case $(uci_get_by_name "$1" "$2" "$3") in
+ 1|on|true|yes|enabled) echo -n 1;;
+ *) echo -n 0;;
+ esac
+}
+
+# 日志和状态栏输出。1 日志文件, 2 系统日志, 4 详细模式, 8 下行状态栏, 16 上行状态栏, 32 失败状态
+_log() {
+ local msg=$1
+ local flag=$2
+ [ -z "$msg" ] && return
+ [ -z "$flag" ] && flag=1
+ local timestamp=$(date +'%Y/%m/%d %H:%M:%S')
+
+ [ $logging -eq 0 -a $(( $flag & 1 )) -ne 0 ] && flag=$(( $flag ^ 1 ))
+ if [ $verbose -eq 0 -a $(( $flag & 4 )) -ne 0 ]; then
+ [ $(( $flag & 1 )) -ne 0 ] && flag=$(( $flag ^ 1 ))
+ [ $(( $flag & 2 )) -ne 0 ] && flag=$(( $flag ^ 2 ))
+ fi
+ if [ $down_acc -eq 0 -a $(( $flag & 8 )) -ne 0 ]; then
+ flag=$(( $flag ^ 8 ))
+ [ $up_acc -ne 0 ] && flag=$(( $flag | 16 ))
+ fi
+ if [ $up_acc -eq 0 -a $(( $flag & 16 )) -ne 0 ]; then
+ flag=$(( $flag ^ 16 ))
+ [ $down_acc -ne 0 ] && flag=$(( $flag | 8 ))
+ fi
+
+ [ $(( $flag & 1 )) -ne 0 ] && echo "$timestamp $msg" >> $LOGFILE 2> /dev/null
+ [ $(( $flag & 2 )) -ne 0 ] && logger -p "daemon.info" -t "$NAME" "$msg"
+
+ [ $(( $flag & 32 )) -eq 0 ] && local color="green" || local color="red"
+ [ $(( $flag & 8 )) -ne 0 ] && echo -n "$timestamp $msg" > $down_state_file 2> /dev/null
+ [ $(( $flag & 16 )) -ne 0 ] && echo -n "$timestamp $msg" > $up_state_file 2> /dev/null
+}
+
+# 清理日志
+clean_log() {
+ [ $logging -eq 1 -a -f "$LOGFILE" ] || return
+ [ $(wc -l "$LOGFILE" | awk '{print $1}') -le 800 ] && return
+ _log "清理日志文件"
+ local logdata=$(tail -n 500 "$LOGFILE")
+ echo "$logdata" > $LOGFILE 2> /dev/null
+ unset logdata
+}
+
+# 获取接口IP地址
+get_bind_ip() {
+ json_cleanup; json_load "$(ubus call network.interface.$network status 2> /dev/null)" >/dev/null 2>&1
+ json_select "ipv4-address" >/dev/null 2>&1; json_select 1 >/dev/null 2>&1
+ json_get_var _bind_ip "address"
+ if [ -z "$_bind_ip" -o "$_bind_ip"x == "0.0.0.0"x ]; then
+ _log "获取网络 $network IP地址失败"
+ return 1
+ else
+ _log "绑定IP地址: $_bind_ip"
+ return 0
+ fi
+}
+
+# 定义基本 HTTP 命令和参数
+gen_http_cmd() {
+ _http_cmd="wget-ssl -nv -t 1 -T 5 -O - --no-check-certificate"
+ _http_cmd="$_http_cmd --bind-address=$_bind_ip"
+}
+
+# 生成设备标识
+gen_device_sign() {
+ local ifname macaddr
+ while : ; do
+ ifname=$(uci get "network.$network.ifname" 2> /dev/null)
+ [ "${ifname:0:1}" == "@" ] && network="${ifname:1}" || break
+ done
+ [ -z "$ifname" ] && { _log "获取网络 $network 信息出错"; return; }
+ json_cleanup; json_load "$(ubus call network.device status {\"name\":\"$ifname\"} 2> /dev/null)" >/dev/null 2>&1
+ json_get_var macaddr "macaddr"
+ [ -z "$macaddr" ] && { _log "获取网络 $network MAC地址出错"; return; }
+ macaddr=$(echo -n "$macaddr" | awk '{print toupper($0)}')
+
+ # 计算peerID
+ local fake_peerid=$(awk -F- '{print toupper($5)}' '/proc/sys/kernel/random/uuid')
+ readonly _peerid="${fake_peerid}004V"
+ _log "_peerid is $_peerid" $(( 1 | 4 ))
+
+ # 计算devicesign
+ # sign = div.10?.device_id + md5(sha1(packageName + businessType + md5(a protocolVersion specific GUID)))
+ local fake_device_id=$(echo -n "${macaddr//:/}" | openssl dgst -md5 | awk '{print $2}')
+ local fake_device_sign=$(echo -n "${fake_device_id}${packageName}${businessType}c7f21687eed3cdb400ca11fc2263c998" \
+ | openssl dgst -sha1 | awk '{print $2}')
+ readonly _devicesign="div101.${fake_device_id}"$(echo -n "$fake_device_sign" | openssl dgst -md5 | awk '{print $2}')
+ _log "_devicesign is $_devicesign" $(( 1 | 4 ))
+}
+
+# 快鸟帐号通用参数
+swjsq_json() {
+ let sequence_xl++
+ # 生成POST数据
+ json_init
+ json_add_string protocolVersion "$protocolVersion"
+ json_add_string sequenceNo "$sequence_xl"
+ json_add_string platformVersion '2'
+ json_add_string isCompressed '0'
+ json_add_string businessType "$businessType"
+ json_add_string clientVersion "$clientVersion"
+ json_add_string peerID "$_peerid"
+ json_add_string appName "ANDROID-$packageName"
+ json_add_string sdkVersion "${sdkVersion##*.}"
+ json_add_string devicesign "$_devicesign"
+ json_add_string deviceModel 'MI'
+ json_add_string deviceName 'Xiaomi Mi'
+ json_add_string OSVersion "7.1.1"
+}
+
+# 帐号登录
+swjsq_login() {
+ swjsq_json
+ if [ -z "$_userid" -o -z "$_loginkey" ]; then
+ access_url='https://mobile-login.xunlei.com/login'
+ json_add_string userName "$username"
+ json_add_string passWord "$password"
+ json_add_string verifyKey
+ json_add_string verifyCode
+ else
+ access_url='https://mobile-login.xunlei.com/loginkey'
+ json_add_string userName "$_userid"
+ json_add_string loginKey "$_loginkey"
+ fi
+ json_close_object
+
+ local ret=$($_http_cmd --user-agent="$agent_xl" "$access_url" --post-data="$(json_dump)")
+ case $? in
+ 0)
+ _log "login is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errorCode"
+ ;;
+ 2) lasterr=-2;;
+ 4) lasterr=-3;;
+ *) lasterr=-1;;
+ esac
+
+ case ${lasterr:=-1} in
+ 0)
+ json_get_var _userid "userID"
+ json_get_var _loginkey "loginKey"
+ json_get_var _sessionid "sessionID"
+ _log "_sessionid is $_sessionid" $(( 1 | 4 ))
+ local outmsg="帐号登录成功"; _log "$outmsg" $(( 1 | 8 ))
+ ;;
+ 15) # 身份信息已失效
+ _userid=; _loginkey=;;
+ -1)
+ local outmsg="帐号登录失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
+ -2)
+ local outmsg="Wget 参数解析错误,请更新 GNU Wget"; _log "$outmsg" $(( 1 | 8 | 32 ));;
+ -3)
+ local outmsg="Wget 网络通信失败,请稍候"; _log "$outmsg";;
+ *)
+ local errorDesc; json_get_var errorDesc "errorDesc"
+ local outmsg="帐号登录失败。错误代码: ${lasterr}"; \
+ [ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 帐号注销
+swjsq_logout() {
+ swjsq_json
+ json_add_string userID "$_userid"
+ json_add_string sessionID "$_sessionid"
+ json_close_object
+
+ local ret=$($_http_cmd --user-agent="$agent_xl" 'https://mobile-login.xunlei.com/logout' --post-data="$(json_dump)")
+ _log "logout is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errorCode"
+
+ case ${lasterr:=-1} in
+ 0)
+ _sessionid=
+ local outmsg="帐号注销成功"; _log "$outmsg" $(( 1 | 8 ));;
+ -1)
+ local outmsg="帐号注销失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local errorDesc; json_get_var errorDesc "errorDesc"
+ local outmsg="帐号注销失败。错误代码: ${lasterr}"; \
+ [ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 获取用户信息
+swjsq_getuserinfo() {
+ [ $1 -eq 1 ] && local _vasid=14 || local _vasid=33
+ swjsq_json
+ json_add_string userID "$_userid"
+ json_add_string sessionID "$_sessionid"
+ json_add_string vasid "$_vasid"
+ json_close_object
+
+ local ret=$($_http_cmd --user-agent="$agent_xl" 'https://mobile-login.xunlei.com/getuserinfo' --post-data="$(json_dump)")
+ _log "getuserinfo $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errorCode"
+
+ [ $1 -eq 1 ] && local outmsg="下行提速会员" || local outmsg="上行提速会员"
+ case ${lasterr:=-1} in
+ 0)
+ local index vasid isVip isYear expireDate can_upgrade
+ json_select "vipList" >/dev/null 2>&1
+ while : ; do
+ json_select ${index:=1} >/dev/null 2>&1
+ [ $? -ne 0 ] && break
+ json_get_var vasid "vasid"
+ json_get_var isVip "isVip"
+ json_get_var isYear "isYear"
+ json_get_var expireDate "expireDate"
+ json_select ".." >/dev/null 2>&1
+ let index++
+ ([ $1 -eq 1 -a ${vasid:-0} -eq 2 ] || [ ${vasid:-0} -eq $_vasid ]) && \
+ [ ${isVip:-0} -eq 1 -o ${isYear:-0} -eq 1 ] && { can_upgrade=1; break; }
+ done
+ if [ ${can_upgrade:-0} -eq 1 ]; then
+ outmsg="获取${outmsg}信息成功。会员到期时间:${expireDate:0:4}-${expireDate:4:2}-${expireDate:6:2}"; \
+ _log "$outmsg" $(( 1 | $1 * 8 ))
+ else
+ if [ ${#expireDate} -ge 8 ]; then
+ outmsg="${outmsg}已到期。会员到期时间:${expireDate:0:4}-${expireDate:4:2}-${expireDate:6:2}"
+ else
+ outmsg="${outmsg}无效"
+ fi
+ _log "$outmsg" $(( 1 | $1 * 8 | 32 ))
+ [ $1 -eq 1 ] && down_acc=0 || up_acc=0
+ fi
+ ;;
+ -1)
+ outmsg="获取${outmsg}信息失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local errorDesc; json_get_var errorDesc "errorDesc"
+ outmsg="获取${outmsg}信息失败。错误代码: ${lasterr}"; \
+ [ -n "$errorDesc" ] && outmsg="${outmsg},原因: $errorDesc"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 获取提速入口
+swjsq_portal() {
+ xlnetacc_var $1
+
+ [ $1 -eq 1 ] && access_url='http://api.portal.swjsq.vip.xunlei.com:81/v2/queryportal' || \
+ access_url='http://api.upportal.swjsq.vip.xunlei.com/v2/queryportal'
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url")
+ _log "portal $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ case ${lasterr:=-1} in
+ 0)
+ local interface_ip interface_port province sp
+ json_get_var interface_ip "interface_ip"
+ json_get_var interface_port "interface_port"
+ json_get_var province "province_name"
+ json_get_var sp "sp_name"
+ if [ $1 -eq 1 ]; then
+ _portal_down="http://$interface_ip:$interface_port/v2"
+ _log "_portal_down is $_portal_down" $(( 1 | 4 ))
+ else
+ _portal_up="http://$interface_ip:$interface_port/v2"
+ _log "_portal_up is $_portal_up" $(( 1 | 4 ))
+ fi
+ local outmsg="获取${link_cn}提速入口成功"; \
+ [ -n "$province" -a -n "$sp" ] && outmsg="${outmsg}。运营商:${province}${sp}"; _log "$outmsg" $(( 1 | $1 * 8 ))
+ ;;
+ -1)
+ local outmsg="获取${link_cn}提速入口失败。迅雷服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local message; json_get_var message "message"
+ local outmsg="获取${link_cn}提速入口失败。错误代码: ${lasterr}"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 获取网络带宽信息
+isp_bandwidth() {
+ xlnetacc_var $1
+
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/bandwidth?${http_args%&dial_account=*}")
+ _log "bandwidth $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ case ${lasterr:=-1} in
+ 0)
+ # 获取带宽数据
+ local can_upgrade bind_dial_account dial_account stream cur_bandwidth max_bandwidth
+ [ $1 -eq 1 ] && stream="downstream" || stream="upstream"
+ json_get_var can_upgrade "can_upgrade"
+ json_get_var bind_dial_account "bind_dial_account"
+ json_get_var dial_account "dial_account"
+ json_select; json_select "bandwidth" >/dev/null 2>&1
+ json_get_var cur_bandwidth "$stream"
+ json_select; json_select "max_bandwidth" >/dev/null 2>&1
+ json_get_var max_bandwidth "$stream"
+ json_select
+ cur_bandwidth=$(expr ${cur_bandwidth:-0} / 1024)
+ max_bandwidth=$(expr ${max_bandwidth:-0} / 1024)
+
+ if [ -n "$bind_dial_account" -a "$bind_dial_account" != "$dial_account" ]; then
+ local outmsg="绑定宽带账号 $bind_dial_account 与当前宽带账号 $dial_account 不一致,请联系迅雷客服解绑(每月仅一次)"; \
+ _log "$outmsg" $(( 1 | 8 | 32 ))
+ down_acc=0; up_acc=0
+ elif [ $can_upgrade -eq 0 ]; then
+ local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
+ local outmsg="${link_cn}无法提速"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ))
+ [ $1 -eq 1 ] && down_acc=0 || up_acc=0
+ elif [ $cur_bandwidth -ge $max_bandwidth ]; then
+ local outmsg="${link_cn}无需提速。当前带宽 ${cur_bandwidth}M,超过最大可提升带宽 ${max_bandwidth}M"; \
+ _log "$outmsg" $(( 1 | $1 * 8 ))
+ [ $1 -eq 1 ] && down_acc=0 || up_acc=0
+ else
+ if [ -z "$_dial_account" -a -n "$dial_account" ]; then
+ _dial_account=$dial_account
+ _log "_dial_account is $_dial_account" $(( 1 | 4 ))
+ fi
+ local outmsg="${link_cn}可以提速。当前带宽 ${cur_bandwidth}M,可提升至 ${max_bandwidth}M"; _log "$outmsg" $(( 1 | $1 * 8 ))
+ fi
+ ;;
+ 724) # 724 账号存在异常
+ lasterr=-2
+ local outmsg="获取${link_cn}网络带宽信息失败。原因: 您的账号存在异常,请联系迅雷客服反馈"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ 3103) # 3103 线路暂不支持
+ lasterr=0
+ local province sp
+ json_get_var province "province_name"; json_get_var sp "sp_name"
+ local outmsg="${link_cn}无法提速。原因: ${province}${sp}线路暂不支持"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ))
+ [ $1 -eq 1 ] && down_acc=0 || up_acc=0
+ ;;
+ -1)
+ local outmsg="获取${link_cn}网络带宽信息失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
+ local outmsg="获取${link_cn}网络带宽信息失败。错误代码: ${lasterr}"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 发送带宽提速信号
+isp_upgrade() {
+ xlnetacc_var $1
+
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/upgrade?$http_args")
+ _log "upgrade $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ case ${lasterr:=-1} in
+ 0)
+ local bandwidth
+ json_select "bandwidth" >/dev/null 2>&1
+ json_get_var bandwidth "downstream"
+ [ ${bandwidth:=0} -ge 1024 ] && bandwidth=$(( $bandwidth / 1024 ))
+ local outmsg="${link_cn}提速成功,带宽已提升到 ${bandwidth}M"; _log "$outmsg" $(( 1 | $1 * 8 ))
+ [ $1 -eq 1 ] && down_acc=2 || up_acc=2
+ ;;
+ 812) # 812 已处于提速状态
+ lasterr=0
+ local outmsg="${link_cn}提速成功,当前宽带已处于提速状态"; _log "$outmsg" $(( 1 | $1 * 8 ))
+ [ $1 -eq 1 ] && down_acc=2 || up_acc=2
+ ;;
+ 724) # 724 账号存在异常
+ lasterr=-2
+ local outmsg="${link_cn}提速失败。原因: 您的账号存在异常,请联系迅雷客服反馈"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ -1)
+ local outmsg="${link_cn}提速失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
+ local outmsg="${link_cn}提速失败。错误代码: ${lasterr}"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 发送提速心跳信号
+isp_keepalive() {
+ xlnetacc_var $1
+
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/keepalive?$http_args")
+ _log "keepalive $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ case ${lasterr:=-1} in
+ 0)
+ local outmsg="${link_cn}心跳信号返回正常"; _log "$outmsg";;
+ 513) # 513 提速通道不存在
+ lasterr=-2
+ local outmsg="${link_cn}提速超时,提速通道不存在"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ -1)
+ local outmsg="${link_cn}心跳信号发送失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
+ local outmsg="${link_cn}提速失效。错误代码: ${lasterr}"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 发送带宽恢复信号
+isp_recover() {
+ xlnetacc_var $1
+
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/recover?$http_args")
+ _log "recover $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ case ${lasterr:=-1} in
+ 0)
+ local outmsg="${link_cn}带宽已恢复"; _log "$outmsg" $(( 1 | $1 * 8 ))
+ [ $1 -eq 1 ] && down_acc=1 || up_acc=1;;
+ -1)
+ local outmsg="${link_cn}带宽恢复失败。运营商服务器未响应,请稍候"; _log "$outmsg";;
+ *)
+ local message; json_get_var message "richmessage"; [ -z "$message" ] && json_get_var message "message"
+ local outmsg="${link_cn}带宽恢复失败。错误代码: ${lasterr}"; \
+ [ -n "$message" ] && outmsg="${outmsg},原因: $message"; _log "$outmsg" $(( 1 | $1 * 8 | 32 ));;
+ esac
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 查询提速信息,未使用
+isp_query() {
+ xlnetacc_var $1
+
+ local ret=$($_http_cmd --user-agent="$user_agent" "$access_url/query_try_info?$http_args")
+ _log "query_try_info $1 is $ret" $(( 1 | 4 ))
+ json_cleanup; json_load "$ret" >/dev/null 2>&1
+ json_get_var lasterr "errno"
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 设置参数变量
+xlnetacc_var() {
+ if [ $1 -eq 1 ]; then
+ let sequence_down++
+ access_url=$_portal_down
+ http_args="sequence=${sequence_down}&client_type=${client_type_down}-${clientVersion}&client_version=${client_type_down//-/}-${clientVersion}&chanel=umeng-10900011&time_and=$(date +%s)000"
+ user_agent=$agent_down
+ link_cn="下行"
+ else
+ let sequence_up++
+ access_url=$_portal_up
+ http_args="sequence=${sequence_up}&client_type=${client_type_up}-${clientVersion}&client_version=${client_type_up//-/}-${clientVersion}"
+ user_agent=$agent_up
+ link_cn="上行"
+ fi
+ http_args="${http_args}&peerid=${_peerid}&userid=${_userid}&sessionid=${_sessionid}&user_type=1&os=android-7.1.1"
+ [ -n "$_dial_account" ] && http_args="${http_args}&dial_account=${_dial_account}"
+}
+
+# 重试循环
+xlnetacc_retry() {
+ if [ $# -ge 3 -a $3 -ne 0 ]; then
+ [ $2 -eq 1 -a $down_acc -ne $3 ] && return 0
+ [ $2 -eq 2 -a $up_acc -ne $3 ] && return 0
+ fi
+
+ local retry=1
+ while : ; do
+ lasterr=
+ eval $1 $2 && break # 成功
+ [ $# -ge 4 -a $retry -ge $4 ] && break || let retry++ # 重试超时
+ case $lasterr in
+ -1) sleep 5s;; # 服务器未响应
+ -2) break;; # 严重错误
+ *) sleep 3s;; # 其它错误
+ esac
+ done
+
+ [ ${lasterr:-0} -eq 0 ] && return 0 || return 1
+}
+
+# 注销已登录帐号
+xlnetacc_logout() {
+ [ -z "$_sessionid" ] && return 2
+ [ $# -ge 1 ] && local retry=$1 || local retry=1
+
+ xlnetacc_retry 'isp_recover' 1 2 $retry
+ xlnetacc_retry 'isp_recover' 2 2 $retry
+ xlnetacc_retry 'swjsq_logout' 0 0 $retry
+ [ $down_acc -ne 0 ] && down_acc=1; [ $up_acc -ne 0 ] && up_acc=1
+ _sessionid=; _dial_account=
+
+ [ $lasterr -eq 0 ] && return 0 || return 1
+}
+
+# 中止信号处理
+sigterm() {
+ _log "trap sigterm, exit" $(( 1 | 4 ))
+ xlnetacc_logout
+ rm -f "$down_state_file" "$up_state_file"
+ exit 0
+}
+
+# 初始化
+xlnetacc_init() {
+ [ "$1" != "--start" ] && return 1
+
+ # 防止重复启动
+ local pid
+ for pid in $(pidof "${0##*/}"); do
+ [ $pid -ne $$ ] && return 1
+ done
+
+ # 读取设置
+ readonly NAME=xlnetacc
+ readonly LOGFILE=/var/log/${NAME}.log
+ readonly down_state_file=/var/state/${NAME}_down_state
+ readonly up_state_file=/var/state/${NAME}_up_state
+ down_acc=$(uci_get_by_bool "general" "down_acc" 0)
+ up_acc=$(uci_get_by_bool "general" "up_acc" 0)
+ readonly logging=$(uci_get_by_bool "general" "logging" 1)
+ readonly verbose=$(uci_get_by_bool "general" "verbose" 0)
+ network=$(uci_get_by_name "general" "network" "wan")
+ readonly username=$(uci_get_by_name "general" "account")
+ readonly password=$(uci_get_by_name "general" "password")
+ local enabled=$(uci_get_by_bool "general" "enabled" 0)
+ ([ $enabled -eq 0 ] || [ $down_acc -eq 0 -a $up_acc -eq 0 ] || [ -z "$username" -o -z "$password" -o -z "$network" ]) && return 2
+
+ [ $logging -eq 1 ] && [ ! -d /var/log ] && mkdir -p /var/log
+ [ -f "$LOGFILE" ] && _log "------------------------------"
+ _log "迅雷快鸟正在启动..."
+
+ # 检查外部调用工具
+ command -v wget-ssl >/dev/null || { _log "GNU Wget 未安装"; return 3; }
+ local opensslchk=$(echo -n 'openssl' | openssl dgst -sha1 | awk '{print $2}')
+ [ "$opensslchk" != 'c898fa1e7226427010e329971e82c669f8d8abb4' ] && { _log "openssl-util 未安装或计算错误"; return 3; }
+
+ # 捕获中止信号
+ trap 'sigterm' INT # Ctrl-C
+ trap 'sigterm' QUIT # Ctrl-\
+ trap 'sigterm' TERM # kill
+
+ # 生成设备标识
+ gen_device_sign
+ [ ${#_peerid} -ne 16 -o ${#_devicesign} -ne 71 ] && return 4
+
+ clean_log
+ [ -d /var/state ] || mkdir -p /var/state
+ return 0
+}
+
+# 程序主体
+xlnetacc_main() {
+ while : ; do
+ # 获取外网IP地址
+ xlnetacc_retry 'get_bind_ip'
+ gen_http_cmd
+
+ # 注销快鸟帐号
+ xlnetacc_logout 3 && sleep 3s
+
+ # 登录快鸟帐号
+ while : ; do
+ lasterr=
+ swjsq_login
+ case $lasterr in
+ 0) break;; # 登录成功
+ -1) sleep 5s;; # 服务器未响应
+ -2) return 7;; # Wget 参数解析错误
+ -3) sleep 3s;; # Wget 网络通信失败
+ 6) sleep 130m;; # 需要输入验证码
+ 8) sleep 3m;; # 服务器系统维护
+ 15) sleep 1s;; # 身份信息已失效
+ *) return 5;; # 登录失败
+ esac
+ done
+
+ # 获取用户信息
+ xlnetacc_retry 'swjsq_getuserinfo' 1 1
+ xlnetacc_retry 'swjsq_getuserinfo' 2 1
+ [ $down_acc -eq 0 -a $up_acc -eq 0 ] && break
+ # 获取提速入口
+ xlnetacc_retry 'swjsq_portal' 1 1
+ xlnetacc_retry 'swjsq_portal' 2 1
+ # 获取带宽信息
+ xlnetacc_retry 'isp_bandwidth' 1 1 10 || { sleep 3m; continue; }
+ xlnetacc_retry 'isp_bandwidth' 2 1 10 || { sleep 3m; continue; }
+ [ $down_acc -eq 0 -a $up_acc -eq 0 ] && break
+ # 带宽提速
+ xlnetacc_retry 'isp_upgrade' 1 1 10 || { sleep 3m; continue; }
+ xlnetacc_retry 'isp_upgrade' 2 1 10 || { sleep 3m; continue; }
+
+ # 心跳保持
+# local retry=1
+ while : ; do
+ clean_log # 清理日志
+ sleep 10m
+# [ $retry -ge 144 ] && break || let retry++ # 心跳最多保持24小时,144=24*60/10
+ xlnetacc_retry 'isp_keepalive' 1 2 5 || break
+ xlnetacc_retry 'isp_keepalive' 2 2 5 || break
+ done
+ done
+ xlnetacc_logout
+ _log "无法提速,迅雷快鸟已停止。"
+ return 6
+}
+
+# 程序入口
+xlnetacc_init "$@" && xlnetacc_main
+exit $?
diff --git a/package/lean/luci-app-xlnetacc/tools/po2lmo/Makefile b/package/lean/luci-app-xlnetacc/tools/po2lmo/Makefile
new file mode 100644
index 0000000000..ad2c133207
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/tools/po2lmo/Makefile
@@ -0,0 +1,12 @@
+
+INSTALL = install
+PREFIX = /usr/bin
+
+po2lmo: src/po2lmo.o src/template_lmo.o
+ $(CC) $(LDFLAGS) -o src/po2lmo src/po2lmo.o src/template_lmo.o
+
+install:
+ $(INSTALL) -m 755 src/po2lmo $(PREFIX)
+
+clean:
+ $(RM) src/po2lmo src/*.o
diff --git a/package/lean/luci-app-xlnetacc/tools/po2lmo/src/po2lmo.c b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/po2lmo.c
new file mode 100644
index 0000000000..0da792b680
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/po2lmo.c
@@ -0,0 +1,247 @@
+/*
+ * lmo - Lua Machine Objects - PO to LMO conversion tool
+ *
+ * Copyright (C) 2009-2012 Jo-Philipp Wich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "template_lmo.h"
+
+static void die(const char *msg)
+{
+ fprintf(stderr, "Error: %s\n", msg);
+ exit(1);
+}
+
+static void usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
+ exit(1);
+}
+
+static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ if( fwrite(ptr, size, nmemb, stream) == 0 )
+ die("Failed to write stdout");
+}
+
+static int extract_string(const char *src, char *dest, int len)
+{
+ int pos = 0;
+ int esc = 0;
+ int off = -1;
+
+ for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
+ {
+ if( (off == -1) && (src[pos] == '"') )
+ {
+ off = pos + 1;
+ }
+ else if( off >= 0 )
+ {
+ if( esc == 1 )
+ {
+ switch (src[pos])
+ {
+ case '"':
+ case '\\':
+ off++;
+ break;
+ }
+ dest[pos-off] = src[pos];
+ esc = 0;
+ }
+ else if( src[pos] == '\\' )
+ {
+ dest[pos-off] = src[pos];
+ esc = 1;
+ }
+ else if( src[pos] != '"' )
+ {
+ dest[pos-off] = src[pos];
+ }
+ else
+ {
+ dest[pos-off] = '\0';
+ break;
+ }
+ }
+ }
+
+ return (off > -1) ? strlen(dest) : -1;
+}
+
+static int cmp_index(const void *a, const void *b)
+{
+ uint32_t x = ((const lmo_entry_t *)a)->key_id;
+ uint32_t y = ((const lmo_entry_t *)b)->key_id;
+
+ if (x < y)
+ return -1;
+ else if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static void print_uint32(uint32_t x, FILE *out)
+{
+ uint32_t y = htonl(x);
+ print(&y, sizeof(uint32_t), 1, out);
+}
+
+static void print_index(void *array, int n, FILE *out)
+{
+ lmo_entry_t *e;
+
+ qsort(array, n, sizeof(*e), cmp_index);
+
+ for (e = array; n > 0; n--, e++)
+ {
+ print_uint32(e->key_id, out);
+ print_uint32(e->val_id, out);
+ print_uint32(e->offset, out);
+ print_uint32(e->length, out);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char line[4096];
+ char key[4096];
+ char val[4096];
+ char tmp[4096];
+ int state = 0;
+ int offset = 0;
+ int length = 0;
+ int n_entries = 0;
+ void *array = NULL;
+ lmo_entry_t *entry = NULL;
+ uint32_t key_id, val_id;
+
+ FILE *in;
+ FILE *out;
+
+ if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
+ usage(argv[0]);
+
+ memset(line, 0, sizeof(key));
+ memset(key, 0, sizeof(val));
+ memset(val, 0, sizeof(val));
+
+ while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
+ {
+ if( state == 0 && strstr(line, "msgid \"") == line )
+ {
+ switch(extract_string(line, key, sizeof(key)))
+ {
+ case -1:
+ die("Syntax error in msgid");
+ case 0:
+ state = 1;
+ break;
+ default:
+ state = 2;
+ }
+ }
+ else if( state == 1 || state == 2 )
+ {
+ if( strstr(line, "msgstr \"") == line || state == 2 )
+ {
+ switch(extract_string(line, val, sizeof(val)))
+ {
+ case -1:
+ state = 4;
+ break;
+ default:
+ state = 3;
+ }
+ }
+ else
+ {
+ switch(extract_string(line, tmp, sizeof(tmp)))
+ {
+ case -1:
+ state = 2;
+ break;
+ default:
+ strcat(key, tmp);
+ }
+ }
+ }
+ else if( state == 3 )
+ {
+ switch(extract_string(line, tmp, sizeof(tmp)))
+ {
+ case -1:
+ state = 4;
+ break;
+ default:
+ strcat(val, tmp);
+ }
+ }
+
+ if( state == 4 )
+ {
+ if( strlen(key) > 0 && strlen(val) > 0 )
+ {
+ key_id = sfh_hash(key, strlen(key));
+ val_id = sfh_hash(val, strlen(val));
+
+ if( key_id != val_id )
+ {
+ n_entries++;
+ array = realloc(array, n_entries * sizeof(lmo_entry_t));
+ entry = (lmo_entry_t *)array + n_entries - 1;
+
+ if (!array)
+ die("Out of memory");
+
+ entry->key_id = key_id;
+ entry->val_id = val_id;
+ entry->offset = offset;
+ entry->length = strlen(val);
+
+ length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
+
+ print(val, length, 1, out);
+ offset += length;
+ }
+ }
+
+ state = 0;
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+ }
+
+ memset(line, 0, sizeof(line));
+ }
+
+ print_index(array, n_entries, out);
+
+ if( offset > 0 )
+ {
+ print_uint32(offset, out);
+ fsync(fileno(out));
+ fclose(out);
+ }
+ else
+ {
+ fclose(out);
+ unlink(argv[2]);
+ }
+
+ fclose(in);
+ return(0);
+}
diff --git a/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.c b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.c
new file mode 100644
index 0000000000..27205a7228
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.c
@@ -0,0 +1,328 @@
+/*
+ * lmo - Lua Machine Objects - Base functions
+ *
+ * Copyright (C) 2009-2010 Jo-Philipp Wich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "template_lmo.h"
+
+/*
+ * Hash function from http://www.azillionmonkeys.com/qed/hash.html
+ * Copyright (C) 2004-2008 by Paul Hsieh
+ */
+
+uint32_t sfh_hash(const char *data, int len)
+{
+ uint32_t hash = len, tmp;
+ int rem;
+
+ if (len <= 0 || data == NULL) return 0;
+
+ rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (;len > 0; len--) {
+ hash += sfh_get16(data);
+ tmp = (sfh_get16(data+2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2*sizeof(uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3: hash += sfh_get16(data);
+ hash ^= hash << 16;
+ hash ^= data[sizeof(uint16_t)] << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += sfh_get16(data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += *data;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+uint32_t lmo_canon_hash(const char *str, int len)
+{
+ char res[4096];
+ char *ptr, prev;
+ int off;
+
+ if (!str || len >= sizeof(res))
+ return 0;
+
+ for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
+ {
+ if (isspace(*str))
+ {
+ if (!isspace(prev))
+ *ptr++ = ' ';
+ }
+ else
+ {
+ *ptr++ = *str;
+ }
+ }
+
+ if ((ptr > res) && isspace(*(ptr-1)))
+ ptr--;
+
+ return sfh_hash(res, ptr - res);
+}
+
+lmo_archive_t * lmo_open(const char *file)
+{
+ int in = -1;
+ uint32_t idx_offset = 0;
+ struct stat s;
+
+ lmo_archive_t *ar = NULL;
+
+ if (stat(file, &s) == -1)
+ goto err;
+
+ if ((in = open(file, O_RDONLY)) == -1)
+ goto err;
+
+ if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
+ {
+ memset(ar, 0, sizeof(*ar));
+
+ ar->fd = in;
+ ar->size = s.st_size;
+
+ fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
+
+ if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
+ goto err;
+
+ idx_offset = ntohl(*((const uint32_t *)
+ (ar->mmap + ar->size - sizeof(uint32_t))));
+
+ if (idx_offset >= ar->size)
+ goto err;
+
+ ar->index = (lmo_entry_t *)(ar->mmap + idx_offset);
+ ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
+ ar->end = ar->mmap + ar->size;
+
+ return ar;
+ }
+
+err:
+ if (in > -1)
+ close(in);
+
+ if (ar != NULL)
+ {
+ if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+ munmap(ar->mmap, ar->size);
+
+ free(ar);
+ }
+
+ return NULL;
+}
+
+void lmo_close(lmo_archive_t *ar)
+{
+ if (ar != NULL)
+ {
+ if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+ munmap(ar->mmap, ar->size);
+
+ close(ar->fd);
+ free(ar);
+
+ ar = NULL;
+ }
+}
+
+
+lmo_catalog_t *_lmo_catalogs = NULL;
+lmo_catalog_t *_lmo_active_catalog = NULL;
+
+int lmo_load_catalog(const char *lang, const char *dir)
+{
+ DIR *dh = NULL;
+ char pattern[16];
+ char path[PATH_MAX];
+ struct dirent *de = NULL;
+
+ lmo_archive_t *ar = NULL;
+ lmo_catalog_t *cat = NULL;
+
+ if (!lmo_change_catalog(lang))
+ return 0;
+
+ if (!dir || !(dh = opendir(dir)))
+ goto err;
+
+ if (!(cat = malloc(sizeof(*cat))))
+ goto err;
+
+ memset(cat, 0, sizeof(*cat));
+
+ snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
+ snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
+
+ while ((de = readdir(dh)) != NULL)
+ {
+ if (!fnmatch(pattern, de->d_name, 0))
+ {
+ snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
+ ar = lmo_open(path);
+
+ if (ar)
+ {
+ ar->next = cat->archives;
+ cat->archives = ar;
+ }
+ }
+ }
+
+ closedir(dh);
+
+ cat->next = _lmo_catalogs;
+ _lmo_catalogs = cat;
+
+ if (!_lmo_active_catalog)
+ _lmo_active_catalog = cat;
+
+ return 0;
+
+err:
+ if (dh) closedir(dh);
+ if (cat) free(cat);
+
+ return -1;
+}
+
+int lmo_change_catalog(const char *lang)
+{
+ lmo_catalog_t *cat;
+
+ for (cat = _lmo_catalogs; cat; cat = cat->next)
+ {
+ if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+ {
+ _lmo_active_catalog = cat;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
+{
+ unsigned int m, l, r;
+ uint32_t k;
+
+ l = 0;
+ r = ar->length - 1;
+
+ while (1)
+ {
+ m = l + ((r - l) / 2);
+
+ if (r < l)
+ break;
+
+ k = ntohl(ar->index[m].key_id);
+
+ if (k == hash)
+ return &ar->index[m];
+
+ if (k > hash)
+ {
+ if (!m)
+ break;
+
+ r = m - 1;
+ }
+ else
+ {
+ l = m + 1;
+ }
+ }
+
+ return NULL;
+}
+
+int lmo_translate(const char *key, int keylen, char **out, int *outlen)
+{
+ uint32_t hash;
+ lmo_entry_t *e;
+ lmo_archive_t *ar;
+
+ if (!key || !_lmo_active_catalog)
+ return -2;
+
+ hash = lmo_canon_hash(key, keylen);
+
+ for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
+ {
+ if ((e = lmo_find_entry(ar, hash)) != NULL)
+ {
+ *out = ar->mmap + ntohl(e->offset);
+ *outlen = ntohl(e->length);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void lmo_close_catalog(const char *lang)
+{
+ lmo_archive_t *ar, *next;
+ lmo_catalog_t *cat, *prev;
+
+ for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
+ {
+ if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+ {
+ if (prev)
+ prev->next = cat->next;
+ else
+ _lmo_catalogs = cat->next;
+
+ for (ar = cat->archives; ar; ar = next)
+ {
+ next = ar->next;
+ lmo_close(ar);
+ }
+
+ free(cat);
+ break;
+ }
+ }
+}
diff --git a/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.h b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.h
new file mode 100644
index 0000000000..57f59aa56b
--- /dev/null
+++ b/package/lean/luci-app-xlnetacc/tools/po2lmo/src/template_lmo.h
@@ -0,0 +1,92 @@
+/*
+ * lmo - Lua Machine Objects - General header
+ *
+ * Copyright (C) 2009-2012 Jo-Philipp Wich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _TEMPLATE_LMO_H_
+#define _TEMPLATE_LMO_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if (defined(__GNUC__) && defined(__i386__))
+#define sfh_get16(d) (*((const uint16_t *) (d)))
+#else
+#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+
+struct lmo_entry {
+ uint32_t key_id;
+ uint32_t val_id;
+ uint32_t offset;
+ uint32_t length;
+} __attribute__((packed));
+
+typedef struct lmo_entry lmo_entry_t;
+
+
+struct lmo_archive {
+ int fd;
+ int length;
+ uint32_t size;
+ lmo_entry_t *index;
+ char *mmap;
+ char *end;
+ struct lmo_archive *next;
+};
+
+typedef struct lmo_archive lmo_archive_t;
+
+
+struct lmo_catalog {
+ char lang[6];
+ struct lmo_archive *archives;
+ struct lmo_catalog *next;
+};
+
+typedef struct lmo_catalog lmo_catalog_t;
+
+
+uint32_t sfh_hash(const char *data, int len);
+uint32_t lmo_canon_hash(const char *data, int len);
+
+lmo_archive_t * lmo_open(const char *file);
+void lmo_close(lmo_archive_t *ar);
+
+
+extern lmo_catalog_t *_lmo_catalogs;
+extern lmo_catalog_t *_lmo_active_catalog;
+
+int lmo_load_catalog(const char *lang, const char *dir);
+int lmo_change_catalog(const char *lang);
+int lmo_translate(const char *key, int keylen, char **out, int *outlen);
+void lmo_close_catalog(const char *lang);
+
+#endif
diff --git a/package/lean/luci-app-zerotier/Makefile b/package/lean/luci-app-zerotier/Makefile
new file mode 100644
index 0000000000..4769dbccb3
--- /dev/null
+++ b/package/lean/luci-app-zerotier/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI for Zerotier
+LUCI_DEPENDS:=+zerotier
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0
+PKG_RELEASE:=15
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
+
diff --git a/package/lean/luci-app-zerotier/luasrc/controller/zerotier.lua b/package/lean/luci-app-zerotier/luasrc/controller/zerotier.lua
new file mode 100644
index 0000000000..e02a4f793e
--- /dev/null
+++ b/package/lean/luci-app-zerotier/luasrc/controller/zerotier.lua
@@ -0,0 +1,23 @@
+module("luci.controller.zerotier",package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/zerotier")then
+return
+end
+
+entry({"admin","vpn"}, firstchild(), "VPN", 45).dependent = false
+
+entry({"admin", "vpn", "zerotier"},firstchild(), _("ZeroTier")).dependent = false
+
+entry({"admin", "vpn", "zerotier", "general"},cbi("zerotier/settings"), _("Base Setting"), 1)
+entry({"admin", "vpn", "zerotier", "log"},form("zerotier/info"), _("Interface Info"), 2)
+
+entry({"admin","vpn","zerotier","status"},call("act_status"))
+end
+
+function act_status()
+local e={}
+ e.running=luci.sys.call("pgrep /usr/bin/zerotier-one >/dev/null")==0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/info.lua b/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/info.lua
new file mode 100644
index 0000000000..9bc3824cfc
--- /dev/null
+++ b/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/info.lua
@@ -0,0 +1,15 @@
+local fs = require "nixio.fs"
+local conffile = "/tmp/zero.info"
+
+f = SimpleForm("logview")
+
+t = f:field(TextValue, "conf")
+t.rmempty = true
+t.rows = 15
+function t.cfgvalue()
+ luci.sys.exec("ifconfig $(ifconfig | grep zt | awk '{print $1}') > /tmp/zero.info")
+ return fs.readfile(conffile) or ""
+end
+t.readonly="readonly"
+
+return f
\ No newline at end of file
diff --git a/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/settings.lua b/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/settings.lua
new file mode 100644
index 0000000000..14f4bdce05
--- /dev/null
+++ b/package/lean/luci-app-zerotier/luasrc/model/cbi/zerotier/settings.lua
@@ -0,0 +1,26 @@
+
+a=Map("zerotier",translate("ZeroTier"),translate("Zerotier is an open source, cross-platform and easy to use virtual LAN"))
+a:section(SimpleSection).template = "zerotier/zerotier_status"
+
+t=a:section(NamedSection,"sample_config","zerotier")
+t.anonymous=true
+t.addremove=false
+
+e=t:option(Flag,"enabled",translate("Enable"))
+e.default=0
+e.rmempty=false
+
+e=t:option(DynamicList,"join",translate('ZeroTier Network ID'))
+e.password=true
+e.rmempty=false
+
+e=t:option(Flag,"nat",translate("Auto NAT Clients"))
+e.default=0
+e.rmempty=false
+e.description = translate("Allow zerotier clients access your LAN network")
+
+e=t:option(DummyValue,"opennewwindow" ,
+ translate(""))
+e.description = translate("Create or manage your zerotier network, and auth clients who could access")
+
+return a
diff --git a/package/lean/luci-app-zerotier/luasrc/view/zerotier/zerotier_status.htm b/package/lean/luci-app-zerotier/luasrc/view/zerotier/zerotier_status.htm
new file mode 100644
index 0000000000..9d216c5d93
--- /dev/null
+++ b/package/lean/luci-app-zerotier/luasrc/view/zerotier/zerotier_status.htm
@@ -0,0 +1,22 @@
+
+
+
+
+ <%:Collecting data...%>
+
+
diff --git a/package/lean/luci-app-zerotier/po/zh-cn/zerotier.po b/package/lean/luci-app-zerotier/po/zh-cn/zerotier.po
new file mode 100644
index 0000000000..3787a890d5
--- /dev/null
+++ b/package/lean/luci-app-zerotier/po/zh-cn/zerotier.po
@@ -0,0 +1,22 @@
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Gtranslator 2.91.7\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgid "Zerotier is an open source, cross-platform and easy to use virtual LAN"
+msgstr "Zerotier是一个开源,跨平台,而且适合内网穿透互联的傻瓜配置虚拟 VPN LAN"
+
+msgid "Auto NAT Clients"
+msgstr "自动允许客户端NAT"
+
+msgid "Allow zerotier clients access your LAN network"
+msgstr "允许Zerotier的拨入客户端访问路由器LAN资源(需要在 Zerotier管理页面设定到LAN网段的路由表)"
+
+msgid "Create or manage your zerotier network, and auth clients who could access"
+msgstr "点击跳转到Zerotier官网管理平台,新建或者管理网络,并允许客户端接入访问你私人网路(新接入的节点默认不允许访问)"
+
+msgid "Interface Info"
+msgstr "接口信息"
diff --git a/package/lean/luci-app-zerotier/root/etc/init.d/zerotier b/package/lean/luci-app-zerotier/root/etc/init.d/zerotier
new file mode 100755
index 0000000000..bb0fe92e7c
--- /dev/null
+++ b/package/lean/luci-app-zerotier/root/etc/init.d/zerotier
@@ -0,0 +1,101 @@
+#!/bin/sh /etc/rc.common
+
+START=90
+
+USE_PROCD=1
+
+PROG=/usr/bin/zerotier-one
+CONFIG_PATH=/var/lib/zerotier-one
+
+section_enabled() {
+ config_get_bool enabled "$1" 'enabled' 0
+ [ $enabled -gt 0 ]
+}
+
+start_instance() {
+ local cfg="$1"
+ local port secret config_path
+ local ARGS=""
+
+ if ! section_enabled "$cfg"; then
+ echo "disabled in config"
+ return 1
+ fi
+
+ [ -d /etc/config/zero ] || mkdir -p /etc/config/zero
+ config_path=/etc/config/zero
+
+ config_get_bool port $cfg 'port'
+ config_get secret $cfg 'secret'
+
+ # Remove existing link or folder
+ rm -rf $CONFIG_PATH
+
+ # Create link from CONFIG_PATH to config_path
+ if [ -n "$config_path" -a "$config_path" != $CONFIG_PATH ]; then
+ if [ ! -d "$config_path" ]; then
+ echo "ZeroTier config_path does not exist: $config_path"
+ return
+ fi
+
+ ln -s $config_path $CONFIG_PATH
+ fi
+
+ mkdir -p $CONFIG_PATH/networks.d
+
+ if [ -n "$port" ]; then
+ ARGS="$ARGS -p$port"
+ fi
+
+ if [ "$secret" = "generate" ]; then
+ echo "Generate secret - please wait..."
+ local sf="/tmp/zt.$cfg.secret"
+
+ zerotier-idtool generate "$sf" > /dev/null
+ [ $? -ne 0 ] && return 1
+
+ secret="$(cat $sf)"
+ rm "$sf"
+
+ uci set zerotier.$cfg.secret="$secret"
+ uci commit zerotier
+ fi
+
+ if [ -n "$secret" ]; then
+ echo "$secret" > $CONFIG_PATH/identity.secret
+ # make sure there is not previous identity.public
+ rm -f $CONFIG_PATH/identity.public
+ fi
+
+ add_join() {
+ # an (empty) config file will cause ZT to join a network
+ touch $CONFIG_PATH/networks.d/$1.conf
+ }
+
+ config_list_foreach $cfg 'join' add_join
+
+ procd_open_instance
+ procd_set_param command $PROG $ARGS $CONFIG_PATH
+ procd_set_param stderr 1
+ procd_close_instance
+}
+
+start_service() {
+ config_load 'zerotier'
+ config_foreach start_instance 'zerotier'
+ touch /tmp/zero.log && nohup /etc/zerotier.start > /tmp/zero.log 2>&1 &
+}
+
+stop_instance() {
+ rm -f /tmp/zero.log
+ local cfg="$1"
+
+ # Remove existing link or folder
+ rm -rf $CONFIG_PATH
+}
+
+stop_service() {
+ config_load 'zerotier'
+ config_foreach stop_instance 'zerotier'
+}
+
diff --git a/package/lean/luci-app-zerotier/root/etc/uci-defaults/40_luci-zerotier b/package/lean/luci-app-zerotier/root/etc/uci-defaults/40_luci-zerotier
new file mode 100755
index 0000000000..95f0ccfc96
--- /dev/null
+++ b/package/lean/luci-app-zerotier/root/etc/uci-defaults/40_luci-zerotier
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@zerotier[-1]
+ add ucitrack zerotier
+ set ucitrack.@zerotier[-1].init=zerotier
+ commit ucitrack
+ delete firewall.zerotier
+ set firewall.zerotier=include
+ set firewall.zerotier.type=script
+ set firewall.zerotier.path=/etc/zerotier.start
+ set firewall.zerotier.reload=1
+ commit firewall
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
diff --git a/package/lean/luci-app-zerotier/root/etc/zerotier.start b/package/lean/luci-app-zerotier/root/etc/zerotier.start
new file mode 100755
index 0000000000..98fcf4eaa6
--- /dev/null
+++ b/package/lean/luci-app-zerotier/root/etc/zerotier.start
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+zero_enable=$(uci get zerotier.sample_config.enabled)
+
+if [ $zero_enable -eq 1 ]; then
+
+if [ -f /tmp/zero.log ];then
+while [ "$(ifconfig | grep zt | awk '{print $1}')" = "" ]
+do
+ sleep 1
+done
+fi
+
+nat_enable=$(uci get zerotier.sample_config.nat)
+zt0=$(ifconfig | grep zt | awk '{print $1}')
+echo "zt interface $zt0 is started!"
+echo $zt0 > /tmp/zt.nif
+iptables -D FORWARD -i $zt0 -j ACCEPT 2>/dev/null
+iptables -D FORWARD -o $zt0 -j ACCEPT 2>/dev/null
+iptables -t nat -D POSTROUTING -o $zt0 -j MASQUERADE 2>/dev/null
+if [ $nat_enable -eq 1 ]; then
+ iptables -I FORWARD -i $zt0 -j ACCEPT
+ iptables -I FORWARD -o $zt0 -j ACCEPT
+ iptables -t nat -I POSTROUTING -o $zt0 -j MASQUERADE
+ ip_segment=$(ip route | grep "dev $zt0 proto" | awk '{print $1}')
+ iptables -t nat -I POSTROUTING -s $ip_segment -j MASQUERADE
+fi
+
+fi
diff --git a/package/lean/luci-app-zerotier/root/etc/zerotier.stop b/package/lean/luci-app-zerotier/root/etc/zerotier.stop
new file mode 100755
index 0000000000..68f3b718a6
--- /dev/null
+++ b/package/lean/luci-app-zerotier/root/etc/zerotier.stop
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+zt0=$(ifconfig | grep zt | awk '{print $1}')
+if [ "$zt0" = "" ]; then
+ zt0=$(cat /tmp/cat /tmp/zt.nif)
+fi
+echo "zt interface $zt0 is stopped!"
+iptables -D FORWARD -i $zt0 -j ACCEPT 2>/dev/null
+iptables -D FORWARD -o $zt0 -j ACCEPT 2>/dev/null
+iptables -t nat -D POSTROUTING -o $zt0 -j MASQUERADE 2>/dev/null
+ip_segment=$(ip route | grep "dev $zt0 proto" | awk '{print $1}')
+iptables -t nat -D POSTROUTING -s $ip_segment -j MASQUERADE 2>/dev/null
diff --git a/package/lean/luci-app-zerotier/root/etc/zerotier/zerotier.log b/package/lean/luci-app-zerotier/root/etc/zerotier/zerotier.log
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/package/lean/luci-lib-fs/Makefile b/package/lean/luci-lib-fs/Makefile
new file mode 100644
index 0000000000..9d78a29713
--- /dev/null
+++ b/package/lean/luci-lib-fs/Makefile
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-lib-fs
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+PKG_INSTALL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-lib-fs
+ SUBMENU:=Lua
+ SECTION:=lang
+ CATEGORY:=Languages
+ TITLE:=luci-lib-fs
+ PKGARCH:=all
+ URL:=https://github.com/lbthomsen/openwrt-luci
+ DEPENDS:=+luci +luci-lib-nixio
+endef
+
+define Package/luci-lib-fs/description
+ luci-lib-fs
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Build/Install
+endef
+
+
+define Package/luci-lib-fs/install
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci
+ $(CP) ./files/*.lua $(1)/usr/lib/lua/luci
+
+endef
+
+$(eval $(call BuildPackage,luci-lib-fs))
diff --git a/package/lean/luci-lib-fs/files/fs.lua b/package/lean/luci-lib-fs/files/fs.lua
new file mode 100644
index 0000000000..a81ff675d4
--- /dev/null
+++ b/package/lean/luci-lib-fs/files/fs.lua
@@ -0,0 +1,244 @@
+--[[
+LuCI - Filesystem tools
+
+Description:
+A module offering often needed filesystem manipulation functions
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local io = require "io"
+local os = require "os"
+local ltn12 = require "luci.ltn12"
+local fs = require "nixio.fs"
+local nutil = require "nixio.util"
+
+local type = type
+
+--- LuCI filesystem library.
+module "luci.fs"
+
+--- Test for file access permission on given path.
+-- @class function
+-- @name access
+-- @param str String value containing the path
+-- @return Number containing the return code, 0 on sucess or nil on error
+-- @return String containing the error description (if any)
+-- @return Number containing the os specific errno (if any)
+access = fs.access
+
+--- Evaluate given shell glob pattern and return a table containing all matching
+-- file and directory entries.
+-- @class function
+-- @name glob
+-- @param filename String containing the path of the file to read
+-- @return Table containing file and directory entries or nil if no matches
+-- @return String containing the error description (if no matches)
+-- @return Number containing the os specific errno (if no matches)
+function glob(...)
+ local iter, code, msg = fs.glob(...)
+ if iter then
+ return nutil.consume(iter)
+ else
+ return nil, code, msg
+ end
+end
+
+--- Checks wheather the given path exists and points to a regular file.
+-- @param filename String containing the path of the file to test
+-- @return Boolean indicating wheather given path points to regular file
+function isfile(filename)
+ return fs.stat(filename, "type") == "reg"
+end
+
+--- Checks wheather the given path exists and points to a directory.
+-- @param dirname String containing the path of the directory to test
+-- @return Boolean indicating wheather given path points to directory
+function isdirectory(dirname)
+ return fs.stat(dirname, "type") == "dir"
+end
+
+--- Read the whole content of the given file into memory.
+-- @param filename String containing the path of the file to read
+-- @return String containing the file contents or nil on error
+-- @return String containing the error message on error
+readfile = fs.readfile
+
+--- Write the contents of given string to given file.
+-- @param filename String containing the path of the file to read
+-- @param data String containing the data to write
+-- @return Boolean containing true on success or nil on error
+-- @return String containing the error message on error
+writefile = fs.writefile
+
+--- Copies a file.
+-- @param source Source file
+-- @param dest Destination
+-- @return Boolean containing true on success or nil on error
+copy = fs.datacopy
+
+--- Renames a file.
+-- @param source Source file
+-- @param dest Destination
+-- @return Boolean containing true on success or nil on error
+rename = fs.move
+
+--- Get the last modification time of given file path in Unix epoch format.
+-- @param path String containing the path of the file or directory to read
+-- @return Number containing the epoch time or nil on error
+-- @return String containing the error description (if any)
+-- @return Number containing the os specific errno (if any)
+function mtime(path)
+ return fs.stat(path, "mtime")
+end
+
+--- Set the last modification time of given file path in Unix epoch format.
+-- @param path String containing the path of the file or directory to read
+-- @param mtime Last modification timestamp
+-- @param atime Last accessed timestamp
+-- @return 0 in case of success nil on error
+-- @return String containing the error description (if any)
+-- @return Number containing the os specific errno (if any)
+function utime(path, mtime, atime)
+ return fs.utimes(path, atime, mtime)
+end
+
+--- Return the last element - usually the filename - from the given path with
+-- the directory component stripped.
+-- @class function
+-- @name basename
+-- @param path String containing the path to strip
+-- @return String containing the base name of given path
+-- @see dirname
+basename = fs.basename
+
+--- Return the directory component of the given path with the last element
+-- stripped of.
+-- @class function
+-- @name dirname
+-- @param path String containing the path to strip
+-- @return String containing the directory component of given path
+-- @see basename
+dirname = fs.dirname
+
+--- Return a table containing all entries of the specified directory.
+-- @class function
+-- @name dir
+-- @param path String containing the path of the directory to scan
+-- @return Table containing file and directory entries or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+function dir(...)
+ local iter, code, msg = fs.dir(...)
+ if iter then
+ local t = nutil.consume(iter)
+ t[#t+1] = "."
+ t[#t+1] = ".."
+ return t
+ else
+ return nil, code, msg
+ end
+end
+
+--- Create a new directory, recursively on demand.
+-- @param path String with the name or path of the directory to create
+-- @param recursive Create multiple directory levels (optional, default is true)
+-- @return Number with the return code, 0 on sucess or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+function mkdir(path, recursive)
+ return recursive and fs.mkdirr(path) or fs.mkdir(path)
+end
+
+--- Remove the given empty directory.
+-- @class function
+-- @name rmdir
+-- @param path String containing the path of the directory to remove
+-- @return Number with the return code, 0 on sucess or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+rmdir = fs.rmdir
+
+local stat_tr = {
+ reg = "regular",
+ dir = "directory",
+ lnk = "link",
+ chr = "character device",
+ blk = "block device",
+ fifo = "fifo",
+ sock = "socket"
+}
+--- Get information about given file or directory.
+-- @class function
+-- @name stat
+-- @param path String containing the path of the directory to query
+-- @return Table containing file or directory properties or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+function stat(path, key)
+ local data, code, msg = fs.stat(path)
+ if data then
+ data.mode = data.modestr
+ data.type = stat_tr[data.type] or "?"
+ end
+ return key and data and data[key] or data, code, msg
+end
+
+--- Set permissions on given file or directory.
+-- @class function
+-- @name chmod
+-- @param path String containing the path of the directory
+-- @param perm String containing the permissions to set ([ugoa][+-][rwx])
+-- @return Number with the return code, 0 on sucess or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+chmod = fs.chmod
+
+--- Create a hard- or symlink from given file (or directory) to specified target
+-- file (or directory) path.
+-- @class function
+-- @name link
+-- @param path1 String containing the source path to link
+-- @param path2 String containing the destination path for the link
+-- @param symlink Boolean indicating wheather to create a symlink (optional)
+-- @return Number with the return code, 0 on sucess or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+function link(src, dest, sym)
+ return sym and fs.symlink(src, dest) or fs.link(src, dest)
+end
+
+--- Remove the given file.
+-- @class function
+-- @name unlink
+-- @param path String containing the path of the file to remove
+-- @return Number with the return code, 0 on sucess or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+unlink = fs.unlink
+
+--- Retrieve target of given symlink.
+-- @class function
+-- @name readlink
+-- @param path String containing the path of the symlink to read
+-- @return String containing the link target or nil on error
+-- @return String containing the error description on error
+-- @return Number containing the os specific errno on error
+readlink = fs.readlink
diff --git a/package/lean/luci-proto-bonding/Makefile b/package/lean/luci-proto-bonding/Makefile
new file mode 100644
index 0000000000..ab73de0f02
--- /dev/null
+++ b/package/lean/luci-proto-bonding/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2017 TDT AG
+#
+# This is free software, licensed under the Apache License Version 2.0.
+# See https://www.apache.org/licenses/LICENSE-2.0 for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Support for Link Aggregation (Channel Bonding)
+LUCI_DEPENDS:=+proto-bonding
+LUCI_PKGARCH:=all
+
+PKG_MAINTAINER:=Helge Mader
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-proto-bonding/luasrc/model/cbi/admin_network/proto_bonding.lua b/package/lean/luci-proto-bonding/luasrc/model/cbi/admin_network/proto_bonding.lua
new file mode 100644
index 0000000000..898a6587c7
--- /dev/null
+++ b/package/lean/luci-proto-bonding/luasrc/model/cbi/admin_network/proto_bonding.lua
@@ -0,0 +1,386 @@
+--[[
+
+Copyright (C) 2018 TDT AG
+
+This is free software, licensed under the Apache License Version 2.0.
+See https://www.apache.org/licenses/LICENSE-2.0 for more information.
+
+]]--
+
+local map, section, net = ...
+
+local ipaddr, netmask, gateway, broadcast
+
+local SYS = require "luci.sys"
+
+local dhcp, slaves, bonding_policy, primary, primary_reselect, min_links, ad_actor_sys_prio, ad_actor_system
+local ad_select, lacp_rate, packets_per_slave, lp_interval, tlb_dynamic_lb, fail_over_mac
+local num_grat_arp__num_unsol_na, xmit_hash_policy, resend_igmp, all_slaves_active, link_monitoring
+local arp_interval, arp_ip_target, arp_all_targets, arp_validate, miimon, downdelay, updelay, use_carrier
+
+local function get_selectable_slaves(field)
+ m.uci:foreach("network", "interface",
+ function (section)
+ if section[".name"] ~= "loopback" then
+ local network_section_ifname = m.uci:get("network", section['.name'], "ifname")
+ local in_use = false
+
+ m.uci:foreach("network", "interface",
+ function (section)
+ if m.uci:get("network", section['.name'], "proto") == "bonding" then
+
+ local bonding_section_slaves = m.uci:get("network", section['.name'], "slaves")
+
+ if bonding_section_slaves ~= nil then
+
+ for this_slave in bonding_section_slaves:gmatch("[%S-]+") do
+
+ if network_section_ifname == this_slave:gsub("-", "") and section['.name'] ~= arg[1] then
+ in_use = true
+ end
+ end
+ end
+ end
+ end
+ )
+
+ if in_use == false and network_section_ifname ~= nil then
+ if network_section_ifname:find("eth") ~= nil then
+ field:value(network_section_ifname, network_section_ifname)
+ end
+ end
+ end
+ end
+ )
+end
+
+
+local function get_selectable_slaves_from_proc(field)
+ local interfaces = SYS.exec("cat /proc/net/dev | grep 'eth' | awk '\{print \$1\}' | tr ':\n' ' '")
+
+ if interfaces ~= nil then
+ for this_interface in interfaces:gmatch("[%S-]+") do
+ local in_use = false
+
+ m.uci:foreach("network", "interface",
+ function (section)
+ if m.uci:get("network", section['.name'], "proto") == "bonding" then
+ local bonding_section_slaves = m.uci:get("network", section['.name'], "slaves")
+
+ if bonding_section_slaves ~= nil then
+
+ for this_slave in bonding_section_slaves:gmatch("[%S-]+") do
+
+ if this_interface == this_slave and section['.name'] ~= arg[1] then
+ in_use = true
+ end
+ end
+ end
+ end
+ end
+ )
+
+ if in_use == false then
+ field:value(this_interface, this_interface)
+ end
+ end
+ end
+end
+
+
+ipaddr = section:taboption("general", Value, "ipaddr",
+ translate("IPv4 address"))
+ipaddr.datatype = "ip4addr"
+ipaddr.optional = false
+ipaddr.rmempty = false
+
+
+netmask = section:taboption("general", Value, "netmask",
+ translate("IPv4 netmask"))
+netmask.datatype = "ip4addr"
+netmask.optional = false
+netmask.rmempty = false
+netmask:value("255.255.255.0")
+netmask:value("255.255.0.0")
+netmask:value("255.0.0.0")
+
+
+-- dhcp = section:taboption("general", Flag, "dhcp",
+-- translate("Use DHCP (Client Mode)"),
+-- translate("Specifies whether the bonding interface should use DHCP client mode"))
+-- dhcp.default = dhcp.disabled
+-- dhcp.rmempty = false
+
+slaves = section:taboption("advanced", MultiValue, "slaves",
+ translate ("Slave Interfaces"),
+ translate("Specifies which slave interfaces should be attached to this bonding interface"))
+slaves.oneline = true
+slaves.widget = "checkbox"
+
+get_selectable_slaves_from_proc(slaves)
+
+bonding_policy = section:taboption("advanced", ListValue, "bonding_policy",
+ translate("Bonding Policy"),
+ translate("Specifies the mode to be used for this bonding interface"))
+bonding_policy.default = "balance-rr"
+bonding_policy:value("balance-rr", translate("Round-Robin policy (balance-rr, 0)"))
+bonding_policy:value("active-backup", translate("Active-Backup policy (active-backup, 1)"))
+bonding_policy:value("balance-xor", translate("XOR policy (balance-xor, 2)"))
+bonding_policy:value("broadcast", translate("Broadcast policy (broadcast, 3)"))
+bonding_policy:value("802.3ad", translate("IEEE 802.3ad Dynamic link aggregation (802.3ad, 4)"))
+bonding_policy:value("balance-tlb", translate("Adaptive transmit load balancing (balance-tlb, 5)"))
+bonding_policy:value("balance-alb", translate("Adaptive load balancing (balance-alb, 6)"))
+
+primary = section:taboption("advanced", ListValue, "primary",
+ translate("Primary Slave"),
+ translate("Specifies which slave is the primary device. It will always be the active slave while it is available"))
+primary.widget = "radio"
+primary.orientation = "horizontal"
+primary:depends("bonding_policy", "active-backup")
+primary:depends("bonding_policy", "balance-tlb")
+primary:depends("bonding_policy", "balance-alb")
+
+get_selectable_slaves_from_proc(primary)
+
+primary_reselect = section:taboption("advanced", ListValue, "primary_reselect",
+ translate("Reselection policy for primary slave"),
+ translate("Specifies the reselection policy for the primary slave when failure of the active slave or recovery of the primary slave occurs"))
+primary_reselect.default = "always"
+primary_reselect:value("always", translate("Primary becomes active slave whenever it comes back up (always, 0)"))
+primary_reselect:value("better", translate("Primary becomes active slave when it comes back up if speed and duplex better than current slave (better, 1)"))
+primary_reselect:value("failure", translate("Only if current active slave fails and the primary slave is up (failure, 2)"))
+primary_reselect:depends("bonding_policy", "active-backup")
+primary_reselect:depends("bonding_policy", "balance-tlb")
+primary_reselect:depends("bonding_policy", "balance-alb")
+
+min_links = section:taboption("advanced", Value, "min_links",
+ translate("Minimum Number of Links"),
+ translate("Specifies the minimum number of links that must be active before asserting carrier"))
+min_links.datatype = "uinteger"
+min_links.default = 0
+min_links:depends("bonding_policy", "802.3ad")
+
+ad_actor_sys_prio = section:taboption("advanced", Value, "ad_actor_sys_prio",
+ translate("System Priority"),
+ translate("Specifies the system priority"))
+ad_actor_sys_prio.datatype = "range(1,65535)"
+ad_actor_sys_prio.default = 65535
+ad_actor_sys_prio:depends("bonding_policy", "802.3ad")
+
+ad_actor_system = section:taboption("advanced", Value, "ad_actor_system",
+ translate("MAC Address For The Actor"),
+ translate("Specifies the mac-address for the actor in protocol packet exchanges (LACPDUs). If empty, masters' mac address defaults to system default"))
+ad_actor_system.datatype = "macaddr"
+ad_actor_system.default = ""
+ad_actor_system:depends("bonding_policy", "802.3ad")
+
+ad_select = section:taboption("advanced", ListValue, "ad_select",
+ translate("Aggregation Selection Logic"),
+ translate("Specifies the aggregation selection logic to use"))
+ad_select.default = "stable"
+ad_select:value("stable", translate("Aggregator: All slaves down or has no slaves (stable, 0)"))
+ad_select:value("bandwidth", translate("Aggregator: Slave added/removed or state changes (bandwidth, 1)"))
+ad_select:value("count", translate("Aggregator: Chosen by the largest number of ports + slave added/removed or state changes (count, 2)"))
+ad_select:depends("bonding_policy", "802.3ad")
+
+lacp_rate = section:taboption("advanced", ListValue, "lacp_rate",
+ translate("LACPDU Packets"),
+ translate("Specifies the rate in which the link partner will be asked to transmit LACPDU packets"))
+lacp_rate.default = "slow"
+lacp_rate:value("slow", translate("Every 30 seconds (slow, 0)"))
+lacp_rate:value("fast", translate("Every second (fast, 1)"))
+lacp_rate:depends("bonding_policy", "802.3ad")
+
+packets_per_slave = section:taboption("advanced", Value, "packets_per_slave",
+ translate("Packets To Transmit Before Moving To Next Slave"),
+ translate("Specifies the number of packets to transmit through a slave before moving to the next one"))
+packets_per_slave.datatype = "range(0,65535)"
+packets_per_slave.default = 1
+packets_per_slave:depends("bonding_policy", "balance-rr")
+
+lp_interval = section:taboption("advanced", Value, "lp_interval",
+ translate("Interval For Sending Learning Packets"),
+ translate("Specifies the number of seconds between instances where the bonding driver sends learning packets to each slaves peer switch"))
+lp_interval.datatype = "range(1,2147483647)"
+lp_interval.default = 1
+lp_interval:depends("bonding_policy", "balance-tlb")
+lp_interval:depends("bonding_policy", "balance-alb")
+
+tlb_dynamic_lb = section:taboption("advanced", ListValue, "tlb_dynamic_lb",
+ translate("Enable Dynamic Shuffling Of Flows"),
+ translate("Specifies whether to shuffle active flows across slaves based on the load"))
+tlb_dynamic_lb.default = "1"
+tlb_dynamic_lb:value("1", translate("Yes"))
+tlb_dynamic_lb:value("0", translate("No"))
+tlb_dynamic_lb:depends("bonding_policy", "balance-tlb")
+
+fail_over_mac = section:taboption("advanced", ListValue, "fail_over_mac",
+ translate("Set same MAC Address to all slaves"),
+ translate("Specifies whether active-backup mode should set all slaves to the same MAC address at enslavement"))
+fail_over_mac.default = "none"
+fail_over_mac:value("none", translate("Yes (none, 0)"))
+fail_over_mac:value("active", translate("Set to currently active slave (active, 1)"))
+fail_over_mac:value("follow", translate("Set to first slave added to the bond (follow, 2)"))
+fail_over_mac:depends("bonding_policy", "active-backup")
+
+num_grat_arp__num_unsol_na = section:taboption("advanced", Value, "num_grat_arp__num_unsol_na",
+ translate("Number of peer notifications after failover event"),
+ translate("Specifies the number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event"))
+num_grat_arp__num_unsol_na.datatype = "range(0,255)"
+num_grat_arp__num_unsol_na.default = 1
+num_grat_arp__num_unsol_na:depends("bonding_policy", "active-backup")
+
+xmit_hash_policy = section:taboption("advanced", ListValue, "xmit_hash_policy",
+ translate("Transmit Hash Policy"),
+ translate("Selects the transmit hash policy to use for slave selection"))
+xmit_hash_policy.default = "layer2"
+xmit_hash_policy:value("layer2", translate("Use XOR of hardware MAC addresses (layer2)"))
+xmit_hash_policy:value("layer2+3", translate("Use XOR of hardware MAC addresses and IP addresses (layer2+3)"))
+xmit_hash_policy:value("layer3+4", translate("Use upper layer protocol information (layer3+4)"))
+xmit_hash_policy:value("encap2+3", translate("Use XOR of hardware MAC addresses and IP addresses, rely on skb_flow_dissect (encap2+3)"))
+xmit_hash_policy:value("encap3+4", translate("Use upper layer protocol information, rely on skb_flow_dissect (encap3+4)"))
+xmit_hash_policy:depends("bonding_policy", "balance-rr")
+xmit_hash_policy:depends("bonding_policy", "active-backup")
+xmit_hash_policy:depends("bonding_policy", "balance-tlb")
+xmit_hash_policy:depends("bonding_policy", "balance-alb")
+xmit_hash_policy:depends("bonding_policy", "balance-xor")
+
+resend_igmp = section:taboption("advanced", Value, "resend_igmp",
+ translate("Number of IGMP membership reports"),
+ translate("Specifies the number of IGMP membership reports to be issued after a failover event in 200ms intervals"))
+resend_igmp.datatype = "range(0,255)"
+resend_igmp.default = 1
+resend_igmp:depends("bonding_policy", "balance-tlb")
+resend_igmp:depends("bonding_policy", "balance-alb")
+
+all_slaves_active = section:taboption("advanced", ListValue, "all_slaves_active",
+ translate("Drop Duplicate Frames"),
+ translate("Specifies that duplicate frames (received on inactive ports) should be dropped or delivered"))
+all_slaves_active.default = "0"
+all_slaves_active:value("0", translate("Yes"))
+all_slaves_active:value("1", translate("No"))
+
+link_monitoring = section:taboption("advanced", ListValue, "link_monitoring",
+ translate("Link Monitoring"),
+ translate("Method of link monitoring"))
+link_monitoring.default = "off"
+link_monitoring:value("off", translate("Off"))
+link_monitoring:value("arp", translate("ARP"))
+link_monitoring:value("mii", translate("MII"))
+
+arp_interval = section:taboption("advanced", Value, "arp_interval",
+ translate("ARP Interval"),
+ translate("Specifies the ARP link monitoring frequency in milliseconds"))
+arp_interval.datatype = "uinteger"
+arp_interval.default = 0
+arp_interval:depends("link_monitoring", "arp")
+
+arp_ip_target = section:taboption("advanced", DynamicList, "arp_ip_target",
+ translate("ARP IP Targets"),
+ translate("Specifies the IP addresses to use for ARP monitoring"))
+arp_ip_target.datatype = "ipaddr"
+arp_ip_target.cast = "string"
+arp_ip_target:depends("link_monitoring", "arp")
+
+arp_all_targets = section:taboption("advanced", ListValue, "arp_all_targets",
+ translate("ARP mode to consider a slave as being up"),
+ translate("Specifies the quantity of ARP IP targets that must be reachable"))
+arp_all_targets.default = "any"
+arp_all_targets:value("any", translate("Consider the slave up when any ARP IP target is reachable (any, 0)"))
+arp_all_targets:value("all", translate("Consider the slave up when all ARP IP targets are reachable (all, 1)"))
+arp_all_targets:depends({link_monitoring="arp", bonding_policy="active-backup"})
+
+arp_validate = section:taboption("advanced", ListValue, "arp_validate",
+ translate("ARP Validation"),
+ translate("Specifies whether ARP probes and replies should be validated or non-ARP traffic should be filtered for link monitoring"))
+arp_validate.default = "filter"
+arp_validate:value("none", translate("No validation or filtering "))
+arp_validate:value("active", translate("Validation only for active slave"))
+arp_validate:value("backup", translate("Validation only for backup slaves"))
+arp_validate:value("all", translate("Validation for all slaves"))
+arp_validate:value("filter", translate("Filtering for all slaves, no validation"))
+arp_validate:value("filter_active", translate("Filtering for all slaves, validation only for active slave"))
+arp_validate:value("filter_backup", translate("Filtering for all slaves, validation only for backup slaves"))
+arp_validate:depends("link_monitoring", "arp")
+
+miimon = section:taboption("advanced", Value, "miimon",
+ translate("MII Interval"),
+ translate("Specifies the MII link monitoring frequency in milliseconds"))
+miimon.datatype = "uinteger"
+miimon.default = 0
+miimon:depends("link_monitoring", "mii")
+
+downdelay = section:taboption("advanced", Value, "downdelay",
+ translate("Down Delay"),
+ translate("Specifies the time in milliseconds to wait before disabling a slave after a link failure detection"))
+downdelay.datatype = "uinteger"
+downdelay.default = 0
+downdelay:depends("link_monitoring", "mii")
+
+updelay = section:taboption("advanced", Value, "updelay",
+ translate("Up Delay"),
+ translate("Specifies the time in milliseconds to wait before enabling a slave after a link recovery detection"))
+updelay.datatype = "uinteger"
+updelay.default = 0
+updelay:depends("link_monitoring", "mii")
+
+use_carrier = section:taboption("advanced", ListValue, "use_carrier",
+ translate("Method to determine link status"),
+ translate("Specifies whether or not miimon should use MII or ETHTOOL ioctls vs. netif_carrier_ok()"))
+use_carrier.default = "1"
+use_carrier:value("0", translate("MII / ETHTOOL ioctls"))
+use_carrier:value("1", translate("netif_carrier_ok()"))
+use_carrier:depends("link_monitoring", "mii")
+
+
+-- we use the bondig_policy validate function to check for other required values
+-- (e.g. slave interfaces, primary interface, ARP targets, ...) as validate functions
+-- are not called for _empty_ fields. bonding_policy will never be empty.
+function bonding_policy.validate(self, value, section)
+
+ local selected_link_monitoring = link_monitoring:formvalue(section)
+ local selected_arp_ip_targets = arp_ip_target:formvalue(section)
+
+ local selected_policy = bonding_policy:formvalue(section)
+
+ local selected_slaves = slaves:formvalue(section)
+ local selected_primary = primary:formvalue(section)
+
+ if selected_link_monitoring == "arp" then
+ if selected_policy == "802.3ad" or selected_policy == "balance-tlb" or selected_policy == "balance-alb" then
+ return nil, translate("ARP monitoring is not supported for the selected policy")
+ end
+
+ if #selected_arp_ip_targets == 0 then
+ return nil, translate("You must select at least one ARP IP target if ARP monitoring is selected")
+ end
+
+ end
+
+ if selected_slaves == nil then
+ return nil, translate("You must select at least one slave interface")
+ end
+
+ if selected_policy == "active-backup" or selected_policy == "balance-tlb" or selected_policy == "balance-alb" then
+ if selected_primary == nil then
+ return nil, translate("You must select a primary interface for the selected policy")
+ else
+ if (type(selected_slaves) == "table") then
+ for key,slave_value in pairs(selected_slaves) do
+ if slave_value == selected_primary then
+ return value
+ end
+ end
+ else
+ if selected_slaves == selected_primary then
+ return value
+ end
+ end
+ end
+
+ return nil, translate("You must select a primary interface which is included in selected slave interfaces")
+ end
+
+ return value
+end
+
diff --git a/package/lean/luci-proto-bonding/luasrc/model/network/proto_bonding.lua b/package/lean/luci-proto-bonding/luasrc/model/network/proto_bonding.lua
new file mode 100644
index 0000000000..2c0e3ddbc3
--- /dev/null
+++ b/package/lean/luci-proto-bonding/luasrc/model/network/proto_bonding.lua
@@ -0,0 +1,31 @@
+--[[
+
+Copyright (C) 2018 TDT AG
+
+This is free software, licensed under the Apache License Version 2.0.
+See https://www.apache.org/licenses/LICENSE-2.0 for more information.
+
+]]--
+
+local proto = luci.model.network:register_protocol("bonding")
+
+function proto.get_i18n(self)
+ return luci.i18n.translate("Link Aggregation (Channel Bonding)")
+end
+
+function proto.is_installed(self)
+ return nixio.fs.access("/lib/netifd/proto/bonding.sh")
+end
+
+function proto.is_virtual(self)
+ return true
+end
+
+function proto.is_floating(self)
+ return true
+end
+
+function proto.opkg_package(self)
+ return "bonding"
+end
+
diff --git a/package/lean/luci-proto-bonding/po/de/bonding.po b/package/lean/luci-proto-bonding/po/de/bonding.po
new file mode 100644
index 0000000000..fc2c1e3c30
--- /dev/null
+++ b/package/lean/luci-proto-bonding/po/de/bonding.po
@@ -0,0 +1,398 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "ARP"
+msgstr ""
+
+msgid "ARP IP Targets"
+msgstr "ARP IP Ziele"
+
+msgid "ARP Interval"
+msgstr "ARP Intervall"
+
+msgid "ARP Validation"
+msgstr "ARP Überprüfung"
+
+msgid "ARP mode to consider a slave as being up"
+msgstr "ARP Methode um ein Slave als UP zu betrachten"
+
+msgid "ARP monitoring is not supported for the selected policy"
+msgstr "ARP Monitoring wird für die gewählte Bonding Methode nicht unterstützt"
+
+msgid "Active-Backup policy (active-backup, 1)"
+msgstr "Aktiv-Backup (active-backup, 1)"
+
+msgid "Adaptive load balancing (balance-alb, 6)"
+msgstr "Adaptives Load Balancing (balance-alb, 6)"
+
+msgid "Adaptive transmit load balancing (balance-tlb, 5)"
+msgstr "Adaptives Sende Load Balancing (balance-tlb, 5)"
+
+msgid "Aggregation Selection Logic"
+msgstr "Gruppierungslogik"
+
+msgid "Aggregator: All slaves down or has no slaves (stable, 0)"
+msgstr "Aggregator: Alle Slaves Down oder keine Slaves vorhanden (stable, 0)"
+
+msgid ""
+"Aggregator: Chosen by the largest number of ports + slave added/removed or "
+"state changes (count, 2)"
+msgstr ""
+"Aggregator: Ausgewählt durch die höchsten Portnummern + Slave hinzugefügt/"
+"entfernt oder Statuswechsel (count, 2)"
+
+msgid "Aggregator: Slave added/removed or state changes (bandwidth, 1)"
+msgstr ""
+"Aggregator: Slave hinzugefügt/entfernt oder Statuswechsel (bandwidth, 1)"
+
+msgid "Bonding Policy"
+msgstr "Bonding Methode"
+
+msgid "Broadcast policy (broadcast, 3)"
+msgstr "Broadcast (broadcast, 3)"
+
+msgid "Consider the slave up when all ARP IP targets are reachable (all, 1)"
+msgstr ""
+"Slave wird als aktiv angesehen, wenn alle ARP IP Ziele erreichbar sind (all, "
+"1)"
+
+msgid "Consider the slave up when any ARP IP target is reachable (any, 0)"
+msgstr ""
+"Slave wird als aktiv angesehen, wenn irgendein ARP IP Ziel erreichbar ist "
+"(any, 0)"
+
+msgid "Down Delay"
+msgstr "Down Verzögerung"
+
+msgid "Drop Duplicate Frames"
+msgstr "Doppelte Frames verwerfen"
+
+msgid "Enable Dynamic Shuffling Of Flows"
+msgstr "Dynamisches Verteilen von Flows aktivieren"
+
+msgid "Every 30 seconds (slow, 0)"
+msgstr "Alle 30 Sekunden (slow, 0)"
+
+msgid "Every second (fast, 1)"
+msgstr "Jede Sekunde (fast, 1)"
+
+msgid "Filtering for all slaves, no validation"
+msgstr "Alle Slaves filtern, keine Überprüfung"
+
+msgid "Filtering for all slaves, validation only for active slave"
+msgstr "Alle Slaves filtern, Überprüfung nur für aktive Slaves"
+
+msgid "Filtering for all slaves, validation only for backup slaves"
+msgstr "Alle Slaves filtern, Überprüfung nur für Backup Slaves"
+
+msgid "IEEE 802.3ad Dynamic link aggregation (802.3ad, 4)"
+msgstr ""
+
+msgid "IPv4 address"
+msgstr "IPv4 Adresse"
+
+msgid "IPv4 netmask"
+msgstr "IPv4 Netzmaske"
+
+msgid "Interval For Sending Learning Packets"
+msgstr "Interval zum Senden von 'Learning Packets'"
+
+msgid "LACPDU Packets"
+msgstr "LACPDU Pakete"
+
+msgid "Link Aggregation (Channel Bonding)"
+msgstr "Link Aggregation (Kanalbündelung)"
+
+msgid "Link Monitoring"
+msgstr "Link Überwachung"
+
+msgid "MAC Address For The Actor"
+msgstr "MAC Adresse für den Aktor"
+
+msgid "MII"
+msgstr ""
+
+msgid "MII / ETHTOOL ioctls"
+msgstr ""
+
+msgid "MII Interval"
+msgstr "MII Intervall"
+
+msgid "Method of link monitoring"
+msgstr "Art der Link Überwachung"
+
+msgid "Method to determine link status"
+msgstr "Methode um Link Status festzustellen"
+
+msgid "Minimum Number of Links"
+msgstr "Minimale Anzahl von Links"
+
+msgid "No"
+msgstr "Nein"
+
+msgid "No validation or filtering"
+msgstr "Keine Überprüfung oder Filterung"
+
+msgid "Number of IGMP membership reports"
+msgstr "Anzahl von IGMP Zugehörigkeitsmeldungen"
+
+msgid "Number of peer notifications after failover event"
+msgstr "Anzahl von Peer Benachrichtigungen nach einem Failover Ereignis"
+
+msgid "Off"
+msgstr "Aus"
+
+msgid ""
+"Only if current active slave fails and the primary slave is up (failure, 2)"
+msgstr ""
+"Nur wenn das derzeit aktive Slave fehlschlägt und das primäre Slave Up ist "
+"(failure, 2)"
+
+msgid "Packets To Transmit Before Moving To Next Slave"
+msgstr "Zu übertragende Pakete vor Wechsel zum nächsten Slave"
+
+msgid "Primary Slave"
+msgstr "Primäres Slave"
+
+msgid ""
+"Primary becomes active slave when it comes back up if speed and duplex "
+"better than current slave (better, 1)"
+msgstr ""
+"Primäres Slave wird zum aktiven Slave wenn es wieder Up ist und "
+"Geschwindikkeit/Duplex besser als derzeitiges Slave (better, 1)"
+
+msgid "Primary becomes active slave whenever it comes back up (always, 0)"
+msgstr ""
+"Primäres Slave wird zum aktiven Slave wenn es wieder Up ist (always, 0)"
+
+msgid "Reselection policy for primary slave"
+msgstr "Methode zur Neuauswahl des primären Slaves"
+
+msgid "Round-Robin policy (balance-rr, 0)"
+msgstr "Round-Robin (balance-rr, 0)"
+
+msgid "Selects the transmit hash policy to use for slave selection"
+msgstr "Selektiert die Hash Übertragunsmethode zur Slave Auswahl"
+
+msgid "Set same MAC Address to all slaves"
+msgstr "Gleiche MAC Adresse für alle Slaves verwenden"
+
+msgid "Set to currently active slave (active, 1)"
+msgstr "Entsprechend dem derzeit aktiven Slave (active, 1)"
+
+msgid "Set to first slave added to the bond (follow, 2)"
+msgstr "Entsprechend dem ersten hinzugefügten Slave (follow, 2)"
+
+msgid "Slave Interfaces"
+msgstr ""
+
+msgid ""
+"Specifies that duplicate frames (received on inactive ports) should be "
+"dropped or delivered"
+msgstr ""
+"Spezifiziert ob doppelte Frames (empfangen auf inaktiven Ports) verworfen "
+"oder zugestellt werden"
+
+msgid "Specifies the ARP link monitoring frequency in milliseconds"
+msgstr "Häufigkeit des ARP Link Monitorings in Millisekunden"
+
+msgid "Specifies the IP addresses to use for ARP monitoring"
+msgstr "Für ARP Monitoring zu verwendende IP Adressen"
+
+msgid "Specifies the MII link monitoring frequency in milliseconds"
+msgstr "Häufigkeit des MII Link Monitorings in Millisekunden"
+
+msgid "Specifies the aggregation selection logic to use"
+msgstr "Spezifiziert die zu verwendende Gruppierungslogik"
+
+msgid ""
+"Specifies the mac-address for the actor in protocol packet exchanges "
+"(LACPDUs). If empty, masters' mac address defaults to system default"
+msgstr ""
+"Selektiert die MAC Adresse für den Aktor für LACPDUs. Wenn nicht angegeben "
+"entspricht die Master Mac Adresse der System Voreinstellung"
+
+msgid ""
+"Specifies the minimum number of links that must be active before asserting "
+"carrier"
+msgstr ""
+"Spezifiziert die minimale Anzahl aktiver Links bevor Carrier signalisiert "
+"wird"
+
+msgid "Specifies the mode to be used for this bonding interface"
+msgstr ""
+"Spezifizert die zu benutzende Bonding Methode für dieses Bonding Interface"
+
+msgid ""
+"Specifies the number of IGMP membership reports to be issued after a "
+"failover event in 200ms intervals"
+msgstr ""
+"Spezifiziert die Anzahl von IGMP Zugehörigkeitsmeldungen nach einem Failover "
+"Ereignis in 200ms Intervallen"
+
+msgid ""
+"Specifies the number of packets to transmit through a slave before moving to "
+"the next one"
+msgstr ""
+"Spezifiziert die Anzahl der auf einem Slave zu übertragenden Pakete vor "
+"Wechsel zum nächsten Slave"
+
+msgid ""
+"Specifies the number of peer notifications (gratuitous ARPs and unsolicited "
+"IPv6 Neighbor Advertisements) to be issued after a failover event"
+msgstr ""
+"Spezifiziert die Anzahl der Peer Benachrichtigungen (gratuitous ARPs and "
+"unsolicited IPv6 Neighbor Advertisements) nach einem Failover Ereignis"
+
+msgid ""
+"Specifies the number of seconds between instances where the bonding driver "
+"sends learning packets to each slaves peer switch"
+msgstr ""
+
+msgid "Specifies the quantity of ARP IP targets that must be reachable"
+msgstr "Spezifizert die Anzahl der ARP IP Ziele die erreichbar sein müssen"
+
+msgid ""
+"Specifies the rate in which the link partner will be asked to transmit "
+"LACPDU packets"
+msgstr ""
+"Spezifiziert die Häufigkeit in welcher der Link Partner aufgefordert wird "
+"LACPDU Pakete zu senden"
+
+msgid ""
+"Specifies the reselection policy for the primary slave when failure of the "
+"active slave or recovery of the primary slave occurs"
+msgstr ""
+"Spezifiziert die Neuauswahl für das primäre Slave wenn ein Ausfall des "
+"aktiven Slaves oder ein Recovery des primären Slaves passiert"
+
+msgid "Specifies the system priority"
+msgstr "Spezifiziert die System Priorität"
+
+msgid ""
+"Specifies the time in milliseconds to wait before disabling a slave after a "
+"link failure detection"
+msgstr ""
+"Spezifizert die Zeit in Millisekunden bevor ein Slave nach Erkennung eines "
+"Link Fehlers deaktiviert wird"
+
+msgid ""
+"Specifies the time in milliseconds to wait before enabling a slave after a "
+"link recovery detection"
+msgstr ""
+"Spezifizert die Zeit in Millisekunden nach der ein Slave nach Erkennung "
+"eines Link Recoveries aktiviert wird"
+
+msgid ""
+"Specifies whether ARP probes and replies should be validated or non-ARP "
+"traffic should be filtered for link monitoring"
+msgstr ""
+"Spezifiziert ob ARP Anfragen und Antworten überprüft oder nicht ARP "
+"Datenverkehr für das Link Monitoring gefiltert werden sollen"
+
+msgid ""
+"Specifies whether active-backup mode should set all slaves to the same MAC "
+"address at enslavement"
+msgstr ""
+"Spezifiziert ob 'active-backup' Modus allen Slaves die gleiche MAC Adresse "
+"beim Zusammenschluss zuweisen soll"
+
+msgid ""
+"Specifies whether or not miimon should use MII or ETHTOOL ioctls vs. "
+"netif_carrier_ok()"
+msgstr ""
+"Spezifiziert ob 'miimon' MII oder ETHTOOL ioctls verwenden soll oder "
+"netif_carrier_ok()"
+
+msgid "Specifies whether the bonding interface should use DHCP client mode"
+msgstr ""
+"Spezifiziert ob das Bonding Interface im DHCP Client Modus arbeiten soll"
+
+msgid ""
+"Specifies whether to shuffle active flows across slaves based on the load"
+msgstr ""
+"Spezifiziert ob aktiver Datenverkehr über Slaves lastabhängig verteilt "
+"werden soll"
+
+msgid ""
+"Specifies which slave interfaces should be attached to this bonding interface"
+msgstr ""
+"Spezifiziert welche Slave Interfaces diesem Bonding Interface zugeordnet "
+"werden sollen"
+
+msgid ""
+"Specifies which slave is the primary device. It will always be the active "
+"slave while it is available"
+msgstr ""
+"Spezifiziert welches Slave Interface als primäres Slave verwendet werden "
+"soll. Es wird immer als aktives Slave verwendet sofern es verfügbar ist."
+
+msgid "System Priority"
+msgstr "System Priorität"
+
+msgid "Transmit Hash Policy"
+msgstr "Hash Übertragungsmethode"
+
+msgid "Up Delay"
+msgstr "Up Verzögerung"
+
+msgid "Use DHCP (Client Mode)"
+msgstr "DHCP Client Mode verwenden"
+
+msgid "Use XOR of hardware MAC addresses (layer2)"
+msgstr "XOR der Hardware MAC Adressen verwenden (layer2)"
+
+msgid "Use XOR of hardware MAC addresses and IP addresses (layer2+3)"
+msgstr "XOR der Hardware MAC und IP Adressen verwenden (layer2+3)"
+
+msgid ""
+"Use XOR of hardware MAC addresses and IP addresses, rely on skb_flow_dissect "
+"(encap2+3)"
+msgstr ""
+"XOR der Hardware MAC und IP Adressen basierend auf skb_flow_dissect "
+"verwenden (encap2+3)"
+
+msgid "Use upper layer protocol information (layer3+4)"
+msgstr "Übergeordnete Protokollschicht Informationen verwenden (layer3+4)"
+
+msgid ""
+"Use upper layer protocol information, rely on skb_flow_dissect (encap3+4)"
+msgstr ""
+"Übergeordnete Protokollschicht Informationen basierend auf skb_flow_dissect "
+"verwenden (encap3+4)"
+
+msgid "Validation for all slaves"
+msgstr "Validierung für alle Slaves"
+
+msgid "Validation only for active slave"
+msgstr "Validierung nur für aktives Slave"
+
+msgid "Validation only for backup slaves"
+msgstr "Validierung nur für Backup Slaves"
+
+msgid "XOR policy (balance-xor, 2)"
+msgstr ""
+
+msgid "Yes"
+msgstr "Ja"
+
+msgid "Yes (none, 0)"
+msgstr "Ja (none, 0)"
+
+msgid "You must select a primary interface for the selected policy"
+msgstr "Für die gewählte Bonding Methode ist ein Primäres Slave erforderlich"
+
+msgid ""
+"You must select a primary interface which is included in selected slave "
+"interfaces"
+msgstr "Das primäre Interface muss in den selektierten Slaves enthalten sein"
+
+msgid ""
+"You must select at least one ARP IP target if ARP monitoring is selected"
+msgstr "Es muss mindestens ein ARP IP Ziel für ARP Monitoring definiert werden"
+
+msgid "You must select at least one slave interface"
+msgstr "Bitte mindestens ein Slave Interface auswählen"
+
+msgid "netif_carrier_ok()"
+msgstr ""
diff --git a/package/lean/luci-proto-bonding/po/en/bonding.po b/package/lean/luci-proto-bonding/po/en/bonding.po
new file mode 100644
index 0000000000..8f734db3de
--- /dev/null
+++ b/package/lean/luci-proto-bonding/po/en/bonding.po
@@ -0,0 +1,345 @@
+msgid "ARP"
+msgstr ""
+
+msgid "ARP IP Targets"
+msgstr ""
+
+msgid "ARP Interval"
+msgstr ""
+
+msgid "ARP Validation"
+msgstr ""
+
+msgid "ARP mode to consider a slave as being up"
+msgstr ""
+
+msgid "ARP monitoring is not supported for the selected policy"
+msgstr ""
+
+msgid "Active-Backup policy (active-backup, 1)"
+msgstr ""
+
+msgid "Adaptive load balancing (balance-alb, 6)"
+msgstr ""
+
+msgid "Adaptive transmit load balancing (balance-tlb, 5)"
+msgstr ""
+
+msgid "Aggregation Selection Logic"
+msgstr ""
+
+msgid "Aggregator: All slaves down or has no slaves (stable, 0)"
+msgstr ""
+
+msgid ""
+"Aggregator: Chosen by the largest number of ports + slave added/removed or "
+"state changes (count, 2)"
+msgstr ""
+
+msgid "Aggregator: Slave added/removed or state changes (bandwidth, 1)"
+msgstr ""
+
+msgid "Bonding Policy"
+msgstr ""
+
+msgid "Broadcast policy (broadcast, 3)"
+msgstr ""
+
+msgid "Consider the slave up when all ARP IP targets are reachable (all, 1)"
+msgstr ""
+
+msgid "Consider the slave up when any ARP IP target is reachable (any, 0)"
+msgstr ""
+
+msgid "Down Delay"
+msgstr ""
+
+msgid "Drop Duplicate Frames"
+msgstr ""
+
+msgid "Enable Dynamic Shuffling Of Flows"
+msgstr ""
+
+msgid "Every 30 seconds (slow, 0)"
+msgstr ""
+
+msgid "Every second (fast, 1)"
+msgstr ""
+
+msgid "Filtering for all slaves, no validation"
+msgstr ""
+
+msgid "Filtering for all slaves, validation only for active slave"
+msgstr ""
+
+msgid "Filtering for all slaves, validation only for backup slaves"
+msgstr ""
+
+msgid "IEEE 802.3ad Dynamic link aggregation (802.3ad, 4)"
+msgstr ""
+
+msgid "IPv4 address"
+msgstr ""
+
+msgid "IPv4 netmask"
+msgstr ""
+
+msgid "Interval For Sending Learning Packets"
+msgstr ""
+
+msgid "LACPDU Packets"
+msgstr ""
+
+msgid "Link Aggregation (Channel Bonding)"
+msgstr ""
+
+msgid "Link Monitoring"
+msgstr ""
+
+msgid "MAC Address For The Actor"
+msgstr ""
+
+msgid "MII"
+msgstr ""
+
+msgid "MII / ETHTOOL ioctls"
+msgstr ""
+
+msgid "MII Interval"
+msgstr ""
+
+msgid "Method of link monitoring"
+msgstr ""
+
+msgid "Method to determine link status"
+msgstr ""
+
+msgid "Minimum Number of Links"
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No validation or filtering"
+msgstr ""
+
+msgid "Number of IGMP membership reports"
+msgstr ""
+
+msgid "Number of peer notifications after failover event"
+msgstr ""
+
+msgid "Off"
+msgstr ""
+
+msgid ""
+"Only if current active slave fails and the primary slave is up (failure, 2)"
+msgstr ""
+
+msgid "Packets To Transmit Before Moving To Next Slave"
+msgstr ""
+
+msgid "Primary Slave"
+msgstr ""
+
+msgid ""
+"Primary becomes active slave when it comes back up if speed and duplex "
+"better than current slave (better, 1)"
+msgstr ""
+
+msgid "Primary becomes active slave whenever it comes back up (always, 0)"
+msgstr ""
+
+msgid "Reselection policy for primary slave"
+msgstr ""
+
+msgid "Round-Robin policy (balance-rr, 0)"
+msgstr ""
+
+msgid "Selects the transmit hash policy to use for slave selection"
+msgstr ""
+
+msgid "Set same MAC Address to all slaves"
+msgstr ""
+
+msgid "Set to currently active slave (active, 1)"
+msgstr ""
+
+msgid "Set to first slave added to the bond (follow, 2)"
+msgstr ""
+
+msgid "Slave Interfaces"
+msgstr ""
+
+msgid ""
+"Specifies that duplicate frames (received on inactive ports) should be "
+"dropped or delivered"
+msgstr ""
+
+msgid "Specifies the ARP link monitoring frequency in milliseconds"
+msgstr ""
+
+msgid "Specifies the IP addresses to use for ARP monitoring"
+msgstr ""
+
+msgid "Specifies the MII link monitoring frequency in milliseconds"
+msgstr ""
+
+msgid "Specifies the aggregation selection logic to use"
+msgstr ""
+
+msgid ""
+"Specifies the mac-address for the actor in protocol packet exchanges "
+"(LACPDUs). If empty, masters' mac address defaults to system default"
+msgstr ""
+
+msgid ""
+"Specifies the minimum number of links that must be active before asserting "
+"carrier"
+msgstr ""
+
+msgid "Specifies the mode to be used for this bonding interface"
+msgstr ""
+
+msgid ""
+"Specifies the number of IGMP membership reports to be issued after a "
+"failover event in 200ms intervals"
+msgstr ""
+
+msgid ""
+"Specifies the number of packets to transmit through a slave before moving to "
+"the next one"
+msgstr ""
+
+msgid ""
+"Specifies the number of peer notifications (gratuitous ARPs and unsolicited "
+"IPv6 Neighbor Advertisements) to be issued after a failover event"
+msgstr ""
+
+msgid ""
+"Specifies the number of seconds between instances where the bonding driver "
+"sends learning packets to each slaves peer switch"
+msgstr ""
+
+msgid "Specifies the quantity of ARP IP targets that must be reachable"
+msgstr ""
+
+msgid ""
+"Specifies the rate in which the link partner will be asked to transmit "
+"LACPDU packets"
+msgstr ""
+
+msgid ""
+"Specifies the reselection policy for the primary slave when failure of the "
+"active slave or recovery of the primary slave occurs"
+msgstr ""
+
+msgid "Specifies the system priority"
+msgstr ""
+
+msgid ""
+"Specifies the time in milliseconds to wait before disabling a slave after a "
+"link failure detection"
+msgstr ""
+
+msgid ""
+"Specifies the time in milliseconds to wait before enabling a slave after a "
+"link recovery detection"
+msgstr ""
+
+msgid ""
+"Specifies whether ARP probes and replies should be validated or non-ARP "
+"traffic should be filtered for link monitoring"
+msgstr ""
+
+msgid ""
+"Specifies whether active-backup mode should set all slaves to the same MAC "
+"address at enslavement"
+msgstr ""
+
+msgid ""
+"Specifies whether or not miimon should use MII or ETHTOOL ioctls vs. "
+"netif_carrier_ok()"
+msgstr ""
+
+msgid "Specifies whether the bonding interface should use DHCP client mode"
+msgstr ""
+
+msgid ""
+"Specifies whether to shuffle active flows across slaves based on the load"
+msgstr ""
+
+msgid ""
+"Specifies which slave interfaces should be attached to this bonding interface"
+msgstr ""
+
+msgid ""
+"Specifies which slave is the primary device. It will always be the active "
+"slave while it is available"
+msgstr ""
+
+msgid "System Priority"
+msgstr ""
+
+msgid "Transmit Hash Policy"
+msgstr ""
+
+msgid "Up Delay"
+msgstr ""
+
+msgid "Use DHCP (Client Mode)"
+msgstr ""
+
+msgid "Use XOR of hardware MAC addresses (layer2)"
+msgstr ""
+
+msgid "Use XOR of hardware MAC addresses and IP addresses (layer2+3)"
+msgstr ""
+
+msgid ""
+"Use XOR of hardware MAC addresses and IP addresses, rely on skb_flow_dissect "
+"(encap2+3)"
+msgstr ""
+
+msgid "Use upper layer protocol information (layer3+4)"
+msgstr ""
+
+msgid ""
+"Use upper layer protocol information, rely on skb_flow_dissect (encap3+4)"
+msgstr ""
+
+msgid "Validation for all slaves"
+msgstr ""
+
+msgid "Validation only for active slave"
+msgstr ""
+
+msgid "Validation only for backup slaves"
+msgstr ""
+
+msgid "XOR policy (balance-xor, 2)"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes (none, 0)"
+msgstr ""
+
+msgid "You must select a primary interface for the selected policy"
+msgstr ""
+
+msgid ""
+"You must select a primary interface which is included in selected slave "
+"interfaces"
+msgstr ""
+
+msgid ""
+"You must select at least one ARP IP target if ARP monitoring is selected"
+msgstr ""
+
+msgid "You must select at least one slave interface"
+msgstr ""
+
+msgid "netif_carrier_ok()"
+msgstr ""
diff --git a/package/lean/luci-proto-bonding/po/templates/bonding.pot b/package/lean/luci-proto-bonding/po/templates/bonding.pot
new file mode 100644
index 0000000000..96878ec981
--- /dev/null
+++ b/package/lean/luci-proto-bonding/po/templates/bonding.pot
@@ -0,0 +1,348 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "ARP"
+msgstr ""
+
+msgid "ARP IP Targets"
+msgstr ""
+
+msgid "ARP Interval"
+msgstr ""
+
+msgid "ARP Validation"
+msgstr ""
+
+msgid "ARP mode to consider a slave as being up"
+msgstr ""
+
+msgid "ARP monitoring is not supported for the selected policy"
+msgstr ""
+
+msgid "Active-Backup policy (active-backup, 1)"
+msgstr ""
+
+msgid "Adaptive load balancing (balance-alb, 6)"
+msgstr ""
+
+msgid "Adaptive transmit load balancing (balance-tlb, 5)"
+msgstr ""
+
+msgid "Aggregation Selection Logic"
+msgstr ""
+
+msgid "Aggregator: All slaves down or has no slaves (stable, 0)"
+msgstr ""
+
+msgid ""
+"Aggregator: Chosen by the largest number of ports + slave added/removed or "
+"state changes (count, 2)"
+msgstr ""
+
+msgid "Aggregator: Slave added/removed or state changes (bandwidth, 1)"
+msgstr ""
+
+msgid "Bonding Policy"
+msgstr ""
+
+msgid "Broadcast policy (broadcast, 3)"
+msgstr ""
+
+msgid "Consider the slave up when all ARP IP targets are reachable (all, 1)"
+msgstr ""
+
+msgid "Consider the slave up when any ARP IP target is reachable (any, 0)"
+msgstr ""
+
+msgid "Down Delay"
+msgstr ""
+
+msgid "Drop Duplicate Frames"
+msgstr ""
+
+msgid "Enable Dynamic Shuffling Of Flows"
+msgstr ""
+
+msgid "Every 30 seconds (slow, 0)"
+msgstr ""
+
+msgid "Every second (fast, 1)"
+msgstr ""
+
+msgid "Filtering for all slaves, no validation"
+msgstr ""
+
+msgid "Filtering for all slaves, validation only for active slave"
+msgstr ""
+
+msgid "Filtering for all slaves, validation only for backup slaves"
+msgstr ""
+
+msgid "IEEE 802.3ad Dynamic link aggregation (802.3ad, 4)"
+msgstr ""
+
+msgid "IPv4 address"
+msgstr ""
+
+msgid "IPv4 netmask"
+msgstr ""
+
+msgid "Interval For Sending Learning Packets"
+msgstr ""
+
+msgid "LACPDU Packets"
+msgstr ""
+
+msgid "Link Aggregation (Channel Bonding)"
+msgstr ""
+
+msgid "Link Monitoring"
+msgstr ""
+
+msgid "MAC Address For The Actor"
+msgstr ""
+
+msgid "MII"
+msgstr ""
+
+msgid "MII / ETHTOOL ioctls"
+msgstr ""
+
+msgid "MII Interval"
+msgstr ""
+
+msgid "Method of link monitoring"
+msgstr ""
+
+msgid "Method to determine link status"
+msgstr ""
+
+msgid "Minimum Number of Links"
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No validation or filtering"
+msgstr ""
+
+msgid "Number of IGMP membership reports"
+msgstr ""
+
+msgid "Number of peer notifications after failover event"
+msgstr ""
+
+msgid "Off"
+msgstr ""
+
+msgid ""
+"Only if current active slave fails and the primary slave is up (failure, 2)"
+msgstr ""
+
+msgid "Packets To Transmit Before Moving To Next Slave"
+msgstr ""
+
+msgid "Primary Slave"
+msgstr ""
+
+msgid ""
+"Primary becomes active slave when it comes back up if speed and duplex "
+"better than current slave (better, 1)"
+msgstr ""
+
+msgid "Primary becomes active slave whenever it comes back up (always, 0)"
+msgstr ""
+
+msgid "Reselection policy for primary slave"
+msgstr ""
+
+msgid "Round-Robin policy (balance-rr, 0)"
+msgstr ""
+
+msgid "Selects the transmit hash policy to use for slave selection"
+msgstr ""
+
+msgid "Set same MAC Address to all slaves"
+msgstr ""
+
+msgid "Set to currently active slave (active, 1)"
+msgstr ""
+
+msgid "Set to first slave added to the bond (follow, 2)"
+msgstr ""
+
+msgid "Slave Interfaces"
+msgstr ""
+
+msgid ""
+"Specifies that duplicate frames (received on inactive ports) should be "
+"dropped or delivered"
+msgstr ""
+
+msgid "Specifies the ARP link monitoring frequency in milliseconds"
+msgstr ""
+
+msgid "Specifies the IP addresses to use for ARP monitoring"
+msgstr ""
+
+msgid "Specifies the MII link monitoring frequency in milliseconds"
+msgstr ""
+
+msgid "Specifies the aggregation selection logic to use"
+msgstr ""
+
+msgid ""
+"Specifies the mac-address for the actor in protocol packet exchanges "
+"(LACPDUs). If empty, masters' mac address defaults to system default"
+msgstr ""
+
+msgid ""
+"Specifies the minimum number of links that must be active before asserting "
+"carrier"
+msgstr ""
+
+msgid "Specifies the mode to be used for this bonding interface"
+msgstr ""
+
+msgid ""
+"Specifies the number of IGMP membership reports to be issued after a "
+"failover event in 200ms intervals"
+msgstr ""
+
+msgid ""
+"Specifies the number of packets to transmit through a slave before moving to "
+"the next one"
+msgstr ""
+
+msgid ""
+"Specifies the number of peer notifications (gratuitous ARPs and unsolicited "
+"IPv6 Neighbor Advertisements) to be issued after a failover event"
+msgstr ""
+
+msgid ""
+"Specifies the number of seconds between instances where the bonding driver "
+"sends learning packets to each slaves peer switch"
+msgstr ""
+
+msgid "Specifies the quantity of ARP IP targets that must be reachable"
+msgstr ""
+
+msgid ""
+"Specifies the rate in which the link partner will be asked to transmit "
+"LACPDU packets"
+msgstr ""
+
+msgid ""
+"Specifies the reselection policy for the primary slave when failure of the "
+"active slave or recovery of the primary slave occurs"
+msgstr ""
+
+msgid "Specifies the system priority"
+msgstr ""
+
+msgid ""
+"Specifies the time in milliseconds to wait before disabling a slave after a "
+"link failure detection"
+msgstr ""
+
+msgid ""
+"Specifies the time in milliseconds to wait before enabling a slave after a "
+"link recovery detection"
+msgstr ""
+
+msgid ""
+"Specifies whether ARP probes and replies should be validated or non-ARP "
+"traffic should be filtered for link monitoring"
+msgstr ""
+
+msgid ""
+"Specifies whether active-backup mode should set all slaves to the same MAC "
+"address at enslavement"
+msgstr ""
+
+msgid ""
+"Specifies whether or not miimon should use MII or ETHTOOL ioctls vs. "
+"netif_carrier_ok()"
+msgstr ""
+
+msgid "Specifies whether the bonding interface should use DHCP client mode"
+msgstr ""
+
+msgid ""
+"Specifies whether to shuffle active flows across slaves based on the load"
+msgstr ""
+
+msgid ""
+"Specifies which slave interfaces should be attached to this bonding interface"
+msgstr ""
+
+msgid ""
+"Specifies which slave is the primary device. It will always be the active "
+"slave while it is available"
+msgstr ""
+
+msgid "System Priority"
+msgstr ""
+
+msgid "Transmit Hash Policy"
+msgstr ""
+
+msgid "Up Delay"
+msgstr ""
+
+msgid "Use DHCP (Client Mode)"
+msgstr ""
+
+msgid "Use XOR of hardware MAC addresses (layer2)"
+msgstr ""
+
+msgid "Use XOR of hardware MAC addresses and IP addresses (layer2+3)"
+msgstr ""
+
+msgid ""
+"Use XOR of hardware MAC addresses and IP addresses, rely on skb_flow_dissect "
+"(encap2+3)"
+msgstr ""
+
+msgid "Use upper layer protocol information (layer3+4)"
+msgstr ""
+
+msgid ""
+"Use upper layer protocol information, rely on skb_flow_dissect (encap3+4)"
+msgstr ""
+
+msgid "Validation for all slaves"
+msgstr ""
+
+msgid "Validation only for active slave"
+msgstr ""
+
+msgid "Validation only for backup slaves"
+msgstr ""
+
+msgid "XOR policy (balance-xor, 2)"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes (none, 0)"
+msgstr ""
+
+msgid "You must select a primary interface for the selected policy"
+msgstr ""
+
+msgid ""
+"You must select a primary interface which is included in selected slave "
+"interfaces"
+msgstr ""
+
+msgid ""
+"You must select at least one ARP IP target if ARP monitoring is selected"
+msgstr ""
+
+msgid "You must select at least one slave interface"
+msgstr ""
+
+msgid "netif_carrier_ok()"
+msgstr ""
diff --git a/package/lean/n2n_v2/Makefile b/package/lean/n2n_v2/Makefile
new file mode 100644
index 0000000000..01f0415c59
--- /dev/null
+++ b/package/lean/n2n_v2/Makefile
@@ -0,0 +1,53 @@
+#
+# Copyright (C) 2007-2012 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=n2n_v2
+PKG_VERSION:=2.4
+PKG_RELEASE:=1
+PKG_REV=25563f31d9aba5f61b3e2fb42941b66dad1f531f
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-r$(PKG_REV).tar.bz2
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_VERSION:=$(PKG_REV)
+PKG_SOURCE_URL:=https://github.com/ntop/n2n.git
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/n2n_v2
+ SECTION:=net
+ CATEGORY:=Network
+ TITLE:=N2N VPN tunneling daemon(V2)
+ URL:=http://www.ntop.org/n2n/
+ SUBMENU:=VPN
+ DEPENDS:=+libpthread +kmod-tun +libopenssl
+endef
+
+define Build/Compile
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ $(TARGET_CONFIGURE_OPTS) \
+ CFLAGS="$(TARGET_CFLAGS)" \
+ INSTALL_PROG=":"
+endef
+
+define Package/n2n_v2/install
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/edge $(1)/usr/sbin/
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/supernode $(1)/usr/sbin/
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_DATA) ./files/n2n_v2.config $(1)/etc/config/n2n_v2
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/n2n_v2.init $(1)/etc/init.d/n2n_v2
+endef
+
+define Package/n2n_v2/conffiles
+/etc/config/n2n_v2
+endef
+
+$(eval $(call BuildPackage,n2n_v2))
diff --git a/package/lean/n2n_v2/files/n2n_v2.config b/package/lean/n2n_v2/files/n2n_v2.config
new file mode 100755
index 0000000000..c765d02dd2
--- /dev/null
+++ b/package/lean/n2n_v2/files/n2n_v2.config
@@ -0,0 +1,15 @@
+config edge
+ option enabled '0'
+ option tunname 'n2n_edge'
+ option mode 'static'
+ option ipaddr '10.0.0.100'
+ option netmask '255.255.255.0'
+ option supernode '1.2.3.4'
+ option port '1234'
+ option community 'example'
+ option key 'password'
+ option route '0'
+
+config supernode
+ option enabled '0'
+ option port '1235'
diff --git a/package/lean/n2n_v2/files/n2n_v2.init b/package/lean/n2n_v2/files/n2n_v2.init
new file mode 100755
index 0000000000..87efbfde76
--- /dev/null
+++ b/package/lean/n2n_v2/files/n2n_v2.init
@@ -0,0 +1,62 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2008-2012 OpenWrt.org
+
+START=90
+
+start_instance() {
+ local cfg="$1"
+
+ config_get type "$cfg" TYPE
+
+ case "$type" in
+ edge)
+ config_get_bool enabled "$cfg" 'enabled' '0'
+ [ "$enabled" = "0" ] && return 1
+ config_get tunname "$cfg" 'tunname'
+ config_get mode "$cfg" 'mode'
+ config_get ipaddr "$cfg" 'ipaddr'
+ config_get netmask "$cfg" 'netmask'
+ config_get supernode "$cfg" 'supernode'
+ config_get port "$cfg" 'port'
+ config_get community "$cfg" 'community'
+ config_get key "$cfg" 'key'
+ config_get_bool route "$cfg" 'route' '0'
+ [ "$route" = "1" ] && args='-r'
+ [ "$mode" = 'dhcp' ] && ipaddr='0.0.0.0'
+ /usr/sbin/edge -d $tunname -a ${mode}:${ipaddr} -c $community $([ -n "$key" ] && echo -k $key) -s ${netmask} -l ${supernode}:${port} $args
+ ;;
+ supernode)
+ config_get_bool enabled "$cfg" 'enabled' '0'
+ [ "$enabled" = "0" ] && return 1
+ config_get port "$cfg" port
+ /usr/sbin/supernode -l $port &
+ ;;
+ esac
+}
+
+stop_instance() {
+ local cfg="$1"
+
+ config_get type "$cfg" TYPE
+
+ case "$type" in
+ edge)
+ killall -9 edge
+ ;;
+ supernode)
+ killall -9 supernode
+ ;;
+ esac
+}
+
+start() {
+ config_load 'n2n_v2'
+ config_foreach start_instance 'edge'
+ config_foreach start_instance 'supernode'
+}
+
+stop() {
+ config_load 'n2n_v2'
+ killall -9 edge
+ killall -9 supernode
+}
diff --git a/package/lean/nft-qos/Makefile b/package/lean/nft-qos/Makefile
new file mode 100644
index 0000000000..78e0ec2b95
--- /dev/null
+++ b/package/lean/nft-qos/Makefile
@@ -0,0 +1,59 @@
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nft-qos
+PKG_VERSION:=1.0.6
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-2.0
+
+PKG_MAINTAINER:=Rosy Song
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/nft-qos
+ SECTION:=utils
+ CATEGORY:=Base system
+ DEPENDS:=+nftables +kmod-nft-netdev +kmod-nft-bridge
+ TITLE:=QoS scripts over nftables
+ PKGARCH:=all
+endef
+
+define Package/nft-qos/description
+ This package provides implementation for qos over nftables.
+ Currently, static/dynamic qos and traffic shaping are supported.
+endef
+
+define Package/nft-qos/conffiles
+/etc/config/nft-qos
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/nft-qos/install
+ $(INSTALL_DIR) $(1)/lib/nft-qos
+ $(INSTALL_DATA) ./files/lib/* $(1)/lib/nft-qos/
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_CONF) ./files/nft-qos.config $(1)/etc/config/nft-qos
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/nft-qos.init $(1)/etc/init.d/nft-qos
+ $(INSTALL_DIR) $(1)/etc/hotplug.d/dhcp
+ $(INSTALL_BIN) ./files/nft-qos-monitor.hotplug $(1)/etc/hotplug.d/dhcp/00-nft-qos-monitor
+ $(INSTALL_BIN) ./files/nft-qos-dynamic.hotplug $(1)/etc/hotplug.d/dhcp/01-nft-qos-dynamic
+endef
+
+$(eval $(call BuildPackage,nft-qos))
diff --git a/package/lean/nft-qos/files/lib/core.sh b/package/lean/nft-qos/files/lib/core.sh
new file mode 100755
index 0000000000..d3c9d641f2
--- /dev/null
+++ b/package/lean/nft-qos/files/lib/core.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+# for uci_validate_section()
+. /lib/functions/procd.sh
+
+NFT_QOS_HAS_BRIDGE=
+NFT_QOS_INET_FAMILY=ip
+NFT_QOS_SCRIPT_TEXT=
+NFT_QOS_SCRIPT_FILE=/tmp/qos.nft
+
+qosdef_appendx() { #
+ NFT_QOS_SCRIPT_TEXT="$NFT_QOS_SCRIPT_TEXT""$1"
+}
+
+qosdef_append_chain_def() { #
+ qosdef_appendx "\t\ttype $1 hook $2 priority $3; policy $4;\n"
+}
+
+qosdef_append_chain_ingress() { #
+ qosdef_appendx "\t\ttype $1 hook ingress device $2 priority $3; policy $4;\n"
+}
+
+# qosdef_append_rule_{MATCH}_{STATEMENT}
+qosdef_append_rule_ip_limit() { #
+ local ipaddr=$1
+ local operator=$2
+ local unit=$3
+ local rate=$4
+
+ qosdef_appendx \
+ "\t\tip $operator $ipaddr limit rate over $rate $unit/second drop\n"
+}
+
+# qosdef_append_rule_{MATCH}_{POLICY}
+qosdef_append_rule_ip_policy() { #