From bcd6b073505d486bc321f3526058d582c35babe7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 Oct 2026 21:11:19 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9B=85=20Sync=202026-10-06=2021:11:19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- honk/Makefile | 2 +- honk/files/honk.init | 30 +- luci-app-accesscontrol/Makefile | 19 - .../luasrc/controller/mia.lua | 17 - .../luasrc/model/cbi/mia.lua | 68 - .../luasrc/view/mia/mia_status.htm | 22 - luci-app-accesscontrol/po/zh-cn/mia.po | 50 - luci-app-accesscontrol/root/etc/config/mia | 3 - luci-app-accesscontrol/root/etc/init.d/mia | 88 - luci-app-accesscontrol/root/etc/mia.include | 1 - .../root/etc/uci-defaults/luci-mia | 26 - .../rpcd/acl.d/luci-app-accesscontrol.json | 11 - .../ucitrack/luci-app-accesscontrol.json | 4 - luci-app-adbyby-plus/Makefile | 18 - .../luasrc/controller/adbyby.lua | 86 - .../luasrc/model/cbi/adbyby/advanced.lua | 67 - .../luasrc/model/cbi/adbyby/base.lua | 66 - .../luasrc/model/cbi/adbyby/black.lua | 23 - .../luasrc/model/cbi/adbyby/block.lua | 23 - .../luasrc/model/cbi/adbyby/esc.lua | 23 - .../luasrc/model/cbi/adbyby/help.lua | 23 - .../luasrc/model/cbi/adbyby/user.lua | 23 - .../luasrc/view/adbyby/adbyby_status.htm | 22 - .../luasrc/view/adbyby/refresh.htm | 35 - luci-app-adbyby-plus/po/zh-cn/adbyby.po | 198 - luci-app-adbyby-plus/po/zh_Hans | 1 - luci-app-adbyby-plus/root/etc/config/adbyby | 11 - luci-app-adbyby-plus/root/etc/init.d/adbyby | 254 - .../root/etc/uci-defaults/luci-adbyby | 38 - .../root/lib/upgrade/keep.d/adbyby | 6 - .../root/usr/share/adbyby/ad-update | 10 - .../root/usr/share/adbyby/adblack.conf | 14 - .../root/usr/share/adbyby/adblock.sh | 10 - .../root/usr/share/adbyby/adbybyfirst.sh | 4 - .../root/usr/share/adbyby/adbybyupdate.sh | 49 - .../root/usr/share/adbyby/adesc.conf | 3 - .../root/usr/share/adbyby/adhost.conf | 74 - .../root/usr/share/adbyby/admem.sh | 46 - .../root/usr/share/adbyby/adupdate.sh | 30 - .../root/usr/share/adbyby/blockip.conf | 1 - .../root/usr/share/adbyby/dnsmasq.adblock | 0 .../root/usr/share/adbyby/dnsmasq.ads | 4 - .../root/usr/share/adbyby/dnsmasq.esc | 3 - .../root/usr/share/adbyby/firewall.include | 16 - .../root/usr/share/adbyby/rule-count | 3 - .../root/usr/share/adbyby/rule-update | 36 - .../root/usr/share/adbyby/rules.txt | 15 - .../rpcd/acl.d/luci-app-adbyby-plus.json | 11 - .../share/ucitrack/luci-app-adbyby-plus.json | 4 - luci-app-aliyundrive-fuse/Makefile | 16 - .../luasrc/controller/aliyundrive-fuse.lua | 40 - .../model/cbi/aliyundrive-fuse/client.lua | 33 - .../luasrc/model/cbi/aliyundrive-fuse/log.lua | 9 - .../aliyundrive-fuse/aliyundrive-fuse_log.htm | 15 - .../aliyundrive-fuse_status.htm | 21 - .../po/zh-cn/aliyundrive-fuse.po | 56 - luci-app-aliyundrive-fuse/po/zh_Hans | 1 - .../etc/uci-defaults/luci-aliyundrive-fuse | 19 - .../rpcd/acl.d/luci-app-aliyundrive-fuse.json | 11 - luci-app-aliyundrive-webdav/Makefile | 7 +- .../luasrc/controller/aliyundrive-webdav.lua | 41 +- .../model/cbi/aliyundrive-webdav/client.lua | 33 +- .../aliyundrive-webdav_qrcode.htm | 158 - .../aliyundrive-webdav_status.htm | 21 +- .../po/zh-cn/aliyundrive-webdav.po | 44 +- .../acl.d/luci-app-aliyundrive-webdav.json | 11 - luci-app-clouddrive2/Makefile | 21 +- .../luasrc/controller/clouddrive2.lua | 16 +- .../luasrc/model/cbi/clouddrive2.lua | 57 +- .../luasrc/model/clouddrive2.lua | 55 + .../luasrc/view/clouddrive2/status.htm | 56 +- luci-app-clouddrive2/po/zh-cn | 1 - luci-app-clouddrive2/po/zh-cn/clouddrive2.po | 59 + .../po/zh_Hans | 0 .../po/zh_Hans/clouddrive2.po | 32 - .../root/etc/config/clouddrive2 | 6 + .../root/etc/init.d/clouddrive2 | 13 + .../root/etc/uci-defaults/luci-clouddrive2 | 24 - .../root/usr/libexec/istorec/clouddrive2.sh | 105 + .../rpcd/acl.d/luci-app-clouddrive2.json | 2 +- .../share/ucitrack/luci-app-clouddrive2.json | 4 + luci-app-design-config/Makefile | 19 - .../luasrc/controller/design-config.lua | 10 - .../luasrc/model/cbi/design-config.lua | 58 - .../view/design-config/other_button.htm | 7 - .../view/design-config/other_dvalue.htm | 8 - .../po/zh-cn/design-config.po | 44 - luci-app-design-config/po/zh_Hans | 1 - luci-app-design-config/root/etc/config/design | 4 - .../root/etc/uci-defaults/luci-design-config | 6 - .../rpcd/acl.d/luci-app-design-config.json | 11 - luci-app-dnsforwarder/Makefile | 22 - .../luasrc/controller/dnsforwarder.lua | 11 - .../luasrc/model/cbi/dnsforwarder/general.lua | 187 - .../luasrc/model/cbi/dnsforwarder/log.lua | 28 - .../po/zh-cn/dnsforwarder.po | 14 - luci-app-dnsforwarder/po/zh_Hans | 1 - luci-app-e2guardian/Makefile | 18 - .../luasrc/controller/e2guardian.lua | 22 - .../luasrc/model/cbi/e2guardian.lua | 399 - luci-app-easymesh/Makefile | 17 - .../luasrc/controller/easymesh.lua | 14 - .../luasrc/model/cbi/easymesh.lua | 128 - luci-app-easymesh/po/zh-cn/easymesh.po | 83 - luci-app-easymesh/po/zh_Hans | 1 - luci-app-easymesh/root/etc/config/easymesh | 3 - luci-app-easymesh/root/etc/init.d/easymesh | 252 - .../root/etc/uci-defaults/luci-easymesh | 19 - .../share/rpcd/acl.d/luci-app-easymesh.json | 11 - .../usr/share/ucitrack/luci-app-easymesh.json | 4 - luci-app-haproxy-tcp/Makefile | 29 - .../luasrc/controller/haproxy.lua | 9 - .../luasrc/model/cbi/haproxy.lua | 68 - luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po | 34 - luci-app-haproxy-tcp/po/zh_Hans | 1 - luci-app-haproxy-tcp/root/etc/config/haproxy | 29 - luci-app-haproxy-tcp/root/etc/haproxy_init.sh | 133 - luci-app-haproxy-tcp/root/etc/haproxy_start | 73 - .../root/etc/uci-defaults/99_luci-haproxy | 7 - .../rpcd/acl.d/luci-app-haproxy-tcp.json | 11 - luci-app-ltqtapi/Makefile | 14 - .../luasrc/controller/ltqtapi.lua | 40 - .../luasrc/model/cbi/luci_ltqtapi/account.lua | 16 - .../luasrc/model/cbi/luci_ltqtapi/contact.lua | 20 - .../view/admin_status/index/telephony.htm | 1 - .../luasrc/view/telephony_status.htm | 32 - luci-app-mwan3helper/Makefile | 18 - .../luasrc/controller/mwan3helper.lua | 21 - .../luasrc/model/cbi/mwan3helper/client.lua | 28 - .../luasrc/model/cbi/mwan3helper/list.lua | 35 - .../view/mwan3helper/mwan3helper_status.htm | 22 - luci-app-mwan3helper/po/zh-cn/mwan3helper.po | 24 - luci-app-mwan3helper/po/zh_Hans | 1 - .../root/etc/config/mwan3helper | 5 - .../root/etc/init.d/mwan3helper | 166 - .../root/etc/mwan3helper/all_cn.txt | 8618 ----------------- .../root/etc/mwan3helper/cernet.txt | 171 - .../root/etc/mwan3helper/chinatelecom.txt | 1962 ---- .../root/etc/mwan3helper/cmcc.txt | 55 - .../root/etc/mwan3helper/crtc.txt | 16 - .../root/etc/mwan3helper/genipset.sh | 7 - .../root/etc/mwan3helper/gfw.txt | 5987 ------------ .../root/etc/mwan3helper/gwbn.txt | 290 - .../root/etc/mwan3helper/othernet.txt | 5209 ---------- .../root/etc/mwan3helper/unicom_cnc.txt | 915 -- .../root/etc/uci-defaults/luci-mwan3helper | 19 - .../rpcd/acl.d/luci-app-mwan3helper.json | 11 - .../share/ucitrack/luci-app-mwan3helper.json | 4 - luci-app-nft-qos/Makefile | 19 - .../luasrc/controller/nft-qos.lua | 52 - .../luasrc/model/cbi/nft-qos/nft-qos.lua | 229 - luci-app-nft-qos/luasrc/view/nft-qos/rate.htm | 187 - luci-app-nft-qos/po/es/nft-qos.po | 245 - luci-app-nft-qos/po/templates/nft-qos.pot | 230 - luci-app-nft-qos/po/zh-cn/nft-qos.po | 244 - luci-app-nft-qos/po/zh-tw/nft-qos.po | 244 - luci-app-nft-qos/po/zh_Hans | 1 - .../root/etc/uci-defaults/luci-nft-qos | 19 - luci-app-pgyvpn/Makefile | 8 - luci-app-pgyvpn/luasrc/controller/pgyvpn.lua | 139 - luci-app-pgyvpn/luasrc/model/cbi/pgyvpn.lua | 25 - luci-app-pgyvpn/luasrc/pgyvpn/config.lua | 30 - .../luasrc/view/pgyvpn/content.htm | 9 - luci-app-pgyvpn/luasrc/view/pgyvpn/status.htm | 51 - luci-app-pgyvpn/luasrc/view/pgyvpn/vpn.htm | 66 - .../www/luci-static/resources/icons/pgy.png | Bin 601 -> 0 bytes luci-app-phtunnel/Makefile | 8 - .../luasrc/controller/oray/phtunnel.lua | 16 - .../luasrc/model/cbi/oray/phtunnel_setup.lua | 15 - luci-app-phtunnel/luasrc/phtunnel.lua | 114 - .../view/oray/phtunnel_inner_status.htm | 31 - .../luasrc/view/oray/phtunnel_log.htm | 17 - .../luasrc/view/oray/phtunnel_log_off.htm | 34 - .../luasrc/view/oray/phtunnel_status.htm | 218 - luci-app-phtunnel/po/zh-cn/phtunnel.po | 8 - luci-app-phtunnel/po/zh_Hans | 1 - .../share/rpcd/acl.d/luci-app-phtunnel.json | 8 - luci-app-pppwn/Makefile | 17 - luci-app-pppwn/luasrc/controller/pppwn.lua | 21 - .../luasrc/model/cbi/pppwn/info.lua | 14 - .../luasrc/model/cbi/pppwn/settings.lua | 34 - .../luasrc/view/pppwn/pppwn_status.htm | 23 - luci-app-pppwn/po/zh-cn/pppwn.po | 20 - luci-app-pppwn/po/zh_Hans | 1 - luci-app-pppwn/root/etc/config/pppwn | 6 - luci-app-pppwn/root/etc/init.d/pppwn | 26 - .../root/etc/uci-defaults/luci-pppwn | 20 - .../usr/share/rpcd/acl.d/luci-app-pppwn.json | 11 - .../usr/share/ucitrack/luci-app-pppwn.json | 4 - luci-app-pppwn/root/www/goldhen.bin | Bin 296112 -> 0 bytes luci-app-pptp-server/Makefile | 10 +- .../luasrc/controller/pptp-server.lua | 18 - .../luasrc/controller/pptpd.lua | 21 + .../luasrc/model/cbi/pptp-server.lua | 37 - .../luasrc/model/cbi/pptpd/online.lua | 84 + .../luasrc/model/cbi/pptpd/settings.lua | 40 + .../luasrc/model/cbi/pptpd/user.lua | 32 + .../luasrc/model/cbi/pptpd/users.lua | 33 + .../view/pptp-server/pptp-server_status.htm | 22 - .../luasrc/view/pptpd/index.htm | 13 + .../luasrc/view/pptpd/status.htm | 3 + luci-app-pptp-server/po/zh-cn/pptp.po | 53 - luci-app-pptp-server/po/zh-cn/pptpd.po | 71 + .../root/etc/config/luci-app-pptp-server | 7 + luci-app-pptp-server/root/etc/config/pptpd | 12 - .../root/etc/init.d/luci-app-pptp-server | 127 + luci-app-pptp-server/root/etc/pptpd.include | 11 - .../etc/uci-defaults/luci-app-pptp-server | 21 + .../root/etc/uci-defaults/luci-pptpd | 47 - .../root/usr/share/pptpd/ip-down | 27 + .../root/usr/share/pptpd/ip-up | 58 + .../rpcd/acl.d/luci-app-pptp-server.json | 4 +- .../share/ucitrack/luci-app-pptp-server.json | 4 + luci-app-pushbot/Makefile | 4 +- luci-app-pushbot/README.md | 93 + .../luasrc/model/cbi/pushbot/setting.lua | 14 - .../root/usr/bin/pushbot/api/pushdeer.json | 31 - luci-app-pushbot/root/usr/bin/pushbot/pushbot | 9 +- luci-app-shadowsocks-libev/Makefile | 16 - .../luasrc/controller/shadowsocks-libev.lua | 33 - .../shadowsocks-libev/instance-details.lua | 47 - .../model/cbi/shadowsocks-libev/instances.lua | 104 - .../model/cbi/shadowsocks-libev/rules.lua | 109 - .../model/cbi/shadowsocks-libev/servers.lua | 31 - .../luasrc/model/shadowsocks-libev.lua | 264 - .../view/shadowsocks-libev/add_instance.htm | 45 - .../uci-defaults/40_luci-shadowsocks-libev | 19 - luci-app-shairplay/Makefile | 14 - .../luasrc/controller/shairplay.lua | 14 - .../luasrc/model/cbi/shairplay.lua | 65 - luci-app-shairplay/po/ja/shairplay.po | 54 - luci-app-shairplay/po/pt-br/shairplay.po | 54 - luci-app-shairplay/po/ru/shairplay.po | 56 - luci-app-shairplay/po/templates/shairplay.pot | 40 - .../root/etc/uci-defaults/40_luci-shairplay | 20 - luci-app-smartinfo/Makefile | 15 - .../luasrc/controller/smartinfo.lua | 132 - .../luasrc/model/cbi/smartinfo.lua | 85 - .../view/admin_status/index/smartinfo.htm | 1 - .../view/smartinfo/detail_begin_flag.htm | 10 - .../view/smartinfo/detail_begin_value.htm | 7 - .../view/smartinfo/detail_end_listvalue.htm | 20 - .../view/smartinfo/detail_end_value.htm | 9 - .../luasrc/view/smartinfo/smart_detail.htm | 82 - .../luasrc/view/smartinfo/smart_status.htm | 80 - luci-app-smartinfo/po/zh-cn/smartinfo.po | 116 - luci-app-smartinfo/po/zh_Hans | 1 - luci-app-smartinfo/root/etc/config/smartinfo | 11 - luci-app-smartinfo/root/etc/init.d/smartinfo | 15 - .../root/usr/lib/smartinfo/smart_checker.sh | 115 - .../root/usr/lib/smartinfo/smart_functions.sh | 56 - .../root/usr/lib/smartinfo/smart_status.sh | 17 - .../share/rpcd/acl.d/luci-app-smartinfo.json | 11 - .../share/ucitrack/luci-app-smartinfo.json | 4 - luci-app-ssrserver-python/Makefile | 19 - .../luasrc/controller/ssrs.lua | 18 - .../luasrc/model/cbi/ssrs.lua | 81 - .../luasrc/view/ssrs/ssrs_status.htm | 22 - luci-app-ssrserver-python/po/zh-cn/ssrs.po | 35 - luci-app-ssrserver-python/po/zh_Hans | 1 - .../root/etc/config/ssrs | 11 - .../root/etc/init.d/ssrs | 96 - .../root/etc/uci-defaults/luci-ssrs | 28 - .../rpcd/acl.d/luci-app-ssrserver-python.json | 11 - .../root/usr/share/ssr/.travis.yml | 21 - .../root/usr/share/ssr/CHANGES | 342 - .../root/usr/share/ssr/CONTRIBUTING.md | 29 - .../root/usr/share/ssr/Dockerfile | 31 - .../root/usr/share/ssr/LICENSE | 202 - .../root/usr/share/ssr/MANIFEST.in | 3 - .../root/usr/share/ssr/README.md | 105 - .../root/usr/share/ssr/README.rst | 113 - .../root/usr/share/ssr/apiconfig.py | 15 - .../root/usr/share/ssr/asyncmgr.py | 99 - .../root/usr/share/ssr/config.json | 25 - .../root/usr/share/ssr/configloader.py | 15 - .../root/usr/share/ssr/db_transfer.py | 631 -- .../root/usr/share/ssr/debian/changelog | 5 - .../root/usr/share/ssr/debian/compat | 1 - .../root/usr/share/ssr/debian/config.json | 11 - .../root/usr/share/ssr/debian/control | 19 - .../root/usr/share/ssr/debian/copyright | 30 - .../root/usr/share/ssr/debian/docs | 2 - .../root/usr/share/ssr/debian/init.d | 149 - .../root/usr/share/ssr/debian/install | 1 - .../root/usr/share/ssr/debian/rules | 5 - .../usr/share/ssr/debian/shadowsocks.default | 12 - .../usr/share/ssr/debian/shadowsocks.manpages | 2 - .../root/usr/share/ssr/debian/source/format | 1 - .../root/usr/share/ssr/debian/sslocal.1 | 59 - .../root/usr/share/ssr/debian/ssserver.1 | 59 - .../root/usr/share/ssr/importloader.py | 24 - .../root/usr/share/ssr/initcfg.bat | 4 - .../root/usr/share/ssr/initcfg.sh | 8 - .../root/usr/share/ssr/initmudbjson.sh | 24 - .../root/usr/share/ssr/logrun.sh | 7 - .../root/usr/share/ssr/mudb.json | 2 - .../root/usr/share/ssr/mujson_mgr.py | 358 - .../root/usr/share/ssr/mysql.json | 13 - .../root/usr/share/ssr/run.sh | 7 - .../root/usr/share/ssr/server.py | 66 - .../root/usr/share/ssr/server_pool.py | 293 - .../root/usr/share/ssr/setup.py | 39 - .../root/usr/share/ssr/setup_cymysql.sh | 6 - .../usr/share/ssr/shadowsocks/__init__.py | 18 - .../usr/share/ssr/shadowsocks/asyncdns.py | 592 -- .../root/usr/share/ssr/shadowsocks/common.py | 418 - .../share/ssr/shadowsocks/crypto/__init__.py | 18 - .../shadowsocks/crypto/ctypes_libsodium.py | 135 - .../ssr/shadowsocks/crypto/ctypes_openssl.py | 188 - .../share/ssr/shadowsocks/crypto/openssl.py | 199 - .../share/ssr/shadowsocks/crypto/rc4_md5.py | 52 - .../share/ssr/shadowsocks/crypto/sodium.py | 140 - .../usr/share/ssr/shadowsocks/crypto/table.py | 181 - .../usr/share/ssr/shadowsocks/crypto/util.py | 139 - .../root/usr/share/ssr/shadowsocks/daemon.py | 208 - .../root/usr/share/ssr/shadowsocks/encrypt.py | 236 - .../usr/share/ssr/shadowsocks/encrypt_test.py | 51 - .../usr/share/ssr/shadowsocks/eventloop.py | 258 - .../root/usr/share/ssr/shadowsocks/local.py | 81 - .../root/usr/share/ssr/shadowsocks/logrun.sh | 8 - .../usr/share/ssr/shadowsocks/lru_cache.py | 179 - .../root/usr/share/ssr/shadowsocks/manager.py | 291 - .../root/usr/share/ssr/shadowsocks/obfs.py | 114 - .../ssr/shadowsocks/obfsplugin/__init__.py | 18 - .../share/ssr/shadowsocks/obfsplugin/auth.py | 787 -- .../ssr/shadowsocks/obfsplugin/auth_chain.py | 840 -- .../ssr/shadowsocks/obfsplugin/http_simple.py | 315 - .../ssr/shadowsocks/obfsplugin/obfs_tls.py | 305 - .../share/ssr/shadowsocks/obfsplugin/plain.py | 104 - .../ssr/shadowsocks/obfsplugin/verify.py | 154 - .../usr/share/ssr/shadowsocks/ordereddict.py | 214 - .../root/usr/share/ssr/shadowsocks/run.sh | 7 - .../root/usr/share/ssr/shadowsocks/server.py | 221 - .../root/usr/share/ssr/shadowsocks/shell.py | 451 - .../root/usr/share/ssr/shadowsocks/stop.sh | 5 - .../root/usr/share/ssr/shadowsocks/tail.sh | 3 - .../usr/share/ssr/shadowsocks/tcprelay.py | 1476 --- .../usr/share/ssr/shadowsocks/udprelay.py | 656 -- .../root/usr/share/ssr/shadowsocks/version.py | 20 - .../root/usr/share/ssr/stop.sh | 5 - .../root/usr/share/ssr/switchrule.py | 8 - .../root/usr/share/ssr/tail.sh | 3 - .../root/usr/share/ssr/tests/aes-cfb1.json | 10 - .../root/usr/share/ssr/tests/aes-cfb8.json | 10 - .../root/usr/share/ssr/tests/aes-ctr.json | 10 - .../root/usr/share/ssr/tests/aes.json | 10 - .../root/usr/share/ssr/tests/assert.sh | 148 - .../root/usr/share/ssr/tests/chacha20.json | 10 - .../ssr/tests/client-multi-server-ip.json | 10 - .../usr/share/ssr/tests/coverage_server.py | 45 - .../root/usr/share/ssr/tests/fastopen.json | 10 - .../usr/share/ssr/tests/ipv6-client-side.json | 10 - .../root/usr/share/ssr/tests/ipv6.json | 10 - .../root/usr/share/ssr/tests/jenkins.sh | 82 - .../usr/share/ssr/tests/libsodium/install.sh | 10 - .../root/usr/share/ssr/tests/nose_plugin.py | 43 - .../root/usr/share/ssr/tests/rc4-md5.json | 10 - .../root/usr/share/ssr/tests/salsa20-ctr.json | 10 - .../root/usr/share/ssr/tests/salsa20.json | 10 - .../server-multi-passwd-client-side.json | 8 - .../ssr/tests/server-multi-passwd-table.json | 19 - .../share/ssr/tests/server-multi-passwd.json | 17 - .../share/ssr/tests/server-multi-ports.json | 8 - .../root/usr/share/ssr/tests/setup_tc.sh | 18 - .../usr/share/ssr/tests/socksify/install.sh | 10 - .../usr/share/ssr/tests/socksify/socks.conf | 5 - .../root/usr/share/ssr/tests/table.json | 10 - .../root/usr/share/ssr/tests/test.py | 158 - .../root/usr/share/ssr/tests/test_command.sh | 32 - .../root/usr/share/ssr/tests/test_daemon.sh | 43 - .../usr/share/ssr/tests/test_large_file.sh | 24 - .../root/usr/share/ssr/tests/test_udp_src.py | 83 - .../root/usr/share/ssr/tests/test_udp_src.sh | 23 - .../root/usr/share/ssr/tests/workers.json | 10 - .../root/usr/share/ssr/utils/README.md | 9 - .../root/usr/share/ssr/utils/autoban.py | 56 - .../share/ssr/utils/fail2ban/shadowsocks.conf | 5 - .../ucitrack/luci-app-ssrserver-python.json | 4 - luci-app-unblockmusic/Makefile | 43 - .../luasrc/controller/unblockmusic.lua | 19 - .../model/cbi/unblockmusic/unblockmusic.lua | 128 - .../cbi/unblockmusic/unblockmusic_log.lua | 15 - .../view/unblockmusic/unblockmusic_status.htm | 22 - .../po/zh-cn/unblockmusic.po | 12 - luci-app-unblockmusic/po/zh_Hans | 1 - .../root/etc/config/unblockmusic | 8 - .../root/etc/hotplug.d/iface/099-unblockmusic | 7 - .../root/etc/init.d/unblockmusic | 261 - .../root/etc/uci-defaults/luci-unblockmusic | 26 - .../root/lib/upgrade/keep.d/unblockmusic | 6 - .../root/usr/bin/UnblockNeteaseMusicCloud | 8 - .../share/UnblockNeteaseMusic/getmusicip.sh | 4 - .../usr/share/UnblockNeteaseMusic/logcheck.sh | 19 - .../share/UnblockNeteaseMusic/update_core.sh | 67 - .../rpcd/acl.d/luci-app-unblockmusic.json | 11 - .../share/ucitrack/luci-app-unblockmusic.json | 4 - luci-app-v2ray-server/Makefile | 18 - .../luasrc/controller/v2ray_server.lua | 58 - .../luasrc/model/cbi/v2ray_server/api/app.lua | 109 - .../model/cbi/v2ray_server/api/gen_config.lua | 184 - .../model/cbi/v2ray_server/api/v2ray.lua | 304 - .../luasrc/model/cbi/v2ray_server/index.lua | 58 - .../luasrc/model/cbi/v2ray_server/user.lua | 315 - .../luasrc/view/v2ray_server/log.htm | 31 - .../view/v2ray_server/users_list_status.htm | 19 - .../luasrc/view/v2ray_server/v2ray.htm | 184 - .../po/zh-cn/v2ray_server.po | 185 - luci-app-v2ray-server/po/zh_Hans | 1 - .../root/etc/config/v2ray_server | 4 - .../root/etc/init.d/v2ray_server | 16 - .../root/etc/uci-defaults/luci-v2ray-server | 28 - .../rpcd/acl.d/luci-app-v2ray-server.json | 11 - .../share/ucitrack/luci-app-v2ray-server.json | 4 - luci-app-verysync/Makefile | 4 +- .../luasrc/controller/verysync.lua | 6 +- .../luasrc/model/cbi/verysync.lua | 3 - .../luasrc/view/verysync/verysync_status.htm | 17 +- luci-app-verysync/po/zh-cn/verysync.po | 3 - luci-app-verysync/root/etc/init.d/verysync | 9 +- .../root/etc/uci-defaults/luci-verysync | 19 - luci-app-webadmin/Makefile | 19 - .../luasrc/controller/webadmin.lua | 12 - .../luasrc/model/cbi/webadmin.lua | 53 - luci-app-webadmin/po/zh-cn/webadmin.po | 46 - luci-app-webadmin/po/zh_Hans | 1 - luci-lib-docker/Makefile | 20 - luci-lib-docker/luasrc/docker.lua | 453 - luci-lib-fs/Makefile | 49 - luci-lib-fs/files/fs.lua | 244 - 430 files changed, 1035 insertions(+), 48585 deletions(-) delete mode 100644 luci-app-accesscontrol/Makefile delete mode 100644 luci-app-accesscontrol/luasrc/controller/mia.lua delete mode 100644 luci-app-accesscontrol/luasrc/model/cbi/mia.lua delete mode 100644 luci-app-accesscontrol/luasrc/view/mia/mia_status.htm delete mode 100644 luci-app-accesscontrol/po/zh-cn/mia.po delete mode 100644 luci-app-accesscontrol/root/etc/config/mia delete mode 100755 luci-app-accesscontrol/root/etc/init.d/mia delete mode 100755 luci-app-accesscontrol/root/etc/mia.include delete mode 100644 luci-app-accesscontrol/root/etc/uci-defaults/luci-mia delete mode 100644 luci-app-accesscontrol/root/usr/share/rpcd/acl.d/luci-app-accesscontrol.json delete mode 100644 luci-app-accesscontrol/root/usr/share/ucitrack/luci-app-accesscontrol.json delete mode 100644 luci-app-adbyby-plus/Makefile delete mode 100644 luci-app-adbyby-plus/luasrc/controller/adbyby.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/advanced.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/base.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/black.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/block.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/esc.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/help.lua delete mode 100644 luci-app-adbyby-plus/luasrc/model/cbi/adbyby/user.lua delete mode 100644 luci-app-adbyby-plus/luasrc/view/adbyby/adbyby_status.htm delete mode 100644 luci-app-adbyby-plus/luasrc/view/adbyby/refresh.htm delete mode 100644 luci-app-adbyby-plus/po/zh-cn/adbyby.po delete mode 120000 luci-app-adbyby-plus/po/zh_Hans delete mode 100644 luci-app-adbyby-plus/root/etc/config/adbyby delete mode 100755 luci-app-adbyby-plus/root/etc/init.d/adbyby delete mode 100644 luci-app-adbyby-plus/root/etc/uci-defaults/luci-adbyby delete mode 100644 luci-app-adbyby-plus/root/lib/upgrade/keep.d/adbyby delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/ad-update delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/adblack.conf delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/adblock.sh delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/adbybyfirst.sh delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/adbybyupdate.sh delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/adesc.conf delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/adhost.conf delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/admem.sh delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/adupdate.sh delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/blockip.conf delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.adblock delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.ads delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.esc delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/firewall.include delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/rule-count delete mode 100755 luci-app-adbyby-plus/root/usr/share/adbyby/rule-update delete mode 100644 luci-app-adbyby-plus/root/usr/share/adbyby/rules.txt delete mode 100644 luci-app-adbyby-plus/root/usr/share/rpcd/acl.d/luci-app-adbyby-plus.json delete mode 100644 luci-app-adbyby-plus/root/usr/share/ucitrack/luci-app-adbyby-plus.json delete mode 100644 luci-app-aliyundrive-fuse/Makefile delete mode 100644 luci-app-aliyundrive-fuse/luasrc/controller/aliyundrive-fuse.lua delete mode 100644 luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/client.lua delete mode 100644 luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/log.lua delete mode 100644 luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_log.htm delete mode 100644 luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_status.htm delete mode 100644 luci-app-aliyundrive-fuse/po/zh-cn/aliyundrive-fuse.po delete mode 120000 luci-app-aliyundrive-fuse/po/zh_Hans delete mode 100644 luci-app-aliyundrive-fuse/root/etc/uci-defaults/luci-aliyundrive-fuse delete mode 100644 luci-app-aliyundrive-fuse/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-fuse.json delete mode 100644 luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_qrcode.htm delete mode 100644 luci-app-aliyundrive-webdav/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-webdav.json mode change 100644 => 100755 luci-app-clouddrive2/luasrc/controller/clouddrive2.lua create mode 100644 luci-app-clouddrive2/luasrc/model/clouddrive2.lua delete mode 120000 luci-app-clouddrive2/po/zh-cn create mode 100644 luci-app-clouddrive2/po/zh-cn/clouddrive2.po rename {luci-app-accesscontrol => luci-app-clouddrive2}/po/zh_Hans (100%) delete mode 100644 luci-app-clouddrive2/po/zh_Hans/clouddrive2.po create mode 100644 luci-app-clouddrive2/root/etc/config/clouddrive2 create mode 100755 luci-app-clouddrive2/root/etc/init.d/clouddrive2 delete mode 100644 luci-app-clouddrive2/root/etc/uci-defaults/luci-clouddrive2 create mode 100755 luci-app-clouddrive2/root/usr/libexec/istorec/clouddrive2.sh create mode 100644 luci-app-clouddrive2/root/usr/share/ucitrack/luci-app-clouddrive2.json delete mode 100644 luci-app-design-config/Makefile delete mode 100644 luci-app-design-config/luasrc/controller/design-config.lua delete mode 100644 luci-app-design-config/luasrc/model/cbi/design-config.lua delete mode 100644 luci-app-design-config/luasrc/view/design-config/other_button.htm delete mode 100644 luci-app-design-config/luasrc/view/design-config/other_dvalue.htm delete mode 100644 luci-app-design-config/po/zh-cn/design-config.po delete mode 120000 luci-app-design-config/po/zh_Hans delete mode 100644 luci-app-design-config/root/etc/config/design delete mode 100644 luci-app-design-config/root/etc/uci-defaults/luci-design-config delete mode 100644 luci-app-design-config/root/usr/share/rpcd/acl.d/luci-app-design-config.json delete mode 100644 luci-app-dnsforwarder/Makefile delete mode 100644 luci-app-dnsforwarder/luasrc/controller/dnsforwarder.lua delete mode 100644 luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/general.lua delete mode 100644 luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua delete mode 100644 luci-app-dnsforwarder/po/zh-cn/dnsforwarder.po delete mode 120000 luci-app-dnsforwarder/po/zh_Hans delete mode 100644 luci-app-e2guardian/Makefile delete mode 100644 luci-app-e2guardian/luasrc/controller/e2guardian.lua delete mode 100644 luci-app-e2guardian/luasrc/model/cbi/e2guardian.lua delete mode 100644 luci-app-easymesh/Makefile delete mode 100644 luci-app-easymesh/luasrc/controller/easymesh.lua delete mode 100644 luci-app-easymesh/luasrc/model/cbi/easymesh.lua delete mode 100755 luci-app-easymesh/po/zh-cn/easymesh.po delete mode 120000 luci-app-easymesh/po/zh_Hans delete mode 100644 luci-app-easymesh/root/etc/config/easymesh delete mode 100755 luci-app-easymesh/root/etc/init.d/easymesh delete mode 100644 luci-app-easymesh/root/etc/uci-defaults/luci-easymesh delete mode 100644 luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json delete mode 100644 luci-app-easymesh/root/usr/share/ucitrack/luci-app-easymesh.json delete mode 100644 luci-app-haproxy-tcp/Makefile delete mode 100644 luci-app-haproxy-tcp/luasrc/controller/haproxy.lua delete mode 100644 luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua delete mode 100644 luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po delete mode 120000 luci-app-haproxy-tcp/po/zh_Hans delete mode 100644 luci-app-haproxy-tcp/root/etc/config/haproxy delete mode 100755 luci-app-haproxy-tcp/root/etc/haproxy_init.sh delete mode 100755 luci-app-haproxy-tcp/root/etc/haproxy_start delete mode 100755 luci-app-haproxy-tcp/root/etc/uci-defaults/99_luci-haproxy delete mode 100644 luci-app-haproxy-tcp/root/usr/share/rpcd/acl.d/luci-app-haproxy-tcp.json delete mode 100644 luci-app-ltqtapi/Makefile delete mode 100644 luci-app-ltqtapi/luasrc/controller/ltqtapi.lua delete mode 100644 luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/account.lua delete mode 100644 luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/contact.lua delete mode 100644 luci-app-ltqtapi/luasrc/view/admin_status/index/telephony.htm delete mode 100644 luci-app-ltqtapi/luasrc/view/telephony_status.htm delete mode 100644 luci-app-mwan3helper/Makefile delete mode 100644 luci-app-mwan3helper/luasrc/controller/mwan3helper.lua delete mode 100644 luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua delete mode 100644 luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua delete mode 100644 luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm delete mode 100644 luci-app-mwan3helper/po/zh-cn/mwan3helper.po delete mode 120000 luci-app-mwan3helper/po/zh_Hans delete mode 100644 luci-app-mwan3helper/root/etc/config/mwan3helper delete mode 100755 luci-app-mwan3helper/root/etc/init.d/mwan3helper delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/all_cn.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/cernet.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/chinatelecom.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/cmcc.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/crtc.txt delete mode 100755 luci-app-mwan3helper/root/etc/mwan3helper/genipset.sh delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt delete mode 100644 luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt delete mode 100644 luci-app-mwan3helper/root/etc/uci-defaults/luci-mwan3helper delete mode 100644 luci-app-mwan3helper/root/usr/share/rpcd/acl.d/luci-app-mwan3helper.json delete mode 100644 luci-app-mwan3helper/root/usr/share/ucitrack/luci-app-mwan3helper.json delete mode 100644 luci-app-nft-qos/Makefile delete mode 100644 luci-app-nft-qos/luasrc/controller/nft-qos.lua delete mode 100644 luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua delete mode 100644 luci-app-nft-qos/luasrc/view/nft-qos/rate.htm delete mode 100644 luci-app-nft-qos/po/es/nft-qos.po delete mode 100644 luci-app-nft-qos/po/templates/nft-qos.pot delete mode 100644 luci-app-nft-qos/po/zh-cn/nft-qos.po delete mode 100644 luci-app-nft-qos/po/zh-tw/nft-qos.po delete mode 120000 luci-app-nft-qos/po/zh_Hans delete mode 100644 luci-app-nft-qos/root/etc/uci-defaults/luci-nft-qos delete mode 100644 luci-app-pgyvpn/Makefile delete mode 100644 luci-app-pgyvpn/luasrc/controller/pgyvpn.lua delete mode 100644 luci-app-pgyvpn/luasrc/model/cbi/pgyvpn.lua delete mode 100644 luci-app-pgyvpn/luasrc/pgyvpn/config.lua delete mode 100644 luci-app-pgyvpn/luasrc/view/pgyvpn/content.htm delete mode 100644 luci-app-pgyvpn/luasrc/view/pgyvpn/status.htm delete mode 100644 luci-app-pgyvpn/luasrc/view/pgyvpn/vpn.htm delete mode 100644 luci-app-pgyvpn/root/www/luci-static/resources/icons/pgy.png delete mode 100644 luci-app-phtunnel/Makefile delete mode 100644 luci-app-phtunnel/luasrc/controller/oray/phtunnel.lua delete mode 100644 luci-app-phtunnel/luasrc/model/cbi/oray/phtunnel_setup.lua delete mode 100644 luci-app-phtunnel/luasrc/phtunnel.lua delete mode 100644 luci-app-phtunnel/luasrc/view/oray/phtunnel_inner_status.htm delete mode 100644 luci-app-phtunnel/luasrc/view/oray/phtunnel_log.htm delete mode 100644 luci-app-phtunnel/luasrc/view/oray/phtunnel_log_off.htm delete mode 100644 luci-app-phtunnel/luasrc/view/oray/phtunnel_status.htm delete mode 100644 luci-app-phtunnel/po/zh-cn/phtunnel.po delete mode 120000 luci-app-phtunnel/po/zh_Hans delete mode 100644 luci-app-phtunnel/root/usr/share/rpcd/acl.d/luci-app-phtunnel.json delete mode 100644 luci-app-pppwn/Makefile delete mode 100644 luci-app-pppwn/luasrc/controller/pppwn.lua delete mode 100644 luci-app-pppwn/luasrc/model/cbi/pppwn/info.lua delete mode 100644 luci-app-pppwn/luasrc/model/cbi/pppwn/settings.lua delete mode 100644 luci-app-pppwn/luasrc/view/pppwn/pppwn_status.htm delete mode 100644 luci-app-pppwn/po/zh-cn/pppwn.po delete mode 120000 luci-app-pppwn/po/zh_Hans delete mode 100644 luci-app-pppwn/root/etc/config/pppwn delete mode 100755 luci-app-pppwn/root/etc/init.d/pppwn delete mode 100644 luci-app-pppwn/root/etc/uci-defaults/luci-pppwn delete mode 100644 luci-app-pppwn/root/usr/share/rpcd/acl.d/luci-app-pppwn.json delete mode 100644 luci-app-pppwn/root/usr/share/ucitrack/luci-app-pppwn.json delete mode 100644 luci-app-pppwn/root/www/goldhen.bin delete mode 100644 luci-app-pptp-server/luasrc/controller/pptp-server.lua create mode 100644 luci-app-pptp-server/luasrc/controller/pptpd.lua delete mode 100644 luci-app-pptp-server/luasrc/model/cbi/pptp-server.lua create mode 100644 luci-app-pptp-server/luasrc/model/cbi/pptpd/online.lua create mode 100644 luci-app-pptp-server/luasrc/model/cbi/pptpd/settings.lua create mode 100644 luci-app-pptp-server/luasrc/model/cbi/pptpd/user.lua create mode 100644 luci-app-pptp-server/luasrc/model/cbi/pptpd/users.lua delete mode 100644 luci-app-pptp-server/luasrc/view/pptp-server/pptp-server_status.htm create mode 100644 luci-app-pptp-server/luasrc/view/pptpd/index.htm create mode 100644 luci-app-pptp-server/luasrc/view/pptpd/status.htm delete mode 100644 luci-app-pptp-server/po/zh-cn/pptp.po create mode 100644 luci-app-pptp-server/po/zh-cn/pptpd.po create mode 100644 luci-app-pptp-server/root/etc/config/luci-app-pptp-server delete mode 100644 luci-app-pptp-server/root/etc/config/pptpd create mode 100755 luci-app-pptp-server/root/etc/init.d/luci-app-pptp-server delete mode 100755 luci-app-pptp-server/root/etc/pptpd.include create mode 100755 luci-app-pptp-server/root/etc/uci-defaults/luci-app-pptp-server delete mode 100644 luci-app-pptp-server/root/etc/uci-defaults/luci-pptpd create mode 100755 luci-app-pptp-server/root/usr/share/pptpd/ip-down create mode 100755 luci-app-pptp-server/root/usr/share/pptpd/ip-up create mode 100644 luci-app-pptp-server/root/usr/share/ucitrack/luci-app-pptp-server.json create mode 100644 luci-app-pushbot/README.md delete mode 100755 luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json delete mode 100644 luci-app-shadowsocks-libev/Makefile delete mode 100644 luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua delete mode 100644 luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm delete mode 100644 luci-app-shadowsocks-libev/root/etc/uci-defaults/40_luci-shadowsocks-libev delete mode 100644 luci-app-shairplay/Makefile delete mode 100644 luci-app-shairplay/luasrc/controller/shairplay.lua delete mode 100644 luci-app-shairplay/luasrc/model/cbi/shairplay.lua delete mode 100644 luci-app-shairplay/po/ja/shairplay.po delete mode 100644 luci-app-shairplay/po/pt-br/shairplay.po delete mode 100644 luci-app-shairplay/po/ru/shairplay.po delete mode 100644 luci-app-shairplay/po/templates/shairplay.pot delete mode 100644 luci-app-shairplay/root/etc/uci-defaults/40_luci-shairplay delete mode 100644 luci-app-smartinfo/Makefile delete mode 100644 luci-app-smartinfo/luasrc/controller/smartinfo.lua delete mode 100644 luci-app-smartinfo/luasrc/model/cbi/smartinfo.lua delete mode 100644 luci-app-smartinfo/luasrc/view/admin_status/index/smartinfo.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_flag.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_value.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/detail_end_listvalue.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/detail_end_value.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/smart_detail.htm delete mode 100644 luci-app-smartinfo/luasrc/view/smartinfo/smart_status.htm delete mode 100644 luci-app-smartinfo/po/zh-cn/smartinfo.po delete mode 120000 luci-app-smartinfo/po/zh_Hans delete mode 100644 luci-app-smartinfo/root/etc/config/smartinfo delete mode 100755 luci-app-smartinfo/root/etc/init.d/smartinfo delete mode 100644 luci-app-smartinfo/root/usr/lib/smartinfo/smart_checker.sh delete mode 100644 luci-app-smartinfo/root/usr/lib/smartinfo/smart_functions.sh delete mode 100644 luci-app-smartinfo/root/usr/lib/smartinfo/smart_status.sh delete mode 100644 luci-app-smartinfo/root/usr/share/rpcd/acl.d/luci-app-smartinfo.json delete mode 100644 luci-app-smartinfo/root/usr/share/ucitrack/luci-app-smartinfo.json delete mode 100644 luci-app-ssrserver-python/Makefile delete mode 100644 luci-app-ssrserver-python/luasrc/controller/ssrs.lua delete mode 100644 luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua delete mode 100644 luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm delete mode 100644 luci-app-ssrserver-python/po/zh-cn/ssrs.po delete mode 120000 luci-app-ssrserver-python/po/zh_Hans delete mode 100644 luci-app-ssrserver-python/root/etc/config/ssrs delete mode 100755 luci-app-ssrserver-python/root/etc/init.d/ssrs delete mode 100644 luci-app-ssrserver-python/root/etc/uci-defaults/luci-ssrs delete mode 100644 luci-app-ssrserver-python/root/usr/share/rpcd/acl.d/luci-app-ssrserver-python.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/CHANGES delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/LICENSE delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/README.md delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/README.rst delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/config.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/configloader.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/compat delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/control delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/docs delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/install delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/debian/rules delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1 delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1 delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/importloader.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/mudb.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/mysql.json delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/run.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/server.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/setup.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/setup_cymysql.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/__init__.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/asyncdns.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/util.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/daemon.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth_chain.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/obfs_tls.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/stop.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tail.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md delete mode 100755 luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py delete mode 100644 luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf delete mode 100644 luci-app-ssrserver-python/root/usr/share/ucitrack/luci-app-ssrserver-python.json delete mode 100644 luci-app-unblockmusic/Makefile delete mode 100644 luci-app-unblockmusic/luasrc/controller/unblockmusic.lua delete mode 100644 luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic.lua delete mode 100644 luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic_log.lua delete mode 100644 luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm delete mode 100644 luci-app-unblockmusic/po/zh-cn/unblockmusic.po delete mode 120000 luci-app-unblockmusic/po/zh_Hans delete mode 100644 luci-app-unblockmusic/root/etc/config/unblockmusic delete mode 100755 luci-app-unblockmusic/root/etc/hotplug.d/iface/099-unblockmusic delete mode 100755 luci-app-unblockmusic/root/etc/init.d/unblockmusic delete mode 100644 luci-app-unblockmusic/root/etc/uci-defaults/luci-unblockmusic delete mode 100644 luci-app-unblockmusic/root/lib/upgrade/keep.d/unblockmusic delete mode 100755 luci-app-unblockmusic/root/usr/bin/UnblockNeteaseMusicCloud delete mode 100755 luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/getmusicip.sh delete mode 100755 luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh delete mode 100755 luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh delete mode 100644 luci-app-unblockmusic/root/usr/share/rpcd/acl.d/luci-app-unblockmusic.json delete mode 100644 luci-app-unblockmusic/root/usr/share/ucitrack/luci-app-unblockmusic.json delete mode 100644 luci-app-v2ray-server/Makefile delete mode 100644 luci-app-v2ray-server/luasrc/controller/v2ray_server.lua delete mode 100755 luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/app.lua delete mode 100755 luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/gen_config.lua delete mode 100755 luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua delete mode 100644 luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua delete mode 100644 luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/user.lua delete mode 100644 luci-app-v2ray-server/luasrc/view/v2ray_server/log.htm delete mode 100644 luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm delete mode 100644 luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm delete mode 100644 luci-app-v2ray-server/po/zh-cn/v2ray_server.po delete mode 120000 luci-app-v2ray-server/po/zh_Hans delete mode 100644 luci-app-v2ray-server/root/etc/config/v2ray_server delete mode 100755 luci-app-v2ray-server/root/etc/init.d/v2ray_server delete mode 100644 luci-app-v2ray-server/root/etc/uci-defaults/luci-v2ray-server delete mode 100644 luci-app-v2ray-server/root/usr/share/rpcd/acl.d/luci-app-v2ray-server.json delete mode 100644 luci-app-v2ray-server/root/usr/share/ucitrack/luci-app-v2ray-server.json delete mode 100644 luci-app-verysync/root/etc/uci-defaults/luci-verysync delete mode 100644 luci-app-webadmin/Makefile delete mode 100644 luci-app-webadmin/luasrc/controller/webadmin.lua delete mode 100644 luci-app-webadmin/luasrc/model/cbi/webadmin.lua delete mode 100644 luci-app-webadmin/po/zh-cn/webadmin.po delete mode 120000 luci-app-webadmin/po/zh_Hans delete mode 100644 luci-lib-docker/Makefile delete mode 100644 luci-lib-docker/luasrc/docker.lua delete mode 100644 luci-lib-fs/Makefile delete mode 100644 luci-lib-fs/files/fs.lua diff --git a/honk/Makefile b/honk/Makefile index 7e64bdb30..d8f8d5830 100644 --- a/honk/Makefile +++ b/honk/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=honk PKG_VERSION:=2026.10.6_pre1 -PKG_RELEASE:=9 +PKG_RELEASE:=10 # Raw upstream release tag. It keeps the dots in qualifiers (e.g. v0.0.1.beta.49) # because that is what the GitHub release asset name uses. diff --git a/honk/files/honk.init b/honk/files/honk.init index 0bfd416b6..33434a9a0 100644 --- a/honk/files/honk.init +++ b/honk/files/honk.init @@ -33,22 +33,25 @@ cleanup_netns() { hijack_resolv_conf() { grep -q ' /tmp/resolv\.conf ' /proc/mounts && return 0 - grep -E '^(search|domain) ' /tmp/resolv.conf > "$RESOLV_HONK" 2>/dev/null - - local dns_list LOGICAL_WAN + local dns_list WANS . /lib/functions/network.sh - network_find_wan LOGICAL_WAN 2>/dev/null - if [ -n "$LOGICAL_WAN" ]; then - dns_list=$(ubus call "network.interface.$LOGICAL_WAN" status 2>/dev/null | \ - jsonfilter -e '@["dns-server"][*]' 2>/dev/null) - fi + unset -v __NETWORK_CACHE + __network_ifstatus WANS "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" 2>/dev/null + set -- $WANS - if [ -z "$dns_list" ] && [ -f "/tmp/resolv.conf.d/resolv.conf.auto" ]; then - dns_list=$(awk '/^nameserver/{print $2}' "/tmp/resolv.conf.d/resolv.conf.auto") + if [ $# -eq 1 ]; then + dns_list=$(ubus call "network.interface.$1" status 2>/dev/null | \ + jsonfilter -e '@["dns-server"][*]' 2>/dev/null) + if [ -z "$dns_list" ] && [ -f "/tmp/resolv.conf.d/resolv.conf.auto" ]; then + dns_list=$(awk '/^nameserver/{print $2}' "/tmp/resolv.conf.d/resolv.conf.auto") + fi fi [ -z "$dns_list" ] && dns_list="119.29.29.29 223.5.5.5" + [ -f /tmp/resolv.conf ] || touch /tmp/resolv.conf + grep -E '^(search|domain) ' /tmp/resolv.conf > "$RESOLV_HONK" 2>/dev/null + for dns in $dns_list; do echo "nameserver $dns" >> "$RESOLV_HONK" done @@ -58,9 +61,10 @@ hijack_resolv_conf() { } restore_resolv_conf() { - grep -q ' /tmp/resolv\.conf ' /proc/mounts || return 0 - mount -o remount,rw,bind /tmp/resolv.conf 2>/dev/null - umount /tmp/resolv.conf 2>/dev/null + while grep -q ' /tmp/resolv\.conf ' /proc/mounts; do + mount -o remount,rw,bind /tmp/resolv.conf 2>/dev/null + umount /tmp/resolv.conf 2>/dev/null || break + done rm -f "$RESOLV_HONK" } diff --git a/luci-app-accesscontrol/Makefile b/luci-app-accesscontrol/Makefile deleted file mode 100644 index d6bdcb552..000000000 --- a/luci-app-accesscontrol/Makefile +++ /dev/null @@ -1,19 +0,0 @@ - -# Copyright (C) 2016 Openwrt.org -# -# This is free software, licensed under the Apache License, Version 2.0 . -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI Access Control Configuration -LUCI_DEPENDS:= -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-accesscontrol -PKG_VERSION:=1 -PKG_RELEASE:=2 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-accesscontrol/luasrc/controller/mia.lua b/luci-app-accesscontrol/luasrc/controller/mia.lua deleted file mode 100644 index 60aa95db0..000000000 --- a/luci-app-accesscontrol/luasrc/controller/mia.lua +++ /dev/null @@ -1,17 +0,0 @@ -module("luci.controller.mia", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/mia") then - return - end - - entry({"admin", "services", "mia"}, cbi("mia"), _("Internet Access Schedule Control"), 30).dependent = true - entry({"admin", "services", "mia", "status"}, call("act_status")).leaf = true -end - -function act_status() - local e = {} - e.running = luci.sys.call("iptables -L INPUT |grep MIA >/dev/null") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end diff --git a/luci-app-accesscontrol/luasrc/model/cbi/mia.lua b/luci-app-accesscontrol/luasrc/model/cbi/mia.lua deleted file mode 100644 index 0d49867e0..000000000 --- a/luci-app-accesscontrol/luasrc/model/cbi/mia.lua +++ /dev/null @@ -1,68 +0,0 @@ -a = Map("mia") -a.title = translate("Internet Access Schedule Control") -a.description = translate("Access Schedule Control Settins") - -a:section(SimpleSection).template = "mia/mia_status" - -t = a:section(TypedSection, "basic") -t.anonymous = true - -e = t:option(Flag, "enable", translate("Enabled")) -e.rmempty = false - -e = t:option(Flag, "strict", translate("Strict Mode")) -e.description = translate("Strict Mode will degrade CPU performance, but it can achieve better results") -e.rmempty = false - -t = a:section(TypedSection, "macbind", translate("Client Rules")) -t.template = "cbi/tblsection" -t.anonymous = true -t.addremove = true - -e = t:option(Flag, "enable", translate("Enabled")) -e.rmempty = false -e.default = "1" - -e = t:option(Value, "macaddr", translate("MAC address (Computer Name)")) -e.rmempty = true -luci.sys.net.mac_hints(function(t,a) -e:value(t,"%s (%s)"%{t,a}) -end) - -e = t:option(Value, "timeon", translate("Start time")) -e.optional = false -e.default = "00:00" - -e = t:option(Value, "timeoff", translate("End time")) -e.optional = false -e.default = "23:59" - -e = t:option(Flag, "z1", translate("Mon")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z2", translate("Tue")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z3", translate("Wed")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z4", translate("Thu")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z5", translate("Fri")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z6", translate("Sat")) -e.rmempty = true -e.default = 1 - -e = t:option(Flag, "z7", translate("Sun")) -e.rmempty = true -e.default = 1 - -return a diff --git a/luci-app-accesscontrol/luasrc/view/mia/mia_status.htm b/luci-app-accesscontrol/luasrc/view/mia/mia_status.htm deleted file mode 100644 index c971ee3c3..000000000 --- a/luci-app-accesscontrol/luasrc/view/mia/mia_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-accesscontrol/po/zh-cn/mia.po b/luci-app-accesscontrol/po/zh-cn/mia.po deleted file mode 100644 index 8f7c16aae..000000000 --- a/luci-app-accesscontrol/po/zh-cn/mia.po +++ /dev/null @@ -1,50 +0,0 @@ -msgid "Internet Access Schedule Control" -msgstr "上网时间控制" - -msgid "Access Schedule Control Settins" -msgstr "设置客户端禁止访问互联网的时间" - -msgid "General switch" -msgstr "开启/关闭" - -msgid "Strict Mode" -msgstr "严格模式" - -msgid "Strict Mode will degrade CPU performance, but it can achieve better results" -msgstr "严格模式会损耗部分CPU资源,但可以按照时间规则立即拦截数据包,效果更好" - -msgid "Client Rules" -msgstr "客户端规则" - -msgid "Description" -msgstr "描述" - -msgid "MAC address (Computer Name)" -msgstr "MAC 地址 (主机名)" - -msgid "Start time" -msgstr "开始时间" - -msgid "End time" -msgstr "结束时间" - -msgid "Mon" -msgstr "一" - -msgid "Tue" -msgstr "二" - -msgid "Wed" -msgstr "三" - -msgid "Thu" -msgstr "四" - -msgid "Fri" -msgstr "五" - -msgid "Sat" -msgstr "六" - -msgid "Sun" -msgstr "日" diff --git a/luci-app-accesscontrol/root/etc/config/mia b/luci-app-accesscontrol/root/etc/config/mia deleted file mode 100644 index 0c4c4f6bc..000000000 --- a/luci-app-accesscontrol/root/etc/config/mia +++ /dev/null @@ -1,3 +0,0 @@ - -config basic - option enable '0' diff --git a/luci-app-accesscontrol/root/etc/init.d/mia b/luci-app-accesscontrol/root/etc/init.d/mia deleted file mode 100755 index 7a84f51f9..000000000 --- a/luci-app-accesscontrol/root/etc/init.d/mia +++ /dev/null @@ -1,88 +0,0 @@ -#!/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=30 - -CONFIG=mia - -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} -} - -add_rule(){ -for i in $(seq 0 100) -do - local enable=$(uci_get_by_type macbind enable '' $i) - local macaddr=$(uci_get_by_type macbind macaddr '' $i) - local timeon=$(uci_get_by_type macbind timeon '' $i) - local timeoff=$(uci_get_by_type macbind timeoff '' $i) - local z1=$(uci_get_by_type macbind z1 '' $i) - local z2=$(uci_get_by_type macbind z2 '' $i) - local z3=$(uci_get_by_type macbind z3 '' $i) - local z4=$(uci_get_by_type macbind z4 '' $i) - local z5=$(uci_get_by_type macbind z5 '' $i) - local z6=$(uci_get_by_type macbind z6 '' $i) - local z7=$(uci_get_by_type macbind z7 '' $i) - [ "$z1" == "1" ] && Z1="Mon," - [ "$z2" == "1" ] && Z2="Tue," - [ "$z3" == "1" ] && Z3="Wed," - [ "$z4" == "1" ] && Z4="Thu," - [ "$z5" == "1" ] && Z5="Fri," - [ "$z6" == "1" ] && Z6="Sat," - [ "$z7" == "1" ] && Z7="Sun" - if [ -z $enable ] || [ -z $macaddr ] || [ -z $timeoff ] || [ -z $timeon ]; then - break - fi - if [ "$enable" == "1" ]; then - iptables -t filter -I MIA -m mac --mac-source $macaddr -m time --kerneltz --timestart $timeon --timestop $timeoff --weekdays $Z1$Z2$Z3$Z4$Z5$Z6$Z7 -j DROP - fi - for n in $(seq 1 7) - do - unset "Z$n" - done -done -} - -del_rule(){ - type=$1 - blackMacAdd=$(iptables -t nat -L $type | grep -w RETURN | grep -w "MAC" | awk '{print $7}') - [ -n "$blackMacAdd" ] && { - for macaddrb in $blackMacAdd - do - iptables -t nat -D $type -m mac --mac-source $macaddrb -j RETURN - done - } -} - -start(){ - stop - enable=$(uci get mia.@basic[0].enable) - [ $enable -eq 0 ] && exit 0 - iptables -t filter -N MIA - iptables -I INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIA - iptables -I INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIA - iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" - iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" - strict=$(uci get mia.@basic[0].strict) - [ $strict -eq 1 ] && iptables -t filter -I FORWARD -m comment --comment "Rule For Control" -j MIA - add_rule -} -stop(){ - iptables -t filter -D FORWARD -m comment --comment "Rule For Control" -j MIA 2>/dev/null - iptables -D INPUT -p udp --dport 53 -m comment --comment "Rule For Control" -j MIA 2>/dev/null - iptables -D INPUT -p tcp --dport 53 -m comment --comment "Rule For Control" -j MIA 2>/dev/null - iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" 2>/dev/null - iptables -t nat -D PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "Rule For Control" 2>/dev/null - iptables -t filter -F MIA 2>/dev/null - iptables -t filter -X MIA 2>/dev/null -} diff --git a/luci-app-accesscontrol/root/etc/mia.include b/luci-app-accesscontrol/root/etc/mia.include deleted file mode 100755 index 359bbd6f0..000000000 --- a/luci-app-accesscontrol/root/etc/mia.include +++ /dev/null @@ -1 +0,0 @@ -/etc/init.d/mia restart diff --git a/luci-app-accesscontrol/root/etc/uci-defaults/luci-mia b/luci-app-accesscontrol/root/etc/uci-defaults/luci-mia deleted file mode 100644 index 6737992c8..000000000 --- a/luci-app-accesscontrol/root/etc/uci-defaults/luci-mia +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-mia.json" ] && { - cat > /usr/share/ucitrack/luci-app-mia.json << EEOF -{ - "config": "mia", - "init": "mia" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@mia[-1] - add ucitrack mia - set ucitrack.@mia[-1].init=mia - commit ucitrack - - delete firewall.mia - set firewall.mia=include - set firewall.mia.type=script - set firewall.mia.path=/etc/mia.include - set firewall.mia.reload=1 - commit firewall -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-accesscontrol/root/usr/share/rpcd/acl.d/luci-app-accesscontrol.json b/luci-app-accesscontrol/root/usr/share/rpcd/acl.d/luci-app-accesscontrol.json deleted file mode 100644 index f20a3c340..000000000 --- a/luci-app-accesscontrol/root/usr/share/rpcd/acl.d/luci-app-accesscontrol.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-accesscontrol": { - "description": "Grant UCI access for luci-app-accesscontrol", - "read": { - "uci": [ "mia" ] - }, - "write": { - "uci": [ "mia" ] - } - } -} diff --git a/luci-app-accesscontrol/root/usr/share/ucitrack/luci-app-accesscontrol.json b/luci-app-accesscontrol/root/usr/share/ucitrack/luci-app-accesscontrol.json deleted file mode 100644 index ddf7c6206..000000000 --- a/luci-app-accesscontrol/root/usr/share/ucitrack/luci-app-accesscontrol.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "mia", - "init": "mia" -} diff --git a/luci-app-adbyby-plus/Makefile b/luci-app-adbyby-plus/Makefile deleted file mode 100644 index ce5d1ae41..000000000 --- a/luci-app-adbyby-plus/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2020 Openwrt.org -# -# This is free software, licensed under the Apache License, Version 2.0 . -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI support for Adbyby -LUCI_DEPENDS:=+adbyby +uclient-fetch +ipset +dnsmasq-full -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-adbyby-plus -PKG_VERSION:=2.0 -PKG_RELEASE:=1 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-adbyby-plus/luasrc/controller/adbyby.lua b/luci-app-adbyby-plus/luasrc/controller/adbyby.lua deleted file mode 100644 index 85adb1466..000000000 --- a/luci-app-adbyby-plus/luasrc/controller/adbyby.lua +++ /dev/null @@ -1,86 +0,0 @@ - -module("luci.controller.adbyby", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/adbyby") then - return - end - - entry({"admin", "services", "adbyby"}, alias("admin", "services", "adbyby", "base"), _("ADBYBY Plus +"), 9).dependent = true - - entry({"admin", "services", "adbyby", "base"}, cbi("adbyby/base"), _("Base Setting"), 10).leaf = true - entry({"admin", "services", "adbyby", "advanced"}, cbi("adbyby/advanced"), _("Advance Setting"), 20).leaf = true - entry({"admin", "services", "adbyby", "help"}, form("adbyby/help"), _("Plus+ Domain List"), 30).leaf = true - entry({"admin", "services", "adbyby", "esc"}, form("adbyby/esc"), _("Bypass Domain List"), 40).leaf = true - entry({"admin", "services", "adbyby", "black"}, form("adbyby/black"), _("Block Domain List"), 50).leaf = true - entry({"admin", "services", "adbyby", "block"}, form("adbyby/block"), _("Block IP List"), 60).leaf = true - entry({"admin", "services", "adbyby", "user"}, form("adbyby/user"), _("User-defined Rule"), 70).leaf = true - - entry({"admin", "services", "adbyby", "refresh"}, call("refresh_data")) - entry({"admin", "services", "adbyby", "run"}, call("act_status")).leaf = true -end - -function act_status() - local e = {} - e.running = luci.sys.call("pgrep adbyby >/dev/null") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end - - -function refresh_data() - local set = luci.http.formvalue("set") - local icount = 0 - -if set == "rule_data" then -luci.sys.exec("/usr/share/adbyby/rule-update") - icount = luci.sys.exec("/usr/share/adbyby/rule-count '/tmp/rules/'") - - if tonumber(icount)>0 then - if nixio.fs.access("/usr/share/adbyby/rules/") then - oldcount = luci.sys.exec("/usr/share/adbyby/rule-count '/usr/share/adbyby/rules/'") - else - oldcount=0 - end - else - retstring ="-1" - end - - if tonumber(icount) ~= tonumber(oldcount) then - luci.sys.exec("rm -f /usr/share/adbyby/rules/data/* /usr/share/adbyby/rules/host/* && cp -a /tmp/rules /usr/share/adbyby/") - luci.sys.exec("/etc/init.d/adbyby restart &") - retstring=tostring(math.ceil(tonumber(icount))) - else - retstring ="0" - end -else -refresh_cmd = "uclient-fetch -q --no-check-certificate -O - 'https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt' > /tmp/adnew.conf" -sret = luci.sys.call(refresh_cmd .. " 2>/dev/null") -if sret== 0 then - luci.sys.call("/usr/share/adbyby/ad-update") - icount = luci.sys.exec("cat /tmp/ad.conf | wc -l") - if tonumber(icount)>0 then - if nixio.fs.access("/usr/share/adbyby/dnsmasq.adblock") then - oldcount = luci.sys.exec("cat /usr/share/adbyby/dnsmasq.adblock | wc -l") - else - oldcount=0 - end - if tonumber(icount) ~= tonumber(oldcount) then - luci.sys.exec("cp -f /tmp/ad.conf /usr/share/adbyby/dnsmasq.adblock") - luci.sys.exec("cp -f /tmp/ad.conf /tmp/etc/dnsmasq-adbyby.d/adblock") - luci.sys.exec("/etc/init.d/adbyby restart &") - retstring=tostring(math.ceil(tonumber(icount))) - else - retstring ="0" - end - else - retstring ="-1" - end - luci.sys.exec("rm -f /tmp/ad.conf") -else - retstring ="-1" -end -end -luci.http.prepare_content("application/json") -luci.http.write_json({ ret=retstring ,retcount=icount}) -end diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/advanced.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/advanced.lua deleted file mode 100644 index 0c14beefd..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/advanced.lua +++ /dev/null @@ -1,67 +0,0 @@ - -local SYS = require "luci.sys" -local ND = SYS.exec("cat /usr/share/adbyby/dnsmasq.adblock | wc -l") - -local ad_count=0 -if nixio.fs.access("/usr/share/adbyby/dnsmasq.adblock") then -ad_count=tonumber(SYS.exec("cat /usr/share/adbyby/dnsmasq.adblock | wc -l")) -end - -local rule_count=0 -if nixio.fs.access("/usr/share/adbyby/rules/") then -rule_count=tonumber(SYS.exec("/usr/share/adbyby/rule-count '/usr/share/adbyby/rules/'")) -end - -m = Map("adbyby") - -s = m:section(TypedSection, "adbyby") -s.anonymous = true - -o = s:option(Flag, "block_ios") -o.title = translate("Block Apple iOS OTA update") -o.default = 0 -o.rmempty = false - -o = s:option(Flag, "block_cnshort") -o.title = translate("Block CNshort APP and Website") -o.default = 0 -o.rmempty = false - -o = s:option(Flag, "cron_mode") -o.title = translate("Update the rule at 6 a.m. every morning and restart adbyby") -o.default = 0 -o.rmempty = false - -o=s:option(DummyValue,"ad_data",translate("Adblock Plus Data")) -o.rawhtml = true -o.template = "adbyby/refresh" -o.value =ad_count .. " " .. translate("Records") - -o=s:option(DummyValue,"rule_data",translate("Subscribe 3rd Rules Data")) -o.rawhtml = true -o.template = "adbyby/refresh" -o.value =rule_count .. " " .. translate("Records") -o.description = translate("AdGuardHome / Host / DNSMASQ rules auto-convert") - -o = s:option(Button,"delete",translate("Delete All Subscribe Rules")) -o.inputstyle = "reset" -o.write = function() - SYS.exec("rm -f /usr/share/adbyby/rules/data/* /usr/share/adbyby/rules/host/*") - SYS.exec("/etc/init.d/adbyby restart 2>&1 &") - luci.http.redirect(luci.dispatcher.build_url("admin", "services", "adbyby", "advanced")) -end - -o = s:option(DynamicList, "subscribe_url", translate("Anti-AD Rules Subscribe")) -o:value("https://cdn.jsdelivr.net/gh/cjx82630/cjxlist/cjx-annoyance.txt", translate("cjx-annoyance")) -o:value("https://easylist-downloads.adblockplus.org/easylistchina.txt", translate("easylistchina")) -o:value("https://easylist-downloads.adblockplus.org/easyprivacy.txt", translate("easyprivacy")) -o:value("https://easylist-downloads.adblockplus.org/easylist.txt", translate("easylist")) -o:value("https://raw.githubusercontent.com/Spam404/lists/master/adblock-list.txt", translate("adblock-list")) -o:value("https://raw.githubusercontent.com/bongochong/CombinedPrivacyBlockLists/master/cpbl-abp-list.txt", translate("cpbl-abp-list")) -o:value("https://anti-ad.net/easylist.txt", translate("anti-ad")) -o:value("https://anti-ad.net/anti-ad-for-dnsmasq.conf", translate("anti-ad-for-dnsmasq")) -o:value("https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/nocoin.txt", translate("nocoin")) -o:value("https://easylist-downloads.adblockplus.org/abp-filters-anti-cv.txt", translate("abp-filters-anti-cv")) -o.rmempty = true - -return m diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/base.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/base.lua deleted file mode 100644 index b9872cb19..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/base.lua +++ /dev/null @@ -1,66 +0,0 @@ -local NXFS = require "nixio.fs" -local SYS = require "luci.sys" -local HTTP = require "luci.http" -local DISP = require "luci.dispatcher" - -local DL = SYS.exec("head -1 /tmp/adbyby/data/lazy.txt | awk -F' ' '{print $3,$4}'") or "" -local DV = SYS.exec("head -1 /tmp/adbyby/data/video.txt | awk -F' ' '{print $3,$4}'") or "" -local NR = SYS.exec("grep -v '^!' /usr/share/adbyby/data/rules.txt | wc -l") -local NU = SYS.exec("cat /usr/share/adbyby/data/user.txt | wc -l") -local UD = NXFS.readfile("/tmp/adbyby.updated") or "1970-01-01 00:00:00" - -m = Map("adbyby") -m.title = translate("Adbyby Plus + Settings") -m.description = translate("Adbyby Plus + can filter all kinds of banners, popups, video ads, and prevent tracking, privacy theft and a variety of malicious websites
Plus + version combination mode can operation with Adblock Plus Host,filtering ads without losing bandwidth") - -m:section(SimpleSection).template = "adbyby/adbyby_status" - -s = m:section(TypedSection, "adbyby") -s.anonymous = true - -o = s:option(Flag, "enable", translate("Enable")) -o.default = 0 -o.rmempty = false - -o = s:option(ListValue, "wan_mode", translate("Running Mode")) -o:value("0", translate("Global Mode (The slowest and the best effects)")) -o:value("1", translate("Plus + Mode (Filter domain name list and blacklist website.Recommended)")) -o:value("2", translate("No filter Mode (Must set in Client Filter Mode Settings manually)")) -o.default = 1 -o.rmempty = false - -o = s:option(Button, "restart", translate("Adbyby and Rule state")) -o.inputtitle = translate("Update Adbyby Rules Manually") -o.description = string.format(""..translate("Last Update Checked")..": %s
"..translate("Lazy Rule")..":%s
"..translate("Video Rule")..":%s", UD, DL, DV) -o.inputstyle = "reload" -o.write = function() - SYS.call("rm -rf /tmp/adbyby.updated /tmp/adbyby/admd5.json && /usr/share/adbyby/adbybyupdate.sh > /tmp/adupdate.log 2>&1 &") - SYS.call("sleep 5") - HTTP.redirect(DISP.build_url("admin", "services", "adbyby")) -end - -t = m:section(TypedSection, "acl_rule", translate("Client Filter Mode Settings")) -t.description = translate("Filter mode settings can be set to specific LAN clients ( No filter , Global filter ) . Does not need to be set by default.") -t.template = "cbi/tblsection" -t.sortable = true -t.anonymous = true -t.addremove = true - -e = t:option(Value, "ipaddr", translate("IP Address")) -e.width = "40%" -e.datatype = "ip4addr" -e.placeholder = "0.0.0.0/0" -luci.ip.neighbors({ family = 4 }, function(entry) - if entry.reachable then - e:value(entry.dest:string()) - end -end) - -e = t:option(ListValue, "filter_mode", translate("Filter Mode")) -e.width = "40%" -e.default = "disable" -e.rmempty = false -e:value("disable", translate("No filter")) -e:value("global", translate("Global filter")) - -return m diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/black.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/black.lua deleted file mode 100644 index 3fd32ec4e..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/black.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/usr/share/adbyby/adblack.conf" - -f = SimpleForm("custom") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 13 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end - -function f.handle(self, state, data) - if state == FORM_VALID then - if data.conf then - fs.writefile(conffile, data.conf:gsub("\r\n", "\n")) - luci.sys.call("/etc/init.d/adbyby restart") - end - end - return true -end - -return f diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/block.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/block.lua deleted file mode 100644 index dd715fd88..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/block.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/usr/share/adbyby/blockip.conf" - -f = SimpleForm("custom") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 13 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end - -function f.handle(self, state, data) - if state == FORM_VALID then - if data.conf then - fs.writefile(conffile, data.conf:gsub("\r\n", "\n")) - luci.sys.call("/etc/init.d/adbyby restart") - end - end - return true -end - -return f diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/esc.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/esc.lua deleted file mode 100644 index 5c6f168a1..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/esc.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/usr/share/adbyby/adesc.conf" - -f = SimpleForm("custom") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 13 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end - -function f.handle(self, state, data) - if state == FORM_VALID then - if data.conf then - fs.writefile(conffile, data.conf:gsub("\r\n", "\n")) - luci.sys.call("/etc/init.d/adbyby restart") - end - end - return true -end - -return f diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/help.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/help.lua deleted file mode 100644 index a0c20b90d..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/help.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/usr/share/adbyby/adhost.conf" - -f = SimpleForm("custom") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 13 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end - -function f.handle(self, state, data) - if state == FORM_VALID then - if data.conf then - fs.writefile(conffile, data.conf:gsub("\r\n", "\n")) - luci.sys.call("/etc/init.d/adbyby restart") - end - end - return true -end - -return f diff --git a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/user.lua b/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/user.lua deleted file mode 100644 index 13887db99..000000000 --- a/luci-app-adbyby-plus/luasrc/model/cbi/adbyby/user.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/usr/share/adbyby/rules.txt" - -f = SimpleForm("custom") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 13 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end - -function f.handle(self, state, data) - if state == FORM_VALID then - if data.conf then - fs.writefile(conffile, data.conf:gsub("\r\n", "\n")) - luci.sys.call("/etc/init.d/adbyby restart") - end - end - return true -end - -return f diff --git a/luci-app-adbyby-plus/luasrc/view/adbyby/adbyby_status.htm b/luci-app-adbyby-plus/luasrc/view/adbyby/adbyby_status.htm deleted file mode 100644 index 3b2f40e06..000000000 --- a/luci-app-adbyby-plus/luasrc/view/adbyby/adbyby_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-adbyby-plus/luasrc/view/adbyby/refresh.htm b/luci-app-adbyby-plus/luasrc/view/adbyby/refresh.htm deleted file mode 100644 index 5c0285bcd..000000000 --- a/luci-app-adbyby-plus/luasrc/view/adbyby/refresh.htm +++ /dev/null @@ -1,35 +0,0 @@ -<%+cbi/valueheader%> - - -<%=self.value%> -<%+cbi/valuefooter%> diff --git a/luci-app-adbyby-plus/po/zh-cn/adbyby.po b/luci-app-adbyby-plus/po/zh-cn/adbyby.po deleted file mode 100644 index f82a0b38c..000000000 --- a/luci-app-adbyby-plus/po/zh-cn/adbyby.po +++ /dev/null @@ -1,198 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: dingpengyu \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: zh_CN\n" -"X-Generator: Poedit 2.3.1\n" - -msgid "ADBYBY Plus +" -msgstr "广告屏蔽大师 Plus+" - -msgid "Adbyby Plus + is Running" -msgstr "广告屏蔽大师 Plus + 正在运行" - -msgid "Adbyby Plus + is Not Running" -msgstr "广告屏蔽大师 Plus + 没有运行" - -msgid "Adbyby Plus + can filter all kinds of banners, popups, video ads, and prevent tracking, privacy theft and a variety of malicious websites
Plus + version combination mode can operation with Adblock Plus Host,filtering ads without losing bandwidth" -msgstr "广告屏蔽大师 Plus + 可以全面过滤各种横幅、弹窗、视频广告,同时阻止跟踪、隐私窃取及各种恶意网站
Plus + 版本可以和 Adblock Plus Host 结合方式运行,过滤广告不损失带宽" - -msgid "Adbyby Plus +" -msgstr "广告屏蔽大师 Plus+" - -msgid "Adbyby Plus + Settings" -msgstr "广告屏蔽大师 Plus+(支持 AdGuardHome /Host / DNSMASQ 规则)" - -msgid "Base Setting" -msgstr "基本设置" - -msgid "Running Mode" -msgstr "运行模式" - -msgid "Global Mode (The slowest and the best effects)" -msgstr "全局模式(最慢, 效果最好)" - -msgid "Plus + Mode (Filter domain name list and blacklist website.Recommended)" -msgstr "Plus + 模式(只过滤列表内域名结合ABP名单。推荐!)" - -msgid "No filter Mode (Must set in Client Filter Mode Settings manually)" -msgstr "手动代理模式(必须手动设置浏览器代理,或者客户端过滤模式设置)" - -msgid "Transparent proxy" -msgstr "透明代理" - -msgid "Click to disable" -msgstr "点击关闭" - -msgid "Click to enable" -msgstr "点击开启" - -msgid "Adbyby and Rule state" -msgstr "规则状态" - -msgid "Restart Adbyby" -msgstr "重启Adbyby" - -msgid "Plus+ Domain List" -msgstr "Plus+ 模式过滤的域名" - -msgid "Bypass Domain List" -msgstr "域名白名单" - -msgid "Black Domain List" -msgstr "域名黑名单" - -msgid "Black IP List" -msgstr "IP黑名单" - -msgid "Advance Setting" -msgstr "高级设置" - -msgid "Update the rule at 6 a.m. every morning and restart adbyby" -msgstr "每天凌晨6点更新规则并重启" - -msgid "ADP Host List" -msgstr "Adblock Plus Hosts 记录数" - -msgid "Adblock Plus Data" -msgstr "Adblock Plus 规则数据库" - -msgid "Subscribe 3rd Rules Data" -msgstr "订阅第三方去广告规则" - -msgid "Records" -msgstr "条记录" - -msgid "Refresh Data" -msgstr "更新数据库" - -msgid "Refresh..." -msgstr "正在更新,请稍候.." - -msgid "Refresh OK!" -msgstr "更新成功!" - -msgid "Refresh Error!" -msgstr "更新失败!" - -msgid "No new data!" -msgstr "你已经是最新数据,无需更新!" - -msgid "Total Records:" -msgstr "新的总纪录数:" - -msgid "Delete All Subscribe Rules" -msgstr "清空所有订阅的规则库" - -msgid "Anti-AD Rules Subscribe" -msgstr "广告过滤规则订阅URL" - -msgid "AdGuardHome / Host / DNSMASQ rules auto-convert" -msgstr "AdGuardHome / Host / DNSMASQ 规则自动识别" - -msgid "Manually force update
Adblock Plus Host List" -msgstr "手动强制更新
Adblock Plus Host List" - -msgid "Update Adbyby Rules Manually" -msgstr "手动更新规则" - -msgid "Block Apple iOS OTA update" -msgstr "拦截 Apple iOS 的OTA更新" - -msgid "Block CNshort APP and Website" -msgstr "拦截 短视频 APP 和网站" - -msgid "RAM Running Mode" -msgstr "内存运行模式" - -msgid "Running Adbyby in RAM.More speed,less disk consumption" -msgstr "在内存中运行Adbyby。更快的速度,更少的存储空间损耗" - -msgid "User-defined Rule" -msgstr "用户自定义规则" - -msgid "Client Filter Mode Settings" -msgstr "客户端过滤模式设置" - -msgid "Filter mode settings can be set to specific LAN clients ( No filter , Global filter ) . Does not need to be set by default." -msgstr "可以为局域网客户端分别设置不同的过滤模式 ( 不过滤 , 全局过滤 ) 。默认无需设置。" - -msgid "IP Address" -msgstr "IP地址" - -msgid "Filter Mode" -msgstr "过滤模式" - -msgid "(!)Note that you should fill to the domain name ONLY. For example, http://www.baidu.com only needs to write to baidu.com. One line for each" -msgstr "这些域名在 Plus 模式中会被过滤。你需要要填写域名即可,例如 http://www.baidu.com,你写 baidu.com 即可。每行一个域名" - -msgid "(!)Will Never filter these Domain" -msgstr "永不过滤白名单内的域名(所有模式中生效)" - -msgid "(!)Will Always block these Domain" -msgstr "拦截黑名单内的域名(所有模式中生效)" - -msgid "(!)Will Always block these IP" -msgstr "拦截黑名单内的IP地址(所有模式中生效)" - -msgid "Each line of the beginning exclamation mark is considered an annotation." -msgstr "每行一条规则,感叹号开头的被认为是注释。" - -msgid "Adblock Plus Host List:" -msgstr "Adblock Plus Host 列表:" - -msgid "Note: It needs to download and convert the rules. The background process may takes 60-120 seconds to run.
After completed it would automatically refresh, please do not duplicate click!" -msgstr "注意:需要下载并转换规则。后台进程可能需要60-120秒运行。完成后会自动刷新,请不要重复点击!" - -msgid "No filter" -msgstr "不过滤" - -msgid "Global filter" -msgstr "全局过滤" - -msgid "Update adbyby rules form official website first" -msgstr "优先从官方网站更新规则" - -msgid "Last Update Checked" -msgstr "上一次检查规则更新" - -msgid "Lazy Rule" -msgstr "正式版规则" - -msgid "Video Rule" -msgstr "测试版规则" - -msgid "Adblock Plus Host List" -msgstr "广告屏蔽大师 Plus 规则列表" - -msgid "Block Domain List" -msgstr "域名黑名单" - -msgid "Block IP List" -msgstr "IP黑名单" diff --git a/luci-app-adbyby-plus/po/zh_Hans b/luci-app-adbyby-plus/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-adbyby-plus/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-adbyby-plus/root/etc/config/adbyby b/luci-app-adbyby-plus/root/etc/config/adbyby deleted file mode 100644 index d2afc6ed7..000000000 --- a/luci-app-adbyby-plus/root/etc/config/adbyby +++ /dev/null @@ -1,11 +0,0 @@ - -config adbyby - option daemon '2' - option lan_mode '0' - option cron_mode '1' - option block_ios '0' - option enable '0' - option wan_mode '1' - option mem_mode '1' - option update_source '1' - diff --git a/luci-app-adbyby-plus/root/etc/init.d/adbyby b/luci-app-adbyby-plus/root/etc/init.d/adbyby deleted file mode 100755 index 5f263a078..000000000 --- a/luci-app-adbyby-plus/root/etc/init.d/adbyby +++ /dev/null @@ -1,254 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=96 -STOP=10 - - -EXTRA_COMMANDS="add_rule del_rule reload_rule" -PROG_PATH=/usr/share/adbyby -DATA_PATH=$PROG_PATH/data -WAN_FILE=/tmp/dnsmasq.d/dnsmasq-adbyby.d/03-adbyby-ipset.conf -CRON_FILE=/etc/crontabs/root -CONFIG=adbyby -ipt_n="iptables -t nat" - -uci_get_by_name() { - local ret=$(uci get $CONFIG.$1.$2 2>/dev/null) - echo ${ret:=$3} -} - -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} -} - -get_config() -{ - config_get_bool enable $1 enable 0 - config_get_bool cron_mode $1 cron_mode 1 - config_get wan_mode $1 wan_mode 1 - config_get_bool block_ios $1 block_ios 0 - config_get_bool mem_mode $1 mem_mode 1 - config_get_bool block_cnshort $1 block_cnshort 0 -} - -add_rules() -{ - rm -f $DATA_PATH/user.bin - grep -v ^! $PROG_PATH/rules.txt > $DATA_PATH/user.txt - cp $PROG_PATH/rules.txt $DATA_PATH/rules.txt -} - - -add_cron() -{ - if [ $cron_mode -eq 1 ]; then - sed -i '/adblock.sh/d' $CRON_FILE - echo '0 6 * * * /usr/share/adbyby/adblock.sh > /tmp/adupdate.log 2>&1' >> $CRON_FILE - crontab $CRON_FILE - fi -} - -del_cron() -{ - sed -i '/adblock.sh/d' $CRON_FILE - /etc/init.d/cron restart -} - -ip_rule() -{ - - ipset -N adbyby_esc hash:ip - $ipt_n -A ADBYBY -m set --match-set adbyby_esc dst -j RETURN - - local icount=$(uci show adbyby | grep 'filter_mode' | wc -l) - let icount=icount-1 - for i in $(seq 0 $icount) - do - local ip=$(uci_get_by_type acl_rule ipaddr '' $i) - local mode=$(uci_get_by_type acl_rule filter_mode '' $i) - case "$mode" in - disable) - $ipt_n -A ADBYBY -s $ip -j RETURN - ;; - global) - $ipt_n -A ADBYBY -s $ip -p tcp -j REDIRECT --to-ports 8118 - $ipt_n -A ADBYBY -s $ip -j RETURN - ;; - esac - done - - - - case $wan_mode in - 0) - ;; - 1) - ipset -N adbyby_wan hash:ip - $ipt_n -A ADBYBY -m set ! --match-set adbyby_wan dst -j RETURN - ;; - 2) - $ipt_n -A ADBYBY -j RETURN - ;; - esac - - echo "create blockip hash:net family inet hashsize 1024 maxelem 65536" > /tmp/blockip.ipset - awk '!/^$/&&!/^#/{printf("add blockip %s'" "'\n",$0)}' /usr/share/adbyby/blockip.conf >> /tmp/blockip.ipset - ipset -! restore < /tmp/blockip.ipset 2>/dev/null - iptables -I FORWARD -m set --match-set blockip dst -j DROP - iptables -I OUTPUT -m set --match-set blockip dst -j DROP -} - -add_dns() -{ - mkdir -p /tmp/dnsmasq.d/dnsmasq-adbyby.d - mkdir -p /tmp/dnsmasq.d - - awk '!/^$/&&!/^#/{printf("ipset=/%s/'"adbyby_esc"'\n",$0)}' $PROG_PATH/adesc.conf > /tmp/dnsmasq.d/dnsmasq-adbyby.d/06-dnsmasq.esc - awk '!/^$/&&!/^#/{printf("address=/%s/'"0.0.0.0"'\n",$0)}' $PROG_PATH/adblack.conf > /tmp/dnsmasq.d/dnsmasq-adbyby.d/07-dnsmasq.black - - echo 'conf-dir=/tmp/dnsmasq.d/dnsmasq-adbyby.d' > /tmp/dnsmasq.d/dnsmasq-adbyby.conf - - local var=1 - if [ $wan_mode -eq 1 ]; then - awk '!/^$/&&!/^#/{printf("ipset=/%s/'"adbyby_wan"'\n",$0)}' $PROG_PATH/adhost.conf > $WAN_FILE - if ls /tmp/dnsmasq.d/dnsmasq-adbyby.d/* >/dev/null 2>&1; then - mkdir -p /tmp/dnsmasq.d - cp /usr/share/adbyby/dnsmasq.adblock /tmp/dnsmasq.d/dnsmasq-adbyby.d/04-dnsmasq.adblock - cp /usr/share/adbyby/dnsmasq.ads /tmp/dnsmasq.d/dnsmasq-adbyby.d/05-dnsmasq.ads - fi - fi - - mkdir -p /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/data /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/host - rm -f /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/data/* /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/host/* - cp -a /usr/share/adbyby/rules/data/* /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/data 2>/dev/null - cp -a /usr/share/adbyby/rules/host/* /tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/host 2>/dev/null - echo 'addn-hosts=/tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/host/' >> /tmp/dnsmasq.d/dnsmasq-adbyby.conf - echo 'conf-dir=/tmp/dnsmasq.d/dnsmasq-adbyby.d/rules/data' >> /tmp/dnsmasq.d/dnsmasq-adbyby.conf - - [ $block_ios -eq 1 ] && echo 'address=/mesu.apple.com/0.0.0.0' >> /tmp/dnsmasq.d/dnsmasq-adbyby.conf - if [ $block_cnshort -eq 1 ]; then - cat <<-EOF >/tmp/etc/dnsmasq-adbyby.d/08-dnsmasq.cnshort -address=/api.amemv.com/0.0.0.0 -address=/.amemv.com/0.0.0.0 -address=/.tiktokv.com/0.0.0.0 -address=/.snssdk.com/0.0.0.0 -address=/.douyin.com/0.0.0.0 -address=/.ixigua.com/0.0.0.0 -address=/.pstatp.com/0.0.0.0 -address=/.ixiguavideo.com/0.0.0.0 -address=/.v.kandian.qq.com/0.0.0.0 -address=/.yximgs.com/0.0.0.0 -address=/.gifshow.com/0.0.0.0 -address=/.ksapisrv.com/0.0.0.0 -address=/.kuaishoupay.com/0.0.0.0 -address=/.ksyun.com/0.0.0.0 -address=/.live.xycdn.com/0.0.0.0 -address=/.danuoyi.alicdn.com/0.0.0.0 -address=/.v.weishi.qq.com/0.0.0.0 -address=/.pearvideo.com/0.0.0.0 -address=/.miaopai.com/0.0.0.0 -address=/.kuaishou.com/0.0.0.0 -address=/.qupai.me/0.0.0.0 -address=/.meipai.com/0.0.0.0 -address=/.huoshan.com/0.0.0.0 -address=/.ergengtv.com/0.0.0.0 -address=/.baijiahao.baidu.com/0.0.0.0 -address=/.xiongzhang.baidu.com/0.0.0.0 - EOF - fi -} - -del_dns() -{ - rm -f /tmp/dnsmasq.d/dnsmasq-adbyby.conf - rm -f /tmp/dnsmasq.d/dnsmasq-adbyby.d/* - rm -f /tmp/adbyby_host.conf -} - - -add_rule() -{ - $ipt_n -N ADBYBY - $ipt_n -A ADBYBY -d 0.0.0.0/8 -j RETURN - $ipt_n -A ADBYBY -d 10.0.0.0/8 -j RETURN - $ipt_n -A ADBYBY -d 127.0.0.0/8 -j RETURN - $ipt_n -A ADBYBY -d 169.254.0.0/16 -j RETURN - $ipt_n -A ADBYBY -d 172.16.0.0/12 -j RETURN - $ipt_n -A ADBYBY -d 192.168.0.0/16 -j RETURN - $ipt_n -A ADBYBY -d 224.0.0.0/4 -j RETURN - $ipt_n -A ADBYBY -d 240.0.0.0/4 -j RETURN - ip_rule - if [ $(ipset list music -name -quiet | grep music) ]; then - $ipt_n -A ADBYBY -m set --match-set music dst -j RETURN 2>/dev/null - fi - $ipt_n -A ADBYBY -p tcp -j REDIRECT --to-ports 8118 2>/dev/null - $ipt_n -I PREROUTING -p tcp --dport 80 -j ADBYBY 2>/dev/null - - mkdir -p /var/etc - echo -e "/etc/init.d/adbyby restart" > "/var/etc/adbyby.include" -} - -del_rule() -{ - $ipt_n -D PREROUTING -p tcp --dport 80 -j ADBYBY 2>/dev/null - $ipt_n -F ADBYBY 2>/dev/null - $ipt_n -X ADBYBY 2>/dev/null - iptables -D FORWARD -m set --match-set blockip dst -j DROP 2>/dev/null - iptables -D OUTPUT -m set --match-set blockip dst -j DROP 2>/dev/null - ipset -F adbyby_esc 2>/dev/null - ipset -X adbyby_esc 2>/dev/null - ipset -F adbyby_wan 2>/dev/null - ipset -X adbyby_wan 2>/dev/null - ipset -F blockip 2>/dev/null - ipset -X blockip 2>/dev/null -} - -reload_rule() -{ - config_load adbyby - config_foreach get_config adbyby - del_rule - [ $enable -eq 0 ] && exit 0 - add_rule -} - -start() -{ - config_load adbyby - config_foreach get_config adbyby - - if [ $enable -ne 0 ]; then - add_cron - [ ! -d "/tmp/adbyby/data" ] && cp -a /usr/share/adbyby /tmp/ && rm -f /tmp/adbyby.updated - add_rules - - /tmp/adbyby/adbyby >/dev/null 2>&1 & - - add_dns - add_rule - /etc/init.d/dnsmasq restart - fi -} - -stop() -{ - kill -9 $(busybox ps -w | grep "/tmp/adbyby/adbyby" | grep -v grep | grep -v update| grep -v adblock | awk '{print $1}') >/dev/null 2>&1 - config_load adbyby - config_foreach get_config adbyby - del_rule - del_cron - del_dns - /etc/init.d/dnsmasq restart -} - -boot() -{ - mkdir -p /tmp/adbyby && cp -a /usr/share/adbyby /tmp/ - start -} diff --git a/luci-app-adbyby-plus/root/etc/uci-defaults/luci-adbyby b/luci-app-adbyby-plus/root/etc/uci-defaults/luci-adbyby deleted file mode 100644 index a0b3e4527..000000000 --- a/luci-app-adbyby-plus/root/etc/uci-defaults/luci-adbyby +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-adbyby.json" ] && { - cat > /usr/share/ucitrack/luci-app-adbyby.json << EEOF -{ - "config": "adbyby", - "init": "adbyby" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@adbyby[-1] - add ucitrack adbyby - set ucitrack.@adbyby[-1].init=adbyby - commit ucitrack - delete firewall.adbyby - set firewall.adbyby=include - set firewall.adbyby.type=script - set firewall.adbyby.path=/var/etc/adbyby.include - set firewall.adbyby.reload=1 - delete firewall.adblock - add firewall rule - rename firewall.@rule[-1]="adblock" - set firewall.@rule[-1].name="adblock" - set firewall.@rule[-1].target="DROP" - set firewall.@rule[-1].src="wan" - set firewall.@rule[-1].proto="tcp" - set firewall.@rule[-1].dest_port="8118" - commit firewall -EOF - -mkdir -p /etc/dnsmasq.d - -/etc/init.d/adbyby stop -/etc/init.d/adbyby enable - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-adbyby-plus/root/lib/upgrade/keep.d/adbyby b/luci-app-adbyby-plus/root/lib/upgrade/keep.d/adbyby deleted file mode 100644 index 49b3eaafd..000000000 --- a/luci-app-adbyby-plus/root/lib/upgrade/keep.d/adbyby +++ /dev/null @@ -1,6 +0,0 @@ -/usr/share/adbyby/adhost.conf -/usr/share/adbyby/adblack.conf -/usr/share/adbyby/blockip.conf -/usr/share/adbyby/adesc.conf -/usr/share/adbyby/rules.txt -/usr/share/adbyby/dnsmasq.adblock diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/ad-update b/luci-app-adbyby-plus/root/usr/share/adbyby/ad-update deleted file mode 100755 index e074b5278..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/ad-update +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -e -if [ -f /tmp/adnew.conf ]; then - if (grep -wq "address=" /tmp/adnew.conf) ; then - cp /tmp/adnew.conf /tmp/ad.conf - else - cat /tmp/adnew.conf | grep ^\|\|[^\*]*\^$ | sed -e 's:||:address\=\/:' -e 's:\^:/0\.0\.0\.0:' > /tmp/ad.conf - fi -fi - -#rm -f /tmp/adnew.conf diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adblack.conf b/luci-app-adbyby-plus/root/usr/share/adbyby/adblack.conf deleted file mode 100644 index a532f5825..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adblack.conf +++ /dev/null @@ -1,14 +0,0 @@ -gvod.aiseejapp.atianqi.com -stat.pandora.xiaomi.com -upgrade.mishop.pandora.xiaomi.com -logonext.tv.kuyun.com -config.kuyun.com -mishop.pandora.xiaomi.com -dvb.pandora.xiaomi.com -api.ad.xiaomi.com -de.pandora.xiaomi.com -data.mistat.xiaomi.com -jellyfish.pandora.xiaomi.com -gallery.pandora.xiaomi.com -o2o.api.xiaomi.com -bss.pandora.xiaomi.com diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adblock.sh b/luci-app-adbyby-plus/root/usr/share/adbyby/adblock.sh deleted file mode 100755 index 8224179dd..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adblock.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -uclient-fetch --no-check-certificate -O - 'https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt' > /tmp/adnew.conf -if [ -s "/tmp/adnew.conf" ];then - /usr/share/adbyby/ad-update -fi - -rm -f /tmp/adbyby.updated -sleep 10 -/etc/init.d/adbyby restart diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyfirst.sh b/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyfirst.sh deleted file mode 100755 index 532fe182d..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyfirst.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -PROG_PATH=$(pwd) -$PROG_PATH/adbybyupdate.sh diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyupdate.sh b/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyupdate.sh deleted file mode 100755 index ca74f20e9..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adbybyupdate.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -if [ ! -f "/tmp/adbyby.updated" ];then - wget_ok="0" - while [ "$wget_ok" = "0" ] - do - uclient-fetch --spider --quiet --timeout=3 http://www.baidu.com - if [ "$?" == "0" ]; then - wget_ok="1" - - touch /tmp/md5.json && uclient-fetch --no-check-certificate -T 10 -O /tmp/md5.json https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/md5.json - adm5=$(md5sum /tmp/md5.json | awk -F' ' '{print $1}') - touch /tmp/adbyby/admd5.json && bmd5=$(md5sum /tmp/adbyby/admd5.json | awk -F' ' '{print $1}') - if [ "$adm5" == "$bmd5" ];then - echo "Rules MD5 are the same!" - echo $(date "+%Y-%m-%d %H:%M:%S") > /tmp/adbyby.updated - exit 0 - elif [ -s /tmp/md5.json ];then - - touch /tmp/lazy.txt && uclient-fetch --no-check-certificate -T 10 -O /tmp/lazy.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/lazy.txt - touch /tmp/video.txt && uclient-fetch --no-check-certificate -T 10 -O /tmp/video.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/video.txt - - touch /tmp/local-md5.json && md5sum /tmp/lazy.txt /tmp/video.txt > /tmp/local-md5.json - - - lazy_local=$(grep 'lazy' /tmp/local-md5.json | awk -F' ' '{print $1}') - video_local=$(grep 'video' /tmp/local-md5.json | awk -F' ' '{print $1}') - lazy_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '2p') - video_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '4p') - - if [ "$lazy_online"x == "$lazy_local"x -a "$video_online"x == "$video_local"x ]; then - echo "adbyby rules MD5 OK!" - mv /tmp/lazy.txt /tmp/adbyby/data/lazy.txt - mv /tmp/video.txt /tmp/adbyby/data/video.txt - mv /tmp/md5.json /tmp/adbyby/admd5.json - echo $(date "+%Y-%m-%d %H:%M:%S") > /tmp/adbyby.updated - fi - fi - else - sleep 10 - fi - done - - rm -f /tmp/adbyby/data/*.bak - - kill -9 $(busybox ps -w | grep "/tmp/adbyby/adbyby" | grep -v grep | grep -v update| grep -v adblock | awk '{print $1}') >/dev/null 2>&1 - /tmp/adbyby/adbyby >/dev/null 2>&1 & -fi - diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adesc.conf b/luci-app-adbyby-plus/root/usr/share/adbyby/adesc.conf deleted file mode 100644 index c5898a128..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adesc.conf +++ /dev/null @@ -1,3 +0,0 @@ -weixin.qq.com -qpic.cn -imtt.qq.com diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adhost.conf b/luci-app-adbyby-plus/root/usr/share/adbyby/adhost.conf deleted file mode 100644 index 33aec9dbd..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adhost.conf +++ /dev/null @@ -1,74 +0,0 @@ -124.232.160.178 -126.net -50bang.org -56.com -acfun.com -acfun.tv -admaster.com.cn -agn.aty.sohu.com -amdc.cp12.ott.cibntv.net -analytics-union.xunlei.com -api.mydianshijia.com -atianqi.com -baidustatic.com -baofeng.com -baofeng.net -bdimg.com -bdstatic.com -cbjs.baidu.com -cnbeta.com -cnbetacdn.com -cntv.cn -cpro.baidu.com -doubleclick.net -fun.tv -funshion.com -googles.com -hitvs.cn -hljtv.com -hm.baidu.com -hoopchina.com.cn -hunantv.com -ifeng.com -img1.kyimg.com -iqiyi.com -itc.cn -jiuzhilan.com -kankan.com -kankan.xunlei.com -ku6.com -kuyun.com -l.qq.com -letv.com -letvcloud.com -letvimg.com -list.video.baidu.com -lives.l.aiseet.atianqi.com -mediav.com -mydrivers.com -nsclick.baidu.com -play.baidu.com -pplive.cn -pps.tv -ppsimg.com -pptv.com -ptqy.gitv.tv -pv.sohu.com -qiyi.com -renren.com -right.com.cn -sclick.baidu.com -serving-sys.com -share.baidu.com -sohu.com -sv-video.play.ott.video.qq.com -tieba.baidu.com -tudou.com -v-56.com -v.baidu.com -v.qq.com -video.sina.com.cn -wasu.cn -www.qq.com -you85.cn -youku.com diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/admem.sh b/luci-app-adbyby-plus/root/usr/share/adbyby/admem.sh deleted file mode 100755 index 56f2841c9..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/admem.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -if [ ! -f "/tmp/adbyby.updated" ];then - touch /tmp/adbyby.mem - wget_ok="0" - while [ "$wget_ok" = "0" ] - do - uclient-fetch --spider --quiet --tries=1 --timeout=3 www.baidu.com - if [ "$?" == "0" ]; then - wget_ok="1" - - touch /tmp/md5.json && uclient-fetch --no-check-certificate -t 1 -T 10 -O /tmp/md5.json https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/md5.json - adm5=$(md5sum /tmp/md5.json | awk -F' ' '{print $1}') - touch /tmp/adbyby/admd5.json && bmd5=$(md5sum /tmp/adbyby/admd5.json | awk -F' ' '{print $1}') - if [ "$adm5" == "$bmd5" ];then - echo "Rules MD5 are the same!" - echo $(date "+%Y-%m-%d %H:%M:%S") > /tmp/adbyby.updated - exit 0 - else - - touch /tmp/lazy.txt && uclient-fetch --no-check-certificate -t 1 -T 10 -O /tmp/lazy.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/lazy.txt - touch /tmp/video.txt && uclient-fetch --no-check-certificate -t 1 -T 10 -O /tmp/video.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/video.txt - - touch /tmp/local-md5.json && md5sum /tmp/lazy.txt /tmp/video.txt > /tmp/local-md5.json - - - lazy_local=$(grep 'lazy' /tmp/local-md5.json | awk -F' ' '{print $1}') - video_local=$(grep 'video' /tmp/local-md5.json | awk -F' ' '{print $1}') - lazy_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '2p') - video_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '4p') - - if [ "$lazy_online"x == "$lazy_local"x -a "$video_online"x == "$video_local"x ]; then - echo "adbyby rules MD5 OK!" - mv /tmp/lazy.txt /tmp/adbyby/data/lazy.txt - mv /tmp/video.txt /tmp/adbyby/data/video.txt - mv /tmp/md5.json /tmp/adbyby/admd5.json - echo $(date "+%Y-%m-%d %H:%M:%S") > /tmp/adbyby.updated - fi - fi - else - sleep 10 - fi - done - - sleep 10 && /etc/init.d/adbyby restart -fi diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/adupdate.sh b/luci-app-adbyby-plus/root/usr/share/adbyby/adupdate.sh deleted file mode 100755 index 3ae7fcc75..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/adupdate.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -rm -f /usr/share/adbyby/data/*.bak - -touch /tmp/local-md5.json && md5sum /usr/share/adbyby/data/lazy.txt /usr/share/adbyby/data/video.txt > /tmp/local-md5.json -touch /tmp/md5.json && uclient-fetch --no-check-certificate -T 10 -O /tmp/md5.json https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/md5.json - -lazy_local=$(grep 'lazy' /tmp/local-md5.json | awk -F' ' '{print $1}') -video_local=$(grep 'video' /tmp/local-md5.json | awk -F' ' '{print $1}') -lazy_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '2p') -video_online=$(sed 's/":"/\n/g' /tmp/md5.json | sed 's/","/\n/g' | sed -n '4p') - -if [ "$lazy_online"x != "$lazy_local"x -o "$video_online"x != "$video_local"x ]; then - echo "MD5 not match! Need update!" - touch /tmp/lazy.txt && uclient-fetch --no-check-certificate -T 10 -O /tmp/lazy.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/lazy.txt - touch /tmp/video.txt && uclient-fetch --no-check-certificate -T 10 -O /tmp/video.txt https://cdn.jsdelivr.net/gh/kongfl888/ad-rules/video.txt - touch /tmp/local-md5.json && md5sum /tmp/lazy.txt /tmp/video.txt > /tmp/local-md5.json - lazy_local=$(grep 'lazy' /tmp/local-md5.json | awk -F' ' '{print $1}') - video_local=$(grep 'video' /tmp/local-md5.json | awk -F' ' '{print $1}') - if [ "$lazy_online"x == "$lazy_local"x -a "$video_online"x == "$video_local"x ]; then - echo "New rules MD5 match!" - mv /tmp/lazy.txt /usr/share/adbyby/data/lazy.txt - mv /tmp/video.txt /usr/share/adbyby/data/video.txt - echo $(date +"%Y-%m-%d %H:%M:%S") > /tmp/adbyby.updated - fi -else - echo "MD5 match! No need to update!" -fi - -rm -f /tmp/lazy.txt /tmp/video.txt /tmp/local-md5.json /tmp/md5.json diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/blockip.conf b/luci-app-adbyby-plus/root/usr/share/adbyby/blockip.conf deleted file mode 100644 index 55cb84b53..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/blockip.conf +++ /dev/null @@ -1 +0,0 @@ -2.2.2.2 diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.adblock b/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.adblock deleted file mode 100644 index e69de29bb..000000000 diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.ads b/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.ads deleted file mode 100644 index ec1a22189..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.ads +++ /dev/null @@ -1,4 +0,0 @@ -address=/p.tanx.com/0.0.0.0 -address=/googlesyndication.com/0.0.0.0 -address=/linkvans.com/0.0.0.0 -server=/valf.atm.youku.com/114.114.114.114 diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.esc b/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.esc deleted file mode 100644 index 6cfd87056..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/dnsmasq.esc +++ /dev/null @@ -1,3 +0,0 @@ -ipset=/weixin.qq.com/adbyby_wan -ipset=/qpic.cn/adbyby_wan -ipset=/imtt.qq.com/adbyby_wan diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/firewall.include b/luci-app-adbyby-plus/root/usr/share/adbyby/firewall.include deleted file mode 100755 index 4dcec1e26..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/firewall.include +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -if [ -f /tmp/blockip.ipset ];then - -adbyby_enable=$(uci get adbyby.@adbyby[0].enable) - -if [ $adbyby_enable -eq 1 ]; then - if pidof adbyby>/dev/null; then - /etc/init.d/adbyby reload_rule - else - /etc/init.d/adbyby restart - fi -fi - -fi - diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/rule-count b/luci-app-adbyby-plus/root/usr/share/adbyby/rule-count deleted file mode 100755 index c99b98cc4..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/rule-count +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -find $1 -type f -exec cat {} \; | wc -l diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/rule-update b/luci-app-adbyby-plus/root/usr/share/adbyby/rule-update deleted file mode 100755 index 495277af4..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/rule-update +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -e - -url_md5() { - echo -n $1|md5sum|cut -d ' ' -f1 -} - -convert_rules() { -mkdir -p /tmp/rules/data -mkdir -p /tmp/rules/host - -uclient-fetch -q --no-check-certificate $1 -O /tmp/adnew.conf - -rulename=$(url_md5 $1) - -if [ -f /tmp/adnew.conf ]; then - if (grep -wq "address=" /tmp/adnew.conf) ; then - cp /tmp/adnew.conf /tmp/rules/data/$rulename.conf - elif (grep -wq -e"0.0.0.0" -e"127.0.0.1" /tmp/adnew.conf) ; then - cp /tmp/adnew.conf /tmp/rules/host/$rulename.txt - else - cat /tmp/adnew.conf | grep ^\|\|[^\*]*\^$ | sed -e 's:||:address\=\/:' -e 's:\^:/0\.0\.0\.0:' > /tmp/rules/data/$rulename.conf - fi -fi -rm -rf /tmp/adnew.conf -} - -ARRAY=$(uci get adbyby.@adbyby[0].subscribe_url 2> /dev/null) - -rm -rf /tmp/rules - -i=1 -for j in $ARRAY -do - convert_rules $j - i=`expr $i + 1` -done diff --git a/luci-app-adbyby-plus/root/usr/share/adbyby/rules.txt b/luci-app-adbyby-plus/root/usr/share/adbyby/rules.txt deleted file mode 100644 index 5c44b0e8c..000000000 --- a/luci-app-adbyby-plus/root/usr/share/adbyby/rules.txt +++ /dev/null @@ -1,15 +0,0 @@ -! ------------------------------ ADByby 自定义过滤语法简表--------------------------------- -! -------------- 规则基于abp规则,并进行了字符替换部分的扩展----------------------------- -! ABP规则请参考https://adblockplus.org/zh_CN/filters,下面为大致摘要 -! "!" 为行注释符,注释行以该符号起始作为一行注释语义,用于规则描述 -! "*" 为字符通配符,能够匹配0长度或任意长度的字符串,该通配符不能与正则语法混用。 -! "^" 为分隔符,可以是除了字母、数字或者 _ - . % 之外的任何字符。 -! "|" 为管线符号,来表示地址的最前端或最末端 -! "||" 为子域通配符,方便匹配主域名下的所有子域。 -! "~" 为排除标识符,通配符能过滤大多数广告,但同时存在误杀, 可以通过排除标识符修正误杀链接。 -! "##" 为元素选择器标识符,后面跟需要隐藏元素的CSS样式例如 #ad_id .ad_class -!! 元素隐藏暂不支持全局规则和排除规则 -!! 字符替换扩展 -! 文本替换选择器标识符,后面跟需要替换的文本数据,格式:$s@模式字符串@替换后的文本@ -! 支持通配符*和? -! ------------------------------------------------------------------------------------------- diff --git a/luci-app-adbyby-plus/root/usr/share/rpcd/acl.d/luci-app-adbyby-plus.json b/luci-app-adbyby-plus/root/usr/share/rpcd/acl.d/luci-app-adbyby-plus.json deleted file mode 100644 index c1fcf27db..000000000 --- a/luci-app-adbyby-plus/root/usr/share/rpcd/acl.d/luci-app-adbyby-plus.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-adbyby-plus": { - "description": "Grant UCI access for luci-app-adbyby-plus", - "read": { - "uci": [ "adbyby" ] - }, - "write": { - "uci": [ "adbyby" ] - } - } -} diff --git a/luci-app-adbyby-plus/root/usr/share/ucitrack/luci-app-adbyby-plus.json b/luci-app-adbyby-plus/root/usr/share/ucitrack/luci-app-adbyby-plus.json deleted file mode 100644 index c8cc28e17..000000000 --- a/luci-app-adbyby-plus/root/usr/share/ucitrack/luci-app-adbyby-plus.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "adbyby", - "init": "adbyby" -} diff --git a/luci-app-aliyundrive-fuse/Makefile b/luci-app-aliyundrive-fuse/Makefile deleted file mode 100644 index 80de10d25..000000000 --- a/luci-app-aliyundrive-fuse/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-aliyundrive-fuse -PKG_VERSION:=0.1.14 -PKG_RELEASE:=1 - -PKG_LICENSE:=MIT -PKG_MAINTAINER:=messense - -LUCI_TITLE:=LuCI Support for aliyundrive-fuse -LUCI_PKGARCH:=all -LUCI_DEPENDS:=+aliyundrive-fuse - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-aliyundrive-fuse/luasrc/controller/aliyundrive-fuse.lua b/luci-app-aliyundrive-fuse/luasrc/controller/aliyundrive-fuse.lua deleted file mode 100644 index 53640bce8..000000000 --- a/luci-app-aliyundrive-fuse/luasrc/controller/aliyundrive-fuse.lua +++ /dev/null @@ -1,40 +0,0 @@ -module("luci.controller.aliyundrive-fuse", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/aliyundrive-fuse") then - return - end - - local page - page = entry({"admin", "services", "aliyundrive-fuse"}, alias("admin", "services", "aliyundrive-fuse", "client"), _("AliyunDrive FUSE"), 10) -- 首页 - page.dependent = true - page.acl_depends = { "luci-app-aliyundrive-fuse" } - - entry({"admin", "services", "aliyundrive-fuse", "client"}, cbi("aliyundrive-fuse/client"), _("Settings"), 10).leaf = true -- 客户端配置 - entry({"admin", "services", "aliyundrive-fuse", "log"}, form("aliyundrive-fuse/log"), _("Log"), 30).leaf = true -- 日志页面 - - entry({"admin", "services", "aliyundrive-fuse", "status"}, call("action_status")).leaf = true - entry({"admin", "services", "aliyundrive-fuse", "logtail"}, call("action_logtail")).leaf = true -end - -function action_status() - local e = {} - e.running = luci.sys.call("pidof aliyundrive-fuse >/dev/null") == 0 - e.application = luci.sys.exec("aliyundrive-fuse --version") - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end - -function action_logtail() - local fs = require "nixio.fs" - local log_path = "/var/log/aliyundrive-fuse.log" - local e = {} - e.running = luci.sys.call("pidof aliyundrive-fuse >/dev/null") == 0 - if fs.access(log_path) then - e.log = luci.sys.exec("tail -n 100 %s | sed 's/\\x1b\\[[0-9;]*m//g'" % log_path) - else - e.log = "" - end - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end diff --git a/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/client.lua b/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/client.lua deleted file mode 100644 index 7d08f6f4e..000000000 --- a/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/client.lua +++ /dev/null @@ -1,33 +0,0 @@ -m = Map("aliyundrive-fuse") -m.title = translate("AliyunDrive FUSE") -m.description = translate("Project GitHub URL") - -m:section(SimpleSection).template = "aliyundrive-fuse/aliyundrive-fuse_status" - -e = m:section(TypedSection, "default") -e.anonymous = true - -enable = e:option(Flag, "enable", translate("Enable")) -enable.rmempty = false - -refresh_token = e:option(Value, "refresh_token", translate("Refresh Token")) -refresh_token.description = translate("How to get refresh token") - -mount_point = e:option(Value, "mount_point", translate("Mount Point")) -mount_point.default = "/mnt/aliyundrive" - -read_buffer_size = e:option(Value, "read_buffer_size", translate("Read Buffer Size")) -read_buffer_size.default = "10485760" -read_buffer_size.datatype = "uinteger" - -domain_id = e:option(Value, "domain_id", translate("Domain ID")) -domain_id.description = translate("Input domain_id option will use Aliyun PDS instead of AliyunDrive") - -allow_other = e:option(Flag, "allow_other", translate("Allow Other users Access")) -allow_other.description = translate("Allow other users to access the drive, enable this if you share with samba") -allow_other.rmempty = false - -debug = e:option(Flag, "debug", translate("Debug Mode")) -debug.rmempty = false - -return m diff --git a/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/log.lua b/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/log.lua deleted file mode 100644 index 7a5acab6d..000000000 --- a/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/log.lua +++ /dev/null @@ -1,9 +0,0 @@ -log = SimpleForm("logview") -log.submit = false -log.reset = false - -t = log:field(DummyValue, '', '') -t.rawhtml = true -t.template = 'aliyundrive-fuse/aliyundrive-fuse_log' - -return log diff --git a/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_log.htm b/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_log.htm deleted file mode 100644 index 04851ba12..000000000 --- a/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_log.htm +++ /dev/null @@ -1,15 +0,0 @@ -<%+cbi/valueheader%> - - - -<%+cbi/valuefooter%> diff --git a/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_status.htm b/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_status.htm deleted file mode 100644 index 22f19d0e0..000000000 --- a/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_status.htm +++ /dev/null @@ -1,21 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-aliyundrive-fuse/po/zh-cn/aliyundrive-fuse.po b/luci-app-aliyundrive-fuse/po/zh-cn/aliyundrive-fuse.po deleted file mode 100644 index 0822aad2b..000000000 --- a/luci-app-aliyundrive-fuse/po/zh-cn/aliyundrive-fuse.po +++ /dev/null @@ -1,56 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -msgid "AliyunDrive" -msgstr "阿里云盘" - -msgid "AliyunDrive FUSE" -msgstr "阿里云盘 FUSE" - -msgid "Enable" -msgstr "启用" - -msgid "Refresh Token" -msgstr "Refresh Token" - -msgid "Mount Point" -msgstr "挂载点" - -msgid "Read Buffer Size" -msgstr "下载缓冲大小(bytes)" - -msgid "Collecting data..." -msgstr "获取数据中..." - -msgid "RUNNING" -msgstr "运行中" - -msgid "NOT RUNNING" -msgstr "未运行" - -msgid "Settings" -msgstr "设置" - -msgid "Log" -msgstr "日志" - -msgid "Debug Mode" -msgstr "调试模式" - -msgid "Project GitHub URL" -msgstr "GitHub 项目地址" - -msgid "How to get refresh token" -msgstr "查看获取 refresh token 的方法" - -msgid "Domain ID" -msgstr "阿里云相册与云盘服务 domainId" - -msgid "Input domain_id option will use Aliyun PDS instead of AliyunDrive" -msgstr "填写此选项将使用阿里云相册与网盘服务而不是阿里云盘" - -msgid "Allow Other users Access" -msgstr "允许其他用户访问" - -msgid "Allow other users to access the drive, enable this if you share with samba" -msgstr "允许其他用户访问此驱动,如果你想用Samba分享请开启此开关" diff --git a/luci-app-aliyundrive-fuse/po/zh_Hans b/luci-app-aliyundrive-fuse/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-aliyundrive-fuse/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-aliyundrive-fuse/root/etc/uci-defaults/luci-aliyundrive-fuse b/luci-app-aliyundrive-fuse/root/etc/uci-defaults/luci-aliyundrive-fuse deleted file mode 100644 index 969bf8349..000000000 --- a/luci-app-aliyundrive-fuse/root/etc/uci-defaults/luci-aliyundrive-fuse +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-aliyundrive-fuse.json" ] && { - cat > /usr/share/ucitrack/luci-app-aliyundrive-fuse.json << EEOF -{ - "config": "aliyundrive-fuse", - "init": "aliyundrive-fuse" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@aliyundrive-fuse[-1] - add ucitrack aliyundrive-fuse - set ucitrack.@aliyundrive-fuse[-1].init=aliyundrive-fuse - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-aliyundrive-fuse/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-fuse.json b/luci-app-aliyundrive-fuse/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-fuse.json deleted file mode 100644 index cc2e58491..000000000 --- a/luci-app-aliyundrive-fuse/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-fuse.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-aliyundrive-fuse": { - "description": "Grant UCI access for luci-app-aliyundrive-fuse", - "read": { - "uci": [ "aliyundrive-fuse" ] - }, - "write": { - "uci": [ "aliyundrive-fuse" ] - } - } -} diff --git a/luci-app-aliyundrive-webdav/Makefile b/luci-app-aliyundrive-webdav/Makefile index ab261702e..ea3a9ac76 100644 --- a/luci-app-aliyundrive-webdav/Makefile +++ b/luci-app-aliyundrive-webdav/Makefile @@ -1,15 +1,16 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-aliyundrive-webdav -PKG_VERSION:=2.2.1 -PKG_RELEASE:=1 +PKG_VERSION:=0.5.5 +PKG_RELEASE:=2 +PKG_PO_VERSION:=$(PKG_VERSION)-$(PKG_RELEASE) PKG_LICENSE:=MIT PKG_MAINTAINER:=messense LUCI_TITLE:=LuCI Support for aliyundrive-webdav LUCI_PKGARCH:=all -LUCI_DEPENDS:=+aliyundrive-webdav +LUCI_DEPENDS:=+aliyundrive-webdav +lua +libuci-lua include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-aliyundrive-webdav/luasrc/controller/aliyundrive-webdav.lua b/luci-app-aliyundrive-webdav/luasrc/controller/aliyundrive-webdav.lua index 04d4dad1c..0829ad2a3 100644 --- a/luci-app-aliyundrive-webdav/luasrc/controller/aliyundrive-webdav.lua +++ b/luci-app-aliyundrive-webdav/luasrc/controller/aliyundrive-webdav.lua @@ -4,21 +4,12 @@ function index() if not nixio.fs.access("/etc/config/aliyundrive-webdav") then return end + entry({"admin", "services", "aliyundrive-webdav"}, alias("admin", "services", "aliyundrive-webdav", "client"),_("AliyunDrive WebDAV"), 10).dependent = true -- 首页 + entry({"admin", "services", "aliyundrive-webdav", "client"}, cbi("aliyundrive-webdav/client"),_("Settings"), 10).leaf = true -- 客户端配置 + entry({"admin", "services", "aliyundrive-webdav", "log"}, form("aliyundrive-webdav/log"),_("Log"), 30).leaf = true -- 日志页面 - local page - page = entry({ "admin", "services", "aliyundrive-webdav" }, alias("admin", "services", "aliyundrive-webdav", "client"), - _("AliyunDrive WebDAV"), 10) -- 首页 - page.dependent = true - page.acl_depends = { "luci-app-aliyundrive-webdav" } - - entry({ "admin", "services", "aliyundrive-webdav", "client" }, cbi("aliyundrive-webdav/client"), _("Settings"), 10).leaf = true -- 客户端配置 - entry({ "admin", "services", "aliyundrive-webdav", "log" }, form("aliyundrive-webdav/log"), _("Log"), 30).leaf = true -- 日志页面 - - entry({ "admin", "services", "aliyundrive-webdav", "status" }, call("action_status")).leaf = true -- 运行状态 - entry({ "admin", "services", "aliyundrive-webdav", "logtail" }, call("action_logtail")).leaf = true -- 日志采集 - entry({ "admin", "services", "aliyundrive-webdav", "qrcode" }, call("action_generate_qrcode")).leaf = true -- 生成扫码登录二维码地址和参数 - entry({ "admin", "services", "aliyundrive-webdav", "query" }, call("action_query_qrcode")).leaf = true -- 查询扫码登录结果 - entry({ "admin", "services", "aliyundrive-webdav", "invalidate-cache" }, call("action_invalidate_cache")).leaf = true -- 清除缓存 + entry({"admin", "services", "aliyundrive-webdav", "status"}, call("action_status")).leaf = true + entry({"admin", "services", "aliyundrive-webdav", "logtail"}, call("action_logtail")).leaf = true end function action_status() @@ -42,25 +33,3 @@ function action_logtail() luci.http.prepare_content("application/json") luci.http.write_json(e) end - -function action_generate_qrcode() - local output = luci.sys.exec("aliyundrive-webdav qr generate") - luci.http.prepare_content("application/json") - luci.http.write(output) -end - -function action_query_qrcode() - local data = luci.http.formvalue() - local sid = data.sid - local output = {} - output.refresh_token = luci.sys.exec("aliyundrive-webdav qr query --sid " .. sid) - luci.http.prepare_content("application/json") - luci.http.write_json(output) -end - -function action_invalidate_cache() - local e = {} - e.ok = luci.sys.call("kill -HUP `pidof aliyundrive-webdav`") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end diff --git a/luci-app-aliyundrive-webdav/luasrc/model/cbi/aliyundrive-webdav/client.lua b/luci-app-aliyundrive-webdav/luasrc/model/cbi/aliyundrive-webdav/client.lua index ce745ba3e..7cdbf8842 100644 --- a/luci-app-aliyundrive-webdav/luasrc/model/cbi/aliyundrive-webdav/client.lua +++ b/luci-app-aliyundrive-webdav/luasrc/model/cbi/aliyundrive-webdav/client.lua @@ -1,3 +1,6 @@ +local uci = luci.model.uci.cursor() +local m, e + m = Map("aliyundrive-webdav") m.title = translate("AliyunDrive WebDAV") m.description = translate("Project GitHub URL") @@ -11,11 +14,7 @@ enable = e:option(Flag, "enable", translate("Enable")) enable.rmempty = false refresh_token = e:option(Value, "refresh_token", translate("Refresh Token")) -refresh_token.description = translate("Double click the input box above to get refresh token by scanning qrcode") - -qrcode = e:option(DummyValue, '', '') -qrcode.rawhtml = true -qrcode.template = 'aliyundrive-webdav/aliyundrive-webdav_qrcode' +refresh_token.description = translate("How to get refresh token") root = e:option(Value, "root", translate("Root Directory")) root.description = translate("Restrict access to a folder of aliyundrive, defaults to / which means no restrictions") @@ -29,9 +28,6 @@ port = e:option(Value, "port", translate("Port")) port.default = "8080" port.datatype = "port" -tls_cert = e:option(Value, "tls_cert", translate("TLS certificate file path")) -tls_key = e:option(Value, "tls_key", translate("TLS private key file path")) - auth_user = e:option(Value, "auth_user", translate("Username")) auth_password = e:option(Value, "auth_password", translate("Password")) auth_password.password = true @@ -40,22 +36,6 @@ read_buffer_size = e:option(Value, "read_buffer_size", translate("Read Buffer Si read_buffer_size.default = "10485760" read_buffer_size.datatype = "uinteger" -prefer_http_download = e:option(Flag, "prefer_http_download", translate("Prefer HTTP Download")) -prefer_http_download.description = translate("Prefer downloading files using HTTP instead of HTTPS protocol") -prefer_http_download.rmempty = false - -redirect = e:option(Flag, "redirect", translate("Enable 302 Redirect")) -redirect.description = translate("Enable 302 redirect when possible") -redirect.rmempty = false - -upload_buffer_size = e:option(Value, "upload_buffer_size", translate("Upload Buffer Size")) -upload_buffer_size.default = "16777216" -upload_buffer_size.datatype = "uinteger" - -skip_upload_same_size = e:option(Flag, "skip_upload_same_size", translate("Skip uploading same size files")) -skip_upload_same_size.description = translate("Reduce the upload traffic by skipping uploading files with the same size") -skip_upload_same_size.rmempty = false - cache_size = e:option(Value, "cache_size", translate("Cache Size")) cache_size.default = "1000" cache_size.datatype = "uinteger" @@ -67,9 +47,8 @@ cache_ttl.datatype = "uinteger" no_trash = e:option(Flag, "no_trash", translate("Delete file permanently instead of trashing")) no_trash.rmempty = false -read_only = e:option(Flag, "read_only", translate("Enable read only mode")) -read_only.description = translate("Disallow upload, modify and delete file operations") -read_only.rmempty = false +domain_id = e:option(Value, "domain_id", translate("Domain ID")) +domain_id.description = translate("Input domain_id option will use Aliyun PDS instead of AliyunDrive") debug = e:option(Flag, "debug", translate("Debug Mode")) debug.rmempty = false diff --git a/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_qrcode.htm b/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_qrcode.htm deleted file mode 100644 index b936f55aa..000000000 --- a/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_qrcode.htm +++ /dev/null @@ -1,158 +0,0 @@ -<%+cbi/valueheader%> - - - - -<%+cbi/valuefooter%> diff --git a/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_status.htm b/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_status.htm index 49cc5d2c7..39bd7d421 100644 --- a/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_status.htm +++ b/luci-app-aliyundrive-webdav/luasrc/view/aliyundrive-webdav/aliyundrive-webdav_status.htm @@ -4,33 +4,18 @@ XHR.poll(3, '<%=url([[admin]], [[services]], [[aliyundrive-webdav]], [[status]]) var tb = document.getElementById('aliyundrive-webdav_status'); if (data && tb) { if (data.running) { - tb.innerHTML = '' + data.application + '<%:RUNNING%>'; + tb.innerHTML = '' + data.application + '<%:RUNNING%>'; } else { - tb.innerHTML = '' + data.application + '<%:NOT RUNNING%>'; + tb.innerHTML = '' + data.application + '<%:NOT RUNNING%>'; } } } ); - -function invalidateCache(button) { - XHR.get('<%=url([[admin]], [[services]], [[aliyundrive-webdav]], [[invalidate-cache]])%>', null, - function(x, data) { - if (data.ok) { - alert("缓存已清除."); - } else { - alert("清除缓存失败!"); - } - } - ); -} //]]>
-

+

<%:Collecting data...%>

-

- -

diff --git a/luci-app-aliyundrive-webdav/po/zh-cn/aliyundrive-webdav.po b/luci-app-aliyundrive-webdav/po/zh-cn/aliyundrive-webdav.po index 53b4473f7..09718aab1 100644 --- a/luci-app-aliyundrive-webdav/po/zh-cn/aliyundrive-webdav.po +++ b/luci-app-aliyundrive-webdav/po/zh-cn/aliyundrive-webdav.po @@ -22,12 +22,6 @@ msgstr "监听主机" msgid "Port" msgstr "监听端口" -msgid "TLS certificate file path" -msgstr "TLS 证书文件路径" - -msgid "TLS private key file path" -msgstr "TLS 私钥文件路径" - msgid "Username" msgstr "用户名" @@ -37,9 +31,6 @@ msgstr "密码" msgid "Read Buffer Size" msgstr "下载缓冲大小(bytes)" -msgid "Upload Buffer Size" -msgstr "上传缓冲大小(bytes)" - msgid "Cache Size" msgstr "目录缓存大小" @@ -49,9 +40,6 @@ msgstr "目录缓存过期时间(单位为秒)" msgid "Collecting data..." msgstr "获取数据中..." -msgid "Invalidate cache" -msgstr "清除缓存" - msgid "RUNNING" msgstr "运行中" @@ -68,10 +56,10 @@ msgid "Debug Mode" msgstr "调试模式" msgid "Project GitHub URL" -msgstr "GitHub 项目地址 | 加入知识星球" +msgstr "GitHub 项目地址" -msgid "Double click the input box above to get refresh token by scanning qrcode" -msgstr "鼠标双击上面的输入框扫码登录自动获取 refresh token" +msgid "How to get refresh token" +msgstr "查看获取 refresh token 的方法" msgid "Restrict access to a folder of aliyundrive, defaults to / which means no restrictions" msgstr "限制只能访问该云盘目录,默认为 / 表示不限制,注意这个参数不是本地磁盘路径" @@ -79,26 +67,8 @@ msgstr "限制只能访问该云盘目录,默认为 / 表示不限制,注意 msgid "Delete file permanently instead of trashing" msgstr "删除文件不放入回收站" -msgid "Enable read only mode" -msgstr "启用只读模式" +msgid "Domain ID" +msgstr "阿里云相册与云盘服务 domainId" -msgid "Disallow upload, modify and delete file operations" -msgstr "禁止上传、修改和删除文件操作" - -msgid "Skip uploading same size files" -msgstr "跳过上传相同大小的文件" - -msgid "Reduce the upload traffic by skipping uploading files with the same size" -msgstr "跳过上传相同大小的文件减少上传流量消耗,但可能会导致修改过的同样大小的文件不会被上传" - -msgid "Prefer HTTP Download" -msgstr "使用 HTTP 下载" - -msgid "Prefer downloading files using HTTP instead of HTTPS protocol" -msgstr "优先使用 HTTP 而不是 HTTPS 协议下载,低端设备上降低 CPU 占用" - -msgid "Enable 302 Redirect" -msgstr "启用 302 重定向" - -msgid "Enable 302 redirect when possible" -msgstr "在可能的情况下启用 302 重定向" +msgid "Input domain_id option will use Aliyun PDS instead of AliyunDrive" +msgstr "填写此选项将使用阿里云相册与网盘服务而不是阿里云盘" diff --git a/luci-app-aliyundrive-webdav/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-webdav.json b/luci-app-aliyundrive-webdav/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-webdav.json deleted file mode 100644 index 31dec7f6e..000000000 --- a/luci-app-aliyundrive-webdav/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-webdav.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-aliyundrive-webdav": { - "description": "Grant UCI access for luci-app-aliyundrive-webdav", - "read": { - "uci": [ "aliyundrive-webdav" ] - }, - "write": { - "uci": [ "aliyundrive-webdav" ] - } - } -} diff --git a/luci-app-clouddrive2/Makefile b/luci-app-clouddrive2/Makefile index 6d0afc581..01a1161ef 100644 --- a/luci-app-clouddrive2/Makefile +++ b/luci-app-clouddrive2/Makefile @@ -1,20 +1,19 @@ -# 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 CloudDrive2 -LUCI_DEPENDS:=+clouddrive2 -LUCI_PKGARCH:=all +PKG_VERSION:=1.0.1-r20240822 +PKG_RELEASE:=2 -PKG_NAME:=luci-app-clouddrive2 -PKG_VERSION:=1 -PKG_RELEASE:=1 +LUCI_TITLE:=LuCI support for CloudDrive2 +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+mount-utils +lsblk +docker +dockerd +luci-lib-taskd + +define Package/luci-app-clouddrive2/conffiles +/etc/config/clouddrive2 +endef include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature + diff --git a/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua b/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua old mode 100644 new mode 100755 index 0a1a4af41..6d6be66be --- a/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua +++ b/luci-app-clouddrive2/luasrc/controller/clouddrive2.lua @@ -1,17 +1,7 @@ + module("luci.controller.clouddrive2", package.seeall) function index() - if not nixio.fs.access("/etc/config/clouddrive2") then - return - end - entry({"admin", "nas"}, firstchild(), _("NAS"), 45).dependent = false - entry({"admin","nas","clouddrive2"},cbi("clouddrive2"),_("CloudDrive2"), 10).acl_depends = { "luci-app-clouddrive2" } - entry({"admin", "nas", "clouddrive2", "status"}, call("act_status")).leaf = true -end - -function act_status() - local e={} - e.running=luci.sys.call("pgrep clouddrive >/dev/null")==0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) + entry({"admin", "services", "clouddrive2"}, alias("admin", "services", "clouddrive2", "config"), _("CloudDrive2"), 30).dependent = true + entry({"admin", "services", "clouddrive2", "config"}, cbi("clouddrive2")) end diff --git a/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua b/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua index 48e453280..4e581da72 100644 --- a/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua +++ b/luci-app-clouddrive2/luasrc/model/cbi/clouddrive2.lua @@ -1,21 +1,58 @@ +--[[ +LuCI - Lua Configuration Interface +]]-- + +local taskd = require "luci.model.tasks" +local clouddrive2_model = require "luci.model.clouddrive2" local m, s, o -m = Map("clouddrive2", translate("CloudDrive2"), translate("Configure and manage CloudDrive2")) +m = taskd.docker_map("clouddrive2", "clouddrive2", "/usr/libexec/istorec/clouddrive2.sh", + translate("CloudDrive2"), + translate("CloudDrive is a powerful multi-cloud drive management tool with local mounting of cloud drives.") + .. translate("Official website:") .. ' https://www.clouddrive2.com/' .. '
' + .. translate("Since mounting within the container requires the use of a special shared mount point, to avoid compatibility issues, only mounting to /mnt/CloudNAS is supported (mounting to other paths cannot be seen by the host). The shared mount point is automatically created by this plug-in, uninstalling the plug-in may cause the deployed CloudDrive container to fail to start or mount (iStoreOS is an exception, because /mnt is the shared mount point by default).") .. '
' + .. translate("Disclaimer: This LuCI plug-in is developed by individuals. It only facilitates users to deploy CloudDrive containers (https://hub.docker.com/u/cloudnas) and has nothing to do with CloudDrive. Since CloudDrive is not open source software, although this plug-in has restricted its permissions to the greatest extent, it does not make any guarantees about the software content and services provided by CloudDrive. Use at your own risk!")) -m:section(SimpleSection).template="clouddrive2/status" +s = m:section(SimpleSection, translate("Service Status"), translate("CloudDrive2 status:")) +s:append(Template("clouddrive2/status")) -s = m:section(TypedSection, "clouddrive2", translate("Settings")) -s.anonymous = true -s.addremove = false +s = m:section(TypedSection, "clouddrive2", translate("Setup"), + translate("The following parameters will only take effect during installation or upgrade:")) +s.addremove=false +s.anonymous=true -o = s:option(Flag, "enabled", translate("Enable")) +o = s:option(Value, "image", translate("Docker image")) o.rmempty = false +o.datatype = "string" +o:value("default", translate("Default (cloudnas/clouddrive2)")) +o:value("cloudnas/clouddrive2-unstable", "cloudnas/clouddrive2-unstable") +o.default = "default" o = s:option(Value, "port", translate("Port")) -o.datatype = "port" o.default = "19798" --- --- o = s:option(Value, "mount_point", translate("Mount Point")) --- o.default = "/mnt/clouddrive" +o.datatype = "port" + +local blocks = clouddrive2_model.blocks() +local home = clouddrive2_model.home() + +o = s:option(Value, "config_path", translate("Config path").."*") +o.rmempty = false +o.datatype = "string" + +local paths, default_path = clouddrive2_model.find_paths(blocks, home, "Configs") +for _, val in pairs(paths) do + o:value(val, val) +end +o.default = default_path + +o = s:option(Value, "cache_path", translate("Temporary file path"), translate("Default use 'temp' in 'config path' if not set, please make sure there has enough space")) +o.datatype = "string" +local paths, default_path = clouddrive2_model.find_paths(blocks, home, "Caches") +for _, val in pairs(paths) do + o:value(val, val) +end + +o = s:option(Flag, "share_mnt", translate("Share /mnt"), translate("CloudDrive can read and write other mount points under /mnt for its synchronization or backup functions")) +o.default = 0 return m diff --git a/luci-app-clouddrive2/luasrc/model/clouddrive2.lua b/luci-app-clouddrive2/luasrc/model/clouddrive2.lua new file mode 100644 index 000000000..a16e6adb5 --- /dev/null +++ b/luci-app-clouddrive2/luasrc/model/clouddrive2.lua @@ -0,0 +1,55 @@ +local util = require "luci.util" +local jsonc = require "luci.jsonc" + +local clouddrive2 = {} + +clouddrive2.blocks = function() + local f = io.popen("lsblk -s -f -b -o NAME,FSSIZE,MOUNTPOINT --json", "r") + local vals = {} + if f then + local ret = f:read("*all") + f:close() + local obj = jsonc.parse(ret) + for _, val in pairs(obj["blockdevices"]) do + local fsize = val["fssize"] + if fsize ~= nil and string.len(fsize) > 10 and val["mountpoint"] then + -- fsize > 1G + vals[#vals+1] = val["mountpoint"] + end + end + end + return vals +end + +clouddrive2.home = function() + local uci = require "luci.model.uci".cursor() + local home_dirs = {} + home_dirs["main_dir"] = uci:get_first("quickstart", "main", "main_dir", "/root") + home_dirs["Configs"] = uci:get_first("quickstart", "main", "conf_dir", home_dirs["main_dir"].."/Configs") + home_dirs["Public"] = uci:get_first("quickstart", "main", "pub_dir", home_dirs["main_dir"].."/Public") + home_dirs["Downloads"] = uci:get_first("quickstart", "main", "dl_dir", home_dirs["Public"].."/Downloads") + home_dirs["Caches"] = uci:get_first("quickstart", "main", "tmp_dir", home_dirs["main_dir"].."/Caches") + return home_dirs +end + +clouddrive2.find_paths = function(blocks, home_dirs, path_name) + local default_path = '' + local configs = {} + + default_path = home_dirs[path_name] .. "/CloudDrive2" + if #blocks == 0 then + table.insert(configs, default_path) + else + for _, val in pairs(blocks) do + table.insert(configs, val .. "/" .. path_name .. "/CloudDrive2") + end + local without_conf_dir = "/root/" .. path_name .. "/CloudDrive2" + if default_path == without_conf_dir then + default_path = configs[1] + end + end + + return configs, default_path +end + +return clouddrive2 diff --git a/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm b/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm index 40f441a77..8a25c4f4f 100644 --- a/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm +++ b/luci-app-clouddrive2/luasrc/view/clouddrive2/status.htm @@ -1,27 +1,31 @@ - - -
-

- <%:Collecting data...%> -

-
\ No newline at end of file + + + +<% end %> \ No newline at end of file diff --git a/luci-app-clouddrive2/po/zh-cn b/luci-app-clouddrive2/po/zh-cn deleted file mode 120000 index 8d69574dd..000000000 --- a/luci-app-clouddrive2/po/zh-cn +++ /dev/null @@ -1 +0,0 @@ -zh_Hans \ No newline at end of file diff --git a/luci-app-clouddrive2/po/zh-cn/clouddrive2.po b/luci-app-clouddrive2/po/zh-cn/clouddrive2.po new file mode 100644 index 000000000..8f1abfcd8 --- /dev/null +++ b/luci-app-clouddrive2/po/zh-cn/clouddrive2.po @@ -0,0 +1,59 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Official website:" +msgstr "官方网站:" + +msgid "CloudDrive is a powerful multi-cloud drive management tool with local mounting of cloud drives." +msgstr "CloudDrive 是一个强大的多云盘管理工具,为用户提供包含云盘本地挂载的一站式的多云盘解决方案。" + +msgid "Since mounting within the container requires the use of a special shared mount point, to avoid compatibility issues, only mounting to /mnt/CloudNAS is supported (mounting to other paths cannot be seen by the host). The shared mount point is automatically created by this plug-in, uninstalling the plug-in may cause the deployed CloudDrive container to fail to start or mount (iStoreOS is an exception, because /mnt is the shared mount point by default)." +msgstr "由于容器内挂载需要使用特殊的共享挂载点,为避免兼容性问题,只支持挂载到 /mnt/CloudNAS 之下(挂载到其他路径宿主看不到)。共享挂载点由此插件自动创建,卸载插件可能导致系统重启以后已经部署的 CloudDrive 容器无法启动或无法挂载(iStoreOS 是例外,因为 /mnt 默认就是共享挂载点)。" + +msgid "Disclaimer: This LuCI plug-in is developed by individuals. It only facilitates users to deploy CloudDrive containers (https://hub.docker.com/u/cloudnas) and has nothing to do with CloudDrive. Since CloudDrive is not open source software, although this plug-in has restricted its permissions to the greatest extent, it does not make any guarantees about the software content and services provided by CloudDrive. Use at your own risk!" +msgstr "免责声明:此 LuCI 插件为个人开发,仅方便用户部署 CloudDrive 容器( https://hub.docker.com/u/cloudnas ),与 CloudDrive 官方无关。由于 CloudDrive 非开源软件,尽管此插件已最大程度限制其权限,但不对 CloudDrive 提供的软件内容和服务做出任何保证。Use at your own risk!" + +msgid "Config path" +msgstr "配置文件路径" + +msgid "Port" +msgstr "端口" + +msgid "Docker image" +msgstr "Docker 镜像" + +msgid "Temporary file path" +msgstr "临时文件路径" + +msgid "Default use 'temp' in 'config path' if not set, please make sure there has enough space" +msgstr "留空则使用配置文件路径下的 temp,请确保有足够空间" + +msgid "Share /mnt" +msgstr "共享 /mnt" + +msgid "CloudDrive can read and write other mount points under /mnt for its synchronization or backup functions" +msgstr "CloudDrive 能够读写 /mnt 下的其他挂载点,用于支持其同步或备份功能" + +msgid "Service Status" +msgstr "服务状态" + +msgid "CloudDrive2 status:" +msgstr "CloudDrive2 的状态信息如下:" + +msgid "Setup" +msgstr "安装配置" + +msgid "The following parameters will only take effect during installation or upgrade:" +msgstr "以下参数只在安装或者升级时才会生效:" + +msgid "Status" +msgstr "状态" + +msgid "CloudDrive2 is running" +msgstr "CloudDrive2 运行中" + +msgid "CloudDrive2 is not running" +msgstr "CloudDrive2 未运行" + +msgid "Open CloudDrive2" +msgstr "打开 CloudDrive2" diff --git a/luci-app-accesscontrol/po/zh_Hans b/luci-app-clouddrive2/po/zh_Hans similarity index 100% rename from luci-app-accesscontrol/po/zh_Hans rename to luci-app-clouddrive2/po/zh_Hans diff --git a/luci-app-clouddrive2/po/zh_Hans/clouddrive2.po b/luci-app-clouddrive2/po/zh_Hans/clouddrive2.po deleted file mode 100644 index 420906a0c..000000000 --- a/luci-app-clouddrive2/po/zh_Hans/clouddrive2.po +++ /dev/null @@ -1,32 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -msgid "Configure and manage CloudDrive2" -msgstr "配置和管理CloudDrive2" - -msgid "Settings" -msgstr "设置" - -msgid "Enable" -msgstr "启用" - -msgid "Port" -msgstr "端口" - -msgid "Mount Point" -msgstr "挂载点" - -msgid "CloudDrive2 Status" -msgstr "CloudDrive2状态" - -msgid "Running Status" -msgstr "运行状态" - -msgid "Collecting data..." -msgstr "正在收集数据..." - -msgid "CloudDrive2 is running" -msgstr "CloudDrive2 正在运行" - -msgid "CloudDrive2 is not running" -msgstr "CloudDrive2 未运行" diff --git a/luci-app-clouddrive2/root/etc/config/clouddrive2 b/luci-app-clouddrive2/root/etc/config/clouddrive2 new file mode 100644 index 000000000..6583a7dbc --- /dev/null +++ b/luci-app-clouddrive2/root/etc/config/clouddrive2 @@ -0,0 +1,6 @@ +config clouddrive2 + option 'port' '19798' + option 'image' 'default' +# option 'config_path' '' +# option 'cache_path' '' + option 'share_mnt' '0' diff --git a/luci-app-clouddrive2/root/etc/init.d/clouddrive2 b/luci-app-clouddrive2/root/etc/init.d/clouddrive2 new file mode 100755 index 000000000..41ef4b9d4 --- /dev/null +++ b/luci-app-clouddrive2/root/etc/init.d/clouddrive2 @@ -0,0 +1,13 @@ +#!/bin/sh /etc/rc.common + +START=98 + +boot() { + # /usr/bin/mountpoint -q /CloudNAS || /usr/bin/mount --make-shared -t tmpfs -o size=4M tmpfs /CloudNAS + [ -d /mnt/CloudNAS ] || mkdir -p /mnt/CloudNAS + if /usr/bin/mountpoint -q /mnt; then + /usr/bin/mount --make-shared /mnt + else + /usr/bin/mount --make-shared -t tmpfs -o size=4M tmpfs /mnt/CloudNAS + fi +} diff --git a/luci-app-clouddrive2/root/etc/uci-defaults/luci-clouddrive2 b/luci-app-clouddrive2/root/etc/uci-defaults/luci-clouddrive2 deleted file mode 100644 index 5ac2ab53a..000000000 --- a/luci-app-clouddrive2/root/etc/uci-defaults/luci-clouddrive2 +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-clouddrive2.json" ] && { - cat > /usr/share/ucitrack/luci-app-clouddrive2.json << EEOF -{ - "config": "clouddrive2", - "init": "clouddrive2" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@clouddrive2[-1] - add ucitrack clouddrive2 - set ucitrack.@clouddrive2[-1].init=clouddrive2 - commit ucitrack -EOF - -mkdir -p /lib/upgrade/keep.d -cat > "/lib/upgrade/keep.d/luci-clouddrive2" <<-EOF -/Waytech/CloudDrive2/ -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-clouddrive2/root/usr/libexec/istorec/clouddrive2.sh b/luci-app-clouddrive2/root/usr/libexec/istorec/clouddrive2.sh new file mode 100755 index 000000000..1e598e672 --- /dev/null +++ b/luci-app-clouddrive2/root/usr/libexec/istorec/clouddrive2.sh @@ -0,0 +1,105 @@ +#!/bin/sh +# Author jjm2473@gmail.com + +ACTION=${1} +shift 1 + +IMAGE_NAME='default' + +get_image() { + IMAGE_NAME=`uci -q get clouddrive2.@clouddrive2[0].image 2>/dev/null` + if [ -z "$IMAGE_NAME" -o "$IMAGE_NAME" == "default" ]; then + IMAGE_NAME="cloudnas/clouddrive2" + fi +} + +do_install() { + get_image + echo "docker pull ${IMAGE_NAME}" + docker pull ${IMAGE_NAME} + docker stop clouddrive2 + docker rm -f clouddrive2 + + do_install_detail +} + +do_install_detail() { + local config=`uci -q get clouddrive2.@clouddrive2[0].config_path 2>/dev/null` + local cache=`uci -q get clouddrive2.@clouddrive2[0].cache_path 2>/dev/null` + local port=`uci -q get clouddrive2.@clouddrive2[0].port 2>/dev/null` + local share_mnt=`uci -q get clouddrive2.@clouddrive2[0].share_mnt 2>/dev/null` + + if [ -z "$config" ]; then + echo "config path is empty!" + exit 1 + fi + + [ -z "$port" ] && port=19798 + + local cmd="docker run --restart=unless-stopped -d -e CLOUDDRIVE_HOME=/Config -v \"$config:/Config\" " + [ -z "$cache" ] || cmd="$cmd -v \"$cache:/Config/temp\" " + + cmd="$cmd\ + --dns=172.17.0.1 \ + -p $port:19798" + + local tz="`uci -q get system.@system[0].zonename | sed 's/ /_/g'`" + [ -z "$tz" ] || cmd="$cmd -e TZ=$tz" + + # make sure shared mount point existed + /etc/init.d/clouddrive2 boot + + if [ "$share_mnt" = 1 ]; then + cmd="$cmd -v /mnt:/mnt" + mountpoint -q /mnt && cmd="$cmd:rslave" + fi + cmd="$cmd -v /mnt/CloudNAS:/mnt/CloudNAS:shared" + + # fuse + cmd="$cmd\ + --cap-add SYS_ADMIN \ + --security-opt apparmor:unconfined \ + --device /dev/fuse:/dev/fuse" + + cmd="$cmd --name clouddrive2 \"$IMAGE_NAME\"" + + echo "$cmd" + eval "$cmd" + +} + +usage() { + echo "usage: $0 sub-command" + echo "where sub-command is one of:" + echo " install Install the CloudDrive2" + echo " upgrade Upgrade the CloudDrive2" + echo " rm/start/stop/restart Remove/Start/Stop/Restart the CloudDrive2" + echo " status CloudDrive2 status" + echo " port CloudDrive2 port" +} + +case ${ACTION} in + "install") + do_install + ;; + "upgrade") + do_install + ;; + "rm") + docker stop clouddrive2 + docker rm -f clouddrive2 + ;; + "start" | "stop" | "restart") + docker ${ACTION} clouddrive2 + ;; + "status") + docker ps --all -f 'name=^/clouddrive2$' --format '{{.State}}' + ;; + "port") + docker ps --all -f 'name=^/clouddrive2$' --format '{{.Ports}}' | grep -om1 '0.0.0.0:[0-9]*->19798/tcp' | sed 's/0.0.0.0:\([0-9]*\)->.*/\1/' + ;; + *) + usage + exit 1 + ;; +esac diff --git a/luci-app-clouddrive2/root/usr/share/rpcd/acl.d/luci-app-clouddrive2.json b/luci-app-clouddrive2/root/usr/share/rpcd/acl.d/luci-app-clouddrive2.json index 6097a29d1..49f217005 100644 --- a/luci-app-clouddrive2/root/usr/share/rpcd/acl.d/luci-app-clouddrive2.json +++ b/luci-app-clouddrive2/root/usr/share/rpcd/acl.d/luci-app-clouddrive2.json @@ -8,4 +8,4 @@ "uci": [ "clouddrive2" ] } } -} \ No newline at end of file +} diff --git a/luci-app-clouddrive2/root/usr/share/ucitrack/luci-app-clouddrive2.json b/luci-app-clouddrive2/root/usr/share/ucitrack/luci-app-clouddrive2.json new file mode 100644 index 000000000..20e9aee7b --- /dev/null +++ b/luci-app-clouddrive2/root/usr/share/ucitrack/luci-app-clouddrive2.json @@ -0,0 +1,4 @@ +{ + "config": "clouddrive2", + "init": "clouddrive2" +} diff --git a/luci-app-design-config/Makefile b/luci-app-design-config/Makefile deleted file mode 100644 index 1ef3ab3ba..000000000 --- a/luci-app-design-config/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-design-config -PKG_VERSION:=1.3 -PKG_RELEASE:=1 - -PKG_MAINTAINER:=gngpp - -LUCI_TITLE:=LuCI page for Design Config -LUCI_PKGARCH:=all -LUCI_DEPENDS:=+luci-compat - -define Package/$(PKG_NAME)/conffiles -/etc/config/design -endef - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-design-config/luasrc/controller/design-config.lua b/luci-app-design-config/luasrc/controller/design-config.lua deleted file mode 100644 index f6529fbc5..000000000 --- a/luci-app-design-config/luasrc/controller/design-config.lua +++ /dev/null @@ -1,10 +0,0 @@ -module("luci.controller.design-config", package.seeall) - -function index() - if not nixio.fs.access('/www/luci-static/design/css/style.css') then - return - end - - local page = entry({"admin", "system", "design-config"}, form("design-config"), _("Design Config"), 90) - page.acl_depends = { "luci-app-design-config" } -end diff --git a/luci-app-design-config/luasrc/model/cbi/design-config.lua b/luci-app-design-config/luasrc/model/cbi/design-config.lua deleted file mode 100644 index 8ce99fae2..000000000 --- a/luci-app-design-config/luasrc/model/cbi/design-config.lua +++ /dev/null @@ -1,58 +0,0 @@ -local nxfs = require 'nixio.fs' -local nutil = require 'nixio.util' -local name = 'design' -local uci = require 'luci.model.uci'.cursor() - -local mode, navbar, navbar_proxy -if nxfs.access('/etc/config/design') then - mode = uci:get_first('design', 'global', 'mode') - navbar = uci:get_first('design', 'global', 'navbar') - navbar_proxy = uci:get_first('design', 'global', 'navbar_proxy') -end - --- [[ 设置 ]]-- -br = SimpleForm('config', translate('Design Config'), translate('Here you can set the mode of the theme and change the proxy tool icon in the navigation bar. [Recommend Chrome]')) -br.reset = false -br.submit = false -s = br:section(SimpleSection) - -o = s:option(ListValue, 'mode', translate('Theme mode')) -o:value('normal', translate('Follow System')) -o:value('light', translate('Force Light')) -o:value('dark', translate('Force Dark')) -o.default = mode -o.rmempty = false -o.description = translate('You can choose Theme color mode here') - -o = s:option(ListValue, 'navbar', translate('Navigation bar setting')) -o:value('display', translate('Display navigation bar')) -o:value('close', translate('Close navigation bar')) -o.default = navbar -o.rmempty = false -o.description = translate('The navigation bar is display by default') - -o = s:option(ListValue, 'navbar_proxy', translate('Navigation bar proxy')) -o:value('openclash', 'openclash') -o:value('shadowsocksr', 'shadowsocksr') -o:value('vssr', 'vssr') -o:value('passwall', 'passwall') -o:value('passwall2', 'passwall2') -o.default = navbar_proxy -o.rmempty = false -o.description = translate('OpenClash by default') - -o = s:option(Button, 'save', translate('Save Changes')) -o.inputstyle = 'reload' - -function br.handle(self, state, data) - if (state == FORM_VALID and data.mode ~= nil and data.navbar ~= nil and data.navbar_proxy ~= nil) then - nxfs.writefile('/tmp/aaa', data) - for key, value in pairs(data) do - uci:set('design','@global[0]',key,value) - end - uci:commit('design') - end - return true -end - -return br diff --git a/luci-app-design-config/luasrc/view/design-config/other_button.htm b/luci-app-design-config/luasrc/view/design-config/other_button.htm deleted file mode 100644 index 1c391ad98..000000000 --- a/luci-app-design-config/luasrc/view/design-config/other_button.htm +++ /dev/null @@ -1,7 +0,0 @@ -<%+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/luci-app-design-config/luasrc/view/design-config/other_dvalue.htm b/luci-app-design-config/luasrc/view/design-config/other_dvalue.htm deleted file mode 100644 index 296c61e4d..000000000 --- a/luci-app-design-config/luasrc/view/design-config/other_dvalue.htm +++ /dev/null @@ -1,8 +0,0 @@ -<%+cbi/valueheader%> - -<% - local val = self:cfgvalue(section) or self.default or "" - write(pcdata(val)) -%> - -<%+cbi/valuefooter%> diff --git a/luci-app-design-config/po/zh-cn/design-config.po b/luci-app-design-config/po/zh-cn/design-config.po deleted file mode 100644 index af48e4c08..000000000 --- a/luci-app-design-config/po/zh-cn/design-config.po +++ /dev/null @@ -1,44 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -msgid "Design Config" -msgstr "Design 主题设置" - -msgid "Here you can set the mode of the theme and change the proxy tool icon in the navigation bar. [Recommend Chrome]" -msgstr "这里可以设置主题的模式和更换导航栏的代理工具图标。[建议使用 Chrome]" - -msgid "Theme mode" -msgstr "主题模式" - -msgid "Follow System" -msgstr "跟随系统" - -msgid "Force Light" -msgstr "强制亮色" - -msgid "Force Dark" -msgstr "强制暗色" - -msgid "You can choose Theme color mode here" -msgstr "你可以选择喜欢的主题模式" - -msgid "Navigation bar setting" -msgstr "导航栏设置" - -msgid "Display navigation bar" -msgstr "显示导航栏" - -msgid "Close navigation bar" -msgstr "关闭导航栏" - -msgid "The navigation bar is display by default" -msgstr "默认显示导航栏" - -msgid "Navigation bar proxy" -msgstr "导航栏代理" - -msgid "OpenClash by default" -msgstr "默认 OpenClash" - -msgid "Save Changes" -msgstr "保存更改" \ No newline at end of file diff --git a/luci-app-design-config/po/zh_Hans b/luci-app-design-config/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-design-config/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-design-config/root/etc/config/design b/luci-app-design-config/root/etc/config/design deleted file mode 100644 index faa653576..000000000 --- a/luci-app-design-config/root/etc/config/design +++ /dev/null @@ -1,4 +0,0 @@ -config global - option mode 'dark' - option navbar 'display' - option navbar_proxy 'openclash' \ No newline at end of file diff --git a/luci-app-design-config/root/etc/uci-defaults/luci-design-config b/luci-app-design-config/root/etc/uci-defaults/luci-design-config deleted file mode 100644 index 935d7c8be..000000000 --- a/luci-app-design-config/root/etc/uci-defaults/luci-design-config +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -sed -i 's/cbi.submit\"] = true/cbi.submit\"] = \"1\"/g' /usr/lib/lua/luci/dispatcher.lua - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-design-config/root/usr/share/rpcd/acl.d/luci-app-design-config.json b/luci-app-design-config/root/usr/share/rpcd/acl.d/luci-app-design-config.json deleted file mode 100644 index c439886f6..000000000 --- a/luci-app-design-config/root/usr/share/rpcd/acl.d/luci-app-design-config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-design-config": { - "description": "Grant UCI access for luci-app-design-config", - "read": { - "uci": [ "design" ] - }, - "write": { - "uci": [ "design" ] - } - } -} diff --git a/luci-app-dnsforwarder/Makefile b/luci-app-dnsforwarder/Makefile deleted file mode 100644 index a676026b7..000000000 --- a/luci-app-dnsforwarder/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# -# 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 Dnsforwarder -LUCI_DEPENDS:=+dnsforwarder -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-dnsforwarder -PKG_VERSION:=3.0.0 -PKG_RELEASE:=1 - -PKG_LICENSE:=GPL-3.0 -PKG_MAINTAINER:=Dennis - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-dnsforwarder/luasrc/controller/dnsforwarder.lua b/luci-app-dnsforwarder/luasrc/controller/dnsforwarder.lua deleted file mode 100644 index 86f44cf68..000000000 --- a/luci-app-dnsforwarder/luasrc/controller/dnsforwarder.lua +++ /dev/null @@ -1,11 +0,0 @@ -module("luci.controller.dnsforwarder", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/dnsforwarder") then - return - end - - entry({"admin", "services", "dnsforwarder"}, alias("admin", "services", "dnsforwarder", "general"), _("Dnsforwarder")).dependent = true - entry({"admin", "services", "dnsforwarder", "general"}, cbi("dnsforwarder/general"), _("General"), 10).leaf = true - entry({"admin", "services", "dnsforwarder", "log"}, cbi("dnsforwarder/log"), _("LOG"), 30).leaf = true -end diff --git a/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/general.lua b/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/general.lua deleted file mode 100644 index 32a1f86c4..000000000 --- a/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/general.lua +++ /dev/null @@ -1,187 +0,0 @@ ---Alex<1886090@gmail.com> ---Dennis - -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/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua b/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua deleted file mode 100644 index 8fe3dc67e..000000000 --- a/luci-app-dnsforwarder/luasrc/model/cbi/dnsforwarder/log.lua +++ /dev/null @@ -1,28 +0,0 @@ -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") -m.title = translate("运行日志") -m.description = 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 diff --git a/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.po b/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.po deleted file mode 100644 index f46021701..000000000 --- a/luci-app-dnsforwarder/po/zh-cn/dnsforwarder.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "Enable" -msgstr "启用" - -msgid "Running" -msgstr "运行中" - -msgid "Not running" -msgstr "未运行" - -msgid "General" -msgstr "常规" - -msgid "LOG" -msgstr "日志" diff --git a/luci-app-dnsforwarder/po/zh_Hans b/luci-app-dnsforwarder/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-dnsforwarder/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-e2guardian/Makefile b/luci-app-e2guardian/Makefile deleted file mode 100644 index 7a219ccd9..000000000 --- a/luci-app-e2guardian/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=E2Guardian LuCI Interface -LUCI_DEPENDS:=+luci-mod-admin-full +e2guardian - -PKG_MAINTAINER:=Marko Ratkaj -PKG_LICENSE:=Apache-2.0 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-e2guardian/luasrc/controller/e2guardian.lua b/luci-app-e2guardian/luasrc/controller/e2guardian.lua deleted file mode 100644 index dd545f50b..000000000 --- a/luci-app-e2guardian/luasrc/controller/e2guardian.lua +++ /dev/null @@ -1,22 +0,0 @@ ---[[ - -LuCI E2Guardian module - -Copyright (C) 2015, Itus Networks, Inc. - -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 - -Author: Marko Ratkaj - Luka Perkov - -]]-- - -module("luci.controller.e2guardian", package.seeall) - -function index() - entry({"admin", "services", "e2guardian"}, cbi("e2guardian"), _("E2Guardian")) -end diff --git a/luci-app-e2guardian/luasrc/model/cbi/e2guardian.lua b/luci-app-e2guardian/luasrc/model/cbi/e2guardian.lua deleted file mode 100644 index b62132108..000000000 --- a/luci-app-e2guardian/luasrc/model/cbi/e2guardian.lua +++ /dev/null @@ -1,399 +0,0 @@ ---[[ - -LuCI E2Guardian module - -Copyright (C) 2015, Itus Networks, Inc. - -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 - -Author: Marko Ratkaj - Luka Perkov - -]]-- - -local fs = require "nixio.fs" -local sys = require "luci.sys" - -m = Map("e2guardian", translate("E2Guardian")) -m.on_after_commit = function() luci.sys.call("/etc/init.d/e2guardian restart") end - -s = m:section(TypedSection, "e2guardian") -s.anonymous = true -s.addremove = false - -s:tab("tab_general", translate("General Settings")) -s:tab("tab_additional", translate("Additional Settings")) -s:tab("tab_logs", translate("Logs")) - - ------------------ General Settings Tab ----------------------- - -filterip = s:taboption("tab_general", Value, "filterip", translate("IP that E2Guardian listens")) -filterip.datatype = "ip4addr" - -filterports = s:taboption("tab_general", Value, "filterports", translate("Port that E2Guardian listens")) -filterports.datatype = "portrange" -filterports.placeholder = "0-65535" - -proxyip = s:taboption("tab_general", Value, "proxyip", translate("IP address of the proxy")) -proxyip.datatype = "ip4addr" -proxyip.default = "127.0.0.1" - -proxyport = s:taboption("tab_general", Value, "proxyport", translate("Port of the proxy")) -proxyport.datatype = "portrange" -proxyport.placeholder = "0-65535" - -languagedir = s:taboption("tab_general", Value, "languagedir", translate("Language dir")) -languagedir.datatype = "string" -languagedir.default = "/usr/share/e2guardian/languages" - -language = s:taboption("tab_general", Value, "language", translate("Language to use")) -language.datatype = "string" -language.default = "ukenglish" - -loglevel = s:taboption("tab_general", ListValue, "loglevel", translate("Logging Settings")) -loglevel:value("0", translate("none")) -loglevel:value("1", translate("just denied")) -loglevel:value("2", translate("all text based")) -loglevel:value("3", translate("all requests")) -loglevel.default = "2" - -logexceptionhits = s:taboption("tab_general", ListValue, "logexceptionhits", translate("Log Exception Hits")) -logexceptionhits:value("0", translate("never")) -logexceptionhits:value("1", translate("log, but don't mark as exceptions")) -logexceptionhits:value("2", translate("log and mark")) -logexceptionhits.default = "2" - -logfileformat = s:taboption("tab_general", ListValue, "logfileformat", translate("Log File Format")) -logfileformat:value("1", translate("DansgGuardian format, space delimited")) -logfileformat:value("2", translate("CSV-style format")) -logfileformat:value("3", translate("Squid Log File Format")) -logfileformat:value("4", translate("Tab delimited")) -logfileformat:value("5", translate("Protex format")) -logfileformat:value("6", translate("Protex format with server field blanked")) -logfileformat.default = "1" - -accessdeniedaddress = s:taboption("tab_general", Value, "accessdeniedaddress", translate("Access denied address"), -translate("Server to which the cgi e2guardian reporting script was copied. Reporting levels 1 and 2 only")) -accessdeniedaddress.datatype = "string" -accessdeniedaddress.default = "http://YOURSERVER.YOURDOMAIN/cgi-bin/e2guardian.pl" - -usecustombannedimage = s:taboption("tab_general", ListValue, "usecustombannedimage", translate("Banned image replacement")) -usecustombannedimage:value("on", translate("Yes")) -usecustombannedimage:value("off", translate("No")) -usecustombannedimage.default = "on" - -custombannedimagefile = s:taboption("tab_general", Value, "custombannedimagefile", translate("Custom banned image file")) -custombannedimagefile.datatype = "string" -custombannedimagefile.default = "/usr/share/e2guardian/transparent1x1.gif" - -usecustombannedflash = s:taboption("tab_general", ListValue, "usecustombannedflash", translate("Banned flash replacement")) -usecustombannedflash:value("on", translate("Yes")) -usecustombannedflash:value("off", translate("No")) -usecustombannedflash.default = "on" - -custombannedflashfile = s:taboption("tab_general", Value, "custombannedflashfile", translate("Custom banned flash file")) -custombannedflashfile.datatype = "string" -custombannedflashfile.default = "/usr/share/e2guardian/blockedflash.swf" - -filtergroups = s:taboption("tab_general", Value, "filtergroups", translate("Number of filter groups")) -filtergroups.datatype = "and(uinteger,min(1))" -filtergroups.default = "1" - -filtergroupslist = s:taboption("tab_general", Value, "filtergroupslist", translate("List of filter groups")) -filtergroupslist.datatype = "string" -filtergroupslist.default = "/etc/e2guardian/lists/filtergroupslist" - -bannediplist = s:taboption("tab_general", Value, "bannediplist", translate("List of banned IPs")) -bannediplist.datatype = "string" -bannediplist.default = "/etc/e2guardian/lists/bannediplist" - -exceptioniplist = s:taboption("tab_general", Value, "exceptioniplist", translate("List of IP exceptions")) -exceptioniplist.datatype = "string" -exceptioniplist.default = "/etc/e2guardian/lists/exceptioniplist" - -perroomblockingdirectory = s:taboption("tab_general", Value, "perroomblockingdirectory", translate("Per-Room blocking definition directory")) -perroomblockingdirectory.datatype = "string" -perroomblockingdirectory.default = "/etc/e2guardian/lists/bannedrooms/" - -showweightedfound = s:taboption("tab_general", ListValue, "showweightedfound", translate("Show weighted phrases found")) -showweightedfound:value("on", translate("Yes")) -showweightedfound:value("off", translate("No")) -showweightedfound.default = "on" - -weightedphrasemode = s:taboption("tab_general", ListValue, "weightedphrasemode", translate("Weighted phrase mode")) -weightedphrasemode:value("0", translate("off")) -weightedphrasemode:value("1", translate("on, normal operation")) -weightedphrasemode:value("2", translate("on, phrase found only counts once on a page")) -weightedphrasemode.default = "2" - -urlcachenumber = s:taboption("tab_general", Value, "urlcachenumber", translate("Clean result caching for URLs")) -urlcachenumber.datatype = "and(uinteger,min(0))" -urlcachenumber.default = "1000" - -urlcacheage = s:taboption("tab_general", Value, "urlcacheage", translate("Age before they should be ignored in seconds")) -urlcacheage.datatype = "and(uinteger,min(0))" -urlcacheage.default = "900" - -scancleancache = s:taboption("tab_general", ListValue, "scancleancache", translate("Cache for content (AV) scans as 'clean'")) -scancleancache:value("on", translate("Yes")) -scancleancache:value("off", translate("No")) -scancleancache.default = "on" - -phrasefiltermode = s:taboption("tab_general", ListValue, "phrasefiltermode", translate("Filtering options")) -phrasefiltermode:value("0", translate("raw")) -phrasefiltermode:value("1", translate("smart")) -phrasefiltermode:value("2", translate("both raw and smart")) -phrasefiltermode:value("3", translate("meta/title")) -phrasefiltermode.default = "2" - -preservecase = s:taboption("tab_general", ListValue, "perservecase", translate("Lower caseing options")) -preservecase:value("0", translate("force lower case")) -preservecase:value("1", translate("don't change")) -preservecase:value("2", translate("scan fist in lower, then in original")) -preservecase.default = "0" - -hexdecodecontent = s:taboption("tab_general", ListValue, "hexdecodecontent", translate("Hex decoding options")) -hexdecodecontent:value("on", translate("Yes")) -hexdecodecontent:value("off", translate("No")) -hexdecodecontent.default = "off" - -forcequicksearch = s:taboption("tab_general", ListValue, "forcequicksearch", translate("Quick search")) -forcequicksearch:value("on", translate("Yes")) -forcequicksearch:value("off", translate("No")) -forcequicksearch.default = "off" - -reverseaddresslookups= s:taboption("tab_general", ListValue, "reverseaddresslookups", translate("Reverse lookups for banned site and URLs")) -reverseaddresslookups:value("on", translate("Yes")) -reverseaddresslookups:value("off", translate("No")) -reverseaddresslookups.default = "off" - -reverseclientiplookups = s:taboption("tab_general", ListValue, "reverseclientiplookups", translate("Reverse lookups for banned and exception IP lists")) -reverseclientiplookups:value("on", translate("Yes")) -reverseclientiplookups:value("off", translate("No")) -reverseclientiplookups.default = "off" - -logclienthostnames = s:taboption("tab_general", ListValue, "logclienthostnames", translate("Perform reverse lookups on client IPs for successful requests")) -logclienthostnames:value("on", translate("Yes")) -logclienthostnames:value("off", translate("No")) -logclienthostnames.default = "off" - -createlistcachefiles = s:taboption("tab_general", ListValue, "createlistcachefiles", translate("Build bannedsitelist and bannedurllist cache files")) -createlistcachefiles:value("on",translate("Yes")) -createlistcachefiles:value("off",translate("No")) -createlistcachefiles.default = "on" - -prefercachedlists = s:taboption("tab_general", ListValue, "prefercachedlists", translate("Prefer cached list files")) -prefercachedlists:value("on", translate("Yes")) -prefercachedlists:value("off", translate("No")) -prefercachedlists.default = "off" - -maxuploadsize = s:taboption("tab_general", Value, "maxuploadsize", translate("Max upload size (in Kbytes)")) -maxuploadsize:value("-1", translate("no blocking")) -maxuploadsize:value("0", translate("complete block")) -maxuploadsize.default = "-1" - -maxcontentfiltersize = s:taboption("tab_general", Value, "maxcontentfiltersize", translate("Max content filter size"), -translate("The value must not be higher than max content ram cache scan size or 0 to match it")) -maxcontentfiltersize.datatype = "and(uinteger,min(0))" -maxcontentfiltersize.default = "256" - -maxcontentramcachescansize = s:taboption("tab_general", Value, "maxcontentramcachescansize", translate("Max content ram cache scan size"), -translate("This is the max size of file that DG will download and cache in RAM")) -maxcontentramcachescansize.datatype = "and(uinteger,min(0))" -maxcontentramcachescansize.default = "2000" - -maxcontentfilecachescansize = s:taboption("tab_general", Value, "maxcontentfilecachescansize", translate("Max content file cache scan size")) -maxcontentfilecachescansize.datatype = "and(uinteger,min(0))" -maxcontentfilecachescansize.default = "20000" - -proxytimeout = s:taboption("tab_general", Value, "proxytimeout", translate("Proxy timeout (5-100)")) -proxytimeout.datatype = "range(5,100)" -proxytimeout.default = "20" - -proxyexchange = s:taboption("tab_general", Value, "proxyexchange", translate("Proxy header excahnge (20-300)")) -proxyexchange.datatype = "range(20,300)" -proxyexchange.default = "20" - -pcontimeout = s:taboption("tab_general", Value, "pcontimeout", translate("Pconn timeout"), -translate("How long a persistent connection will wait for other requests")) -pcontimeout.datatype = "range(5,300)" -pcontimeout.default = "55" - -filecachedir = s:taboption("tab_general", Value, "filecachedir", translate("File cache directory")) -filecachedir.datatype = "string" -filecachedir.default = "/tmp" - -deletedownloadedtempfiles = s:taboption("tab_general", ListValue, "deletedownloadedtempfiles", translate("Delete file cache after user completes download")) -deletedownloadedtempfiles:value("on", translate("Yes")) -deletedownloadedtempfiles:value("off", translate("No")) -deletedownloadedtempfiles.default = "on" - -initialtrickledelay = s:taboption("tab_general", Value, "initialtrickledelay", translate("Initial Trickle delay"), -translate("Number of seconds a browser connection is left waiting before first being sent *something* to keep it alive")) -initialtrickledelay.datatype = "and(uinteger,min(0))" -initialtrickledelay.default = "20" - -trickledelay = s:taboption("tab_general", Value, "trickledelay", translate("Trickle delay"), -translate("Number of seconds a browser connection is left waiting before being sent more *something* to keep it alive")) -trickledelay.datatype = "and(uinteger,min(0))" -trickledelay.default = "10" - -downloadmanager = s:taboption("tab_general", Value, "downloadmanager", translate("Download manager")) -downloadmanager.datatype = "string" -downloadmanager.default = "/etc/e2guardian/downloadmanagers/default.conf" - -contentscannertimeout = s:taboption("tab_general", Value, "contentscannertimeout", translate("Content scanner timeout")) -contentscannertimeout.datatype = "and(uinteger,min(0))" -contentscannertimeout.default = "60" - -contentscanexceptions = s:taboption("tab_general", ListValue, "contentscanexceptions", translate("Content scan exceptions")) -contentscanexceptions:value("on", translate("Yes")) -contentscanexceptions:value("off", translate("No")) -contentscanexceptions.default = "off" - -recheckreplacedurls = s:taboption("tab_general", ListValue, "recheckreplacedurls", translate("e-check replaced URLs")) -recheckreplacedurls:value("on", translate("Yes")) -recheckreplacedurls:value("off", translate("No")) -recheckreplacedurls.default = "off" - -forwardedfor = s:taboption("tab_general", ListValue, "forwardedfor", translate("Misc setting: forwardedfor"), -translate("If on, it may help solve some problem sites that need to know the source ip.")) -forwardedfor:value("on", translate("Yes")) -forwardedfor:value("off", translate("No")) -forwardedfor.default = "off" - -usexforwardedfor = s:taboption("tab_general", ListValue, "usexforwardedfor", translate("Misc setting: usexforwardedfor"), -translate("This is for when you have squid between the clients and E2Guardian")) -usexforwardedfor:value("on", translate("Yes")) -usexforwardedfor:value("off", translate("No")) -usexforwardedfor.default = "off" - -logconnectionhandlingerrors = s:taboption("tab_general", ListValue, "logconnectionhandlingerrors", translate("Log debug info about log()ing and accept()ing")) -logconnectionhandlingerrors:value("on", translate("Yes")) -logconnectionhandlingerrors:value("off", translate("No")) -logconnectionhandlingerrors.default = "on" - -logchildprocesshandling = s:taboption("tab_general", ListValue, "logchildprocesshandling", translate("Log child process handling")) -logchildprocesshandling:value("on", translate("Yes")) -logchildprocesshandling:value("off", translate("No")) -logchildprocesshandling.default = "off" - -maxchildren = s:taboption("tab_general", Value, "maxchildren", translate("Max number of processes to spawn")) -maxchildren.datatype = "and(uinteger,min(0))" -maxchildren.default = "180" - -minchildren = s:taboption("tab_general", Value, "minchildren", translate("Min number of processes to spawn")) -minchildren.datatype = "and(uinteger,min(0))" -minchildren.default = "20" - -minsparechildren = s:taboption("tab_general", Value, "minsparechildren", translate("Min number of processes to keep ready")) -minsparechildren.datatype = "and(uinteger,min(0))" -minsparechildren.default = "16" - -preforkchildren = s:taboption("tab_general", Value, "preforkchildren", translate("Sets minimum nuber of processes when it runs out")) -preforkchildren.datatype = "and(uinteger,min(0))" -preforkchildren.default = "10" - -maxsparechildren = s:taboption("tab_general", Value, "maxsparechildren", translate("Sets the maximum number of processes to have doing nothing")) -maxsparechildren.datatype = "and(uinteger,min(0))" -maxsparechildren.default = "32" - -maxagechildren = s:taboption("tab_general", Value, "maxagechildren", translate("Max age of child process")) -maxagechildren.datatype = "and(uinteger,min(0))" -maxagechildren.default = "500" - -maxips = s:taboption("tab_general", Value, "maxips", translate("Max number of clinets allowed to connect")) -maxips:value("0", translate("no limit")) -maxips.default = "0" - -ipipcfilename = s:taboption("tab_general", Value, "ipipcfilename", translate("IP list IPC server directory and filename")) -ipipcfilename.datatype = "string" -ipipcfilename.default = "/tmp/.dguardianipc" - -urlipcfilename = s:taboption("tab_general", Value, "urlipcfilename", translate("Defines URL list IPC server directory and filename used to communicate with the URL cache process")) -urlipcfilename.datatype = "string" -urlipcfilename.default = "/tmp/.dguardianurlipc" - -ipcfilename = s:taboption("tab_general", Value, "ipcfilename", translate("Defines URL list IPC server directory and filename used to communicate with the URL cache process")) -ipcfilename.datatype = "string" -ipcfilename.default = "/tmp/.dguardianipipc" - -nodeamon = s:taboption("tab_general", ListValue, "nodeamon", translate("Disable deamoning")) -nodeamon:value("on", translate("Yes")) -nodeamon:value("off", translate("No")) -nodeamon.default = "off" - -nologger = s:taboption("tab_general", ListValue, "nologger", translate("Disable logger")) -nologger:value("on", translate("Yes")) -nologger:value("off", translate("No")) -nologger.default = "off" - -logadblock = s:taboption("tab_general", ListValue, "logadblock", translate("Enable logging of ADs")) -logadblock:value("on", translate("Yes")) -logadblock:value("off", translate("No")) -logadblock.default = "off" - -loguseragent = s:taboption("tab_general", ListValue, "loguseragent", translate("Enable logging of client user agent")) -loguseragent:value("on", translate("Yes")) -loguseragent:value("off", translate("No")) -loguseragent.default = "off" - -softrestart = s:taboption("tab_general", ListValue, "softrestart", translate("Enable soft restart")) -softrestart:value("on", translate("Yes")) -softrestart:value("off", translate("No")) -softrestart.default = "off" - - ------------------------- Additional Settings Tab ---------------------------- - -e2guardian_config_file = s:taboption("tab_additional", TextValue, "_data", "") -e2guardian_config_file.wrap = "off" -e2guardian_config_file.rows = 25 -e2guardian_config_file.rmempty = false - -function e2guardian_config_file.cfgvalue() - local uci = require "luci.model.uci".cursor_state() - file = "/etc/e2guardian/e2guardianf1.conf" - if file then - return fs.readfile(file) or "" - else - return "" - end -end - -function e2guardian_config_file.write(self, section, value) - if value then - local uci = require "luci.model.uci".cursor_state() - file = "/etc/e2guardian/e2guardianf1.conf" - fs.writefile(file, value:gsub("\r\n", "\n")) - end -end - - ----------------------------- Logs Tab ----------------------------- - -e2guardian_logfile = s:taboption("tab_logs", TextValue, "lines", "") -e2guardian_logfile.wrap = "off" -e2guardian_logfile.rows = 25 -e2guardian_logfile.rmempty = true - -function e2guardian_logfile.cfgvalue() - local uci = require "luci.model.uci".cursor_state() - file = "/tmp/e2guardian/access.log" - if file then - return fs.readfile(file) or "" - else - return "Can't read log file" - end -end - -function e2guardian_logfile.write() - return "" -end - -return m diff --git a/luci-app-easymesh/Makefile b/luci-app-easymesh/Makefile deleted file mode 100644 index 5111f7b05..000000000 --- a/luci-app-easymesh/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -#-- Copyright (C) 2021 dz -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI Support for easymesh -LUCI_DEPENDS:=+kmod-cfg80211 +batctl-default +kmod-batman-adv +dawn -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-easymesh -PKG_VERSION:=2.0 -PKG_RELEASE:=1 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-easymesh/luasrc/controller/easymesh.lua b/luci-app-easymesh/luasrc/controller/easymesh.lua deleted file mode 100644 index a004058d5..000000000 --- a/luci-app-easymesh/luasrc/controller/easymesh.lua +++ /dev/null @@ -1,14 +0,0 @@ --- Copyright (C) 2021 dz - -module("luci.controller.easymesh", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/easymesh") then - return - end - - local page - page = entry({"admin", "network", "easymesh"}, cbi("easymesh"), _("EASY MESH"), 60) - page.dependent = true - page.acl_depends = { "luci-app-easymesh" } -end diff --git a/luci-app-easymesh/luasrc/model/cbi/easymesh.lua b/luci-app-easymesh/luasrc/model/cbi/easymesh.lua deleted file mode 100644 index e6ccdb4b0..000000000 --- a/luci-app-easymesh/luasrc/model/cbi/easymesh.lua +++ /dev/null @@ -1,128 +0,0 @@ --- Copyright (C) 2021 dz - -local m, s, o -local sys = require "luci.sys" -local uci = require "luci.model.uci".cursor() - -m = Map("easymesh") - -function detect_Node() - local data = {} - local lps = luci.util.execi(" batctl n 2>/dev/null | tail +2 | sed 's/^[ ][ ]*//g' | sed 's/[ ][ ]*/ /g' | sed 's/$/ /g' ") - for value in lps do - local row = {} - local pos = string.find(value, " ") - local IFA = string.sub(value, 1, pos - 1) - local value = string.sub(value, pos + 1, string.len(value)) - pos = string.find(value, " ") - local pos = string.find(value, " ") - local Neighbora = string.sub(value, 1, pos - 1) - local value = string.sub(value, pos + 1, string.len(value)) - pos = string.find(value, " ") - local pos = string.find(value, " ") - local lastseena = string.sub(value, 1, pos - 1) - local value = string.sub(value, pos + 1, string.len(value)) - pos = string.find(value, " ") - row["IF"] = IFA - row["Neighbor"] = Neighbora - row["lastseen"] = lastseena - table.insert(data, row) - end - return data -end -local Nodes = luci.sys.exec("batctl n 2>/dev/null| tail +3 | wc -l") -local Node = detect_Node() - -v = m:section(Table, Node, "" ,translate("Active node:" .. Nodes .. "")) -v:option(DummyValue, "IF") -v:option(DummyValue, "Neighbor") -v:option(DummyValue, "lastseen") - --- Basic -s = m:section(TypedSection, "easymesh", translate("Settings")) -s.description = translate("General Settings") -s.anonymous = true - ----- Eanble -o = s:option(Flag, "enabled", translate("Enable")) -o.description = translate("Enable or disable EASY MESH") -o.default = 0 -o.rmempty = false - -o = s:option(ListValue, "role", translate("role")) -o:value("off", translate("off")) -o:value("server", translate("host MESH")) -o:value("client", translate("son MESH")) -o.rmempty = false - -apRadio = s:option(ListValue, "apRadio", translate("MESH Radio device")) -apRadio.description = translate("The radio device which MESH use") -uci:foreach("wireless", "wifi-device", - function(s) - apRadio:value(s['.name']) - end) -apRadio:value("all", translate("ALL")) -o.default = "radio0" -o.rmempty = false - ----- mesh -o = s:option(Value, "mesh_id", translate("MESH ID")) -o.default = "easymesh" -o.description = translate("MESH ID") - -enable = s:option(Flag, "encryption", translate("Encryption")) -enable.default = 0 -enable.rmempty = false - -o = s:option(Value, "key", translate("Key")) -o.default = "easymesh" -o:depends("encryption", 1) - ----- kvr -enable = s:option(Flag, "kvr", translate("K/V/R")) -enable.default = 1 -enable.rmempty = false - -o = s:option(Value, "mobility_domain", translate("Mobility Domain")) -o.description = translate("4-character hexadecimal ID") -o.default = "4f57" -o.datatype = "and(hexstring,rangelength(4,4))" -o:depends("kvr", 1) - -o = s:option(Value, "rssi_val", translate("Threshold for an good RSSI")) -o.default = "-60" -o.atatype = "range(-1,-120)" -o:depends("kvr", 1) - -o = s:option(Value, "low_rssi_val", translate("Threshold for an bad RSSI")) -o.default = "-88" -o.atatype = "range(-1,-120)" -o:depends("kvr", 1) - ----- ap_mode -enable = s:option(Flag, "ap_mode", translate("AP MODE Enable")) -enable.description = translate("Enable or disable AP MODE") -enable.default = 0 -enable.rmempty = false - -o = s:option(Value, "ipaddr", translate("IPv4-Address")) -o.default = "192.168.1.10" -o.datatype = "ip4addr" -o:depends("ap_mode", 1) - -o = s:option(Value, "netmask", translate("IPv4 netmask")) -o.default = "255.255.255.0" -o.datatype = "ip4addr" -o:depends("ap_mode", 1) - -o = s:option(Value, "gateway", translate("IPv4 gateway")) -o.default = "192.168.1.1" -o.datatype = "ip4addr" -o:depends("ap_mode", 1) - -o = s:option(Value, "dns", translate("Use custom DNS servers")) -o.default = "192.168.1.1" -o.datatype = "ip4addr" -o:depends("ap_mode", 1) - -return m diff --git a/luci-app-easymesh/po/zh-cn/easymesh.po b/luci-app-easymesh/po/zh-cn/easymesh.po deleted file mode 100755 index d2759869c..000000000 --- a/luci-app-easymesh/po/zh-cn/easymesh.po +++ /dev/null @@ -1,83 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -msgid "easymesh" -msgstr "easymesh" - -msgid "Active node:" .. Nodes .. "" -msgstr "活动节点:" .. Nodes .. "" - -msgid "IF" -msgstr "IF" - -msgid "Neighbor" -msgstr "节点邻居设备" - -msgid "lastseen" -msgstr "上次连接延时" - -msgid "EASY MESH" -msgstr "简单MESH" - -msgid "Settings" -msgstr "设置" - -msgid "General Settings" -msgstr "基本设置" - -msgid "Enable" -msgstr "启用" - -msgid "role" -msgstr "角色" - -msgid "off" -msgstr "关闭" - -msgid "host MESH" -msgstr "主MESH" - -msgid "son MESH" -msgstr "子MESH" - -msgid "MESH Radio device" -msgstr "MESH无线设备" - -msgid "The radio device which MESH use" -msgstr "使用MESH组网的无线设备" - -msgid "AUTO" -msgstr "自动" - -msgid "Enable or disable EASY MESH" -msgstr "启用或禁用简单MESH" - -msgid "Encryption" -msgstr "加密" - -msgid "Key" -msgstr "密码" - -msgid "Threshold for an good RSSI" -msgstr "快速漫游接入值" - -msgid "Threshold for an bad RSSI" -msgstr "快速漫游踢出值" - -msgid "AP MODE Enable" -msgstr "启用AP模式" - -msgid "Enable or disable AP MODE" -msgstr "启用或禁用AP模式" - -msgid "IPv4-Address" -msgstr "IPv4 地址" - -msgid "IPv4 netmask" -msgstr "IPv4 子网掩码" - -msgid "IPv4 gateway" -msgstr "IPv4 网关" - -msgid "Use custom DNS servers" -msgstr "使用自定义的 DNS 服务器" diff --git a/luci-app-easymesh/po/zh_Hans b/luci-app-easymesh/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-easymesh/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-easymesh/root/etc/config/easymesh b/luci-app-easymesh/root/etc/config/easymesh deleted file mode 100644 index 3446d21c1..000000000 --- a/luci-app-easymesh/root/etc/config/easymesh +++ /dev/null @@ -1,3 +0,0 @@ - -config easymesh 'config' - option enabled '0' diff --git a/luci-app-easymesh/root/etc/init.d/easymesh b/luci-app-easymesh/root/etc/init.d/easymesh deleted file mode 100755 index b920a3c00..000000000 --- a/luci-app-easymesh/root/etc/init.d/easymesh +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/sh /etc/rc.common -START=99 -STOP=70 - -load_easymesh_config() { - enable=$(uci -q get easymesh.config.enabled) - mesh_bat0=$(uci -q get network.bat0) - ap_mode=$(uci -q get easymesh.config.ap_mode) - lan=$(uci -q get network.lan.device) - netmask=$(uci -q get easymesh.config.netmask) - gateway=$(uci -q get easymesh.config.gateway) - dns=$(uci -q get easymesh.config.dns) - ap_ipaddr=$(uci -q get network.lan.ipaddr) - ap_ipaddr1=$(sed -n '1p' /etc/easymesh 2>/dev/null) - apRadio=$(uci -q get easymesh.config.apRadio) - kvr=$(uci -q get easymesh.config.kvr) - iapp=$(uci -q get easymesh.config.iapp) - brlan=$(uci -q get network.@device[0].name) - role=$(uci -q get easymesh.config.role) -} - -ap_mode_stop() { - ap_ipaddr=$(uci -q get network.lan.ipaddr) - ap_ipaddr1=$(sed -n '1p' /etc/easymesh 2>/dev/null) - dns1=$(sed -n '2p' /etc/easymesh 2>/dev/null) - if [ "$ap_ipaddr" = "$ap_ipaddr1" ]; then - uci -q delete network.lan.gateway - uci -q del_list network.lan.dns=$dns1 - uci commit network - - echo "" >/etc/easymesh - - uci -q delete dhcp.lan.dynamicdhcp - uci -q delete dhcp.lan.ignore - uci commit dhcp - - /etc/init.d/odhcpd enable && /etc/init.d/odhcpd start - /etc/init.d/firewall enable && /etc/init.d/firewall start >/dev/null 2>&1 - fi -} - -add_wifi_mesh() { - mesh_nwi_mesh=$(uci -q get network.nwi_mesh_${apall}) - mesh_apRadio=$(uci -q get wireless.mesh_${apall}.device) - mesh_mesh=$(uci -q get wireless.mesh_${apall}) - mesh_id=$(uci -q get easymesh.config.mesh_id) - mobility_domain=$(uci -q get easymesh.config.mobility_domain) - key=$(uci -q get easymesh.config.key) - encryption=$(uci -q get easymesh.config.encryption) - - if [ "$mesh_nwi_mesh" != "interface" ]; then - uci set network.nwi_mesh_$apall=interface - uci set network.nwi_mesh_$apall.proto='batadv_hardif' - uci set network.nwi_mesh_$apall.master='bat0' - uci set network.nwi_mesh_$apall.mtu='1536' - uci commit network - fi - - if [ "$mesh_mesh" != "wifi-iface" ]; then - uci set wireless.mesh_$apall=wifi-iface - uci set wireless.mesh_$apall.device=$apall - uci set wireless.mesh_$apall.ifname=mesh_${apall} - uci set wireless.mesh_$apall.network=nwi_mesh_${apall} - uci set wireless.mesh_$apall.mode='mesh' - uci set wireless.mesh_$apall.mesh_id=$mesh_id - uci set wireless.mesh_$apall.mesh_fwding='0' - uci set wireless.mesh_$apall.mesh_ttl='1' - uci set wireless.mesh_$apall.mcast_rate='24000' - uci set wireless.mesh_$apall.disabled='0' - uci commit wireless - fi - - if [ "$mesh_mesh" = "wifi-iface" ]; then - if [ "$mesh_apRadio" != "$apall" ]; then - uci set wireless.mesh_$apall.device=$apall - uci commit wireless - fi - fi - - if [ "$encryption" != 1 ]; then - uci set wireless.mesh_$apall.encryption='none' - uci commit wireless - else - uci set wireless.mesh_$apall.encryption='sae' - uci set wireless.mesh_$apall.key=$key - uci commit wireless - fi -} - -add_kvr() { - kvr=$(uci -q get easymesh.config.kvr) - mobility_domain=$(uci -q get easymesh.config.mobility_domain) - for apall in $(uci -X show wireless | grep wifi-device | awk -F'[.=]' '{print $2}'); do - if [ "$kvr" = 1 ]; then - uci set wireless.default_$apall.ieee80211k='1' - uci set wireless.default_$apall.rrm_neighbor_report='1' - uci set wireless.default_$apall.rrm_beacon_report='1' - uci set wireless.default_$apall.ieee80211v='1' - uci set wireless.default_$apall.bss_transition='1' - uci set wireless.default_$apall.ieee80211r='1' - uci set wireless.default_$apall.encryption='psk2+ccmp' - uci set wireless.default_$apall.mobility_domain=$mobility_domain - uci set wireless.default_$apall.ft_over_ds='1' - uci set wireless.default_$apall.ft_psk_generate_local='1' - uci commit wireless - else - uci -q delete wireless.default_$apall.ieee80211k - uci -q delete wireless.default_$apall.ieee80211v - uci -q delete wireless.default_$apall.ieee80211r - uci commit wireless - fi - done -} - -add_dawn() { - kvr=$(uci -q get easymesh.config.kvr) - rssi_val=$(uci -q get easymesh.config.rssi_val) - low_rssi_val=$(uci -q get easymesh.config.low_rssi_val) - - if [ "$kvr" = 1 ]; then - uci set dawn.@metric[0].rssi_val=$rssi_val - uci set dawn.@metric[0].low_rssi_val=$low_rssi_val - uci commit dawn - /etc/init.d/dawn enable && /etc/init.d/dawn start - else - /etc/init.d/dawn stop && /etc/init.d/dawn disable - fi -} - -set_easymesh() { - load_easymesh_config - if [ "$enable" = 1 ]; then - if [ "$mesh_bat0" != "interface" ]; then - uci set network.bat0=interface - uci set network.bat0.proto='batadv' - uci set network.bat0.routing_algo='BATMAN_IV' - uci set network.bat0.aggregated_ogms='1' - uci set network.bat0.ap_isolation='0' - uci set network.bat0.bonding='0' - uci set network.bat0.bridge_loop_avoidance='1' - uci set network.bat0.distributed_arp_table='1' - uci set network.bat0.fragmentation='1' - # uci set network.bat0.gw_bandwidth='10000/2000' - # uci set network.bat0.gw_sel_class='20' - uci set network.bat0.hop_penalty='30' - uci set network.bat0.isolation_mark='0x00000000/0x00000000' - uci set network.bat0.log_level='0' - uci set network.bat0.multicast_fanout='16' - uci set network.bat0.multicast_mode='1' - uci set network.bat0.network_coding='0' - uci set network.bat0.orig_interval='1000' - - if [ "$role" = "server" ]; then - uci set network.bat0.gw_mode='server' - elif [ "$role" = "client" ]; then - uci set network.bat0.gw_mode='client' - else - uci set network.bat0.gw_mode='off' - fi - - if [ "$brlan" = "br-lan" ]; then - uci add_list network.@device[0].ports='bat0' - else - uci set network.lan.device="${lan} bat0" - fi - uci commit network - fi - - if [ "$apRadio" = "all" ]; then - for apall in $(uci -X show wireless | grep wifi-device | awk -F'[.=]' '{print $2}'); do - add_wifi_mesh - done - else - apall=$apRadio - add_wifi_mesh - fi - - add_kvr - add_dawn - - if [ "$ap_mode" = 1 ]; then - if [ "$ap_ipaddr" != "$ipaddr" ]; then - uci set network.lan.ipaddr=$ipaddr - uci set network.lan.netmask=$netmask - uci set network.lan.gateway=$gateway - uci add_list network.lan.dns=$dns - uci commit network - - echo "" >/etc/easymesh - echo "$ipaddr" >/etc/easymesh - echo "$dns" >>/etc/easymesh - - uci set dhcp.lan.dynamicdhcp='0' - uci set dhcp.lan.ignore='1' - uci -q delete dhcp.lan.ra - uci -q delete dhcp.lan.dhcpv6 - uci -q delete dhcp.lan.ra_management - uci commit dhcp - - /etc/init.d/odhcpd stop && /etc/init.d/odhcpd disable - /etc/init.d/firewall stop && /etc/init.d/firewall disable >/dev/null 2>&1 - fi - else - ap_mode_stop - fi - else - if [ "$mesh_bat0" = "interface" ]; then - uci -q delete network.bat0 - if [ "$brlan" = "br-lan" ]; then - uci -q del_list network.@device[0].ports='bat0' - else - sed -i 's/ bat0//' /etc/config/network - fi - uci commit network - fi - - for apall in $(uci -X show wireless | grep wifi-device | awk -F'[.=]' '{print $2}'); do - mesh_nwi_mesh=$(uci -q get network.nwi_mesh_${apall}) - mesh_mesh=$(uci -q get wireless.mesh_${apall}) - - if [ "$mesh_nwi_mesh" = "interface" ]; then - uci -q delete network.nwi_mesh_$apall - uci commit network - fi - - if [ "$mesh_mesh" = "wifi-iface" ]; then - uci -q delete wireless.mesh_$apall - uci commit wireless - fi - done - - add_kvr - add_dawn - - if [ "$ap_mode" = 1 ]; then - ap_mode_stop - fi - fi - /etc/init.d/network restart -} - -start() { - return 0 -} - -stop() { - return 0 -} - -restart() { - set_easymesh -} diff --git a/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh b/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh deleted file mode 100644 index b295875dd..000000000 --- a/luci-app-easymesh/root/etc/uci-defaults/luci-easymesh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-easymesh.json" ] && { - cat > /usr/share/ucitrack/luci-app-easymesh.json << EEOF -{ - "config": "easymesh", - "init": "easymesh" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@easymesh[-1] - add ucitrack easymesh - set ucitrack.@easymesh[-1].init=easymesh - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json b/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json deleted file mode 100644 index 55ab19eae..000000000 --- a/luci-app-easymesh/root/usr/share/rpcd/acl.d/luci-app-easymesh.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-easymesh": { - "description": "Grant UCI access for luci-app-easymesh", - "read": { - "uci": [ "easymesh" ] - }, - "write": { - "uci": [ "easymesh" ] - } - } -} diff --git a/luci-app-easymesh/root/usr/share/ucitrack/luci-app-easymesh.json b/luci-app-easymesh/root/usr/share/ucitrack/luci-app-easymesh.json deleted file mode 100644 index 850d101b0..000000000 --- a/luci-app-easymesh/root/usr/share/ucitrack/luci-app-easymesh.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "easymesh", - "init": "easymesh" -} diff --git a/luci-app-haproxy-tcp/Makefile b/luci-app-haproxy-tcp/Makefile deleted file mode 100644 index b9e3a5853..000000000 --- a/luci-app-haproxy-tcp/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# 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> - -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 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua b/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua deleted file mode 100644 index 849209694..000000000 --- a/luci-app-haproxy-tcp/luasrc/controller/haproxy.lua +++ /dev/null @@ -1,9 +0,0 @@ -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 diff --git a/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua b/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua deleted file mode 100644 index 382fa5b02..000000000 --- a/luci-app-haproxy-tcp/luasrc/model/cbi/haproxy.lua +++ /dev/null @@ -1,68 +0,0 @@ ---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 = 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 = 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/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po b/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po deleted file mode 100644 index f0f2aa837..000000000 --- a/luci-app-haproxy-tcp/po/zh-cn/haproxy-tcp.po +++ /dev/null @@ -1,34 +0,0 @@ -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/luci-app-haproxy-tcp/po/zh_Hans b/luci-app-haproxy-tcp/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-haproxy-tcp/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-haproxy-tcp/root/etc/config/haproxy b/luci-app-haproxy-tcp/root/etc/config/haproxy deleted file mode 100644 index 5d324ca3f..000000000 --- a/luci-app-haproxy-tcp/root/etc/config/haproxy +++ /dev/null @@ -1,29 +0,0 @@ - -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/luci-app-haproxy-tcp/root/etc/haproxy_init.sh b/luci-app-haproxy-tcp/root/etc/haproxy_init.sh deleted file mode 100755 index 9f969ee59..000000000 --- a/luci-app-haproxy-tcp/root/etc/haproxy_init.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/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 resolvers mydns 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 resolvers mydns 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 -} diff --git a/luci-app-haproxy-tcp/root/etc/haproxy_start b/luci-app-haproxy-tcp/root/etc/haproxy_start deleted file mode 100755 index f0c9e52b1..000000000 --- a/luci-app-haproxy-tcp/root/etc/haproxy_start +++ /dev/null @@ -1,73 +0,0 @@ -#!/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/luci-app-haproxy-tcp/root/etc/uci-defaults/99_luci-haproxy b/luci-app-haproxy-tcp/root/etc/uci-defaults/99_luci-haproxy deleted file mode 100755 index 72913f935..000000000 --- a/luci-app-haproxy-tcp/root/etc/uci-defaults/99_luci-haproxy +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -/etc/init.d/haproxy disable -/etc/init.d/haproxy stop - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-haproxy-tcp/root/usr/share/rpcd/acl.d/luci-app-haproxy-tcp.json b/luci-app-haproxy-tcp/root/usr/share/rpcd/acl.d/luci-app-haproxy-tcp.json deleted file mode 100644 index 6041d9e58..000000000 --- a/luci-app-haproxy-tcp/root/usr/share/rpcd/acl.d/luci-app-haproxy-tcp.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-haproxy-tcp": { - "description": "Grant UCI access for luci-app-haproxy-tcp", - "read": { - "uci": [ "haproxy" ] - }, - "write": { - "uci": [ "haproxy" ] - } - } -} diff --git a/luci-app-ltqtapi/Makefile b/luci-app-ltqtapi/Makefile deleted file mode 100644 index 88392341b..000000000 --- a/luci-app-ltqtapi/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# 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 Support for Lantiq Devices -LUCI_DEPENDS:=@BROKEN - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-ltqtapi/luasrc/controller/ltqtapi.lua b/luci-app-ltqtapi/luasrc/controller/ltqtapi.lua deleted file mode 100644 index 46c768401..000000000 --- a/luci-app-ltqtapi/luasrc/controller/ltqtapi.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Copyright 2019 John Crispin --- Licensed to the public under the Apache License 2.0. - -module("luci.controller.ltqtapi", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/telephony") then - return - end - - page = node("admin", "telephony") - page.target = firstchild() - page.title = _("VoIP") - page.order = 90 - - entry({"admin", "telephony", "account"}, cbi("luci_ltqtapi/account") , _("Account"), 10) - entry({"admin", "telephony", "contact"}, cbi("luci_ltqtapi/contact") , _("Contacts"), 20) - - entry({"admin", "telephony", "status"}, call("tapi_status")).leaf = true -end - -function tapi_status() - local st = { } - local state = require "luci.model.uci".cursor_state() - state:load("telephony") - - st.status = "Offline"; - if state:get("telephony", "state", "port1", "0") == "0" then - st.line1 = "Idle"; - else - st.line1 = "Calling"; - end - if state:get("telephony", "state", "port2", "0") == "0" then - st.line2 = "Idle"; - else - st.line2 = "Calling"; - end - luci.http.prepare_content("application/json") - luci.http.write_json(st) -end diff --git a/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/account.lua b/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/account.lua deleted file mode 100644 index 907db56e8..000000000 --- a/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/account.lua +++ /dev/null @@ -1,16 +0,0 @@ --- Copyright 2010 John Crispin --- Licensed to the public under the Apache License 2.0. - -m = Map("telephony", translate("VoIP")) -m.on_after_commit = function() luci.sys.call("/etc/init.d/telephony restart") end - -s = m:section(TypedSection, "account", translate("Account"), translate("Here You can specify the SIP account that you want to use.")) -s.anonymous = true -s.addremove = true - -s:option(Value, "realm", translate("Realm")) -s:option(Value, "username", translate("Username")) -s:option(Value, "password", translate("Password")) -s:option(Flag, "disabled", translate("Disabled")) - -return m diff --git a/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/contact.lua b/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/contact.lua deleted file mode 100644 index b32feded7..000000000 --- a/luci-app-ltqtapi/luasrc/model/cbi/luci_ltqtapi/contact.lua +++ /dev/null @@ -1,20 +0,0 @@ --- Copyright 2010 John Crispin --- Licensed to the public under the Apache License 2.0. - -m = Map("telephony", translate("VoIP")) -m.on_after_commit = function() luci.sys.call("/etc/init.d/telephony reload") end - -s = m:section(TypedSection, "contact", translate("Contact"), translate("Here You can specify the SIP contacts that you want to use.")) -s.anonymous = true -s.addremove = true -s.template = "cbi/tsection" - -s:option(Value, "desc", translate("Name")) -s:option(Value, "code", translate("Shortdial")) -s:option(Value, "dial", translate("Dial")) - -t = s:option(ListValue, "type", translate("Type")) -t:value("sip", "SIP") -t:value("realm", "Landline") - -return m diff --git a/luci-app-ltqtapi/luasrc/view/admin_status/index/telephony.htm b/luci-app-ltqtapi/luasrc/view/admin_status/index/telephony.htm deleted file mode 100644 index c7a58dd3e..000000000 --- a/luci-app-ltqtapi/luasrc/view/admin_status/index/telephony.htm +++ /dev/null @@ -1 +0,0 @@ -<%+telephony_status%> diff --git a/luci-app-ltqtapi/luasrc/view/telephony_status.htm b/luci-app-ltqtapi/luasrc/view/telephony_status.htm deleted file mode 100644 index d861ef9fc..000000000 --- a/luci-app-ltqtapi/luasrc/view/telephony_status.htm +++ /dev/null @@ -1,32 +0,0 @@ - - -
- <%:Current Telephony State%> - - - - - - - - - -
<%:Uplink%><%:Port1%><%:Port2%>

<%:Collecting data...%>
-
diff --git a/luci-app-mwan3helper/Makefile b/luci-app-mwan3helper/Makefile deleted file mode 100644 index 710f0713e..000000000 --- a/luci-app-mwan3helper/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# 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_NAME:=luci-app-mwan3helper -PKG_VERSION:=1 -PKG_RELEASE:=2 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua b/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua deleted file mode 100644 index ad9fffabc..000000000 --- a/luci-app-mwan3helper/luasrc/controller/mwan3helper.lua +++ /dev/null @@ -1,21 +0,0 @@ --- 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/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua b/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua deleted file mode 100644 index 876ab9159..000000000 --- a/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/client.lua +++ /dev/null @@ -1,28 +0,0 @@ -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.description = translate("启用 DNS 防污染并打开GFW IPSet(与SSR的GFW列表模式不能同时开启)") -o.rmempty = false - -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/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua b/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua deleted file mode 100644 index 2f845aed6..000000000 --- a/luci-app-mwan3helper/luasrc/model/cbi/mwan3helper/list.lua +++ /dev/null @@ -1,35 +0,0 @@ -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/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm b/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm deleted file mode 100644 index 048a8814f..000000000 --- a/luci-app-mwan3helper/luasrc/view/mwan3helper/mwan3helper_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-mwan3helper/po/zh-cn/mwan3helper.po b/luci-app-mwan3helper/po/zh-cn/mwan3helper.po deleted file mode 100644 index ff4180036..000000000 --- a/luci-app-mwan3helper/po/zh-cn/mwan3helper.po +++ /dev/null @@ -1,24 +0,0 @@ -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/luci-app-mwan3helper/po/zh_Hans b/luci-app-mwan3helper/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-mwan3helper/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-mwan3helper/root/etc/config/mwan3helper b/luci-app-mwan3helper/root/etc/config/mwan3helper deleted file mode 100644 index f47fff8c1..000000000 --- a/luci-app-mwan3helper/root/etc/config/mwan3helper +++ /dev/null @@ -1,5 +0,0 @@ - -config mwan3helper 'config' - option dns '208.67.222.222:443' - option enabled '0' - diff --git a/luci-app-mwan3helper/root/etc/init.d/mwan3helper b/luci-app-mwan3helper/root/etc/init.d/mwan3helper deleted file mode 100755 index dd71017b8..000000000 --- a/luci-app-mwan3helper/root/etc/init.d/mwan3helper +++ /dev/null @@ -1,166 +0,0 @@ -#!/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/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt b/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt deleted file mode 100644 index e4b3da944..000000000 --- a/luci-app-mwan3helper/root/etc/mwan3helper/gfw.txt +++ /dev/null @@ -1,5987 +0,0 @@ -000webhost.com -030buy.com -0rz.tw -1000giri.net -100ke.org -10beasts.net -10conditionsoflove.com -10musume.com -10.tt -123rf.com -12bet.com -12vpn.com -12vpn.net -1337x.to -138.com -141hongkong.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 -1-apple.com.tw -1bao.org -1dumb.com -1e100.net -1eew.com -1mobile.com -1mobile.tw -1pondo.tv -2000fun.com -2008xianzhang.info -2017.hk -2047.name -21andy.com -21join.com -21pron.com -21sextury.com -228.net.tw -233abc.com -24hrs.ca -24smile.org -25u.com -2-hand.info -2lipstube.com -2shared.com -2waky.com -30boxes.com -315lz.com -32red.com -36rain.com -3a5a.com -3-a.net -3arabtv.com -3boys2girls.com -3d-game.com -3proxy.ru -3ren.ca -3tui.net -404museum.com -43110.cf -466453.com -4bluestones.biz -4chan.com -4dq.com -4everproxy.com -4irc.com -4mydomain.com -4pu.com -4rbtv.com -4shared.com -4sqi.net -51.ca -51jav.org -51luoben.com -5278.cc -5299.tv -56cun04.jigsy.com -5aimiku.com -5i01.com -5isotoi5.org -5maodang.com -63i.com -64museum.org -64tianwang.com -64wiki.com -666kb.com -66.ca -6do.news -6parkbbs.com -6park.com -6parker.com -6parknews.com -7capture.com -7cow.com -85cc.net -85cc.us -85st.com -881903.com -888.com -888poker.com -89.64.charter.constitutionalism.solutions -89-64.org -8-d.com -8news.com.tw -8z1.net -9001700.com -908taiwan.org -91porn.com -91vps.club -92ccav.com -991.com -99btgc01.com -99cn.info -9bis.com -9bis.net -9gag.com -9news.com.au -a248.e.akamai.net -a5.com.ru -aamacau.com -abc.com -abchinese.com -abclite.net -abc.net.au -abc.pp.ru -abc.xyz -abebooks.com -abematv.akamaized.net -abitno.linpie.com -ablwang.com -aboluowang.com -aboutgfw.com -about.google -abs.edu -accim.org -accountkit.com -aceros-de-hispania.com -acevpn.com -acg18.me -acgbox.org -acgkj.com -acgnx.se -ac.jiruan.net -acmedia365.com -acmetoy.com -acnw.com.au -actfortibet.org -actimes.com.au -activpn.com -aculo.us -adcex.com -addictedtocoffee.de -addyoutube.com -adelaidebbs.com -admin.recaptcha.net -admob.com -adpl.org.hk -adsense.com -ads-twitter.com -adult.friendfinder.com -adultfriendfinder.com -adultkeep.net -adult-sex-games.com -advanscene.com -advertfan.com -advertisercommunity.com -ae.hao123.com -aenhancers.com -ae.org -aex.com -afantibbs.com -af.mil -afr.com -afreecatv.com -agnesb.fr -agoogleaday.com -agro.hk -ai.binwang.me -ai.google -ai-kan.net -aiph.net -airasia.com -airconsole.com -airvpn.org -aisex.com -aiss.anws.gov.tw -ait.org.tw -aiweiweiblog.com -aiweiwei.com -ai-wen.net -akademiye.org -akamaihd.net -akiba-online.com -akiba-web.com -akow.org -alabout.com -alanhou.com -alarab.qa -alasbarricadas.org -alexlur.org -alforattv.net -alhayat.com -alicejapan.co.jp -aliengu.com -al-islam.com -alive.bar -alkasir.com -all4mom.org -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 -al-qimmah.net -alternate-tools.com -alternativeto.net -altrec.com -alvinalexander.com -alwaysdata.com -alwaysdata.net -alwaysvpn.com -am730.com.hk -amazon.co.jp -amazon.com -ameblo.jp -americangreencard.com -americanunfinished.com -amiblockedornot.com -amigobbs.net -amitabhafoundation.us -amnesty.org -amnesty.org.hk -amnesty.tw -amnestyusa.org -amnyemachen.org -amoiist.com -ampproject.org -amtb-taipei.org -anchor.fm -anchorfree.com -ancsconf.org -andfaraway.net -android.com -androidify.com -androidplus.co -androidtv.com -android-x86.org -andygod.com -angela-merkel.de -angelfire.com -angola.org -angularjs.org -animecrazy.net -aniscartujo.com -annatam.com -anobii.com -anontext.com -anonymise.us -anonymitynetwork.com -anonymizer.com -anonymouse.org -a-normal-day.com -anpopo.com -answering-islam.org -anthonycalzadilla.com -anti1984.com -antichristendom.com -antiwave.net -anyporn.com -anysex.com -ao3.org -aobo.com.au -aofriend.com -aofriend.com.au -aojiao.org -aolchannels.aol.com -aomiwang.com -apartmentratings.com -apartments.com -apat1989.org -apetube.com -api.ai -apiary.io -apidocs.linksalpha.com -api.dropboxapi.com -apigee.com -api.linksalpha.com -api.proxlet.com -api.pureapk.com -api.recaptcha.net -api-secure.recaptcha.net -api-verify.recaptcha.net -apkcombo.com -apk-dl.com -apkmirror.com -apkmonk.com -apkplz.com -apkpure.com -apk.support -aplusvpn.com -app.box.com -appbrain.com -app.cloudcone.com -appdownloader.net -app.evozi.com -app.heywire.com -appledaily.com -appledaily.com.hk -appledaily.com.tw -appshopper.com -app.smartmailcloud.com -appsocks.net -appspot.com -appsto.re -app.tutanota.com -aptoide.com -archive.fo -archive.is -archive.li -archiveofourown.com -archiveofourown.org -archive.org -archive.ph -archives.gov -archives.gov.tw -archive.today -arctosia.com -areca-backup.org -arena.taipei -arethusa.su -ar.hao123.com -arlingtoncemetery.mil -army.mil -art4tibet1998.org -arte.tv -artofpeacefoundation.org -artstation.com -artsy.net -asacp.org -asdfg.jp -asg.to -asia-gaming.com -asiaharvest.org -asianage.com -asianews.it -asiansexdiary.com -asianspiss.com -asianwomensfilm.de -asiatgp.com -asiatoday.us -askstudent.com -askynz.net -aspi.org.au -aspistrategist.org.au -assembla.com -assets.bwbx.io -assimp.org -astrill.com -atchinese.com -atc.org.au -atdmt.com -atgfw.org -athenaeizou.com -atlanta168.com -atlaspost.com -atnext.com -auntology.fandom.com -authorizeddns.net -authorizeddns.org -authorizeddns.us -autodraw.com -avaaz.org -avbody.tv -avcity.tv -av.com -avcool.com -avdb.in -avdb.tv -av-e-body.com -avfantasy.com -avg.com -avgle.com -avidemux.org -avmoo.com -avmoo.net -avmoo.pw -avmo.pw -av.movie -av.nightlife141.com -avoision.com -avyahoo.com -axios.com -axureformac.com -azerbaycan.tv -azerimix.com -azubu.tv -azurewebsites.net -b0ne.com -babylonbee.com -babynet.com.hk -backchina.com -backpackers.com.tw -backtotiananmen.com -badiucao.com -badjojo.com -badoo.com -bahamut.com.tw -baidu.jp -baijie.org -bailandaily.com -baixing.me -bakgeekhome.tk -banana-vpn.com -bandcamp.com -band.us -bandwagonhost.com -bangbrosnetwork.com -bangchen.net -bangdream.space -bangkokpost.com -bangyoulater.com -bankmobilevibe.com -bannedbook.org -bannednews.org -banorte.com -baramangaonline.com -barenakedislam.com -barnabu.co.uk -bartender.dowjones.com -barton.de -bastillepost.com -bayvoice.net -bbcchinese.com -bbc.com -bbc.co.uk -bb-chat.tv -bbchat.tv -bbci.co.uk -bbc.in -bbg.gov -bbkz.com -bbnradio.org -bbs.brockbbs.com -bbs.cantonese.asia -bbsdigest.com -bbs.ecstart.com -bbsfeed.com -bbs.hanminzu.org -bbs.hasi.wang -bbs.huasing.org -bbs.junglobal.net -bbs.kimy.com.tw -bbsland.com -bbs.mikocon.com -bbsmo.com -bbs.morbell.com -bbs.mychat.to -bbs.netbig.com -bbsone.com -bbs.ozchinese.com -bbs.qmzdd.com -bbs.sina.com -bbs.skykiwi.com -bbs.sou-tong.org -bbs.tuitui.info -bbs-tw.com -bbtoystore.com -bb.ttv.com.tw -bcast.co.nz -bcc.com.tw -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 -berlinerbericht.de -berlintwitterwall.com -berm.co.nz -bestforchina.org -bestgore.com -bestpornstardb.com -bestvpnanalysis.com -bestvpn.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 -biedian.me -bigfools.com -bigjapanesesex.com -bigmoney.biz -bignews.org -big.one -bigone.com -bigsound.org -bild.de -biliworld.com -billypan.com -binance.com -binux.me -bird.so -bitbay.net -bitc.bme.emory.edu -bitcointalk.org -bitcoinworld.com -bit.do -bitfinex.com -bithumb.com -bitinka.com.ar -bit.ly -bitmex.com -bitshare.com -bitsnoop.com -bitterwinter.org -bitvise.com -bitz.ai -bit-z.com -bizhat.com -bjnewlife.org -bjs.org -bjzc.org -blacklogic.com -blackvpn.com -bl-doujinsouko.com -blewpass.com -blinkx.com -blinw.com -blip.tv -blockcn.com -blockless.com -blogblog.com -blog.calibre-ebook.com -blogcatalog.com -blogcity.me -blog.cnyes.com -blog.cryptographyengineering.com -blog.de -blogdns.org -blog.exblog.co.jp -blog.excite.co.jp -blog.expofutures.com -blog.fizzik.com -blog.foolsmountain.com -blog.fuckgfw233.org -blogger.com -blog.google -blog.goo.ne.jp -blogimg.jp -blog.istef.info -blog.jackjia.com -blog.jp -blog.kangye.org -blog.lester850.info -bloglines.com -bloglovin.com -blog.martinoei.com -blog.pathtosharepoint.com -blog.pentalogic.net -blog.qooza.hk -blog.ranxiang.com -blogs.icerocket.com -blog.sina.com.tw -blogs.libraryinformationtechnology.com -blog.sogoo.org -blog.soylent.com -blogspot.ae -blogspot.al -blogspot.am -blogspot.ba -blogspot.be -blogspot.bg -blogspot.ca -blogspot.cat -blogspot.ch -blogspot.cl -blogspot.com -blogspot.com.ar -blogspot.com.au -blogspot.com.br -blogspot.com.by -blogspot.com.co -blogspot.com.cy -blogspot.com.ee -blogspot.com.eg -blogspot.com.es -blogspot.com.mt -blogspot.com.ng -blogspot.com.tr -blogspot.com.uy -blogspot.co.uk -blogspot.cz -blogspot.de -blogspot.dk -blogspot.fi -blogspot.fr -blogspot.gr -blogspot.hk -blogspot.hr -blogspot.hu -blogspot.ie -blogspot.in -blogspot.is -blogspot.it -blogspot.jp -blogspot.kr -blogspot.li -blogspot.lt -blogspot.lu -blogspot.md -blogspot.mk -blogspot.mx -blogspot.my -blogspot.nl -blogspot.no -blogspot.pe -blogspot.pt -blogspot.qa -blogspot.ro -blogspot.ru -blogspot.se -blogspot.sg -blogspot.si -blogspot.sk -blogspot.sn -blogspot.tw -blogspot.ug -blogs.tampabay.com -blogs.yahoo.co.jp -blog.syx86.cn -blog.syx86.com -blog.taragana.com -blogtd.net -blogtd.org -blog.tiney.com -blog.workflow.is -blog.xuite.net -blog.youthwant.com.tw -blog.youxu.info -bloodshed.net -bloomberg.cn -bloomberg.com -bloomberg.de -bloombergview.com -bloomfortune.com -blueangellive.com -bmfinn.com -bnews.co -bnn.co -bnrmetal.com -boardreader.com -bod.asia -bodog88.com -b-ok.cc -bolehvpn.net -bolin.netfirms.com -bonbonme.com -bonbonsex.com -bonfoundation.org -bongacams.com -boobstagram.com -book.com.tw -bookepub.com -books.com.tw -booktopia.com.au -book.zi5.me -boomssr.com -botanwang.com -bot.nu -bowenpress.com -boxpn.com -boxunblog.com -boxunclub.com -boxun.com -boxun.tv -boyangu.com -boyfriendtv.com -boysfood.com -boysmaster.com -brainyquote.com -brandonhutchinson.com -braumeister.org -brave.com -bravotube.net -brazzers.com -break.com -breakgfw.com -breaking911.com -breakingtweets.com -breakwall.net -br.hao123.com -briefdream.com -briian.com -brizzly.com -brkmd.com -broadbook.com -broadpressinc.com -brookings.edu -br.st -brucewang.net -brutaltgp.com -bt2mag.com -bt95.com -btaia.com -btbtav.com -btc98.com -btcbank.bank -btctrade.im -btdigg.org -btku.me -btku.org -btspread.com -btsynckeys.com -budaedu.org -buddhanet.com.tw -buddhistchannel.tv -buffered.com -bullguard.com -bullogger.com -bullog.org -bunbunhk.com -busayari.com -business-humanrights.org -businessinsider.com -businesstoday.com.tw -businessweek.com -busu.org -busytrade.com -buugaa.com -buy.yahoo.com.tw -buzzhand.com -buzzhand.net -buzzorange.com -bvpn.com -bwgyhw.com -bwh1.net -bwsj.hk -bx.in.th -bx.tl -bynet.co.il -bypasscensorship.org -c100tibet.org -c2cx.com -cablegatesearch.net -cachinese.com -cacnw.com -cactusvpn.com -cafepress.com -cahr.org.tw -caijinglengyan.com -calameo.com -calebelston.com -calgarychinese.ca -calgarychinese.com -calgarychinese.net -cam4.com -cam4.jp -cam4.sg -camfrog.com -campaignforuyghurs.org -cams.com -cams.org.sg -canadameet.com -canalporno.com -canyu.org -caobian.info -caochangqing.com -cao.im -cap.org.hk -carabinasypistolas.com -cardinalkungfoundation.org -carfax.com -caribbeancom.com -cari.com.my -carmotorshow.com -carrd.co -cartoonmovement.com -casadeltibetbcn.org -casatibet.org.mx -casinobellini.com -casinoking.com -casinoriva.com -casino.williamhill.com -castbox.fm -catch22.net -catchgod.com -catfightpayperview.xxx -catholic.org.hk -catholic.org.tw -cathvoice.org.tw -cato.org -cattt.com -cbc.ca -cbsnews.com -cbs.ntu.edu.tw -cbtc.org.hk -cccat.cc -cccat.co -ccdtr.org -cchere.com -ccim.org -cclife.ca -cclifefl.org -cclife.org -ccthere.com -ccthere.net -cctmweb.net -cctongbao.com -ccue.ca -ccue.com -ccvoice.ca -ccw.org.tw -cdbook.org -cdcparty.com -cdef.org -cdig.info -cdjp.org -cdn1.lp.saboom.com -cdn-apple.com -cdn.assets.lfpcontent.com -cdnews.com.tw -cdn.helixstudios.net -cdn-images.mailchimp.com -cdninstagram.com -cdn.printfriendly.com -cdn.seatguru.com -cdn.softlayer.net -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.revocationcheck.com -certificate-transparency.org -c-est-simple.com -cfhks.org.hk -cfos.de -cfr.org -cftfc.com -cgdepot.org -cgst.edu -changeip.name -changeip.net -changeip.org -change.org -changp.com -changsa.net -channelnewsasia.com -chaoex.com -chapm25.com -chatnook.com -chaturbate.com -checkgfw.com -chengmingmag.com -chenguangcheng.com -chenpokong.com -chenpokong.net -chenpokongvip.com -chenshan20042005.wordpress.com -cherrysave.com -chhongbi.org -chicagoncmtv.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 -china-mmm.jp.net -china-mmm.net -china-mmm.sa.com -chinamule.com -chinamz.org -chinanewscenter.com -chinapost.com.tw -chinapress.com.my -china-review.com.ua -chinarightsia.org -chinasmile.net -chinasocialdemocraticparty.com -chinasoul.org -chinasucks.net -chinatimes.com -chinatopsex.com -chinatown.com.au -chinatweeps.com -china.ucanews.com -chinaview.wordpress.com -chinaway.org -china-week.com -chinaworker.info -chinaxchina.com -chinayouth.org.hk -chinayuanmin.org -chinesedaily.com -chinesedailynews.com -chinesedemocracy.com -chinese.donga.com -chinese.engadget.com -chinesegay.org -chinese-hermit.net -chinese.irib.ir -chinese-leaders.org -chinese-memorial.org -chinesen.de -chinesenews.net.au -chinesepen.org -chineseradioseattle.com -chinese.soifind.com -chinesetalks.net -chineseupress.com -chingcheong.com -chinman.net -chithu.org -chobit.cc -chrdnet.com -christianfreedom.org -christianstudy.com -christiantimes.org.hk -christusrex.org -chrlawyers.hk -chromecast.com -chrome.com -chromeexperiments.com -chromercise.com -chromestatus.com -chromium.org -ch.shvoong.com -chuang-yen.org -chubold.com -chubun.com -churchinhongkong.org -chushigangdrug.ch -cienen.com -cineastentreff.de -cipfg.org -circlethebayfortibet.org -cirosantilli.com -citizencn.com -citizenlab.ca -citizenlab.org -citizenscommission.hk -citizensradio.org -city365.ca -city9x.com -citypopulation.de -citytalk.tw -civicparty.hk -civildisobediencemovement.org -civilhrfront.org -civiliangunner.com -civilmedia.tw -ck101.com -clarionproject.org -classicalguitarblog.net -clb.org.hk -cl.d0z.net -cldr.unicode.org -cleansite.biz -cleansite.info -cleansite.us -clearharmony.net -clearsurance.com -clearwisdom.net -clementine-player.org -cling.omy.sg -clinica-tibet.ru -clipfish.de -cloakpoint.com -cloud.mail.ru -club1069.com -clubhouseapi.com -clyp.it -cmcn.org -cmi.org.tw -cmp.hku.hk -cms.gov -cmule.com -cmule.org -cmx.im -cn2.streetvoice.com -cn6.eu -cnabc.com -cna.com.tw -cnbbnews.wordpress.com -cn.dayabook.com -cnd.org -cnex.org.cn -cn.fmnnow.com -cn.freeones.com -cn.giganews.com -cn.ibtimes.com -cnineu.com -cnn.com -cnnews.chosun.com -cn.nytstyle.com -cnpolitics.org -cn-proxy.com -cnproxy.com -cn.sandscotaicentral.com -cn.shafaqna.com -cn.streetvoice.com -cn.theaustralian.com.au -cn.uncyclopedia.wikia.com -cn.uptodown.com -cn.voa.mobi -coat.co.jp -cobinhood.com -cochina.co -cochina.org -code1984.com -codeshare.io -codeskulptor.org -coin2co.in -coinbene.com -coinegg.com -coinex.com -coingi.com -coinrail.co.kr -cointiger.com -cointobe.com -coinut.com -collateralmurder.com -collateralmurder.org -comefromchina.com -com.google -comic-mega.me -commandarms.com -commentshk.com -communistcrimes.org -communitychoicecu.com -community.windy.com -comparitech.com -compileheart.com -compress.to -co.ng.mil -connect.facebook.net -conoha.jp -contactmagazine.net -contests.twilio.com -convio.net -coobay.com -coolaler.com -coolder.com -coolloud.org.tw -coolncute.com -coolstuffinc.com -corumcollege.com -cosmic.monar.ch -cos-moe.com -cosplayjav.pl -costco.com -cotweet.com -counter.social -coursehero.com -cpj.org -cq99.us -crackle.com -crazys.cc -crazyshit.com -crbug.com -crchina.org -crd-net.org -creaders.net -creadersnet.com -creativelab5.com -crisisresponse.google -cristyli.com -crocotube.com -crossfire.co.kr -crossthewall.net -crossvpn.net -crrev.com -crucial.com -crunchyroll.com -csdparty.com -csis.org -csmonitor.com -c-span.org -c-spanvideo.org -css.pixnet.in -csuchen.de -csw.org.uk -ctao.org -ctfriend.net -ctitv.com.tw -ct.org.tw -ctowc.org -cts.com.tw -ctwant.com -cuhkacs.org -cuihua.org -cuiweiping.net -culture.tw -cumlouder.com -curvefish.com -cusp.hk -cusu.hk -cutscenes.net -cw.com.tw -cyberghost.natado.com -cyberghostvpn.com -cynscribe.com -cytode.us -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 -dailymail.co.uk -dailymotion.com -dailynews.sina.com -dailysabah.com -dailyview.tw -daiphapinfo.net -dajiyuan.com -dajiyuan.de -dajiyuan.eu -dajusha.baywords.com -dalailama80.org -dalailama-archives.org -dalailamacenter.org -dalailama.com -dalailamafellows.org -dalailamafilm.com -dalailamafoundation.org -dalailamahindi.com -dalailamainaustralia.org -dalailamajapanese.com -dalailama.mn -dalailamaprotesters.info -dalailamaquotes.org -dalailama.ru -dalailamatrust.org -dalailama.usc.edu -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.flurry.com -data.gov.tw -data-vocabulary.org -daum.net -david-kilgour.com -dawangidc.com -daxa.cn -daylife.com -dbc.hk -dbgjd.com -db.tt -dcard.tw -dcmilitary.com -ddc.com.tw -ddhw.info -ddns.info -ddns.me.uk -ddns.mobi -ddns.ms -ddns.name -ddns.net -ddns.us -deadline.com -deaftone.com -debug.com -deck.ly -decodet.co -deepmind.com -deezer.com -definebabe.com -deja.com -delcamp.net -delicious.com -democrats.org -demo.opera-mini.net -demosisto.hk -depositphotos.com -derekhsu.homeip.net -de-sci.org -desc.se -design.google -desipro.de -dessci.com -destiny.xfiles.to -destroy-china.jp -deutsche-welle.de -developers.box.net -deviantart.com -deviantart.net -devio.us -devpn.com -dfas.mil -dfn.org -d-fukyu.com -dharamsalanet.com -dharmakara.net -dhcp.biz -diaoyuislands.org -dictionary.goo.ne.jp -difangwenge.org -digiland.tw -digisfera.com -digitalnomadsproject.org -diigo.com -dilber.se -dingchin.com.tw -dipity.com -directcreative.com -discoins.com -disconnect.me -discordapp.com -discordapp.net -discord.com -discord.gg -discuss4u.com -discuss.com.hk -dish.com -disk.yandex.com -disp.cc -disqus.com -dit-inc.us -dizhidizhi.com -dizhuzhishang.com -djangosnippets.org -djorz.com -dl.box.net -dlive.tv -dl-laby.jp -dlsite.com -dlyoutube.com -dm530.net -dmcdn.net -dmhy.org -dmm.co.jp -dns04.com -dns05.com -dns1.us -dns2go.com -dns2.us -dnscrypt.org -dns-dns.com -dnset.com -dns.google -dnsrd.com -dnssec.net -dns-stuff.com -dnvod.tv -doctorvoice.org -documentingreality.com -dogfartnetwork.com -dojin.com -dok-forum.net -dolc.de -dolf.org.hk -dollf.com -domain.club.tw -domains.google -domaintoday.com.au -dongtaiwang.com -dongtaiwang.net -dongyangjing.com -dontfilter.us -dontmovetochina.com -dorjeshugden.com -dotplane.com -dotsub.com -dotvpn.com -doubibackup.com -doub.io -doubmirror.cf -dougscripts.com -douhokanko.net -doujincafe.com -dowei.org -download.aircrack-ng.org -download.cnet.com -download.ithome.com.tw -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 -dubox.com -duck.com -duckdns.org -duckduckgo.com -duckduckgo-owned-server.yahoo.net -duckload.com -duckmylife.com -duga.jp -duihuahrjournal.org -duihua.org -dumb1.com -dunyabulteni.net -duoweitimes.com -duping.net -duplicati.com -dupola.com -dupola.net -dushi.ca -duyaoss.com -dvdpac.com -dvorak.org -dw.com -dw.de -dwnews.com -dwnews.net -dw-world.com -dw-world.de -dynamicdns.biz -dynamicdns.co.uk -dynamicdns.me.uk -dynamic-dns.net -dynamicdns.org.uk -dynawebinc.com -dyndns-ip.com -dyndns.org -dyndns-pics.com -dyndns.pro -dynssl.com -dynu.com -dynu.net -dynupdate.no-ip.com -dysfz.cc -dzze.com -e123.hk -earlytibet.com -earthcam.com -earthvpn.com -eastern-ark.com -easternlightning.org -eastturkestan.com -eastturkistancc.org -eastturkistangovernmentinexile.us -eastturkistan-gov.org -easyca.ca -easypic.com -ebony-beauty.com -ebookbrowse.com -ebookee.com -ebook.hyread.com.tw -ebtcbank.com -ecfa.org.tw -echofon.com -ecimg.tw -e-classical.com.tw -ecministry.net -economist.com -ecsm.vs.com -edgecastcdn.net -edicypages.com -edmontonchina.cn -edmontonservice.com -edns.biz -edoors.com -edubridge.com -edupro.org -eeas.europa.eu -eesti.ee -eevpn.com -efcc.org.hk -effers.com -efksoft.com -efukt.com -e-gold.com -e-hentaidb.com -e-hentai.org -eic-av.com -e-info.org.tw -eireinikotaerukai.com -eisbb.com -eksisozluk.com -electionsmeter.com -elgoog.im -ellawine.org -elpais.com -eltondisney.com -emaga.com -emanna.com -embr.in -emilylau.org.hk -empfil.com -emule-ed2k.com -emulefans.com -emuparadise.me -enanyang.my -encrypt.me -encyclopedia.com -enewstree.com -enfal.de -en.favotter.net -engagedaily.org -englishforeveryone.org -englishfromengland.co.uk -englishpen.org -en.hao123.com -enlighten.org.tw -entermap.com -environment.google -epac.to -epa.gov.tw -episcopalchurch.org -epochhk.com -epochtimes-bg.com -epochtimes.co.il -epochtimes.co.kr -epochtimes.com -epochtimes.cz -epochtimes.de -epochtimes.fr -epochtimes.ie -epochtimes.it -epochtimes.jp -epochtimes-romania.com -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 -eromangadouzin.com -eromanga-kingdom.com -eromon.net -eroprofile.com -eroticsaloon.net -esg.t91y.com -eslite.com -esmtp.biz -esu.dog -esurance.com -etaa.org.au -etadult.com -etaiwannews.com -etherdelta.com -etherscan.io -etizer.org -etokki.com -etools.ncol.com -etowns.net -etowns.org -e-traderland.net -etsy.com -ettoday.net -etvonline.hk -eucasino.com -eulam.com -eu.org -eurekavpt.com -euronews.com -evchk.wikia.com -evschool.net -exblog.jp -exchristian.hk -exhentai.org -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 -e-zone.com.hk -ezpc.tk -ezpeer.com -ezua.com -f8.com -facebook.br -facebook.com -facebook.design -facebook.hu -facebook.in -facebookmail.com -facebook.nl -facebookquotes4u.com -facebook.se -faceless.me -facesofnyfw.com -facesoftibetanselfimmolators.info -factpedia.org -fa.gov.tw -fail.hk -faith100.org -faithfuleye.com -faiththedog.info -fakku.net -fallenark.com -falsefire.com -falunart.org -falunasia.info -falunau.org -falunaz.net -falun.caltech.edu -falun-co.org -falundafa-dc.org -falundafa-florida.org -falundafaindia.org -falundafamuseum.org -falundafa-nc.org -falundafa.org -falundafa-pa.net -falundafa-sacramento.org -falungong.club -falungong.de -falungong.org.uk -falunhr.org -faluninfo.de -faluninfo.net -falun-ny.net -falunpilipinas.net -falunworld.net -familyfed.org -famunion.com -fangbinxing.com -fangeming.com -fangeqiang.com -fanglizhi.info -fangmincn.org -fangong.forums-free.com -fangongheike.com -fangong.org -fanhaodang.com -fan-qiang.com -fanqiangdang.com -fanqianghou.com -fanqiang.network -fanqiang.tk -fanqiangyakexi.net -fanqiangzhe.com -fanswong.com -fantv.hk -fanyue.info -fapdu.com -faproxy.com -faqserv.com -fartit.com -farwestchina.com -fastestvpn.com -fastpic.ru -fastssh.com -faststone.org -fast.wistia.com -fatbtc.com -favstar.fm -fawanghuihui.org -faydao.com -faz.net -fbaddins.com -fbcdn.net -fb.com -fb.me -fbsbx.com -fb.watch -fbworkmail.com -fc2blog.net -fc2china.com -fc2cn.com -fc2.com -fda.gov.tw -fdc64.de -fdc64.org -fdc89.jp -feedburner.com -feedly.com -feeds.fileforum.com -feedx.net -feelssh.com -feer.com -feifeiss.com -feitianacademy.org -feitian-california.org -feministteacher.com -fengzhenghu.com -fengzhenghu.net -fevernet.com -fffff.at -ff.im -fflick.com -ffvpn.com -fgmtv.net -fgmtv.org -fhreports.net -fiddle.jshell.net -figprayer.com -fileflyer.com -files2me.com -fileserve.com -filesor.com -fillthesquare.org -filmingfortibet.org -filmy.olabloga.pl -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 -firstpost.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 -fnc.ebc.net.tw -fochk.org -focustaiwan.tw -focusvpn.com -fofg-europe.net -fofg.org -fofldfradio.org -fooooo.com -foreignaffairs.com -foreignpolicy.com -forum4hk.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 -forum.xinbao.de -fotile.me -fourface.nodesnoop.com -fourthinternational.org -foxdie.us -foxgay.com -foxsub.com -foxtang.com -fpmtmexico.org -fpmt.org -fpmt-osel.org -fpmt.tw -fqok.org -fqrouter.com -fq.wikia.com -franklc.com -freakshare.com -free4u.com.ar -freealim.com -freebeacon.com -freebrowser.org -freechal.com -freechinaforum.org -freechina.net -freechina.news -freechinaweibo.com -freeddns.com -freeddns.org -freedomchina.info -freedomcollection.org -freedomhouse.org -freedominfonetweb.wordpress.com -freedomsherald.org -freeforums.org -freefq.com -free.fr -freefuckvids.com -freegao.com -free-gate.org -free-hada-now.org -freehongkong.org -freeilhamtohti.org -freekazakhs.org -freekwonpyong.org -freelotto.com -freeman2.com -freemoren.com -freemorenews.com -freemuse.org -freenet-china.org -freenetproject.org -freenewscn.com -freeopenvpn.com -freeoz.org -free-proxy.cz -free-ssh.com -freessh.us -free-ss.site -freetcp.com -freetibetanheroes.org -freetibet.net -freetibet.org -freetribe.me -freeviewmovies.com -freevpn.me -freevpn.nl -freewallpaper4.me -freewebs.com -freewechat.com -freeweibo.com -freewww.biz -freewww.info -freexinwen.com -freeyellow.com -freeyoutubeproxy.net -friendfeed.com -friendfeed-media.com -friends-of-tibet.org -friendsoftibet.org -fring.com -fringenetwork.com -fromchinatousa.net -frommel.net -from-pr.com -from-sd.com -frontlinedefenders.org -frootvpn.com -fscked.org -fsurf.com -ftchinese.com -ftp1.biz -ftpserver.biz -ftv.com.tw -ftvnews.com.tw -fucd.com -fuckcnnic.net -fuckgfw.org -fulione.com -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 -fuyindiantai.org -fuyin.net -fuyu.org.tw -fw.cm -fxcm-chinese.com -fxnetworks.com -fzh999.com -fzh999.net -fzlm.com -g0v.social -g6hentai.com -gab.com -gabocorp.com -gaeproxy.com -gaforum.org -gagaoolala.com -galaxymacau.com -galenwu.com -galstars.net -game735.com -gamebase.com.tw -gamejolt.com -gamer2-cds.cdn.hinet.net -gamer-cds.cdn.hinet.net -gamer.com.tw -gamez.com.tw -gamousa.com -ganges.com -gaoming.net -gaopi.net -gaozhisheng.net -gaozhisheng.org -gardennetworks.com -gardennetworks.org -g-area.org -gartlive.com -gatecoin.com -gate.io -gate-project.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 -g.co -gcpnews.com -gcr.io -gdbt.net -gdzf.org -geek-art.net -geekerhome.com -geekheart.info -gekikame.com -gelbooru.com -genius.com -geocities.co.jp -geocities.com -geocities.jp -geph.io -gerefoundation.org -get.app -getastrill.com -getchu.com -getcloak.com -get.dev -getfoxyproxy.org -getfreedur.com -getgom.com -get.how -geti2p.net -getiton.com -getjetso.com -getlantern.org -getmalus.com -getmdl.io -getoutline.org -get.page -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 -ghidra-sre.org -ghostpath.com -ghut.org -giantessnight.com -gifree.com -giga-web.jp -gigporno.ru -girlbanker.com -gist.github.com -githubassets.com -github.blog -github.com -github.io -githubusercontent.com -git.io -gizlen.net -gjczz.com -glass8.eu -global.bing.com -globaljihad.net -globalmediaoutreach.com -globalmuseumoncommunism.org -globalrescue.net -globaltm.org -globalvoicesonline.org -globalvoices.org -globalvpn.net -glock.com -gloryhole.com -glorystar.me -gluckman.com -glype.com -gmail.com -gmbd.cn -gmhz.org -gmll.org -gmodules.com -gmozomg.izihost.org -gnci.org.hk -gnews.org -go141.com -goagent.biz -goagent.codeplex.com -goagentplus.com -gobet.cc -godfootsteps.org -godns.work -godoc.org -godsdirectcontact.co.uk -godsdirectcontact.org -godsdirectcontact.org.tw -godsimmediatecontact.com -gofundme.com -gogotunnel.com -gohappy.com.tw -gojet.krtco.com.tw -gokbayrak.com -golang.org -goldbet.com -goldbetsports.com -golden-ages.org -goldeneyevault.com -goldenfrog.com -goldjizz.com -goldstep.net -goldwave.com -go.nesnode.com -gongmeng.info -gongm.in -gongminliliang.com -gongwt.com -gooday.xyz -gooddns.info -goodreaders.com -goodreads.com -goodtv.com.tw -goodtv.tv -goofind.com -goo.gl -google.ad -google.ae -google.al -google.am -googleapis.cn -googleapis.com -googleapps.com -googlearth.com -googleartproject.com -google.as -google.at -google.az -google.ba -google.be -google.bf -google.bg -google.bi -google.bj -googleblog.com -googlebot.com -google.bs -google.bt -google.by -google.ca -google.cat -google.cd -google.cf -google.cg -google.ch -googlechinawebmaster.com -google.ci -google.cl -google.cm -google.cn -google.co.ao -google.co.bw -google.co.ck -google.co.cr -googlecode.com -google.co.id -google.co.il -google.co.in -google.co.jp -google.co.ke -google.co.kr -google.co.ls -google.com -google.co.ma -google.com.af -google.com.ag -google.com.ai -google.com.ar -google.com.au -google.com.bd -google.com.bh -google.com.bn -google.com.bo -google.com.br -google.com.bz -google.com.co -google.com.cu -google.com.cy -google.com.do -google.com.ec -google.com.eg -google.com.et -google.com.fj -google.com.gh -google.com.gi -google.com.gt -google.com.hk -google.com.jm -google.com.kh -google.com.kw -google.com.lb -google.com.ly -googlecommerce.com -google.com.mm -google.com.mt -google.com.mx -google.com.my -google.com.na -google.com.nf -google.com.ng -google.com.ni -google.com.np -google.com.om -google.com.pa -google.com.pe -google.com.pg -google.com.ph -google.com.pk -google.com.pr -google.com.py -google.com.qa -google.com.sa -google.com.sb -google.com.sg -google.com.sl -google.com.sv -google.com.tj -google.com.tr -google.com.tw -google.com.ua -google.com.uy -google.com.vc -google.com.vn -google.co.mz -google.co.nz -google.co.th -google.co.tz -google.co.ug -google.co.uk -google.co.uz -google.co.ve -google.co.vi -google.co.za -google.co.zm -google.co.zw -google.cv -google.cz -google.de -google.dev -google.dj -google.dk -google.dm -googledomains.com -googledrive.com -google.dz -googleearth.com -google.ee -google.es -google.fi -googlefiber.net -google.fm -google.fr -google.ga -google.ge -google.gg -google.gl -google.gm -google.gp -google.gr -googlegroups.com -google.gy -google.hn -googlehosted.com -google.hr -google.ht -google.hu -googleideas.com -google.ie -google.im -googleinsidesearch.com -google.iq -google.is -google.it -google.je -google.jo -google.kg -google.ki -google.kz -google.la -googlelabs.com -google.li -google.lk -google.lt -google.lu -google.lv -googlemail.com -googlemashups.com -google.md -google.me -google.mg -google.mk -google.ml -google.mn -google.ms -google.mu -google.mv -google.mw -google.ne -google.nl -google.no -google.nr -google.nu -googlepagecreator.com -google.pl -googleplay.com -googleplus.com -google.pn -google.ps -google.pt -google.ro -google.rs -google.ru -google.rw -google.sc -googlescholar.comUSA -google.se -google.sh -google.si -googlesile.com -google.sk -google.sm -google.sn -google.so -googlesource.com -google.sr -google.st -google.td -google.tg -google.tk -google.tl -google.tm -google.tn -google.to -google.tt -googleusercontent.com -google.vg -googlevideo.com -google.vu -googleweblight.com -google.ws -googlezip.net -gopetition.com -go-pki.com -goproxing.net -goreforum.com -goregrish.com -gospelherald.com -gotdns.ch -got-game.org -gotgeeks.com -gotrusted.com -gotw.ca -gov.taipei -gov.tw -g-queen.com -gr8domain.biz -gr8name.biz -grammaly.com -grandtrial.org -grangorz.org -graphis.ne.jp -graphql.org -greasespot.net -greatfire.org -greatfire.us7.list-manage.com -greatfirewall.biz -great-firewall.com -greatfirewallofchina.net -greatfirewallofchina.org -great-roc.org -greatroc.org -greatroc.tw -greatzhonghua.org -greenfieldbookstore.com.hk -greenparty.org.tw -greenpeace.com.tw -greenpeace.org -greenreadings.com -greenvpn.net -greenvpn.org -grotty-monday.com -groups.google.cn -grow.google -gs-discuss.com -gsearch.media -gsp.target.com -gstatic.com -gtricks.com -gts-vpn.com -gtv1.org -gtv.org -guaguass.com -guaguass.org -guancha.org -guaneryu.com -guangming.com.my -guangnianvpn.com -guardster.com -gu-chu-sum.org -guishan.org -gumroad.com -gunsamerica.com -gunsandammo.com -gun-world.net -guo.media -guruonline.hk -gutteruncensored.com -gvlib.com -gvm.com.tw -gvt0.com -gvt1.com -gvt3.com -gwins.org -gwtproject.org -gyalwarinpoche.com -gyatsostudio.com -gzm.tv -gzone-anime.info -h1n1china.org -h528.com -h5dm.com -h5galgame.me -hacg.club -hacg.in -hacg.li -hacg.me -hacg.red -hacken.cc -hacker.org -hackmd.io -hackthatphone.net -hahlo.com -hakkatv.org.tw -handcraftedsoftware.org -hanime.tv -hanunyi.com -hao.news -happy-vpn.com -haproxy.org -hardsextube.com -harunyahya.com -hautelookcdn.com -hautelook.com -have8.com -hbg.com -hbo.com -h-china.org -hclips.com -hdlt.me -hd.stheadline.com -hdtvb.net -hdzog.com -heartyit.com -heavy-r.com -hecaitou.net -hechaji.com -hec.su -heeact.edu.tw -hegre-art.com -heix.pp.ru -helloandroid.com -helloqueer.com -helloss.pw -hellotxt.com -hellouk.org -helpeachpeople.com -helplinfen.com -help.linksalpha.com -helpster.de -helpuyghursnow.org -helpzhuling.org -hentai.to -hentaitube.tv -hentaivideoworld.com -heqinglian.net -heritage.org -heungkongdiscuss.com -hexieshe.com -hexieshe.xyz -hexxeh.net -heyzo.com -hgseav.com -hhdcb3office.org -hhthesakyatrizin.org -hidden-advent.org -hidecloud.com -hidein.net -hideipvpn.com -hideman.net -hide.me -hideme.nl -hidemyass.com -hidemycomp.com -hidemy.name -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 -hi-on.org.tw -hitbtc.com -hitomi.la -hiwifi.com -hizb-ut-tahrir.info -hizb-ut-tahrir.org -hizbuttahrir.org -hjclub.info -hk01.com -hk32168.com -hka8964.wordpress.com -hkacg.com -hkacg.net -hkanews.wordpress.com -hkatvnews.com -hkbc.net -hkbf.org -hkbookcity.com -hkchronicles.com -hkchurch.org -hkci.org.hk -hkcmi.edu -hkcnews.com -hkcoc.com -hkcoc.weather.com.hk -hkctu.org.hk -hkdailynews.com.hk -hkday.net -hkdc.us -hkdf.org -hkej.com -hkepc.com -hket.com -hkfaa.com -hkfreezone.com -hk.frienddy.com -hkfront.org -hkgalden.com -hk.geocities.com -hkgolden.com -hkgpao.com -hk.gradconnection.com -hkgreenradio.org -hk.hao123img.com -hkheadline.com -hkhkhk.com -hkhrc.org.hk -hkhrm.org.hk -hkip.org.uk -hkjc.com -hk.jiepang.com -hkjp.org -hk.knowledge.yahoo.com -hklft.com -hklts.org.hk -hkmap.live -hk.myblog.yahoo.com -hk.news.yahoo.com -hkopentv.com -hkpeanut.com -hkptu.org -hk-pub.com -hk.rd.yahoo.com -hkreporter.com -hkreporter.loved.hk -hk.search.yahoo.com -hkupop.hku.hk -hkusu.net -hk.video.news.yahoo.com -hkvwet.com -hkwcc.org.hk -hk.yahoo.com -hkzone.org -h-moe.com -hmonghot.com -hmv.co.jp -hmvdigital.ca -hmvdigital.com -hnjhj.com -hnntube.com -hola.com -hola.org -holymountaincn.com -holyspiritspeaks.org -homedepot.com -homeperversion.com -homeservershow.com -home.sina.com -home.so-net.net.tw -hongkong.fandom.com -hongkongfp.com -hongmeimei.com -hongzhi.li -honven.xyz -hootsuite.com -hoover.org -hoovers.com -hopedialogue.org -hopto.org -hornygamer.com -hornytrip.com -hotair.com -hotav.tv -hotels.cn -hotfrog.com.tw -hotgoo.com -hotpornshow.com -hotpot.hk -hotshame.com -hotspotshield.com -hottg.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 -hrweb.org -hrw.org -hsjp.net -hsselite.com -hstern.net -hst.net.tw -hstt.net -htkou.net -htl.li -ht.ly -html5rocks.com -https443.net -https443.org -huaglad.com -huanghuagang.org -huangyiyu.com -huaren4us.com -huaren.us -huashangnews.com -huaxiabao.org -huaxia-news.com -huaxin.ph -hua-yue.net -huayuworld.org -hudatoriq.web.id -hudson.org -huffingtonpost.com -hugoroy.eu -huhaitai.com -huhamhire.com -huhangfei.com -huiyi.in -hulkshare.com -hulu.com -huluim.com -hungerstrikeforaids.org -hung-ya.com -huobi.co -huobi.com -huobi.me -huobi.pro -huobipro.com -huobi.sc -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 -i1.hk -i2p2.de -i2runner.com -i818hk.com -iam.soy -iamtopone.com -iask.bz -iask.ca -iav19.com -ibiblio.org -ibit.am -iblist.com -iblogserv-f.net -ibros.org -ibvpn.com -i-cable.com -icams.com -ice.audionow.com -icij.org -icl-fi.org -icoco.com -iconpaper.org -icu-project.org -idemocracy.asia -identi.ca -id.hao123.com -id.heroku.com -idiomconnection.com -idouga.com -idreamx.com -idv.tw -ieasy5.com -ied2k.net -ienergy1.com -iepl.us -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 -ihao.org -iicns.com -iipdigital.usembassy.gov -ikstar.com -ikwb.com -ilbe.com -ilhamtohtiinstitute.org -i.lithium.com -illusionfactory.com -ilove80.be -ilovelongtoes.com -im88.tw -imageab.com -imagefap.com -imageflea.com -images.comico.tw -images-gaytube.com -imageshack.us -imagevenue.com -imagezilla.net -imb.org -imdb.com -imgchili.net -img.dlsite.jp -img.ly -imgmega.com -imgur.com -imkev.com -imlive.com -immigration.gov.tw -immoral.jp -impact.org.au -impp.mn -im.tv -in99.org -incapdns.net -incloak.com -incredibox.fr -independent.co.uk -indiablooms.com -indianarrative.com -indiandefensenews.in -indiemerch.com -in-disguise.com -info-graf.fr -initiativesforchina.org -inkui.com -inmediahk.net -innermongolia.org -inoreader.com -inote.tw -insecam.org -insidevoa.com -instagram.com -instanthq.com -institut-tibetain.org -international-news.newsmagazine.asia -internetdefenseleague.org -internetfreedom.org -internet.org -internetpopculture.com -inthenameofconfuciusmovie.com -investigating.wordpress.com -inxian.com -iownyour.biz -iownyour.org -ipalter.com -i-part.com.tw -ipfire.org -ipfs.io -iphone4hongkong.com -iphonehacks.com -iphonetaiwan.org -iphonix.fr -ipicture.ru -ipjetable.net -ipobar.com -ipoock.com -iportal.me -ippotv.com -ipredator.se -iptvbin.com -iptv.com.tw -ipvanish.com -iredmail.org -ironbigfools.compython.net -ironpython.net -ironsocket.com -isaacmao.com -is-a-hunter.com -isasecret.com -i-scmp.com -isc.sans.edu -is.gd -isgreat.org -islahhaber.net -islamawareness.net -islamhouse.com -islamicity.com -islamicpluralism.org -islam.org.hk -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 -its.caltech.edu -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 -izlesem.org -izles.net -jamaat.org -jamestown.org -jamyangnorbu.com -jandyx.com -janwongphoto.com -japanfirst.asianfreeforum.com -japantimes.co.jp -japan-whores.com -jav101.com -jav2be.com -jav68.tv -javakiba.org -javbus.com -jav.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 -ja.wikipedia.org -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 -jims.net -jinbushe.org -jingpin.org -jingsim.org -jinpianwang.com -jinroukong.com -jintian.net -jinx.com -jitouch.com -jizzthis.com -jjgirls.com -jkb.cc -jkforum.net -jkub.com -jma.go.jp -j.mp -jmscult.com -joachims.org -jobnewera.wordpress.com -jobso.tv -joinclubhouse.com -joinmastodon.org -journalchretien.net -journalofdemocracy.org -joymiihub.com -joyourself.com -jp.hao123.com -jpl.nasa.gov -jpopforum.net -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 -justmysocks1.net -justpaste.it -justtristan.com -juyuange.org -juziyue.com -jwmusic.org -jyxf.net -kadokawa.co.jp -kagyumonlam.org -kagyunews.com.hk -kagyuoffice.org -kagyuoffice.org.tw -kagyu.org -kagyu.org.za -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.org -karmapa-teachings.org -ka-wai.com -kawaiikawaii.jp -kawase.com -kba-tx.org -kb.monitorware.com -kcoolonline.com -k-doujin.net -kebrum.com -kechara.com -keepandshare.com -keezmovies.com -kendatire.com -kendincos.net -kenengba.com -keontech.net -kepard.com -kex.com -keycdn.com -khabdha.org -khatrimaza.org -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 -kinokuniya.com -kir.jp -kissbbao.cn -kiwi.kz -kkbox.com -kknews.cc -kk-whys.co.jp -kmuh.org.tw -knowledgerush.com -knowyourmeme.com -kobobooks.com -kobo.com -kodingen.com -kompozer.net -konachan.com -kone.com -koolsolutions.com -koornk.com -koranmandarin.com -korenan2.com -kqes.net -ksdl.org -ksnews.com.tw -kspcoin.com -ktzhk.com -kucoin.com -kui.name -kun.im -kurashsultan.com -kurtmunger.com -kusocity.com -kwcg.ca -kwok7.com -kwongwah.com.my -kxsw.life -kyofun.com -kyohk.net -kyoyue.com -kyzyhello.com -kzeng.info -labiennale.org -lab.skk.moe -ladbrokes.com -la-forum.org -lagranepoca.com -lala.im -lalulalu.com -lama.com.tw -lamayeshe.com -lamnia.co.uk -lamrim.com -landofhope.tv -lanterncn.cn -lantosfoundation.org -laod.cn -laogai.org -laogairesearch.org -laomiu.com -laoyang.info -laptoplockdown.com -laqingdan.net -larsgeorge.com -lastcombat.com -lastfm.es -latelinenews.com -lausan.hk -lbank.info -ld.hao123img.com -leafyvpn.net -lecloud.net -leeao.com.cn -lefora.com -left21.hk -legalporno.com -legaltech.law.com -legsjapan.com -leirentv.ca -leisurecafe.ca -leisurepro.com -lematin.ch -lemonde.fr -lenwhite.com -leorockwell.com -lerosua.org -lers.google -lesoir.be -letou.com -letscorp.net -le-vpn.com -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 -library.usc.cuhk.edu.hk -lidecheng.com -lifemiles.com -lighten.org.tw -lighti.me -lightnovel.cn -lightyearvpn.com -lihkg.com -like.com -limiao.net -line-apps.com -linear-abematv.akamaized.net -line.me -line.naver.jp -line-scdn.net -linglingfa.com -lingvodics.com -linkideo.com -link-o-rama.com -linkuswell.com -linux.org.hk -linuxtoy.org -lionsroar.com -lipuman.com -liquidvpn.com -listennotes.com -listentoyoutube.com -listorious.com -lists.w3.org -liudejun.com -liuhanyu.com -liujianshu.com -liuxiaobo.net -liu-xiaobo.org -liuxiaotong.com -livecoin.net -livedoor.jp -liveleak.com -livemint.com -livestation.com -livestream.com -livevideo.com -livingonline.us -livingstream.com -liwangyang.com -lizhizhuangbi.com -lkcn.net -llss.me -lncn.org -load.to -lobsangwangyal.com -localbitcoins.com -localdomain.ws -localpresshk.com -lockestek.com -logbot.net -login.target.com -logiqx.com -logos.com.hk -londonchinese.ca -longhair.hk -longmusic.com -longtermly.net -longtoes.com -lookpic.com -looktoronto.com -lotsawahouse.org -lotuslight.org.hk -lotuslight.org.tw -lovetvshow.com -lpsg.com -lrfz.com -lrip.org -lsd.org.hk -lsforum.net -lsmchinese.org -lsmkorean.org -lsm.org -lsmradio.com -lsmwebcast.com -lsxszzg.com -ltn.com.tw -luckydesigner.space -luke54.com -luke54.org -lupm.org -lushstories.com -luxebc.com -lvhai.org -lvv2.com -lyfhk.net -lzjscript.com -lzmtnews.org -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 -ma.hao123.com -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 -manyvoices.news -maplew.com -marc.info -marguerite.su -martau.com -martincartoons.com -martsangkagyuofficial.org -maruta.be -marxist.com -marxist.net -marxists.org -mash.to -maskedip.com -mastodon.cloud -mastodon.host -mastodon.social -mastodon.xyz -matainja.com -material.io -mathable.io -mathiew-badimon.com -matome-plus.com -matome-plus.net -matrix.org -matsushimakaede.com -matters.news -matthewdgreen.wordpress.com -mattwilcox.net -maturejp.com -maxing.jp -mayimayi.com -mcadforums.com -mcaf.ee -mcfog.com -mcreasite.com -md-t.org -meansys.com -mediachinese.com -mediafire.com -mediafreakcity.com -media.nu.nl -media.org.hk -medium.com -meetav.com -meetup.com -mefeedia.com -mefound.com -mega.co.nz -mega.io -mega.nz -megaproxy.com -megarotic.com -megavideo.com -megurineluka.com -meirixiaochao.com -meltoday.com -me.me -memehk.com -meme.yahoo.com -memorybbs.com -memrijttm.org -memri.org -mercatox.com -mercdn.net -mercyprophet.org -mergersandinquisitions.org -meridian-trust.org -meripet.biz -meripet.com -merit-times.com.tw -meshrep.com -mesotw.com -messenger.com -metacafe.com -metafilter.com -metart.com -metarthunter.com -meteorshowersonline.com -metrohk.com.hk -metrolife.ca -metroradio.com.hk -mewe.com -meyou.jp -me.youthwant.com.tw -meyul.com -mfxmedia.com -mgoon.com -mgstage.com -mh4u.org -m.hkgalden.com -mhradio.org -michaelanti.com -michaelmarketl.com -microvpn.com -middle-way.net -mihk.hk -mihr.com -mihua.org -mike.cz.cc -mikesoltys.com -milph.net -milsurps.com -mimiai.net -mimivip.com -mimivv.com -mindrolling.org -mingdemedia.org -minghui-a.org -minghui-b.org -minghui.org -minghui.or.kr -minghui-school.org -minghuiyw.wordpress.com -mingjinglishi.com -mingjingnews.com -mingjingtimes.com -mingpaocanada.com -mingpao.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 -mirrormedia.mg -mist.vip -mitao.com.tw -mitbbsau.com -mitbbs.com -mixero.com -mixi.jp -mixpod.com -mixx.com -mizzmona.com -mjib.gov.tw -mjlsh.usc.cuhk.edu.hk -mk5000.com -mlcool.com -mlzs.work -mmaaxx.com -mm-cg.com -m.me -mmmca.com -mnewstv.com -mobatek.net -mobile01.com -mobileways.de -mobypicture.com -moby.to -modernchinastudies.org -moeaic.gov.tw -moeerolibrary.com -mofa.gov.tw -mofaxiehui.com -mofos.com -mog.com -mohu.club -mohu.ml -mohu.rocks -mojim.com -mol.gov.tw -molihua.org -mondex.org -moneyhome.biz -money-link.com.tw -mo.nightlife141.com -monitorchina.org -monster.com -moodyz.com -moonbbs.com -moonbingo.com -moptt.tw -morningsun.org -moroneta.com -mos.ru -motherless.com -motiyun.com -motor4ik.ru -mousebreaker.com -movements.org -moviefap.com -mp3buscador.com -mpettis.com -mpfinance.com -mpinews.com -m.plixi.com -mponline.hk -mqxd.org -mrbasic.com -mrbonus.com -mrface.com -mrslove.com -mrtweet.com -msa-it.org -msguancha.com -msha.gov -m.slandr.net -mswe1.org -m-team.cc -mthruf.com -mtw.tl -mubi.com -muchosucko.com -mullvad.net -multiply.com -multiproxy.org -multiupload.com -mummysgold.com -murmur.tw -musicade.net -muslimvideo.com -muzi.com -muzi.net -muzu.tv -mvdis.gov.tw -mvg.jp -mx981.com -mx.hao123.com -my03.com -myactimes.com -myanniu.com -myaudiocast.com -myav.com.tw -mybbs.us -mybet.com -myca168.com -mycanadanow.com -mychinamyhome.com -mychinanet.com -mychinanews.com -mychinese.news -mycnnews.com -mycould.com -mydad.info -myddns.com -myeasytv.com -myeclipseide.com -my-formosa.com -myforum.com.hk -myforum.com.uk -myfreecams.com -myfreepaysite.com -myfreshnet.com -myftp.info -myftp.name -myiphide.com -mykomica.org -mylftv.com -my.mail.ru -mymediarom.com -mymoe.moe -mymom.info -mymusic.net.tw -mynetav.net -mynetav.org -mynumber.org -my.opera.com -myparagliding.com -my.pcloud.com -mypicture.info -mypop3.net -mypop3.org -mypopescu.com -my-private-network.co.uk -my-proxy.com -myradio.hk -myreadingmanga.info -mysecondarydns.com -myshare.url.com.tw -mysinablog.com -mysite.verizon.net -myspacecdn.com -myspace.com -mytalkbox.com -mytizi.com -mywww.biz -myz.info -naacoalition.org -naitik.net -nakido.com -nakuz.com -nalandabodhi.org -nalandawest.org -namgyalmonastery.org -namgyal.org -namsisi.com -nanyang.com -nanyangpost.com -nanzao.com -naol.ca -naol.cc -nat.gov.tw -nationalawakening.org -nationalinterest.org -national-lottery.co.uk -nationalreview.com -nationsonline.org -nationwide.com -nat.moe -naughtyamerica.com -navyfamily.navy.mil -navyreserve.navy.mil -naweeklytimes.com -nbcnews.com -nbtvpn.com -nccwatch.org.tw -nch.com.tw -nchrd.org -ncn.org -nde.de -ndi.org -ndr.de -ned.org -nekoslovakia.net -nemesis2.qx.net -neo-miracle.com -neowin.net -nepusoku.com -netalert.me -netbirds.com -netcolony.com -net-fits.pro -netflav.com -netflix.com -netflix.net -netme.cc -netsneak.com -network54.com -networkedblogs.com -networktunnel.net -neverforget8964.org -new-3lunch.net -new96.ca -new-akiba.com -newcenturymc.com -newcenturynews.com -newchen.com -newgrounds.com -newhighlandvision.com -newipnow.com -newlandmagazine.com.au -newnews.ca -news100.com.tw -newsancai.com -newschinacomment.org -newscn.org -news.cnyes.com -newsdetox.ca -newsdh.com -news.hk.msn.com -news.msn.com.tw -news.nationalgeographic.com -news.now.com -news.omy.sg -newspeak.cc -news.seehua.com -news.sina.com.hk -news.sina.com.tw -news.sinchew.com.my -news.singtao.ca -newstamago.com -newstapa.org -newstarnet.com -news.tvb.com -news.tvbs.com.tw -newsweek.com -news.yahoo.com -news.ycombinator.com -newtaiwan.com.tw -newtalk.tw -newyorker.com -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 -nicovideo.jp -nighost.org -nikkei.com -ninecommentaries.com -ninjacloak.com -ninjaproxy.ninja -nintendium.com -ninth.biz -nitter.cc -nitter.net -nitter.pussthecat.org -niu.moe -niusnews.com -njactb.org -njuice.com -nko.navy.mil -nlfreevpn.com -nmsl.website -nnews.eu -nobelprize.org -nobel.se -nobodycanstop.us -nofile.io -no-ip.org -nokogiri.org -nokola.com -noodlevpn.com -norbulingka.org -nordstrom.com -nordstromimage.com -nordstromrack.com -nordvpn.com -notify.dropboxapi.com -nottinghampost.com -novelasia.com -now.com -now.im -nownews.com -nowtorrents.com -noxinfluencer.com -noypf.com -npa.go.jp -npa.gov.tw -npnt.me -npsboost.com -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 -ntbk.gov.tw -ntbna.gov.tw -ntbt.gov.tw -ntd.tv -ntdtv.ca -ntdtv.co.kr -ntdtv.com -ntdtv.com.tw -ntdtv.cz -ntdtvla.com -ntdtv.org -ntdtv.ru -ntrfun.com -ntsna.gov.tw -nubiles.net -nuexpo.com -nukistream.com -nurgo-software.com -nusatrip.com -nutaku.net -nutsvpn.work -nuuvem.com -nuvid.com -nuzcom.com -nvdst.com -nvquan.org -nvtongzhisheng.org -nwtca.org -nyaa.eu -nyaa.si -nybooks.com -nydus.ca -nylon-angel.com -nylonstockingsonline.com -nypost.com -ny.stgloballink.com -nytchina.com -nytcn.me -nytco.com -nyt.com -nytimes.com -nytimes.map.fastly.net -nytimg.com -nyti.ms -nytstyle.com -ny.visiontimes.com -nzchinese.com -nzchinese.net.nz -observechina.net -obutu.com -ocaspro.com -occupytiananmen.com -oclp.hk -ocreampies.com -ocry.com -ocsp.int-x3.letsencrypt.org -october-review.org -oculuscdn.com -oculus.com -oex.com -offbeatchina.com -officeoftibet.com -ofile.org -ogaoga.org -ogate.org -ohmyrss.com -oikos.com.tw -oiktv.com -oizoblog.com -okayfreedom.com -okex.com -okk.tw -ok.ru -old-cat.net -old.honeynet.org -old.nabble.com -olumpo.com -olympicwatch.org -omgili.com -omni7.jp -omnitalk.com -omnitalk.org -omny.fm -on2.com -onapp.com -on.cc -onedrive.live.com -onedumb.com -onejav.com -onion.city -onlinecha.com -online.recoveryversion.org -onlineyoutube.com -onlygayvideo.com -onlytweets.com -onmoon.com -onmoon.net -onmypc.biz -onmypc.info -onmypc.net -onmypc.org -onmypc.us -onthehunt.com -ontrac.com -oopsforum.com -openallweb.com -open.com.hk -opendemocracy.net -opendn.xyz -openervpn.in -openid.net -openleaks.org -opensource.google -opentech.fund -openvpn.net -openvpn.org -openwebster.com -openwrt.org.cn -opml.radiotime.com -opus-gaming.com -ordns.he.net -organcare.org.tw -organharvestinvestigation.net -organiccrap.com -orgasm.com -orgfree.com -orientaldaily.com.my -orient-doll.com -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 -overdaily.org -overplay.net -oversea.istarshine.com -ovpn.com -owl.li -owltail.com -ow.ly -oxfordscholarship.com -oyax.com -oyghan.com -ozchinese.com -ozvoice.org -ozxw.com -ozyoyo.com -pachosting.com -pacificpoker.com -packetix.net -pacopacomama.com -padmanet.com -page2rss.com -page.bid.yahoo.com -pagodabox.com -palacemoon.com -paldengyal.com -paljorpublications.com -paltalk.com -panamapapers.sueddeutsche.de -pandapow.co -pandapow.net -pandavpn-jp.com -pandavpnpro.com -pandora.com -pandora.tv -panluan.net -panoramio.com -pao-pao.net -paperb.us -paper.li -paradisehill.cc -paradisepoker.com -parkansky.com -parler.com -parse.com -parsevideo.com -partycasino.com -partypoker.com -passion.com -passiontimes.hk -pastebin.com -paste.ee -pastie.org -paxful.com -pbs.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 -peing.net -pekingduck.org -pemulihan.or.id -penchinese.com -penchinese.net -pengyulong.com -pen.io -penisbot.com -penthouse.com -pentoy.hk -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 -phobos.apple.com -phosphation13.rssing.com -photodharma.net -photofocus.com -phuquocservices.com -picacomiccn.com -picacomic.com -picasaweb.com -picidae.net -picturedip.com -picturesocial.com -pictures.playboy.com -pimg.tw -pin6.com -pin-cong.com -pincong.rocks -ping.fm -pinimg.com -pinkrod.com -pinoy-n.com -pinterest.at -pinterest.ca -pinterest.co.kr -pinterest.com -pinterest.com.mx -pinterest.co.uk -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 -plexvpn.pro -plm.org.hk -plunder.com -plurk.com -plus28.com -plusbb.com -plus.codes -pmatehunter.com -pmates.com -po2b.com -pobieramy.top -podbean.com -podictionary.com -pokerstars.com -pokerstars.net -politicalchina.org -politicalconsultation.org -politiscales.net -poloniex.com -polymerhk.com -polymer-project.org -popo.tw -popvote.hk -popyard.com -popyard.org -porn2.com -porn5.com -pornbase.org -porn.com -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 -post01.com -post76.com -post852.com -postadult.com -postimg.org -posts.careerengine.us -potato.im -potvpn.com -powerapple.com -power.com -powercx.com -powerphoto.org -prayforchina.net -premeforwindows7.com -premproxy.com -presentationzen.com -presidentlee.tw -prestige-av.com -pride.google -prism-break.org -prisoneralert.com -pritunl.com -privacybox.de -private.com -privateinternetaccess.com -privatepaste.com -privatetunnel.com -privatevpn.com -procopytips.com -project-syndicate.org -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 -pshvpn.com -psiphon3.com -psiphon.ca -psiphon.civisec.org -psiphontoday.com -pt.im -pts.org.tw -ptt.cc -pttvan.org -pubu.com.tw -puffinbrowser.com -puffstore.com -pullfolio.com -pulse.yahoo.com -punyu.com -pure18.com -pureconcepts.net -pureinsight.org -purepdf.com -purevpn.com -purplelotus.org -purpose.nike.com -pursuestar.com -pushchinawall.com -pussyspace.com -putihome.org -putlocker.com -putty.org -puuko.com -pwned.com -python.com -python.com.tw -pythonhackers.com -pytorch.org -qanote.com -qgirl.com.tw -qhigh.com -qianbai.tw -qiandao.today -qiangwaikan.com -qiangyou.org -qidian.ca -qienkuen.org -qi-gong.me -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 -quoracdn.net -quora.com -quran.com -quranexplorer.com -qusi8.net -qvodzy.org -qxbbs.org -qz.com -r0.ru -r18.com -radicalparty.org -radiko.jp -radioaustralia.net.au -radio.garden -radiohilight.net -radioline.co -radiovaticana.org -radiovncr.com -rael.org -ra.gg -raggedbanner.com -raidcall.com.tw -raidtalk.com.tw -rainbowplan.org -raindrop.io -raizoji.or.jp -ramcity.com.au -rangwang.biz -rangzen.com -rangzen.net -rangzen.org -ranyunfei.com -rapbull.net -rapidgator.net -rapidmoviez.com -rapidvpn.com -rarbgprx.org -raremovie.cc -raremovie.net -rateyourmusic.com -rawgit.com -rawgithub.com -raw.githubusercontent.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 -reason.com -rebatesrule.net -recordhistory.org -recovery.org.tw -recoveryversion.com.tw -redballoonsolidarity.org -redbubble.com -redchinacn.net -redchinacn.org -redd.it -reddit.com -redditlist.com -redditmedia.com -redditstatic.com -redhotlabs.com -red-lang.org -redtube.com -referer.us -reflectivecode.com -registry.google -relaxbbs.com -relay.com.tw -releaseinternational.org -religioustolerance.org -renminbao.com -renyurenquan.org -research.jmsc.hku.hk -resilio.com -resistchina.org -retweeteffect.com -retweetist.com -retweetrank.com -reuters.com -reutersmedia.net -revleft.com -revver.com -rfachina.com -rfalive1.akacast.akamaistream.net -rfamobile.org -rfa.org -rfaweb.org -rferl.org -rfi.fr -rfi.my -rightbtc.com -rigpa.org -riku.me -rileyguide.com -riseup.net -ritouki.jp -ritter.vg -rixcloud.com -rixcloud.us -rlwlw.com -rmjdw132.info -rmjdw.com -roadshow.hk -roboforex.com -robustnessiskey.com -rocket-inc.net -rocksdb.org -rojo.com -rolfoundation.org -rolia.net -rolsociety.org -ronjoneswriter.com -roodo.com -rosechina.net -rotten.com -rsdlmonitor.com -rsf-chinese.org -rsf.org -rsgamen.org -rsshub.app -rssmeme.com -rtalabel.org -rthk.hk -rthklive2-lh.akamaihd.net -rthk.org.hk -rti.org.tw -rti.tw -rtycminnesota.org -ruanyifeng.com -rukor.org -rule34.xxx -runbtx.com -rushbee.com -ruten.com.tw -rutube.ru -ruyiseek.com -rxhj.net -s1heng.com -s1.nudezz.com -s1s1s1.com -s3.amazonaws.com -s3-ap-northeast-1.amazonaws.com -s3-ap-southeast-2.amazonaws.com -s8forum.com -sacks.com -sacom.hk -sadistic-v.com -sadpanda.us -safervpn.com -safety.google -sa.hao123.com -saintyculture.com -saiq.me -sakuralive.com -sakya.org -salvation.org.hk -samair.ru -sambhota.org -sankei.com -sanmin.com.tw -sapikachu.net -saveliuxiaobo.com -savemedia.com -savethedate.foo -savethesounds.info -savetibet.de -savetibet.fr -savetibet.nl -savetibet.org -savetibet.ru -savetibetstore.org -saveuighur.org -savevid.com -say2.info -sbme.me -sbs.com.au -scache1.vzw.com -scache2.vzw.com -scache.vzw.com -scasino.com -schema.org -sciencenets.com -scieron.com -scmpchinese.com -scmp.com -scramble.io -scratch.mit.edu -scribd.com -scriptspot.com -s-cute.com -s-dragon.org -search.aol.com -search.com -searchtruth.com -search.xxx -search.yahoo.co.jp -search.yahoo.com -searx.me -seattlefdc.com -secretchina.com -secretgarden.no -secretsline.biz -secure.hustler.com -secure.logmein.com -secure.raxcdn.com -secureservercdn.net -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 -sex3.com -sex8.cc -sexandsubmission.com -sexbot.com -sex.com -sexhuang.com -sexhu.com -sexidude.com -sexinsex.net -sextvx.com -sexxxy.biz -sfileydy.com -sf.net -sfshibao.com -sftindia.org -sftuk.org -shadeyouvpn.com -shadow.ma -shadowsky.xyz -shadowsocks9.com -shadowsocks.asia -shadowsocks.be -shadowsocks.com -shadowsocks.com.hk -shadowsocks.org -shadowsocks-r.com -shambalapost.com -shambhalasun.com -shangfang.org -shapeservices.com -share.america.gov -sharebee.com -sharecool.org -share.dmhy.org -share.ovi.com -share.youthwant.com.tw -sharpdaily.com.hk -sharpdaily.hk -sharpdaily.tw -shattered.io -shat-tibet.com -sheikyermami.com -shellfire.de -shenshou.org -shenyun.com -shenyunperformingarts.org -shenyunshop.com -shenzhoufilm.com -shenzhouzhengdao.org -sherabgyaltsen.com -shiatv.net -shicheng.org -shiksha.com -shinychan.com -shipcamouflage.com -shireyishunjian.com -shitaotv.org -shixiao.org -shizhao.org -shkspr.mobi -shodanhq.com -shooshtime.com -shop2000.com.tw -shopee.tw -shopping.com -showbiz.omy.sg -showhaotu.com -showtime.jp -showwe.tw -shutterstock.com -shwchurch3.com -shwchurch.org -siddharthasintent.org -sidelinesnews.com -sidelinessportseatery.com -sierrafriendsoftibet.org -signal.org -sijihuisuo.club -sijihuisuo.com -silkbook.com -simbolostwitter.com -simplecd.org -simpleproductivityblog.com -sinchew.com.my -singaporepools.com.sg -singfortibet.com -singpao.com.hk -singtao.com -singtaousa.com -sinoants.com -sinoca.com -sinocast.com -sinocism.com -sinoinsider.com -sino-monthly.com -sinomontreal.ca -sinonet.ca -sinopitt.info -sinoquebec.com -sipml5.org -sis001.com -sis001.us -sis.xxx -site2unblock.com -site90.net -sitebro.tw -sitekreator.com -siteks.uk.to -sitemaps.org -six-degrees.io -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 -smartdnsproxy.com -smarthide.com -smchbooks.com -smh.com.au -smhric.org -smith.edu -sm-miracle.com -smyxy.org -snapchat.com -snaptu.com -sndcdn.com -sneakme.net -snowlionpub.com -sobees.com -socialblade.com -socialwhale.com -sockscap64.com -sockslist.net -socks-proxy.net -soc.mil -socrec.org -sod.co.jp -softether.co.jp -softether-download.com -softether.org -softfamous.com -softnology.biz -softsmirror.cf -softwarebychuck.com -softwaredownload.gitbooks.io -sogclub.com -sogrady.me -sohcradio.com -sohfrance.org -soh.tw -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 -soulcaliburhentai.net -soul-plus.net -soumo.info -soundcloud.com -soundofhope.kr -soundofhope.org -soup.io -soupofmedia.com -sourceforge.net -sourcewadio.com -southnews.com.tw -southpark.cc.com -south-plus.org -sowers.org.hk -soylentnews.org -spaces.hightail.com -spankbang.com -spankingtube.com -spankwire.com -spb.com -speakerdeck.com -specxinzl.jigsy.com -speedify.com -spem.at -spencertipping.com -spendee.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 -squirrelvpn.com -srcf.ucam.org -srocket.us -ss7.vzw.com -ss.carryzhou.com -ssglobal.co -ssglobal.me -ssh91.com -ssl443.org -ss.levyhsu.com -ss-link.com -ssl.webpack.de -sspanel.net -sspro.ml -ss.pythonic.life -ssrshare.com -ssr.tools -sss.camp -sstmlt.moe -sstmlt.net -sstm.moe -stackoverflow.com -stage64.hk -standupfortibet.org -standwithhk.org -stanford.edu -starfishfx.com -starp2p.com -startpage.com -startuplivingchina.com -stat.gov.tw -static01.nyt.com -static.comico.tw -static-economist.com -staticflickr.com -static.shemalez.com -static.techspot.com -statueofdemocracy.org -stboy.net -stc.com.sa -steamcommunity.com -steel-storm.com -steemit.com -steganos.com -steganos.net -stepchina.com -stephaniered.com -sthoo.com -stickam.com -stickeraction.com -stileproject.com -sto.cc -stoporganharvesting.org -stoptibetcrisis.net -storagenewsletter.com -store.steampowered.com -stories.google -storify.com -stormmediagroup.com -storm.mg -stoweboyd.com -straitstimes.com -stranabg.com -straplessdildo.com -streamable.com -streamate.com -streamingthe.net -streema.com -strikingly.com -strongvpn.com -strongwindpress.com -studentsforafreetibet.org -student.tw -stumbleupon.com -stupidvideos.com -subacme.rerouted.org -successfn.com -suche.gmx.net -sugarsync.com -sugobbs.com -sugumiru18.com -suissl.com -sujiatun.wordpress.com -sulian.me -summify.com -sumrando.com -sun1911.com -sundayguardianlive.com -sunmedia.ca -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 -surfeasy.com.au -surfshark.com -suroot.com -surrenderat20.net -sustainability.google -svsfx.com -swagbucks.com -swissinfo.ch -swissvpn.net -switch1.jp -switchvpn.net -sydneytoday.com -sylfoundation.org -syncback.com -synergyse.com -sysresccd.org -sytes.net -szbbs.net -szetowah.org.hk -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 -tagwalk.com -tagwa.org.au -tahr.org.tw -taipei.gov.tw -taipeisociety.org -taiwanbible.com -taiwancon.com -taiwandaily.net -taiwandc.org -taiwanhot.net -taiwanjobs.gov.tw -taiwanjustice.com -taiwanjustice.net -taiwankiss.com -taiwannation.50webs.com -taiwannation.com -taiwannation.com.tw -taiwanncf.org.tw -taiwannews.com.tw -taiwan-sex.com -taiwantp.net -taiwantt.org.tw -taiwanus.net -taiwanyes.com -taiwanyes.ning.com -talk853.com -talkboxapp.com -talkcc.com -talkonly.net -tamiaode.tk -tanc.org -tangben.com -tangren.us -taoism.net -taolun.info -tapanwap.com -tapatalk.com -tarr.uspto.gov -tascn.com.au -taup.net -taweet.com -tbcollege.org -tbicn.org -tbi.org.hk -tbjyt.org -tbpic.info -tbrc.org -tbsec.org -tbskkinabalu.page.tl -tbsmalaysia.org -tbsn.org -tbs-rainbow.org -tbsseattle.org -tbssqh.org -tbswd.org -tbtemple.org.uk -tbthouston.org -tccwonline.org -tcewf.org -tchrd.org -tcnynj.org -t.co -tcpspeed.co -tcpspeed.com -tcsofbc.org -tcsovi.org -tdm.com.mo -teachparentstech.org -teamamericany.com -tech2.in.com -technews.tw -techviz.net -teck.in -teco-hk.org -teco-mo.org -teddysun.com -teeniefuck.net -teensinasia.com -tehrantimes.com -telecomspace.com -telegram.dog -telegramdownload.com -telegram.me -telegram.org -telegra.ph -telegraph.co.uk -telesco.pe -tellme.pw -tenacy.com -tensorflow.org -tenzinpalmo.com -tew.org -textnow.me -tfhub.dev -tfiflve.com -t-g.com -thaicn.com -thb.gov.tw -theatlantic.com -theatrum-belli.com -thebcomplex.com -theblaze.com -theblemish.com -thebobs.com -thebodyshop-usa.com -thecenter.mit.edu -thechinabeat.org -thechinacollection.org -theconversation.com -thedalailamamovie.com -thediplomat.com -thedw.us -thefacebook.com -thefrontier.hk -thegay.com -thegioitinhoc.vn -thegly.com -theguardian.com -thehots.info -thehousenews.com -thehun.net -theinitium.com -thenewslens.com -thepiratebay.org -theporndude.com -theportalwiki.com -theprint.in -thereallove.kr -therock.net.nz -thesaturdaypaper.com.au -thestandnews.com -thetibetcenter.org -thetibetconnection.org -thetibetmuseum.org -thetibetpost.com -thetinhat.com -thetrotskymovie.com -thevivekspot.com -thewgo.org -theync.com -th.hao123.com -thinkgeek.com -thinkingtaiwan.com -thinkwithgoogle.com -thisav.com -thlib.org -thomasbernhard.org -thongdreams.com -threatchaos.com -throughnightsfire.com -thumbzilla.com -thywords.com -thywords.com.tw -tiananmenduizhi.com -tiananmenmother.org -tiananmenuniv.com -tiananmenuniv.net -tiandixing.org -tianhuayuan.com -tianlawoffice.com -tiantibooks.org -tianti.io -tianyantong.org.cn -tianzhu.org -tibet3rdpole.org -tibetaction.net -tibetaid.org -tibetalk.com -tibetanaidproject.org -tibetan-alliance.org -tibetanarts.org -tibetanbuddhistinstitute.org -tibetancommunity.org -tibetancommunityuk.net -tibetanculture.org -tibetanfeministcollective.org -tibetan.fr -tibetanjournal.com -tibetanlanguage.org -tibetanliberation.org -tibetanpaintings.com -tibetanphotoproject.com -tibetanpoliticalreview.org -tibetanreview.net -tibetansports.org -tibetanwomen.org -tibetanyouthcongress.org -tibetanyouth.org -tibet.a.se -tibet.at -tibet.ca -tibetcharity.dk -tibetcharity.in -tibetchild.org -tibetcity.com -tibetcollection.com -tibet.com -tibetcorps.org -tibet-envoy.eu -tibetexpress.net -tibetfocus.com -tibet-foundation.org -tibet.fr -tibetfund.org -tibetgermany.com -tibetgermany.de -tibethaus.com -tibetheritagefund.org -tibethouse.jp -tibethouse.org -tibet-house-trust.co.uk -tibethouse.us -tibetinfonet.net -tibet-initiative.de -tibetjustice.org -tibetkomite.dk -tibetlibre.free.fr -tibet-munich.de -tibetmuseum.org -tibet.net -tibetnetwork.org -tibet.nu -tibetoffice.ch -tibetoffice.com.au -tibetoffice.eu -tibetoffice.org -tibetonline.com -tibetonline.tv -tibetoralhistory.org -tibet.org -tibet.org.tw -tibetpolicy.eu -tibetrelieffund.co.uk -tibetsites.com -tibet.sk -tibetsociety.com -tibetsun.com -tibetsupportgroup.org -tibetswiss.ch -tibettelegraph.com -tibettimes.net -tibet.to -tibetwrites.org -ticket.com.tw -tigervpn.com -tiltbrush.com -timdir.com -time.com -times.hinet.net -timesnownews.com -timesofindia.indiatimes.com -timsah.com -timtales.com -tinc-vpn.org -tineye.com -tintuc101.com -tiny.cc -tinychat.com -tinypaste.com -tipo.gov.tw -tistory.com -tkcs-collins.com -tl.gd -tma.co.jp -tmagazine.com -tmdfish.com -t.me -tmi.me -tmpp.org -tn1.shemalez.com -tn2.shemalez.com -tn3.shemalez.com -tnaflix.com -tngrnow.com -tngrnow.net -tnp.org -togetter.com -toh.info -tokyo-247.com -tokyocn.com -tokyo-hot.com -tokyo-porn-tube.com -tongil.or.kr -tono-oka.jp -tonyyan.net -toodoc.com -toonel.net -top10vpn.com -top81.ws -topbtc.com -topic.youthwant.com.tw -topnews.in -to-porno.com -toppornsites.com -topshareware.com -topsy.com -toptip.ca -top.tv -tora.to -tor.blingblingsquad.net -torcn.com -torguard.net -torproject.org -torrentprivacy.com -torrentproject.se -torrenty.org -torrentz.eu -tor.updatestar.com -torvpn.com -t.orzdream.com -tosh.comedycentral.com -totalvpn.com -toutiaoabc.com -towngain.com -toypark.in -toythieves.com -toytractorshow.com -tparents.org -tpi.org.tw -tracfone.com -traffichaus.com -transparency.org -trans.wenweipo.com -treemall.com.tw -trendsmap.com -trialofccp.org -trickip.net -trickip.org -trimondi.de -trouw.nl -trtc.com.tw -trt.net.tr -truebuddha-md.org -trulyergonomic.com -truth101.co.tv -truthontour.org -truveo.com -tryheart.jp -tsctv.net -tsdr.uspto.gov -tsemtulku.com -tsquare.tv -tsunagarumon.com -tsu.org.tw -tt1069.com -tttan.com -ttvnw.net -tu8964.com -tubaholic.com -tube8.com -tube911.com -tube.com -tubecup.com -tubegals.com -tubeislam.com -tubepornclassic.com -tubestack.com -tubewolf.com -tuibeitu.net -tuidang.net -tuidang.org -tuidang.se -tui.orzdream.com -tuitwit.com -tumblr.com -tumutanzi.com -tumview.com -tunein.com -tunnelbear.com -tunnelr.com -tunsafe.com -turansam.org -turbobit.net -turbohide.com -turbotwitter.com -turkistantimes.com -turntable.fm -tushycash.com -tuvpn.com -tuzaijidi.com -tvants.com -tvboxnow.com -tv.com -tvider.com -tv.jtbc.joins.com -tvmost.com.hk -tvplayvideos.com -tvunetworks.com -tw01.org -twaitter.com -tw.answers.yahoo.com -twapperkeeper.com -twaud.io -twavi.com -twbbs.net.tw -twbbs.org -twbbs.tw -tw.bid.yahoo.com -tw-blog.com -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 -tweets.seraph.me -tweettunnel.com -tweetwally.com -tweetymail.com -tweez.net -twelve.today -twerkingbutt.com -twftp.org -tw.gigacircle.com -twgreatdaily.com -tw.hao123.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 -twimg.edgesuite.net -twindexx.com -twip.me -twipple.jp -tw.iqiyi.com -twishort.com -twistar.cc -twisterio.com -twister.net.co -twisternow.com -twistory.net -twit2d.com -twitbrowser.net -twitcause.com -twitchcdn.net -twitch.tv -twitgether.com -twitgoo.com -twitiq.com -twitlonger.com -twitmania.com -twitoaster.com -twitonmsn.com -twitpic.com -twitstat.com -twittbot.net -twitter4j.org -twitter.com -twittercounter.com -twitterfeed.com -twittergadget.com -twitter.jp -twitterkr.com -twittermail.com -twitterrific.com -twittertim.es -twitthat.com -twitturk.com -twitturly.com -twitvid.com -twitzap.com -twiyia.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 -twnorth.org.tw -tw-npo.org -twreporter.org -twskype.com -twstar.net -tw.streetvoice.com -twtkr.com -tw.tomonews.net -twtr2src.ogaoga.org -twtrland.com -twt.tl -twttr.com -twurl.nl -tw.voa.mobi -twyac.org -tw.yahoo.com -txxx.com -tycool.com -typepad.com -u15.info -u9un.com -ub0.cc -ubddns.org -uberproxy.net -ucdc1998.org -uchicago.edu -uc-japan.org -uderzo.it -udnbkk.com -udn.com -udn.com.tw -uforadio.com.tw -ufreevpn.com -ugo.com -uhdwallpapers.org -uhrp.org -uighurbiz.net -uighur.narod.ru -uighur.nl -ukcdp.co.uk -ukliferadio.co.uk -uku.im -ulike.net -ulop.net -ultravpn.fr -ultraxs.com -umich.edu -unblock.cn.com -unblockdmm.com -unblocker.yt -unblocksit.es -unblock-us.com -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 -updates.tdesktop.com -upghsbc.com -upholdjustice.org -upload4u.info -uploaded.net -uploaded.to -uploadstation.com -upmedia.mg -upornia.com -uproxy.org -uptodown.com -upwill.org -ur7s.com -uraban.me -urbandictionary.com -urbansurvival.com -urchin.com -urlborg.com -urlparser.com -usacn.com -usaip.eu -userapi.nytlog.com -users.skynet.be -usfk.mil -ushuarencity.echainhost.com -usinfo.state.gov -usma.edu -usmc.mil -usmgtcg.ning.com -usno.navy.mil -usocctn.com -ustibetcommittee.org -us.to -ustream.tv -usunitednews.com -usus.cc -utopianpal.com -uu-gg.com -uukanshu.com -uvwxyz.xyz -uwants.com -uwants.net -uyghuramerican.org -uyghurbiz.org -uyghurcanadiansociety.org -uyghurcongress.org -uyghur.co.uk -uyghurensemble.co.uk -uyghur-j.org -uyghurpen.org -uyghurpress.com -uyghurstudies.org -uygur.fc2web.com -uygur.org -uymaarip.com -v2ex.com -v2fly.org -v2raycn.com -v2ray.com -v2raytech.com -valeursactuelles.com -van001.com -van698.com -vanemu.cn -vanilla-jp.com -vanpeople.com -vansky.com -vaticannews.va -vatn.org -vcfbuilder.org -vcf-online.org -vds.rightster.com -vegasred.com -vegas.williamhill.com -velkaepocha.sk -venbbs.com -venchina.com -venetianmacao.com -ventureswell.com -veoh.com -vermonttibet.org -versavpn.com -verybs.com -vevo.com -vft.com.tw -viber.com -vica.info -victimsofcommunism.org -vidble.com -video.aol.ca -video.aol.com -video.aol.co.uk -video.ap.org -videobam.com -videodetective.com -video.fdbox.com -video.foxbusiness.com -videomega.tv -videomo.com -video.pbs.org -videopediaworld.com -videopress.com -video.yahoo.com -vidinfo.org -vid.me -vietdaikynguyen.com -vijayatemple.org -vilavpn.com -vimeo.com -vimperator.org -vincnd.com -vine.co -vinniev.com -vip-enterprise.com -virtualrealporn.com -visibletweets.com -vital247.org -viu.com -viu.tv -vivahentai4u.net -vivatube.com -vivthomas.com -vizvaz.com -vjav.com -vjmedia.com.hk -vllcs.org -vlog.xuite.net -vmixcore.com -vmpsoft.com -vnet.link -vn.hao123.com -voa-11.akacast.akamaistream.net -voacambodia.com -voacantonese.com -voachineseblog.com -voachinese.com -voagd.com -voaindonesia.com -voanews.com -voatibetan.com -voatibetanenglish.com -vocativ.com -vocn.tv -vocus.cc -vod-abematv.akamaized.net -vod.wwe.com -voicettank.org -vot.org -vovo2000.com -voxer.com -voy.com -vpn4all.com -vpn.ac -vpnaccount.org -vpnaccounts.com -vpnbook.com -vpn.cjb.net -vpn.cmu.edu -vpncomparison.org -vpncoupons.com -vpncup.com -vpndada.com -vpnfan.com -vpnfire.com -vpnfires.biz -vpnforgame.net -vpngate.jp -vpngate.net -vpngratis.net -vpnhq.com -vpnhub.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 -vpn.sv.cmu.edu -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 -vultryhw.com -w3schools.com -waffle1999.com -wahas.com -waigaobu.com -waikeung.org -wailaike.net -waiwaier.com -wallmama.com -wallornot.org -wallpapercasa.com -wallproxy.com -wallsttv.com -waltermartin.com -waltermartin.org -wanderinghorse.net -wangafu.net -wangjinbo.org -wanglixiong.com -wango.org -wangruoshui.net -want-daily.com -wanz-factory.com -wapedia.mobi -warbler.iconfactory.net -warroom.org -waselpro.com -washeng.net -washingtonpost.com -watch8x.com -watchinese.com -watchmygf.net -watchout.tw -wattpad.com -waveprotocol.org -wav.tv -waymo.com -wda.gov.tw -wdf5.com -wearehairy.com -wearn.com -web2project.net -webbang.net -web.dev -webevader.org -webfreer.com -webjb.org -weblagu.com -webmproject.org -webrtc.org -webrush.net -website.informer.com -websitepulse.com -webs-tv.net -webwarper.net -webworkerdaily.com -wechatlawsuit.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 -welt.de -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 -whatsonweibo.com -wheatseeds.org -wheelockslatin.com -whereiswerner.com -wheretowatch.com -whippedass.com -whitebear.freebearblog.org -whodns.xyz -whoer.net -whotalking.com -whylover.com -whyx.org -w.idaiwan.com -widevine.com -wikaba.com -wiki.cnitter.com -wiki.esu.im -wiki.gamerp.jp -wiki.jqueryui.com -wiki.keso.cn -wikileaks.ch -wikileaks.com -wikileaks.de -wikileaks.eu -wikileaks-forum.com -wikileaks.lu -wikileaks.org -wikileaks.pl -wikilivres.info -wikimapia.org -wikimedia.org -wiki.moegirl.org -wiki.oauth.net -wikipedia.org -wiki.phonegap.com -wikiwand.com -wikiwiki.jp -wildammo.com -williamhill.com -willw.net -windowsphoneme.com -windscribe.com -wingamestore.com -wingy.site -winning11.com -winwhispers.info -wionews.com -wire.com -wiredbytes.com -wiredpen.com -wireguard.com -wisdompubs.org -wisevid.com -withgoogle.com -withyoutube.com -witnessleeteaching.com -witopia.net -wizcrafts.net -wjbk.org -wlcnew.jigsy.com -wlx.sowiki.net -wnacg.com -wnacg.org -wn.com -wo3ttt.wordpress.com -woeser.com -woesermiddle-way.net -wokar.org -wolfax.com -wombo.ai -woolyss.com -woopie.jp -woopie.tv -wordpress.com -workatruna.com -workerdemo.org.hk -workerempowerment.org -workersthebig.net -worldcat.org -worldjournal.com -worldvpn.net -wo.tc -wow.com -wowgirls.com -wowlegacy.ml -wow-life.net -wowporn.com -wowrk.com -woxinghuiguo.com -woyaolian.org -wozy.in -wp.com -wpoforum.com -wqyd.org -wrchina.org -wretch.cc -writer.zoho.com -wsj.com -wsjhk.com -wsj.net -wtbn.org -wtfpeople.com -wuerkaixi.com -wufafangwen.com -wufi.org.tw -wuguoguang.com -wujieliulan.com -wujie.net -wukangrui.net -wuw.red -wuyanblog.com -wwitv.com -www1.american.edu -www1.biz -www2.ohchr.org -www2.rocketbbs.com -www.abclite.net -www.ajsands.com -www.americorps.gov -www.antd.org -www.aolnews.com -www.businessinsider.com.au -www.cmoinc.org -www.cool18.com -www.dmm.com -www.dwheeler.com -www.eastturkistan.net -www.gmiddle.com -www.gmiddle.net -wwwhost.biz -www.hustlercash.com -www.idlcoyote.com -www.imdb.com -www.kindleren.com -www.klip.me -www.lamenhu.com -www.lib.virginia.edu -www.linksalpha.com -www.metro.taipei -www.monlamit.org -www.moztw.org -www.m-sport.co.uk -www.nbc.com -www.orchidbbs.com -www.owind.com -www.oxid.it -www.powerpointninja.com -www.s4miniarchive.com -www.sciencemag.org -www.shadowsocks.com -www.skype.com -www.tablesgenerator.com -www.taiwanonline.cc -www.taup.org.tw -www.thechinastory.org -www.wangruowang.org -www.wan-press.org -www.websnapr.com -www.zensur.freerk.com -wzyboy.im -x1949x.com -x24hr.com -x365x.com -xanga.com -x-art.com -xa.yimg.com -xbabe.com -x-berry.com -xbookcn.com -xbtce.com -xcafe.in -xcity.jp -x.company -xcritic.com -xda-developers.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 -xiaxiaoqiang.net -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 -xixicui.icu -xizang-zhiye.org -xjp.cc -xjtravelguide.com -xkiwi.tk -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 -xsden.info -xskywalker.com -xskywalker.net -xtube.com -xuchao.net -xuchao.org -xuehua.us -xuzhiyong.net -xvbelink.com -xvideo.cc -xvideos-cdn.com -xvideos.com -xvideos.es -xvinlink.com -x-wall.org -xxbbx.com -xxlmovies.com -xxuz.com -xxx.com -xxxfuckmom.com -xxxx.com.au -xxx.xxx -xxxy.biz -xxxy.info -xxxymovies.com -xysblogs.org -xys.dxiong.com -xys.org -xyy69.com -xyy69.info -y2mate.com -yahoo.com.hk -yakbutterblues.com -yam.com -yam.org.tw -yande.re -yanghengjun.com -yangjianli.com -yasni.co.uk -yayabay.com -ydy.com -yeahteentube.com -yecl.net -yeelou.com -yeeyi.com -yegle.net -yes123.com.tw -yesasia.com -yesasia.com.hk -yes-news.com -yespornplease.com -yes.xxx -yeyeclub.com -ygto.com -yhcw.net -yibada.com -yibaochina.com -yidio.com -yigeni.com -yilubbs.com -yingsuoss.com -yinlei.org -yipub.com -yiyechat.com -yizhihongxing.com -yobit.net -yobt.com -yobt.tv -yogichen.org -yolasite.com -yomiuri.co.jp -yong.hu -yorkbbs.ca -youdontcare.com -you-get.org -youjizz.com -youmaker.com -youngpornvideos.com -youngspiration.hk -youpai.org -youporn.com -youporngay.com -yourepeat.com -your-freedom.net -yourlisten.com -yourlust.com -yourprivatevpn.com -yourtrap.com -yousendit.com -youshun12.com -youthforfreechina.org -youthnetradio.org -youtu.be -youtubecn.com -youtube.com -youtubeeducation.com -youtubegaming.com -youtubekids.com -youtube-nocookie.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 -ywpw.com -yx51.net -yyii.org -yyjlymb.xyz -yzzk.com -zacebook.com -zalmos.com -zannel.com -zaobao.com -zaobao.com.sg -zaozon.com -zapto.org -zattoo.com -zb.com -zdnet.com.tw -zello.com -zengjinyan.org -zenmate.com -zenmate.com.ru -zerohedge.com -zeronet.io -zeutch.com -zfreet.com -zgsddh.com -zgzcjj.net -zhanbin.net -zhangboli.net -zhangtianliang.com -zhanlve.org -zhao.1984.city -zhao.jinhai.de -zh.ecdm.wikia.com -zhenghui.org -zhengjian.org -zhengwunet.org -zhenlibu1984.com -zhenlibu.info -zhenxiang.biz -zhinengluyou.com -zhongguo.ca -zhongguorenquan.org -zhongguotese.net -zhongmeng.org -zhoushuguang.com -zh.pokerstrategy.com -zh.pttpedia.wikia.com -zhreader.com -zhuangbi.me -zhuanxing.cn -zhuatieba.com -zhuichaguoji.org -zh.uncyclopedia.wikia.com -zh.wikinews.org -zh.wikiquote.org -zh.wikisource.org -ziddu.com -zillionk.com -zi.media -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 -zophar.net -zorrovpn.com -zozotown.com -zpn.im -zspeeder.me -zsrhao.com -zuobiao.me -zuo.la -zuola.com -zvereff.com -zynaima.com -zynamics.com -zyns.com -zyxel.com -zyzc9.com -zzcartoon.com -zzcloud.me -zzux.com diff --git a/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt b/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt deleted file mode 100644 index 0c354199e..000000000 --- a/luci-app-mwan3helper/root/etc/mwan3helper/gwbn.txt +++ /dev/null @@ -1,290 +0,0 @@ -1.94.0.0/15 -14.1.24.0/22 -14.103.0.0/16 -14.196.0.0/15 -42.196.0.0/14 -43.242.56.0/22 -43.242.144.0/22 -43.243.144.0/22 -43.243.148.0/22 -43.247.200.0/22 -43.247.204.0/22 -43.247.208.0/22 -43.247.224.0/22 -43.250.16.0/22 -43.250.20.0/22 -45.121.240.0/22 -45.121.244.0/22 -45.121.248.0/22 -45.121.252.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.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.76.0/22 -45.123.80.0/22 -45.123.84.0/22 -45.123.88.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.100.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 -59.152.16.0/22 -59.152.20.0/22 -59.152.24.0/22 -59.152.28.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 -60.194.0.0/15 -60.206.0.0/15 -61.14.212.0/22 -61.14.216.0/22 -61.14.220.0/22 -101.38.0.0/15 -101.40.0.0/15 -101.104.0.0/14 -101.130.0.0/15 -103.23.56.0/22 -103.24.184.0/22 -103.38.0.0/22 -103.38.228.0/22 -103.38.232.0/22 -103.41.148.0/22 -103.41.152.0/22 -103.62.72.0/22 -103.62.76.0/22 -103.62.80.0/22 -103.62.84.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.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.196.0/22 -103.63.200.0/22 -103.63.204.0/22 -103.63.208.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.216.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.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.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.204.88.0/22 -103.204.112.0/22 -103.204.148.0/22 -103.204.152.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 -103.238.20.0/22 -103.238.24.0/22 -103.238.40.0/22 -115.31.72.0/22 -115.31.76.0/22 -115.47.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 -115.190.0.0/15 -116.199.128.0/19 -117.58.0.0/17 -117.120.64.0/18 -118.28.0.0/15 -124.14.0.0/15 -124.192.0.0/15 -124.200.0.0/13 -124.254.0.0/18 -175.188.0.0/14 -180.88.0.0/14 -180.235.112.0/22 -202.4.252.0/22 -202.160.156.0/22 -202.168.136.0/22 -202.168.140.0/22 -203.78.156.0/22 -203.82.112.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.86.64.0/20 -203.114.80.0/22 -203.114.84.0/22 -203.114.88.0/22 -203.114.92.0/22 -203.207.92.0/22 -203.207.112.0/20 -203.207.192.0/21 -203.207.208.0/20 -211.101.0.0/18 -211.103.128.0/17 -211.164.0.0/14 -218.249.0.0/16 -219.90.68.0/22 -219.90.72.0/22 -219.90.76.0/22 -219.238.0.0/15 -220.112.0.0/14 -223.208.0.0/14 -223.255.0.0/17 diff --git a/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt b/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt deleted file mode 100644 index d2855404d..000000000 --- a/luci-app-mwan3helper/root/etc/mwan3helper/othernet.txt +++ /dev/null @@ -1,5209 +0,0 @@ -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.116.0.0/15 -1.118.0.0/16 -1.119.0.0/17 -1.119.128.0/17 -14.1.96.0/22 -14.1.108.0/22 -14.102.156.0/22 -14.102.180.0/22 -14.130.0.0/15 -14.192.60.0/22 -27.0.132.0/22 -27.0.160.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.116.44.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 -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.136.0.0/13 -43.144.0.0/12 -43.160.0.0/11 -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.228.204.0/22 -43.228.240.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.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.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.144.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.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.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.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.220.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.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.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.100.0/22 -43.251.116.0/22 -43.251.192.0/22 -43.251.236.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.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.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.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.64.0/22 -45.121.92.0/22 -45.121.96.0/22 -45.121.212.0/22 -45.122.0.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.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.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.124.28.0/22 -45.124.32.0/22 -45.124.44.0/22 -45.124.76.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.125.12.0/22 -45.125.16.0/22 -45.125.28.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.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.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.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.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.24.0.0/15 -58.30.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.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.32.0/22 -59.152.36.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.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.192.0.0/10 -60.55.0.0/16 -60.63.0.0/16 -60.200.0.0/14 -60.205.0.0/16 -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.29.192.0/19 -61.29.224.0/20 -61.29.240.0/21 -61.29.248.0/22 -61.29.254.0/23 -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.33.128.0/17 -101.34.0.0/15 -101.36.0.0/18 -101.36.64.0/19 -101.36.128.0/17 -101.37.0.0/16 -101.42.0.0/15 -101.44.0.0/14 -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.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.244.0.0/16 -101.245.0.0/16 -101.246.0.0/15 -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.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.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.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.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.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.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.116.0/22 -103.41.140.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.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.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.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.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.56.0/22 -103.56.72.0/22 -103.56.76.0/22 -103.56.100.0/22 -103.56.104.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.176.0/22 -103.61.188.0/22 -103.62.52.0/22 -103.62.88.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.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.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.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.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.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.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.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.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.80.0/22 -103.86.84.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.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.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.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.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.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.72.0/22 -103.107.108.0/22 -103.107.164.0/22 -103.107.168.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.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.80.0/23 -103.110.92.0/22 -103.110.116.0/22 -103.110.127.0/24 -103.110.128.0/23 -103.110.136.0/22 -103.110.152.0/22 -103.110.188.0/22 -103.110.204.0/22 -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.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.88.0/22 -103.120.96.0/22 -103.120.100.0/22 -103.120.140.0/22 -103.120.196.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.126.100.0/22 -103.126.124.0/22 -103.126.128.0/22 -103.126.132.0/22 -103.126.208.0/22 -103.126.241.0/24 -103.129.52.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.168.0/22 -103.131.176.0/22 -103.131.224.0/22 -103.131.228.0/22 -103.131.240.0/22 -103.132.60.0/22 -103.132.64.0/22 -103.132.68.0/22 -103.132.72.0/22 -103.132.76.0/22 -103.132.80.0/22 -103.132.104.0/22 -103.132.108.0/22 -103.132.112.0/22 -103.132.116.0/22 -103.132.120.0/22 -103.132.160.0/22 -103.132.164.0/22 -103.132.188.0/22 -103.132.208.0/22 -103.132.234.0/23 -103.133.12.0/22 -103.133.40.0/22 -103.133.128.0/22 -103.133.136.0/22 -103.133.176.0/22 -103.133.232.0/22 -103.134.12.0/24 -103.134.196.0/22 -103.135.80.0/22 -103.135.124.0/22 -103.135.148.0/22 -103.135.156.0/22 -103.135.160.0/22 -103.135.164.0/22 -103.135.176.0/22 -103.135.184.0/22 -103.135.192.0/22 -103.135.196.0/22 -103.135.236.0/22 -103.136.128.0/22 -103.136.232.0/22 -103.137.58.0/23 -103.137.76.0/22 -103.137.136.0/23 -103.137.149.0/24 -103.137.180.0/22 -103.137.236.0/22 -103.138.2.0/23 -103.138.12.0/23 -103.138.80.0/22 -103.138.134.0/23 -103.138.156.0/23 -103.138.208.0/23 -103.138.220.0/23 -103.138.246.0/23 -103.138.248.0/23 -103.139.0.0/23 -103.139.2.0/23 -103.139.22.0/23 -103.139.113.0/24 -103.139.134.0/23 -103.139.136.0/23 -103.139.172.0/23 -103.139.200.0/23 -103.139.204.0/23 -103.139.212.0/23 -103.140.8.0/23 -103.140.14.0/23 -103.140.70.0/23 -103.140.126.0/23 -103.140.140.0/23 -103.140.144.0/23 -103.140.152.0/23 -103.140.194.0/23 -103.140.228.0/23 -103.141.10.0/23 -103.141.36.0/23 -103.141.58.0/23 -103.141.128.0/23 -103.141.186.0/23 -103.142.58.0/23 -103.142.82.0/23 -103.142.96.0/23 -103.142.102.0/23 -103.142.122.0/23 -103.142.140.0/23 -103.142.154.0/23 -103.142.156.0/23 -103.142.172.0/23 -103.142.180.0/23 -103.142.186.0/23 -103.142.190.0/23 -103.142.220.0/23 -103.142.230.0/24 -103.142.234.0/23 -103.142.238.0/23 -103.142.248.0/23 -103.143.16.0/23 -103.143.18.0/23 -103.143.31.0/24 -103.143.74.0/23 -103.143.120.0/23 -103.143.124.0/23 -103.143.132.0/23 -103.143.134.0/23 -103.143.174.0/23 -103.143.228.0/23 -103.144.40.0/23 -103.144.52.0/23 -103.144.66.0/23 -103.144.70.0/23 -103.144.72.0/23 -103.144.108.0/23 -103.144.136.0/23 -103.144.148.0/23 -103.144.158.0/23 -103.144.240.0/23 -103.145.38.0/23 -103.145.40.0/23 -103.145.42.0/23 -103.145.60.0/23 -103.145.72.0/23 -103.145.80.0/23 -103.145.86.0/23 -103.145.92.0/23 -103.145.94.0/23 -103.145.98.0/23 -103.145.106.0/23 -103.145.122.0/23 -103.145.188.0/23 -103.145.190.0/23 -103.146.6.0/23 -103.146.72.0/23 -103.146.88.0/23 -103.146.90.0/23 -103.146.124.0/23 -103.146.126.0/23 -103.146.138.0/23 -103.146.230.0/23 -103.146.236.0/23 -103.146.252.0/23 -103.147.12.0/23 -103.147.124.0/23 -103.147.198.0/23 -103.147.206.0/23 -103.148.174.0/23 -103.149.6.0/23 -103.149.17.0/24 -103.149.44.0/23 -103.149.110.0/23 -103.149.132.0/23 -103.149.144.0/23 -103.149.156.0/23 -103.149.181.0/24 -103.149.190.0/23 -103.149.210.0/23 -103.149.214.0/23 -103.149.220.0/23 -103.149.242.0/23 -103.149.244.0/23 -103.149.246.0/23 -103.149.248.0/23 -103.150.24.0/23 -103.150.66.0/23 -103.150.72.0/23 -103.150.122.0/23 -103.150.126.0/23 -103.150.128.0/23 -103.150.130.0/23 -103.150.146.0/23 -103.150.164.0/23 -103.150.172.0/23 -103.150.180.0/23 -103.150.200.0/23 -103.150.210.0/23 -103.150.214.0/23 -103.150.216.0/23 -103.150.244.0/23 -103.151.4.0/23 -103.151.44.0/23 -103.151.138.0/23 -103.151.142.0/23 -103.151.148.0/23 -103.151.150.0/23 -103.151.158.0/23 -103.151.178.0/23 -103.151.206.0/23 -103.151.216.0/23 -103.151.228.0/23 -103.152.14.0/23 -103.152.24.0/23 -103.152.28.0/23 -103.152.30.0/23 -103.152.56.0/23 -103.152.76.0/23 -103.152.80.0/23 -103.152.98.0/23 -103.152.112.0/23 -103.152.120.0/23 -103.152.122.0/23 -103.152.132.0/23 -103.152.152.0/23 -103.152.168.0/23 -103.152.170.0/23 -103.152.186.0/23 -103.152.190.0/23 -103.152.192.0/23 -103.152.200.0/23 -103.152.208.0/23 -103.152.224.0/23 -103.152.226.0/23 -103.152.246.0/23 -103.152.250.0/23 -103.153.4.0/23 -103.153.36.0/23 -103.153.98.0/23 -103.153.100.0/23 -103.153.114.0/23 -103.153.122.0/23 -103.153.128.0/23 -103.153.132.0/23 -103.153.138.0/23 -103.153.146.0/23 -103.153.160.0/23 -103.154.18.0/23 -103.154.30.0/23 -103.154.32.0/23 -103.154.40.0/23 -103.154.66.0/23 -103.154.162.0/23 -103.154.164.0/23 -103.154.168.0/23 -103.154.242.0/23 -103.155.14.0/23 -103.155.16.0/23 -103.155.34.0/23 -103.155.48.0/23 -103.155.76.0/23 -103.155.100.0/23 -103.155.110.0/23 -103.155.120.0/23 -103.155.214.0/23 -103.155.248.0/23 -103.156.24.0/23 -103.156.28.0/23 -103.156.68.0/23 -103.156.78.0/23 -103.156.104.0/23 -103.156.158.0/23 -103.156.166.0/23 -103.156.174.0/23 -103.156.186.0/23 -103.156.228.0/23 -103.157.30.0/23 -103.157.138.0/23 -103.157.174.0/23 -103.157.212.0/23 -103.157.234.0/23 -103.157.254.0/23 -103.158.0.0/23 -103.158.13.0/24 -103.158.16.0/23 -103.158.190.0/23 -103.158.200.0/23 -103.158.224.0/23 -103.159.80.0/23 -103.159.122.0/23 -103.159.124.0/23 -103.159.134.0/23 -103.159.142.0/23 -103.160.32.0/23 -103.160.34.0/23 -103.160.112.0/23 -103.160.114.0/23 -103.160.244.0/23 -103.160.254.0/23 -103.161.14.0/23 -103.161.102.0/23 -103.161.138.0/23 -103.161.208.0/23 -103.161.220.0/23 -103.161.254.0/23 -103.162.10.0/23 -103.162.32.0/23 -103.162.38.0/23 -103.162.116.0/23 -103.163.28.0/23 -103.163.32.0/23 -103.163.46.0/23 -103.163.74.0/23 -103.163.180.0/23 -103.164.4.0/23 -103.164.32.0/23 -103.164.40.0/23 -103.164.42.0/23 -103.164.64.0/23 -103.164.76.0/23 -103.164.178.0/23 -103.164.226.0/23 -103.165.44.0/23 -103.165.52.0/23 -103.165.82.0/23 -103.166.20.0/23 -103.166.50.0/23 -103.166.52.0/23 -103.166.54.0/23 -103.166.84.0/23 -103.166.242.0/23 -103.166.246.0/23 -103.167.0.0/23 -103.167.36.0/23 -103.168.98.0/23 -103.168.170.0/23 -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.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.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.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.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.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.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.212.0/22 -103.203.56.0/22 -103.203.96.0/22 -103.203.100.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.140.0/22 -103.204.144.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.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.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.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.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.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.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.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.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.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.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.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.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.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.36.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.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.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.84.0/22 -103.251.128.0/22 -103.251.192.0/22 -103.251.204.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.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.212.88.0/22 -113.212.100.0/22 -113.214.0.0/15 -114.31.64.0/22 -114.31.68.0/22 -114.60.0.0/14 -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/17 -114.117.128.0/17 -114.118.0.0/16 -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.32.0.0/14 -115.42.56.0/22 -115.44.0.0/15 -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 -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/17 -116.196.128.0/18 -116.196.192.0/18 -116.197.160.0/22 -116.197.164.0/22 -116.198.0.0/16 -116.204.0.0/17 -116.204.132.0/22 -116.204.168.0/22 -116.204.216.0/22 -116.204.232.0/22 -116.205.0.0/16 -116.206.92.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.59.0.0/16 -117.72.0.0/15 -117.100.0.0/15 -117.103.16.0/20 -117.103.128.0/20 -117.106.0.0/15 -117.112.0.0/13 -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.40.0/21 -118.26.56.0/21 -118.26.64.0/19 -118.26.96.0/21 -118.26.112.0/21 -118.26.120.0/21 -118.26.128.0/20 -118.26.160.0/19 -118.26.192.0/18 -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.132.0.0/14 -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/21 -118.191.8.0/22 -118.191.12.0/24 -118.191.16.0/21 -118.191.64.0/20 -118.191.80.0/22 -118.191.128.0/19 -118.191.176.0/20 -118.191.192.0/20 -118.191.208.0/24 -118.191.216.0/22 -118.191.223.0/24 -118.191.224.0/24 -118.191.240.0/20 -118.192.0.0/16 -118.193.0.0/21 -118.193.8.0/21 -118.193.48.0/21 -118.193.128.0/17 -118.194.0.0/17 -118.194.128.0/18 -118.194.192.0/19 -118.194.224.0/22 -118.194.240.0/21 -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.10.0.0/17 -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/15 -120.46.0.0/16 -120.47.0.0/16 -120.48.0.0/15 -120.53.0.0/16 -120.54.0.0/15 -120.64.0.0/14 -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.76.0.0/15 -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.136.0/23 -122.10.164.0/22 -122.10.168.0/21 -122.10.176.0/20 -122.10.192.0/22 -122.10.200.0/21 -122.10.208.0/21 -122.10.216.0/22 -122.10.228.0/22 -122.10.232.0/21 -122.10.240.0/22 -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.224.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.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.253.108.0/22 -123.253.240.0/22 -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.70.0.0/15 -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.196.0.0/16 -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.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/17 -140.210.128.0/17 -142.70.0.0/16 -142.86.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 -149.41.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.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.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.236.0/22 -150.242.240.0/22 -150.242.244.0/22 -150.242.248.0/22 -150.248.0.0/16 -152.104.128.0/17 -152.136.0.0/16 -154.8.128.0/17 -156.59.0.0/16 -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.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 -158.60.128.0/17 -158.79.0.0/16 -159.27.0.0/16 -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.120.0.0/16 -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 -163.228.0.0/16 -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/16 -175.25.0.0/16 -175.26.0.0/16 -175.27.0.0/16 -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/15 -180.186.0.0/16 -180.187.0.0/16 -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 -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 -185.203.36.0/22 -188.131.128.0/17 -192.51.188.0/24 -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 -192.197.113.0/24 -193.112.0.0/16 -198.175.100.0/22 -199.212.57.0/24 -202.3.134.0/24 -202.5.208.0/22 -202.5.212.0/22 -202.5.216.0/22 -202.8.120.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.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.52.144.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.101.0/24 -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.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.68.0/24 -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.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.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.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.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.83.8.0/22 -203.83.12.0/22 -203.83.56.0/21 -203.86.0.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.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.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.96.0/20 -203.207.128.0/18 -203.207.200.0/21 -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 -204.52.191.0/24 -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.64.0/18 -211.101.128.0/17 -211.102.0.0/16 -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 -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 -219.72.0.0/16 -219.82.0.0/16 -219.83.128.0/17 -220.101.192.0/18 -220.154.0.0/15 -220.231.128.0/17 -220.232.64.0/18 -220.234.0.0/16 -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 -221.137.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.236.0/22 diff --git a/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt b/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt deleted file mode 100644 index 22abf2732..000000000 --- a/luci-app-mwan3helper/root/etc/mwan3helper/unicom_cnc.txt +++ /dev/null @@ -1,915 +0,0 @@ -1.24.0.0/13 -1.56.0.0/13 -1.188.0.0/14 -14.204.0.0/15 -27.0.128.0/22 -27.0.164.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.240.216.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.246.228.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.100.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.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.248.96.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.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 -59.80.0.0/15 -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.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.67.192.0/22 -103.68.128.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.78.228.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.48.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.101.180.0/22 -103.107.44.0/22 -103.107.188.0/22 -103.107.212.0/22 -103.108.244.0/22 -103.110.132.0/22 -103.110.156.0/22 -103.117.188.0/22 -103.120.224.0/22 -103.123.56.0/22 -103.131.152.0/22 -103.141.242.0/23 -103.142.128.0/23 -103.158.8.0/23 -103.166.138.0/23 -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.240.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.220.120.0/22 -103.220.124.0/22 -103.220.128.0/22 -103.220.164.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.224.232.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.243.136.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 -106.74.0.0/16 -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.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.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.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.52.0.0/16 -120.72.32.0/19 -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.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.58.240.0/20 -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/15 -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.8.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.188.0.0/17 -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.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.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/luci-app-mwan3helper/root/etc/uci-defaults/luci-mwan3helper b/luci-app-mwan3helper/root/etc/uci-defaults/luci-mwan3helper deleted file mode 100644 index 9867a8956..000000000 --- a/luci-app-mwan3helper/root/etc/uci-defaults/luci-mwan3helper +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-mwan3helper.json" ] && { - cat > /usr/share/ucitrack/luci-app-mwan3helper.json << EEOF -{ - "config": "mwan3helper", - "init": "mwan3helper" -} -EEOF -} - -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/luci-app-mwan3helper/root/usr/share/rpcd/acl.d/luci-app-mwan3helper.json b/luci-app-mwan3helper/root/usr/share/rpcd/acl.d/luci-app-mwan3helper.json deleted file mode 100644 index f8fa9fbc3..000000000 --- a/luci-app-mwan3helper/root/usr/share/rpcd/acl.d/luci-app-mwan3helper.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-mwan3helper": { - "description": "Grant UCI access for luci-app-mwan3helper", - "read": { - "uci": [ "mwan3helper" ] - }, - "write": { - "uci": [ "mwan3helper" ] - } - } -} diff --git a/luci-app-mwan3helper/root/usr/share/ucitrack/luci-app-mwan3helper.json b/luci-app-mwan3helper/root/usr/share/ucitrack/luci-app-mwan3helper.json deleted file mode 100644 index a022abd3a..000000000 --- a/luci-app-mwan3helper/root/usr/share/ucitrack/luci-app-mwan3helper.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "mwan3helper", - "init": "mwan3helper" -} diff --git a/luci-app-nft-qos/Makefile b/luci-app-nft-qos/Makefile deleted file mode 100644 index ae17a188f..000000000 --- a/luci-app-nft-qos/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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_NAME:=luci-app-nft-qos -PKG_VERSION:=1 -PKG_RELEASE:=1 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-nft-qos/luasrc/controller/nft-qos.lua b/luci-app-nft-qos/luasrc/controller/nft-qos.lua deleted file mode 100644 index 2fb6e1024..000000000 --- a/luci-app-nft-qos/luasrc/controller/nft-qos.lua +++ /dev/null @@ -1,52 +0,0 @@ --- 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/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua b/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua deleted file mode 100644 index 61a6d76a7..000000000 --- a/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua +++ /dev/null @@ -1,229 +0,0 @@ --- 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/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm b/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm deleted file mode 100644 index fbe19aba7..000000000 --- a/luci-app-nft-qos/luasrc/view/nft-qos/rate.htm +++ /dev/null @@ -1,187 +0,0 @@ -<%# - 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.%>
- -
- <%:Realtime Download Rate%> -
-
-
-
<%:IP Address%>
-
<%:Download Rate%>
-
<%:Bytes Total%>
-
<%:Packets Total%>
-
-
-
- <%:Collecting data...%> -
-
-
-
-
- -
- <%:Realtime Upload Rate%> -
-
-
-
<%:IP Address%>
-
<%:Upload Rate%>
-
<%:Bytes Total%>
-
<%:Packets Total%>
-
-
-
- <%:Collecting data...%> -
-
-
-
-
- -<%+footer%> diff --git a/luci-app-nft-qos/po/es/nft-qos.po b/luci-app-nft-qos/po/es/nft-qos.po deleted file mode 100644 index 4cdcb95f2..000000000 --- a/luci-app-nft-qos/po/es/nft-qos.po +++ /dev/null @@ -1,245 +0,0 @@ -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/luci-app-nft-qos/po/templates/nft-qos.pot b/luci-app-nft-qos/po/templates/nft-qos.pot deleted file mode 100644 index 8fecfa073..000000000 --- a/luci-app-nft-qos/po/templates/nft-qos.pot +++ /dev/null @@ -1,230 +0,0 @@ -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/luci-app-nft-qos/po/zh-cn/nft-qos.po b/luci-app-nft-qos/po/zh-cn/nft-qos.po deleted file mode 100644 index 37a0d1841..000000000 --- a/luci-app-nft-qos/po/zh-cn/nft-qos.po +++ /dev/null @@ -1,244 +0,0 @@ -# -# Yangfl , 2019. -# -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2020-06-07 10:45+0800\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.3.1\n" -"Last-Translator: dingpengyu \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/luci-app-nft-qos/po/zh-tw/nft-qos.po b/luci-app-nft-qos/po/zh-tw/nft-qos.po deleted file mode 100644 index 5be9d8abf..000000000 --- a/luci-app-nft-qos/po/zh-tw/nft-qos.po +++ /dev/null @@ -1,244 +0,0 @@ -# -# 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/luci-app-nft-qos/po/zh_Hans b/luci-app-nft-qos/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-nft-qos/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-nft-qos/root/etc/uci-defaults/luci-nft-qos b/luci-app-nft-qos/root/etc/uci-defaults/luci-nft-qos deleted file mode 100644 index 270233b34..000000000 --- a/luci-app-nft-qos/root/etc/uci-defaults/luci-nft-qos +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-nft-qos.json" ] && { - cat > /usr/share/ucitrack/luci-app-nft-qos.json << EEOF -{ - "config": "nft-qos", - "init": "nft-qos" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@nft-qos[-1] - add ucitrack nft-qos - set ucitrack.@nft-qos[-1].init=nft-qos - commit ucitrack -EOF - -rm -rf /tmp/luci-*cache -exit 0 diff --git a/luci-app-pgyvpn/Makefile b/luci-app-pgyvpn/Makefile deleted file mode 100644 index cf9c51257..000000000 --- a/luci-app-pgyvpn/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI Support for the PuGongYing VPN -LUCI_DEPENDS:=+lua-md5 +pgyvpn - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-pgyvpn/luasrc/controller/pgyvpn.lua b/luci-app-pgyvpn/luasrc/controller/pgyvpn.lua deleted file mode 100644 index bc80d4493..000000000 --- a/luci-app-pgyvpn/luasrc/controller/pgyvpn.lua +++ /dev/null @@ -1,139 +0,0 @@ -module("luci.controller.pgyvpn", package.seeall) - -local cbi = require ('luci.model.uci').cursor() -local h = require 'luci.http' -local dsp = require 'luci.dispatcher' - --- vpn_status: --- 99 vpn还在连接中 --- 1 已组网 --- 0 未组网 - --- err_code: --- -1 用户名密码错误 --- -2 用户名密码为空 --- --- login_status: --- 1 登录成功 对应vpn_status >= 0 && ~= 99 --- 0 未登录 对应vpn_status < 0 or == 99 - -function check() - local vpn_status = cbi:get("pgyvpn","base","vpn_status") - local login_status = cbi:get("pgyvpn","base","login_status") - local err_code = cbi:get("pgyvpn","base","err_code") - - -- logining - if vpn_status == "99" then - os.execute("sleep 1") - if login_status == "0" then - h.redirect(dsp.build_url("admin", "services", "check")) - else - h.redirect(dsp.build_url("admin", "services", "status")) - end - - -- login err or not in group - elseif vpn_status == "0" then - - -- no in group - if err_code == "0" then - h.redirect(dsp.build_url("admin", "services", "status")) - end - - -- login err and redirecut to login page - if err_code == "-1" or err_code == "-2" or err_code == "-3" or err_code == "-4" or err_code == "-5" or err_code == "-8" or err_code == "-9" or err_code == "-10" then - h.redirect(dsp.build_url("admin", "services", "pgyvpn")) - os.execute("/etc/init.d/pgyvpn stop > /dev/null"); - elseif err_code == "-11" then - h.redirect(dsp.build_url("admin", "services", "pgyvpn")) - - -- login err and redirecut to status page - elseif err_code == "-6" or err_code == "-7" then - h.redirect(dsp.build_url("admin", "services", "status")) - - else - os.execute("sleep 1") - h.redirect(dsp.build_url("admin", "services", "check")) - end - - -- login success redirecut to status page - elseif vpn_status == "1" then - h.redirect(dsp.build_url("admin", "services", "status")) - - end -end - -function login() - local user = h.formvalue("user") - local pwd = h.formvalue("pwd") - require("luci.sys") - cbi:section("pgyvpn", "base", "base", { - user = user, - pwd = pwd - }) - - cbi:set("pgyvpn","base","login_status","0") - cbi:set("pgyvpn","base","vpn_status","0") - cbi:set("pgyvpn","base","err_code","0") - cbi:set("pgyvpn","base","enable_status","1") - - if pwd == "" or user == "" then - cbi:set("pgyvpn","base","vpn_status","0") - cbi:set("pgyvpn","base","err_code","-2") - cbi:save("pgyvpn") - cbi:commit("pgyvpn") - h.redirect(dsp.build_url("admin", "services", "pgyvpn")) - else - cbi:set("pgyvpn","base","vpn_status","99") - cbi:set("pgyvpn","base","vpnid","") - cbi:save("pgyvpn") - cbi:commit("pgyvpn") - os.execute("/etc/init.d/pgyvpn restart > /dev/null"); - h.redirect(dsp.build_url("admin", "services", "check")) - end -end - -function logout() - cbi:set("pgyvpn","base","login_status","0") - cbi:set("pgyvpn","base","vpn_status","0") - cbi:set("pgyvpn","base","err_code","0") - cbi:set("pgyvpn","base","enable_status","0") -- 退出登录不启用 - cbi:save("pgyvpn") - cbi:commit("pgyvpn") - os.execute("/etc/init.d/pgyvpn stop > /dev/null"); - h.redirect(dsp.build_url("admin", "services", "pgyvpn")) -end - -function index() - local page - - if not nixio.fs.access("/etc/config/pgyvpn") then - return - end - ---1: page with cbi - -- page = entry( - -- {"admin", "services", "pgyvpn"}, - -- cbi("pgyvpn",{hideapplybtn=false,hidesavebtn=true,hideresetbtn=true}), - -- _("PGYVPN"), - -- ) - -- page.dependent = true - ---2: page with htm --- - local cbi = require ('luci.model.uci').cursor() - local vpn_status = cbi:get("pgyvpn","base","vpn_status") - local login_status = cbi:get("pgyvpn","base","login_status") - - --点击 服务->PGYVPN 时, 判断当前vpn登录情形 未登录则展示登录页面,已登录则展示vpn状态页面 - if login_status == "1" then - entry({"admin", "services", "pgyvpn"}, template("pgyvpn/status"), "蒲公英智能组网", 12) -- vpn链接成功则显示vpn状态页面(组网/未组网) - else - entry({"admin", "services", "pgyvpn"}, template("pgyvpn/vpn"), "蒲公英智能组网", 12) -- 未链接vpn 则显示登录页面 - end - - -- 定义一些基本的入口 - entry({"admin", "services", "status"}, template("pgyvpn/status"), nil) -- 状态页面 - entry({"admin", "services", "login"}, call("login"), nil) -- 登入 - entry({"admin", "services", "logout"}, call("logout"), nil) -- 登出 - entry({"admin", "services", "check"}, call("check"), nil) -- 检测vpn 链接状态 -end diff --git a/luci-app-pgyvpn/luasrc/model/cbi/pgyvpn.lua b/luci-app-pgyvpn/luasrc/model/cbi/pgyvpn.lua deleted file mode 100644 index bec6ffae9..000000000 --- a/luci-app-pgyvpn/luasrc/model/cbi/pgyvpn.lua +++ /dev/null @@ -1,25 +0,0 @@ -local sys = require("luci.sys") -local fs = require("nixio.fs") - -m = Map("pgyvpn", translate("蒲公英智能组网"), - translate("蒲公英 SD-WAN 为企业提供智能组网整体解决方案,全面覆盖互联网、专线、无线网络等" .. - "常见接入方式,帮助用户快速部署并引入多线动态 BGP 网络出口宽带,大幅提升网络链接品质," .. - "组建虚拟局域网,打破地域限制,无需公网 IP,实现各地区设备,信息互联互通")) -pgyvpn = m:section(NamedSection, "base", "") -pgyvpn.addremove=false -pgyvpn.anonymous = false - -user = pgyvpn:option(Value, "user", translate("")) -user.placeholder = "请输入SN码/贝锐账号" -user.rmempty = false -- 账号不为空 -pwd = pgyvpn:option(Value, "pwd", translate("")) -pwd.placeholder = "请输入密码" -pwd.password = true -- 以* 号的形式显示密码 -pwd.rmempty = false -- 密码不为空 - -local apply = luci.http.formvalue("cbi.apply") -if apply then - os.execute("/etc/init.d/pgyvpn restart > /dev/null"); -end - -return m diff --git a/luci-app-pgyvpn/luasrc/pgyvpn/config.lua b/luci-app-pgyvpn/luasrc/pgyvpn/config.lua deleted file mode 100644 index ee1559d86..000000000 --- a/luci-app-pgyvpn/luasrc/pgyvpn/config.lua +++ /dev/null @@ -1,30 +0,0 @@ -module "luci.pgyvpn.config" - ---[[ - 单独拿出来这些提示信息和 url 因为不通厂商会经常修改这些数据,之后直接修改此处的配置即可 ---]] - -URL_LINK = { - -- text href - CHECK_NET = {"查看网络", "https://pgy-api.oray.com/embed/authorization/redirect?sn="}, - SN_CODE = {"如何获取SN码","http://oray.cn/42D"}, - REGIST = {"注册帐号","http://oray.cn/42g"}, - OFFICIAL = {"官方网址","http://oray.cn/42d"}, - CONSOLE = {"云平台","http://oray.cn/42e"}, - HELP = {"使用帮助","http://oray.cn/42D"}, - MANAGER_NOT_ENOUGH = {"联系管理员","http://oray.cn/42k"}, - MANAGER_FORBIDEN_SERVICE = {"联系管理员","http://oray.cn/42F"}, - RENEW = {"立即续费","http://oray.cn/42s"}, - FORGET_PWD = {"忘记密码","https://console.oray.com/passport/forgot-password.html"} -} - -VPN_ERROR = { - -- ERR_USER_OR_PWD = {-1 , "帐号或密码错误", URL_LINK.MANAGER }, -- just do a test - ERR_USER_OR_PWD = {-1 , "帐号或密码错误"}, - ERR_NO_USER_OR_PWD = {-2 , "请输入帐号或密码" }, - ERR_NOT_ENOUGH_MEMBER = {-3 , "组网成员数不足", URL_LINK.MANAGER_NOT_ENOUGH }, - ERR_SERVICE = {-9 , "帐号服务已禁用", URL_LINK.MANAGER_FORBIDEN_SERVICE}, - ERR_OUT_OF_SERVICE = {-5 , "组网服务已到期", URL_LINK.RENEW}, - ERR_AUTH = {-10 , "授权无效"}, - ERR_CONNECT_SERVER = {-11 , "链接服务器出错"} -} diff --git a/luci-app-pgyvpn/luasrc/view/pgyvpn/content.htm b/luci-app-pgyvpn/luasrc/view/pgyvpn/content.htm deleted file mode 100644 index 30caaab07..000000000 --- a/luci-app-pgyvpn/luasrc/view/pgyvpn/content.htm +++ /dev/null @@ -1,9 +0,0 @@ -<% - local config = require("luci.pgyvpn.config") -%> - -

蒲公英智能组网

-
- 蒲公英SD-WAN,为个人和企业用户提供简单灵活、安全稳定的异地组网方案。让用户不受地域及公网IP限制,快速组建异地虚拟局域网,解决数据远程互访难题。
- <%=config.URL_LINK.OFFICIAL[1]%>: target="_blank" onclick="123">pgy.oray.com,前往 target="_blank" onclick="123"><%=config.URL_LINK.CONSOLE[1]%> 注册体验组网服务,点此查看 target="_blank" onclick="123"><%=config.URL_LINK.HELP[1]%> -
diff --git a/luci-app-pgyvpn/luasrc/view/pgyvpn/status.htm b/luci-app-pgyvpn/luasrc/view/pgyvpn/status.htm deleted file mode 100644 index ec3fd49cf..000000000 --- a/luci-app-pgyvpn/luasrc/view/pgyvpn/status.htm +++ /dev/null @@ -1,51 +0,0 @@ -<%+header%> -
- <%+pgyvpn/content%> - <% - local cbi = require ('luci.model.uci').cursor() - local config = require("luci.pgyvpn.config") - - local base = cbi:get("pgyvpn", "base") - local vpn_status = cbi:get("pgyvpn", "base","vpn_status") - local login_status = cbi:get("pgyvpn", "base","login_status") - local err_code = cbi:get("pgyvpn", "base","err_code") - local user = cbi:get("pgyvpn", "base","user") - local vpnid = cbi:get("pgyvpn", "base","vpnid") - local vpnpwd = cbi:get("pgyvpn", "base","vpnpwd") - local md5 = require "md5" - local pwd = cbi:get("pgyvpn","base","pwd") - local pwd_md5 = md5.sumhexa(pwd) - - if vpnid ~= nil and vpnid ~= "" then - user = vpnid - end - - if vpnpwd ~= nil and vpnpwd ~= "" then - pwd_md5 = vpnpwd - end - local href_url = config.URL_LINK.CHECK_NET[2]..user.."&auth="..pwd_md5 - %> - - <% if vpn_status == '99' then %> - - <% elseif login_status == '1' then %> -
- 状态 -
" method="post"> - - - - - -
SN码<%=user%>
组网状态<% if vpn_status == '1' then %>已组网<% elseif vpn_status == '0' and ( err_code == "0" or err_code == "-6" or err_code == "-7" or err_code == "-11" or err_code == "4294967295" ) then %>未组网<% end %>
-
- onclick="window.open('<%=href_url%>')" size="0" /> - -
-
-
- <% else %> - - <% end %> -
-<%+footer%> diff --git a/luci-app-pgyvpn/luasrc/view/pgyvpn/vpn.htm b/luci-app-pgyvpn/luasrc/view/pgyvpn/vpn.htm deleted file mode 100644 index f01ec901f..000000000 --- a/luci-app-pgyvpn/luasrc/view/pgyvpn/vpn.htm +++ /dev/null @@ -1,66 +0,0 @@ -<%+header%> -
- <% - local cbi = require ('luci.model.uci').cursor() - local config = require("luci.pgyvpn.config") - - local row = cbi:get("pgyvpn", "base") - local user = cbi:get("pgyvpn", "base","user") - local pwd = cbi:get("pgyvpn", "base","pwd") - local vpn_status = cbi:get("pgyvpn", "base","vpn_status") - local err_code = cbi:get("pgyvpn", "base","err_code") - local err_code_str = "" - %> - - <% if vpn_status == "0" then %> - <% if err_code and err_code ~= "0" then %> - -
- <% - for k ,v in pairs(config.VPN_ERROR) do - if v[1] == tonumber(err_code) then - err_code_str = v[2] - if v[3] then - err_code_str = err_code_str ..",请".. ""..v[3][1].."" - end - break - end - end - %> - <%=err_code_str%> -
- - <% cbi:set("pgyvpn", "base","err_code","0") %> - <% cbi:commit("pgyvpn") %> - - <% end %> - <% end %> - - <%+pgyvpn/content%> -
- 登录 -
" method="post"> - -
- - -
-
-
-
-<%+footer%> diff --git a/luci-app-pgyvpn/root/www/luci-static/resources/icons/pgy.png b/luci-app-pgyvpn/root/www/luci-static/resources/icons/pgy.png deleted file mode 100644 index 70d691e4830bfa6ff7074d67f6fe8e21e93fbad9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 601 zcmV-f0;c_mP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0q#jeK~y+TWBd;U z3={yY2Hbi3i=lPjTZWU5zcYX&O=UP3svLzGG(_1U3XlMX0iN?8GweA2o`HdznSp_s z5hC~xsN~CE24^z?hCL~A5HTn~F+jWj9>cZ!-xwIUfSMVhnqdH>?E60kWuR;Blqy37 z85o$LG{dwL?-{P${tC321yeH!fW&|X-+l0%q4)4Rs34LH7)!1}oXiBozyC2X`~qT- zCJvw-ATbyKc?T%>zh4{71-szE``-)-tIEx=B};)}YNQkc!(X5V5QYJe znxFp|ZoK*l;ev?s&wem4us|dttOOYjrOPvTYw$2+SqL)x@6m#|24ph~0M%Z2j${BF z{P@eTJW?9UL4bT05r`p30A?K60DVbd{Qdz7p{jucML|}C4iEsU)t7*z39tc5LTr$z z1EQ0UzJn!@!OBaYAYKFkkXP847_`KZ3;=-%K@vb`{%0uI`VuMv0aJmA>iCt<2m^qD z-4Os(3k4_%iM#G5!@V+PhCjei*Pd{n;X5!oe|`J|NtK`|{q~nZ7UMQ z;Nm9?8;$@I3lA_+f!qj;VTP}N7%UC=8IEPjgVn$QRs+sG{lU n5N0rt;sEoJ0agQu2Mi1Vsdv6ENl8hz00000NkvXXu0mjfGzaUZ diff --git a/luci-app-phtunnel/Makefile b/luci-app-phtunnel/Makefile deleted file mode 100644 index b11e80e45..000000000 --- a/luci-app-phtunnel/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI Support for the PHTunnel -LUCI_DEPENDS:=+cgi-io +curl +luci-compat +luci-lib-json +phtunnel - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-phtunnel/luasrc/controller/oray/phtunnel.lua b/luci-app-phtunnel/luasrc/controller/oray/phtunnel.lua deleted file mode 100644 index 033ea3cf8..000000000 --- a/luci-app-phtunnel/luasrc/controller/oray/phtunnel.lua +++ /dev/null @@ -1,16 +0,0 @@ -module("luci.controller.oray.phtunnel", package.seeall) - -function index() - entry({"admin", "services", "phtunnel"}, alias("admin", "services", "phtunnel", "setup"), _("Phtunnel")) - entry({"admin", "services", "phtunnel", "setup"}, cbi("oray/phtunnel_setup"), _("Setup"), 1).leaf = true - entry({"admin", "services", "phtunnel", "status"}, template("oray/phtunnel_status"), _("Status"), 2).leaf = true - -- entry({"admin", "oray", "phtunnel", "log"}, template("oray/phtunnel_log"), _("Log"), 3).leaf = true - - local node = entry({"admin", "services", "phtunnel", "inner_status"}, template("oray/phtunnel_inner_status"), nil, 4) - node.leaf = true - node.hidden = true - - node = entry({"admin", "services", "phtunnel", "log_off"}, template("oray/phtunnel_log_off"), nil, 5) - node.leaf = true - node.hidden = true -end diff --git a/luci-app-phtunnel/luasrc/model/cbi/oray/phtunnel_setup.lua b/luci-app-phtunnel/luasrc/model/cbi/oray/phtunnel_setup.lua deleted file mode 100644 index d1efb8744..000000000 --- a/luci-app-phtunnel/luasrc/model/cbi/oray/phtunnel_setup.lua +++ /dev/null @@ -1,15 +0,0 @@ -m = Map("phtunnel") -m.reset = true - -local s = m:section(NamedSection, "base", "base", translate("Base Setup")) - -enabled = s:option(Flag, "enabled", translate("Enabled")) - -enabled.rmempty = false - -m.apply_on_parse = true -m.on_after_apply = function(self) - io.popen("/etc/init.d/phtunnel restart") -end - -return m diff --git a/luci-app-phtunnel/luasrc/phtunnel.lua b/luci-app-phtunnel/luasrc/phtunnel.lua deleted file mode 100644 index a613f7125..000000000 --- a/luci-app-phtunnel/luasrc/phtunnel.lua +++ /dev/null @@ -1,114 +0,0 @@ -module("luci.phtunnel", package.seeall) -require("luci.sys") -require("luci.json") - -local function write_log(log) - luci.sys.exec("logger -t phtunnel_web '" .. string.gsub(log, "'", "") .. "'") --用gsub去掉单引号 -end - -function write_info_log(log) - write_log("[info] " .. log) -end - -function write_err_log(log) - write_log("[err] " .. log) -end - -function convert_status(status) - if status == 0 then - return "offline" - elseif status == 1 then - return "online" - elseif status == 2 then - return "loginning" - elseif status == 3 then - return "retry" - else - return "unknown" - end -end - ---获取基本信息 -function get_base_info() - local cmd = "curl -s -o- -L http://127.0.0.1:16062/ora_service/getsn" - -- write_info_log("command for get base info: " .. cmd) - - local http_result = luci.sys.exec(cmd) - if http_result then - local result = luci.json.decode(http_result) - if type(result) == "table" and result.result_code == 0 and type(result.data) == "table" then - return result.result_code, result.data.device_sn, result.data.device_sn_pwd, result.data.status, result.data.public_ip - else - write_err_log("get base info failed : result=(" .. http_result .. ")") - end - else - write_err_log("get base info failed : curl") - end -end - ---获取登录后帐号信息 -function get_login_info() - local cmd = "curl -s -o- -L http://127.0.0.1:16062/ora_service/getmgrurl" - -- write_info_log("command for get login info: " .. cmd) - - local http_result = luci.sys.exec(cmd) - if http_result then - local result = luci.json.decode(http_result) - if type(result) == "table" and result.result_code == 0 and type(result.data) == "table" then - return result.result_code, result.data.url, result.data.account - else - write_err_log("get login info failed : result=(" .. http_result .. ")") - end - else - write_err_log("get login info failed : curl") - end -end - ---获取扫描信息 -function get_qrimage(sn, pwd) - local post_json = { sn = sn, password = pwd } - local post_file = "/tmp/phtunnel_qrimg_post" - local f = io.open(post_file, "w") - if f then - f:write(luci.json.encode(post_json)) - f:close() - - local cmd = "curl -s -X POST -o- -d '@" .. post_file .. "' --header 'Content-Type: application/json' -k 'https://hsk-api.oray.com/devices/qrcode'" - -- write_info_log("command for get qrcode image : " .. cmd) - - local post_data = "" - local http_result = luci.sys.exec(cmd) - os.remove(post_file) - if http_result then - local result = luci.json.decode(http_result) - if type(result) == "table" then - return result.qrcode, result.qrcodeimg, result.ttl - else - write_err_log("get qrcode image failed : result=(" .. http_result .. ")") - end - end - else - write_log("get qrcode image failed : curl") - end -end - ---解绑 -function unbind_account(sn, pwd) - local post_json = { sn = sn, password = pwd } - local post_file = "/tmp/phtunnel_unbind_post" - local f = io.open(post_file, "w") - if f then - f:write(luci.json.encode(post_json)) - f:close() - - local cmd = "curl -s -X POST -o- -d '@" .. post_file .. "' --header 'Content-Type: application/json' -k 'https://hsk-api.oray.com/devices/unbinding' -f || echo fail" - -- write_info_log("command for get unbind : " .. cmd) - - local post_data = "" - local http_result = luci.sys.exec(cmd) - --os.remove(post_file) - return #http_result == 0 - else - write_log("unbind account failed : curl") - end -end diff --git a/luci-app-phtunnel/luasrc/view/oray/phtunnel_inner_status.htm b/luci-app-phtunnel/luasrc/view/oray/phtunnel_inner_status.htm deleted file mode 100644 index 81b3c8d80..000000000 --- a/luci-app-phtunnel/luasrc/view/oray/phtunnel_inner_status.htm +++ /dev/null @@ -1,31 +0,0 @@ -<% -require("luci.json") -require("luci.sys") -require("luci.phtunnel") - -local write_err_log = luci.phtunnel.write_err_log -local manager_url - -local function main() - --登录信息查看 - local login_info_code - login_info_code, manager_url = luci.phtunnel.get_login_info() - - if not login_info_code then - write_err_log("get manager url failed : inner_status") - return - end - - if login_info_code ~= 0 then - write_err_log("get manger url failed and code = " .. login_info_code .. " : inner_status") - return - end - -end - -main() -%> - -{ - "url" : "<%=(manager_url and manager_url or "")%>" -} diff --git a/luci-app-phtunnel/luasrc/view/oray/phtunnel_log.htm b/luci-app-phtunnel/luasrc/view/oray/phtunnel_log.htm deleted file mode 100644 index 9aad0da68..000000000 --- a/luci-app-phtunnel/luasrc/view/oray/phtunnel_log.htm +++ /dev/null @@ -1,17 +0,0 @@ -<%+header%> - -<% -require("luci.sys") -require("luci.model.uci") - -local uci = luci.model.uci.cursor() -local log_file = uci:get("phtunnel", "log", "path") - -if not log_file or log_file == "" then - log_file = "/var/log/oraybox/phtunnel.log" -end -%> - -<%=string.gsub(luci.sys.exec("tail -n 5000 " .. log_file), "\n", "
")%> - -<%+footer%> diff --git a/luci-app-phtunnel/luasrc/view/oray/phtunnel_log_off.htm b/luci-app-phtunnel/luasrc/view/oray/phtunnel_log_off.htm deleted file mode 100644 index 9ea78d60c..000000000 --- a/luci-app-phtunnel/luasrc/view/oray/phtunnel_log_off.htm +++ /dev/null @@ -1,34 +0,0 @@ -<% -require("luci.phtunnel") - -local ret_code = 0 -local ret_msg = "" - -local function main() - local base_info_code, device_sn, device_pwd, device_status, public_ip = luci.phtunnel.get_base_info() - if not base_info_code then - ret_code = 1 - ret_msg = "phtunnel is not running" - return - end - - if base_info_code ~= 0 then - ret_code = 2 - ret_msg = "get sn failed and code = " .. base_info_code - return - end - - if not luci.phtunnel.unbind_account(device_sn, device_pwd) then - ret_code = 3 - ret_msg = "unbind failed" - return - end -end - -main() -%> - -{ - "code" : "<%=ret_code%>", - "error_msg" : "<%=_(ret_msg)%>" -} diff --git a/luci-app-phtunnel/luasrc/view/oray/phtunnel_status.htm b/luci-app-phtunnel/luasrc/view/oray/phtunnel_status.htm deleted file mode 100644 index 2ef3c2a9b..000000000 --- a/luci-app-phtunnel/luasrc/view/oray/phtunnel_status.htm +++ /dev/null @@ -1,218 +0,0 @@ -<%+header%> - -<% -require("luci.sys") -require("luci.model.uci") - -local uci = luci.model.uci.cursor() -local log_file = uci:get("phtunnel", "log", "path") - -if not log_file or log_file == "" then - log_file = "/var/log/oraybox/phtunnel.log" -end -%> - - - - -<% -require("luci.phtunnel") -local utl = require "luci.util" - -local function fileExists(path) - local file = io.open(path, "rb") - if file then file:close() end - return file ~= nil -end - -local function grant_download_permission() - local dsp = require "luci.dispatcher" - local sid = dsp.context.authsession - local access, _ = utl.ubus("session", "access", - {ubus_rpc_session = sid, - scope = "file", - object = log_file, - ["function"] = "read"}) - - if type(access) == "table" and access.access == false then - local acl = {} - acl[1] = {} - acl[1][1] = log_file - acl[1][2] = "read" - utl.ubus("session", "grant", { ubus_rpc_session = sid, scope = "file", objects = acl}) - end -end - -local function display_download_log() - if fileExists(log_file) then - print('Download Log File
') - grant_download_permission() - end -end - ---输出二 -local function display_qrimage(sn, pwd) - local qrcode, qrcodeimg, ttl = luci.phtunnel.get_qrimage(sn, pwd) - if qrcode and qrcodeimg then - print("
") - end -end - -local function main() - local base_info_code, device_sn, device_pwd, device_status, public_ip = luci.phtunnel.get_base_info() - if not base_info_code then - print("phtunnel is not running") - return - end - - if base_info_code ~= 0 then - print("get sn failed and code = " .. base_info_code) - return - end - - --输出基本信息 - print('SN:' .. (device_sn and device_sn or "no sn") .. "
") - print('Device Status:' .. (device_status and luci.phtunnel.convert_status(device_status) or "no status") .. "
") - print('Public IP:' .. (public_ip and public_ip or "no public ip") .. "
") - - --登录信息查看 - local login_info_code, url, bind_account = luci.phtunnel.get_login_info() - if not login_info_code then - print("get manager url failed") - return - end - - if login_info_code ~= 0 then - print("get manger url failed and code = " .. login_info_code) - return - end - - if not url or url == "" then - display_qrimage(device_sn, device_pwd) - print("") - return - end - - print("bind account:" .. (bind_account and bind_account or "*") .. "") - print("(log off)
"); - print("manager link
"); -end - -main() -display_download_log() -%> - -<%+footer%> diff --git a/luci-app-phtunnel/po/zh-cn/phtunnel.po b/luci-app-phtunnel/po/zh-cn/phtunnel.po deleted file mode 100644 index 269cfc367..000000000 --- a/luci-app-phtunnel/po/zh-cn/phtunnel.po +++ /dev/null @@ -1,8 +0,0 @@ -msgid "Phtunnel" -msgstr "花生壳内网穿透" - -msgid "Base Setup" -msgstr "基本设置" - -msgid "Setup" -msgstr "设置" diff --git a/luci-app-phtunnel/po/zh_Hans b/luci-app-phtunnel/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-phtunnel/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-phtunnel/root/usr/share/rpcd/acl.d/luci-app-phtunnel.json b/luci-app-phtunnel/root/usr/share/rpcd/acl.d/luci-app-phtunnel.json deleted file mode 100644 index 9e9f1c130..000000000 --- a/luci-app-phtunnel/root/usr/share/rpcd/acl.d/luci-app-phtunnel.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "phtunnel-luci-app": { - "description": "phtunnel luci interface", - "read": { - "cgi-io": [ "download" ] - } - } -} diff --git a/luci-app-pppwn/Makefile b/luci-app-pppwn/Makefile deleted file mode 100644 index 639f8ed0e..000000000 --- a/luci-app-pppwn/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2022 Openwrt.org -# -# This is free software, licensed under the Apache License, Version 2.0 . -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI support for PPPWN -LUCI_DEPENDS:=+pppwn-cpp -LUCI_PKGARCH:=all -PKG_NAME:=luci-app-pppwn -PKG_VERSION:=1 -PKG_RELEASE:=2 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-pppwn/luasrc/controller/pppwn.lua b/luci-app-pppwn/luasrc/controller/pppwn.lua deleted file mode 100644 index c71b78ef2..000000000 --- a/luci-app-pppwn/luasrc/controller/pppwn.lua +++ /dev/null @@ -1,21 +0,0 @@ -module("luci.controller.pppwn", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/pppwn") then - return - end - - entry({"admin", "services", "pppwn"}, alias("admin", "services", "pppwn", "general"), _("PS4 PPP PWN"), 80) - entry({"admin", "services", "pppwn", "general"}, cbi("pppwn/settings"), _("Base Setting"), 1) - entry({"admin", "services", "pppwn", "log"}, form("pppwn/info"), _("Log"), 2) - - entry({"admin", "services", "pppwn", "status"}, call("status")).leaf = true -end - -function status() - local e = {} - e.running = luci.sys.call("pgrep pppwn >/dev/null") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end - diff --git a/luci-app-pppwn/luasrc/model/cbi/pppwn/info.lua b/luci-app-pppwn/luasrc/model/cbi/pppwn/info.lua deleted file mode 100644 index 5cb2a7393..000000000 --- a/luci-app-pppwn/luasrc/model/cbi/pppwn/info.lua +++ /dev/null @@ -1,14 +0,0 @@ -local fs = require "nixio.fs" -local conffile = "/var/log/pppwn.log" - -f = SimpleForm("logview") - -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 19 -function t.cfgvalue() - return fs.readfile(conffile) or "" -end -t.readonly = "readonly" - -return f diff --git a/luci-app-pppwn/luasrc/model/cbi/pppwn/settings.lua b/luci-app-pppwn/luasrc/model/cbi/pppwn/settings.lua deleted file mode 100644 index 74ced9c6e..000000000 --- a/luci-app-pppwn/luasrc/model/cbi/pppwn/settings.lua +++ /dev/null @@ -1,34 +0,0 @@ -local sys = require "luci.sys" - -m = Map("pppwn", "PPPwn - PlayStation 4 PPPoE RCE", translate("PPPwn is a kernel remote code execution exploit for PlayStation 4 up to FW 11.00.")) - -m:section(SimpleSection).template = "pppwn/pppwn_status" - -s = m:section(TypedSection, "pppwn") -s.addremove = false -s.anonymous = true - -enable=s:option(Flag, "enable", translate("Enabled")) -enable.rmempty = false - - -source=s:option(Value, "source", translate("PPPwn Running Port")) -source.datatype = "network" -source.default = "br-lan" -source.rmempty = false -for _, e in ipairs(sys.net.devices()) do - if e ~= "lo" then source:value(e) end -end - -fwver=s:option(Value, "fwver", translate("PlayStation 4 System Version")) -fwver.default = "1100" -fwver.rmempty = false -fwver:value("700", translate("7.00")) -fwver:value("900", translate("9.00")) -fwver:value("1100", translate("11.00")) -fwver.description = translate("1100 means Ver 11.00 etc.") - -goldhen = s:option(DummyValue, "goldhen", translate("Download GoldHEN Payload BIN")) -goldhen.description = translate(""..translate("Copy goldhen.bin to the root directory of an exfat/fat32 USB and insert it into your PS4").."") - -return m diff --git a/luci-app-pppwn/luasrc/view/pppwn/pppwn_status.htm b/luci-app-pppwn/luasrc/view/pppwn/pppwn_status.htm deleted file mode 100644 index f2552d829..000000000 --- a/luci-app-pppwn/luasrc/view/pppwn/pppwn_status.htm +++ /dev/null @@ -1,23 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
- diff --git a/luci-app-pppwn/po/zh-cn/pppwn.po b/luci-app-pppwn/po/zh-cn/pppwn.po deleted file mode 100644 index 9142c0e3c..000000000 --- a/luci-app-pppwn/po/zh-cn/pppwn.po +++ /dev/null @@ -1,20 +0,0 @@ -msgid "PS4 PPP PWN" -msgstr "PS4 PPP 越狱服务器" - -msgid "PPPwn is a kernel remote code execution exploit for PlayStation 4 up to FW 11.00." -msgstr "PPPwn 是一个可以越狱 PlayStation 4 的PPP服务器,目前最高支持系统 11.00 版本" - -msgid "PPPwn Running Port" -msgstr "PPPwn 监听网络接口" - -msgid "PlayStation 4 System Version" -msgstr "PlayStation 4 系统版本" - -msgid "1100 means Ver 11.00 etc." -msgstr "1100 代表 11.00 版本,以此类推" - -msgid "Download GoldHEN Payload BIN" -msgstr "点击下载 GoldHEN Payload 二进制文件" - -msgid "Copy goldhen.bin to the root directory of an exfat/fat32 USB and insert it into your PS4." -msgstr "首次越狱请把 goldhen.bin 文件复制到一个 exfat/fat32 U盘根目录,并插入您的 PS4" diff --git a/luci-app-pppwn/po/zh_Hans b/luci-app-pppwn/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-pppwn/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-pppwn/root/etc/config/pppwn b/luci-app-pppwn/root/etc/config/pppwn deleted file mode 100644 index 59c0a1353..000000000 --- a/luci-app-pppwn/root/etc/config/pppwn +++ /dev/null @@ -1,6 +0,0 @@ - -config pppwn 'config' - option port 'br-lan' - option fwver '1100' - option enable '0' - diff --git a/luci-app-pppwn/root/etc/init.d/pppwn b/luci-app-pppwn/root/etc/init.d/pppwn deleted file mode 100755 index 168fd2a8d..000000000 --- a/luci-app-pppwn/root/etc/init.d/pppwn +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh /etc/rc.common -# Copyright (C) 2008-2022 OpenWrt.org - -START=99 - -PROG=/usr/bin/pppwn - -start() { - config_load "pppwn" - - config_get "port" "config" "port" - config_get "fwver" "config" "fwver" - - local enable - config_get_bool enable config enable 0 - - [ "${enable}" -gt 0 ] || return - - $PROG --interface $port --fw $fwver --stage1 "/etc/pppwn/stage1.bin" --stage2 "/etc/pppwn/stage2.bin" -a > /var/log/pppwn.log & - -} - -stop() { - ps -w | grep -v "grep" | grep "$PROG" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 & - echo "" > /var/log/pppwn.log -} \ No newline at end of file diff --git a/luci-app-pppwn/root/etc/uci-defaults/luci-pppwn b/luci-app-pppwn/root/etc/uci-defaults/luci-pppwn deleted file mode 100644 index 5c2cc2def..000000000 --- a/luci-app-pppwn/root/etc/uci-defaults/luci-pppwn +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-pppwn.json" ] && { - cat > /usr/share/ucitrack/luci-app-pppwn.json << EEOF -{ - "config": "pppwn", - "init": "pppwn" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@pppwn[-1] - add ucitrack pppwn - set ucitrack.@pppwn[-1].init=pppwn - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache - -exit 0 diff --git a/luci-app-pppwn/root/usr/share/rpcd/acl.d/luci-app-pppwn.json b/luci-app-pppwn/root/usr/share/rpcd/acl.d/luci-app-pppwn.json deleted file mode 100644 index 3aacc62a3..000000000 --- a/luci-app-pppwn/root/usr/share/rpcd/acl.d/luci-app-pppwn.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-pppwn": { - "description": "Grant UCI access for luci-app-pppwn", - "read": { - "uci": [ "pppwn" ] - }, - "write": { - "uci": [ "pppwn" ] - } - } -} diff --git a/luci-app-pppwn/root/usr/share/ucitrack/luci-app-pppwn.json b/luci-app-pppwn/root/usr/share/ucitrack/luci-app-pppwn.json deleted file mode 100644 index 1dd0de021..000000000 --- a/luci-app-pppwn/root/usr/share/ucitrack/luci-app-pppwn.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "pppwn", - "init": "pppwn" -} diff --git a/luci-app-pppwn/root/www/goldhen.bin b/luci-app-pppwn/root/www/goldhen.bin deleted file mode 100644 index 70fdd34ccfabbdc8b8a6bbe6cdeeca351a159060..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296112 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�GDs?*TV;_0HE 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�i*!|CMsV7-TF|fpGIRTJtQ_T?X2 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

T%i0VPG#b#kr7wK1 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 URa8}UR#0+MQFK&wRX_880O1$UMF0Q* diff --git a/luci-app-pptp-server/Makefile b/luci-app-pptp-server/Makefile index 665513a10..618e72cbd 100644 --- a/luci-app-pptp-server/Makefile +++ b/luci-app-pptp-server/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 2016 Openwrt.org +# Copyright (C) 2018-2021 Lienol # # This is free software, licensed under the Apache License, Version 2.0 . # @@ -6,12 +6,10 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for PPTP VPN Server -LUCI_DEPENDS:=+pptpd +kmod-mppe +ppp +LUCI_DEPENDS:=+pptpd +kmod-mppe +ppp +luci-lib-jsonc LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-pptp-server -PKG_VERSION:=1.0 -PKG_RELEASE:=1 +PKG_VERSION:=20211223 +PKG_RELEASE:=2 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-pptp-server/luasrc/controller/pptp-server.lua b/luci-app-pptp-server/luasrc/controller/pptp-server.lua deleted file mode 100644 index 6cf247294..000000000 --- a/luci-app-pptp-server/luasrc/controller/pptp-server.lua +++ /dev/null @@ -1,18 +0,0 @@ -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 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/luci-app-pptp-server/luasrc/controller/pptpd.lua b/luci-app-pptp-server/luasrc/controller/pptpd.lua new file mode 100644 index 000000000..6b71f2ca1 --- /dev/null +++ b/luci-app-pptp-server/luasrc/controller/pptpd.lua @@ -0,0 +1,21 @@ +-- Copyright 2018-2020 Lienol +module("luci.controller.pptpd", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/luci-app-pptp-server") then return end + + entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false + entry({"admin", "vpn", "pptpd"}, alias("admin", "vpn", "pptpd", "settings"), _("PPTP VPN Server"), 48) + entry({"admin", "vpn", "pptpd", "settings"}, cbi("pptpd/settings"), _("General Settings"), 10).leaf = true + entry({"admin", "vpn", "pptpd", "users"}, cbi("pptpd/users"), _("Users Manager"), 20).leaf = true + entry({"admin", "vpn", "pptpd", "user"}, cbi("pptpd/user")).leaf = true + entry({"admin", "vpn", "pptpd", "online"}, cbi("pptpd/online"), _("Online Users"), 30).leaf = true + entry({"admin", "vpn", "pptpd", "status"}, call("status")).leaf = true +end + +function status() + local e = {} + e.status = luci.sys.call("pidof %s >/dev/null" % "pptpd") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) +end diff --git a/luci-app-pptp-server/luasrc/model/cbi/pptp-server.lua b/luci-app-pptp-server/luasrc/model/cbi/pptp-server.lua deleted file mode 100644 index b28fc632e..000000000 --- a/luci-app-pptp-server/luasrc/model/cbi/pptp-server.lua +++ /dev/null @@ -1,37 +0,0 @@ -mp = Map("pptpd") -mp.title = translate("PPTP VPN Server") -mp.description = translate("PPTP VPN Server connectivity using the native built-in VPN Client on Windows/Linux or Android") - -mp:section(SimpleSection).template = "pptp-server/pptp-server_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.description = translate("LAN DHCP reserved start-to-end IP addresses with the same subnet mask") -clientip.datatype = "string" - -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/luci-app-pptp-server/luasrc/model/cbi/pptpd/online.lua b/luci-app-pptp-server/luasrc/model/cbi/pptpd/online.lua new file mode 100644 index 000000000..a99a0d4c6 --- /dev/null +++ b/luci-app-pptp-server/luasrc/model/cbi/pptpd/online.lua @@ -0,0 +1,84 @@ +local o = require "luci.dispatcher" +local fs = require "nixio.fs" +local jsonc = require "luci.jsonc" + +local sessions = {} +local session_path = "/var/etc/pptpd/session" +if fs.access(session_path) then + for filename in fs.dir(session_path) do + local session_file = session_path .. "/" .. filename + local file = io.open(session_file, "r") + local t = jsonc.parse(file:read("*a")) + if t then + t.session_file = session_file + sessions[#sessions + 1] = t + end + file:close() + end +end + +local blacklist = {} +local firewall_user_path = "/etc/firewall.user" +if fs.access(firewall_user_path) then + for line in io.lines(firewall_user_path) do + local m = line:match('pptpd%-blacklist%-([^\n]+)') + if m then + local t = {} + t.ip = m + blacklist[#blacklist + 1] = t + end + end +end + +f = SimpleForm("processes", translate("PPTP VPN Server")) +f.reset = false +f.submit = false +f.description = translate("Simple, quick and convenient PPTP VPN, universal across the platform") + +t = f:section(Table, sessions, translate("Online Users")) +t:option(DummyValue, "username", translate("Username")) +t:option(DummyValue, "interface", translate("Interface")) +t:option(DummyValue, "ip", translate("Client IP")) +t:option(DummyValue, "remote_ip", translate("IP address")) +t:option(DummyValue, "login_time", translate("Login Time")) + +_blacklist = t:option(Button, "_blacklist", translate("Blacklist")) +function _blacklist.render(e, t, a) + e.title = translate("Add to Blacklist") + e.inputstyle = "remove" + Button.render(e, t, a) +end +function _blacklist.write(t, s) + local e = t.map:get(s, "remote_ip") + luci.util.execi("echo 'iptables -I INPUT -s %s -p tcp --dport 1723 -j DROP ## pptpd-blacklist-%s' >> /etc/firewall.user" % {e, e}) + luci.util.execi("iptables -I INPUT -s %s -p tcp --dport 1723 -j DROP" % {e}) + luci.util.execi("rm -f " .. t.map:get(s, "session_file")) + null, t.tag_error[s] = luci.sys.process.signal(t.map:get(s, "pid"), 9) + luci.http.redirect(o.build_url("admin/vpn/pptpd/online")) +end + +_kill = t:option(Button, "_kill", translate("Forced offline")) +_kill.inputstyle = "reset" +function _kill.write(t, s) + luci.util.execi("rm -f " .. t.map:get(s, "session_file")) + null, t.tag_error[t] = luci.sys.process.signal(t.map:get(s, "pid"), 9) + luci.http.redirect(o.build_url("admin/vpn/pptpd/online")) +end + +t = f:section(Table, blacklist, translate("Blacklist")) +t:option(DummyValue, "ip", translate("IP address")) + +_blacklist2 = t:option(Button, "_blacklist2", translate("Blacklist")) +function _blacklist2.render(e, t, a) + e.title = translate("Remove from Blacklist") + e.inputstyle = "apply" + Button.render(e, t, a) +end +function _blacklist2.write(t, s) + local e = t.map:get(s, "ip") + luci.util.execi("sed -i -e '/## pptpd-blacklist-%s/d' /etc/firewall.user" % {e}) + luci.util.execi("iptables -D INPUT -s %s -p tcp --dport 1723 -j DROP" % {e}) + luci.http.redirect(o.build_url("admin/vpn/pptpd/online")) +end + +return f diff --git a/luci-app-pptp-server/luasrc/model/cbi/pptpd/settings.lua b/luci-app-pptp-server/luasrc/model/cbi/pptpd/settings.lua new file mode 100644 index 000000000..b5f410359 --- /dev/null +++ b/luci-app-pptp-server/luasrc/model/cbi/pptpd/settings.lua @@ -0,0 +1,40 @@ +local s = require "luci.sys" +local net = require"luci.model.network".init() +local ifaces = s.net:devices() +local m, s, o +m = Map("luci-app-pptp-server", translate("PPTP VPN Server")) +m.description = translate("Simple, quick and convenient PPTP VPN, universal across the platform") +m.template = "pptpd/index" + +s = m:section(TypedSection, "service") +s.anonymous = true + +o = s:option(DummyValue, "pptpd_status", translate("Current Condition")) +o.template = "pptpd/status" +o.value = translate("Collecting data...") + +o = s:option(Flag, "enabled", translate("Enabled")) +o.rmempty = false + +o = s:option(Value, "localip", translate("Server IP"), translate("VPN Server IP address, it not required.")) +o.datatype = "ipaddr" +o.placeholder = translate("192.168.1.2") +o.rmempty = true +o.default = "192.168.1.2" + +o = s:option(Value, "remoteip", translate("Client IP"), translate("VPN Client IP address, it not required.")) +o.placeholder = translate("192.168.1.10-20") +o.rmempty = true +o.default = "192.168.1.10-20" + +--[[ +o = s:option(Value, "dns", translate("DNS IP address"), translate("This will be sent to the client, it not required.")) +o.placeholder = translate("192.168.1.1") +o.datatype = "ipaddr" +o.rmempty = true +o.default = "192.168.1.1" +]]-- + +o = s:option(Flag, "mppe", translate("Enable MPPE Encryption"), translate("Allows 128-bit encrypted connection.")) +o.rmempty = false +return m diff --git a/luci-app-pptp-server/luasrc/model/cbi/pptpd/user.lua b/luci-app-pptp-server/luasrc/model/cbi/pptpd/user.lua new file mode 100644 index 000000000..3677b7083 --- /dev/null +++ b/luci-app-pptp-server/luasrc/model/cbi/pptpd/user.lua @@ -0,0 +1,32 @@ +local d = require "luci.dispatcher" + +m = Map("luci-app-pptp-server", translate("Users Manager")) +m.redirect = d.build_url("admin", "vpn", "pptpd", "users") + +s = m:section(NamedSection, arg[1], "users", "") +s.addremove = false +s.anonymous = true + +o = s:option(Flag, "enabled", translate("Enabled")) +o.default = 1 +o.rmempty = false + +o = s:option(Value, "username", translate("Username")) +o.placeholder = translate("Username") +o.rmempty = false + +o = s:option(Value, "password", translate("Password")) +o.placeholder = translate("Password") +o.rmempty = false + +o = s:option(Value, "ipaddress", translate("IP address")) +o.placeholder = translate("Automatically") +o.datatype = "ip4addr" +o.rmempty = true + +o = s:option(DynamicList, "routes", translate("Static Routes")) +o.placeholder = "192.168.10.0/24" +o.datatype = "ipmask4" +o.rmempty = true + +return m diff --git a/luci-app-pptp-server/luasrc/model/cbi/pptpd/users.lua b/luci-app-pptp-server/luasrc/model/cbi/pptpd/users.lua new file mode 100644 index 000000000..d11ba5853 --- /dev/null +++ b/luci-app-pptp-server/luasrc/model/cbi/pptpd/users.lua @@ -0,0 +1,33 @@ +local d = require "luci.dispatcher" + +m = Map("luci-app-pptp-server", translate("PPTP VPN Server")) +m.description = translate("Simple, quick and convenient PPTP VPN, universal across the platform") + +s = m:section(TypedSection, "users", translate("Users Manager")) +s.addremove = true +s.anonymous = true +s.template = "cbi/tblsection" +s.extedit = d.build_url("admin", "vpn", "pptpd", "user", "%s") +function s.create(e, t) + t = TypedSection.create(e, t) + luci.http.redirect(e.extedit:format(t)) +end + +o = s:option(Flag, "enabled", translate("Enabled")) +o.default = 1 +o.rmempty = false + +o = s:option(Value, "username", translate("Username")) +o.placeholder = translate("Username") +o.rmempty = false + +o = s:option(Value, "password", translate("Password")) +o.placeholder = translate("Password") +o.rmempty = false + +o = s:option(Value, "ipaddress", translate("IP address")) +o.placeholder = translate("Automatically") +o.datatype = "ip4addr" +o.rmempty = true + +return m diff --git a/luci-app-pptp-server/luasrc/view/pptp-server/pptp-server_status.htm b/luci-app-pptp-server/luasrc/view/pptp-server/pptp-server_status.htm deleted file mode 100644 index dd4446e0a..000000000 --- a/luci-app-pptp-server/luasrc/view/pptp-server/pptp-server_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -

-

- <%:Collecting data...%> -

-
diff --git a/luci-app-pptp-server/luasrc/view/pptpd/index.htm b/luci-app-pptp-server/luasrc/view/pptpd/index.htm new file mode 100644 index 000000000..ac1f295ed --- /dev/null +++ b/luci-app-pptp-server/luasrc/view/pptpd/index.htm @@ -0,0 +1,13 @@ +<% include("cbi/map") %> + diff --git a/luci-app-pptp-server/luasrc/view/pptpd/status.htm b/luci-app-pptp-server/luasrc/view/pptpd/status.htm new file mode 100644 index 000000000..28118586f --- /dev/null +++ b/luci-app-pptp-server/luasrc/view/pptpd/status.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> +<%=pcdata(self:cfgvalue(section) or self.default or "")%> +<%+cbi/valuefooter%> diff --git a/luci-app-pptp-server/po/zh-cn/pptp.po b/luci-app-pptp-server/po/zh-cn/pptp.po deleted file mode 100644 index c1055f512..000000000 --- a/luci-app-pptp-server/po/zh-cn/pptp.po +++ /dev/null @@ -1,53 +0,0 @@ -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 Android" -msgstr "使用 Windows/Linux 或者 Android 内置的 PPTP VPN 客户端进行连接" diff --git a/luci-app-pptp-server/po/zh-cn/pptpd.po b/luci-app-pptp-server/po/zh-cn/pptpd.po new file mode 100644 index 000000000..4ccdd9fd0 --- /dev/null +++ b/luci-app-pptp-server/po/zh-cn/pptpd.po @@ -0,0 +1,71 @@ +msgid "PPTP VPN Server" +msgstr "PPTP VPN 服务器" + +msgid "Simple, quick and convenient PPTP VPN, universal across the platform" +msgstr "简单快捷方便的PPTP VPN,全平台通用。" + +msgid "Current Condition" +msgstr "当前状态" + +msgid "General settings" +msgstr "基本设置" + +msgid "Enabled" +msgstr "启用" + +msgid "Server IP" +msgstr "服务器 IP 地址" + +msgid "VPN Server IP address, it not required." +msgstr "VPN 服务器远程地址,留空将自动设置。" + +msgid "Client IP" +msgstr "客户端 IP 地址" + +msgid "VPN Client IP address, it not required." +msgstr "分配给客户端的 IP 地址范围,留空将自动设置。" + +msgid "Enable MPPE Encryption" +msgstr "启用MPPE 加密" + +msgid "Allows 128-bit encrypted connection." +msgstr "允许使用 128 位加密连接。" + +msgid "Users Manager" +msgstr "用户管理" + +msgid "Username" +msgstr "用户名" + +msgid "Password" +msgstr "密码" + +msgid "IP address" +msgstr "IP 地址" + +msgid "Automatically" +msgstr "自动分配" + +msgid "Online Users" +msgstr "在线用户" + +msgid "Login Time" +msgstr "登录时间" + +msgid "Blacklist" +msgstr "黑名单" + +msgid "Add to Blacklist" +msgstr "加入黑名单" + +msgid "Remove from Blacklist" +msgstr "移出黑名单" + +msgid "Forced offline" +msgstr "强制下线" + +msgid "NOT RUNNING" +msgstr "未运行" + +msgid "RUNNING" +msgstr "运行中" \ No newline at end of file diff --git a/luci-app-pptp-server/root/etc/config/luci-app-pptp-server b/luci-app-pptp-server/root/etc/config/luci-app-pptp-server new file mode 100644 index 000000000..044cce629 --- /dev/null +++ b/luci-app-pptp-server/root/etc/config/luci-app-pptp-server @@ -0,0 +1,7 @@ + +config service 'pptpd' + option mppe '1' + option localip '192.168.2.1' + option remoteip '192.168.2.10-20' + option enabled '0' + diff --git a/luci-app-pptp-server/root/etc/config/pptpd b/luci-app-pptp-server/root/etc/config/pptpd deleted file mode 100644 index c55cfa60c..000000000 --- a/luci-app-pptp-server/root/etc/config/pptpd +++ /dev/null @@ -1,12 +0,0 @@ - -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/luci-app-pptp-server/root/etc/init.d/luci-app-pptp-server b/luci-app-pptp-server/root/etc/init.d/luci-app-pptp-server new file mode 100755 index 000000000..a33c98fd3 --- /dev/null +++ b/luci-app-pptp-server/root/etc/init.d/luci-app-pptp-server @@ -0,0 +1,127 @@ +#!/bin/sh /etc/rc.common + +START=99 +CONFIG="luci-app-pptp-server" +PPTP_PATH=/var/etc/pptpd +PPTP_CONFIG_FILE=${PPTP_PATH}/pptpd.conf +PPTP_OPTIONS_FILE=${PPTP_PATH}/options.pptpd +CHAP_SECRETS=/etc/ppp/chap-secrets + +localip=$(uci -q get ${CONFIG}.@service[0].localip) +[ -z "${localip}" ] && localip="172.16.100.1" + +ipt_flag="PPTP VPN Server" + +get_enabled_anonymous_secs() { + uci -q show "${CONFIG}" | grep "${1}\[.*\.enabled='1'" | cut -d '.' -sf2 +} + +ipt_rule() { + if [ "$1" = "add" ]; then + iptables -w -t nat -I POSTROUTING -s ${localip%.*}.0/24 -m comment --comment "${ipt_flag}" -j MASQUERADE 2>/dev/null + iptables -w -t nat -I POSTROUTING -d ${localip%.*}.0/24 -m comment --comment "${ipt_flag}" -j MASQUERADE 2>/dev/null + iptables -w -I FORWARD -s ${localip%.*}.0/24 -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null + iptables -w -I INPUT -p tcp --dport 1723 -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null + iptables -w -t mangle -I OUTPUT -p tcp --sport 1723 -m comment --comment "${ipt_flag}" -j RETURN 2>/dev/null + else + ipt_del() { + for i in $(seq 1 $($1 -nL $2 | grep -c "${ipt_flag}")); do + local index=$($1 --line-number -nL $2 | grep "${ipt_flag}" | head -1 | awk '{print $1}') + $1 -w -D $2 $index 2>/dev/null + done + } + ipt_del "iptables" "FORWARD" + ipt_del "iptables" "INPUT" + ipt_del "iptables -t nat" "POSTROUTING" + ipt_del "iptables -t mangle" "OUTPUT" + fi +} + +gen_include() { + echo '#!/bin/sh' > /var/etc/${CONFIG}.include + extract_rules() { + echo "*$1" + iptables-save -t $1 | grep "${ipt_flag}" | \ + sed -e "s/^-A \(INPUT\)/-I \1 1/" + echo 'COMMIT' + } + cat <<-EOF >> /var/etc/${CONFIG}.include + iptables-save -c | grep -v "${ipt_flag}" | iptables-restore -c + iptables-restore -n <<-EOT + $(extract_rules filter) + $(extract_rules nat) + EOT + exit 0 + EOF + return 0 +} + +start() { + local enabled=$(uci -q get ${CONFIG}.@service[0].enabled) + [ "${enabled}" -eq 1 ] || return 1 + touch ${CHAP_SECRETS} + mkdir -p ${PPTP_PATH} + + cp /etc/ppp/options.pptpd ${PPTP_OPTIONS_FILE} + sed -i '/mppe/d' ${PPTP_OPTIONS_FILE} 2>/dev/null + sed -i '/ms-dns/d' ${PPTP_OPTIONS_FILE} 2>/dev/null + sed -i '/name/d' ${PPTP_OPTIONS_FILE} 2>/dev/null + echo "name pptp-server">> ${PPTP_OPTIONS_FILE} + + local mppe=$(uci -q get ${CONFIG}.@service[0].mppe) + [ -n "${mppe}" ] && [ "${mppe}" -eq 1 ] && echo "mppe required,no40,no56,stateless" >> ${PPTP_OPTIONS_FILE} + + echo "ms-dns ${localip}">> ${PPTP_OPTIONS_FILE} + + cp /etc/pptpd.conf ${PPTP_CONFIG_FILE} + sed -i '/localip/d' ${PPTP_CONFIG_FILE} 2>/dev/null + sed -i '/remoteip/d' ${PPTP_CONFIG_FILE} 2>/dev/null + sed -i '/option/d' ${PPTP_CONFIG_FILE} 2>/dev/null + sed -i '/name/d' ${PPTP_CONFIG_FILE} 2>/dev/null + echo "name pptp-server">> ${PPTP_CONFIG_FILE} + + local remoteip=$(uci -q get ${CONFIG}.@service[0].remoteip) + [ -z "${remoteip}" ] && remoteip="172.16.100.10-20" + + echo "localip ${localip}" >> ${PPTP_CONFIG_FILE} + echo "remoteip ${remoteip}" >> ${PPTP_CONFIG_FILE} + echo "option ${PPTP_OPTIONS_FILE}" >> ${PPTP_CONFIG_FILE} + + local _users=$(get_enabled_anonymous_secs "@users") + [ -n "${_users}" ] && { + for _user in ${_users}; do + local u_enabled=$(uci -q get ${CONFIG}.${_user}.enabled) + [ "${u_enabled}" -eq 1 ] || continue + + local u_username=$(uci -q get ${CONFIG}.${_user}.username) + [ -n "${u_username}" ] || continue + + local u_password=$(uci -q get ${CONFIG}.${_user}.password) + [ -n "${u_password}" ] || continue + + local u_ipaddress=$(uci -q get ${CONFIG}.${_user}.ipaddress) + [ -n "${u_ipaddress}" ] || u_ipaddress="*" + + echo "${u_username} pptp-server ${u_password} ${u_ipaddress}" >> ${CHAP_SECRETS} + done + } + + echo "ip-up-script /usr/share/pptpd/ip-up" >> ${PPTP_OPTIONS_FILE} + echo "ip-down-script /usr/share/pptpd/ip-down" >> ${PPTP_OPTIONS_FILE} + + for m in arc4 sha1_generic slhc crc-ccitt ppp_generic ppp_async ppp_mppe; do + insmod ${m} >/dev/null 2>&1 + done + /usr/sbin/pptpd -c ${PPTP_CONFIG_FILE} + + ipt_rule add + gen_include +} + +stop() { + sed -i '/pptp-server/d' ${CHAP_SECRETS} 2>/dev/null + top -bn1 | grep "${PPTP_PATH}" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 + ipt_rule del + rm -rf /var/etc/${CONFIG}.include + rm -rf ${PPTP_PATH} +} diff --git a/luci-app-pptp-server/root/etc/pptpd.include b/luci-app-pptp-server/root/etc/pptpd.include deleted file mode 100755 index 2b628c572..000000000 --- a/luci-app-pptp-server/root/etc/pptpd.include +++ /dev/null @@ -1,11 +0,0 @@ -iptables -D forwarding_rule -i pppoe+ -j RETURN 2>/dev/null -iptables -D forwarding_rule -o pppoe+ -j RETURN 2>/dev/null -iptables -D forwarding_rule -i ppp+ -m conntrack --ctstate NEW -j ACCEPT 2>/dev/null -iptables -D forwarding_rule -o ppp+ -m conntrack --ctstate NEW -j ACCEPT 2>/dev/null - -iptables -A forwarding_rule -i pppoe+ -j RETURN -iptables -A forwarding_rule -o pppoe+ -j RETURN -iptables -A forwarding_rule -i ppp+ -m conntrack --ctstate NEW -j ACCEPT -iptables -A forwarding_rule -o ppp+ -m conntrack --ctstate NEW -j ACCEPT - -echo 1 > /proc/sys/net/ipv4/conf/br-lan/proxy_arp diff --git a/luci-app-pptp-server/root/etc/uci-defaults/luci-app-pptp-server b/luci-app-pptp-server/root/etc/uci-defaults/luci-app-pptp-server new file mode 100755 index 000000000..e6d82a35e --- /dev/null +++ b/luci-app-pptp-server/root/etc/uci-defaults/luci-app-pptp-server @@ -0,0 +1,21 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete firewall.luci_app_pptp_server + set firewall.luci_app_pptp_server=include + set firewall.luci_app_pptp_server.type=script + set firewall.luci_app_pptp_server.path=/var/etc/pptpd.include +EOF + +if [ -x "/sbin/fw3" ] && [ ! -L "/sbin/fw3" ]; then + uci -q set firewall.luci_app_pptp_server.reload='1' +else + uci -q delete firewall.luci_app_pptp_server.reload + uci -q delete firewall.luci_app_pptp_server.fw4_compatible +fi +uci commit firewall + +/etc/init.d/pptpd disable 2>/dev/null +/etc/init.d/pptpd stop 2>/dev/null +rm -rf /tmp/luci-*cache +exit 0 diff --git a/luci-app-pptp-server/root/etc/uci-defaults/luci-pptpd b/luci-app-pptp-server/root/etc/uci-defaults/luci-pptpd deleted file mode 100644 index 7aae2d2db..000000000 --- a/luci-app-pptp-server/root/etc/uci-defaults/luci-pptpd +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-pptpd.json" ] && { - cat > /usr/share/ucitrack/luci-app-pptpd.json << EEOF -{ - "config": "pptpd", - "init": "pptpd" -} -EEOF -} - -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/luci-app-pptp-server/root/usr/share/pptpd/ip-down b/luci-app-pptp-server/root/usr/share/pptpd/ip-down new file mode 100755 index 000000000..cc65dae9d --- /dev/null +++ b/luci-app-pptp-server/root/usr/share/pptpd/ip-down @@ -0,0 +1,27 @@ +#!/bin/sh + +_LOGOUT_TIME="$(date "+%Y-%m-%d %H:%M:%S")" +CONFIG="luci-app-pptp-server" +PPTP_PATH=/var/etc/pptpd +PPTP_SESSION_PATH=${PPTP_PATH}/session + +_USERNAME=${PEERNAME} +_IFACE=${1} +_TTY=${2} +_SPEED=${3} +_LOCALIP=${4} +_PEERIP=${5} +_REMOTEIP=${6} +_BYTES_SENT=${BYTES_SENT} +_BYTES_RCVD=${BYTES_RCVD} +_CONNECT_TIME=${CONNECT_TIME} + +rm -f ${PPTP_SESSION_PATH}/${_USERNAME}.${_IFACE} +rm -f /var/run/${_IFACE}.pid + +#可根据退出的账号自定义脚本,如静态路由表,组网等。 +SCRIPT="/usr/share/pptpd/ip-down.d/${_USERNAME}" +[ -s "$SCRIPT" ] && { + [ ! -x "$SCRIPT" ] && chmod 0755 "$SCRIPT" + "$SCRIPT" "$@" +} diff --git a/luci-app-pptp-server/root/usr/share/pptpd/ip-up b/luci-app-pptp-server/root/usr/share/pptpd/ip-up new file mode 100755 index 000000000..be277dd2d --- /dev/null +++ b/luci-app-pptp-server/root/usr/share/pptpd/ip-up @@ -0,0 +1,58 @@ +#!/bin/sh + +_LOGIN_TIME="$(date "+%Y-%m-%d %H:%M:%S")" +CONFIG="luci-app-pptp-server" +PPTP_PATH=/var/etc/pptpd +PPTP_SESSION_PATH=${PPTP_PATH}/session + +_USERNAME=${PEERNAME} +_IFACE=${1} +_TTY=${2} +_SPEED=${3} +_LOCALIP=${4} +_PEERIP=${5} +_REMOTEIP=${6} + +_PID="$(cat /var/run/${_IFACE}.pid 2>/dev/null)" + +mkdir -p ${PPTP_SESSION_PATH} + +cat <<-EOF > ${PPTP_SESSION_PATH}/${_USERNAME}.${_IFACE} + { + "username": "${_USERNAME}", + "interface": "${_IFACE}", + "tty": "${_TTY}", + "speed": "${_SPEED}", + "ip": "${_PEERIP}", + "remote_ip": "${_REMOTEIP}", + "pid": "${_PID}", + "login_time": "${_LOGIN_TIME}" + } +EOF + +#只能单用户使用 +cfgid=$(uci show ${CONFIG} | grep "@users" | grep "\.username='${_USERNAME}'" | cut -d '.' -sf 2) +[ -n "$cfgid" ] && { + HAS_LOGIN=$(ls ${PPTP_SESSION_PATH} | grep "^${_USERNAME}\.ppp" | grep -v "${_IFACE}") + [ -n "$HAS_LOGIN" ] && { + #踢出之前的用户 + KO_IFACE=$(echo $HAS_LOGIN | awk -F '.' '{print $2}') + KO_PID=$(cat /var/run/${KO_IFACE}.pid 2>/dev/null) + [ -n "$KO_PID" ] && kill -9 ${KO_PID} >/dev/null 2>&1 + rm -f ${PPTP_SESSION_PATH}/${HAS_LOGIN} + rm -f /var/run/${KO_IFACE}.pid + } + routes=$(uci -q get ${CONFIG}.${cfgid}.routes) + [ -n "$routes" ] && { + for router in ${routes}; do + route add -net ${router} dev ${_IFACE} >/dev/null 2>&1 + done + } +} + +#可根据登录的账号自定义脚本,如组网、日志、限速、权限等特殊待遇。 +SCRIPT="/usr/share/pptpd/ip-up.d/${_USERNAME}" +[ -s "$SCRIPT" ] && { + [ ! -x "$SCRIPT" ] && chmod 0755 "$SCRIPT" + "$SCRIPT" "$@" +} diff --git a/luci-app-pptp-server/root/usr/share/rpcd/acl.d/luci-app-pptp-server.json b/luci-app-pptp-server/root/usr/share/rpcd/acl.d/luci-app-pptp-server.json index 55d3cdf53..8f5ca8739 100644 --- a/luci-app-pptp-server/root/usr/share/rpcd/acl.d/luci-app-pptp-server.json +++ b/luci-app-pptp-server/root/usr/share/rpcd/acl.d/luci-app-pptp-server.json @@ -2,10 +2,10 @@ "luci-app-pptp-server": { "description": "Grant UCI access for luci-app-pptp-server", "read": { - "uci": [ "pptpd" ] + "uci": [ "luci-app-pptp-server" ] }, "write": { - "uci": [ "pptpd" ] + "uci": [ "luci-app-pptp-server" ] } } } diff --git a/luci-app-pptp-server/root/usr/share/ucitrack/luci-app-pptp-server.json b/luci-app-pptp-server/root/usr/share/ucitrack/luci-app-pptp-server.json new file mode 100644 index 000000000..5d242d238 --- /dev/null +++ b/luci-app-pptp-server/root/usr/share/ucitrack/luci-app-pptp-server.json @@ -0,0 +1,4 @@ +{ + "config": "luci-app-pptp-server", + "init": "luci-app-pptp-server" +} diff --git a/luci-app-pushbot/Makefile b/luci-app-pushbot/Makefile index 2a7798195..70bf67d17 100644 --- a/luci-app-pushbot/Makefile +++ b/luci-app-pushbot/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-pushbot -PKG_VERSION:=3.60 -PKG_RELEASE:=1 +PKG_VERSION:=3.55 +PKG_RELEASE:=2 PKG_MAINTAINER:=tty228 zzsj0928 diff --git a/luci-app-pushbot/README.md b/luci-app-pushbot/README.md new file mode 100644 index 000000000..19e50e6f1 --- /dev/null +++ b/luci-app-pushbot/README.md @@ -0,0 +1,93 @@ +# 改名公告 +#### 2021年04月25日 起luci-app-serverchand 改名为 luci-app-pushbot + +如需拉取编译 +请把: + +`# git clone https://github.com/zzsj0928/luci-app-serverchand package/luci-app-serverchand` + +改为 + +`git clone https://github.com/zzsj0928/luci-app-pushbot package/luci-app-pushbot` + +并把 .config 中 + +`CONFIG_PACKAGE_luci-app-serverchand=y` + +改为 + +`CONFIG_PACKAGE_luci-app-pushbot=y` + +注意:本次改名需要提前备份serverchand配置,并于PushBot中重新配置。 + +再次谢谢各位支持 + +# 申明 +- 本插件由[tty228/luci-app-serverchan](https://github.com/tty228/luci-app-serverchan)原创. +- 因微信推送存在诸多弊端(无法分开聊天工具与功能性消息推送,通知内不显示内容,内容需要点开才能查看等), +- 故由 然后七年 @zzsj0928 重新修改为本插件,为钉钉机器人API使用。 +- 本插件工作在:openwrt +- 本插件支持:钉钉推送,企业微信推送,PushPlus推送,微信推送,企业微信应用推送,飞书推送,钉钉机器人推送,企业微信机器人推送,飞书机器人推送,一对多推送,Bark推送(仅iOS) +- 自20210911之后的版本,支持Bark群组,群组名默认为设备名 +- 自20210901之后的版本,增加依赖jq,请重新编译或在安装前同步安装jq + +# 显示效果 +## 通知栏:直接显示推送主题,一目了然,按设备不同,分组显示 + + +## 消息列表:直接显示最新推送的标题 + + +## 消息内容:直接显示所有推送信息,不用二次点开再查看 + + +# 下载 +- [luci-app-pushbot](https://github.com/zzsj0928/luci-app-pushbot/releases) + + +----------------------------------------------------- +##################################################### +----------------------------------------------------- + +# 以下为原插件简介: + +# 简介 +- 用于 OpenWRT/LEDE 路由器上进行 Server酱 微信/Telegram 推送的插件 +- 基于 serverchan 提供的接口发送信息,Server酱说明:http://sc.ftqq.com/1.version +- **基于斐讯 k3 制作,不同系统不同设备,请自行修改部分代码,无测试条件无法重现的 bug 不考虑修复** +- 依赖 iputils-arping + curl 命令,安装前请 `opkg update`,小内存路由谨慎安装 +- 使用主动探测设备连接的方式检测设备在线状态,以避免WiFi休眠机制,主动探测较为耗时,**如遇设备休眠频繁,请自行调整超时设置** +- 流量统计功能依赖 wrtbwmon ,自行选装或编译,该插件与 Routing/NAT 、Flow Offloading 冲突,开启无法获取流量,自行选择,L大版本直接编译 luci-app-wrtbwmon + +#### 主要功能 +- 路由 ip/ipv6 变动推送 +- 设备别名 +- 设备上线推送 +- 设备离线推送及流量使用情况 +- CPU 负载、温度监视 +- 定时推送设备运行状态 +- MAC 白名单、黑名单、按接口检测设备 +- 免打扰 +- 无人值守任务 + +#### 说明 +- 潘多拉系统、或不支持 sh 的系统,请将脚本开头 `#!/bin/sh` 改为 `#!/bin/bash`,或手动安装 `sh` +- 追新是没有意义的,没有问题没必要更新,上班事情忙完了,摸鱼又不会摸,只能靠写几行 bug ,才能缓解无聊这样子 + +#### 已知问题 +- 直接关闭接口时,该接口的离线设备会忽略检测 +- 部分设备无法读取到设备名,脚本使用 `cat /var/dhcp.leases` 命令读取设备名,如果 dhcp 中不存在设备名,则无法读取设备名(如二级路由设备、静态ip设备),请使用设备名备注 + +# Download +- [luci-app-serverchan](https://github.com/tty228/luci-app-serverchan/releases) +- [wrtbwmon](https://github.com/brvphoenix/wrtbwmon) +- [luci-app-wrtbwmon](https://github.com/brvphoenix/luci-app-wrtbwmon) + +#### ps +- 新功能看情况开发 +- 王者荣耀新赛季,不思进取中 +- 欢迎各种代码提交 +- 提交bug时请尽量带上设备信息,日志与描述(如执行`/usr/bin/serverchan/serverchan`后的提示、日志信息、/tmp/serverchan/ipAddress 文件信息) +- 三言两句恕我无能为力 +- 武汉加油 + diff --git a/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua b/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua index f6330cf28..048989797 100644 --- a/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua +++ b/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua @@ -46,7 +46,6 @@ a:value("/usr/bin/pushbot/api/ent_wechat.json",translate("企业微信")) a:value("/usr/bin/pushbot/api/feishu.json",translate("飞书")) a:value("/usr/bin/pushbot/api/bark.json",translate("Bark")) a:value("/usr/bin/pushbot/api/pushplus.json",translate("PushPlus")) -a:value("/usr/bin/pushbot/api/pushdeer.json",translate("PushDeer")) a:value("/usr/bin/pushbot/api/diy.json",translate("自定义推送")) a=s:taboption("basic", Value,"dd_webhook",translate('Webhook'), translate("钉钉机器人 Webhook")..",只输入access_token=后面的即可
调用代码获取
点击这里

") @@ -85,19 +84,6 @@ a=s:taboption("basic", Value,"pp_topic",translate('PushPlus Topic'), translate(" a.rmempty = true a:depends("pp_topic_enable","1") -a=s:taboption("basic", Value,"pushdeer_key",translate('PushDeer Key'), translate("PushDeer Key").."
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushdeer.json") - -a=s:taboption("basic", Flag,"pushdeer_srv_enable",translate("自建 PushDeer 服务器")) -a.default=0 -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushdeer.json") - -a=s:taboption("basic", Value,"pushdeer_srv",translate('PushDeer Server'), translate("PushDeer 自建服务器地址").."
如https://your.domain:port
具体自建服务器设定参见:点击这里

") -a.rmempty = true -a:depends("pushdeer_srv_enable","1") - a=s:taboption("basic", Value,"fs_webhook",translate('WebHook'), translate("飞书 WebHook").."
调用代码获取点击这里

") a.rmempty = true a:depends("jsonpath","/usr/bin/pushbot/api/feishu.json") diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json b/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json deleted file mode 100755 index 441d7d306..000000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "_api": "这是 PushDeer推送 api 文件", - "_api": "【PushDeer推送】", - - "url": "${pushdeer_srv}/message/push", - "data": "@${tempjsonpath}", - "content_type": "Content-Type:application/json", - "str_title_start": "**【", - "str_title_end": "】**", - "str_linefeed": "\\n\\n", - "str_splitline": "\\n\\n---\\n\\n", - "str_space": " ", - "str_tab": " ", - "table_tab": "┋", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "**", - "type": - { - "pushkey": "\"${pushdeer_key}\"", - "type": "\"markdown\"", - "text": "\"${1}\"", - "desp": "\"${nowtime}${str_linefeed}${2}\"" - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/pushbot b/luci-app-pushbot/root/usr/bin/pushbot/pushbot index d00a6121a..a0e1162ac 100755 --- a/luci-app-pushbot/root/usr/bin/pushbot/pushbot +++ b/luci-app-pushbot/root/usr/bin/pushbot/pushbot @@ -18,7 +18,7 @@ function read_config(){ "pushbot_sheep" "starttime" "endtime" "pushbot_whitelist" "pushbot_blacklist" "pushbot_interface" "MAC_online_list" "MAC_offline_list" \ "up_timeout" "down_timeout" "timeout_retry_count" "thread_num" "soc_code" "pve_host" "pve_port"\ "err_enable" "err_sheep_enable" "err_device_aliases" "network_err_event" "system_time_event" "autoreboot_time" "network_restart_time" "public_ip_event" "public_ip_retry_count" \ - "jsonpath" "dd_webhook" "we_webhook" "pp_token" "pp_channel" "pp_webhook" "pp_topic_enable" "pp_topic" "fs_webhook" "pushdeer_key" "pushdeer_srv_enable" "pushdeer_srv" "bark_srv_enable" "bark_srv" "bark_token" "bark_sound" "bark_icon" "bark_icon_enable" "bark_level" + "jsonpath" "dd_webhook" "we_webhook" "pp_token" "pp_channel" "pp_webhook" "pp_topic_enable" "pp_topic" "fs_webhook" "bark_srv_enable" "bark_srv" "bark_token" "bark_sound" "bark_icon" "bark_icon_enable" "bark_level" for str_version in "wrtbwmon" "iputils-arping" "curl" "iw"; do eval `echo ${str_version:0:2}"_version"`=`opkg list-installed|grep -w ^${str_version}|awk '{print $3}'` 2>/dev/null @@ -45,7 +45,6 @@ function read_config(){ [ -z "$down_timeout" ] || [ "$down_timeout" -eq "0" ] && down_timeout="20";down_timeout=`expr ${down_timeout} / 2 + 1` [ -z "$timeout_retry_count" ] && timeout_retry_count="2";[ "$timeout_retry_count" -eq "0" ] && timeout_retry_count="1" [ ! -z "$bark_token" ] && [ -z "$bark_srv" ] && bark_srv="https://api.day.app" - [ ! -z "$pushdeer_key" ] && [ -z "$pushdeer_srv" ] && pushdeer_srv="https://api2.pushdeer.com" # 字符串读取 str_title_start=`/usr/bin/jq -r '.str_title_start' ${jsonpath}` @@ -86,7 +85,7 @@ function pushbot_init(){ [ ! -f "/usr/sbin/wrtbwmon" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】未安装 wrtbwmon ,流量统计不可用" >> ${logfile} [ -z "$ip_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 iputils-arping 版本号,请确认插件是否正常运行" >> ${logfile} [ -z "$cu_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 curl 版本号,请确认插件是否正常运行" >> ${logfile} - [ -z "${dd_webhook}${pp_token}${we_webhook}${fs_webhook}${bark_token}${pushdeer_key}" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】请填写正确的Token/Webhook " >> ${logfile} && return 1 + [ -z "${dd_webhook}${pp_token}${we_webhook}${fs_webhook}${bark_token}" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】请填写正确的Token/Webhook " >> ${logfile} && return 1 local interfacelist=`getinterfacelist` && [ -z "$interfacelist" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法正确获取接口信息,请确认插件是否正常运行" >> ${logfile} return 0 } @@ -113,7 +112,7 @@ function down_oui(){ [ $logrow -lt "10" ] && rm -f ${oui_base} >/dev/null 2>&1 if [ ! -z "$oui_data" ] && [ "$oui_data" -ne "3" ] && [ ! -f ${oui_base} ]; then echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】设备MAC厂商信息不存在,重新下载" >> ${logfile} - wget --no-check-certificate -t 3 -T 15 -O ${dir}oui.txt https://standards-oui.ieee.org/oui/oui.txt >/dev/null 2>&1 + wget --no-check-certificate -t 3 -T 15 -O ${dir}oui.txt https://linuxnet.ca/ieee/oui.txt >/dev/null 2>&1 if [ -f ${dir}oui.txt ] && [ "$oui_data" -eq "1" ]; then cat ${dir}oui.txt|grep "base 16"|grep -i "apple\|aruba\|asus\|autelan\|belkin\|bhu\|buffalo\|cctf\|cisco\|comba\|datang\|dell\|dlink\|dowell\|ericsson\|fast\|feixun\|\ fiberhome\|fujitsu\|grentech\|h3c\|hisense\|hiwifi\|honghai\|honghao\|hp\|htc\|huawei\|intel\|jinli\|jse\|lenovo\|lg\|liteon\|malata\|meizu\|mercury\|meru\|moto\|netcore\|\ @@ -222,7 +221,7 @@ function getname(){ done ( ! echo "$tmp_name"|grep -q -w "unknown\|*" ) && [ ! -z "$tmp_name" ] && echo "$tmp_name" && return || unset tmp_name # 为unknown时重新读取 [ -f "$oui_base" ] && local tmp_name=$(cat $oui_base|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp'|sed 's/ /_/g') - [ ! -z "$oui_data" ] && [ "$oui_data" -eq "4" ] && local tmp_name=$(curl -sS "https://standards-oui.ieee.org/oui/oui.txt"|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp'|sed 's/ /_/g') + [ ! -z "$oui_data" ] && [ "$oui_data" -eq "4" ] && local tmp_name=$(curl -sS "http://standards-oui.ieee.org/oui.txt"|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp'|sed 's/ /_/g') [ -z "$tmp_name" ] && local tmp_name="unknown" echo "$tmp_name" } diff --git a/luci-app-shadowsocks-libev/Makefile b/luci-app-shadowsocks-libev/Makefile deleted file mode 100644 index 05b0a66c4..000000000 --- a/luci-app-shadowsocks-libev/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (C) 2017 Yousong Zhou -# -# This is free software, licensed under the Apache License, Version 2.0 . -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI Support for shadowsocks-libev -LUCI_DEPENDS:= - -PKG_LICENSE:=Apache-2.0 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua b/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua deleted file mode 100644 index 05d12e38b..000000000 --- a/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. --- -module("luci.controller.shadowsocks-libev", package.seeall) - -function index() - entry({"admin", "services", "shadowsocks-libev"}, - alias("admin", "services", "shadowsocks-libev", "instances"), - _("Shadowsocks-libev"), 59) - - entry({"admin", "services", "shadowsocks-libev", "instances"}, - arcombine(cbi("shadowsocks-libev/instances"), cbi("shadowsocks-libev/instance-details")), - _("Local Instances"), 10).leaf = true - - entry({"admin", "services", "shadowsocks-libev", "servers"}, - cbi("shadowsocks-libev/servers"), - _("Remote Servers"), 20).leaf = true - - entry({"admin", "services", "shadowsocks-libev", "rules"}, - cbi("shadowsocks-libev/rules"), - _("Redir Rules"), 30).leaf = true - - entry({"admin", "services", "shadowsocks-libev", "status"}, call("ss_status"), nil).leaf = true - -end - -function ss_status() - local ut = require "luci.util" - local rv = ut.ubus("service", "list", {name = "shadowsocks-libev"})["shadowsocks-libev"] or {_=0} - - luci.http.prepare_content("application/json") - luci.http.write_json(rv) -end diff --git a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua b/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua deleted file mode 100644 index 195a31f2f..000000000 --- a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua +++ /dev/null @@ -1,47 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. - -local ds = require "luci.dispatcher" -local ss = require "luci.model.shadowsocks-libev" - -local sname = arg[1] -local redirect_url = ds.build_url("admin/services/shadowsocks-libev/instances") -local s, o - -local m = Map("shadowsocks-libev") -local sdata = m:get(sname) -if not sdata then - luci.http.redirect(redirect_url) - return -end -local stype = sdata[".type"] -m.redirect = redirect_url -m.title = "shadowsocks-libev - %s - %s" % {stype, sname} - - -s = m:section(NamedSection, sname, stype) -s:tab("general", translate("General Settings")) -s:tab("advanced", translate("Advanced Settings")) -s:taboption("general", Flag, "disabled", translate("Disable")) -ss.option_install_package(s, "general") -ss.options_common(s, "advanced") - -if stype == "ss_server" then - ss.options_server(s, "general") - o = s:taboption("general", Value, "bind_address", - translate("Bind address"), - translate("The address ss-server will initiate connection from")) - o.datatype = "ipaddr" - o.placeholder = "0.0.0.0" - ss.values_ipaddr(o) -else - ss.options_client(s, "general") - if stype == "ss_tunnel" then - o = s:taboption("general", Value, "tunnel_address", - translate("Tunnel address"), - translate("The address ss-tunnel will forward traffic to")) - o.datatype = "hostport" - end -end - -return m diff --git a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua b/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua deleted file mode 100644 index 62a90fb41..000000000 --- a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua +++ /dev/null @@ -1,104 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. - -local ds = require "luci.dispatcher" -local ss = require "luci.model.shadowsocks-libev" -local ut = require "luci.util" -local m, s, o - -m = Map("shadowsocks-libev", - translate("Local Instances"), - translate("Instances of shadowsocks-libev components, e.g. ss-local, \ - ss-redir, ss-tunnel, ss-server, etc. To enable an instance it \ - is required to enable both the instance itself and the remote \ - server it refers to.")) - -local instances = {} -local cfgtypes = { "ss_local", "ss_redir", "ss_server", "ss_tunnel" } - -for sname, sdata in pairs(m:get()) do - local key, value = ss.cfgvalue_overview(sdata) - if key ~= nil then - instances[key] = value - end -end - -s = m:section(Table, instances) -s.addremove = true -s.template_addremove = "shadowsocks-libev/add_instance" -s.extedit = function(self, section) - local value = instances[section] - if type(value) == "table" then - return ds.build_url(unpack(ds.context.requestpath), - "services/shadowsocks-libev/instances", - value[".name"]) - end -end -s.parse = function(self, ...) - Table.parse(self, ...) - - local crval = REMOVE_PREFIX .. self.config - local name = self.map:formvaluetable(crval) - for k,v in pairs(name) do - local value = instances[k] - local sname = value[".name"] - if type(value) == "table" then - m:del(sname) - instances[k] = nil - for _, oname in ipairs({"redir_tcp", "redir_udp"}) do - local ovalue = m:get("ss_rules", oname) - if ovalue == sname then - m:del("ss_rules", oname) - end - end - end - end - - local stype = m:formvalue("_newinst.type") - local sname = m:formvalue("_newinst.name") - if ut.contains(cfgtypes, stype) then - local created - if sname and #sname > 0 then - created = m:set(sname, nil, stype) - else - created = m:add(stype) - sname = created - end - if created then - m.uci:save("shadowsocks-libev") - luci.http.redirect(ds.build_url( - "admin/services/shadowsocks-libev/instances", sname - )) - end - end -end - -o = s:option(DummyValue, "name", translate("Name")) -o.rawhtml = true -o = s:option(DummyValue, "overview", translate("Overview")) -o.rawhtml = true - -s:option(DummyValue, "running", translate("Running")) - -o = s:option(Button, "disabled", translate("Enable/Disable")) -o.render = function(self, section, scope) - if instances[section].disabled then - self.title = translate("Disabled") - self.inputstyle = "reset" - else - self.title = translate("Enabled") - self.inputstyle = "save" - end - Button.render(self, section, scope) -end -o.write = function(self, section) - local sdata = instances[section] - if type(sdata) == "table" then - local sname = sdata[".name"] - local disabled = not sdata["disabled"] - sdata["disabled"] = disabled - m:set(sname, "disabled", tostring(disabled)) - end -end - -return m diff --git a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua b/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua deleted file mode 100644 index 4a01bed24..000000000 --- a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua +++ /dev/null @@ -1,109 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. - -local ss = require("luci.model.shadowsocks-libev") - -local m, s, o - -m = Map("shadowsocks-libev", - translate("Redir Rules"), - translate("On this page you can configure how traffics are to be \ - forwarded to ss-redir instances. \ - If enabled, packets will first have their src ip addresses checked \ - against Src ip/net bypass, Src ip/net forward, \ - Src ip/net checkdst and if none matches Src default \ - will give the default action to be taken. \ - If the prior check results in action checkdst, packets will continue \ - to have their dst addresses checked.")) - -local sdata = m:get('ss_rules') -if not sdata then - m:set('ss_rules', nil, 'ss_rules') - m:set('ss_rules', 'disabled', "1") -end - -function src_dst_option(s, ...) - local o = s:taboption(...) - o.datatype = "or(ip4addr,cidr4)" -end - -s = m:section(NamedSection, "ss_rules", "ss_rules") -s:tab("general", translate("General Settings")) -s:tab("src", translate("Source Settings")) -s:tab("dst", translate("Destination Settings")) - -s:taboption('general', Flag, "disabled", translate("Disable")) -ss.option_install_package(s, 'general') - -o = s:taboption('general', ListValue, "redir_tcp", - translate("ss-redir for TCP")) -ss.values_redir(o, 'tcp') -o = s:taboption('general', ListValue, "redir_udp", - translate("ss-redir for UDP")) -ss.values_redir(o, 'udp') - -o = s:taboption('general', ListValue, "local_default", - translate("Local-out default"), - translate("Default action for locally generated TCP packets")) -ss.values_actions(o) -o = s:taboption('general', DynamicList, "ifnames", - translate("Ingress interfaces"), - translate("Only apply rules on packets from these network interfaces")) -ss.values_ifnames(o) -s:taboption('general', Value, "ipt_args", - translate("Extra arguments"), - translate("Passes additional arguments to iptables. Use with care!")) - -src_dst_option(s, 'src', DynamicList, "src_ips_bypass", - translate("Src ip/net bypass"), - translate("Bypass ss-redir for packets with src address in this list")) -src_dst_option(s, 'src', DynamicList, "src_ips_forward", - translate("Src ip/net forward"), - translate("Forward through ss-redir for packets with src address in this list")) -src_dst_option(s, 'src', DynamicList, "src_ips_checkdst", - translate("Src ip/net checkdst"), - translate("Continue to have dst address checked for packets with src address in this list")) -o = s:taboption('src', ListValue, "src_default", - translate("Src default"), - translate("Default action for packets whose src address do not match any of the src ip/net list")) -ss.values_actions(o) - -src_dst_option(s, 'dst', DynamicList, "dst_ips_bypass", - translate("Dst ip/net bypass"), - translate("Bypass ss-redir for packets with dst address in this list")) -src_dst_option(s, 'dst', DynamicList, "dst_ips_forward", - translate("Dst ip/net forward"), - translate("Forward through ss-redir for packets with dst address in this list")) - -o = s:taboption('dst', FileBrowser, "dst_ips_bypass_file", - translate("Dst ip/net bypass file"), - translate("File containing ip/net for the purposes as with Dst ip/net bypass")) -o.datatype = "file" -s:taboption('dst', FileBrowser, "dst_ips_forward_file", - translate("Dst ip/net forward file"), - translate("File containing ip/net for the purposes as with Dst ip/net forward")) -o.datatype = "file" -o = s:taboption('dst', ListValue, "dst_default", - translate("Dst default"), - translate("Default action for packets whose dst address do not match any of the dst ip list")) -ss.values_actions(o) - -local installed = os.execute("iptables -m recent -h &>/dev/null") == 0 -if installed then - o = s:taboption('dst', Flag, "dst_forward_recentrst") -else - m:set('ss_rules', 'dst_forward_recentrst', "0") - o = s:taboption("dst", Button, "_install") - o.inputtitle = translate("Install package iptables-mod-conntrack-extra") - o.inputstyle = "apply" - o.write = function() - return luci.http.redirect( - luci.dispatcher.build_url("admin/system/packages") .. - "?submit=1&install=iptables-mod-conntrack-extra" - ) - end -end -o.title = translate("Forward recentrst") -o.description = translate("Forward those packets whose dst have recently sent to us multiple tcp-rst") - -return m diff --git a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua b/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua deleted file mode 100644 index 71c66562e..000000000 --- a/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua +++ /dev/null @@ -1,31 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. - -local ds = require "luci.dispatcher" -local ss = require("luci.model.shadowsocks-libev") - -local m, s - -m = Map("shadowsocks-libev", - translate("Remote Servers"), - translate("Definition of remote shadowsocks servers. \ - Disable any of them will also disable instances refering to it.")) - -local sname = arg[1] -if sname then - if not m:get(sname) then - luci.http.redirect(ds.build_url("admin/services/shadowsocks-libev/servers")) - return - end - s = m:section(NamedSection, sname, "server") - m.title = m.title .. ' - ' .. sname -else - s = m:section(TypedSection, "server") - s.template = 'cbi/tblsection' - s.addremove = true -end - -s:option(Flag, "disabled", translate("Disable")) -ss.options_server(s) - -return m diff --git a/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua b/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua deleted file mode 100644 index af1a75b6f..000000000 --- a/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua +++ /dev/null @@ -1,264 +0,0 @@ --- Copyright 2017 Yousong Zhou --- Licensed to the public under the Apache License 2.0. - -local _up = getfenv(3) -local ut = require("luci.util") -local sys = require("luci.sys") -local ds = require("luci.dispatcher") -local nw = require("luci.model.network") -nw.init() -module("luci.model.shadowsocks-libev", function(m) - setmetatable(m, {__index=function (self, k) - local tb = _up - return rawget(self, k) or _up[k] - end}) -end) - -function values_actions(o) - o:value("bypass") - o:value("forward") - if o.option ~= "dst_default" then - o:value("checkdst") - end -end - -function values_redir(o, xmode) - o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata) - local disabled = ucival_to_bool(sdata["disabled"]) - local sname = sdata[".name"] - local mode = sdata["mode"] or "tcp_only" - if not disabled and mode:find(xmode) then - local desc = "%s - %s" % {sname, mode} - o:value(sname, desc) - end - end) - o:value("", "") - o.default = "" -end - -function values_serverlist(o) - o.map.uci.foreach("shadowsocks-libev", "server", function(sdata) - local sname = sdata[".name"] - local server = sdata["server"] - local server_port = sdata["server_port"] - if server and server_port then - local disabled = ucival_to_bool(sdata[".disabled"]) and " - disabled" or "" - local desc = "%s - %s:%s%s" % {sname, server, server_port, disabled} - o:value(sname, desc) - end - end) -end - -function values_ipaddr(o) - for _, v in ipairs(nw:get_interfaces()) do - for _, a in ipairs(v:ipaddrs()) do - o:value(a:host():string(), '%s (%s)' %{ a:host(), v:shortname() }) - end - end -end - -function values_ifnames(o) - for _, v in ipairs(sys.net.devices()) do - o:value(v) - end -end - -function options_client(s, tab) - local o - - o = s:taboption(tab, ListValue, "server", translate("Remote server")) - values_serverlist(o) - o = s:taboption(tab, Value, "local_address", translate("Local address")) - o.datatype = "ipaddr" - o.placeholder = "0.0.0.0" - values_ipaddr(o) - o = s:taboption(tab, Value, "local_port", translate("Local port")) - o.datatype = "port" -end - -function options_server(s, tab) - local o - local optfunc - - if tab == nil then - optfunc = function(...) return s:option(...) end - else - optfunc = function(...) return s:taboption(tab, ...) end - end - - o = optfunc(Value, "server", translate("Server")) - o.datatype = "host" - o.size = 16 - o = optfunc(Value, "server_port", translate("Server port")) - o.datatype = "port" - o.size = 5 - o = optfunc(ListValue, "method", translate("Method")) - for _, m in ipairs(methods) do - o:value(m) - end - o = optfunc(Value, "key", translate("Key (base64 encoding)")) - o.datatype = "base64" - o.password = true - o.size = 12 - o = optfunc(Value, "password", translate("Password")) - o.password = true - o.size = 12 -end - -function options_common(s, tab) - local o - - o = s:taboption(tab, ListValue, "mode", translate("Mode of operation")) - for _, m in ipairs(modes) do - o:value(m) - end - o.default = "tcp_and_udp" - o = s:taboption(tab, Value, "mtu", translate("MTU")) - o.datatype = "uinteger" - o = s:taboption(tab, Value, "timeout", translate("Timeout (sec)")) - o.datatype = "uinteger" - s:taboption(tab, Value, "user", translate("Run as")) - - s:taboption(tab, Flag, "verbose", translate("Verbose")) - s:taboption(tab, Flag, "ipv6_first", translate("IPv6 First"), translate("Prefer IPv6 addresses when resolving names")) - s:taboption(tab, Flag, "fast_open", translate("Enable TCP Fast Open")) - s:taboption(tab, Flag, "no_delay", translate("Enable TCP_NODELAY")) - s:taboption(tab, Flag, "reuse_port", translate("Enable SO_REUSEPORT")) -end - -function ucival_to_bool(val) - return val == "true" or val == "1" or val == "yes" or val == "on" -end - -function cfgvalue_overview(sdata) - local stype = sdata[".type"] - local lines = {} - - if stype == "ss_server" then - cfgvalue_overview_(sdata, lines, names_options_server) - cfgvalue_overview_(sdata, lines, names_options_common) - cfgvalue_overview_(sdata, lines, { - "bind_address", - }) - elseif stype == "ss_local" or stype == "ss_redir" or stype == "ss_tunnel" then - cfgvalue_overview_(sdata, lines, names_options_client) - if stype == "ss_tunnel" then - cfgvalue_overview_(sdata, lines, {"tunnel_address"}) - end - cfgvalue_overview_(sdata, lines, names_options_common) - else - return nil, nil - end - local sname = sdata[".name"] - local key = "%s.%s" % {stype, sname} - local value = { - [".name"] = sname, - name = '%s.%s' % {stype, sname}, - overview = table.concat(lines, "
"), - disabled = ucival_to_bool(sdata["disabled"]), - } - return key, value -end - -function cfgvalue_overview_(sdata, lines, names) - local line - - for _, n in ipairs(names) do - local v = sdata[n] - if v ~= nil then - if n == "key" or n == "password" then - v = translate("") - end - local fv = "%s" % ut.pcdata(v) - if sdata[".type"] ~= "ss_server" and n == "server" then - fv = '%s' % { - ds.build_url("admin/services/shadowsocks-libev/servers", v), fv} - end - line = n .. ": " .. fv - table.insert(lines, line) - end - end -end - -function option_install_package(s, tab) - local bin = s.sectiontype:gsub("_", "-", 1) - local installed = nixio.fs.access("/usr/bin/" .. bin) - if installed then - return - end - local opkg_package = "shadowsocks-libev-" .. bin - local p_install - if tab then - p_install = s:taboption(tab, Button, "_install") - else - p_install = s:option(Button, "_install") - end - p_install.title = translate("Package is not installed") - p_install.inputtitle = translate("Install package %q" % opkg_package) - p_install.inputstyle = "apply" - - function p_install.write() - return luci.http.redirect( - luci.dispatcher.build_url("admin/system/packages") .. - "?submit=1&install=%s" % opkg_package - ) - end -end - -names_options_server = { - "server", - "server_port", - "method", - "key", - "password", -} - -names_options_client = { - "server", - "local_address", - "local_port", -} - -names_options_common = { - "verbose", - "ipv6_first", - "fast_open", - "no_delay", - "reuse_port", - "mode", - "mtu", - "timeout", - "user", -} - -modes = { - "tcp_only", - "tcp_and_udp", - "udp_only", -} - -methods = { - -- aead - "aes-128-gcm", - "aes-192-gcm", - "aes-256-gcm", - "chacha20-ietf-poly1305", - "xchacha20-ietf-poly1305", - -- stream - "table", - "rc4", - "rc4-md5", - "aes-128-cfb", - "aes-192-cfb", - "aes-256-cfb", - "aes-128-ctr", - "aes-192-ctr", - "aes-256-ctr", - "bf-cfb", - "camellia-128-cfb", - "camellia-192-cfb", - "camellia-256-cfb", - "salsa20", - "chacha20", - "chacha20-ietf", -} diff --git a/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm b/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm deleted file mode 100644 index 219d89b07..000000000 --- a/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm +++ /dev/null @@ -1,45 +0,0 @@ -
-
- - - - - - -
- - - - - -
-
- diff --git a/luci-app-shadowsocks-libev/root/etc/uci-defaults/40_luci-shadowsocks-libev b/luci-app-shadowsocks-libev/root/etc/uci-defaults/40_luci-shadowsocks-libev deleted file mode 100644 index 9f86bdac3..000000000 --- a/luci-app-shadowsocks-libev/root/etc/uci-defaults/40_luci-shadowsocks-libev +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-shadowsocks-libev.json" ] && { - cat > /usr/share/ucitrack/luci-app-shadowsocks-libev.json << EEOF -{ - "config": "shadowsocks-libev", - "init": "shadowsocks-libev" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@shadowsocks-libev[-1] - add ucitrack shadowsocks-libev - set ucitrack.@shadowsocks-libev[-1].init=shadowsocks-libev - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-shairplay/Makefile b/luci-app-shairplay/Makefile deleted file mode 100644 index c5250f116..000000000 --- a/luci-app-shairplay/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# 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 Support for Shairplay -LUCI_DEPENDS:=+shairplay - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-shairplay/luasrc/controller/shairplay.lua b/luci-app-shairplay/luasrc/controller/shairplay.lua deleted file mode 100644 index d3223016a..000000000 --- a/luci-app-shairplay/luasrc/controller/shairplay.lua +++ /dev/null @@ -1,14 +0,0 @@ --- Copyright 2014 Álvaro Fernández Rojas --- Licensed to the public under the Apache License 2.0. - -module("luci.controller.shairplay", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/shairplay") then - return - end - - local page = entry({"admin", "services", "shairplay"}, cbi("shairplay"), _("Shairplay")) - page.dependent = true - -end diff --git a/luci-app-shairplay/luasrc/model/cbi/shairplay.lua b/luci-app-shairplay/luasrc/model/cbi/shairplay.lua deleted file mode 100644 index 4e078d7e5..000000000 --- a/luci-app-shairplay/luasrc/model/cbi/shairplay.lua +++ /dev/null @@ -1,65 +0,0 @@ --- Copyright 2014 Álvaro Fernández Rojas --- Licensed to the public under the Apache License 2.0. - -m = Map("shairplay", "Shairplay", translate("Shairplay is a simple AirPlay server implementation, here you can configure the settings.")) - -s = m:section(TypedSection, "shairplay", "") -s.addremove = true -s.anonymous = false - -enable=s:option(Flag, "disabled", translate("Enabled")) -enable.enabled="0" -enable.disabled="1" -enable.default = "1" -enable.rmempty = false -respawn=s:option(Flag, "respawn", translate("Respawn")) -respawn.default = false - -apname = s:option(Value, "apname", translate("Airport Name")) -apname.rmempty = true - -port=s:option(Value, "port", translate("Port")) -port.rmempty = true -port.datatype = "port" - -pw = s:option(Value, "password", translate("Password")) -pw.rmempty = true -pw.password = true - -hwaddr=s:option(Value, "hwaddr", translate("HW Address")) -hwaddr.rmempty = true -hwaddr.datatype = "macaddr" - -ao_driver=s:option(ListValue, "ao_driver", translate("AO Driver")) -ao_driver:value("", translate("Default")) -ao_driver:value("alsa") ---ao_driver:value("alsa05") ---ao_driver:value("arts") ---ao_driver:value("esd") ---ao_driver:value("irix") ---ao_driver:value("nas") -ao_driver:value("oss") ---ao_driver:value("sun") - -ao_devicename=s:option(Value, "ao_devicename", translate("AO Device Name")) -ao_devicename.rmempty = true - -ao_deviceid = s:option(ListValue, "ao_deviceid", translate("AO Device ID")) -ao_deviceid.rmempty = true -ao_deviceid:value("", translate("Default")) -local pats = io.popen("find /proc/asound/ -type d -name 'card*' | sort") -if pats then - local l - while true do - l = pats:read("*l") - if not l then break end - - l = string.gsub(l, "/proc/asound/card", "") - if l then - ao_deviceid:value(l) - end - end - pats:close() -end - -return m diff --git a/luci-app-shairplay/po/ja/shairplay.po b/luci-app-shairplay/po/ja/shairplay.po deleted file mode 100644 index 07fa7be6e..000000000 --- a/luci-app-shairplay/po/ja/shairplay.po +++ /dev/null @@ -1,54 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2017-01-03 15:06+0900\n" -"Last-Translator: INAGAKI Hiroshi \n" -"Language-Team: \n" -"Language: ja\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.11\n" -"X-Poedit-Basepath: .\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "AO Device ID" -msgstr "オーディオ出力 デバイスID" - -msgid "AO Device Name" -msgstr "オーディオ出力 デバイス名" - -msgid "AO Driver" -msgstr "オーディオ出力 デバイスドライバー" - -msgid "Airport Name" -msgstr "Airport名" - -msgid "Default" -msgstr "デフォルト" - -msgid "Enabled" -msgstr "有効" - -msgid "HW Address" -msgstr "ハードウェア アドレス" - -msgid "Password" -msgstr "パスワード" - -msgid "Port" -msgstr "ポート" - -msgid "Respawn" -msgstr "リスポーン" - -msgid "Shairplay" -msgstr "" - -msgid "" -"Shairplay is a simple AirPlay server implementation, here you can configure " -"the settings." -msgstr "" -"Shairplayは、シンプルなAirPlay サーバー実装です。ここでは、設定を行うことがで" -"きます。" diff --git a/luci-app-shairplay/po/pt-br/shairplay.po b/luci-app-shairplay/po/pt-br/shairplay.po deleted file mode 100644 index c7d0ab18a..000000000 --- a/luci-app-shairplay/po/pt-br/shairplay.po +++ /dev/null @@ -1,54 +0,0 @@ -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 1.8.11\n" -"Last-Translator: Luiz Angelo Daros de Luca \n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"Language: pt_BR\n" -"X-Poedit-Bookmarks: -1,5,-1,-1,-1,-1,-1,-1,-1,-1\n" - -msgid "AO Device ID" -msgstr "Identificador do dispositivo AO" - -msgid "AO Device Name" -msgstr "Nome do dispositivo AO" - -msgid "AO Driver" -msgstr "Driver do AO" - -msgid "Airport Name" -msgstr "Nome do Airport" - -msgid "Default" -msgstr "Padrão" - -msgid "Enabled" -msgstr "Habilitado" - -msgid "HW Address" -msgstr "Endereço de Hardware" - -msgid "Password" -msgstr "Senha" - -msgid "Port" -msgstr "Porta" - -msgid "Respawn" -msgstr "Redisparar" - -msgid "Shairplay" -msgstr "Shairplay" - -msgid "" -"Shairplay is a simple AirPlay server implementation, here you can configure " -"the settings." -msgstr "" -"Shairplay é uma implementação simples de um servidor AirPlay. Aqui você pode " -"configurá-lo." diff --git a/luci-app-shairplay/po/ru/shairplay.po b/luci-app-shairplay/po/ru/shairplay.po deleted file mode 100644 index 037e90b52..000000000 --- a/luci-app-shairplay/po/ru/shairplay.po +++ /dev/null @@ -1,56 +0,0 @@ -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"Project-Id-Version: LuCI: shairplay\n" -"POT-Creation-Date: 2017-12-06 11:15+0300\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" - -msgid "AO Device ID" -msgstr "AO ID устройства" - -msgid "AO Device Name" -msgstr "AO имя устройства" - -msgid "AO Driver" -msgstr "AO драйвер" - -msgid "Airport Name" -msgstr "Имя Air-порта" - -msgid "Default" -msgstr "По умолчанию" - -msgid "Enabled" -msgstr "Включено" - -msgid "HW Address" -msgstr "HW адрес" - -msgid "Password" -msgstr "Пароль" - -msgid "Port" -msgstr "Порт" - -msgid "Respawn" -msgstr "Перерождение" - -msgid "Shairplay" -msgstr "Shairplay" - -msgid "" -"Shairplay is a simple AirPlay server implementation, here you can configure " -"the settings." -msgstr "" -"Shairplay является простой реализацией сервера AirPlay, здесь вы можете его " -"настроить." diff --git a/luci-app-shairplay/po/templates/shairplay.pot b/luci-app-shairplay/po/templates/shairplay.pot deleted file mode 100644 index b3da99e0b..000000000 --- a/luci-app-shairplay/po/templates/shairplay.pot +++ /dev/null @@ -1,40 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" - -msgid "AO Device ID" -msgstr "" - -msgid "AO Device Name" -msgstr "" - -msgid "AO Driver" -msgstr "" - -msgid "Airport Name" -msgstr "" - -msgid "Default" -msgstr "" - -msgid "Enabled" -msgstr "" - -msgid "HW Address" -msgstr "" - -msgid "Password" -msgstr "" - -msgid "Port" -msgstr "" - -msgid "Respawn" -msgstr "" - -msgid "Shairplay" -msgstr "" - -msgid "" -"Shairplay is a simple AirPlay server implementation, here you can configure " -"the settings." -msgstr "" diff --git a/luci-app-shairplay/root/etc/uci-defaults/40_luci-shairplay b/luci-app-shairplay/root/etc/uci-defaults/40_luci-shairplay deleted file mode 100644 index 6ab44fbec..000000000 --- a/luci-app-shairplay/root/etc/uci-defaults/40_luci-shairplay +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-shairplay.json" ] && { - cat > /usr/share/ucitrack/luci-app-shairplay.json << EEOF -{ - "config": "shairplay", - "init": "shairplay" -} -EEOF -} - -# needed for "Save and Apply" to restart shairplay -uci -q batch <<-EOF >/dev/null - delete ucitrack.@shairplay[-1] - add ucitrack shairplay - set ucitrack.@shairplay[-1].init="shairplay" - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-smartinfo/Makefile b/luci-app-smartinfo/Makefile deleted file mode 100644 index ffba0dad8..000000000 --- a/luci-app-smartinfo/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2019 Openwrt.org -# -# This is free software, licensed under the GNU General Public License v3. -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI disk monitor -LUCI_DEPENDS:=+smartmontools -LUCI_PKGARCH:=all -PKG_MAINTAINER:=animefansxj - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-smartinfo/luasrc/controller/smartinfo.lua b/luci-app-smartinfo/luasrc/controller/smartinfo.lua deleted file mode 100644 index 5938adcc7..000000000 --- a/luci-app-smartinfo/luasrc/controller/smartinfo.lua +++ /dev/null @@ -1,132 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - smartctl support - -Script by animefans_xj @ nvacg.org (af_xj@hotmail.com , xujun@smm.cn) - -Licensed under the GNU GPL License, Version 3 (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.gnu.org/licenses/gpl.txt - -$Id$ -]]-- - - -module("luci.controller.smartinfo",package.seeall) - -function index() - require("luci.i18n") - luci.i18n.loadc("smartinfo") - if not nixio.fs.access("/etc/config/smartinfo") then - return - end - - local page = entry({"admin","nas","smartinfo"},cbi("smartinfo"),_("S.M.A.R.T Info")) - page.i18n="smartinfo" - page.dependent=true - entry({"admin","nas","smartinfo","smartdetail"},call("smart_detail")).leaf = true - - entry({"admin","nas","smartinfo","status"}, call("smart_status")).leaf = true - entry({"admin","nas","smartinfo","run"},call("run_smart")).leaf=true - entry({"admin","nas","smartinfo","smartattr"},call("smart_attr")).leaf=true - -end - - -function smart_status() - local cmd = io.popen("/usr/lib/smartinfo/smart_status.sh") - if cmd then - local dev = { } - while true do - local ln = cmd:read("*l") - if not ln then - break - elseif ln:match("^/%l+/%l+:%a+") then - local name,status = ln:match("^/%l+/(%l+):(%a+)") - local model,size - - if (status=="OK" or status=="Failed" or status=="Unsupported") then - model="%s %s" % {nixio.fs.readfile("/sys/class/block/%s/device/vendor" % name), nixio.fs.readfile("/sys/class/block/%s/device/model" % name)} - local s = tonumber((nixio.fs.readfile("/sys/class/block/%s/size" % name))) - size = "%s MB" % {s and math.floor(s / 2048)} - else - model="Unavailabled" - size="Unavailabled" - end - - if name and status then - dev[#dev+1]= { - name = name, - model = model, - size = size, - status = status - } - end - else - - end - end - - cmd:close() - luci.http.prepare_content("application/json") - luci.http.write_json(dev) - end -end - -function run_smart(dev) - local cmd = io.popen("smartctl --attributes -d sat /dev/%s" % dev) - if cmd then - local report = {} - local ln = cmd:read("*all") - report = { - out = ln - } - cmd:close() - luci.http.prepare_content("application/json") - luci.http.write_json(report) - end -end - -function smart_detail(dev) - luci.template.render("smartinfo/smart_detail", {dev=dev}) -end - -function smart_attr(dev) - local cmd = io.popen("smartctl --attributes -d sat /dev/%s" % dev) - if cmd then - local attr = { } - while true do - local ln = cmd:read("*l") - if not ln then - break - elseif ln:match("^.*%d+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+") then - local id,attrbute,flag,value,worst,thresh,type,updated,raw = ln:match("^%s*(%d+)%s+([%a%p]+)%s+(%w+)%s+(%d+)%s+(%d+)%s+(%d+)%s+([%a%p]+)%s+(%a+)%s+[%w%p]+%s+(.+)") - - id= "%x" % id - if not id:match("^%w%w") then - id = "0%s" % id - end - - attr[#attr+1]= { - id = id:upper(), - attrbute = attrbute, - flag = flag, - value = value, - worst = worst, - thresh = thresh, - type = type, - updated = updated, - raw = raw - } - else - - end - end - - cmd:close() - luci.http.prepare_content("application/json") - luci.http.write_json(attr) - end - -end diff --git a/luci-app-smartinfo/luasrc/model/cbi/smartinfo.lua b/luci-app-smartinfo/luasrc/model/cbi/smartinfo.lua deleted file mode 100644 index 4cea5b3eb..000000000 --- a/luci-app-smartinfo/luasrc/model/cbi/smartinfo.lua +++ /dev/null @@ -1,85 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - smartctl support - -Script by animefans_xj @ nvacg.org (af_xj@hotmail.com , xujun@smm.cn) - -Licensed under the GNU GPL License, Version 3 (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.gnu.org/licenses/gpl.txt - -$Id$ -]]-- - - -require("luci.sys") -require("luci.util") - -m=Map("smartinfo",translate("S.M.A.R.T Info"),translate("This widget allow you to monitor your storage devices which support S.M.A.R.T technology.")) - -s=m:section(TypedSection,"smartinfo",translate("S.M.A.R.T Report Settings")) -s.addremove=false -s.anonymous=true - -m:section(SimpleSection).template="smartinfo/smart_status" - -s:tab("general",translate("Global")) ---s:tab("detail",translate("Detail")) - -enable=s:taboption("general", Flag, "enabled", translate("Enabled")) -enable.rmempty=false -enable.default = 0 -function enable.cfgvalue(self,section) - return luci.sys.init.enabled("smartinfo") and self.enabled or self.disabled -end -function enable.write(self,section,value) - if value == "1" then - luci.sys.call("/etc/init.d/smartinfo stop > /dev/null") - luci.sys.call("/etc/init.d/smartinfo enable > /dev/null") - luci.sys.call("/etc/init.d/smartinfo start > /dev/null") - else - luci.sys.call("/etc/init.d/smartinfo stop > /dev/null") - luci.sys.call("/etc/init.d/smartinfo disable > /dev/null") - end -end -check_interval=s:taboption("general",Value,"time_step",translate("Check Interval")) -check_interval.rmempty=false -check_interval.placeholder="1" -check_interval.datatype="range(1,60)" -check_interval.template="smartinfo/detail_begin_value" -time_unit=s:taboption("general",ListValue,"time_unit",translate("Unit")) -time_unit.rmempty=false -time_unit.template="smartinfo/detail_end_listvalue" -time_unit:value("minute",translate("Minutes")) -time_unit:value("hour",translate("Hours")) -time_unit:value("day",translate("Days")) -time_unit:value("week",translate("Weeks")) - -log_enable=s:taboption("general",Flag,"log_enabled",translate("Log to file")) -log_enable.template="smartinfo/detail_begin_flag" -log_enable.rmempty=false -log_path=s:taboption("general",Value,"log_path",translate("at")) -log_path.template="smartinfo/detail_end_value" -log_path.placeholder="/tmp/log" -log_path.rmempty=false - -touch_enable=s:taboption("general",Flag,"touch_enabled",translate("Touch a warning file when disk fail")) -touch_enable.template="smartinfo/detail_begin_flag" -touch_enable.rmempty=false -touch_path=s:taboption("general",Value,"touch_path",translate("at")) -touch_path.template="smartinfo/detail_end_value" -touch_path.placeholder="/tmp" -touch_path.rmempty=false - -devices=s:taboption("general",DynamicList,"device",translate("Devices to monitor")) -local device_name = nil -local device_model = nil -for d in nixio.fs.glob("/dev/sd?") do - device_name=nixio.fs.basename(d) - device_model="%s %s" % {nixio.fs.readfile("/sys/class/block/%s/device/vendor" % device_name), nixio.fs.readfile("/sys/class/block/%s/device/model" % device_name)} - devices:value(d, "%s: %s" % {device_name, device_model}) -end - - -return m diff --git a/luci-app-smartinfo/luasrc/view/admin_status/index/smartinfo.htm b/luci-app-smartinfo/luasrc/view/admin_status/index/smartinfo.htm deleted file mode 100644 index 8af75d197..000000000 --- a/luci-app-smartinfo/luasrc/view/admin_status/index/smartinfo.htm +++ /dev/null @@ -1 +0,0 @@ -<%+smartinfo/smart_status%> diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_flag.htm b/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_flag.htm deleted file mode 100644 index e294a1b65..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_flag.htm +++ /dev/null @@ -1,10 +0,0 @@ - - - /> -<%+cbi/valueheader%> - /> diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_value.htm b/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_value.htm deleted file mode 100644 index edca5138b..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/detail_begin_value.htm +++ /dev/null @@ -1,7 +0,0 @@ - - -<%+cbi/valueheader%> - /> diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_listvalue.htm b/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_listvalue.htm deleted file mode 100644 index 64bed9951..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_listvalue.htm +++ /dev/null @@ -1,20 +0,0 @@ - - -  -<% if self.widget == "select" then %> - -<% elseif self.widget == "radio" then - local c = 0 - for i, key in pairs(self.keylist) do - c = c + 1 -%> - /> - ><%=self.vallist[i]%> -<% if c == self.size then c = 0 %><% if self.orientation == "horizontal" then %> <% else %>
<% end %> -<% end end %> -<% end %> -<%+cbi/valuefooter%> diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_value.htm b/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_value.htm deleted file mode 100644 index 79c092cfa..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/detail_end_value.htm +++ /dev/null @@ -1,9 +0,0 @@ - - -   - <%= self.title %> - /> -<%+cbi/valuefooter%> diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/smart_detail.htm b/luci-app-smartinfo/luasrc/view/smartinfo/smart_detail.htm deleted file mode 100644 index c9f553916..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/smart_detail.htm +++ /dev/null @@ -1,82 +0,0 @@ - - - S.M.A.R.T detail of <%=dev%> - - - - - - - -
-
-
- <%:S.M.A.R.T Attrbutes%>: /dev/<%=dev%> - - - - - - - - - - - - - - - -
<%:ID%><%:Attrbute%><%:Flag%><%:Value%><%:Worst%><%:Thresh%><%:Type%><%:Updated%><%:Raw%>

<%:Collecting data...%>
-
-
-
- - \ No newline at end of file diff --git a/luci-app-smartinfo/luasrc/view/smartinfo/smart_status.htm b/luci-app-smartinfo/luasrc/view/smartinfo/smart_status.htm deleted file mode 100644 index 1fe8012a1..000000000 --- a/luci-app-smartinfo/luasrc/view/smartinfo/smart_status.htm +++ /dev/null @@ -1,80 +0,0 @@ - - - - -
- <%:S.M.A.R.T Status%> - - - - - - - - - - - -
<%:Device%><%:Model%><%:Size%><%:Status%><%:Detail%>

<%:Collecting data...%>
-
diff --git a/luci-app-smartinfo/po/zh-cn/smartinfo.po b/luci-app-smartinfo/po/zh-cn/smartinfo.po deleted file mode 100644 index 753bcbef7..000000000 --- a/luci-app-smartinfo/po/zh-cn/smartinfo.po +++ /dev/null @@ -1,116 +0,0 @@ -msgid "" -msgstr "" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-02-12 20:45+0800\n" -"PO-Revision-Date: 2016-02-16 21:20+0800\n" -"Last-Translator: 穿越蓝天 < admin@nvacg.com , af_xj@hotmail.com >\n" -"Language: 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.5.4\n" -"Project-Id-Version: \n" -"Language-Team: \n" - -msgid "S.M.A.R.T Info" -msgstr "磁盘监控" - -msgid "This widget allow you to monitor your storage devices which support S.M.A.R.T technology." -msgstr "该工具帮助您通过S.M.A.R.T技术来监控您硬盘的健康状况." - -msgid "S.M.A.R.T Report Settings" -msgstr "S.M.A.R.T 报告设置" - -msgid "Enabled" -msgstr "启用" - -msgid "Check Interval" -msgstr "检查间隔" - -msgid "Unit" -msgstr "单位" - -msgid "Minutes" -msgstr "分" - -msgid "Hours" -msgstr "小时" - -msgid "Days" -msgstr "天" - -msgid "Weeks" -msgstr "周" - -msgid "Log to file" -msgstr "记录到文件" - -msgid "at" -msgstr "位于" - -msgid "Touch a warning file when disk fail" -msgstr "故障时生成示警文件" - -msgid "Devices to monitor" -msgstr "监控这些设备" - -msgid "S.M.A.R.T Status" -msgstr "S.M.A.R.T 状况" - -msgid "Device" -msgstr "设备" - -msgid "Model" -msgstr "型号" - -msgid "Size" -msgstr "大小" - -msgid "Status" -msgstr "状况" - -msgid "Collecting data..." -msgstr "正在收集数据" - -msgid "Detail" -msgstr "详细" - -msgid "Show" -msgstr "查看" - -msgid "Global" -msgstr "全局" - -msgid "No Attrbute to display." -msgstr "没有可显示的属性" - -msgid "S.M.A.R.T Attrbutes" -msgstr "S.M.A.R.T 属性" - -msgid "ID" -msgstr "ID" - -msgid "Attrbute" -msgstr "属性名称" - -msgid "Flag" -msgstr "标记" - -msgid "Value" -msgstr "值" - -msgid "Worst" -msgstr "最坏" - -msgid "Thresh" -msgstr "临界值" - -msgid "Type" -msgstr "类型" - -msgid "Updated" -msgstr "更新" - -msgid "Raw" -msgstr "原始值" diff --git a/luci-app-smartinfo/po/zh_Hans b/luci-app-smartinfo/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-smartinfo/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-smartinfo/root/etc/config/smartinfo b/luci-app-smartinfo/root/etc/config/smartinfo deleted file mode 100644 index 8205c01fa..000000000 --- a/luci-app-smartinfo/root/etc/config/smartinfo +++ /dev/null @@ -1,11 +0,0 @@ - -config smartinfo 'main' - option enabled '1' - list device '/dev/sda' - option log_enabled '1' - option log_path '/tmp/DiskLog' - option time_unit 'day' - option time_step '1' - option touch_enabled '1' - option touch_path '/tmp' - diff --git a/luci-app-smartinfo/root/etc/init.d/smartinfo b/luci-app-smartinfo/root/etc/init.d/smartinfo deleted file mode 100755 index 9f9b30c90..000000000 --- a/luci-app-smartinfo/root/etc/init.d/smartinfo +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh /etc/rc.common -START=95 - -boot() { - return 0 -} - -start() { - /usr/lib/smartinfo/smart_checker.sh > /dev/null 2>&1 & -} - -stop() { - killall -9 smart_checker.sh -} - diff --git a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_checker.sh b/luci-app-smartinfo/root/usr/lib/smartinfo/smart_checker.sh deleted file mode 100644 index 3153749e4..000000000 --- a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_checker.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# Copyright (C) 2016 NVACG , 穿越蓝天/animefans_xj -# af_xj@hotmail.com -# -#Licensed under the GNU GPL License, Version 3 (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.gnu.org/licenses/gpl.txt - -. /lib/functions.sh -. /usr/lib/smartinfo/smart_functions.sh - -config_load "smartinfo" - -init() { - local enable time time_step time_unit log_enabled log_path touch_enable touch_path -} - -config_get enabled main enabled -config_get time_step main time_step -config_get time_unit main time_unit -config_get log_enabled main log_enabled -config_get log_path main log_path -config_get touch_enabled main touch_enabled -config_get touch_path main touch_path - -echo "SMART CHECK SCRIPT" -echo "Paramter ===================" -echo "Enabled: $enabled" -echo "Time Step: $time_step" -echo "Time Unit: $time_unit" -echo "Log: $log_enabled" -echo "Log ath: $log_path" -echo "Warn: $touch_enabled" -echo "Warn Path: $touch_path" - -if [ $enabled -ne 1 ]; then - exit 0 -fi - - -# 根据选定的时间间隔生成秒数 -case $time_unit in - "minute") - let time=time_step*60 - ;; - "hour") - let time=time_step*3600 - ;; - "day") - let time=time_step*86400 - ;; - "week") - let time=time_step*604800 - ;; - *) - let time=time_step*60 - ;; -esac - -echo "Calculated Time: $time" -echo "" - - -main_process() { - do_smart_check $1 - local RETVAL=$? - local Device=`echo $1 |awk -F/ '{print $NF}'` - local Model - # 如果指定的磁盘在线则获取型号 - [ -d /sys/class/block/$Device ] && Model=`cat /sys/class/block/$Device/device/vendor``cat /sys/class/block/$Device/device/model` - # 如果路径不存在则创建目录 - [ $log_enabled ] && ! [ -d $log_path ] && mkdir -p $log_path - [ $touch_enabled ] && ! [ -d $touch_path ] && mkdir -p $touch_path - case $RETVAL in - 0) - # 设备健康 - [ $log_enabled ] && [ -d $log_path ] && echo `date '+%Y-%m-%d %H:%M'`" [$Model]: Passed Disk Check." >> $log_path/smartinfo_$Device.log - [ -f $touch_path/$Device-DiskDamaged ] && rm $touch_path/$Device-DiskDamaged - ;; - 1) - # 设备故障 - logger -p daemon.warn -t smartinfo "Device $1 S.M.A.R.T status Failed!" - [ $log_enabled ] && [ -d $log_path ] && echo `date '+%Y-%m-%d %H:%M'`" [$Model]: Disk Damaged." >> $log_path/smartinfo_$Device.log - [ $touch_enabled ] && [ -d $touch_path ] && touch $touch_path/$Device-DiskDamaged - ;; - 2) - # 设备离线 - logger -p daemon.warn -t smartinfo "Device $1 Offline." - [ $log_enabled ] && [ -d $log_path ] && echo `date '+%Y-%m-%d %H:%M'`" : Disk Offline." >> $log_path/smartinfo_$Device.log - [ -f $touch_path/$Device-DiskDamaged ] && rm $touch_path/$Device-DiskDamaged - ;; - 3) - # 不支持S.M.A.R.T - [ $log_enabled ] && [ -d $log_path ] && echo `date '+%Y-%m-%d %H:%M'`" [$Model]: Unsupport S.M.A.R.T." >> $log_path/smartinfo_$Device.log - [ -f $touch_path/$Device-DiskDamaged ] && rm $touch_path/$Device-DiskDamaged - ;; - *) - ;; - esac -} - - -while [ true ] -do - echo `date '+%Y-%m-%d %H:%M'`" Execute the S.M.A.R.T Check." - # 执行S.M.A.R.T检测 - config_list_foreach main device main_process - - # 循环延迟 - sleep $time -done - - diff --git a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_functions.sh b/luci-app-smartinfo/root/usr/lib/smartinfo/smart_functions.sh deleted file mode 100644 index 6a0c3aa38..000000000 --- a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_functions.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# Copyright (C) 2016 NVACG , 穿越蓝天/animefans_xj -# af_xj@hotmail.com -# -#Licensed under the GNU GPL License, Version 3 (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.gnu.org/licenses/gpl.txt - -do_smart_check() { - # 看看指定的设备是否在线 - if [ -b $1 ]; then - # 如果在线,看看是否支持S.M.A.R.T。支持,则对其执行SMART检测,判断输出是否包含PASSED - result=`/usr/sbin/smartctl -d sat -H $1` - if [ $? -eq "2" ]; then - # 不支持S.M.A.R.T - return 3 - fi - - result=`echo $result | grep -c PASSED` - if [ $result -ne 0 ]; then - #无故障 - return 0 - else - #故障 - return 1 - fi - else - # 设备离线 - return "2" - fi -} - -do_simple_smart_check() { - # 看看指定的设备是否在线 - if [ -b $1 ]; then - # 如果在线,则对其执行SMART检测,判断输出是否包含PASSED - result=`/usr/sbin/smartctl -d sat -H $1` - if [ $? -eq "2" ]; then - # 不支持S.M.A.R.T - echo "$1:Unsupported" - exit 0 - fi - - result=`echo $result | grep -c PASSED` - if [ $result -ne 0 ]; then - echo "$1:OK" - else - echo "$1:Failed" - fi - else - # 如果不在线则输出设备离线 - echo "$1:Offline" - fi -} diff --git a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_status.sh b/luci-app-smartinfo/root/usr/lib/smartinfo/smart_status.sh deleted file mode 100644 index d5203c90f..000000000 --- a/luci-app-smartinfo/root/usr/lib/smartinfo/smart_status.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# Copyright (C) 2016 NVACG , 穿越蓝天/animefans_xj -# af_xj@hotmail.com -# -#Licensed under the GNU GPL License, Version 3 (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.gnu.org/licenses/gpl.txt - -. /lib/functions.sh -. /usr/lib/smartinfo/smart_functions.sh - -config_load "smartinfo" - -# 执行S.M.A.R.T检测 -config_list_foreach main device do_simple_smart_check diff --git a/luci-app-smartinfo/root/usr/share/rpcd/acl.d/luci-app-smartinfo.json b/luci-app-smartinfo/root/usr/share/rpcd/acl.d/luci-app-smartinfo.json deleted file mode 100644 index 997d55a21..000000000 --- a/luci-app-smartinfo/root/usr/share/rpcd/acl.d/luci-app-smartinfo.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-smartinfo": { - "description": "Grant UCI access for luci-app-smartinfo", - "read": { - "uci": [ "smartinfo" ] - }, - "write": { - "uci": [ "smartinfo" ] - } - } -} diff --git a/luci-app-smartinfo/root/usr/share/ucitrack/luci-app-smartinfo.json b/luci-app-smartinfo/root/usr/share/ucitrack/luci-app-smartinfo.json deleted file mode 100644 index 9bec113bc..000000000 --- a/luci-app-smartinfo/root/usr/share/ucitrack/luci-app-smartinfo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "smartinfo", - "init": "smartinfo" -} diff --git a/luci-app-ssrserver-python/Makefile b/luci-app-ssrserver-python/Makefile deleted file mode 100644 index b58375804..000000000 --- a/luci-app-ssrserver-python/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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:=+python3 -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-ssrserver-python -PKG_VERSION:=3.2.0 -PKG_RELEASE:=3 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-ssrserver-python/luasrc/controller/ssrs.lua b/luci-app-ssrserver-python/luasrc/controller/ssrs.lua deleted file mode 100644 index cf022adaf..000000000 --- a/luci-app-ssrserver-python/luasrc/controller/ssrs.lua +++ /dev/null @@ -1,18 +0,0 @@ -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 - 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 -w | grep ssrs.json |grep -v grep >/dev/null") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end diff --git a/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua b/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua deleted file mode 100644 index a83c400f3..000000000 --- a/luci-app-ssrserver-python/luasrc/model/cbi/ssrs.lua +++ /dev/null @@ -1,81 +0,0 @@ -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", -"chacha20", -"chacha20-ietf-poly1305", -} - -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") -a.title = 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") -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/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm b/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm deleted file mode 100644 index dd5c11285..000000000 --- a/luci-app-ssrserver-python/luasrc/view/ssrs/ssrs_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-ssrserver-python/po/zh-cn/ssrs.po b/luci-app-ssrserver-python/po/zh-cn/ssrs.po deleted file mode 100644 index e3b31875f..000000000 --- a/luci-app-ssrserver-python/po/zh-cn/ssrs.po +++ /dev/null @@ -1,35 +0,0 @@ -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/luci-app-ssrserver-python/po/zh_Hans b/luci-app-ssrserver-python/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-ssrserver-python/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-ssrserver-python/root/etc/config/ssrs b/luci-app-ssrserver-python/root/etc/config/ssrs deleted file mode 100644 index a85b5ca5e..000000000 --- a/luci-app-ssrserver-python/root/etc/config/ssrs +++ /dev/null @@ -1,11 +0,0 @@ - -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/luci-app-ssrserver-python/root/etc/init.d/ssrs b/luci-app-ssrserver-python/root/etc/init.d/ssrs deleted file mode 100755 index e6bb0835c..000000000 --- a/luci-app-ssrserver-python/root/etc/init.d/ssrs +++ /dev/null @@ -1,96 +0,0 @@ -#!/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_ipv6": "::", - "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 -} diff --git a/luci-app-ssrserver-python/root/etc/uci-defaults/luci-ssrs b/luci-app-ssrserver-python/root/etc/uci-defaults/luci-ssrs deleted file mode 100644 index a2236353a..000000000 --- a/luci-app-ssrserver-python/root/etc/uci-defaults/luci-ssrs +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-ssrs.json" ] && { - cat > /usr/share/ucitrack/luci-app-ssrs.json << EEOF -{ - "config": "ssrs", - "init": "ssrs" -} -EEOF -} - -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/luci-app-ssrserver-python/root/usr/share/rpcd/acl.d/luci-app-ssrserver-python.json b/luci-app-ssrserver-python/root/usr/share/rpcd/acl.d/luci-app-ssrserver-python.json deleted file mode 100644 index 277c67983..000000000 --- a/luci-app-ssrserver-python/root/usr/share/rpcd/acl.d/luci-app-ssrserver-python.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-ssrserver-python": { - "description": "Grant UCI access for luci-app-ssrserver-python", - "read": { - "uci": [ "ssrs" ] - }, - "write": { - "uci": [ "ssrs" ] - } - } -} diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml b/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml deleted file mode 100644 index 014fa0714..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES b/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES deleted file mode 100644 index 0cd91768e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/CHANGES +++ /dev/null @@ -1,342 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md b/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md deleted file mode 100644 index fbdb9c11b..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/CONTRIBUTING.md +++ /dev/null @@ -1,29 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile b/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile deleted file mode 100644 index ec7b2c6e7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE b/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in b/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in deleted file mode 100644 index 1882dd7dc..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -recursive-include shadowsocks *.py -include README.rst -include LICENSE diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/README.md b/luci-app-ssrserver-python/root/usr/share/ssr/README.md deleted file mode 100644 index 53eafeaa8..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/README.md +++ /dev/null @@ -1,105 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/README.rst b/luci-app-ssrserver-python/root/usr/share/ssr/README.rst deleted file mode 100644 index bf2a3ec32..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/README.rst +++ /dev/null @@ -1,113 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py b/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py deleted file mode 100644 index 5ee8be127..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/apiconfig.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py b/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py deleted file mode 100644 index 9bf4d0936..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/asyncmgr.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/config.json b/luci-app-ssrserver-python/root/usr/share/ssr/config.json deleted file mode 100644 index 55f12b5f4..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/config.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py b/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py deleted file mode 100644 index cf9d61961..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/configloader.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py b/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py deleted file mode 100644 index 67bda6083..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/db_transfer.py +++ /dev/null @@ -1,631 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog b/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog deleted file mode 100644 index 4e7ad163f..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat b/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat deleted file mode 100644 index 45a4fb75d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/compat +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json b/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json deleted file mode 100644 index 35cb14a6c..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/debian/control b/luci-app-ssrserver-python/root/usr/share/ssr/debian/control deleted file mode 100644 index da0092004..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/control +++ /dev/null @@ -1,19 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright b/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright deleted file mode 100644 index 7be81625e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/copyright +++ /dev/null @@ -1,30 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs b/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs deleted file mode 100644 index 0208fc1fe..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/docs +++ /dev/null @@ -1,2 +0,0 @@ -README.md -README.rst diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d b/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d deleted file mode 100644 index 2f4f3521e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/init.d +++ /dev/null @@ -1,149 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/debian/install b/luci-app-ssrserver-python/root/usr/share/ssr/debian/install deleted file mode 100644 index a61486462..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/install +++ /dev/null @@ -1 +0,0 @@ -debian/config.json etc/shadowsocks/ \ No newline at end of file diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules b/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules deleted file mode 100755 index 62e2bb6f6..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/rules +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- - -%: - dh $@ --with python2 --buildsystem=python_distutils diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default b/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default deleted file mode 100644 index a52060279..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.default +++ /dev/null @@ -1,12 +0,0 @@ -# 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/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages b/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages deleted file mode 100644 index 3df8a3341..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/shadowsocks.manpages +++ /dev/null @@ -1,2 +0,0 @@ -debian/sslocal.1 -debian/ssserver.1 \ No newline at end of file diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format b/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format deleted file mode 100644 index 163aaf8d8..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1 b/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1 deleted file mode 100644 index 0c2cf5165..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/sslocal.1 +++ /dev/null @@ -1,59 +0,0 @@ -.\" 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/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1 b/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1 deleted file mode 100644 index 0c2cf5165..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/debian/ssserver.1 +++ /dev/null @@ -1,59 +0,0 @@ -.\" 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/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py b/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py deleted file mode 100644 index c917cb7d9..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/importloader.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat b/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat deleted file mode 100644 index 6d0bce695..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.bat +++ /dev/null @@ -1,4 +0,0 @@ -@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/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh b/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh deleted file mode 100755 index 862d1abed..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/initcfg.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh b/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh deleted file mode 100755 index 09b07f332..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/initmudbjson.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh b/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh deleted file mode 100755 index 94153fe9e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/logrun.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json b/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json deleted file mode 100644 index 0d4f101c7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/mudb.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py b/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py deleted file mode 100644 index 2eb05d59d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/mujson_mgr.py +++ /dev/null @@ -1,358 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json b/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json deleted file mode 100644 index 1849e9e81..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/mysql.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/run.sh b/luci-app-ssrserver-python/root/usr/share/ssr/run.sh deleted file mode 100755 index 0de8d204c..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/server.py b/luci-app-ssrserver-python/root/usr/share/ssr/server.py deleted file mode 100644 index ba863b684..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/server.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py b/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py deleted file mode 100644 index d159817a3..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/server_pool.py +++ /dev/null @@ -1,293 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py deleted file mode 100644 index c4484c046..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/common.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py deleted file mode 100644 index 401c7b726..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py deleted file mode 100644 index efecfd415..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_libsodium.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py deleted file mode 100644 index 0ef8ce0f8..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/ctypes_openssl.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py deleted file mode 100644 index 0a8ca53fb..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/openssl.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py deleted file mode 100644 index b0105ec29..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/rc4_md5.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py deleted file mode 100644 index 51d476bed..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/sodium.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py deleted file mode 100644 index 60c2f2451..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/crypto/table.py +++ /dev/null @@ -1,181 +0,0 @@ -# !/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py deleted file mode 100644 index 44f905250..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py deleted file mode 100644 index d5e507789..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/encrypt_test.py +++ /dev/null @@ -1,51 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py deleted file mode 100644 index 341620efd..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/eventloop.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py deleted file mode 100755 index 9f54d9306..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/local.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh deleted file mode 100755 index fc081e1d2..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/logrun.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py deleted file mode 100644 index b05f363d8..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/lru_cache.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/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.abc.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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py deleted file mode 100644 index 80d0a320b..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/manager.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py deleted file mode 100644 index 3dfdb141f..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfs.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py deleted file mode 100644 index 401c7b726..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py deleted file mode 100755 index a745e098f..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/auth.py +++ /dev/null @@ -1,787 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py deleted file mode 100644 index ff3c5fdfb..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/http_simple.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py deleted file mode 100644 index 8c6355c38..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/plain.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py deleted file mode 100644 index 0dc0ca6d1..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/obfsplugin/verify.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py deleted file mode 100644 index e1918f5e0..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/ordereddict.py +++ /dev/null @@ -1,214 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh deleted file mode 100755 index 43720308d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py deleted file mode 100755 index 081538933..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/server.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py deleted file mode 100755 index a1547d082..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/shell.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh deleted file mode 100755 index d7d29589e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/stop.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh deleted file mode 100755 index aa3713939..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tail.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -tail -f ssserver.log diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py deleted file mode 100644 index 595e2be73..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/tcprelay.py +++ /dev/null @@ -1,1476 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py deleted file mode 100644 index b9606cd81..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/udprelay.py +++ /dev/null @@ -1,656 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py b/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py deleted file mode 100644 index f3e1ef796..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/shadowsocks/version.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh b/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh deleted file mode 100755 index 56567daa0..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/stop.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py b/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py deleted file mode 100644 index 6687e12cf..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/switchrule.py +++ /dev/null @@ -1,8 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh deleted file mode 100755 index f36f605ee..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tail.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd `dirname $0` -tail -f ssserver.log diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json deleted file mode 100644 index 40d0b2107..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb1.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json deleted file mode 100644 index fb7014b1e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-cfb8.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json deleted file mode 100644 index 1fed8a8c7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes-ctr.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json deleted file mode 100644 index a3d95b9b5..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/aes.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh deleted file mode 100755 index b0c679cbc..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/assert.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json deleted file mode 100644 index 541a9beb5..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/chacha20.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json deleted file mode 100644 index 1823c2a75..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/client-multi-server-ip.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py b/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py deleted file mode 100644 index 23cc8cd71..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/coverage_server.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json deleted file mode 100644 index f3980b65e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/fastopen.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json deleted file mode 100644 index 6c3cfaf8d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6-client-side.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json deleted file mode 100644 index d855f9c47..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/ipv6.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh deleted file mode 100755 index ea5c1630b..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/jenkins.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh deleted file mode 100755 index b0e35fa13..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/libsodium/install.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py b/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py deleted file mode 100644 index 86b1a8653..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/nose_plugin.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json deleted file mode 100644 index 26ba0dfa7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/rc4-md5.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json deleted file mode 100644 index 5ca6c45f5..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20-ctr.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json deleted file mode 100644 index 7e303800d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/salsa20.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json deleted file mode 100644 index c822c98b9..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-client-side.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "server": "127.0.0.1", - "server_port": "8385", - "local_port": 1081, - "password": "foobar5", - "timeout": 60, - "method": "aes-256-cfb" -} diff --git a/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json deleted file mode 100644 index a2c0a8089..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd-table.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json deleted file mode 100644 index b1407f0a1..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-passwd.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json deleted file mode 100644 index 5bdbcab64..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/server-multi-ports.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh deleted file mode 100755 index 1a5fa208b..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/setup_tc.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh deleted file mode 100755 index 8eff72df0..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/install.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf b/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf deleted file mode 100644 index 13db772fd..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/socksify/socks.conf +++ /dev/null @@ -1,5 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json deleted file mode 100644 index cca6ac27e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/table.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py deleted file mode 100755 index 408340134..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh deleted file mode 100755 index a1a777b0d..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_command.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh deleted file mode 100755 index 7a192bdb1..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_daemon.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh deleted file mode 100755 index 7a61caff1..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_large_file.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py deleted file mode 100644 index e8fa5057e..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh b/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh deleted file mode 100755 index 6a778abc1..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/test_udp_src.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/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/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json b/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json deleted file mode 100644 index 2015ff6c6..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/tests/workers.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md b/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md deleted file mode 100644 index f624309c7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/utils/README.md +++ /dev/null @@ -1,9 +0,0 @@ -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/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py b/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py deleted file mode 100755 index 8a8acd867..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/utils/autoban.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/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 re -import sys -import argparse - -_VALID_IP_RE = re.compile(r'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$') - -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 and _VALID_IP_RE.match(ip): - 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/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf b/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf deleted file mode 100644 index 9b1c7ec7a..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ssr/utils/fail2ban/shadowsocks.conf +++ /dev/null @@ -1,5 +0,0 @@ -[Definition] - -_daemon = shadowsocks - -failregex = ^\s+ERROR\s+can not parse header when handling connection from :\d+$ diff --git a/luci-app-ssrserver-python/root/usr/share/ucitrack/luci-app-ssrserver-python.json b/luci-app-ssrserver-python/root/usr/share/ucitrack/luci-app-ssrserver-python.json deleted file mode 100644 index 86ee6d5d7..000000000 --- a/luci-app-ssrserver-python/root/usr/share/ucitrack/luci-app-ssrserver-python.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "ssrs", - "init": "ssrs" -} diff --git a/luci-app-unblockmusic/Makefile b/luci-app-unblockmusic/Makefile deleted file mode 100644 index 5c17fc481..000000000 --- a/luci-app-unblockmusic/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2020 Openwrt.org -# -# This is free software, licensed under the Apache License, Version 2.0 . -# -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-unblockmusic -PKG_VERSION:=2.3.5 -PKG_RELEASE:=1 - -PKG_LICENSE:=Apache-2.0 -PKG_MAINTAINER:=lean - -PKG_CONFIG_DEPENDS:= \ - CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_Go \ - CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_NodeJS - -LUCI_TITLE:=LuCI support for Unblock NeteaseCloudMusic -LUCI_PKGARCH:=all -LUCI_DEPENDS:=+dnsmasq-full +ipset +uclient-fetch \ - +PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_Go:UnblockNeteaseMusic-Go \ - +PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_NodeJS:UnblockNeteaseMusic - -define Package/$(PKG_NAME)/config -config PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_Go - bool "UnblockNeteaseMusic Golang Version" - default y if i386||x86_64||arm||aarch64 - -config PACKAGE_$(PKG_NAME)_INCLUDE_UnblockNeteaseMusic_NodeJS - bool "UnblockNeteaseMusic NodeJS Version" - depends on HAS_FPU || KERNEL_MIPS_FPU_EMULATOR || SOFT_FLOAT - default n -endef - -define Package/$(PKG_NAME)/conffiles -/etc/config/unblockmusic -endef - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua b/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua deleted file mode 100644 index 3f7616e98..000000000 --- a/luci-app-unblockmusic/luasrc/controller/unblockmusic.lua +++ /dev/null @@ -1,19 +0,0 @@ -module("luci.controller.unblockmusic", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/unblockmusic") then - return - end - - entry({"admin", "services", "unblockmusic"}, alias("admin", "services", "unblockmusic", "general"), _("Unblock Netease Music"), 50).dependent = true - entry({"admin", "services", "unblockmusic", "general"}, cbi("unblockmusic/unblockmusic"), _("Base Setting"), 1).leaf = true - entry({"admin", "services", "unblockmusic", "log"}, form("unblockmusic/unblockmusic_log"), _("Log"), 2).leaf = true - 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 UnblockNeteaseMusic | grep -v grep | grep -v logcheck.sh >/dev/null") == 0 - luci.http.prepare_content("application/json") - luci.http.write_json(e) -end diff --git a/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic.lua b/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic.lua deleted file mode 100644 index 7dcc39c08..000000000 --- a/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic.lua +++ /dev/null @@ -1,128 +0,0 @@ -local fs = require "nixio.fs" - -mp = Map("unblockmusic") -mp.title = 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.description = translate("启用后,路由器自动分流解锁,大部分设备无需设置代理") -enabled.default = 0 -enabled.rmempty = false - -apptype = s:option(ListValue, "apptype", translate("解锁程序选择")) -if nixio.fs.access("/usr/bin/UnblockNeteaseMusic") then -apptype:value("go", translate("Golang 版本")) -end -if nixio.fs.access("/usr/share/UnblockNeteaseMusic/app.js") then -apptype:value("nodejs", translate("NodeJS 版本")) -end -apptype:value("cloud", translate("云解锁( [CTCGFW] 云服务器)")) - -speedtype = s:option(Value, "musicapptype", translate("音源选择")) -speedtype:value("default", translate("默认")) -speedtype:value("netease", translate("网易云音乐")) -speedtype:value("qq", translate("QQ音乐")) -speedtype:value("baidu", translate("百度音乐")) -speedtype:value("kugou", translate("酷狗音乐")) -speedtype:value("kuwo", translate("酷我音乐")) -speedtype:value("migu", translate("咪咕音乐")) -speedtype:value("joox", translate("JOOX音乐")) -speedtype.default = "kuwo" -speedtype:depends("apptype", "nodejs") -speedtype:depends("apptype", "go") - -cloudserver = s:option(Value, "cloudserver", translate("服务器位置")) -cloudserver.description = translate("自定义服务器格式为 IP[域名]:HTTP端口:HTTPS端口
如果服务器为LAN内网IP,需要将这个服务器IP放入例外客户端 (不代理HTTP和HTTPS)") -cloudserver.default = "" -cloudserver.rmempty = true -cloudserver:depends("apptype", "cloud") - -search_limit = s:option(Value, "search_limit", translate("搜索结果限制")) -search_limit.description = translate("在搜索页面显示其他平台搜索结果个数,可填(0-3)") -search_limit.default = "0" -search_limit:depends("apptype", "go") - -flac = s:option(Flag, "flac_enabled", translate("启用无损音质")) -flac.description = translate("目前仅支持酷我、QQ、咪咕") -flac.default = "1" -flac.rmempty = false -flac:depends("apptype", "nodejs") -flac:depends("apptype", "go") - -replace_music_source = s:option(ListValue, "replace_music_source", translate("强制音乐音源替换")) -replace_music_source:value("0", translate("不强制替换音乐音源")) -replace_music_source:value("192000", translate("当音质低于 192 Kbps(中)时")) -replace_music_source:value("320000", translate("当音质低于 320 Kbps(高)时")) -replace_music_source:value("600000", translate("当音质低于 999 Kbps(无损)时")) -replace_music_source.description = translate("当音乐音质低于指定数值时,尝试强制使用其他平台的高音质版本进行替换") -replace_music_source.default = "0" -replace_music_source.rmempty = false -replace_music_source:depends("apptype", "nodejs") - -local_vip = s:option(Flag, "local_vip", translate("启用本地 VIP")) -local_vip.description = translate("启用后,可以使用去广告、个性换肤、鲸云音效等本地功能") -local_vip.default = 0 -local_vip.rmempty = false -local_vip:depends("apptype", "nodejs") - -autoupdate = s:option(Flag, "autoupdate", translate("自动检查更新主程序")) -autoupdate.description = translate("每天自动检测并更新到最新版本") -autoupdate.default = "1" -autoupdate.rmempty = false -autoupdate:depends("apptype", "nodejs") - -download_certificate = s:option(DummyValue, "opennewwindow", translate("HTTPS 证书")) -download_certificate.description = translate("
Mac/iOS客户端需要安装 CA根证书并信任
iOS系统需要在“设置 -> 通用 -> 关于本机 -> 证书信任设置”中,信任 UnblockNeteaseMusic Root CA
Linux 设备请在启用时加入 --ignore-certificate-errors 参数") - -local ver = fs.readfile("/usr/share/UnblockNeteaseMusic/core_ver") or "0.00" - -restart = s:option(Button, "restart", translate("手动更新")) -restart.inputtitle = translate("更新核心版本") -restart.description = string.format(translate("NodeJS 解锁主程序版本") .. ": %s ", ver) -restart.inputstyle = "reload" -restart.write = function() - luci.sys.exec("/usr/share/UnblockNeteaseMusic/update_core.sh luci_update 2>&1") - luci.http.redirect(luci.dispatcher.build_url("admin", "services", "unblockmusic")) -end -restart:depends("apptype", "nodejs") - -acl_mode = s:option(ListValue, "acl_mode", translate("默认解锁模式")) -acl_mode:value(0, translate("解锁")) -acl_mode:value(1, translate("不解锁")) -acl_mode.default = 0 - -t = mp:section(TypedSection, "acl_rule") -t.title = translate("例外客户端规则") -t.description = translate("可以为局域网客户端分别设置不同的例外模式,默认无需设置") -t.template = "cbi/tblsection" -t.sortable = true -t.anonymous = true -t.addremove = true - -ipaddr = t:option(Value, "ipaddr", translate("IP 地址")) -ipaddr.width = "40%" -ipaddr.datatype = "ip4addr" -ipaddr.placeholder = "0.0.0.0/0" -luci.ip.neighbors({ family = 4 }, function(entry) - if entry.reachable then - ipaddr:value(entry.dest:string()) - end -end) - -filter_mode = t:option(ListValue, "filter_mode", translate("例外协议")) -filter_mode.width = "40%" -filter_mode.default = "disable" -filter_mode.rmempty = false -filter_mode:value("disable", translate("不代理HTTP和HTTPS")) -filter_mode:value("http", translate("不代理HTTP")) -filter_mode:value("https", translate("不代理HTTPS")) -filter_mode:value("proxy_http", translate("代理HTTP")) -filter_mode:value("proxy_http_https", translate("代理HTTP和HTTPS")) - -return mp diff --git a/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic_log.lua b/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic_log.lua deleted file mode 100644 index 6bbbd6a44..000000000 --- a/luci-app-unblockmusic/luasrc/model/cbi/unblockmusic/unblockmusic_log.lua +++ /dev/null @@ -1,15 +0,0 @@ -local fs = require "nixio.fs" -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 | grep -v -e'running' -e'TLS' -e'Transport' -e'POST' -e'github' -e'consumed' -e'starting' -e'error' > /tmp/music.log") - return fs.readfile(conffile) or "" -end -t.readonly = "readonly" - -return f diff --git a/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm b/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm deleted file mode 100644 index cd5e1e8d2..000000000 --- a/luci-app-unblockmusic/luasrc/view/unblockmusic/unblockmusic_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-unblockmusic/po/zh-cn/unblockmusic.po b/luci-app-unblockmusic/po/zh-cn/unblockmusic.po deleted file mode 100644 index 19b29e026..000000000 --- a/luci-app-unblockmusic/po/zh-cn/unblockmusic.po +++ /dev/null @@ -1,12 +0,0 @@ - -msgid "Unblock Netease Music" -msgstr "解锁网易云灰色歌曲" - -msgid "Base Setting" -msgstr "基本设置" - -msgid "Log" -msgstr "日志" - -msgid "Port" -msgstr "端口" diff --git a/luci-app-unblockmusic/po/zh_Hans b/luci-app-unblockmusic/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-unblockmusic/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-unblockmusic/root/etc/config/unblockmusic b/luci-app-unblockmusic/root/etc/config/unblockmusic deleted file mode 100644 index 191abc100..000000000 --- a/luci-app-unblockmusic/root/etc/config/unblockmusic +++ /dev/null @@ -1,8 +0,0 @@ - -config unblockmusic - option musicapptype 'kuwo' - option autoupdate '1' - option endpoint 'http://music.163.com' - option enabled '0' - option apptype 'go' - diff --git a/luci-app-unblockmusic/root/etc/hotplug.d/iface/099-unblockmusic b/luci-app-unblockmusic/root/etc/hotplug.d/iface/099-unblockmusic deleted file mode 100755 index 3caadee09..000000000 --- a/luci-app-unblockmusic/root/etc/hotplug.d/iface/099-unblockmusic +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -[ "$ACTION" = ifup ] || exit 0 - -sleep 10 -ipset flush music 2>/dev/null -/usr/share/UnblockNeteaseMusic/getmusicip.sh diff --git a/luci-app-unblockmusic/root/etc/init.d/unblockmusic b/luci-app-unblockmusic/root/etc/init.d/unblockmusic deleted file mode 100755 index 1a969977c..000000000 --- a/luci-app-unblockmusic/root/etc/init.d/unblockmusic +++ /dev/null @@ -1,261 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=97 -STOP=10 - -NAME=unblockmusic - -uci_get_by_type() { - local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null) - echo ${ret:=$3} -} - -uci_get_by_name() { - local index=0 - if [ -n $4 ]; then - - index=$4 - fi - local ret=$(uci get $NAME.@$1[$index].$2 2>/dev/null) - echo ${ret:=$3} -} - -check_host() { - local host=$1 - if echo $host | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then - hostip=$host - elif [ "$host" != "${host#*:[0-9a-fA-F]}" ]; then - hostip=$host - else - hostip=$(ping $host -W 1 -s 1 -c 1 | grep PING | cut -d'(' -f 2 | cut -d')' -f1) - if echo $hostip | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then - hostip=$hostip - else - hostip="127.0.0.1" - fi - fi - echo -e $hostip -} - -ip_rule() -{ - local icount=$(uci show unblockmusic | grep 'filter_mode' | wc -l) - let icount=icount-1 - for i in $(seq 0 $icount) - do - local ip=$(uci_get_by_name acl_rule ipaddr '' $i) - local mode=$(uci_get_by_name acl_rule filter_mode '' $i) - - case "$mode" in - http) - ipset -! add music_http $ip - ;; - https) - ipset -! add music_https $ip - ;; - disable) - ipset -! add music_http $ip - ipset -! add music_https $ip - ;; - proxy_http) - ipset -! add music_proxy_http $ip - ;; - proxy_http_https) - ipset -! add music_proxy_http $ip - ipset -! add music_proxy_https $ip - esac - done -} - -ENABLE=$(uci_get_by_type unblockmusic enabled 0) -TYPE=$(uci_get_by_type unblockmusic musicapptype default) -AUTOUPDATE=$(uci_get_by_type unblockmusic autoupdate 0) -APPTYPE=$(uci_get_by_type unblockmusic apptype go) -SEARCHLIMIT=$(uci_get_by_type unblockmusic search_limit 0) -FLAC=$(uci_get_by_type unblockmusic flac_enabled 0) -REPLACE_MUSIC_SOURCE=$(uci_get_by_type unblockmusic replace_music_source 0) -LOCAL_VIP=$(uci_get_by_type unblockmusic local_vip 0) -ACLMODE=$(uci_get_by_type unblockmusic acl_mode 0) - -CLOUD=$(uci_get_by_type unblockmusic cloudserver "127.0.0.1:5200:5201") -cloudadd=$(echo "$CLOUD" | awk -F ':' '{print $1}') -cloudhttp=$(echo "$CLOUD" | awk -F ':' '{print $2}') -cloudhttps=$(echo "$CLOUD" | awk -F ':' '{print $3}') - -cloudip=$(check_host $cloudadd) - -CRON_FILE=/etc/crontabs/root - -ipt_n="iptables -t nat" - -add_rule() -{ - ipset -! -N music hash:ip - ipset -! -N music_http hash:ip - ipset -! -N music_https hash:ip - ipset -! -N music_proxy_http hash:ip - ipset -! -N music_proxy_https 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 - if [ "$APPTYPE" != "cloud" ]; then - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_http src --dport 80 -j RETURN - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_https src --dport 443 -j RETURN - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_proxy_http src --dport 80 -j REDIRECT --to-ports 5200 - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_proxy_https src --dport 443 -j REDIRECT --to-ports 5201 - - if [ "$ACLMODE" == "0" ]; then - $ipt_n -A CLOUD_MUSIC -p tcp --dport 80 -j REDIRECT --to-ports 5200 - $ipt_n -A CLOUD_MUSIC -p tcp --dport 443 -j REDIRECT --to-ports 5201 - fi - else - local lan_addr - - . /lib/functions/network.sh - - network_flush_cache - network_get_ipaddr lanaddr "lan" - - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_http src --dport 80 -j RETURN - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_https src --dport 443 -j RETURN - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_proxy_http src --dport 80 -j DNAT --to $cloudip:$cloudhttp - $ipt_n -A CLOUD_MUSIC -p tcp -m set --match-set music_proxy_https src --dport 443 -j DNAT --to $cloudip:$cloudhttps - - if [ "$ACLMODE" == "0" ]; then - $ipt_n -A CLOUD_MUSIC -p tcp --dport 80 -j DNAT --to $cloudip:$cloudhttp - $ipt_n -A CLOUD_MUSIC -p tcp --dport 443 -j DNAT --to $cloudip:$cloudhttps - fi - - $ipt_n -I POSTROUTING -p tcp -m multiport --dport $cloudhttp,$cloudhttps -d $cloudip -j SNAT --to-source $lanaddr - fi - - $ipt_n -I PREROUTING -p tcp -m set --match-set music dst -j CLOUD_MUSIC - iptables -I OUTPUT -d 223.252.199.10 -j DROP - - ip_rule -} - -del_rule(){ - $ipt_n -D PREROUTING -p tcp -m set --match-set music dst -j CLOUD_MUSIC 2>/dev/null - - if [ "$APPTYPE" == "cloud" ]; then - local lan_addr - - . /lib/functions/network.sh - - network_flush_cache - network_get_ipaddr lanaddr "lan" - $ipt_n -D POSTROUTING -p tcp -m multiport --dport $cloudhttp,$cloudhttps -d $cloudip -j SNAT --to-source $lanaddr 2>/dev/null - fi - $ipt_n -F CLOUD_MUSIC 2>/dev/null - $ipt_n -X CLOUD_MUSIC 2>/dev/null - iptables -D OUTPUT -d 223.252.199.10 -j DROP 2>/dev/null - - ipset flush music 2>/dev/null - ipset -X music_http 2>/dev/null - ipset -X music_https 2>/dev/null - ipset -X music_proxy_http 2>/dev/null - ipset -X music_proxy_https 2>/dev/null - - rm -f /tmp/dnsmasq.d/dnsmasq-163.conf - /etc/init.d/dnsmasq reload >/dev/null 2>&1 -} - -set_firewall(){ - rm -f /tmp/dnsmasq.d/dnsmasq-163.conf - mkdir -p /tmp/dnsmasq.d - cat <<-EOF > "/tmp/dnsmasq.d/dnsmasq-163.conf" -ipset=/.music.163.com/music -ipset=/interface.music.163.com/music -ipset=/interface3.music.163.com/music -ipset=/apm.music.163.com/music -ipset=/apm3.music.163.com/music -ipset=/clientlog.music.163.com/music -ipset=/clientlog3.music.163.com/music - EOF - /etc/init.d/dnsmasq reload >/dev/null 2>&1 - - add_rule - - mkdir -p /var/etc - echo -e "/etc/init.d/unblockmusic restart" > "/var/etc/unblockmusic.include" -} - -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 - - rm -f /tmp/unblockmusic.log - echo "$(date -R) # Start UnblockNeteaseMusic" >/tmp/unblockmusic.log - - if [ "$TYPE" = "default" ]; then - musictype=" " - else - musictype="-o $TYPE" - fi - - if [ "$APPTYPE" == "nodejs" ]; then - if [ $FLAC -eq 1 ]; then - export ENABLE_FLAC=true - fi - if [ $REPLACE_MUSIC_SOURCE -ne 0 ]; then - export MIN_BR=$REPLACE_MUSIC_SOURCE - fi - if [ $LOCAL_VIP -eq 1 ]; then - export ENABLE_LOCAL_VIP=true - fi - node /usr/share/UnblockNeteaseMusic/app.js -p 5200 $musictype >>/tmp/unblockmusic.log 2>&1 & - node /usr/share/UnblockNeteaseMusic/app.js -e https://music.163.com -p 5203:5201 $musictype >>/tmp/unblockmusic.log 2>&1 & - add_cron - echo "$(date -R) # UnblockNeteaseMusic Nodejs Version (http:5200, https:5201)" >>/tmp/unblockmusic.log - elif [ "$APPTYPE" == "go" ]; then - if [ $FLAC -eq 1 ]; then - ENABLE_FLAC="-b " - fi - UnblockNeteaseMusic -o "kuwo:kugou" $ENABLE_FLAC -p 5200 -sp 5201 -c /usr/share/UnblockNeteaseMusicGo/server.crt -k /usr/share/UnblockNeteaseMusicGo/server.key -m 0 -e -sl ${SEARCHLIMIT} -l /tmp/unblockmusic.log & - echo "$(date -R) # UnblockNeteaseMusic Golang Version (http:5200, https:5201)" >>/tmp/unblockmusic.log - else - kill -9 $(busybox ps -w | grep 'sleep 60m' | grep -v grep | awk '{print $1}') >/dev/null 2>&1 - /usr/bin/UnblockNeteaseMusicCloud >/dev/null 2>&1 & - echo "$(date -R) # UnblockNeteaseMusic Cloud Version - Server: $cloudip (http:$cloudhttp, https:$cloudhttps)" >>/tmp/unblockmusic.log - fi - - set_firewall - - if [ "$APPTYPE" != "cloud" ]; then - /usr/share/UnblockNeteaseMusic/logcheck.sh >/dev/null 2>&1 & - fi -} - -stop() -{ - kill -9 $(busybox ps -w | grep UnblockNeteaseMusic | 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/luci-app-unblockmusic/root/etc/uci-defaults/luci-unblockmusic b/luci-app-unblockmusic/root/etc/uci-defaults/luci-unblockmusic deleted file mode 100644 index 02bf5cb3f..000000000 --- a/luci-app-unblockmusic/root/etc/uci-defaults/luci-unblockmusic +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-unblockmusic.json" ] && { - cat > /usr/share/ucitrack/luci-app-unblockmusic.json << EEOF -{ - "config": "unblockmusic", - "init": "unblockmusic" -} -EEOF -} - -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/luci-app-unblockmusic/root/lib/upgrade/keep.d/unblockmusic b/luci-app-unblockmusic/root/lib/upgrade/keep.d/unblockmusic deleted file mode 100644 index 80f52c1c9..000000000 --- a/luci-app-unblockmusic/root/lib/upgrade/keep.d/unblockmusic +++ /dev/null @@ -1,6 +0,0 @@ -/usr/share/UnblockNeteaseMusic/ca.crt -/usr/share/UnblockNeteaseMusic/server.crt -/usr/share/UnblockNeteaseMusic/server.key -/usr/share/UnblockNeteaseMusicGo/ca.crt -/usr/share/UnblockNeteaseMusicGo/server.crt -/usr/share/UnblockNeteaseMusicGo/server.key diff --git a/luci-app-unblockmusic/root/usr/bin/UnblockNeteaseMusicCloud b/luci-app-unblockmusic/root/usr/bin/UnblockNeteaseMusicCloud deleted file mode 100755 index f1b79ac9e..000000000 --- a/luci-app-unblockmusic/root/usr/bin/UnblockNeteaseMusicCloud +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -while true -do - ipset -! flush music - uclient-fetch -q -T 10 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 - sleep 60m -done diff --git a/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/getmusicip.sh b/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/getmusicip.sh deleted file mode 100755 index 98fb5de83..000000000 --- a/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/getmusicip.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -ipset -! -N music hash:ip -uclient-fetch -q -T 10 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/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh b/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh deleted file mode 100755 index 7a6d48712..000000000 --- a/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/logcheck.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -log_max_size=100 -log_file="/tmp/unblockmusic.log" -log_size=0 - -/usr/share/UnblockNeteaseMusic/getmusicip.sh -sleep 29s - -while true -do - icount=`busybox ps -w | grep UnblockNeteaseMusic | grep -v grep | grep -v logcheck.sh` - if [ -z "$icount" ]; then - /etc/init.d/unblockmusic restart - fi - log_size=$(expr $(ls -l $log_file | awk '{print $5}') / 1024) - [ $log_size -ge $log_max_size ] && echo "$(date -R) # Start UnblockNeteaseMusic" >/tmp/unblockmusic.log - sleep 29s -done diff --git a/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh b/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh deleted file mode 100755 index b2293e083..000000000 --- a/luci-app-unblockmusic/root/usr/share/UnblockNeteaseMusic/update_core.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh - -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="$(uclient-fetch --no-check-certificate -O- https://github.com/UnblockNeteaseMusic/server/commits/enhanced |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 - 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 - [ "${luci_update}" == "n" ] && /etc/init.d/unblockmusic restart - 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 - - uclient-fetch --no-check-certificate -T 10 -O /tmp/unblockneteasemusic/core/core.tar.gz "https://github.com/UnblockNeteaseMusic/server/archive/enhanced.tar.gz" >/dev/null 2>&1 - tar -zxf "/tmp/unblockneteasemusic/core/core.tar.gz" -C "/tmp/unblockneteasemusic/core/" >/dev/null 2>&1 - if [ -e "/usr/share/UnblockNeteaseMusic/ca.crt" ] && [ -e "/usr/share/UnblockNeteaseMusic/server.crt" ] && [ -e "/usr/share/UnblockNeteaseMusic/server.key" ] ; then - rm -f /tmp/unblockneteasemusic/core/server-enhanced/ca.crt /tmp/unblockneteasemusic/core/server-enhanced/server.crt /tmp/unblockneteasemusic/core/server-enhanced/server.key - fi - cp -a /tmp/unblockneteasemusic/core/server-enhanced/* "/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 - echo -e "${latest_ver}" > /usr/share/UnblockNeteaseMusic/local_ver - cat /usr/share/UnblockNeteaseMusic/package.json | grep version |awk -F ':' '{print $2}' | sed -r 's/.*"(.+)".*/\1/' > /usr/share/UnblockNeteaseMusic/core_ver - 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 - - /etc/init.d/unblockmusic restart -} - -function main(){ - check_if_already_running - check_latest_version -} - - luci_update="n" - [ "$1" == "luci_update" ] && luci_update="y" - main diff --git a/luci-app-unblockmusic/root/usr/share/rpcd/acl.d/luci-app-unblockmusic.json b/luci-app-unblockmusic/root/usr/share/rpcd/acl.d/luci-app-unblockmusic.json deleted file mode 100644 index 4121e4806..000000000 --- a/luci-app-unblockmusic/root/usr/share/rpcd/acl.d/luci-app-unblockmusic.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-unblockmusic": { - "description": "Grant UCI access for luci-app-unblockmusic", - "read": { - "uci": [ "unblockmusic" ] - }, - "write": { - "uci": [ "unblockmusic" ] - } - } -} diff --git a/luci-app-unblockmusic/root/usr/share/ucitrack/luci-app-unblockmusic.json b/luci-app-unblockmusic/root/usr/share/ucitrack/luci-app-unblockmusic.json deleted file mode 100644 index c85080d96..000000000 --- a/luci-app-unblockmusic/root/usr/share/ucitrack/luci-app-unblockmusic.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "unblockmusic", - "init": "unblockmusic" -} diff --git a/luci-app-v2ray-server/Makefile b/luci-app-v2ray-server/Makefile deleted file mode 100644 index 4528e7f16..000000000 --- a/luci-app-v2ray-server/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2018-2020 Lienol -# -# This is free software, licensed under the GNU General Public License v3. -# - -include $(TOPDIR)/rules.mk - -LUCI_TITLE:=LuCI support for V2ray Server -LUCI_DEPENDS:=+unzip -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-v2ray-server -PKG_VERSION:=13 -PKG_RELEASE:=2 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua b/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua deleted file mode 100644 index f7e283662..000000000 --- a/luci-app-v2ray-server/luasrc/controller/v2ray_server.lua +++ /dev/null @@ -1,58 +0,0 @@ --- Copyright 2018-2019 Lienol -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", "services", "v2ray_server"}, cbi("v2ray_server/index"), _("V2ray Server"), 3).dependent = true - entry({"admin", "services", "v2ray_server", "config"}, cbi("v2ray_server/user")).leaf = true - entry({"admin", "services", "v2ray_server", "users_status"}, call("users_status")).leaf = true - entry({"admin", "services", "v2ray_server", "check"}, call("v2ray_check")).leaf = true - entry({"admin", "services", "v2ray_server", "update"}, call("v2ray_update")).leaf = true - entry({"admin", "services", "v2ray_server", "get_log"}, call("get_log")).leaf = true - entry({"admin", "services", "v2ray_server", "clear_log"}, call("clear_log")).leaf = true -end - -local function http_write_json(content) - http.prepare_content("application/json") - http.write_json(content or {code = 1}) -end - -function get_log() - luci.http.write(luci.sys.exec("[ -f '/var/log/v2ray_server/app.log' ] && cat /var/log/v2ray_server/app.log")) -end - -function clear_log() - luci.sys.call("echo '' > /var/log/v2ray_server/app.log") -end - -function 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 diff --git a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/app.lua b/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/app.lua deleted file mode 100755 index 32ce2aac0..000000000 --- a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/app.lua +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/lua - -local action = arg[1] -local sys = require 'luci.sys' -local jsonc = require "luci.jsonc" -local ucic = require"luci.model.uci".cursor() - -local CONFIG = "v2ray_server" -local CONFIG_PATH = "/var/etc/" .. CONFIG -local LOG_PATH = "/var/log/" .. CONFIG -local LOG_APP_FILE = LOG_PATH .. "/app.log" -local BIN_PATH = "/var/bin/" -local BIN_PATH_FILE = BIN_PATH .. CONFIG - -local function log(...) - local f, err = io.open(LOG_APP_FILE, "a") - if f and err == nil then - local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ") - f:write(str .. "\n") - f:close() - end -end - -local function cmd(cmd) - sys.call(cmd) -end - -local function gen_include() - cmd(string.format("echo '#!/bin/sh' > /var/etc/%s.include", CONFIG)) - local function extract_rules(a) - local result = "*" .. a - result = result .. "\n" .. sys.exec('iptables-save -t ' .. a .. ' | grep "V2RAY-SERVER" | sed -e "s/^-A \\(INPUT\\)/-I \\1 1/"') - result = result .. "COMMIT" - return result - end - local f, err = io.open("/var/etc/" .. CONFIG .. ".include", "a") - if f and err == nil then - f:write('iptables-save -c | grep -v "V2RAY-SERVER" | iptables-restore -c' .. "\n") - f:write('iptables-restore -n <<-EOT' .. "\n") - f:write(extract_rules("filter") .. "\n") - f:write("EOT" .. "\n") - f:close() - end -end - -local function start() - local enabled = tonumber(ucic:get(CONFIG, "@global[0]", "enable") or 0) - if enabled == nil or enabled == 0 then - return - end - cmd(string.format("mkdir -p %s %s", CONFIG_PATH, LOG_PATH)) - cmd(string.format("touch %s", LOG_APP_FILE)) - cmd("iptables -N V2RAY-SERVER") - cmd("iptables -I INPUT -j V2RAY-SERVER") - ucic:foreach(CONFIG, "user", function(user) - local id = user[".name"] - local enable = user.enable - if enable and tonumber(enable) == 1 then - local remarks = user.remarks - local port = tonumber(user.port) - if nixio.fs.access("/usr/bin/xray") and not nixio.fs.access(BIN_PATH_FILE) then - cmd(string.format("mkdir -p %s", BIN_PATH)) - cmd(string.format("cp -a /usr/bin/xray %s", BIN_PATH_FILE)) - end - local bin = BIN_PATH_FILE - local config = {} - local config_file = CONFIG_PATH .. "/" .. id .. ".json" - - config = require("luci.model.cbi.v2ray_server.api.gen_config").gen_config(user) - bin = bin .. " -config " .. config_file - - if next(config) then - local f, err = io.open(config_file, "w") - if f and err == nil then - f:write(jsonc.stringify(config, 1)) - f:close() - end - log(string.format("%s %s 生成配置文件并运行 - %s", remarks, port, config_file)) - end - - if bin then - cmd(bin .. ">/dev/null 2>&1 &") - end - - local bind_local = user.bind_local or 0 - if bind_local and tonumber(bind_local) ~= 1 then - cmd(string.format('iptables -A V2RAY-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - cmd(string.format('iptables -A V2RAY-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - end - end - end) - gen_include() -end - -local function stop() - cmd(string.format("ps -w | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &", CONFIG_PATH)) - cmd("iptables -D INPUT -j V2RAY-SERVER 2>/dev/null") - cmd("iptables -F V2RAY-SERVER 2>/dev/null") - cmd("iptables -X V2RAY-SERVER 2>/dev/null") - cmd(string.format("rm -rf %s %s /var/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG)) -end - -if action then - if action == "start" then - start() - elseif action == "stop" then - stop() - end -end \ No newline at end of file diff --git a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/gen_config.lua b/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/gen_config.lua deleted file mode 100755 index 8e2278f10..000000000 --- a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/gen_config.lua +++ /dev/null @@ -1,184 +0,0 @@ -module("luci.model.cbi.v2ray_server.api.gen_config", package.seeall) - -function gen_config(user) - local settings = nil - local routing = nil - local outbounds = { - {protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"} - } - - if user.protocol == "vmess" or user.protocol == "vless" then - if user.uuid then - local clients = {} - for i = 1, #user.uuid do - clients[i] = { - id = user.uuid[i], - flow = (user.xtls and user.xtls == "1") and user.flow or nil, - level = tonumber(user.level), - alterId = tonumber(user.alter_id) - } - end - settings = { - clients = clients, - decryption = user.decryption or "none" - } - end - elseif user.protocol == "socks" then - settings = { - auth = (user.auth and user.auth == "1") and "password" or "noauth", - accounts = (user.auth and user.auth == "1") and { - { - user = user.username, - pass = user.password - } - } - } - elseif user.protocol == "http" then - settings = { - allowTransparent = false, - accounts = (user.auth and user.auth == "1") and { - { - user = user.username, - pass = user.password - } - } - } - user.transport = "tcp" - user.tcp_guise = "none" - elseif user.protocol == "shadowsocks" then - settings = { - method = user.method, - password = user.password, - level = tonumber(user.level) or 1, - network = user.ss_network or "TCP,UDP" - } - elseif user.protocol == "trojan" then - if user.uuid then - local clients = {} - for i = 1, #user.uuid do - clients[i] = { - password = user.uuid[i], - level = tonumber(user.level) - } - end - settings = { - clients = clients - } - end - elseif user.protocol == "mtproto" then - settings = { - users = { - { - level = tonumber(user.level) or 1, - secret = (user.password == nil) and "" or user.password - } - } - } - end - - routing = { - domainStrategy = "IPOnDemand", - rules = { - { - type = "field", - ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}, - outboundTag = (user.accept_lan == nil or user.accept_lan == "0") and "blocked" or "direct" - } - } - } - - local config = { - log = { - -- error = "/var/etc/v2ray_server/log/" .. user[".name"] .. ".log", - loglevel = "warning" - }, - -- 传入连接 - inbounds = { - { - listen = (user.bind_local == "1") and "127.0.0.1" or nil, - port = tonumber(user.port), - protocol = user.protocol, - settings = settings, - streamSettings = { - network = user.transport, - security = "none", - xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and { - --alpn = {"http/1.1"}, - disableSystemRoot = false, - certificates = { - { - certificateFile = user.tls_certificateFile, - keyFile = user.tls_keyFile - } - } - } or nil, - tlsSettings = (user.tls and user.tls == "1") and { - disableSystemRoot = false, - certificates = { - { - certificateFile = user.tls_certificateFile, - keyFile = user.tls_keyFile - } - } - } or nil, - tcpSettings = (user.transport == "tcp") and { - header = { - type = user.tcp_guise, - request = (user.tcp_guise == "http") and { - path = user.tcp_guise_http_path or {"/"}, - headers = { - Host = user.tcp_guise_http_host or {} - } - } or nil - } - } or nil, - kcpSettings = (user.transport == "mkcp") and { - mtu = tonumber(user.mkcp_mtu), - tti = tonumber(user.mkcp_tti), - uplinkCapacity = tonumber(user.mkcp_uplinkCapacity), - downlinkCapacity = tonumber(user.mkcp_downlinkCapacity), - congestion = (user.mkcp_congestion == "1") and true or false, - readBufferSize = tonumber(user.mkcp_readBufferSize), - writeBufferSize = tonumber(user.mkcp_writeBufferSize), - seed = (user.mkcp_seed and user.mkcp_seed ~= "") and user.mkcp_seed or nil, - header = {type = user.mkcp_guise} - } or nil, - wsSettings = (user.transport == "ws") and { - acceptProxyProtocol = false, - headers = (user.ws_host) and {Host = user.ws_host} or nil, - path = user.ws_path - } or nil, - httpSettings = (user.transport == "h2") and { - path = user.h2_path, host = user.h2_host - } or nil, - dsSettings = (user.transport == "ds") and { - path = user.ds_path - } or nil, - quicSettings = (user.transport == "quic") and { - security = user.quic_security, - key = user.quic_key, - header = {type = user.quic_guise} - } or nil - } - } - }, - -- 传出连接 - outbounds = outbounds, - routing = routing - } - - if user.tls and user.tls == "1" then - config.inbounds[1].streamSettings.security = "tls" - if user.xtls and user.xtls == "1" then - config.inbounds[1].streamSettings.security = "xtls" - config.inbounds[1].streamSettings.tlsSettings = nil - end - end - - if user.transport == "mkcp" or user.transport == "quic" then - config.inbounds[1].streamSettings.security = "none" - config.inbounds[1].streamSettings.tlsSettings = nil - end - - return config -end diff --git a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua b/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua deleted file mode 100755 index e39eb6fb3..000000000 --- a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/api/v2ray.lua +++ /dev/null @@ -1,304 +0,0 @@ -module("luci.model.cbi.v2ray_server.api.v2ray", package.seeall) -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" -local ipkg = require "luci.model.ipkg" - -local appname = "v2ray_server" -local v2ray_api ="https://api.github.com/repos/XTLS/Xray-core/releases/latest" -local wget = "/usr/bin/wget" -local wget_args = {"--no-check-certificate", "--quiet", "--timeout=100", "--tries=3"} -local command_timeout = 300 - -local LEDE_BOARD = nil -local DISTRIB_TARGET = nil -local is_armv7 = false - -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 fs.access("/usr/lib/os-release") then - LEDE_BOARD = sys.exec("echo -n `grep 'LEDE_BOARD' /usr/lib/os-release | awk -F '[\\042\\047]' '{print $2}'`") - end - if fs.access("/etc/openwrt_release") then - DISTRIB_TARGET = sys.exec("echo -n `grep 'DISTRIB_TARGET' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}'`") - end - - if arch == "mips" then - if LEDE_BOARD and LEDE_BOARD ~= "" then - if string.match(LEDE_BOARD, "ramips") == "ramips" then - arch = "ramips" - else - arch = sys.exec("echo '" .. LEDE_BOARD .. "' | grep -oE 'ramips|ar71xx'") - end - elseif DISTRIB_TARGET and DISTRIB_TARGET ~= "" then - if string.match(DISTRIB_TARGET, "ramips") == "ramips" then - arch = "ramips" - else - arch = sys.exec("echo '" .. DISTRIB_TARGET .. "' | grep -oE 'ramips|ar71xx'") - end - end - end - - return util.trim(arch) -end - -local function get_file_info(arch) - local file_tree = "" - local sub_version = "" - - if arch == "x86_64" then - file_tree = "64" - elseif arch == "aarch64" then - file_tree = "arm64" - elseif arch == "ramips" then - file_tree = "mipsle" - elseif arch == "ar71xx" then - file_tree = "mips" - elseif arch:match("^i[%d]86$") then - file_tree = "32" - elseif arch:match("^armv[5-8]") then - file_tree = "arm" - sub_version = arch:match("[5-8]") - if LEDE_BOARD and string.match(LEDE_BOARD, "bcm53xx") == "bcm53xx" then - sub_version = "5" - elseif DISTRIB_TARGET and string.match(DISTRIB_TARGET, "bcm53xx") == - "bcm53xx" then - sub_version = "5" - end - sub_version = "5" - if sub_version == "7" then is_armv7 = true end - 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(wget .. " --no-check-certificate --timeout=10 -t 1 -O- " .. url) - - if json_content == "" then return {} end - - return jsonc.parse(json_content) or {} -end - -function get_v2ray_file_path() return "/usr/bin" end - -function get_v2ray_version() - if get_v2ray_file_path() and get_v2ray_file_path() ~= "" then - if fs.access(get_v2ray_file_path() .. "/xray") then - return luci.sys.exec("echo -n `" .. get_v2ray_file_path() .. "/xray -version | awk '{print $2}' | sed -n 1P" .. "`") - end - end - return "" -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.") - } - 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, - now_version = get_v2ray_version(), - version = remote_version, - html_url = html_url, - error = i18n.translate("New version found, but failed to get new version download url.") - } - end - - return { - code = 0, - update = needs_update, - now_version = get_v2ray_version(), - version = remote_version, - url = {html = html_url, download = download_url} - } -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 = ipkg.installed("unzip") - if isinstall_unzip == nil then - ipkg.update() - ipkg.install("unzip") - 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 = get_v2ray_file_path() - - sys.call("mkdir -p " .. client_file) - - if not arch or arch == "" then arch = auto_get_arch() end - local file_tree, sub_version = get_file_info(arch) - local result = nil - result = exec("/bin/mv", {"-f", file .. "/xray", 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 .. "/xray"}) - - sys.call("/bin/rm -rf /tmp/v2ray_extract.*") - - return {code = 0} -end diff --git a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua b/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua deleted file mode 100644 index f3383e002..000000000 --- a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/index.lua +++ /dev/null @@ -1,58 +0,0 @@ -local ds = require "luci.dispatcher" - -m = Map("v2ray_server") -m.title = translate("V2ray Server") - -t = m:section(TypedSection, "global", translate("Global Settings")) -t.anonymous = true -t.addremove = false - -e = t:option(Flag, "enable", translate("Enable")) -e.rmempty = false -t:append(Template("v2ray_server/v2ray")) - -t = m:section(TypedSection, "user", translate("Users Manager")) -t.anonymous = true -t.addremove = true -t.template = "cbi/tblsection" -t.extedit = ds.build_url("admin", "services", "v2ray_server", "config", "%s") -function t.create(t, e) - local uuid = luci.sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)") or "" - uuid = string.gsub(uuid, "-", "") - local e = TypedSection.create(t, uuid) - luci.http.redirect(ds.build_url("admin", "services", "v2ray_server", "config", uuid)) -end -function t.remove(t, a) - t.map.proceed = true - t.map:del(a) - luci.http.redirect(ds.build_url("admin", "services", "v2ray_server")) -end - -e = t:option(Flag, "enable", translate("Enable")) -e.width = "5%" -e.rmempty = false - -e = t:option(DummyValue, "status", translate("Status")) -e.rawhtml = true -e.cfgvalue = function(t, n) - return string.format('%s', n, translate("Collecting data...")) -end - -e = t:option(DummyValue, "remarks", translate("Remarks")) - -e = t:option(DummyValue, "port", translate("Port")) -e.width = "10%" - -e = t:option(DummyValue, "protocol", translate("Protocol")) -e.cfgvalue = function(self, section) - local str = "未知" - local protocol = m:get(section, "protocol") or "" - if protocol ~= "" then str = (protocol:gsub("^%l", string.upper)) end - return str -end - -m:append(Template("v2ray_server/log")) - -m:append(Template("v2ray_server/users_list_status")) - -return m diff --git a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/user.lua b/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/user.lua deleted file mode 100644 index 3002b9950..000000000 --- a/luci-app-v2ray-server/luasrc/model/cbi/v2ray_server/user.lua +++ /dev/null @@ -1,315 +0,0 @@ -local d = require "luci.dispatcher" -local appname = "v2ray_server" - -local v_ss_encrypt_method_list = { - "aes-128-cfb", "aes-256-cfb", "aes-128-gcm", "aes-256-gcm", "chacha20", "chacha20-ietf", "chacha20-poly1305", "chacha20-ietf-poly1305" -} - -local header_type_list = { - "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" -} - -m = Map(appname, translate("Server Config")) -m.redirect = d.build_url("admin", "services", appname) - -s = m:section(NamedSection, arg[1], "user", "") -s.addremove = false -s.dynamic = false - -enable = s:option(Flag, "enable", translate("Enable")) -enable.default = "1" -enable.rmempty = false - -remarks = s:option(Value, "remarks", translate("Remarks")) -remarks.default = translate("Remarks") -remarks.rmempty = false - -protocol = s:option(ListValue, "protocol", translate("Protocol")) -protocol:value("vmess", "Vmess") -protocol:value("vless", "VLESS") -protocol:value("http", "HTTP") -protocol:value("socks", "Socks") -protocol:value("shadowsocks", "Shadowsocks") -protocol:value("trojan", "Trojan") -protocol:value("mtproto", "MTProto") - -port = s:option(Value, "port", translate("Port")) -port.datatype = "port" -port.rmempty = false - -auth = s:option(Flag, "auth", translate("Auth")) -auth.validate = function(self, value, t) - if value and value == "1" then - local user_v = username:formvalue(t) or "" - local pass_v = password:formvalue(t) or "" - if user_v == "" or pass_v == "" then - return nil, translate("Username and Password must be used together!") - end - end - return value -end -auth:depends({ protocol = "socks" }) -auth:depends({ protocol = "http" }) - -username = s:option(Value, "username", translate("Username")) -username:depends("auth", "1") - -password = s:option(Value, "password", translate("Password")) -password.password = true -password:depends("auth", "1") -password:depends({ protocol = "shadowsocks" }) - -mtproto_password = s:option(Value, "mtproto_password", translate("Password"), translate("The MTProto protocol must be 32 characters and can only contain characters from 0 to 9 and a to f.")) -mtproto_password:depends({ protocol = "mtproto" }) -mtproto_password.default = arg[1] -function mtproto_password.cfgvalue(self, section) - return m:get(section, "password") -end -function mtproto_password.write(self, section, value) - m:set(section, "password", value) -end - -decryption = s:option(Value, "decryption", translate("Encrypt Method")) -decryption.default = "none" -decryption:depends({ protocol = "vless" }) - -v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method")) -for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end -v_ss_encrypt_method:depends({ protocol = "shadowsocks" }) -function v_ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function v_ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -ss_network = s:option(ListValue, "ss_network", translate("Transport")) -ss_network.default = "tcp,udp" -ss_network:value("tcp", "TCP") -ss_network:value("udp", "UDP") -ss_network:value("tcp,udp", "TCP,UDP") -ss_network:depends({ protocol = "shadowsocks" }) - -uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Password")) -for i = 1, 3 do - uuid:value(luci.sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)")) -end -uuid:depends({ protocol = "vmess" }) -uuid:depends({ protocol = "vless" }) -uuid:depends({ protocol = "trojan" }) - -alter_id = s:option(Value, "alter_id", translate("Alter ID")) -alter_id.default = 16 -alter_id:depends({ protocol = "vmess" }) - -level = s:option(Value, "level", translate("User Level")) -level.default = 1 -level:depends({ protocol = "vmess" }) -level:depends({ protocol = "vless" }) -level:depends({ protocol = "shadowsocks" }) -level:depends({ protocol = "trojan" }) -level:depends({ protocol = "mtproto" }) - -tls = s:option(Flag, "tls", translate("TLS")) -tls.default = 0 -tls.validate = function(self, value, t) - if value then - if value == "1" then - local ca = tls_certificateFile:formvalue(t) or "" - local key = tls_keyFile:formvalue(t) or "" - if ca == "" or key == "" then - return nil, translate("Public key and Private key path can not be empty!") - end - end - return value - end -end -tls:depends({ protocol = "vmess" }) -tls:depends({ protocol = "vless" }) -tls:depends({ protocol = "socks" }) -tls:depends({ protocol = "shadowsocks" }) - -xtls = s:option(Flag, "xtls", translate("XTLS")) -xtls.default = 0 -xtls:depends({ protocol = "vless", tls = "1" }) - -flow = s:option(Value, "flow", translate("flow")) -flow.default = "xtls-rprx-direct" -flow:value("xtls-rprx-origin") -flow:value("xtls-rprx-origin-udp443") -flow:value("xtls-rprx-direct") -flow:value("xtls-rprx-direct-udp443") -flow:value("xtls-rprx-splice") -flow:value("xtls-rprx-splice-udp443") -flow:depends("xtls", "1") - --- [[ TLS部分 ]] -- - -tls_serverName = s:option(Value, "tls_serverName", translate("Domain")) -tls_serverName:depends("tls", "1") - -tls_certificateFile = s:option(Value, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") -tls_certificateFile.validate = function(self, value, t) - if value and value ~= "" then - if not nixio.fs.access(value) then - return nil, translate("Can't find this file!") - else - return value - end - end - return nil -end -tls_certificateFile:depends("tls", "1") - -tls_keyFile = s:option(Value, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") -tls_keyFile.validate = function(self, value, t) - if value and value ~= "" then - if not nixio.fs.access(value) then - return nil, translate("Can't find this file!") - else - return value - end - end - return nil -end -tls_keyFile:depends("tls", "1") - -transport = s:option(ListValue, "transport", translate("Transport")) -transport:value("tcp", "TCP") -transport:value("mkcp", "mKCP") -transport:value("ws", "WebSocket") -transport:value("h2", "HTTP/2") -transport:value("ds", "DomainSocket") -transport:value("quic", "QUIC") -transport:depends({ protocol = "vmess" }) -transport:depends({ protocol = "vless" }) -transport:depends({ protocol = "socks" }) -transport:depends({ protocol = "shadowsocks" }) -transport:depends({ protocol = "trojan" }) - --- [[ WebSocket部分 ]]-- - -ws_host = s:option(Value, "ws_host", translate("WebSocket Host")) -ws_host:depends("transport", "ws") -ws_host:depends("ss_transport", "ws") -ws_host:depends("trojan_transport", "h2+ws") -ws_host:depends("trojan_transport", "ws") - -ws_path = s:option(Value, "ws_path", translate("WebSocket Path")) -ws_path:depends("transport", "ws") -ws_path:depends("ss_transport", "ws") -ws_path:depends("trojan_transport", "h2+ws") -ws_path:depends("trojan_transport", "ws") - --- [[ HTTP/2部分 ]]-- - -h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host")) -h2_host:depends("transport", "h2") -h2_host:depends("ss_transport", "h2") -h2_host:depends("trojan_transport", "h2+ws") -h2_host:depends("trojan_transport", "h2") - -h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path")) -h2_path:depends("transport", "h2") -h2_path:depends("ss_transport", "h2") -h2_path:depends("trojan_transport", "h2+ws") -h2_path:depends("trojan_transport", "h2") - --- [[ TCP部分 ]]-- - --- TCP伪装 -tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type")) -tcp_guise:value("none", "none") -tcp_guise:value("http", "http") -tcp_guise:depends("transport", "tcp") - --- HTTP域名 -tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host")) -tcp_guise_http_host:depends("tcp_guise", "http") - --- HTTP路径 -tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path")) -tcp_guise_http_path:depends("tcp_guise", "http") - --- [[ mKCP部分 ]]-- - -mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)')) -for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end -mkcp_guise:depends("transport", "mkcp") - -mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU")) -mkcp_mtu.default = "1350" -mkcp_mtu:depends("transport", "mkcp") - -mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI")) -mkcp_tti.default = "20" -mkcp_tti:depends("transport", "mkcp") - -mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity")) -mkcp_uplinkCapacity.default = "5" -mkcp_uplinkCapacity:depends("transport", "mkcp") - -mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity")) -mkcp_downlinkCapacity.default = "20" -mkcp_downlinkCapacity:depends("transport", "mkcp") - -mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion")) -mkcp_congestion:depends("transport", "mkcp") - -mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize")) -mkcp_readBufferSize.default = "1" -mkcp_readBufferSize:depends("transport", "mkcp") - -mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize")) -mkcp_writeBufferSize.default = "1" -mkcp_writeBufferSize:depends("transport", "mkcp") - -mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed")) -mkcp_seed:depends("transport", "mkcp") - --- [[ DomainSocket部分 ]]-- - -ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray.")) -ds_path:depends("transport", "ds") - --- [[ QUIC部分 ]]-- -quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method")) -quic_security:value("none") -quic_security:value("aes-128-gcm") -quic_security:value("chacha20-poly1305") -quic_security:depends("transport", "quic") - -quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key")) -quic_key:depends("transport", "quic") - -quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type")) -for a, t in ipairs(header_type_list) do quic_guise:value(t) end -quic_guise:depends("transport", "quic") - --- [[ VLESS Fallback部分 ]]-- ---[[ -fallback = s:option(Flag, "fallback", translate("Fallback")) -fallback:depends({ protocol = "vless", transport = "tcp", tls = "1" }) - -fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn") -fallback_alpn:depends("fallback", "1") - -fallback_path = s:option(Value, "fallback_path", "Fallback path") -fallback_path:depends("fallback", "1") - -fallback_dest = s:option(Value, "fallback_dest", "Fallback dest") -fallback_dest:depends("fallback", "1") - -fallback_xver = s:option(Value, "fallback_xver", "Fallback xver") -fallback_xver.default = 0 -fallback_xver:depends("fallback", "1") -]]-- - -bind_local = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally,It is recommended to turn on when using reverse proxies.")) -bind_local.default = "0" - -accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) -accept_lan.default = "0" -accept_lan.rmempty = false - -return m diff --git a/luci-app-v2ray-server/luasrc/view/v2ray_server/log.htm b/luci-app-v2ray-server/luasrc/view/v2ray_server/log.htm deleted file mode 100644 index ff934865c..000000000 --- a/luci-app-v2ray-server/luasrc/view/v2ray_server/log.htm +++ /dev/null @@ -1,31 +0,0 @@ - -
- - <%:Logs%> - - - -
\ No newline at end of file diff --git a/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm b/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm deleted file mode 100644 index 5783334df..000000000 --- a/luci-app-v2ray-server/luasrc/view/v2ray_server/users_list_status.htm +++ /dev/null @@ -1,19 +0,0 @@ - \ No newline at end of file diff --git a/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm b/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm deleted file mode 100644 index 5155dc3da..000000000 --- a/luci-app-v2ray-server/luasrc/view/v2ray_server/v2ray.htm +++ /dev/null @@ -1,184 +0,0 @@ -<% -local v2ray_version=luci.sys.exec("/usr/bin/xray -version | awk '{print $2}' | sed -n 1P") --%> - - - -
- -
-
- 【 <%=v2ray_version%>】 - - -
-
-
\ No newline at end of file diff --git a/luci-app-v2ray-server/po/zh-cn/v2ray_server.po b/luci-app-v2ray-server/po/zh-cn/v2ray_server.po deleted file mode 100644 index cdef357e2..000000000 --- a/luci-app-v2ray-server/po/zh-cn/v2ray_server.po +++ /dev/null @@ -1,185 +0,0 @@ -msgid "V2ray Server" -msgstr "V2ray 服务器" - -msgid "Global Settings" -msgstr "全局设置" - -msgid "Caddy path" -msgstr "Caddy 路径" - -msgid "if you want to run from memory, change the path, such as /tmp/caddy, Then save the application and update it manually." -msgstr "如果你希望从内存中运行,请更改路径,例如/tmp/caddy,然后保存应用后,再手动更新。" - -msgid "Server Config" -msgstr "服务器配置" - -msgid "Users Manager" -msgstr "用户管理" - -msgid "Remarks" -msgstr "备注" - -msgid "Bind Local" -msgstr "本机监听" - -msgid "When selected, it can only be accessed locally,It is recommended to turn on when using reverse proxies." -msgstr "当勾选时,只能由本机访问此端口,当开启反向代理时建议勾选此项。" - -msgid "Port" -msgstr "端口" - -msgid "User name" -msgstr "用户名" - -msgid "Password" -msgstr "密码" - -msgid "Protocol" -msgstr "协议" - -msgid "Null" -msgstr "无" - -msgid "Alter ID" -msgstr "额外ID(AlterID)" - -msgid "User Level" -msgstr "用户等级(Level)" - -msgid "When OTA is enabled, V2Ray will reject connections that are not OTA enabled. This option is invalid when using AEAD encryption." -msgstr "开启 OTA 后,V2Ray 会拒绝未启用 OTA 的连接。当使用 AEAD 加密时,该选项无效。" - -msgid "Transport" -msgstr "传输方式" - -msgid "Camouflage Type" -msgstr "伪装类型" - -msgid "Camouflage Domain,you can not fill in" -msgstr "伪装域名,也可以不填写" - -msgid "Reverse Proxy" -msgstr "反向代理" - -msgid "Reverse Proxy Type" -msgstr "反向代理类型" - -msgid "can not has conflict" -msgstr "请不要冲突" - -msgid "Use HTTPS" -msgstr "使用HTTPS" - -msgid "TLS Settings" -msgstr "TLS配置" - -msgid "Nginx does not support HTTP/2 reverse proxies" -msgstr "Nginx 不支持HTTP/2反向代理" - -msgid "as:" -msgstr "如:" - -msgid "Public key absolute path" -msgstr "公钥文件绝对路径" - -msgid "Private key absolute path" -msgstr "私钥文件绝对路径" - -msgid "
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)" -msgstr "
none:默认值,不进行伪装,发送的数据是没有特征的数据包。
srtp:伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime)。
utp:伪装成 uTP 数据包,会被识别为 BT 下载数据。
wechat-video:伪装成微信视频通话的数据包。
dtls:伪装成 DTLS 1.2 数据包。
wireguard:伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议)" - -msgid "transit node" -msgstr "中转到此节点" - -msgid "This is the node inside passwall, which you can't use if you don't have it installed." -msgstr "这里显示的是passwall里的节点,如果没有安装,将无法使用此功能。" - -msgid "Accept LAN Access" -msgstr "接受局域网访问" - -msgid "When selected, it can accessed lan , this will not be safe!" -msgstr "当勾选时,可以直接访问局域网,这将不安全!(非特殊情况不建议开启)" - -msgid "Logs" -msgstr "日志" - -msgid "Clear logs" -msgstr "清空日志" - -msgid "Can't determine ARCH, or ARCH not supported." -msgstr "无法确认ARCH架构,或是不支持。" - -msgid "Get remote version info failed." -msgstr "获取远程版本信息失败。" - -msgid "New version found, but failed to get new version download url." -msgstr "发现新版本,但未能获得新版本的下载地址。" - -msgid "Download url is required." -msgstr "请指定下载地址。" - -msgid "File download failed or timed out: %s" -msgstr "文件下载失败或超时:%s" - -msgid "File path required." -msgstr "请指定文件路径。" - -msgid "Can't find client in file: %s" -msgstr "无法在文件中找到客户端:%s" - -msgid "Client file is required." -msgstr "请指定客户端文件。" - -msgid "The client file is not suitable for current device." -msgstr "客户端文件不适合当前设备。" - -msgid "Can't move new file to path: %s" -msgstr "无法移动新文件到:%s" - -msgid "Update..." -msgstr "更新中" - -msgid "It is the latest version" -msgstr "已是最新版本" - -msgid "Update successful" -msgstr "更新成功" - -msgid "Click to update" -msgstr "点击更新" - -msgid "Updating..." -msgstr "更新中" - -msgid "Unexpected error" -msgstr "意外错误" - -msgid "Updating, are you sure to close?" -msgstr "正在更新,你确认要关闭吗?" - -msgid "Downloading..." -msgstr "下载中" - -msgid "Unpacking..." -msgstr "解压中" - -msgid "Moving..." -msgstr "移动中" - -msgid "Enabled" -msgstr "启用" - -msgid "Status" -msgstr "状态" - -msgid "Current Condition" -msgstr "当前状态" - -msgid "NOT RUNNING" -msgstr "未运行" - -msgid "RUNNING" -msgstr "运行中" - -msgid "Manually update" -msgstr "检查更新" diff --git a/luci-app-v2ray-server/po/zh_Hans b/luci-app-v2ray-server/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-v2ray-server/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-v2ray-server/root/etc/config/v2ray_server b/luci-app-v2ray-server/root/etc/config/v2ray_server deleted file mode 100644 index 086ee4591..000000000 --- a/luci-app-v2ray-server/root/etc/config/v2ray_server +++ /dev/null @@ -1,4 +0,0 @@ - -config global - option enable '0' - diff --git a/luci-app-v2ray-server/root/etc/init.d/v2ray_server b/luci-app-v2ray-server/root/etc/init.d/v2ray_server deleted file mode 100755 index 2b61654a9..000000000 --- a/luci-app-v2ray-server/root/etc/init.d/v2ray_server +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 - -start() { - /usr/lib/lua/luci/model/cbi/v2ray_server/api/app.lua start -} - -stop() { - /usr/lib/lua/luci/model/cbi/v2ray_server/api/app.lua stop -} - -restart() { - stop - start -} diff --git a/luci-app-v2ray-server/root/etc/uci-defaults/luci-v2ray-server b/luci-app-v2ray-server/root/etc/uci-defaults/luci-v2ray-server deleted file mode 100644 index e951d71bb..000000000 --- a/luci-app-v2ray-server/root/etc/uci-defaults/luci-v2ray-server +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-v2ray_server.json" ] && { - cat > /usr/share/ucitrack/luci-app-v2ray_server.json << EEOF -{ - "config": "v2ray_server", - "init": "v2ray_server" -} -EEOF -} - -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=/var/etc/v2ray_server.include - set firewall.v2ray_server.reload=1 - commit firewall -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 - -rm -rf /tmp/luci-*cache -exit 0 diff --git a/luci-app-v2ray-server/root/usr/share/rpcd/acl.d/luci-app-v2ray-server.json b/luci-app-v2ray-server/root/usr/share/rpcd/acl.d/luci-app-v2ray-server.json deleted file mode 100644 index db27975cb..000000000 --- a/luci-app-v2ray-server/root/usr/share/rpcd/acl.d/luci-app-v2ray-server.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-v2ray-server": { - "description": "Grant UCI access for luci-app-v2ray-server", - "read": { - "uci": [ "v2ray_server" ] - }, - "write": { - "uci": [ "v2ray_server" ] - } - } -} diff --git a/luci-app-v2ray-server/root/usr/share/ucitrack/luci-app-v2ray-server.json b/luci-app-v2ray-server/root/usr/share/ucitrack/luci-app-v2ray-server.json deleted file mode 100644 index 5922688a0..000000000 --- a/luci-app-v2ray-server/root/usr/share/ucitrack/luci-app-v2ray-server.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "config": "v2ray_server", - "init": "v2ray_server" -} diff --git a/luci-app-verysync/Makefile b/luci-app-verysync/Makefile index 09afdbb77..ece870193 100644 --- a/luci-app-verysync/Makefile +++ b/luci-app-verysync/Makefile @@ -8,10 +8,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for Verysync LUCI_DEPENDS:=+verysync LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-verysync PKG_VERSION:=1.0 -PKG_RELEASE:=2 +PKG_RELEASE:=3 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-verysync/luasrc/controller/verysync.lua b/luci-app-verysync/luasrc/controller/verysync.lua index f89a2a1c8..04518d5bb 100644 --- a/luci-app-verysync/luasrc/controller/verysync.lua +++ b/luci-app-verysync/luasrc/controller/verysync.lua @@ -6,12 +6,12 @@ function index() end entry({"admin", "nas", "verysync"}, cbi("verysync"), _("Verysync"), 10).dependent = true - entry({"admin", "nas", "verysync", "status"}, call("act_status")).leaf = true + entry({"admin","nas","verysync","status"},call("act_status")).leaf=true end function act_status() - local e = {} - e.running = luci.sys.call("pgrep verysync >/dev/null") == 0 + local e={} + e.running=luci.sys.call("pgrep verysync >/dev/null")==0 luci.http.prepare_content("application/json") luci.http.write_json(e) end diff --git a/luci-app-verysync/luasrc/model/cbi/verysync.lua b/luci-app-verysync/luasrc/model/cbi/verysync.lua index 097d6f188..f8bfd26bb 100644 --- a/luci-app-verysync/luasrc/model/cbi/verysync.lua +++ b/luci-app-verysync/luasrc/model/cbi/verysync.lua @@ -21,7 +21,4 @@ o.placeholder = "8886" o.default = "8886" o.rmempty = false -o = s:option(Value, "profile", translate("Store configuration files in the Path")) -o.default = '/etc/verysync/' - return m diff --git a/luci-app-verysync/luasrc/view/verysync/verysync_status.htm b/luci-app-verysync/luasrc/view/verysync/verysync_status.htm index bd7874c58..16f028803 100644 --- a/luci-app-verysync/luasrc/view/verysync/verysync_status.htm +++ b/luci-app-verysync/luasrc/view/verysync/verysync_status.htm @@ -5,24 +5,19 @@ XHR.poll(3, '<%=url([[admin]], [[nas]], [[verysync]], [[status]])%>', null, if (data && tb) { if (data.running) { - var links = 'Verysync <%:RUNNING%>'; + var links = 'Verysync <%:RUNNING%>'; tb.innerHTML = links; } else { - tb.innerHTML = 'Verysync <%:NOT RUNNING%>'; + tb.innerHTML = 'Verysync <%:NOT RUNNING%>'; } } } ); -function openClient() { - var curWwwPath = window.document.location.href; - var pathName = window.document.location.pathname; - var pos = curWwwPath.indexOf(pathName); - var localhostPath = curWwwPath.substring(0, pos); - var clientPort = window.document.getElementById("cbid.verysync.config.port").value - var url = localhostPath + ":" + clientPort; - window.open(url) -}; +function openwebui(){ + var url = window.location.host+":<%=luci.sys.exec("uci -q get verysync.config.port"):gsub("^%s*(.-)%s*$", "%1")%>"; + window.open('http://'+url,'target',''); +} //]]> diff --git a/luci-app-verysync/po/zh-cn/verysync.po b/luci-app-verysync/po/zh-cn/verysync.po index 647e401ff..30b23ea82 100644 --- a/luci-app-verysync/po/zh-cn/verysync.po +++ b/luci-app-verysync/po/zh-cn/verysync.po @@ -6,6 +6,3 @@ msgstr "简单易用的多平台文件同步软件, 惊人的传输速度是不 msgid "Open Verysync page" msgstr "打开微力同步页面" - -msgid "Store configuration files in the Path" -msgstr "配置文件存放路径" diff --git a/luci-app-verysync/root/etc/init.d/verysync b/luci-app-verysync/root/etc/init.d/verysync index 1ebffe972..bb54ecadf 100755 --- a/luci-app-verysync/root/etc/init.d/verysync +++ b/luci-app-verysync/root/etc/init.d/verysync @@ -1,20 +1,17 @@ #!/bin/sh /etc/rc.common -START=50 +START=99 STOP=10 -VERYSYNC="/usr/bin/verysync" - start() { local enabled="$(uci get verysync.config.enabled)" local port="$(uci get verysync.config.port)" - local profile="$(uci get verysync.config.profile)" stop [ "${enabled}" == "1" ] || exit 0 export HOME="/root/" - "${VERYSYNC}" -gui-address="0.0.0.0:${port}" -logfile="/var/log/verysync.log" -home="${profile}" -no-browser >/dev/null 2>&1 & + verysync -gui-address="0.0.0.0:${port}" -logfile="/var/log/verysync.log" -home="/etc/verysync/" -no-browser >/dev/null 2>&1 & } stop() { - kill -9 `pgrep "${VERYSYNC}"` >/dev/null 2>&1 + kill -9 `pgrep verysync` >/dev/null 2>&1 } diff --git a/luci-app-verysync/root/etc/uci-defaults/luci-verysync b/luci-app-verysync/root/etc/uci-defaults/luci-verysync deleted file mode 100644 index 13da5d674..000000000 --- a/luci-app-verysync/root/etc/uci-defaults/luci-verysync +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-verysync.json" ] && { - cat > /usr/share/ucitrack/luci-app-verysync.json << EEOF -{ - "config": "verysync", - "init": "verysync" -} -EEOF -} - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@verysync[-1] - add ucitrack verysync - set ucitrack.@verysync[-1].init=verysync - commit ucitrack -EOF - -rm -f /tmp/luci-indexcache -exit 0 diff --git a/luci-app-webadmin/Makefile b/luci-app-webadmin/Makefile deleted file mode 100644 index 71e365e4e..000000000 --- a/luci-app-webadmin/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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 -LUCI_PKGARCH:=all - -PKG_NAME:=luci-app-webadmin -PKG_VERSION:=2 -PKG_RELEASE:=2 - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-webadmin/luasrc/controller/webadmin.lua b/luci-app-webadmin/luasrc/controller/webadmin.lua deleted file mode 100644 index 5f173005a..000000000 --- a/luci-app-webadmin/luasrc/controller/webadmin.lua +++ /dev/null @@ -1,12 +0,0 @@ --- 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 - - entry({"admin", "system", "webadmin"}, cbi("webadmin"), _("Web Admin"), 1).leaf = true -end diff --git a/luci-app-webadmin/luasrc/model/cbi/webadmin.lua b/luci-app-webadmin/luasrc/model/cbi/webadmin.lua deleted file mode 100644 index dc80851c5..000000000 --- a/luci-app-webadmin/luasrc/model/cbi/webadmin.lua +++ /dev/null @@ -1,53 +0,0 @@ --- Copyright 2015 Daniel Dickinson --- Licensed to the public under the Apache License 2.0. - -local fs = require("nixio.fs") - -m = Map("uhttpd") -m.title = translate("Web Admin Settings") -m.description = translate("Web Admin Settings Page") - -ucs = m:section(TypedSection, "uhttpd") -ucs.addremove = false -ucs.anonymous = true - -lhttp = ucs:option(DynamicList, "listen_http", translate("HTTP listeners (address:port)")) -lhttp.description = 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.description = translate("Redirect all HTTP to HTTPS when SSl cert was installed") -o.default = o.enabled -o.rmempty = false - -o = ucs:option(Flag, "rfc1918_filter", translate("Ignore private IPs on public interface")) -o.description = 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/luci-app-webadmin/po/zh-cn/webadmin.po b/luci-app-webadmin/po/zh-cn/webadmin.po deleted file mode 100644 index d2dab011e..000000000 --- a/luci-app-webadmin/po/zh-cn/webadmin.po +++ /dev/null @@ -1,46 +0,0 @@ -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" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: dingpengyu \n" -"Language-Team: \n" -"X-Generator: Poedit 2.3.1\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/luci-app-webadmin/po/zh_Hans b/luci-app-webadmin/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-webadmin/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-lib-docker/Makefile b/luci-lib-docker/Makefile deleted file mode 100644 index 41c1f45b6..000000000 --- a/luci-lib-docker/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-lib-docker -PKG_VERSION:=v0.3.4 -PKG_RELEASE:=1 -PKG_MAINTAINER:=lisaac \ - Florian Eckert -PKG_LICENSE:=AGPL-3.0 - -LUCI_TYPE:=col - -LUCI_TITLE:=LuCI library for docker -LUCI_DESCRIPTION:=Docker Engine API for LuCI - -LUCI_DEPENDS:=@(aarch64||arm||x86_64) +luci-lib-jsonc -LUCI_PKGARCH:=all - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-lib-docker/luasrc/docker.lua b/luci-lib-docker/luasrc/docker.lua deleted file mode 100644 index b99f6ce0e..000000000 --- a/luci-lib-docker/luasrc/docker.lua +++ /dev/null @@ -1,453 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface -Copyright 2019 lisaac -]]-- -require "nixio.util" -require "luci.util" -local jsonc = require "luci.jsonc" -local nixio = require "nixio" -local ltn12 = require "luci.ltn12" -local fs = require "nixio.fs" - -local urlencode = luci.util.urlencode or luci.http and luci.http.protocol and luci.http.protocol.urlencode -local json_stringify = jsonc.stringify -local json_parse = jsonc.parse - -local chunksource = function(sock, buffer) - buffer = buffer or "" - return function() - local output - local _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n") - if not count then - local newblock, code = sock:recv(1024) - if not newblock then return nil, code end - buffer = buffer .. newblock - _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n") - end - count = tonumber(count, 16) - if not count then - return nil, -1, "invalid encoding" - elseif count == 0 then -- finial - return nil - elseif count <= #buffer - endp then - --data >= count - output = buffer:sub(endp + 1, endp + count) - if count == #buffer - endp then -- [data] - buffer = buffer:sub(endp + count + 1) - count, code = sock:recvall(2) --read \r\n - if not count then return nil, code end - elseif count + 1 == #buffer - endp then -- [data]\r - buffer = buffer:sub(endp + count + 2) - count, code = sock:recvall(1) --read \n - if not count then return nil, code end - else -- [data]\r\n[count]\r\n[data]... - buffer = buffer:sub(endp + count + 3) -- cut buffer - end - return output - else - -- data < count - output = buffer:sub(endp + 1, endp + count) - buffer = buffer:sub(endp + count + 1) - local remain, code = sock:recvall(count - #output) --need read remaining - if not remain then return nil, code end - output = output .. remain - count, code = sock:recvall(2) --read \r\n - if not count then return nil, code end - return output - end - end -end - -local chunksink = function (sock) - return function(chunk, err) - if not chunk then - return sock:writeall("0\r\n\r\n") - else - return sock:writeall(("%X\r\n%s\r\n"):format(#chunk, tostring(chunk))) - end - end -end - -local docker_stream_filter = function(buffer) - buffer = buffer or "" - if #buffer < 8 then - return "" - end - local stream_type = ((string.byte(buffer, 1) == 1) and "stdout") or ((string.byte(buffer, 1) == 2) and "stderr") or ((string.byte(buffer, 1) == 0) and "stdin") or "stream_err" - local valid_length = - tonumber(string.byte(buffer, 5)) * 256 * 256 * 256 + tonumber(string.byte(buffer, 6)) * 256 * 256 + tonumber(string.byte(buffer, 7)) * 256 + tonumber(string.byte(buffer, 8)) - if valid_length > #buffer + 8 then - return "" - end - return stream_type .. ": " .. string.sub(buffer, 9, valid_length + 8) - -- return string.sub(buffer, 9, valid_length + 8) -end - -local open_socket = function(req_options) - local socket - if type(req_options) ~= "table" then return socket end - if req_options.socket_path then - socket = nixio.socket("unix", "stream") - if socket:connect(req_options.socket_path) ~= true then return nil end - elseif req_options.host and req_options.port then - socket = nixio.connect(req_options.host, req_options.port) - end - if socket then - return socket - else - return nil - end -end - -local send_http_socket = function(options, docker_socket, req_header, req_body, callback) - if docker_socket:send(req_header) == 0 then - return { - headers={code=498,message="bad path", protocol="HTTP/1.1"}, - body={message="can\'t send data to socket"} - } - end - - if req_body and type(req_body) == "function" and req_header and req_header:match("chunked") then - -- chunked send - req_body(chunksink(docker_socket)) - elseif req_body and type(req_body) == "function" then - -- normal send by req_body function - req_body(docker_socket) - elseif req_body and type(req_body) == "table" then - -- json - docker_socket:send(json_stringify(req_body)) - if options.debug then io.popen("echo '".. json_stringify(req_body) .. "' >> " .. options.debug_path) end - elseif req_body then - docker_socket:send(req_body) - if options.debug then io.popen("echo '".. req_body .. "' >> " .. options.debug_path) end - end - - local linesrc = docker_socket:linesource() - -- read socket using source http://w3.impa.br/~diego/software/luasocket/ltn12.html - --http://lua-users.org/wiki/FiltersSourcesAndSinks - -- handle response header - local line = linesrc() - if not line then - docker_socket:close() - return { - headers = {code=499, message="bad socket path", protocol="HTTP/1.1"}, - body = {message="no data receive from socket"} - } - end - local response = {code = 0, headers = {}, body = {}} - - local p, code, msg = line:match("^([%w./]+) ([0-9]+) (.*)") - response.protocol = p - response.code = tonumber(code) - response.message = msg - line = linesrc() - while line and line ~= "" do - local key, val = line:match("^([%w-]+)%s?:%s?(.*)") - if key and key ~= "Status" then - if type(response.headers[key]) == "string" then - response.headers[key] = {response.headers[key], val} - elseif type(response.headers[key]) == "table" then - response.headers[key][#response.headers[key] + 1] = val - else - response.headers[key] = val - end - end - line = linesrc() - end - -- handle response body - local body_buffer = linesrc(true) - response.body = {} - if type(callback) ~= "function" then - if response.headers["Transfer-Encoding"] == "chunked" then - local source = chunksource(docker_socket, body_buffer) - code = ltn12.pump.all(source, (ltn12.sink.table(response.body))) and response.code or 555 - response.code = code - else - local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource()) - code = ltn12.pump.all(body_source, (ltn12.sink.table(response.body))) and response.code or 555 - response.code = code - end - else - if response.headers["Transfer-Encoding"] == "chunked" then - local source = chunksource(docker_socket, body_buffer) - callback(response, source) - else - local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource()) - callback(response, body_source) - end - end - docker_socket:close() - return response -end - -local gen_header = function(options, http_method, api_group, api_action, name_or_id, request) - local header, query, path - name_or_id = (name_or_id ~= "") and name_or_id or nil - - if request and type(request.query) == "table" then - local k, v - for k, v in pairs(request.query) do - if type(v) == "table" then - query = (query and query .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v)) - elseif type(v) == "boolean" then - query = (query and query .. "&" or "?") .. k .. "=" .. (v and "true" or "false") - elseif type(v) == "number" or type(v) == "string" then - query = (query and query .. "&" or "?") .. k .. "=" .. v - end - end - end - path = (api_group and ("/" .. api_group) or "") .. (name_or_id and ("/" .. name_or_id) or "") .. (api_action and ("/" .. api_action) or "") .. (query or "") - header = (http_method or "GET") .. " " .. path .. " " .. options.protocol .. "\r\n" - header = header .. "Host: " .. options.host .. "\r\n" - header = header .. "User-Agent: " .. options.user_agent .. "\r\n" - header = header .. "Connection: close\r\n" - - if request and type(request.header) == "table" then - local k, v - for k, v in pairs(request.header) do - header = header .. k .. ": " .. v .. "\r\n" - end - end - - -- when requst_body is function, we need to custom header using custom header - if request and request.body and type(request.body) == "function" then - if not header:match("Content-Length:") then - header = header .. "Transfer-Encoding: chunked\r\n" - end - elseif http_method == "POST" and request and request.body and type(request.body) == "table" then - local conetnt_json = json_stringify(request.body) - header = header .. "Content-Type: application/json\r\n" - header = header .. "Content-Length: " .. #conetnt_json .. "\r\n" - elseif request and request.body and type(request.body) == "string" then - header = header .. "Content-Length: " .. #request.body .. "\r\n" - end - header = header .. "\r\n" - if options.debug then io.popen("echo '".. header .. "' >> " .. options.debug_path) end - return header -end - -local call_docker = function(options, http_method, api_group, api_action, name_or_id, request, callback) - local req_options = setmetatable({}, {__index = options}) - - local req_header = gen_header(req_options, http_method, api_group, api_action, name_or_id, request) - local req_body = request and request.body or nil - local docker_socket = open_socket(req_options) - - if docker_socket then - return send_http_socket(options, docker_socket, req_header, req_body, callback) - else - return { - headers = {code=497, message="bad socket path or host", protocol="HTTP/1.1"}, - body = {message="can\'t connect to socket"} - } - end -end - -local gen_api = function(_table, http_method, api_group, api_action) - local _api_action - if api_action == "get_archive" or api_action == "put_archive" then - _api_action = "archive" - elseif api_action == "df" then - _api_action = "system/df" - elseif api_action ~= "list" and api_action ~= "inspect" and api_action ~= "remove" then - _api_action = api_action - elseif (api_group == "containers" or api_group == "images" or api_group == "exec") and (api_action == "list" or api_action == "inspect") then - _api_action = "json" - end - - local fp = function(self, request, callback) - local name_or_id = request and (request.name or request.id or request.name_or_id) or nil - if api_action == "list" then - if (name_or_id ~= "" and name_or_id ~= nil) then - if api_group == "images" then - name_or_id = nil - else - request.query = request and request.query or {} - request.query.filters = request.query.filters or {} - request.query.filters.name = request.query.filters.name or {} - request.query.filters.name[#request.query.filters.name + 1] = name_or_id - name_or_id = nil - end - end - elseif api_action == "create" then - if (name_or_id ~= "" and name_or_id ~= nil) then - request.query = request and request.query or {} - request.query.name = request.query.name or name_or_id - name_or_id = nil - end - elseif api_action == "logs" then - local body_buffer = "" - local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback) - if response.code >= 200 and response.code < 300 then - for i, v in ipairs(response.body) do - body_buffer = body_buffer .. docker_stream_filter(response.body[i]) - end - response.body = body_buffer - end - return response - end - local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback) - if response.headers and response.headers["Content-Type"] == "application/json" then - if #response.body == 1 then - response.body = json_parse(response.body[1]) - else - local tmp = {} - for _, v in ipairs(response.body) do - tmp[#tmp+1] = json_parse(v) - end - response.body = tmp - end - end - return response - end - - if api_group then - _table[api_group][api_action] = fp - else - _table[api_action] = fp - end -end - -local _docker = {containers = {}, exec = {}, images = {}, networks = {}, volumes = {}} - -gen_api(_docker, "GET", "containers", "list") -gen_api(_docker, "POST", "containers", "create") -gen_api(_docker, "GET", "containers", "inspect") -gen_api(_docker, "GET", "containers", "top") -gen_api(_docker, "GET", "containers", "logs") -gen_api(_docker, "GET", "containers", "changes") -gen_api(_docker, "GET", "containers", "stats") -gen_api(_docker, "POST", "containers", "resize") -gen_api(_docker, "POST", "containers", "start") -gen_api(_docker, "POST", "containers", "stop") -gen_api(_docker, "POST", "containers", "restart") -gen_api(_docker, "POST", "containers", "kill") -gen_api(_docker, "POST", "containers", "update") -gen_api(_docker, "POST", "containers", "rename") -gen_api(_docker, "POST", "containers", "pause") -gen_api(_docker, "POST", "containers", "unpause") -gen_api(_docker, "POST", "containers", "update") -gen_api(_docker, "DELETE", "containers", "remove") -gen_api(_docker, "POST", "containers", "prune") -gen_api(_docker, "POST", "containers", "exec") -gen_api(_docker, "POST", "exec", "start") -gen_api(_docker, "POST", "exec", "resize") -gen_api(_docker, "GET", "exec", "inspect") -gen_api(_docker, "GET", "containers", "get_archive") -gen_api(_docker, "PUT", "containers", "put_archive") -gen_api(_docker, "GET", "containers", "export") --- TODO: attch - -gen_api(_docker, "GET", "images", "list") -gen_api(_docker, "POST", "images", "create") -gen_api(_docker, "GET", "images", "inspect") -gen_api(_docker, "GET", "images", "history") -gen_api(_docker, "POST", "images", "tag") -gen_api(_docker, "DELETE", "images", "remove") -gen_api(_docker, "GET", "images", "search") -gen_api(_docker, "POST", "images", "prune") -gen_api(_docker, "GET", "images", "get") -gen_api(_docker, "POST", "images", "load") - -gen_api(_docker, "GET", "networks", "list") -gen_api(_docker, "GET", "networks", "inspect") -gen_api(_docker, "DELETE", "networks", "remove") -gen_api(_docker, "POST", "networks", "create") -gen_api(_docker, "POST", "networks", "connect") -gen_api(_docker, "POST", "networks", "disconnect") -gen_api(_docker, "POST", "networks", "prune") - -gen_api(_docker, "GET", "volumes", "list") -gen_api(_docker, "GET", "volumes", "inspect") -gen_api(_docker, "DELETE", "volumes", "remove") -gen_api(_docker, "POST", "volumes", "create") - -gen_api(_docker, "GET", nil, "events") -gen_api(_docker, "GET", nil, "version") -gen_api(_docker, "GET", nil, "info") -gen_api(_docker, "GET", nil, "_ping") -gen_api(_docker, "GET", nil, "df") - -function _docker.new(options) - local docker = {} - local _options = options or {} - docker.options = { - socket_path = _options.socket_path or nil, - host = _options.socket_path and "localhost" or _options.host, - port = not _options.socket_path and _options.port or nil, - tls = _options.tls or nil, - tls_cacert = _options.tls and _options.tls_cacert or nil, - tls_cert = _options.tls and _options.tls_cert or nil, - tls_key = _options.tls and _options.tls_key or nil, - version = _options.version or "v1.40", - user_agent = _options.user_agent or "LuCI", - protocol = _options.protocol or "HTTP/1.1", - debug = _options.debug or false, - debug_path = _options.debug and _options.debug_path or nil - } - setmetatable( - docker, - { - __index = function(t, key) - if _docker[key] ~= nil then - return _docker[key] - else - return _docker.containers[key] - end - end - } - ) - setmetatable( - docker.containers, - { - __index = function(t, key) - if key == "options" then - return docker.options - end - end - } - ) - setmetatable( - docker.networks, - { - __index = function(t, key) - if key == "options" then - return docker.options - end - end - } - ) - setmetatable( - docker.images, - { - __index = function(t, key) - if key == "options" then - return docker.options - end - end - } - ) - setmetatable( - docker.volumes, - { - __index = function(t, key) - if key == "options" then - return docker.options - end - end - } - ) - setmetatable( - docker.exec, - { - __index = function(t, key) - if key == "options" then - return docker.options - end - end - } - ) - return docker -end - -return _docker diff --git a/luci-lib-fs/Makefile b/luci-lib-fs/Makefile deleted file mode 100644 index edce82382..000000000 --- a/luci-lib-fs/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# -# 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:=2 - -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-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/luci-lib-fs/files/fs.lua b/luci-lib-fs/files/fs.lua deleted file mode 100644 index a81ff675d..000000000 --- a/luci-lib-fs/files/fs.lua +++ /dev/null @@ -1,244 +0,0 @@ ---[[ -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